simplibs-extractor-html 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. simplibs/extractor_html/__init__.py +82 -0
  2. simplibs/extractor_html/actions/ApplyToHtmlBlocks.py +29 -0
  3. simplibs/extractor_html/actions/CleanupHtmlWhitespace.py +22 -0
  4. simplibs/extractor_html/actions/CompactHtml.py +22 -0
  5. simplibs/extractor_html/actions/HtmlOutline.py +23 -0
  6. simplibs/extractor_html/actions/RemoveTemplateImports.py +22 -0
  7. simplibs/extractor_html/actions/StripHtmlComments.py +23 -0
  8. simplibs/extractor_html/actions/StripTemplateComments.py +27 -0
  9. simplibs/extractor_html/actions/TemplateOutline.py +23 -0
  10. simplibs/extractor_html/actions/__init__.py +57 -0
  11. simplibs/extractor_html/annotations/TemplateEngine.py +26 -0
  12. simplibs/extractor_html/annotations/__init__.py +14 -0
  13. simplibs/extractor_html/constants/HTML_ANY_TAG_PATTERN.py +16 -0
  14. simplibs/extractor_html/constants/HTML_BLOCK_LANGUAGE_BY_TYPE.py +41 -0
  15. simplibs/extractor_html/constants/HTML_COMMENT_END_PATTERN.py +19 -0
  16. simplibs/extractor_html/constants/HTML_CONDITIONAL_PATTERN.py +23 -0
  17. simplibs/extractor_html/constants/HTML_DEPENDENCY_DIR_NAMES.py +18 -0
  18. simplibs/extractor_html/constants/HTML_EXTRA_LANGUAGE_BY_SUFFIX.py +31 -0
  19. simplibs/extractor_html/constants/HTML_HEADING_ELEMENTS.py +15 -0
  20. simplibs/extractor_html/constants/HTML_ID_ATTRIBUTE_PATTERN.py +21 -0
  21. simplibs/extractor_html/constants/HTML_IMPLIED_END.py +34 -0
  22. simplibs/extractor_html/constants/HTML_LICENSE_START.py +14 -0
  23. simplibs/extractor_html/constants/HTML_LICENSE_TAGS.py +14 -0
  24. simplibs/extractor_html/constants/HTML_OUTLINE_ELEMENTS.py +32 -0
  25. simplibs/extractor_html/constants/HTML_OUTLINE_TEXT_LIMIT.py +15 -0
  26. simplibs/extractor_html/constants/HTML_OUTPUT_DIR_NAMES.py +43 -0
  27. simplibs/extractor_html/constants/HTML_PLAIN_SUFFIXES.py +18 -0
  28. simplibs/extractor_html/constants/HTML_RAW_TEXT_END_PATTERNS.py +28 -0
  29. simplibs/extractor_html/constants/HTML_SUFFIXES.py +22 -0
  30. simplibs/extractor_html/constants/HTML_TAG_NAME_PATTERN.py +16 -0
  31. simplibs/extractor_html/constants/HTML_TEMPLATE_BLOCKS.py +38 -0
  32. simplibs/extractor_html/constants/HTML_TEMPLATE_IMPORT_TAGS.py +29 -0
  33. simplibs/extractor_html/constants/HTML_TEMPLATE_OUTLINE_TAGS.py +35 -0
  34. simplibs/extractor_html/constants/HTML_TEMPLATE_SUFFIXES.py +19 -0
  35. simplibs/extractor_html/constants/HTML_TEMPLATE_TAG_NAME_PATTERN.py +16 -0
  36. simplibs/extractor_html/constants/HTML_TEMPLATE_TOKEN_PATTERN.py +16 -0
  37. simplibs/extractor_html/constants/HTML_TEXT_ELEMENTS.py +16 -0
  38. simplibs/extractor_html/constants/HTML_TOKEN_PATTERN.py +26 -0
  39. simplibs/extractor_html/constants/HTML_TYPE_ATTRIBUTE_PATTERN.py +21 -0
  40. simplibs/extractor_html/constants/HTML_VOID_ELEMENTS.py +21 -0
  41. simplibs/extractor_html/constants/HTML_WRAPPED_STEM_ENDING.py +14 -0
  42. simplibs/extractor_html/constants/HTML_WRAPPED_SUFFIXES.py +15 -0
  43. simplibs/extractor_html/constants/__init__.py +77 -0
  44. simplibs/extractor_html/file_rules/__init__.py +56 -0
  45. simplibs/extractor_html/file_rules/atomic/DEPENDENCY_DIRS.py +17 -0
  46. simplibs/extractor_html/file_rules/atomic/DOT_NAME.py +19 -0
  47. simplibs/extractor_html/file_rules/atomic/OUTPUT_DIRS.py +17 -0
  48. simplibs/extractor_html/file_rules/atomic/VCS_DIRS.py +21 -0
  49. simplibs/extractor_html/file_rules/atomic/WRAPPED_HTML.py +22 -0
  50. simplibs/extractor_html/file_rules/atomic/__init__.py +26 -0
  51. simplibs/extractor_html/file_rules/html/HTML_BASE.py +22 -0
  52. simplibs/extractor_html/file_rules/html/HTML_FILES.py +24 -0
  53. simplibs/extractor_html/file_rules/html/HTML_PLAIN_BASE.py +24 -0
  54. simplibs/extractor_html/file_rules/html/HTML_STRICT_EXCLUDE.py +21 -0
  55. simplibs/extractor_html/file_rules/html/__init__.py +20 -0
  56. simplibs/extractor_html/logic/__init__.py +44 -0
  57. simplibs/extractor_html/logic/_helpers/HtmlBlock.py +31 -0
  58. simplibs/extractor_html/logic/_helpers/HtmlOutlineBuilder.py +189 -0
  59. simplibs/extractor_html/logic/_helpers/HtmlScan.py +50 -0
  60. simplibs/extractor_html/logic/_helpers/HtmlTag.py +32 -0
  61. simplibs/extractor_html/logic/_helpers/Ranges.py +48 -0
  62. simplibs/extractor_html/logic/_helpers/TemplateScan.py +43 -0
  63. simplibs/extractor_html/logic/_helpers/TemplateTag.py +33 -0
  64. simplibs/extractor_html/logic/_helpers/__init__.py +68 -0
  65. simplibs/extractor_html/logic/_helpers/blank_spans.py +40 -0
  66. simplibs/extractor_html/logic/_helpers/build_html_outline.py +38 -0
  67. simplibs/extractor_html/logic/_helpers/build_template_outline.py +75 -0
  68. simplibs/extractor_html/logic/_helpers/find_blanks_start.py +29 -0
  69. simplibs/extractor_html/logic/_helpers/find_block_language.py +35 -0
  70. simplibs/extractor_html/logic/_helpers/find_comments.py +60 -0
  71. simplibs/extractor_html/logic/_helpers/find_protected_spans.py +48 -0
  72. simplibs/extractor_html/logic/_helpers/find_template_import_spans.py +37 -0
  73. simplibs/extractor_html/logic/_helpers/fit_removal_span.py +40 -0
  74. simplibs/extractor_html/logic/_helpers/is_license_comment.py +26 -0
  75. simplibs/extractor_html/logic/_helpers/line_of.py +23 -0
  76. simplibs/extractor_html/logic/_helpers/raise_block_result_invalid.py +38 -0
  77. simplibs/extractor_html/logic/_helpers/replace_spans.py +42 -0
  78. simplibs/extractor_html/logic/_helpers/scan_checked.py +45 -0
  79. simplibs/extractor_html/logic/_helpers/scan_html.py +208 -0
  80. simplibs/extractor_html/logic/_helpers/scan_templates.py +128 -0
  81. simplibs/extractor_html/logic/_helpers/scan_templates_checked.py +39 -0
  82. simplibs/extractor_html/logic/_helpers/skip_html_comment.py +41 -0
  83. simplibs/extractor_html/logic/_helpers/skip_html_tag.py +46 -0
  84. simplibs/extractor_html/logic/_helpers/tidy_lines.py +86 -0
  85. simplibs/extractor_html/logic/_helpers/warn_problems.py +32 -0
  86. simplibs/extractor_html/logic/apply_to_html_blocks.py +103 -0
  87. simplibs/extractor_html/logic/cleanup_html_whitespace.py +54 -0
  88. simplibs/extractor_html/logic/compact_html.py +44 -0
  89. simplibs/extractor_html/logic/html_comment_spans.py +54 -0
  90. simplibs/extractor_html/logic/html_outline.py +66 -0
  91. simplibs/extractor_html/logic/strip_html_comments.py +63 -0
  92. simplibs/extractor_html/logic/templates/__init__.py +25 -0
  93. simplibs/extractor_html/logic/templates/remove_template_imports.py +51 -0
  94. simplibs/extractor_html/logic/templates/strip_template_comments.py +60 -0
  95. simplibs/extractor_html/logic/templates/template_comment_spans.py +49 -0
  96. simplibs/extractor_html/logic/templates/template_outline.py +62 -0
  97. simplibs/extractor_html/plugin/__init__.py +14 -0
  98. simplibs/extractor_html/plugin/register_all.py +60 -0
  99. simplibs/extractor_html/presets/__init__.py +43 -0
  100. simplibs/extractor_html/presets/html_clean_steps.py +54 -0
  101. simplibs/extractor_html/presets/html_no_imports_steps.py +50 -0
  102. simplibs/extractor_html/presets/html_outline_steps.py +28 -0
  103. simplibs/extractor_html/presets/template_structure_steps.py +27 -0
  104. simplibs/extractor_html/profiles/DJANGO_CLEAN.py +36 -0
  105. simplibs/extractor_html/profiles/DJANGO_NO_IMPORTS.py +32 -0
  106. simplibs/extractor_html/profiles/DJANGO_OUTLINE.py +32 -0
  107. simplibs/extractor_html/profiles/DJANGO_STRUCTURE.py +32 -0
  108. simplibs/extractor_html/profiles/HTML_CODE_CLEAN.py +32 -0
  109. simplibs/extractor_html/profiles/HTML_CODE_NO_IMPORTS.py +30 -0
  110. simplibs/extractor_html/profiles/HTML_DATA_ALL.py +26 -0
  111. simplibs/extractor_html/profiles/HTML_OUTLINE.py +31 -0
  112. simplibs/extractor_html/profiles/HTML_PROFILES.py +42 -0
  113. simplibs/extractor_html/profiles/HTML_SRC_CLEAN.py +31 -0
  114. simplibs/extractor_html/profiles/JINJA_CLEAN.py +35 -0
  115. simplibs/extractor_html/profiles/JINJA_NO_IMPORTS.py +32 -0
  116. simplibs/extractor_html/profiles/JINJA_OUTLINE.py +30 -0
  117. simplibs/extractor_html/profiles/JINJA_STRUCTURE.py +31 -0
  118. simplibs/extractor_html/profiles/__init__.py +86 -0
  119. simplibs/extractor_html/profiles/steps_for_engine.py +39 -0
  120. simplibs/extractor_html/profiles/steps_for_pages.py +36 -0
  121. simplibs/extractor_html/py.typed +1 -0
  122. simplibs_extractor_html-0.1.0.dist-info/METADATA +504 -0
  123. simplibs_extractor_html-0.1.0.dist-info/RECORD +127 -0
  124. simplibs_extractor_html-0.1.0.dist-info/WHEEL +5 -0
  125. simplibs_extractor_html-0.1.0.dist-info/entry_points.txt +2 -0
  126. simplibs_extractor_html-0.1.0.dist-info/licenses/LICENSE +21 -0
  127. simplibs_extractor_html-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,54 @@
