utils.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. from __future__ import annotations
  2. from collections.abc import Callable, Iterable, MutableMapping
  3. from collections.abc import MutableMapping as MutableMappingABC
  4. from pathlib import Path
  5. from typing import TYPE_CHECKING, Any, TypedDict, cast
  6. if TYPE_CHECKING:
  7. from typing_extensions import NotRequired
  8. EnvType = MutableMapping[str, Any] # note: could use TypeAlias in python 3.10
  9. """Type for the environment sandbox used in parsing and rendering,
  10. which stores mutable variables for use by plugins and rules.
  11. """
  12. class OptionsType(TypedDict):
  13. """Options for parsing."""
  14. maxNesting: int
  15. """Internal protection, recursion limit."""
  16. html: bool
  17. """Enable HTML tags in source."""
  18. linkify: bool
  19. """Enable autoconversion of URL-like texts to links."""
  20. typographer: bool
  21. """Enable smartquotes and replacements."""
  22. quotes: str
  23. """Quote characters."""
  24. xhtmlOut: bool
  25. """Use '/' to close single tags (<br />)."""
  26. breaks: bool
  27. """Convert newlines in paragraphs into <br>."""
  28. langPrefix: str
  29. """CSS language prefix for fenced blocks."""
  30. highlight: Callable[[str, str, str], str] | None
  31. """Highlighter function: (content, lang, attrs) -> str."""
  32. store_labels: NotRequired[bool]
  33. """Store link label in link/image token's metadata (under Token.meta['label']).
  34. This is a Python only option, and is intended for the use of round-trip parsing.
  35. """
  36. class PresetType(TypedDict):
  37. """Preset configuration for markdown-it."""
  38. options: OptionsType
  39. """Options for parsing."""
  40. components: MutableMapping[str, MutableMapping[str, list[str]]]
  41. """Components for parsing and rendering."""
  42. class OptionsDict(MutableMappingABC): # type: ignore
  43. """A dictionary, with attribute access to core markdownit configuration options."""
  44. # Note: ideally we would probably just remove attribute access entirely,
  45. # but we keep it for backwards compatibility.
  46. def __init__(self, options: OptionsType) -> None:
  47. self._options = cast(OptionsType, dict(options))
  48. def __getitem__(self, key: str) -> Any:
  49. return self._options[key] # type: ignore[literal-required]
  50. def __setitem__(self, key: str, value: Any) -> None:
  51. self._options[key] = value # type: ignore[literal-required]
  52. def __delitem__(self, key: str) -> None:
  53. del self._options[key] # type: ignore
  54. def __iter__(self) -> Iterable[str]: # type: ignore
  55. return iter(self._options)
  56. def __len__(self) -> int:
  57. return len(self._options)
  58. def __repr__(self) -> str:
  59. return repr(self._options)
  60. def __str__(self) -> str:
  61. return str(self._options)
  62. @property
  63. def maxNesting(self) -> int:
  64. """Internal protection, recursion limit."""
  65. return self._options["maxNesting"]
  66. @maxNesting.setter
  67. def maxNesting(self, value: int) -> None:
  68. self._options["maxNesting"] = value
  69. @property
  70. def html(self) -> bool:
  71. """Enable HTML tags in source."""
  72. return self._options["html"]
  73. @html.setter
  74. def html(self, value: bool) -> None:
  75. self._options["html"] = value
  76. @property
  77. def linkify(self) -> bool:
  78. """Enable autoconversion of URL-like texts to links."""
  79. return self._options["linkify"]
  80. @linkify.setter
  81. def linkify(self, value: bool) -> None:
  82. self._options["linkify"] = value
  83. @property
  84. def typographer(self) -> bool:
  85. """Enable smartquotes and replacements."""
  86. return self._options["typographer"]
  87. @typographer.setter
  88. def typographer(self, value: bool) -> None:
  89. self._options["typographer"] = value
  90. @property
  91. def quotes(self) -> str:
  92. """Quote characters."""
  93. return self._options["quotes"]
  94. @quotes.setter
  95. def quotes(self, value: str) -> None:
  96. self._options["quotes"] = value
  97. @property
  98. def xhtmlOut(self) -> bool:
  99. """Use '/' to close single tags (<br />)."""
  100. return self._options["xhtmlOut"]
  101. @xhtmlOut.setter
  102. def xhtmlOut(self, value: bool) -> None:
  103. self._options["xhtmlOut"] = value
  104. @property
  105. def breaks(self) -> bool:
  106. """Convert newlines in paragraphs into <br>."""
  107. return self._options["breaks"]
  108. @breaks.setter
  109. def breaks(self, value: bool) -> None:
  110. self._options["breaks"] = value
  111. @property
  112. def langPrefix(self) -> str:
  113. """CSS language prefix for fenced blocks."""
  114. return self._options["langPrefix"]
  115. @langPrefix.setter
  116. def langPrefix(self, value: str) -> None:
  117. self._options["langPrefix"] = value
  118. @property
  119. def highlight(self) -> Callable[[str, str, str], str] | None:
  120. """Highlighter function: (content, langName, langAttrs) -> escaped HTML."""
  121. return self._options["highlight"]
  122. @highlight.setter
  123. def highlight(self, value: Callable[[str, str, str], str] | None) -> None:
  124. self._options["highlight"] = value
  125. def read_fixture_file(path: str | Path) -> list[list[Any]]:
  126. text = Path(path).read_text(encoding="utf-8")
  127. tests = []
  128. section = 0
  129. last_pos = 0
  130. lines = text.splitlines(keepends=True)
  131. for i in range(len(lines)):
  132. if lines[i].rstrip() == ".":
  133. if section == 0:
  134. tests.append([i, lines[i - 1].strip()])
  135. section = 1
  136. elif section == 1:
  137. tests[-1].append("".join(lines[last_pos + 1 : i]))
  138. section = 2
  139. elif section == 2:
  140. tests[-1].append("".join(lines[last_pos + 1 : i]))
  141. section = 0
  142. last_pos = i
  143. return tests