justhtml 3.7.0__tar.gz → 3.8.0__tar.gz

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 (158) hide show
  1. {justhtml-3.7.0 → justhtml-3.8.0}/CHANGELOG.md +28 -0
  2. {justhtml-3.7.0 → justhtml-3.8.0}/PKG-INFO +1 -1
  3. {justhtml-3.7.0 → justhtml-3.8.0}/pyproject.toml +1 -1
  4. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/core/constants.py +2 -0
  5. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/parser/__init__.py +24 -37
  6. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/parser/engine.py +1008 -930
  7. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/parser/scanner.py +64 -10
  8. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/parser/stream.py +5 -12
  9. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/transforms/runtime.py +49 -3
  10. {justhtml-3.7.0 → justhtml-3.8.0}/test-summary.txt +21 -18
  11. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_parser_engine.py +254 -18
  12. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_parser_regressions.py +3 -3
  13. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_parser_tree.py +9 -0
  14. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_stream.py +37 -7
  15. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_transforms.py +186 -1
  16. {justhtml-3.7.0 → justhtml-3.8.0}/.github/copilot-instructions.md +0 -0
  17. {justhtml-3.7.0 → justhtml-3.8.0}/.github/workflows/ci.yml +0 -0
  18. {justhtml-3.7.0 → justhtml-3.8.0}/.github/workflows/publish.yml +0 -0
  19. {justhtml-3.7.0 → justhtml-3.8.0}/.gitignore +0 -0
  20. {justhtml-3.7.0 → justhtml-3.8.0}/.pre-commit-config.yaml +0 -0
  21. {justhtml-3.7.0 → justhtml-3.8.0}/AGENTS.md +0 -0
  22. {justhtml-3.7.0 → justhtml-3.8.0}/CODE_OF_CONDUCT.md +0 -0
  23. {justhtml-3.7.0 → justhtml-3.8.0}/CONTRIBUTING.md +0 -0
  24. {justhtml-3.7.0 → justhtml-3.8.0}/LICENSE +0 -0
  25. {justhtml-3.7.0 → justhtml-3.8.0}/Makefile +0 -0
  26. {justhtml-3.7.0 → justhtml-3.8.0}/PERFORMANCE.md +0 -0
  27. {justhtml-3.7.0 → justhtml-3.8.0}/README.md +0 -0
  28. {justhtml-3.7.0 → justhtml-3.8.0}/SECURITY.md +0 -0
  29. {justhtml-3.7.0 → justhtml-3.8.0}/assets/justhtml-readme-explainer.png +0 -0
  30. {justhtml-3.7.0 → justhtml-3.8.0}/benchmarks/chromium_parser_diff.py +0 -0
  31. {justhtml-3.7.0 → justhtml-3.8.0}/benchmarks/correctness.py +0 -0
  32. {justhtml-3.7.0 → justhtml-3.8.0}/benchmarks/fuzz.py +0 -0
  33. {justhtml-3.7.0 → justhtml-3.8.0}/benchmarks/html5lib_engine_diff.py +0 -0
  34. {justhtml-3.7.0 → justhtml-3.8.0}/benchmarks/parser_engine_gate.py +0 -0
  35. {justhtml-3.7.0 → justhtml-3.8.0}/benchmarks/performance.py +0 -0
  36. {justhtml-3.7.0 → justhtml-3.8.0}/benchmarks/profile.py +0 -0
  37. {justhtml-3.7.0 → justhtml-3.8.0}/docs/_config.yml +0 -0
  38. {justhtml-3.7.0 → justhtml-3.8.0}/docs/_layouts/default.html +0 -0
  39. {justhtml-3.7.0 → justhtml-3.8.0}/docs/api.md +0 -0
  40. {justhtml-3.7.0 → justhtml-3.8.0}/docs/assets/search.js +0 -0
  41. {justhtml-3.7.0 → justhtml-3.8.0}/docs/bleach-migration.md +0 -0
  42. {justhtml-3.7.0 → justhtml-3.8.0}/docs/building.md +0 -0
  43. {justhtml-3.7.0 → justhtml-3.8.0}/docs/cli.md +0 -0
  44. {justhtml-3.7.0 → justhtml-3.8.0}/docs/comparison.md +0 -0
  45. {justhtml-3.7.0 → justhtml-3.8.0}/docs/correctness.md +0 -0
  46. {justhtml-3.7.0 → justhtml-3.8.0}/docs/encoding.md +0 -0
  47. {justhtml-3.7.0 → justhtml-3.8.0}/docs/errors.md +0 -0
  48. {justhtml-3.7.0 → justhtml-3.8.0}/docs/fragments.md +0 -0
  49. {justhtml-3.7.0 → justhtml-3.8.0}/docs/html-cleaning.md +0 -0
  50. {justhtml-3.7.0 → justhtml-3.8.0}/docs/index.md +0 -0
  51. {justhtml-3.7.0 → justhtml-3.8.0}/docs/linkify.md +0 -0
  52. {justhtml-3.7.0 → justhtml-3.8.0}/docs/migration-examples.md +0 -0
  53. {justhtml-3.7.0 → justhtml-3.8.0}/docs/playground/app.js +0 -0
  54. {justhtml-3.7.0 → justhtml-3.8.0}/docs/playground/index.html +0 -0
  55. {justhtml-3.7.0 → justhtml-3.8.0}/docs/playground/py/__init__.py +0 -0
  56. {justhtml-3.7.0 → justhtml-3.8.0}/docs/playground/py/install_latest_justhtml.py +0 -0
  57. {justhtml-3.7.0 → justhtml-3.8.0}/docs/playground/py/render.py +0 -0
  58. {justhtml-3.7.0 → justhtml-3.8.0}/docs/playground/py/use_local_repo.py +0 -0
  59. {justhtml-3.7.0 → justhtml-3.8.0}/docs/quickstart.md +0 -0
  60. {justhtml-3.7.0 → justhtml-3.8.0}/docs/sanitization.md +0 -0
  61. {justhtml-3.7.0 → justhtml-3.8.0}/docs/selectors.md +0 -0
  62. {justhtml-3.7.0 → justhtml-3.8.0}/docs/streaming.md +0 -0
  63. {justhtml-3.7.0 → justhtml-3.8.0}/docs/text.md +0 -0
  64. {justhtml-3.7.0 → justhtml-3.8.0}/docs/transforms.md +0 -0
  65. {justhtml-3.7.0 → justhtml-3.8.0}/docs/unsafe-handling.md +0 -0
  66. {justhtml-3.7.0 → justhtml-3.8.0}/docs/url-cleaning.md +0 -0
  67. {justhtml-3.7.0 → justhtml-3.8.0}/llms.txt +0 -0
  68. {justhtml-3.7.0 → justhtml-3.8.0}/project_ideas.md +0 -0
  69. {justhtml-3.7.0 → justhtml-3.8.0}/run_tests.py +0 -0
  70. {justhtml-3.7.0 → justhtml-3.8.0}/scripts/release.py +0 -0
  71. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/__init__.py +0 -0
  72. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/__main__.py +0 -0
  73. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/core/__init__.py +0 -0
  74. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/core/doctype.py +0 -0
  75. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/core/entities.py +0 -0
  76. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/core/errors.py +0 -0
  77. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/core/rawtext.py +0 -0
  78. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/core/text.py +0 -0
  79. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/core/types.py +0 -0
  80. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/dom/__init__.py +0 -0
  81. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/dom/builder.py +0 -0
  82. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/parser/context.py +0 -0
  83. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/parser/encoding.py +0 -0
  84. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/parser/options.py +0 -0
  85. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/py.typed +0 -0
  86. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/__init__.py +0 -0
  87. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/css.py +0 -0
  88. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/dom.py +0 -0
  89. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/policy.py +0 -0
  90. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/policy_defaults.py +0 -0
  91. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/rawtext.py +0 -0
  92. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/url/__init__.py +0 -0
  93. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/url/policy.py +0 -0
  94. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/url/runtime.py +0 -0
  95. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/sanitizer/url/spec.py +0 -0
  96. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/selector/__init__.py +0 -0
  97. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/selector/core.py +0 -0
  98. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/serializer/__init__.py +0 -0
  99. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/serializer/html.py +0 -0
  100. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/serializer/markdown.py +0 -0
  101. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/transforms/__init__.py +0 -0
  102. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/transforms/compile.py +0 -0
  103. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/transforms/linkify.py +0 -0
  104. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/transforms/linkify_core.py +0 -0
  105. {justhtml-3.7.0 → justhtml-3.8.0}/src/justhtml/transforms/spec.py +0 -0
  106. {justhtml-3.7.0 → justhtml-3.8.0}/tests/README.md +0 -0
  107. {justhtml-3.7.0 → justhtml-3.8.0}/tests/__init__.py +0 -0
  108. {justhtml-3.7.0 → justhtml-3.8.0}/tests/data/wikipedia.html +0 -0
  109. {justhtml-3.7.0 → justhtml-3.8.0}/tests/harness/__init__.py +0 -0
  110. {justhtml-3.7.0 → justhtml-3.8.0}/tests/harness/encoding.py +0 -0
  111. {justhtml-3.7.0 → justhtml-3.8.0}/tests/harness/regressions.py +0 -0
  112. {justhtml-3.7.0 → justhtml-3.8.0}/tests/harness/reporter.py +0 -0
  113. {justhtml-3.7.0 → justhtml-3.8.0}/tests/harness/serializer.py +0 -0
  114. {justhtml-3.7.0 → justhtml-3.8.0}/tests/harness/tree.py +0 -0
  115. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-sanitize-tests/cases.json +0 -0
  116. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/character_preprocessing.dat +0 -0
  117. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/document_shell_recovery.dat +0 -0
  118. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/entities.test +0 -0
  119. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/foreign_content.dat +0 -0
  120. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/foreign_stack_recovery.dat +0 -0
  121. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/frameset_recovery.dat +0 -0
  122. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/head_noscript_recovery.dat +0 -0
  123. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/iframe_srcdoc.dat +0 -0
  124. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/in_body_recovery.dat +0 -0
  125. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/raw_text_recovery.dat +0 -0
  126. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/select_recovery.dat +0 -0
  127. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/selectedcontent.dat +0 -0
  128. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/table_recovery.dat +0 -0
  129. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/template_recovery.dat +0 -0
  130. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/tokenizer_recovery.dat +0 -0
  131. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/tokenizer_state_transitions.test +0 -0
  132. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/tokenizer_states.test +0 -0
  133. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/xml_coercion.dat +0 -0
  134. {justhtml-3.7.0 → justhtml-3.8.0}/tests/justhtml-tests/xml_coercion_invalid_characters.test +0 -0
  135. {justhtml-3.7.0 → justhtml-3.8.0}/tests/linkify-it/LICENSE.txt +0 -0
  136. {justhtml-3.7.0 → justhtml-3.8.0}/tests/linkify-it/README.md +0 -0
  137. {justhtml-3.7.0 → justhtml-3.8.0}/tests/linkify-it/fixtures/links.txt +0 -0
  138. {justhtml-3.7.0 → justhtml-3.8.0}/tests/linkify-it/fixtures/not_links.txt +0 -0
  139. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_builder.py +0 -0
  140. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_cli.py +0 -0
  141. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_docs_examples.py +0 -0
  142. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_encoding.py +0 -0
  143. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_entities.py +0 -0
  144. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_errors.py +0 -0
  145. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_linkify_internals.py +0 -0
  146. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_linkify_it.py +0 -0
  147. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_linkify_transform.py +0 -0
  148. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_node.py +0 -0
  149. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_playground_local_repo_file_list.py +0 -0
  150. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_sanitize.py +0 -0
  151. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_sanitize_integration.py +0 -0
  152. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_sanitize_transform.py +0 -0
  153. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_selector.py +0 -0
  154. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_serialize.py +0 -0
  155. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_transform_runtime.py +0 -0
  156. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_transforms_compiler.py +0 -0
  157. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_transforms_sanitize_integration.py +0 -0
  158. {justhtml-3.7.0 → justhtml-3.8.0}/tests/test_wikipedia.py +0 -0
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [3.8.0] - 2026-07-19
11
+
12
+ ### Performance
13
+
14
+ On the web100k parser benchmark (1,000 documents, three iterations), the default `JustHTML()` constructor now completes in 13.020 seconds rather than 21.648 seconds in 3.7.1: a 40% reduction in parse time, or about 1.7× higher throughput.
15
+
16
+ - Speed up the default `JustHTML()` constructor by compiling common start/end-tag and URL-sanitization decisions, and by folding only names that the parser encounters instead of copying and lowercasing the full input.
17
+ - Reduce tree-construction overhead by adaptively tracking open elements, removing completed formatting entries without leaving tombstones, and deferring formatting-attribute signatures until duplicate tracking actually needs them.
18
+ - Remove redundant constructor-path work for discarded end-tag attributes, canonical attribute names, shallow non-paragraph stack updates, active-formatting bookkeeping, and parser state that could never become active.
19
+ - Cut fixed default-constructor overhead by reusing built-in execution plans, skipping no-op finalization and error-list work, lazily allocating formatting indexes, and collapsing duplicate shell, mode, and end-of-input state.
20
+ - Collapse common start-tag and text insertion-mode checks into synchronized state flags, avoiding repeated condition ladders in the default `JustHTML()` constructor.
21
+ - Avoid open-elements membership scans for ordinary HTML insertions and batch large disallowed-wrapper projections instead of repeatedly searching and shifting the same child lists.
22
+ - Remove already-resolved sanitizer insertion, unknown-tag condition ladders, template-mode lookups, and wrapper-list copies from the default `JustHTML()` constructor path.
23
+
24
+ ### Security
25
+
26
+ - (Severity: Low) Keep sanitization projection linear for large runs of disallowed sibling wrappers and avoid depth-proportional parent validation for nested wrappers. Previously, crafted wrapper-heavy HTML could make the default `JustHTML()` constructor take quadratic time.
27
+
28
+ ## [3.7.1] - 2026-07-17
29
+
30
+ ### Fixed
31
+
32
+ - Make `CollapseWhitespace(...)` trim text at block-sibling and line-break boundaries, including `<pre>`, `<hr>`, and `<br>`.
33
+ - Preserve visible spacing around closed dialogs and hidden elements while ignoring comments, processing instructions, empty text nodes, and native non-rendering elements when locating rendered siblings.
34
+ - Collapse whitespace across adjacent text runs exposed by transforms such as `Unwrap(...)` and `DropComments(...)` without merging the text nodes.
35
+
8
36
  ## [3.7.0] - 2026-07-15
