include.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import os
  2. from typing import TYPE_CHECKING, Any, Dict, List, Match, Union
  3. from ._base import BaseDirective, DirectivePlugin
  4. if TYPE_CHECKING:
  5. from ..block_parser import BlockParser
  6. from ..core import BaseRenderer, BlockState
  7. from ..markdown import Markdown
  8. class Include(DirectivePlugin):
  9. def parse(
  10. self, block: "BlockParser", m: Match[str], state: "BlockState"
  11. ) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
  12. source_file = state.env.get("__file__")
  13. if not source_file:
  14. return {"type": "block_error", "raw": "Missing source file"}
  15. encoding = "utf-8"
  16. options = self.parse_options(m)
  17. if options:
  18. attrs = dict(options)
  19. if "encoding" in attrs:
  20. encoding = attrs["encoding"]
  21. else:
  22. attrs = {}
  23. relpath = self.parse_title(m)
  24. dest = os.path.join(os.path.dirname(source_file), relpath)
  25. dest = os.path.normpath(dest)
  26. if dest == source_file:
  27. return {
  28. "type": "block_error",
  29. "raw": "Could not include self: " + relpath,
  30. }
  31. if not os.path.isfile(dest):
  32. return {
  33. "type": "block_error",
  34. "raw": "Could not find file: " + relpath,
  35. }
  36. with open(dest, "rb") as f:
  37. content = f.read().decode(encoding)
  38. ext = os.path.splitext(relpath)[1]
  39. if ext in {".md", ".markdown", ".mkd"}:
  40. new_state = block.state_cls()
  41. new_state.env["__file__"] = dest
  42. new_state.process(content)
  43. block.parse(new_state)
  44. return new_state.tokens
  45. elif ext in {".html", ".xhtml", ".htm"}:
  46. return {"type": "block_html", "raw": content}
  47. attrs["filepath"] = dest
  48. return {
  49. "type": "include",
  50. "raw": content,
  51. "attrs": attrs,
  52. }
  53. def __call__(self, directive: BaseDirective, md: "Markdown") -> None:
  54. directive.register("include", self.parse)
  55. if md.renderer and md.renderer.NAME == "html":
  56. md.renderer.register("include", render_html_include)
  57. def render_html_include(renderer: "BaseRenderer", text: str, **attrs: Any) -> str:
  58. return '<pre class="directive-include">\n' + text + "</pre>\n"