codekingpro/portable-devtools
115k
1from __future__ import annotations2 3from collections.abc import Callable, Iterable, MutableMapping4from collections.abc import MutableMapping as MutableMappingABC5from pathlib import Path6from typing import TYPE_CHECKING, Any, TypedDict, cast7 8if TYPE_CHECKING:9 from typing_extensions import NotRequired10 11 12EnvType = MutableMapping[str, Any] # note: could use TypeAlias in python 3.1013"""Type for the environment sandbox used in parsing and rendering,14which stores mutable variables for use by plugins and rules.15"""16 17 18class OptionsType(TypedDict):19 """Options for parsing."""20 21 maxNesting: int22 """Internal protection, recursion limit."""23 html: bool24 """Enable HTML tags in source."""25 linkify: bool26 """Enable autoconversion of URL-like texts to links."""27 typographer: bool28 """Enable smartquotes and replacements."""29 quotes: str30 """Quote characters."""31 xhtmlOut: bool32 """Use '/' to close single tags (<br />)."""33 breaks: bool34 """Convert newlines in paragraphs into <br>."""35 langPrefix: str36 """CSS language prefix for fenced blocks."""37 highlight: Callable[[str, str, str], str] | None38 """Highlighter function: (content, lang, attrs) -> str."""39 store_labels: NotRequired[bool]40 """Store link label in link/image token's metadata (under Token.meta['label']).41 42 This is a Python only option, and is intended for the use of round-trip parsing.43 """44 tasklists: NotRequired[bool]45 """Enable GFM task list checkbox detection in list items."""46 alerts: NotRequired[bool]47 """Enable GitHub-style alert detection in blockquotes."""48 tasklists_editable: NotRequired[bool]49 """When True, rendered task list checkboxes are interactive (no disabled attribute)."""50 strikethrough_single_tilde: NotRequired[bool]51 """Allow single tilde ``~text~`` for strikethrough in addition to double."""52 53 54class PresetType(TypedDict):55 """Preset configuration for markdown-it."""56 57 options: OptionsType58 """Options for parsing."""59 components: MutableMapping[str, MutableMapping[str, list[str]]]60 """Components for parsing and rendering."""61 62 63class OptionsDict(MutableMappingABC): # type: ignore64 """A dictionary, with attribute access to core markdownit configuration options."""65 66 # Note: ideally we would probably just remove attribute access entirely,67 # but we keep it for backwards compatibility.68 69 def __init__(self, options: OptionsType) -> None:70 self._options = cast(OptionsType, dict(options))71 72 def __getitem__(self, key: str) -> Any:73 return self._options[key] # type: ignore[literal-required]74 75 def __setitem__(self, key: str, value: Any) -> None:76 self._options[key] = value # type: ignore[literal-required]77 78 def __delitem__(self, key: str) -> None:79 del self._options[key] # type: ignore80 81 def __iter__(self) -> Iterable[str]: # type: ignore82 return iter(self._options)83 84 def __len__(self) -> int:85 return len(self._options)86 87 def __repr__(self) -> str:88 return repr(self._options)89 90 def __str__(self) -> str:91 return str(self._options)92 93 @property94 def maxNesting(self) -> int:95 """Internal protection, recursion limit."""96 return self._options["maxNesting"]97 98 @maxNesting.setter99 def maxNesting(self, value: int) -> None:100 self._options["maxNesting"] = value101 102 @property103 def html(self) -> bool:104 """Enable HTML tags in source."""105 return self._options["html"]106 107 @html.setter108 def html(self, value: bool) -> None:109 self._options["html"] = value110 111 @property112 def linkify(self) -> bool:113 """Enable autoconversion of URL-like texts to links."""114 return self._options["linkify"]115 116 @linkify.setter117 def linkify(self, value: bool) -> None:118 self._options["linkify"] = value119 120 @property121 def typographer(self) -> bool:122 """Enable smartquotes and replacements."""123 return self._options["typographer"]124 125 @typographer.setter126 def typographer(self, value: bool) -> None:127 self._options["typographer"] = value128 129 @property130 def quotes(self) -> str:131 """Quote characters."""132 return self._options["quotes"]133 134 @quotes.setter135 def quotes(self, value: str) -> None:136 self._options["quotes"] = value137 138 @property139 def xhtmlOut(self) -> bool:140 """Use '/' to close single tags (<br />)."""141 return self._options["xhtmlOut"]142 143 @xhtmlOut.setter144 def xhtmlOut(self, value: bool) -> None:145 self._options["xhtmlOut"] = value146 147 @property148 def breaks(self) -> bool:149 """Convert newlines in paragraphs into <br>."""150 return self._options["breaks"]151 152 @breaks.setter153 def breaks(self, value: bool) -> None:154 self._options["breaks"] = value155 156 @property157 def langPrefix(self) -> str:158 """CSS language prefix for fenced blocks."""159 return self._options["langPrefix"]160 161 @langPrefix.setter162 def langPrefix(self, value: str) -> None:163 self._options["langPrefix"] = value164 165 @property166 def highlight(self) -> Callable[[str, str, str], str] | None:167 """Highlighter function: (content, langName, langAttrs) -> escaped HTML."""168 return self._options["highlight"]169 170 @highlight.setter171 def highlight(self, value: Callable[[str, str, str], str] | None) -> None:172 self._options["highlight"] = value173 174 175def read_fixture_file(path: str | Path) -> list[list[Any]]:176 text = Path(path).read_text(encoding="utf-8")177 tests = []178 section = 0179 last_pos = 0180 lines = text.splitlines(keepends=True)181 for i in range(len(lines)):182 if lines[i].rstrip() == ".":183 if section == 0:184 tests.append([i, lines[i - 1].strip()])185 section = 1186 elif section == 1:187 tests[-1].append("".join(lines[last_pos + 1 : i]))188 section = 2189 elif section == 2:190 tests[-1].append("".join(lines[last_pos + 1 : i]))191 section = 0192 193 last_pos = i194 return tests195 