diff --git a/stubs/html5lib/@tests/stubtest_allowlist.txt b/stubs/html5lib/@tests/stubtest_allowlist.txt
new file mode 100644
index 000000000000..7d9f867632d2
--- /dev/null
+++ b/stubs/html5lib/@tests/stubtest_allowlist.txt
@@ -0,0 +1,6 @@
+# Side effects from module initialization:
+html5lib.serializer.k
+html5lib.serializer.v
+html5lib.treeadapters.sax.prefix
+html5lib.treeadapters.sax.localName
+html5lib.treeadapters.sax.namespace
diff --git a/stubs/html5lib/html5lib/serializer.pyi b/stubs/html5lib/html5lib/serializer.pyi
index e7232e2e1d11..e831607ff02d 100644
--- a/stubs/html5lib/html5lib/serializer.pyi
+++ b/stubs/html5lib/html5lib/serializer.pyi
@@ -1,20 +1,53 @@
from _typeshed import Incomplete
from collections.abc import Generator
-from typing import overload
+from typing import Literal, overload
-k: str
-v: str | int
-
-def htmlentityreplace_errors(exc: Exception) -> tuple[str | bytes, int]: ...
-@overload
-def serialize(input, tree: str = "etree", encoding: None = None, **serializer_opts) -> str: ...
+def htmlentityreplace_errors(exc: UnicodeError) -> tuple[str | bytes, int]: ...
@overload
-def serialize(input, tree: str, encoding: str, **serializer_opts) -> bytes: ...
+def serialize(
+ input,
+ tree: Literal["dom", "genshi", "lxml", "etree"] = "etree",
+ encoding: Literal[""] | None = None,
+ *,
+ quote_attr_values: Literal["legacy", "spec", "always"] = "legacy",
+ quote_char: str = '"',
+ use_best_quote_char: bool = ..., # default value depends on whether quote_char was passed
+ omit_optional_tags: bool = True,
+ minimize_boolean_attributes: bool = True,
+ use_trailing_solidus: bool = False,
+ space_before_trailing_solidus: bool = True,
+ escape_lt_in_attrs: bool = False,
+ escape_rcdata: bool = False,
+ resolve_entities: bool = True,
+ alphabetical_attributes: bool = False,
+ inject_meta_charset: bool = True,
+ strip_whitespace: bool = False,
+ sanitize: bool = False,
+) -> str: ...
@overload
-def serialize(input, *, encoding: str, **serializer_opts) -> bytes: ...
+def serialize(
+ input,
+ tree: Literal["dom", "genshi", "lxml", "etree"] = "etree",
+ encoding: str = ...,
+ *,
+ quote_attr_values: Literal["legacy", "spec", "always"] = "legacy",
+ quote_char: str = '"',
+ use_best_quote_char: bool = ..., # default value depends on whether quote_char was passed
+ omit_optional_tags: bool = True,
+ minimize_boolean_attributes: bool = True,
+ use_trailing_solidus: bool = False,
+ space_before_trailing_solidus: bool = True,
+ escape_lt_in_attrs: bool = False,
+ escape_rcdata: bool = False,
+ resolve_entities: bool = True,
+ alphabetical_attributes: bool = False,
+ inject_meta_charset: bool = True,
+ strip_whitespace: bool = False,
+ sanitize: bool = False,
+) -> bytes: ...
class HTMLSerializer:
- quote_attr_values: str
+ quote_attr_values: Literal["legacy", "spec", "always"]
quote_char: str
use_best_quote_char: bool
omit_optional_tags: bool
@@ -28,15 +61,38 @@ class HTMLSerializer:
inject_meta_charset: bool
strip_whitespace: bool
sanitize: bool
- options: Incomplete
- errors: Incomplete
+ options: tuple[str, ...]
+ errors: list[Incomplete]
strict: bool
- def __init__(self, **kwargs) -> None: ...
- def encode(self, string): ...
- def encodeStrict(self, string): ...
- encoding: Incomplete
- def serialize(self, treewalker, encoding=None) -> Generator[Incomplete]: ...
- def render(self, treewalker, encoding=None): ...
- def serializeError(self, data: str = "XXX ERROR MESSAGE NEEDED") -> None: ...
+ def __init__(
+ self,
+ *,
+ quote_attr_values: Literal["legacy", "spec", "always"] = "legacy",
+ quote_char: str = '"',
+ use_best_quote_char: bool = ..., # default value depends on whether quote_char was passed
+ omit_optional_tags: bool = True,
+ minimize_boolean_attributes: bool = True,
+ use_trailing_solidus: bool = False,
+ space_before_trailing_solidus: bool = True,
+ escape_lt_in_attrs: bool = False,
+ escape_rcdata: bool = False,
+ resolve_entities: bool = True,
+ alphabetical_attributes: bool = False,
+ inject_meta_charset: bool = True,
+ strip_whitespace: bool = False,
+ sanitize: bool = False,
+ ) -> None: ...
+ def encode(self, string: str) -> str | bytes: ... # result depends on self.encoding
+ def encodeStrict(self, string: str) -> str | bytes: ... # result depends on self.encoding
+ encoding: str | None
+ @overload
+ def serialize(self, treewalker, encoding: Literal[""] | None = None) -> Generator[str]: ...
+ @overload
+ def serialize(self, treewalker, encoding: str = ...) -> Generator[bytes]: ...
+ @overload
+ def render(self, treewalker, encoding: Literal[""] | None = None) -> str: ...
+ @overload
+ def render(self, treewalker, encoding: str = ...) -> bytes: ...
+ def serializeError(self, data="XXX ERROR MESSAGE NEEDED") -> None: ...
class SerializeError(Exception): ...
diff --git a/stubs/html5lib/html5lib/treeadapters/sax.pyi b/stubs/html5lib/html5lib/treeadapters/sax.pyi
index 3ac32ef19361..22c93013ce67 100644
--- a/stubs/html5lib/html5lib/treeadapters/sax.pyi
+++ b/stubs/html5lib/html5lib/treeadapters/sax.pyi
@@ -1,6 +1,3 @@
-prefix: str | None
-localName: str
-namespace: str
prefix_mapping: dict[str, str]
def to_sax(walker, handler) -> None: ...