1
+ from simplibs.validate import validate_call
2
+ # Outers
3
+ from ..annotations import TemplateEngine
4
+ # Inners
5
+ from ._helpers import Ranges, find_protected_spans, scan_checked, tidy_lines
6
+
7
+
8
+ @validate_call
9
+ def cleanup_html_whitespace(text: str, *, engine: TemplateEngine | None = None) -> str:
10
+ """Tidy the whitespace of a page without changing how it looks: trailing blanks go, runs of blank lines become one.
11
+
12
+ Args:
13
+ text: The source.
14
+ engine: The template language of the page (`"django"` or `"jinja"`); `None` for a page that is only HTML.
15
+
16
+ Returns:
17
+ The page with no blanks at the ends of the lines and no more than one blank line in a row, and none
18
+ at the start or at the end. The lines that touch the content of `pre`, `textarea`, `script`, `style`
19
+ or a verbatim block are left exactly as they were. A page that looks damaged comes back unchanged,
20
+ with a warning.
21
+ """
22
+
23
+ # 1. Read the page; a damaged one is left as it is
24
+ scan = scan_checked(text, engine=engine)
25
+ if scan is None:
26
+ return text
27
+
28
+ # 2. Tidy every line that has no part in a protected range
29
+ return tidy_lines(text, Ranges(find_protected_spans(scan)), keep_blank_line=True)
30
+
31
+
32
+ _DESIGN_NOTES = """
33
+ # cleanup_html_whitespace — Tidy, and Leave the `pre` Alone
34
+
35
+ ## Purpose
36
+ The step of the profiles that clean pages. The general cleaning of
37
+ `simplibs-text` does not know HTML: it would trim the lines of a `<pre>` and
38
+ change what the reader sees. This one reads the page first.
39
+
40
+ ## What Is Safe
41
+ Between tags a browser treats a run of blanks and line breaks as one space, so
42
+ the blanks at the end of a line and the extra blank lines between elements
43
+ change nothing on screen.
44
+
45
+ ## What Is Left Alone
46
+ `pre`, `textarea`, `script`, `style` and verbatim blocks (see `find_protected_spans`).
47
+
48
+ ## Idempotent
49
+ A second run finds nothing to tidy.
50
+
51
+ ## Known Limits
52
+ Inline text is judged as the other text: `a <b>b</b>` keeps its two spaces,
53
+ because the step trims line ends only; it never joins or splits lines.
54
+ """
@@ -0,0 +1,44 @@
1
+ from simplibs.validate import validate_call
2
+ # Outers
3
+ from ..annotations import TemplateEngine
4
+ # Inners
5
+ from ._helpers import Ranges, find_protected_spans, scan_checked, tidy_lines
6
+
7
+
8
+ @validate_call
9
+ def compact_html(text: str, *, engine: TemplateEngine | None = None) -> str:
10
+ """Remove every blank line of a page and the trailing blanks of its lines, without changing how it looks.
11
+
12
+ Args:
13
+ text: The source.
14
+ engine: The template language of the page (`"django"` or `"jinja"`); `None` for a page that is only HTML.
15
+
16
+ Returns:
17
+ The page with no blank lines and no blanks at the ends of the lines. The lines that touch the content of
18
+ `pre`, `textarea`, `script`, `style` or a verbatim block are left exactly as they were. A page that looks
19
+ damaged comes back unchanged, with a warning.
20
+ """
21
+
22
+ # 1. Read the page; a damaged one is left as it is
23
+ scan = scan_checked(text, engine=engine)
24
+ if scan is None:
25
+ return text
26
+
27
+ # 2. Tidy every line that has no part in a protected range, dropping all the blank ones
28
+ return tidy_lines(text, Ranges(find_protected_spans(scan)), keep_blank_line=False)
29
+
30
+
31
+ _DESIGN_NOTES = """
32
+ # compact_html — The Page Without Air
33
+
34
+ ## Purpose
35
+ The step of the profiles that reduce a page to its content: the same as
36
+ `cleanup_html_whitespace`, but no blank line survives. It is the counterpart
37
+ of the `COMPACT` preset of `simplibs-text` for pages.
38
+
39
+ ## Same Care
40
+ The protected ranges are the same as for the gentler step.
41
+
42
+ ## Idempotent
43
+ A second run finds nothing to drop.
44
+ """
@@ -0,0 +1,54 @@
1
+ from simplibs.text import Span
2
+ from simplibs.validate import validate_call
3
+ # Outers
4
+ from ..annotations import TemplateEngine
5
+ # Inners
6
+ from ._helpers import find_comments
7
+
8
+
9
+ @validate_call
10
+ def html_comment_spans(
11
+ text: str,
12
+ *,
13
+ engine: TemplateEngine | None = None,
14
+ keep_conditional: bool = True,
15
+ keep_license: bool = False,
16
+ ) -> list[Span]:
17
+ """Find the HTML comments of a page.
18
+
19
+ Args:
20
+ text: The source.
21
+ engine: The template language of the page (`"django"` or `"jinja"`); `None` for a page that is only HTML.
22
+ keep_conditional: Leave out the Internet Explorer conditional comments (`<!--[if IE]> ... <![endif]-->`).
23
+ keep_license: Leave out the comments that tools keep (`<!--! ... -->`, `@license`, `@preserve`).
24
+
25
+ Returns:
26
+ A span for each comment, from the blanks in front of it to its end; an empty list for a page
27
+ that looks damaged (a comment, a tag or a `script` that is never closed), with a warning.
28
+ """
29
+
30
+ # 1. The comments the scanner found, the ones that are to stay aside
31
+ return find_comments(
32
+ text,
33
+ engine=engine,
34
+ keep_conditional=keep_conditional,
35
+ keep_license=keep_license,
36
+ )
37
+
38
+
39
+ _DESIGN_NOTES = """
40
+ # html_comment_spans — Where the Comments Are
41
+
42
+ ## Purpose
43
+ The spans of the comments, for anything that wants to look at them or cut them.
44
+
45
+ ## What Is a Comment
46
+ `<!-- ... -->` outside an attribute, a `script`, a `style`, a `textarea` and a
47
+ `title`. A `<!--` inside any of those is text. Template comments (`{# #}`) are
48
+ not reported here.
49
+
50
+ ## Never Raises
51
+ The scanner has no syntax it refuses: whatever the text is, it gets an answer.
52
+ A page that looks damaged is answered with no spans and a warning, so a step
53
+ that cuts them leaves the page as it is.
54
+ """
@@ -0,0 +1,66 @@
1
+ from simplibs.types import int_positive
2
+ from simplibs.validate import validate_call
3
+ # Outers
4
+ from ..annotations import TemplateEngine
5
+ # Inners
6
+ from ._helpers import build_html_outline, scan_checked
7
+
8
+
9
+ @validate_call
10
+ def html_outline(text: str, *, engine: TemplateEngine | None = None, max_depth: int_positive | None = None) -> str:
11
+ """Write an overview of a page: its landmarks, its headings and everything that has an `id`, indented by nesting.
12
+
13
+ Args:
14
+ text: The source.
15
+ engine: The template language of the page (`"django"` or `"jinja"`); `None` for a page that is only HTML.
16
+ max_depth: How many levels of nesting to list (`1` is the top level only); `None` lists all.
17
+
18
+ Returns:
19
+ One line for each listed element: `header#top`, `h2 "Opening hours"`; an empty string for a page
20
+ with nothing to list. A page that looks damaged (a comment, a tag or a `script` that is never
21
+ closed) comes back unchanged, with a warning.
22
+
23
+ Example:
24
+ html_outline('<main><h1>Hi</h1><div id="a"></div></main>')
25
+ # 'main\\n h1 "Hi"\\n div#a\\n'
26
+ """
27
+
28
+ # 1. Read the page with its tags; a damaged one is left as it is
29
+ scan = scan_checked(text, engine=engine, collect_tags=True)
30
+ if scan is None:
31
+ return text
32
+
33
+ # 2. The outline
34
+ return build_html_outline(text, scan, max_depth)
35
+
36
+
37
+ _DESIGN_NOTES = """
38
+ # html_outline — What a Page Is Made Of, Without the Markup
39
+
40
+ ## Purpose
41
+ The step of the profile `html/outline`: the parts of a page and what they are
42
+ called, in a fraction of its size. For a reader with a limited window it is the
43
+ map of a page that would not fit.
44
+
45
+ ## What Is Listed
46
+ * the landmarks (`header`, `nav`, `main`, `section`, `article`, `aside`,
47
+ `footer`, `form`, `table`, `dialog`, `template`),
48
+ * the title and the headings, with their text,
49
+ * every element with an `id`, written `tag#id`.
50
+
51
+ Depth is the number of listed elements around, so the `div`s between them are
52
+ not counted.
53
+
54
+ ## Not Listed
55
+ Classes, attributes other than the `id`, text outside the headings, scripts and
56
+ styles. They are the content; the outline is the shape.
57
+
58
+ ## Templates
59
+ With an `engine` the constructs are read first, so `<div {% if a > b %}>` is a
60
+ tag. An `id` that contains a template construct is written as it stands
61
+ (`item-{{ i }}`), and so is a heading with `{{ title }}` in it.
62
+
63
+ ## A Damaged Page
64
+ The whole text is returned, as `python_outline` does for a source it cannot
65
+ parse, so the reader sees the page and the report says why it is not an outline.
66
+ """
@@ -0,0 +1,63 @@
1
+ from simplibs.text import remove_spans
2
+ from simplibs.validate import validate_call
3
+ # Outers
4
+ from ..annotations import TemplateEngine
5
+ # Inners
6
+ from ._helpers import find_comments
7
+
8
+
9
+ @validate_call
10
+ def strip_html_comments(
11
+ text: str,
12
+ *,
13
+ engine: TemplateEngine | None = None,
14
+ keep_conditional: bool = True,
15
+ keep_license: bool = False,
16
+ ) -> str:
17
+ """Remove the HTML comments of a page.
18
+
19
+ Args:
20
+ text: The source.
21
+ engine: The template language of the page (`"django"` or `"jinja"`); `None` for a page that is only HTML.
22
+ keep_conditional: Keep the Internet Explorer conditional comments (`<!--[if IE]> ... <![endif]-->`).
23
+ keep_license: Keep the comments that tools keep (`<!--! ... -->`, `@license`, `@preserve`).
24
+
25
+ Returns:
26
+ The text without HTML comments; a line that held only a comment disappears. A page that looks
27
+ damaged (a comment, a tag or a `script` that is never closed) comes back unchanged, with a warning.
28
+ """
29
+
30
+ # 1. Which comments go
31
+ comments = find_comments(
32
+ text,
33
+ engine=engine,
34
+ keep_conditional=keep_conditional,
35
+ keep_license=keep_license,
36
+ )
37
+
38
+ # 2. Cut them
39
+ return remove_spans(text, comments)
40
+
41
+
42
+ _DESIGN_NOTES = """
43
+ # strip_html_comments — The Comments Out, The Scripts Untouched
44
+
45
+ ## Purpose
46
+ A step of the profiles that clean pages. It removes only `<!-- ... -->`: a
47
+ `<!--` inside an attribute, a `script`, a `style` or a `textarea` is text and
48
+ stays.
49
+
50
+ ## Options
51
+ `engine` tells the scanner that the page is a template, so `{{ }}`, `{% %}`
52
+ and `{# #}` are read first and cannot confuse the HTML. It does not remove
53
+ template comments; that is a step of its own. `keep_conditional` is on by
54
+ default: an Internet Explorer conditional comment holds real tags, and
55
+ `keep_license` is off by default.
56
+
57
+ ## A Damaged Page
58
+ It is returned as it is, and the warning makes the extractor call the file
59
+ `partial`. The step never cuts on a basis it does not trust.
60
+
61
+ ## Idempotent
62
+ A second run finds nothing and returns the very same text.
63
+ """
@@ -0,0 +1,25 @@
1
+ from .remove_template_imports import remove_template_imports
2
+ from .strip_template_comments import strip_template_comments
3
+ from .template_comment_spans import template_comment_spans
4
+ from .template_outline import template_outline
5
+
6
+
7
+ _DESIGN_NOTES = """
8
+ # templates — The Steps for Django and Jinja Templates
9
+
10
+ Plain imports, no `__all__` (the package root decides what is public).
11
+
12
+ ## Internal Components Registry
13
+
14
+ | Component | Type | Description |
15
+ | :------------------------ | :------- | :--------------------------------------------------------------- |
16
+ | `remove_template_imports` | Function | Removes `{% load %}` (Django), `{% import %}` and `{% from %}` (Jinja). |
17
+ | `strip_template_comments` | Function | Removes `{# ... #}` and `{% comment %} ... {% endcomment %}`. |
18
+ | `template_comment_spans` | Function | The spans of the template comments. |
19
+ | `template_outline` | Function | The skeleton of a template: extends, include, blocks. |
20
+
21
+ ## Layering & Design
22
+ - Every step takes the engine as a required keyword: `"django"` or `"jinja"`.
23
+ - The steps read the template constructs only; the HTML around them is not read.
24
+ - A template that looks damaged is left as it is and reported by a warning.
25
+ """
@@ -0,0 +1,51 @@
1
+ from simplibs.text import remove_spans
2
+ from simplibs.validate import validate_call
3
+ # Outers
4
+ from ...annotations import TemplateEngine
5
+ from .._helpers import find_template_import_spans, scan_templates_checked
6
+
7
+
8
+ @validate_call
9
+ def remove_template_imports(text: str, *, engine: TemplateEngine) -> str:
10
+ """Remove the tags that bring names into a template: `{% load %}` (Django), `{% import %}` and `{% from %}` (Jinja).
11
+
12
+ Args:
13
+ text: The source.
14
+ engine: `"django"` or `"jinja"`.
15
+
16
+ Returns:
17
+ The text without those tags; a line that held only one disappears. A template that looks damaged
18
+ (a `{% comment %}` that is never closed) comes back unchanged, with a warning.
19
+ """
20
+
21
+ # 1. Read the constructs; a damaged template is left as it is
22
+ scan = scan_templates_checked(text, engine=engine)
23
+ if scan is None:
24
+ return text
25
+
26
+ # 2. Each tag with one of the engine's import names is a range; cut them
27
+ return remove_spans(text, find_template_import_spans(text, scan, engine))
28
+
29
+
30
+ _DESIGN_NOTES = """
31
+ # remove_template_imports — The Page Without Its Imports
32
+
33
+ ## Purpose
34
+ A step of the profile of pages without imports: what a template needs from
35
+ other files is the part of its text that says least about the page itself.
36
+
37
+ ## What Is an Import
38
+ Only the tags of `HTML_TEMPLATE_IMPORT_TAGS`: `load` in Django, `import` and
39
+ `from` in Jinja. `{% extends %}` and `{% include %}` stay, as they say what
40
+ the page is made of.
41
+
42
+ ## By the Scan, Not by a Line Pattern
43
+ Tags are read by the scanner of templates, so a `{% load static %}` inside a
44
+ `{% comment %}` or a `{% verbatim %}` is never cut, and one on the same line
45
+ as other text (`{% load a %}{% load b %}`) is cut alone.
46
+
47
+ ## Removing Is Not Harmless
48
+ A template that uses `{% static 'x' %}` no longer says where `static` comes
49
+ from. That is exactly what the reader of a page without imports gives up, as
50
+ the reader of a program without its `import` lines does.
51
+ """
@@ -0,0 +1,60 @@
1
+ from simplibs.text import remove_spans
2
+ from simplibs.validate import validate_call
3
+ # Outers
4
+ from ...annotations import TemplateEngine
5
+ from .._helpers import scan_templates_checked
6
+
7
+
8
+ @validate_call
9
+ def strip_template_comments(text: str, *, engine: TemplateEngine) -> str:
10
+ """Remove the comments of a template: `{# ... #}` and `{% comment %} ... {% endcomment %}`.
11
+
12
+ Args:
13
+ text: The source.
14
+ engine: `"django"` (a `{# #}` stays on one line; `{% comment %}` exists) or `"jinja"` (a `{# #}`
15
+ may span lines; there is no comment tag).
16
+
17
+ Returns:
18
+ The text without template comments; a line that held only a comment disappears. A template that
19
+ looks damaged (a `{% comment %}` that is never closed) comes back unchanged, with a warning.
20
+ """
21
+
22
+ # 1. Read the constructs; a damaged template is left as it is
23
+ scan = scan_templates_checked(text, engine=engine)
24
+ if scan is None:
25
+ return text
26
+
27
+ # 2. Cut the comments
28
+ return remove_spans(text, scan.comments)
29
+
30
+
31
+ _DESIGN_NOTES = """
32
+ # strip_template_comments — The Notes of the Author
33
+
34
+ ## Purpose
35
+ The step of the profiles that clean templates. An HTML comment is sent to the
36
+ browser, a template comment never leaves the server: this step removes the
37
+ second kind, `strip_html_comments` the first. A profile uses both, in either
38
+ order.
39
+
40
+ ## Why `engine` Is Required
41
+ Whether a `{# ... #}` may span lines, and whether `{% comment %}` exists at all,
42
+ depends on the engine, and a page cannot say which one it is written for.
43
+ Guessing would cut text in one engine that is not a comment in the other.
44
+
45
+ ## No Gap Needed
46
+ A comment between two words (`a{# c #}b`) disappears from the output of the
47
+ engine without a trace, so cutting it changes nothing.
48
+
49
+ ## Whitespace Control
50
+ Jinja's `{#- ... -#}` also trims the whitespace around it in the output.
51
+ Removing the comment keeps that whitespace, so the rendered text can differ
52
+ by a blank; the structure of the template does not.
53
+
54
+ ## A Damaged Template
55
+ It is returned as it is, and the warning makes the extractor call the file
56
+ `partial`.
57
+
58
+ ## Idempotent
59
+ A second run finds nothing and returns the very same text.
60
+ """
@@ -0,0 +1,49 @@
1
+ from simplibs.text import Span
2
+ from simplibs.validate import validate_call
3
+ # Outers
4
+ from ...annotations import TemplateEngine
5
+ from .._helpers import scan_templates_checked
6
+
7
+
8
+ @validate_call
9
+ def template_comment_spans(text: str, *, engine: TemplateEngine) -> list[Span]:
10
+ """Find the comments of a template: `{# ... #}` and `{% comment %} ... {% endcomment %}`.
11
+
12
+ Args:
13
+ text: The source.
14
+ engine: `"django"` (a `{# #}` stays on one line; `{% comment %}` exists) or `"jinja"` (a `{# #}`
15
+ may span lines; there is no comment tag).
16
+
17
+ Returns:
18
+ A span for each comment, from the blanks in front of it to its end; an empty list for a template
19
+ that looks damaged (a `{% comment %}` that is never closed), with a warning.
20
+ """
21
+
22
+ # 1. Read the constructs; a damaged template is left as it is
23
+ scan = scan_templates_checked(text, engine=engine)
24
+ if scan is None:
25
+ return []
26
+
27
+ return list(scan.comments)
28
+
29
+
30
+ _DESIGN_NOTES = """
31
+ # template_comment_spans — Where the Template Comments Are
32
+
33
+ ## Purpose
34
+ The spans of the comments that the template engine drops and the browser never
35
+ sees, for anything that wants to look at them or cut them.
36
+
37
+ ## What Is a Comment
38
+ `{# ... #}` in both engines, and in Django `{% comment %} ... {% endcomment %}`
39
+ with an optional note. What stands inside is not read: a `{{ x }}` in a
40
+ comment is text of the comment.
41
+
42
+ ## What Is Not
43
+ A `{# ... #}` inside `{% verbatim %}` or `{% raw %}` is plain text for the
44
+ engine and is not reported. A `{#` whose `#}` is on a later line is, in
45
+ Django, only text.
46
+
47
+ ## Never Raises
48
+ A template that looks damaged is answered with no spans and a warning.
49
+ """
@@ -0,0 +1,62 @@
1
+ from simplibs.types import int_positive
2
+ from simplibs.validate import validate_call
3
+ # Outers
4
+ from ...annotations import TemplateEngine
5
+ from .._helpers import build_template_outline, scan_templates_checked, warn_problems
6
+
7
+
8
+ @validate_call
9
+ def template_outline(text: str, *, engine: TemplateEngine, max_depth: int_positive | None = None) -> str:
10
+ """Write the skeleton of a template: `extends`, `include`, the blocks, and in Jinja the macros.
11
+
12
+ Args:
13
+ text: The source.
14
+ engine: `"django"` or `"jinja"`.
15
+ max_depth: How many levels of blocks to list (`1` is the top level only); `None` lists all.
16
+
17
+ Returns:
18
+ One line for each of those tags, written as it stands in the template and indented by the depth of
19
+ its blocks; an empty string for a template with none. A template that looks damaged (a
20
+ `{% comment %}` that is never closed, blocks that do not pair up) comes back unchanged, with a warning.
21
+
22
+ Example:
23
+ template_outline("{% extends 'b.html' %}{% block a %}{% include 'c.html' %}{% endblock %}", engine="django")
24
+ # "{% extends 'b.html' %}\\n{% block a %}\\n {% include 'c.html' %}\\n"
25
+ """
26
+
27
+ # 1. Read the constructs; a damaged template is left as it is
28
+ scan = scan_templates_checked(text, engine=engine)
29
+ if scan is None:
30
+ return text
31
+
32
+ # 2. The skeleton; blocks that do not pair up mean the nesting cannot be trusted
33
+ lines, problems = build_template_outline(scan, engine, max_depth)
34
+ if problems:
35
+ warn_problems(problems)
36
+ return text
37
+
38
+ return "\n".join(lines) + "\n" if lines else ""
39
+
40
+
41
+ _DESIGN_NOTES = """
42
+ # template_outline — What the Template Is Built Of
43
+
44
+ ## Purpose
45
+ The step of the profile for templates: which template this one extends, what it
46
+ includes and which blocks it defines or fills in, in the order and nesting in
47
+ which they stand. A reader who has the outlines of all the templates of a
48
+ project sees how its pages are put together without reading a page.
49
+
50
+ ## Why `engine` Is Required
51
+ A Jinja `macro` is part of the skeleton and a Django one does not exist, and a
52
+ `{# #}` that spans lines is a comment only in Jinja. A page cannot say which
53
+ engine it is written for.
54
+
55
+ ## Tags in Comments
56
+ A tag inside `{% comment %}` or `{% verbatim %}` is not a tag of the template
57
+ and is not listed.
58
+
59
+ ## A Damaged Template
60
+ It is returned as it is, with a warning, so the extractor calls the file
61
+ `partial`.
62
+ """
@@ -0,0 +1,14 @@
1
+ from .register_all import register_all
2
+
3
+
4
+ _DESIGN_NOTES = """
5
+ # plugin — The Meeting Point With the Extractor
6
+
7
+ Plain imports, no `__all__`.
8
+
9
+ ## Internal Components Registry
10
+
11
+ | Component | Type | Description |
12
+ | :------------- | :------- | :--------------------------------------------------------------- |
13
+ | `register_all` | Function | Registers the languages and profiles; the target of the entry point. |
14
+ """
@@ -0,0 +1,60 @@
1
+ from simplibs.extractor import register_language, register_profile
2
+ from simplibs.validate import validate_call
3
+ # Outers
4
+ from ..constants import HTML_EXTRA_LANGUAGE_BY_SUFFIX
5
+ from ..profiles import HTML_PROFILES
6
+
7
+
8
+ @validate_call
9
+ def register_all(*, replace_existing: bool = True) -> list[str]:
10
+ """Register every profile and language of this library in the registries of the extractor.
11
+
12
+ The extractor calls it, with no arguments, for every installed add-on
13
+ (entry-point group `simplibs.extractor.plugins`).
14
+
15
+ Args:
16
+ replace_existing: Replace a profile or a language that already has the key (the default, so calling it twice changes nothing).
17
+
18
+ Returns:
19
+ The keys of the registered profiles (`"html/code-clean"`...).
20
+
21
+ Raises:
22
+ StateError: A profile key is taken and `replace_existing` is `False`.
23
+ ParamError: A language suffix is taken and `replace_existing` is `False`.
24
+ """
25
+
26
+ # 1. The languages of the other suffixes, so the output fences them properly
27
+ for suffix, language in HTML_EXTRA_LANGUAGE_BY_SUFFIX.items():
28
+ register_language(suffix, language, replace_existing=replace_existing)
29
+
30
+ # 2. Register each profile
31
+ keys: list[str] = []
32
+ for profile in HTML_PROFILES:
33
+ registered = register_profile(profile, replace_existing=replace_existing)
34
+ keys.append(registered.key)
35
+
36
+ return keys
37
+
38
+
39
+ _DESIGN_NOTES = """
40
+ # register_all — What the Entry Point Calls
41
+
42
+ ## Purpose
43
+ `pip install simplibs-extractor-html` is the whole installation: the entry
44
+ point `html = "simplibs.extractor_html:register_all"` makes the extractor find
45
+ this function, and the thirteen profiles appear in `--list-systems`.
46
+
47
+ ## The Rules of an Add-On Function
48
+ It takes no required argument, registers with `replace_existing=True`, only
49
+ registers (no scanning, no network, no printing) and uses only the public API
50
+ of the extractor (`Profile`, `register_profile`, `register_language`).
51
+
52
+ ## Languages
53
+ `.htm`, `.xhtml` and the suffixes of the Jinja family get their fence language
54
+ here. Without it the extractor would write their code blocks with no language
55
+ and a viewer would not colour them.
56
+
57
+ ## Order
58
+ Languages first: a profile that is registered is usable at once, and its
59
+ output should already be fenced correctly.
60
+ """
@@ -0,0 +1,43 @@
1
+ from .html_clean_steps import html_clean_steps
2
+ from .html_no_imports_steps import html_no_imports_steps
3
+ from .html_outline_steps import html_outline_steps
4
+ from .template_structure_steps import template_structure_steps
5
+
6
+
7
+ __all__ = [
8
+ "html_clean_steps",
9
+ "html_no_imports_steps",
10
+ "html_outline_steps",
11
+ "template_structure_steps",
12
+ ]
13
+
14
+
15
+ _DESIGN_NOTES = """
16
+ # simplibs.extractor_html.presets — Ready-Made Pipeline Steps
17
+
18
+ ## Purpose
19
+ The chains of actions that the profiles use. Each is a function of the template
20
+ engine (`None` for a page that is only HTML), because the chain is the same
21
+ for every engine and only the option of its actions differs:
22
+
23
+ from simplibs.extractor_html.presets import html_clean_steps
24
+ steps = html_clean_steps("django")
25
+
26
+ ## Domain Folder
27
+ `presets/` has its own `__all__` and is not re-exported from the package root.
28
+ It stands to the right of `actions` (it uses them) and to the left of `profiles`.
29
+
30
+ ## The Other Add-Ons
31
+ The presets for the code inside a page come from the add-ons of its languages
32
+ (`JS_CLEAN`, `CSS_PLAIN_CLEAN`...): this library depends on both, and a program
33
+ that wants other steps for the blocks calls `apply_to_html_blocks` itself.
34
+
35
+ ## Internal Components Registry
36
+
37
+ | Component | Type | Description |
38
+ | :------------------------ | :----- | :---------------------------------------------------------------- |
39
+ | `html_clean_steps` | Preset | Comments out, code inside cleaned, whitespace tidied. |
40
+ | `html_no_imports_steps` | Preset | The same, without imports and blank lines. |
41
+ | `html_outline_steps` | Preset | The landmarks, headings and ids of a page. |
42
+ | `template_structure_steps`| Preset | The `extends`, `include` and blocks of a template. |
43
+ """