9
37
 
10
38
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: justhtml
3
- Version: 3.7.0
3
+ Version: 3.8.0
4
4
  Summary: A pure Python HTML5 parser that just works.
5
5
  Project-URL: Homepage, https://github.com/emilstenstrom/justhtml
6
6
  Project-URL: Issues, https://github.com/emilstenstrom/justhtml/issues
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "justhtml"
3
3
  authors = [{ name = "Emil Stenström", email = "emil@emilstenstrom.se" }]
4
- version = "3.7.0"
4
+ version = "3.8.0"
5
5
  description = "A pure Python HTML5 parser that just works."
6
6
  readme = "README.md"
7
7
  license = { file = "LICENSE" }
@@ -230,6 +230,7 @@ WHITESPACE_TRIMMING_BLOCK_ELEMENTS: Final[frozenset[str]] = frozenset(
230
230
  "header",
231
231
  "hgroup",
232
232
  "html",
233
+ "hr",
233
234
  "li",
234
235
  "listing",
235
236
  "main",
@@ -237,6 +238,7 @@ WHITESPACE_TRIMMING_BLOCK_ELEMENTS: Final[frozenset[str]] = frozenset(
237
238
  "nav",
238
239
  "ol",
239
240
  "p",
241
+ "pre",
240
242
  "search",
241
243
  "section",
242
244
  "summary",
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING, Any, cast
5
+ from typing import TYPE_CHECKING, Any
6
6
 
7
7
  from justhtml.core.types import ParseError
8
8
  from justhtml.dom import Document, DocumentFragment, Node, QueryMatch, Text
@@ -20,7 +20,6 @@ from .engine import (
20
20
  ParseEngine,
21
21
  can_compile_engine_plan,
22
22
  compile_default_engine_plan,
23
- compile_engine_plan,
24
23
  compile_raw_engine_plan,
25
24
  )
26
25
 
@@ -150,7 +149,9 @@ class JustHTML:
150
149
  self.encoding = None
151
150
 
152
151
  html_str: str
153
- if isinstance(html, (Node, Text)):
152
+ if type(html) is str:
153
+ html_str = html
154
+ elif isinstance(html, (Node, Text)):
154
155
  html_for_serialization = html
155
156
  if sanitize_enabled or has_sanitize_transform or has_harden_rawtext_transform:
156
157
  from justhtml.sanitizer import ( # noqa: PLC0415
@@ -179,40 +180,28 @@ class JustHTML:
179
180
  # Node location tracking is opt-in to avoid slowing down the common case.
180
181
  should_collect = collect_errors or strict
181
182
 
182
- engine_policy: SanitizationPolicy | None = None
183
- if sanitize_enabled and not transforms:
184
- if policy is not None:
185
- engine_policy = policy
186
- else:
187
- from justhtml.sanitizer import DEFAULT_DOCUMENT_POLICY, DEFAULT_POLICY # noqa: PLC0415
188
-
189
- engine_policy = DEFAULT_POLICY if fragment else DEFAULT_DOCUMENT_POLICY
190
-
191
- xml_coercion = False
192
- emit_bogus_markup_as_text = False
193
- discard_bom = bool(getattr(_parser_opts, "discard_bom", True))
194
- if discard_bom and html_str.startswith("\ufeff"):
195
- html_str = html_str[1:]
196
- if _parser_opts is not None:
197
- xml_coercion = bool(getattr(_parser_opts, "xml_coercion", False))
198
- emit_bogus_markup_as_text = bool(getattr(_parser_opts, "emit_bogus_markup_as_text", False))
183
+ if _parser_opts is None:
184
+ discard_bom = True
185
+ xml_coercion = False
186
+ emit_bogus_markup_as_text = False
187
+ else:
188
+ discard_bom = _parser_opts.discard_bom
189
+ xml_coercion = _parser_opts.xml_coercion
190
+ emit_bogus_markup_as_text = _parser_opts.emit_bogus_markup_as_text
199
191
  if emit_bogus_markup_as_text:
200
192
  track_tag_spans = True
193
+ if discard_bom and html_str.startswith("\ufeff"):
194
+ html_str = html_str[1:]
201
195
 
202
196
  use_compiled_safe_engine = (
203
- engine_policy is not None and not transforms and can_compile_engine_plan(engine_policy, fragment=fragment)
197
+ sanitize_enabled
198
+ and not transforms
199
+ and (policy is None or can_compile_engine_plan(policy, fragment=fragment))
204
200
  )
205
201
  if use_compiled_safe_engine:
206
- if policy is None:
207
- engine_plan = compile_default_engine_plan(fragment=fragment, scripting_enabled=scripting_enabled)
208
- else:
209
- engine_plan = compile_engine_plan(
210
- policy=cast("SanitizationPolicy", engine_policy),
211
- fragment=fragment,
212
- scripting_enabled=scripting_enabled,
213
- )
202
+ engine_plan = compile_default_engine_plan(fragment, scripting_enabled)
214
203
  else:
215
- engine_plan = compile_raw_engine_plan(fragment=fragment, scripting_enabled=scripting_enabled)
204
+ engine_plan = compile_raw_engine_plan(fragment, scripting_enabled)
216
205
 
217
206
  engine = ParseEngine(
218
207
  html_str,
@@ -223,14 +212,14 @@ class JustHTML:
223
212
  collect_errors=should_collect,
224
213
  max_errors=max_errors,
225
214
  iframe_srcdoc=iframe_srcdoc,
226
- track_node_locations=bool(track_node_locations),
227
- track_tag_spans=bool(track_node_locations) or track_tag_spans,
215
+ track_node_locations=track_node_locations,
216
+ track_tag_spans=track_node_locations or track_tag_spans,
228
217
  emit_bogus_markup_as_text=emit_bogus_markup_as_text,
229
218
  xml_coercion=xml_coercion,
230
219
  )
231
220
  self.root = engine.parse()
232
221
 
233
- transform_errors: list[ParseError] = _BoundedErrorList(max_errors - len(engine.errors))
222
+ errors = engine.errors
234
223
  if not use_compiled_safe_engine:
235
224
  transform_errors = self._apply_constructor_transforms(
236
225
  transforms=transforms,
@@ -239,11 +228,9 @@ class JustHTML:
239
228
  has_sanitize_transform=has_sanitize_transform,
240
229
  max_errors=max_errors - len(engine.errors),
241
230
  )
231
+ errors = engine.errors + transform_errors if should_collect else transform_errors
242
232
 
243
- if should_collect:
244
- self.errors = self._sorted_errors(engine.errors + transform_errors)
245
- else:
246
- self.errors = transform_errors
233
+ self.errors = self._sorted_errors(errors) if should_collect else errors
247
234
  if strict and self.errors:
248
235
  raise StrictModeError(self.errors[0])
249
236