diff --git a/pyrightconfig.stricter.json b/pyrightconfig.stricter.json
index 18439b1b27cd..ac621b5f7375 100644
--- a/pyrightconfig.stricter.json
+++ b/pyrightconfig.stricter.json
@@ -23,11 +23,6 @@
         "stdlib/tkinter/scrolledtext.pyi",
         "stdlib/tkinter/tix.pyi",
         "stdlib/tkinter/ttk.pyi",
-        "stdlib/xml/dom/NodeFilter.pyi",
-        "stdlib/xml/dom/expatbuilder.pyi",
-        "stdlib/xml/dom/minidom.pyi",
-        "stdlib/xml/dom/pulldom.pyi",
-        "stdlib/xml/sax",
         "stubs/aiofiles/aiofiles/tempfile/temptypes.pyi",
         "stubs/antlr4-python3-runtime",
         "stubs/Authlib",
diff --git a/stdlib/xml/dom/NodeFilter.pyi b/stdlib/xml/dom/NodeFilter.pyi
index 80fb73d23433..007df982e06a 100644
--- a/stdlib/xml/dom/NodeFilter.pyi
+++ b/stdlib/xml/dom/NodeFilter.pyi
@@ -1,7 +1,10 @@
+from typing import Literal
+from xml.dom.minidom import Node
+
 class NodeFilter:
-    FILTER_ACCEPT: int
-    FILTER_REJECT: int
-    FILTER_SKIP: int
+    FILTER_ACCEPT: Literal[1]
+    FILTER_REJECT: Literal[2]
+    FILTER_SKIP: Literal[3]
 
     SHOW_ALL: int
     SHOW_ELEMENT: int
@@ -16,4 +19,4 @@ class NodeFilter:
     SHOW_DOCUMENT_TYPE: int
     SHOW_DOCUMENT_FRAGMENT: int
     SHOW_NOTATION: int
-    def acceptNode(self, node) -> int: ...
+    def acceptNode(self, node: Node) -> int: ...
diff --git a/stdlib/xml/dom/__init__.pyi b/stdlib/xml/dom/__init__.pyi
index 8738015638a9..d9615f9aacfe 100644
--- a/stdlib/xml/dom/__init__.pyi
+++ b/stdlib/xml/dom/__init__.pyi
@@ -1,69 +1,100 @@
-from typing import Any, Final
+from typing import Any, Final, Literal
 
 from .domreg import getDOMImplementation as getDOMImplementation, registerDOMImplementation as registerDOMImplementation
 
 class Node:
-    ELEMENT_NODE: int
-    ATTRIBUTE_NODE: int
-    TEXT_NODE: int
-    CDATA_SECTION_NODE: int
-    ENTITY_REFERENCE_NODE: int
-    ENTITY_NODE: int
-    PROCESSING_INSTRUCTION_NODE: int
-    COMMENT_NODE: int
-    DOCUMENT_NODE: int
-    DOCUMENT_TYPE_NODE: int
-    DOCUMENT_FRAGMENT_NODE: int
-    NOTATION_NODE: int
+    ELEMENT_NODE: Literal[1]
+    ATTRIBUTE_NODE: Literal[2]
+    TEXT_NODE: Literal[3]
+    CDATA_SECTION_NODE: Literal[4]
+    ENTITY_REFERENCE_NODE: Literal[5]
+    ENTITY_NODE: Literal[6]
+    PROCESSING_INSTRUCTION_NODE: Literal[7]
+    COMMENT_NODE: Literal[8]
+    DOCUMENT_NODE: Literal[9]
+    DOCUMENT_TYPE_NODE: Literal[10]
+    DOCUMENT_FRAGMENT_NODE: Literal[11]
+    NOTATION_NODE: Literal[12]
 
 # ExceptionCode
-INDEX_SIZE_ERR: Final[int]
-DOMSTRING_SIZE_ERR: Final[int]
-HIERARCHY_REQUEST_ERR: Final[int]
-WRONG_DOCUMENT_ERR: Final[int]
-INVALID_CHARACTER_ERR: Final[int]
-NO_DATA_ALLOWED_ERR: Final[int]
-NO_MODIFICATION_ALLOWED_ERR: Final[int]
-NOT_FOUND_ERR: Final[int]
-NOT_SUPPORTED_ERR: Final[int]
-INUSE_ATTRIBUTE_ERR: Final[int]
-INVALID_STATE_ERR: Final[int]
-SYNTAX_ERR: Final[int]
-INVALID_MODIFICATION_ERR: Final[int]
-NAMESPACE_ERR: Final[int]
-INVALID_ACCESS_ERR: Final[int]
-VALIDATION_ERR: Final[int]
+INDEX_SIZE_ERR: Final = 1
+DOMSTRING_SIZE_ERR: Final = 2
+HIERARCHY_REQUEST_ERR: Final = 3
+WRONG_DOCUMENT_ERR: Final = 4
+INVALID_CHARACTER_ERR: Final = 5
+NO_DATA_ALLOWED_ERR: Final = 6
+NO_MODIFICATION_ALLOWED_ERR: Final = 7
+NOT_FOUND_ERR: Final = 8
+NOT_SUPPORTED_ERR: Final = 9
+INUSE_ATTRIBUTE_ERR: Final = 10
+INVALID_STATE_ERR: Final = 11
+SYNTAX_ERR: Final = 12
+INVALID_MODIFICATION_ERR: Final = 13
+NAMESPACE_ERR: Final = 14
+INVALID_ACCESS_ERR: Final = 15
+VALIDATION_ERR: Final = 16
 
 class DOMException(Exception):
     code: int
     def __init__(self, *args: Any, **kw: Any) -> None: ...
     def _get_code(self) -> int: ...
 
-class IndexSizeErr(DOMException): ...
-class DomstringSizeErr(DOMException): ...
-class HierarchyRequestErr(DOMException): ...
-class WrongDocumentErr(DOMException): ...
-class InvalidCharacterErr(DOMException): ...
-class NoDataAllowedErr(DOMException): ...
-class NoModificationAllowedErr(DOMException): ...
-class NotFoundErr(DOMException): ...
-class NotSupportedErr(DOMException): ...
-class InuseAttributeErr(DOMException): ...
-class InvalidStateErr(DOMException): ...
-class SyntaxErr(DOMException): ...
-class InvalidModificationErr(DOMException): ...
-class NamespaceErr(DOMException): ...
-class InvalidAccessErr(DOMException): ...
-class ValidationErr(DOMException): ...
+class IndexSizeErr(DOMException):
+    code: Literal[1]
+
+class DomstringSizeErr(DOMException):
+    code: Literal[2]
+
+class HierarchyRequestErr(DOMException):
+    code: Literal[3]
+
+class WrongDocumentErr(DOMException):
+    code: Literal[4]
+
+class InvalidCharacterErr(DOMException):
+    code: Literal[5]
+
+class NoDataAllowedErr(DOMException):
+    code: Literal[6]
+
+class NoModificationAllowedErr(DOMException):
+    code: Literal[7]
+
+class NotFoundErr(DOMException):
+    code: Literal[8]
+
+class NotSupportedErr(DOMException):
+    code: Literal[9]
+
+class InuseAttributeErr(DOMException):
+    code: Literal[10]
+
+class InvalidStateErr(DOMException):
+    code: Literal[11]
+
+class SyntaxErr(DOMException):
+    code: Literal[12]
+
+class InvalidModificationErr(DOMException):
+    code: Literal[13]
+
+class NamespaceErr(DOMException):
+    code: Literal[14]
+
+class InvalidAccessErr(DOMException):
+    code: Literal[15]
+
+class ValidationErr(DOMException):
+    code: Literal[16]
 
 class UserDataHandler:
-    NODE_CLONED: int
-    NODE_IMPORTED: int
-    NODE_DELETED: int
-    NODE_RENAMED: int
-
-XML_NAMESPACE: Final[str]
-XMLNS_NAMESPACE: Final[str]
-XHTML_NAMESPACE: Final[str]
+    NODE_CLONED: Literal[1]
+    NODE_IMPORTED: Literal[2]
+    NODE_DELETED: Literal[3]
+    NODE_RENAMED: Literal[4]
+
+XML_NAMESPACE: Final = "http://www.w3.org/XML/1998/namespace"
+XMLNS_NAMESPACE: Final = "http://www.w3.org/2000/xmlns/"
+XHTML_NAMESPACE: Final = "http://www.w3.org/1999/xhtml"
 EMPTY_NAMESPACE: Final[None]
 EMPTY_PREFIX: Final[None]
diff --git a/stdlib/xml/dom/expatbuilder.pyi b/stdlib/xml/dom/expatbuilder.pyi
index 45f0af7aa979..228ad07e15ad 100644
--- a/stdlib/xml/dom/expatbuilder.pyi
+++ b/stdlib/xml/dom/expatbuilder.pyi
@@ -1,7 +1,11 @@
-from _typeshed import Incomplete, ReadableBuffer, SupportsRead
+from _typeshed import ReadableBuffer, SupportsRead
 from typing import Any, NoReturn
-from xml.dom.minidom import Document, DOMImplementation, Node, TypeInfo
+from typing_extensions import TypeAlias
+from xml.dom.minidom import Document, DocumentFragment, DOMImplementation, Element, Node, TypeInfo
 from xml.dom.xmlbuilder import DOMBuilderFilter, Options
+from xml.parsers.expat import XMLParserType
+
+_Model: TypeAlias = tuple[int, int, str | None, tuple[Any, ...]]  # same as in pyexpat
 
 TEXT_NODE = Node.TEXT_NODE
 CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE
@@ -10,45 +14,56 @@ FILTER_ACCEPT = DOMBuilderFilter.FILTER_ACCEPT
 FILTER_REJECT = DOMBuilderFilter.FILTER_REJECT
 FILTER_SKIP = DOMBuilderFilter.FILTER_SKIP
 FILTER_INTERRUPT = DOMBuilderFilter.FILTER_INTERRUPT
-theDOMImplementation: DOMImplementation | None
+theDOMImplementation: DOMImplementation
 
 class ElementInfo:
-    tagName: Incomplete
-    def __init__(self, tagName, model: Incomplete | None = None) -> None: ...
-    def getAttributeType(self, aname) -> TypeInfo: ...
-    def getAttributeTypeNS(self, namespaceURI, localName) -> TypeInfo: ...
+    tagName: str
+    def __init__(self, tagName: str, model: _Model | None = None) -> None: ...
+    def getAttributeType(self, aname: str) -> TypeInfo: ...
+    def getAttributeTypeNS(self, namespaceURI: str | None, localName: str) -> TypeInfo: ...
     def isElementContent(self) -> bool: ...
     def isEmpty(self) -> bool: ...
-    def isId(self, aname) -> bool: ...
-    def isIdNS(self, euri, ename, auri, aname) -> bool: ...
+    def isId(self, aname: str) -> bool: ...
+    def isIdNS(self, euri: str, ename: str, auri: str, aname: str) -> bool: ...
 
 class ExpatBuilder:
     document: Document  # Created in self.reset()
-    curNode: Incomplete  # Created in self.reset()
+    curNode: DocumentFragment | Element | Document  # Created in self.reset()
     def __init__(self, options: Options | None = None) -> None: ...
-    def createParser(self): ...
-    def getParser(self): ...
+    def createParser(self) -> XMLParserType: ...
+    def getParser(self) -> XMLParserType: ...
     def reset(self) -> None: ...
-    def install(self, parser) -> None: ...
+    def install(self, parser: XMLParserType) -> None: ...
     def parseFile(self, file: SupportsRead[ReadableBuffer | str]) -> Document: ...
     def parseString(self, string: str | ReadableBuffer) -> Document: ...
-    def start_doctype_decl_handler(self, doctypeName, systemId, publicId, has_internal_subset) -> None: ...
+    def start_doctype_decl_handler(
+        self, doctypeName: str, systemId: str | None, publicId: str | None, has_internal_subset: bool
+    ) -> None: ...
     def end_doctype_decl_handler(self) -> None: ...
-    def pi_handler(self, target, data) -> None: ...
-    def character_data_handler_cdata(self, data) -> None: ...
-    def character_data_handler(self, data) -> None: ...
+    def pi_handler(self, target: str, data: str) -> None: ...
+    def character_data_handler_cdata(self, data: str) -> None: ...
+    def character_data_handler(self, data: str) -> None: ...
     def start_cdata_section_handler(self) -> None: ...
     def end_cdata_section_handler(self) -> None: ...
-    def entity_decl_handler(self, entityName, is_parameter_entity, value, base, systemId, publicId, notationName) -> None: ...
-    def notation_decl_handler(self, notationName, base, systemId, publicId) -> None: ...
-    def comment_handler(self, data) -> None: ...
-    def external_entity_ref_handler(self, context, base, systemId, publicId) -> int: ...
-    def first_element_handler(self, name, attributes) -> None: ...
-    def start_element_handler(self, name, attributes) -> None: ...
-    def end_element_handler(self, name) -> None: ...
-    def element_decl_handler(self, name, model) -> None: ...
-    def attlist_decl_handler(self, elem, name, type, default, required) -> None: ...
-    def xml_decl_handler(self, version, encoding, standalone) -> None: ...
+    def entity_decl_handler(
+        self,
+        entityName: str,
+        is_parameter_entity: bool,
+        value: str | None,
+        base: str | None,
+        systemId: str,
+        publicId: str | None,
+        notationName: str | None,
+    ) -> None: ...
+    def notation_decl_handler(self, notationName: str, base: str | None, systemId: str, publicId: str | None) -> None: ...
+    def comment_handler(self, data: str) -> None: ...
+    def external_entity_ref_handler(self, context: str, base: str | None, systemId: str | None, publicId: str | None) -> int: ...
+    def first_element_handler(self, name: str, attributes: list[str]) -> None: ...
+    def start_element_handler(self, name: str, attributes: list[str]) -> None: ...
+    def end_element_handler(self, name: str) -> None: ...
+    def element_decl_handler(self, name: str, model: _Model) -> None: ...
+    def attlist_decl_handler(self, elem: str, name: str, type: str, default: str | None, required: bool) -> None: ...
+    def xml_decl_handler(self, version: str, encoding: str | None, standalone: int) -> None: ...
 
 class FilterVisibilityController:
     filter: DOMBuilderFilter
@@ -57,7 +72,7 @@ class FilterVisibilityController:
     def acceptNode(self, node: Node) -> int: ...
 
 class FilterCrutch:
-    def __init__(self, builder) -> None: ...
+    def __init__(self, builder: ExpatBuilder) -> None: ...
 
 class Rejecter(FilterCrutch):
     def start_element_handler(self, *args: Any) -> None: ...
@@ -68,33 +83,39 @@ class Skipper(FilterCrutch):
     def end_element_handler(self, *args: Any) -> None: ...
 
 class FragmentBuilder(ExpatBuilder):
-    fragment: Incomplete | None
-    originalDocument: Incomplete
-    context: Incomplete
-    def __init__(self, context, options: Options | None = None) -> None: ...
+    fragment: DocumentFragment | None
+    originalDocument: Document
+    context: Node
+    def __init__(self, context: Node, options: Options | None = None) -> None: ...
+    def reset(self) -> None: ...
+    def parseFile(self, file: SupportsRead[ReadableBuffer | str]) -> DocumentFragment: ...  # type: ignore[override]
+    def parseString(self, string: ReadableBuffer | str) -> DocumentFragment: ...  # type: ignore[override]
+    def external_entity_ref_handler(self, context: str, base: str | None, systemId: str | None, publicId: str | None) -> int: ...
 
 class Namespaces:
-    def createParser(self): ...
-    def install(self, parser) -> None: ...
-    def start_namespace_decl_handler(self, prefix, uri) -> None: ...
-    def start_element_handler(self, name, attributes) -> None: ...
-    def end_element_handler(self, name) -> None: ...
+    def createParser(self) -> XMLParserType: ...
+    def install(self, parser: XMLParserType) -> None: ...
+    def start_namespace_decl_handler(self, prefix: str | None, uri: str) -> None: ...
+    def start_element_handler(self, name: str, attributes: list[str]) -> None: ...
+    def end_element_handler(self, name: str) -> None: ...  # only exists if __debug__
 
 class ExpatBuilderNS(Namespaces, ExpatBuilder): ...
 class FragmentBuilderNS(Namespaces, FragmentBuilder): ...
 class ParseEscape(Exception): ...
 
 class InternalSubsetExtractor(ExpatBuilder):
-    subset: Any | None
-    def getSubset(self) -> Any | None: ...
+    subset: str | list[str] | None = None
+    def getSubset(self) -> str: ...
     def parseFile(self, file: SupportsRead[ReadableBuffer | str]) -> None: ...  # type: ignore[override]
     def parseString(self, string: str | ReadableBuffer) -> None: ...  # type: ignore[override]
-    def start_doctype_decl_handler(self, name, publicId, systemId, has_internal_subset) -> None: ...  # type: ignore[override]
+    def start_doctype_decl_handler(  # type: ignore[override]
+        self, name: str, publicId: str | None, systemId: str | None, has_internal_subset: bool
+    ) -> None: ...
     def end_doctype_decl_handler(self) -> NoReturn: ...
-    def start_element_handler(self, name, attrs) -> NoReturn: ...
+    def start_element_handler(self, name: str, attrs: list[str]) -> NoReturn: ...
 
-def parse(file: str | SupportsRead[ReadableBuffer | str], namespaces: bool = True): ...
-def parseString(string: str | ReadableBuffer, namespaces: bool = True): ...
-def parseFragment(file, context, namespaces: bool = True): ...
-def parseFragmentString(string: str, context, namespaces: bool = True): ...
+def parse(file: str | SupportsRead[ReadableBuffer | str], namespaces: bool = True) -> Document: ...
+def parseString(string: str | ReadableBuffer, namespaces: bool = True) -> Document: ...
+def parseFragment(file: str | SupportsRead[ReadableBuffer | str], context: Node, namespaces: bool = True) -> DocumentFragment: ...
+def parseFragmentString(string: str | ReadableBuffer, context: Node, namespaces: bool = True) -> DocumentFragment: ...
 def makeBuilder(options: Options) -> ExpatBuilderNS | ExpatBuilder: ...
diff --git a/stdlib/xml/dom/minidom.pyi b/stdlib/xml/dom/minidom.pyi
index d7da59a7ed4b..51bbf4993657 100644
--- a/stdlib/xml/dom/minidom.pyi
+++ b/stdlib/xml/dom/minidom.pyi
@@ -1,33 +1,92 @@
 import sys
 import xml.dom
+from _collections_abc import dict_keys, dict_values
 from _typeshed import Incomplete, ReadableBuffer, SupportsRead, SupportsWrite
-from typing import ClassVar, Literal, NoReturn, TypeVar, overload
-from typing_extensions import Self
-from xml.dom.minicompat import NodeList
+from collections.abc import Iterable, Sequence
+from types import TracebackType
+from typing import Any, ClassVar, Generic, Literal, NoReturn, Protocol, TypeVar, overload
+from typing_extensions import Self, TypeAlias
+from xml.dom.minicompat import EmptyNodeList, NodeList
 from xml.dom.xmlbuilder import DocumentLS, DOMImplementationLS
 from xml.sax.xmlreader import XMLReader
 
+_NSName: TypeAlias = tuple[str | None, str]
+
+# Entity can also have children, but it's not implemented the same way as the
+# others, so is deliberately omitted here.
+_NodesWithChildren: TypeAlias = DocumentFragment | Attr | Element | Document
+_NodesThatAreChildren: TypeAlias = CDATASection | Comment | DocumentType | Element | Notation | ProcessingInstruction | Text
+
+_AttrChildren: TypeAlias = Text  # Also EntityReference, but we don't implement it
+_ElementChildren: TypeAlias = Element | ProcessingInstruction | Comment | Text | CDATASection
+_EntityChildren: TypeAlias = Text  # I think; documentation is a little unclear
+_DocumentFragmentChildren: TypeAlias = Element | Text | CDATASection | ProcessingInstruction | Comment | Notation
+_DocumentChildren: TypeAlias = Comment | DocumentType | Element | ProcessingInstruction
+
 _N = TypeVar("_N", bound=Node)
+_ChildNodeVar = TypeVar("_ChildNodeVar", bound=_NodesThatAreChildren)
+_ChildNodePlusFragmentVar = TypeVar("_ChildNodePlusFragmentVar", bound=_NodesThatAreChildren | DocumentFragment)
+_DocumentChildrenVar = TypeVar("_DocumentChildrenVar", bound=_DocumentChildren)
+_ImportableNodeVar = TypeVar(
+    "_ImportableNodeVar",
+    bound=DocumentFragment
+    | Attr
+    | Element
+    | ProcessingInstruction
+    | CharacterData
+    | Text
+    | Comment
+    | CDATASection
+    | Entity
+    | Notation,
+)
+
+class _DOMErrorHandler(Protocol):
+    def handleError(self, error: Exception) -> bool: ...
+
+class _UserDataHandler(Protocol):
+    def handle(self, operation: int, key: str, data: Any, src: Node, dst: Node) -> None: ...
 
 def parse(
     file: str | SupportsRead[ReadableBuffer | str], parser: XMLReader | None = None, bufsize: int | None = None
 ) -> Document: ...
 def parseString(string: str | ReadableBuffer, parser: XMLReader | None = None) -> Document: ...
-def getDOMImplementation(features=None) -> DOMImplementation | None: ...
+@overload
+def getDOMImplementation(features: None = None) -> DOMImplementation: ...
+@overload
+def getDOMImplementation(features: str | Iterable[tuple[str, str | None]]) -> DOMImplementation | None: ...
 
 class Node(xml.dom.Node):
-    namespaceURI: str | None
-    parentNode: Incomplete
-    ownerDocument: Incomplete
-    nextSibling: Incomplete
-    previousSibling: Incomplete
-    prefix: Incomplete
+    parentNode: _NodesWithChildren | Entity | None
+    ownerDocument: Document | None
+    nextSibling: _NodesThatAreChildren | None
+    previousSibling: _NodesThatAreChildren | None
+    namespaceURI: str | None  # non-null only for Element and Attr
+    prefix: str | None  # non-null only for NS Element and Attr
+
+    # These aren't defined on Node, but they exist on all Node subclasses
+    # and various methods of Node require them to exist.
+    childNodes: (
+        NodeList[_DocumentFragmentChildren]
+        | NodeList[_AttrChildren]
+        | NodeList[_ElementChildren]
+        | NodeList[_DocumentChildren]
+        | NodeList[_EntityChildren]
+        | EmptyNodeList
+    )
+    nodeType: ClassVar[Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]]
+    nodeName: str | None  # only possibly None on DocumentType
+
+    # Not defined on Node, but exist on all Node subclasses.
+    nodeValue: str | None  # non-null for Attr, ProcessingInstruction, Text, Comment, and CDATASection
+    attributes: NamedNodeMap | None  # non-null only for Element
+
     @property
-    def firstChild(self) -> Node | None: ...
+    def firstChild(self) -> _NodesThatAreChildren | None: ...
     @property
-    def lastChild(self) -> Node | None: ...
+    def lastChild(self) -> _NodesThatAreChildren | None: ...
     @property
-    def localName(self) -> str | None: ...
+    def localName(self) -> str | None: ...  # non-null only for Element and Attr
     def __bool__(self) -> Literal[True]: ...
     if sys.version_info >= (3, 9):
         @overload
@@ -95,62 +154,125 @@ class Node(xml.dom.Node):
         ) -> bytes: ...
 
     def hasChildNodes(self) -> bool: ...
-    def insertBefore(self, newChild, refChild): ...
-    def appendChild(self, node: _N) -> _N: ...
-    def replaceChild(self, newChild, oldChild): ...
-    def removeChild(self, oldChild): ...
-    def normalize(self) -> None: ...
-    def cloneNode(self, deep): ...
-    def isSupported(self, feature, version): ...
-    def isSameNode(self, other): ...
-    def getInterface(self, feature): ...
-    def getUserData(self, key): ...
-    def setUserData(self, key, data, handler): ...
-    childNodes: Incomplete
+    def insertBefore(  # type: ignore[misc]
+        self: _NodesWithChildren,  # pyright: ignore[reportGeneralTypeIssues]
+        newChild: _ChildNodePlusFragmentVar,
+        refChild: _NodesThatAreChildren | None,
+    ) -> _ChildNodePlusFragmentVar: ...
+    def appendChild(  # type: ignore[misc]
+        self: _NodesWithChildren, node: _ChildNodePlusFragmentVar  # pyright: ignore[reportGeneralTypeIssues]
+    ) -> _ChildNodePlusFragmentVar: ...
+    @overload
+    def replaceChild(  # type: ignore[misc]
+        self: _NodesWithChildren, newChild: DocumentFragment, oldChild: _ChildNodeVar
+    ) -> _ChildNodeVar | DocumentFragment: ...
+    @overload
+    def replaceChild(  # type: ignore[misc]
+        self: _NodesWithChildren, newChild: _NodesThatAreChildren, oldChild: _ChildNodeVar
+    ) -> _ChildNodeVar | None: ...
+    def removeChild(self: _NodesWithChildren, oldChild: _ChildNodeVar) -> _ChildNodeVar: ...  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
+    def normalize(self: _NodesWithChildren) -> None: ...  # type: ignore[misc]  # pyright: ignore[reportGeneralTypeIssues]
+    def cloneNode(self, deep: bool) -> Self | None: ...
+    def isSupported(self, feature: str, version: str | None) -> bool: ...
+    def isSameNode(self, other: Node) -> bool: ...
+    def getInterface(self, feature: str) -> Self | None: ...
+    def getUserData(self, key: str) -> Any | None: ...
+    def setUserData(self, key: str, data: Any, handler: _UserDataHandler) -> Any: ...
     def unlink(self) -> None: ...
     def __enter__(self) -> Self: ...
-    def __exit__(self, et, ev, tb) -> None: ...
+    def __exit__(self, et: type[BaseException] | None, ev: BaseException | None, tb: TracebackType | None) -> None: ...
+
+_DFChildrenVar = TypeVar("_DFChildrenVar", bound=_DocumentFragmentChildren)
+_DFChildrenPlusFragment = TypeVar("_DFChildrenPlusFragment", bound=_DocumentFragmentChildren | DocumentFragment)
 
 class DocumentFragment(Node):
-    nodeType: int
-    nodeName: str
-    nodeValue: Incomplete
-    attributes: Incomplete
-    parentNode: Incomplete
-    childNodes: Incomplete
+    nodeType: ClassVar[Literal[11]]
+    nodeName: Literal["#document-fragment"]
+    nodeValue: None
+    attributes: None
+
+    parentNode: None
+    nextSibling: None
+    previousSibling: None
+    childNodes: NodeList[_DocumentFragmentChildren]
+    @property
+    def firstChild(self) -> _DocumentFragmentChildren | None: ...
+    @property
+    def lastChild(self) -> _DocumentFragmentChildren | None: ...
+
+    namespaceURI: None
+    prefix: None
+    @property
+    def localName(self) -> None: ...
     def __init__(self) -> None: ...
+    def insertBefore(  # type: ignore[override]
+        self, newChild: _DFChildrenPlusFragment, refChild: _DocumentFragmentChildren | None
+    ) -> _DFChildrenPlusFragment: ...
+    def appendChild(self, node: _DFChildrenPlusFragment) -> _DFChildrenPlusFragment: ...  # type: ignore[override]
+    @overload  # type: ignore[override]
+    def replaceChild(self, newChild: DocumentFragment, oldChild: _DFChildrenVar) -> _DFChildrenVar | DocumentFragment: ...
+    @overload
+    def replaceChild(self, newChild: _DocumentFragmentChildren, oldChild: _DFChildrenVar) -> _DFChildrenVar | None: ...  # type: ignore[override]
+    def removeChild(self, oldChild: _DFChildrenVar) -> _DFChildrenVar: ...  # type: ignore[override]
+
+_AttrChildrenVar = TypeVar("_AttrChildrenVar", bound=_AttrChildren)
+_AttrChildrenPlusFragment = TypeVar("_AttrChildrenPlusFragment", bound=_AttrChildren | DocumentFragment)
 
 class Attr(Node):
-    name: str
-    nodeType: int
-    attributes: Incomplete
-    specified: bool
-    ownerElement: Incomplete
+    nodeType: ClassVar[Literal[2]]
+    nodeName: str  # same as Attr.name
+    nodeValue: str  # same as Attr.value
+    attributes: None
+
+    parentNode: None
+    nextSibling: None
+    previousSibling: None
+    childNodes: NodeList[_AttrChildren]
+    @property
+    def firstChild(self) -> _AttrChildren | None: ...
+    @property
+    def lastChild(self) -> _AttrChildren | None: ...
+
     namespaceURI: str | None
-    childNodes: Incomplete
-    nodeName: Incomplete
-    nodeValue: str
+    prefix: str | None
+    @property
+    def localName(self) -> str: ...
+
+    name: str
     value: str
-    prefix: Incomplete
+    specified: bool
+    ownerElement: Element | None
+
     def __init__(
-        self, qName: str, namespaceURI: str | None = None, localName: str | None = None, prefix: Incomplete | None = None
+        self, qName: str, namespaceURI: str | None = None, localName: str | None = None, prefix: str | None = None
     ) -> None: ...
     def unlink(self) -> None: ...
     @property
     def isId(self) -> bool: ...
     @property
-    def schemaType(self): ...
+    def schemaType(self) -> TypeInfo: ...
+    def insertBefore(self, newChild: _AttrChildrenPlusFragment, refChild: _AttrChildren | None) -> _AttrChildrenPlusFragment: ...  # type: ignore[override]
+    def appendChild(self, node: _AttrChildrenPlusFragment) -> _AttrChildrenPlusFragment: ...  # type: ignore[override]
+    @overload  # type: ignore[override]
+    def replaceChild(self, newChild: DocumentFragment, oldChild: _AttrChildrenVar) -> _AttrChildrenVar | DocumentFragment: ...
+    @overload
+    def replaceChild(self, newChild: _AttrChildren, oldChild: _AttrChildrenVar) -> _AttrChildrenVar | None: ...  # type: ignore[override]
+    def removeChild(self, oldChild: _AttrChildrenVar) -> _AttrChildrenVar: ...  # type: ignore[override]
 
+# In the DOM, this interface isn't specific to Attr, but our implementation is
+# because that's the only place we use it.
 class NamedNodeMap:
-    def __init__(self, attrs, attrsNS, ownerElement) -> None: ...
-    def item(self, index): ...
-    def items(self): ...
-    def itemsNS(self): ...
-    def __contains__(self, key): ...
-    def keys(self): ...
-    def keysNS(self): ...
-    def values(self): ...
-    def get(self, name: str, value: Incomplete | None = None): ...
+    def __init__(self, attrs: dict[str, Attr], attrsNS: dict[_NSName, Attr], ownerElement: Element) -> None: ...
+    @property
+    def length(self) -> int: ...
+    def item(self, index: int) -> Node | None: ...
+    def items(self) -> list[tuple[str, str]]: ...
+    def itemsNS(self) -> list[tuple[_NSName, str]]: ...
+    def __contains__(self, key: str | _NSName) -> bool: ...
+    def keys(self) -> dict_keys[str, Attr]: ...
+    def keysNS(self) -> dict_keys[_NSName, Attr]: ...
+    def values(self) -> dict_values[str, Attr]: ...
+    def get(self, name: str, value: Attr | None = None) -> Attr | None: ...
     __hash__: ClassVar[None]  # type: ignore[assignment]
     def __len__(self) -> int: ...
     def __eq__(self, other: object) -> bool: ...
@@ -158,135 +280,227 @@ class NamedNodeMap:
     def __gt__(self, other: NamedNodeMap) -> bool: ...
     def __le__(self, other: NamedNodeMap) -> bool: ...
     def __lt__(self, other: NamedNodeMap) -> bool: ...
-    def __getitem__(self, attname_or_tuple: tuple[str, str | None] | str): ...
+    def __getitem__(self, attname_or_tuple: _NSName | str) -> Attr: ...
     def __setitem__(self, attname: str, value: Attr | str) -> None: ...
     def getNamedItem(self, name: str) -> Attr | None: ...
-    def getNamedItemNS(self, namespaceURI: str, localName: str | None) -> Attr | None: ...
+    def getNamedItemNS(self, namespaceURI: str | None, localName: str) -> Attr | None: ...
     def removeNamedItem(self, name: str) -> Attr: ...
-    def removeNamedItemNS(self, namespaceURI: str, localName: str | None): ...
-    def setNamedItem(self, node: Attr) -> Attr: ...
-    def setNamedItemNS(self, node: Attr) -> Attr: ...
-    def __delitem__(self, attname_or_tuple: tuple[str, str | None] | str) -> None: ...
-    @property
-    def length(self) -> int: ...
+    def removeNamedItemNS(self, namespaceURI: str | None, localName: str) -> Attr: ...
+    def setNamedItem(self, node: Attr) -> Attr | None: ...
+    def setNamedItemNS(self, node: Attr) -> Attr | None: ...
+    def __delitem__(self, attname_or_tuple: _NSName | str) -> None: ...
 
 AttributeList = NamedNodeMap
 
 class TypeInfo:
-    namespace: Incomplete | None
-    name: str
-    def __init__(self, namespace: Incomplete | None, name: str) -> None: ...
+    namespace: str | None
+    name: str | None
+    def __init__(self, namespace: Incomplete | None, name: str | None) -> None: ...
+
+_ElementChildrenVar = TypeVar("_ElementChildrenVar", bound=_ElementChildren)
+_ElementChildrenPlusFragment = TypeVar("_ElementChildrenPlusFragment", bound=_ElementChildren | DocumentFragment)
 
 class Element(Node):
-    nodeType: int
-    nodeValue: Incomplete
-    schemaType: Incomplete
-    parentNode: Incomplete
-    tagName: str
-    nodeName: str
-    prefix: Incomplete
+    nodeType: ClassVar[Literal[1]]
+    nodeName: str  # same as Element.tagName
+    nodeValue: None
+    @property
+    def attributes(self) -> NamedNodeMap: ...  # type: ignore[override]
+
+    parentNode: Document | Element | DocumentFragment | None
+    nextSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None
+    previousSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None
+    childNodes: NodeList[_ElementChildren]
+    @property
+    def firstChild(self) -> _ElementChildren | None: ...
+    @property
+    def lastChild(self) -> _ElementChildren | None: ...
+
     namespaceURI: str | None
-    childNodes: Incomplete
-    nextSibling: Incomplete
+    prefix: str | None
+    @property
+    def localName(self) -> str: ...
+
+    schemaType: TypeInfo
+    tagName: str
+
     def __init__(
-        self, tagName, namespaceURI: str | None = None, prefix: Incomplete | None = None, localName: Incomplete | None = None
+        self, tagName: str, namespaceURI: str | None = None, prefix: str | None = None, localName: str | None = None
     ) -> None: ...
     def unlink(self) -> None: ...
     def getAttribute(self, attname: str) -> str: ...
-    def getAttributeNS(self, namespaceURI: str, localName): ...
+    def getAttributeNS(self, namespaceURI: str | None, localName: str) -> str: ...
     def setAttribute(self, attname: str, value: str) -> None: ...
-    def setAttributeNS(self, namespaceURI: str, qualifiedName: str, value) -> None: ...
-    def getAttributeNode(self, attrname: str): ...
-    def getAttributeNodeNS(self, namespaceURI: str, localName): ...
-    def setAttributeNode(self, attr): ...
-    setAttributeNodeNS: Incomplete
+    def setAttributeNS(self, namespaceURI: str | None, qualifiedName: str, value: str) -> None: ...
+    def getAttributeNode(self, attrname: str) -> Attr | None: ...
+    def getAttributeNodeNS(self, namespaceURI: str | None, localName: str) -> Attr | None: ...
+    def setAttributeNode(self, attr: Attr) -> Attr | None: ...
+    setAttributeNodeNS = setAttributeNode
     def removeAttribute(self, name: str) -> None: ...
-    def removeAttributeNS(self, namespaceURI: str, localName) -> None: ...
-    def removeAttributeNode(self, node): ...
-    removeAttributeNodeNS: Incomplete
+    def removeAttributeNS(self, namespaceURI: str | None, localName: str) -> None: ...
+    def removeAttributeNode(self, node: Attr) -> Attr: ...
+    removeAttributeNodeNS = removeAttributeNode
     def hasAttribute(self, name: str) -> bool: ...
-    def hasAttributeNS(self, namespaceURI: str, localName) -> bool: ...
+    def hasAttributeNS(self, namespaceURI: str | None, localName: str) -> bool: ...
     def getElementsByTagName(self, name: str) -> NodeList[Element]: ...
-    def getElementsByTagNameNS(self, namespaceURI: str, localName: str) -> NodeList[Element]: ...
+    def getElementsByTagNameNS(self, namespaceURI: str | None, localName: str) -> NodeList[Element]: ...
     def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
     def hasAttributes(self) -> bool: ...
-    def setIdAttribute(self, name) -> None: ...
-    def setIdAttributeNS(self, namespaceURI: str, localName) -> None: ...
-    def setIdAttributeNode(self, idAttr) -> None: ...
-    @property
-    def attributes(self) -> NamedNodeMap: ...
+    def setIdAttribute(self, name: str) -> None: ...
+    def setIdAttributeNS(self, namespaceURI: str | None, localName: str) -> None: ...
+    def setIdAttributeNode(self, idAttr: Attr) -> None: ...
+    def insertBefore(  # type: ignore[override]
+        self, newChild: _ElementChildrenPlusFragment, refChild: _ElementChildren | None
+    ) -> _ElementChildrenPlusFragment: ...
+    def appendChild(self, node: _ElementChildrenPlusFragment) -> _ElementChildrenPlusFragment: ...  # type: ignore[override]
+    @overload  # type: ignore[override]
+    def replaceChild(
+        self, newChild: DocumentFragment, oldChild: _ElementChildrenVar
+    ) -> _ElementChildrenVar | DocumentFragment: ...
+    @overload
+    def replaceChild(self, newChild: _ElementChildren, oldChild: _ElementChildrenVar) -> _ElementChildrenVar | None: ...  # type: ignore[override]
+    def removeChild(self, oldChild: _ElementChildrenVar) -> _ElementChildrenVar: ...  # type: ignore[override]
 
 class Childless:
-    attributes: Incomplete
-    childNodes: Incomplete
-    firstChild: Incomplete
-    lastChild: Incomplete
-    def appendChild(self, node) -> NoReturn: ...
-    def hasChildNodes(self) -> bool: ...
-    def insertBefore(self, newChild, refChild) -> NoReturn: ...
-    def removeChild(self, oldChild) -> NoReturn: ...
+    attributes: None
+    childNodes: EmptyNodeList
+    @property
+    def firstChild(self) -> None: ...
+    @property
+    def lastChild(self) -> None: ...
+    def appendChild(self, node: _NodesThatAreChildren | DocumentFragment) -> NoReturn: ...
+    def hasChildNodes(self) -> Literal[False]: ...
+    def insertBefore(
+        self, newChild: _NodesThatAreChildren | DocumentFragment, refChild: _NodesThatAreChildren | None
+    ) -> NoReturn: ...
+    def removeChild(self, oldChild: _NodesThatAreChildren) -> NoReturn: ...
     def normalize(self) -> None: ...
-    def replaceChild(self, newChild, oldChild) -> NoReturn: ...
+    def replaceChild(self, newChild: _NodesThatAreChildren | DocumentFragment, oldChild: _NodesThatAreChildren) -> NoReturn: ...
 
 class ProcessingInstruction(Childless, Node):
-    nodeType: int
-    target: Incomplete
-    data: Incomplete
-    def __init__(self, target, data) -> None: ...
-    nodeValue: Incomplete
-    nodeName: Incomplete
+    nodeType: ClassVar[Literal[7]]
+    nodeName: str  # same as ProcessingInstruction.target
+    nodeValue: str  # same as ProcessingInstruction.data
+    attributes: None
+
+    parentNode: Document | Element | DocumentFragment | None
+    nextSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None
+    previousSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None
+    childNodes: EmptyNodeList
+    @property
+    def firstChild(self) -> None: ...
+    @property
+    def lastChild(self) -> None: ...
+
+    namespaceURI: None
+    prefix: None
+    @property
+    def localName(self) -> None: ...
+
+    target: str
+    data: str
+
+    def __init__(self, target: str, data: str) -> None: ...
     def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
 
 class CharacterData(Childless, Node):
-    ownerDocument: Incomplete
-    previousSibling: Incomplete
+    nodeValue: str
+    attributes: None
+
+    childNodes: EmptyNodeList
+    nextSibling: _NodesThatAreChildren | None
+    previousSibling: _NodesThatAreChildren | None
+
+    @property
+    def localName(self) -> None: ...
+
+    ownerDocument: Document | None
+    data: str
+
     def __init__(self) -> None: ...
+    @property
+    def length(self) -> int: ...
     def __len__(self) -> int: ...
-    data: str
-    nodeValue: Incomplete
     def substringData(self, offset: int, count: int) -> str: ...
     def appendData(self, arg: str) -> None: ...
     def insertData(self, offset: int, arg: str) -> None: ...
     def deleteData(self, offset: int, count: int) -> None: ...
     def replaceData(self, offset: int, count: int, arg: str) -> None: ...
-    @property
-    def length(self) -> int: ...
 
 class Text(CharacterData):
-    nodeType: int
-    nodeName: str
-    attributes: Incomplete
-    data: Incomplete
+    nodeType: ClassVar[Literal[3]]
+    nodeName: Literal["#text"]
+    nodeValue: str  # same as CharacterData.data, the content of the text node
+    attributes: None
+
+    parentNode: Attr | Element | DocumentFragment | None
+    nextSibling: _DocumentFragmentChildren | _ElementChildren | _AttrChildren | None
+    previousSibling: _DocumentFragmentChildren | _ElementChildren | _AttrChildren | None
+    childNodes: EmptyNodeList
+    @property
+    def firstChild(self) -> None: ...
+    @property
+    def lastChild(self) -> None: ...
+
+    namespaceURI: None
+    prefix: None
+    @property
+    def localName(self) -> None: ...
+
+    data: str
     def splitText(self, offset: int) -> Self: ...
     def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
-    def replaceWholeText(self, content) -> Self | None: ...
+    def replaceWholeText(self, content: str) -> Self | None: ...
     @property
     def isWhitespaceInElementContent(self) -> bool: ...
     @property
     def wholeText(self) -> str: ...
 
 class Comment(CharacterData):
-    nodeType: int
-    nodeName: str
-    def __init__(self, data) -> None: ...
+    nodeType: ClassVar[Literal[8]]
+    nodeName: Literal["#comment"]
+    nodeValue: str  # same as CharacterData.data, the content of the comment
+    attributes: None
+
+    parentNode: Document | Element | DocumentFragment | None
+    nextSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None
+    previousSibling: _DocumentChildren | _ElementChildren | _DocumentFragmentChildren | None
+    childNodes: EmptyNodeList
+    @property
+    def firstChild(self) -> None: ...
+    @property
+    def lastChild(self) -> None: ...
+
+    namespaceURI: None
+    prefix: None
+    @property
+    def localName(self) -> None: ...
+    def __init__(self, data: str) -> None: ...
     def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
 
 class CDATASection(Text):
-    nodeType: int
-    nodeName: str
+    nodeType: ClassVar[Literal[4]]  # type: ignore[assignment]
+    nodeName: Literal["#cdata-section"]  # type: ignore[assignment]
+    nodeValue: str  # same as CharacterData.data, the content of the CDATA Section
+    attributes: None
+
+    parentNode: Element | DocumentFragment | None
+    nextSibling: _DocumentFragmentChildren | _ElementChildren | None
+    previousSibling: _DocumentFragmentChildren | _ElementChildren | None
+
     def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
 
-class ReadOnlySequentialNamedNodeMap:
-    def __init__(self, seq=()) -> None: ...
+class ReadOnlySequentialNamedNodeMap(Generic[_N]):
+    def __init__(self, seq: Sequence[_N] = ()) -> None: ...
     def __len__(self) -> int: ...
-    def getNamedItem(self, name): ...
-    def getNamedItemNS(self, namespaceURI: str, localName): ...
-    def __getitem__(self, name_or_tuple): ...
-    def item(self, index): ...
-    def removeNamedItem(self, name) -> None: ...
-    def removeNamedItemNS(self, namespaceURI: str, localName) -> None: ...
-    def setNamedItem(self, node) -> None: ...
-    def setNamedItemNS(self, node) -> None: ...
+    def getNamedItem(self, name: str) -> _N | None: ...
+    def getNamedItemNS(self, namespaceURI: str | None, localName: str) -> _N | None: ...
+    def __getitem__(self, name_or_tuple: str | _NSName) -> _N | None: ...
+    def item(self, index: int) -> _N | None: ...
+    def removeNamedItem(self, name: str) -> NoReturn: ...
+    def removeNamedItemNS(self, namespaceURI: str | None, localName: str) -> NoReturn: ...
+    def setNamedItem(self, node: Node) -> NoReturn: ...
+    def setNamedItemNS(self, node: Node) -> NoReturn: ...
     @property
     def length(self) -> int: ...
 
@@ -295,38 +509,85 @@ class Identified:
     systemId: str | None
 
 class DocumentType(Identified, Childless, Node):
-    nodeType: int
-    nodeValue: Incomplete
-    name: Incomplete
-    internalSubset: Incomplete
-    entities: Incomplete
-    notations: Incomplete
-    nodeName: Incomplete
-    def __init__(self, qualifiedName: str) -> None: ...
-    def cloneNode(self, deep): ...
+    nodeType: ClassVar[Literal[10]]
+    nodeName: str | None  # same as DocumentType.name
+    nodeValue: None
+    attributes: None
+
+    parentNode: Document | None
+    nextSibling: _DocumentChildren | None
+    previousSibling: _DocumentChildren | None
+    childNodes: EmptyNodeList
+    @property
+    def firstChild(self) -> None: ...
+    @property
+    def lastChild(self) -> None: ...
+
+    namespaceURI: None
+    prefix: None
+    @property
+    def localName(self) -> None: ...
+
+    name: str | None
+    internalSubset: str | None
+    entities: ReadOnlySequentialNamedNodeMap[Entity]
+    notations: ReadOnlySequentialNamedNodeMap[Notation]
+
+    def __init__(self, qualifiedName: str | None) -> None: ...
+    def cloneNode(self, deep: bool) -> DocumentType | None: ...
     def writexml(self, writer: SupportsWrite[str], indent: str = "", addindent: str = "", newl: str = "") -> None: ...
 
 class Entity(Identified, Node):
-    attributes: Incomplete
-    nodeType: int
-    nodeValue: Incomplete
-    actualEncoding: Incomplete
-    encoding: Incomplete
-    version: Incomplete
-    nodeName: Incomplete
-    notationName: Incomplete
-    childNodes: Incomplete
-    def __init__(self, name, publicId, systemId, notation) -> None: ...
-    def appendChild(self, newChild) -> NoReturn: ...
-    def insertBefore(self, newChild, refChild) -> NoReturn: ...
-    def removeChild(self, oldChild) -> NoReturn: ...
-    def replaceChild(self, newChild, oldChild) -> NoReturn: ...
+    nodeType: ClassVar[Literal[6]]
+    nodeName: str  # entity name
+    nodeValue: None
+    attributes: None
+
+    parentNode: None
+    nextSibling: None
+    previousSibling: None
+    childNodes: NodeList[_EntityChildren]
+    @property
+    def firstChild(self) -> _EntityChildren | None: ...
+    @property
+    def lastChild(self) -> _EntityChildren | None: ...
+
+    namespaceURI: None
+    prefix: None
+    @property
+    def localName(self) -> None: ...
+
+    actualEncoding: str | None
+    encoding: str | None
+    version: str | None
+    notationName: str | None
+
+    def __init__(self, name: str, publicId: str | None, systemId: str | None, notation: str | None) -> None: ...
+    def appendChild(self, newChild: _EntityChildren) -> NoReturn: ...  # type: ignore[override]
+    def insertBefore(self, newChild: _EntityChildren, refChild: _EntityChildren | None) -> NoReturn: ...  # type: ignore[override]
+    def removeChild(self, oldChild: _EntityChildren) -> NoReturn: ...  # type: ignore[override]
+    def replaceChild(self, newChild: _EntityChildren, oldChild: _EntityChildren) -> NoReturn: ...  # type: ignore[override]
 
 class Notation(Identified, Childless, Node):
-    nodeType: int
-    nodeValue: Incomplete
-    nodeName: Incomplete
-    def __init__(self, name, publicId, systemId) -> None: ...
+    nodeType: ClassVar[Literal[12]]
+    nodeName: str  # notation name
+    nodeValue: None
+    attributes: None
+
+    parentNode: DocumentFragment | None
+    nextSibling: _DocumentFragmentChildren | None
+    previousSibling: _DocumentFragmentChildren | None
+    childNodes: EmptyNodeList
+    @property
+    def firstChild(self) -> None: ...
+    @property
+    def lastChild(self) -> None: ...
+
+    namespaceURI: None
+    prefix: None
+    @property
+    def localName(self) -> None: ...
+    def __init__(self, name: str, publicId: str | None, systemId: str | None) -> None: ...
 
 class DOMImplementation(DOMImplementationLS):
     def hasFeature(self, feature: str, version: str | None) -> bool: ...
@@ -335,53 +596,67 @@ class DOMImplementation(DOMImplementationLS):
     def getInterface(self, feature: str) -> Self | None: ...
 
 class ElementInfo:
-    tagName: Incomplete
-    def __init__(self, name) -> None: ...
-    def getAttributeType(self, aname): ...
-    def getAttributeTypeNS(self, namespaceURI: str, localName): ...
-    def isElementContent(self): ...
-    def isEmpty(self): ...
-    def isId(self, aname): ...
-    def isIdNS(self, namespaceURI: str, localName): ...
+    tagName: str
+    def __init__(self, name: str) -> None: ...
+    def getAttributeType(self, aname: str) -> TypeInfo: ...
+    def getAttributeTypeNS(self, namespaceURI: str | None, localName: str) -> TypeInfo: ...
+    def isElementContent(self) -> bool: ...
+    def isEmpty(self) -> bool: ...
+    def isId(self, aname: str) -> bool: ...
+    def isIdNS(self, namespaceURI: str | None, localName: str) -> bool: ...
+
+_DocumentChildrenPlusFragment = TypeVar("_DocumentChildrenPlusFragment", bound=_DocumentChildren | DocumentFragment)
 
 class Document(Node, DocumentLS):
-    implementation: Incomplete
-    nodeType: int
-    nodeName: str
-    nodeValue: Incomplete
-    attributes: Incomplete
-    parentNode: Incomplete
-    previousSibling: Incomplete
-    nextSibling: Incomplete
-    actualEncoding: Incomplete
+    nodeType: ClassVar[Literal[9]]
+    nodeName: Literal["#document"]
+    nodeValue: None
+    attributes: None
+
+    parentNode: None
+    previousSibling: None
+    nextSibling: None
+    childNodes: NodeList[_DocumentChildren]
+    @property
+    def firstChild(self) -> _DocumentChildren | None: ...
+    @property
+    def lastChild(self) -> _DocumentChildren | None: ...
+
+    namespaceURI: None
+    prefix: None
+    @property
+    def localName(self) -> None: ...
+
+    implementation: DOMImplementation
+    actualEncoding: str | None
     encoding: str | None
     standalone: bool | None
-    version: Incomplete
+    version: str | None
     strictErrorChecking: bool
-    errorHandler: Incomplete
-    documentURI: Incomplete
+    errorHandler: _DOMErrorHandler | None
+    documentURI: str | None
     doctype: DocumentType | None
-    childNodes: Incomplete
+    documentElement: Element | None
+
     def __init__(self) -> None: ...
-    def appendChild(self, node: _N) -> _N: ...
-    documentElement: Incomplete
-    def removeChild(self, oldChild): ...
+    def appendChild(self, node: _DocumentChildrenVar) -> _DocumentChildrenVar: ...  # type: ignore[override]
+    def removeChild(self, oldChild: _DocumentChildrenVar) -> _DocumentChildrenVar: ...  # type: ignore[override]
     def unlink(self) -> None: ...
-    def cloneNode(self, deep): ...
+    def cloneNode(self, deep: bool) -> Document | None: ...
     def createDocumentFragment(self) -> DocumentFragment: ...
     def createElement(self, tagName: str) -> Element: ...
     def createTextNode(self, data: str) -> Text: ...
     def createCDATASection(self, data: str) -> CDATASection: ...
     def createComment(self, data: str) -> Comment: ...
-    def createProcessingInstruction(self, target, data): ...
-    def createAttribute(self, qName) -> Attr: ...
-    def createElementNS(self, namespaceURI: str, qualifiedName: str): ...
-    def createAttributeNS(self, namespaceURI: str, qualifiedName: str) -> Attr: ...
+    def createProcessingInstruction(self, target: str, data: str) -> ProcessingInstruction: ...
+    def createAttribute(self, qName: str) -> Attr: ...
+    def createElementNS(self, namespaceURI: str | None, qualifiedName: str) -> Element: ...
+    def createAttributeNS(self, namespaceURI: str | None, qualifiedName: str) -> Attr: ...
     def getElementById(self, id: str) -> Element | None: ...
     def getElementsByTagName(self, name: str) -> NodeList[Element]: ...
-    def getElementsByTagNameNS(self, namespaceURI: str, localName: str) -> NodeList[Element]: ...
+    def getElementsByTagNameNS(self, namespaceURI: str | None, localName: str) -> NodeList[Element]: ...
     def isSupported(self, feature: str, version: str | None) -> bool: ...
-    def importNode(self, node, deep): ...
+    def importNode(self, node: _ImportableNodeVar, deep: bool) -> _ImportableNodeVar: ...
     if sys.version_info >= (3, 9):
         def writexml(
             self,
@@ -402,4 +677,18 @@ class Document(Node, DocumentLS):
             encoding: Incomplete | None = None,
         ) -> None: ...
 
-    def renameNode(self, n, namespaceURI: str, name): ...
+    @overload
+    def renameNode(self, n: Element, namespaceURI: str, name: str) -> Element: ...
+    @overload
+    def renameNode(self, n: Attr, namespaceURI: str, name: str) -> Attr: ...
+    @overload
+    def renameNode(self, n: Element | Attr, namespaceURI: str, name: str) -> Element | Attr: ...
+    def insertBefore(
+        self, newChild: _DocumentChildrenPlusFragment, refChild: _DocumentChildren | None  # type: ignore[override]
+    ) -> _DocumentChildrenPlusFragment: ...
+    @overload  # type: ignore[override]
+    def replaceChild(
+        self, newChild: DocumentFragment, oldChild: _DocumentChildrenVar
+    ) -> _DocumentChildrenVar | DocumentFragment: ...
+    @overload
+    def replaceChild(self, newChild: _DocumentChildren, oldChild: _DocumentChildrenVar) -> _DocumentChildrenVar | None: ...
diff --git a/stdlib/xml/dom/pulldom.pyi b/stdlib/xml/dom/pulldom.pyi
index 50250de5cb2f..d9458654c185 100644
--- a/stdlib/xml/dom/pulldom.pyi
+++ b/stdlib/xml/dom/pulldom.pyi
@@ -1,11 +1,12 @@
 import sys
-from _typeshed import Incomplete, SupportsRead
-from collections.abc import Sequence
-from typing import Final, Literal
-from typing_extensions import TypeAlias
-from xml.dom.minidom import Document, DOMImplementation, Element, Text
+from _typeshed import Incomplete, Unused
+from collections.abc import MutableSequence, Sequence
+from typing import Final, Literal, NoReturn
+from typing_extensions import Self, TypeAlias
+from xml.dom.minidom import Comment, Document, DOMImplementation, Element, ProcessingInstruction, Text
+from xml.sax import _SupportsReadClose
 from xml.sax.handler import ContentHandler
-from xml.sax.xmlreader import XMLReader
+from xml.sax.xmlreader import AttributesImpl, AttributesNSImpl, Locator, XMLReader
 
 START_ELEMENT: Final = "START_ELEMENT"
 END_ELEMENT: Final = "END_ELEMENT"
@@ -16,79 +17,93 @@ PROCESSING_INSTRUCTION: Final = "PROCESSING_INSTRUCTION"
 IGNORABLE_WHITESPACE: Final = "IGNORABLE_WHITESPACE"
 CHARACTERS: Final = "CHARACTERS"
 
+_NSName: TypeAlias = tuple[str | None, str]
 _DocumentFactory: TypeAlias = DOMImplementation | None
-_Node: TypeAlias = Document | Element | Text
 
-_Event: TypeAlias = tuple[
-    Literal[
-        Literal["START_ELEMENT"],
-        Literal["END_ELEMENT"],
-        Literal["COMMENT"],
-        Literal["START_DOCUMENT"],
-        Literal["END_DOCUMENT"],
-        Literal["PROCESSING_INSTRUCTION"],
-        Literal["IGNORABLE_WHITESPACE"],
-        Literal["CHARACTERS"],
-    ],
-    _Node,
-]
+_Event: TypeAlias = (
+    tuple[Literal["START_ELEMENT"], Element]
+    | tuple[Literal["END_ELEMENT"], Element]
+    | tuple[Literal["COMMENT"], Comment]
+    | tuple[Literal["START_DOCUMENT"], Document]
+    | tuple[Literal["END_DOCUMENT"], Document]
+    | tuple[Literal["PROCESSING_INSTRUCTION"], ProcessingInstruction]
+    | tuple[Literal["IGNORABLE_WHITESPACE"], Text]
+    | tuple[Literal["CHARACTERS"], Text]
+)
 
 class PullDOM(ContentHandler):
     document: Document | None
     documentFactory: _DocumentFactory
-    firstEvent: Incomplete
-    lastEvent: Incomplete
-    elementStack: Sequence[Incomplete]
-    pending_events: Sequence[Incomplete]
+
+    # firstEvent is a list of length 2
+    # firstEvent[0] is always None
+    # firstEvent[1] is None prior to any events, after which it's a
+    # list of length 2, where the first item is of type _Event
+    # and the second item is None.
+    firstEvent: list[Incomplete]
+
+    # lastEvent is also a list of length 2. The second item is always None,
+    # and the first item is of type _Event
+    # This is a slight lie: The second item is sometimes temporarily what was just
+    # described for the type of lastEvent, after which lastEvent is always updated
+    # with `self.lastEvent = self.lastEvent[1]`.
+    lastEvent: list[Incomplete]
+
+    elementStack: MutableSequence[Element | Document]
+    pending_events: (
+        list[Sequence[tuple[Literal["COMMENT"], str] | tuple[Literal["PROCESSING_INSTRUCTION"], str, str] | None]] | None
+    )
     def __init__(self, documentFactory: _DocumentFactory = None) -> None: ...
-    def pop(self) -> Element: ...
-    def setDocumentLocator(self, locator) -> None: ...
-    def startPrefixMapping(self, prefix, uri) -> None: ...
-    def endPrefixMapping(self, prefix) -> None: ...
-    def startElementNS(self, name, tagName, attrs) -> None: ...
-    def endElementNS(self, name, tagName) -> None: ...
-    def startElement(self, name, attrs) -> None: ...
-    def endElement(self, name) -> None: ...
-    def comment(self, s) -> None: ...
-    def processingInstruction(self, target, data) -> None: ...
-    def ignorableWhitespace(self, chars) -> None: ...
-    def characters(self, chars) -> None: ...
+    def pop(self) -> Element | Document: ...
+    def setDocumentLocator(self, locator: Locator) -> None: ...
+    def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ...
+    def endPrefixMapping(self, prefix: str | None) -> None: ...
+    def startElementNS(self, name: _NSName, tagName: str | None, attrs: AttributesNSImpl) -> None: ...
+    def endElementNS(self, name: _NSName, tagName: str | None) -> None: ...
+    def startElement(self, name: str, attrs: AttributesImpl) -> None: ...
+    def endElement(self, name: str) -> None: ...
+    def comment(self, s: str) -> None: ...
+    def processingInstruction(self, target: str, data: str) -> None: ...
+    def ignorableWhitespace(self, chars: str) -> None: ...
+    def characters(self, chars: str) -> None: ...
     def startDocument(self) -> None: ...
-    def buildDocument(self, uri, tagname): ...
+    def buildDocument(self, uri: str | None, tagname: str | None) -> Element: ...
     def endDocument(self) -> None: ...
     def clear(self) -> None: ...
 
 class ErrorHandler:
-    def warning(self, exception) -> None: ...
-    def error(self, exception) -> None: ...
-    def fatalError(self, exception) -> None: ...
+    def warning(self, exception: BaseException) -> None: ...
+    def error(self, exception: BaseException) -> NoReturn: ...
+    def fatalError(self, exception: BaseException) -> NoReturn: ...
 
 class DOMEventStream:
-    stream: SupportsRead[bytes] | SupportsRead[str]
-    parser: XMLReader
+    stream: _SupportsReadClose[bytes] | _SupportsReadClose[str]
+    parser: XMLReader  # Set to none after .clear() is called
     bufsize: int
-    def __init__(self, stream: SupportsRead[bytes] | SupportsRead[str], parser: XMLReader, bufsize: int) -> None: ...
-    pulldom: Incomplete
+    pulldom: PullDOM
+    def __init__(self, stream: _SupportsReadClose[bytes] | _SupportsReadClose[str], parser: XMLReader, bufsize: int) -> None: ...
     if sys.version_info < (3, 11):
-        def __getitem__(self, pos): ...
+        def __getitem__(self, pos: Unused) -> _Event: ...
 
-    def __next__(self): ...
-    def __iter__(self): ...
-    def getEvent(self) -> _Event: ...
-    def expandNode(self, node: _Node) -> None: ...
+    def __next__(self) -> _Event: ...
+    def __iter__(self) -> Self: ...
+    def getEvent(self) -> _Event | None: ...
+    def expandNode(self, node: Document) -> None: ...
     def reset(self) -> None: ...
     def clear(self) -> None: ...
 
 class SAX2DOM(PullDOM):
-    def startElementNS(self, name, tagName, attrs) -> None: ...
-    def startElement(self, name, attrs) -> None: ...
-    def processingInstruction(self, target, data) -> None: ...
-    def ignorableWhitespace(self, chars) -> None: ...
-    def characters(self, chars) -> None: ...
+    def startElementNS(self, name: _NSName, tagName: str | None, attrs: AttributesNSImpl) -> None: ...
+    def startElement(self, name: str, attrs: AttributesImpl) -> None: ...
+    def processingInstruction(self, target: str, data: str) -> None: ...
+    def ignorableWhitespace(self, chars: str) -> None: ...
+    def characters(self, chars: str) -> None: ...
 
 default_bufsize: int
 
 def parse(
-    stream_or_string: str | SupportsRead[bytes] | SupportsRead[str], parser: XMLReader | None = None, bufsize: int | None = None
+    stream_or_string: str | _SupportsReadClose[bytes] | _SupportsReadClose[str],
+    parser: XMLReader | None = None,
+    bufsize: int | None = None,
 ) -> DOMEventStream: ...
 def parseString(string: str, parser: XMLReader | None = None) -> DOMEventStream: ...
diff --git a/stdlib/xml/dom/xmlbuilder.pyi b/stdlib/xml/dom/xmlbuilder.pyi
index ab76d362e23f..6fb18bbc4eda 100644
--- a/stdlib/xml/dom/xmlbuilder.pyi
+++ b/stdlib/xml/dom/xmlbuilder.pyi
@@ -1,32 +1,9 @@
-from _typeshed import Incomplete, Unused
+from _typeshed import SupportsRead
 from typing import Any, Literal, NoReturn
-from typing_extensions import TypeAlias
-from urllib.request import OpenerDirector
-from xml.dom.expatbuilder import ExpatBuilder, ExpatBuilderNS
-from xml.dom.minidom import Node
+from xml.dom.minidom import Document, Node, _DOMErrorHandler
 
 __all__ = ["DOMBuilder", "DOMEntityResolver", "DOMInputSource"]
 
-# UNKNOWN TYPES:
-# - `Options.errorHandler`.
-#       The same as `_DOMBuilderErrorHandlerType`?
-#       Maybe `xml.sax.handler.ErrorHandler`?
-# - Return type of DOMBuilder.getFeature().
-#       We could get rid of the `Incomplete` if we knew more
-#       about `Options.errorHandler`.
-
-# ALIASES REPRESENTING MORE UNKNOWN TYPES:
-
-# probably the same as `Options.errorHandler`?
-# Maybe `xml.sax.handler.ErrorHandler`?
-_DOMBuilderErrorHandlerType: TypeAlias = Incomplete | None
-# probably some kind of IO...
-_DOMInputSourceCharacterStreamType: TypeAlias = Incomplete | None
-# probably a string??
-_DOMInputSourceStringDataType: TypeAlias = Incomplete | None
-# probably a string??
-_DOMInputSourceEncodingType: TypeAlias = Incomplete | None
-
 class Options:
     namespaces: int
     namespace_declarations: bool
@@ -45,37 +22,35 @@ class Options:
     charset_overrides_xml_encoding: bool
     infoset: bool
     supported_mediatypes_only: bool
-    errorHandler: Any | None
-    filter: DOMBuilderFilter | None  # a guess, but seems likely
+    errorHandler: _DOMErrorHandler | None
+    filter: DOMBuilderFilter | None
 
 class DOMBuilder:
-    entityResolver: DOMEntityResolver | None  # a guess, but seems likely
-    errorHandler: _DOMBuilderErrorHandlerType
-    filter: DOMBuilderFilter | None  # a guess, but seems likely
+    entityResolver: DOMEntityResolver | None
+    errorHandler: _DOMErrorHandler | None
+    filter: DOMBuilderFilter | None
     ACTION_REPLACE: Literal[1]
     ACTION_APPEND_AS_CHILDREN: Literal[2]
     ACTION_INSERT_AFTER: Literal[3]
     ACTION_INSERT_BEFORE: Literal[4]
+    def __init__(self) -> None: ...
     def setFeature(self, name: str, state: int) -> None: ...
     def supportsFeature(self, name: str) -> bool: ...
-    def canSetFeature(self, name: str, state: int) -> bool: ...
+    def canSetFeature(self, name: str, state: Literal[1, 0]) -> bool: ...
     # getFeature could return any attribute from an instance of `Options`
     def getFeature(self, name: str) -> Any: ...
-    def parseURI(self, uri: str) -> ExpatBuilder | ExpatBuilderNS: ...
-    def parse(self, input: DOMInputSource) -> ExpatBuilder | ExpatBuilderNS: ...
-    # `input` and `cnode` argtypes for `parseWithContext` are unknowable
-    # as the function does nothing with them, and always raises an exception.
-    # But `input` is *probably* `DOMInputSource`?
-    def parseWithContext(self, input: Unused, cnode: Unused, action: Literal[1, 2, 3, 4]) -> NoReturn: ...
+    def parseURI(self, uri: str) -> Document: ...
+    def parse(self, input: DOMInputSource) -> Document: ...
+    def parseWithContext(self, input: DOMInputSource, cnode: Node, action: Literal[1, 2, 3, 4]) -> NoReturn: ...
 
 class DOMEntityResolver:
     def resolveEntity(self, publicId: str | None, systemId: str) -> DOMInputSource: ...
 
 class DOMInputSource:
-    byteStream: OpenerDirector | None
-    characterStream: _DOMInputSourceCharacterStreamType
-    stringData: _DOMInputSourceStringDataType
-    encoding: _DOMInputSourceEncodingType
+    byteStream: SupportsRead[bytes] | None
+    characterStream: SupportsRead[str] | None
+    stringData: str | None
+    encoding: str | None
     publicId: str | None
     systemId: str | None
     baseURI: str | None
@@ -86,18 +61,14 @@ class DOMBuilderFilter:
     FILTER_SKIP: Literal[3]
     FILTER_INTERRUPT: Literal[4]
     whatToShow: int
-    def acceptNode(self, element: Unused) -> Literal[1]: ...
-    def startContainer(self, element: Unused) -> Literal[1]: ...
+    def acceptNode(self, element: Node) -> Literal[1, 2, 3, 4]: ...
+    def startContainer(self, element: Node) -> Literal[1, 2, 3, 4]: ...
 
 class DocumentLS:
     async_: bool
     def abort(self) -> NoReturn: ...
-    # `load()` and `loadXML()` always raise exceptions
-    # so the argtypes of `uri` and `source` are unknowable.
-    # `source` is *probably* `DOMInputSource`?
-    # `uri` is *probably* a str? (see DOMBuilder.parseURI())
-    def load(self, uri: Unused) -> NoReturn: ...
-    def loadXML(self, source: Unused) -> NoReturn: ...
+    def load(self, uri: str) -> NoReturn: ...
+    def loadXML(self, source: str) -> NoReturn: ...
     def saveXML(self, snode: Node | None) -> str: ...
 
 class DOMImplementationLS:
diff --git a/stdlib/xml/etree/ElementInclude.pyi b/stdlib/xml/etree/ElementInclude.pyi
index 5a15772ec2a9..10c305826453 100644
--- a/stdlib/xml/etree/ElementInclude.pyi
+++ b/stdlib/xml/etree/ElementInclude.pyi
@@ -1,9 +1,14 @@
 import sys
 from _typeshed import FileDescriptorOrPath
-from collections.abc import Callable
-from typing import Final
+from typing import Final, Literal, Protocol, overload
 from xml.etree.ElementTree import Element
 
+class _Loader(Protocol):
+    @overload
+    def __call__(self, href: FileDescriptorOrPath, parse: Literal["xml"], encoding: str | None = None) -> Element: ...
+    @overload
+    def __call__(self, href: FileDescriptorOrPath, parse: Literal["text"], encoding: str | None = None) -> str: ...
+
 XINCLUDE: Final[str]
 XINCLUDE_INCLUDE: Final[str]
 XINCLUDE_FALLBACK: Final[str]
@@ -13,17 +18,15 @@ if sys.version_info >= (3, 9):
 
 class FatalIncludeError(SyntaxError): ...
 
-def default_loader(href: FileDescriptorOrPath, parse: str, encoding: str | None = None) -> str | Element: ...
+@overload
+def default_loader(href: FileDescriptorOrPath, parse: Literal["xml"], encoding: str | None = None) -> Element: ...
+@overload
+def default_loader(href: FileDescriptorOrPath, parse: Literal["text"], encoding: str | None = None) -> str: ...
 
-# TODO: loader is of type default_loader ie it takes a callable that has the
-# same signature as default_loader. But default_loader has a keyword argument
-# Which can't be represented using Callable...
 if sys.version_info >= (3, 9):
-    def include(
-        elem: Element, loader: Callable[..., str | Element] | None = None, base_url: str | None = None, max_depth: int | None = 6
-    ) -> None: ...
+    def include(elem: Element, loader: _Loader | None = None, base_url: str | None = None, max_depth: int | None = 6) -> None: ...
 
     class LimitedRecursiveIncludeError(FatalIncludeError): ...
 
 else:
-    def include(elem: Element, loader: Callable[..., str | Element] | None = None) -> None: ...
+    def include(elem: Element, loader: _Loader | None = None) -> None: ...
diff --git a/stdlib/xml/etree/ElementPath.pyi b/stdlib/xml/etree/ElementPath.pyi
index c3f6207ea241..ebfb4f1ffbb9 100644
--- a/stdlib/xml/etree/ElementPath.pyi
+++ b/stdlib/xml/etree/ElementPath.pyi
@@ -1,6 +1,6 @@
-from collections.abc import Callable, Generator
+from collections.abc import Callable, Generator, Iterable
 from re import Pattern
-from typing import TypeVar
+from typing import Any, Literal, TypeVar, overload
 from typing_extensions import TypeAlias
 from xml.etree.ElementTree import Element
 
@@ -8,27 +8,34 @@ xpath_tokenizer_re: Pattern[str]
 
 _Token: TypeAlias = tuple[str, str]
 _Next: TypeAlias = Callable[[], _Token]
-_Callback: TypeAlias = Callable[[_SelectorContext, list[Element]], Generator[Element, None, None]]
+_Callback: TypeAlias = Callable[[_SelectorContext, Iterable[Element]], Generator[Element, None, None]]
+_T = TypeVar("_T")
 
 def xpath_tokenizer(pattern: str, namespaces: dict[str, str] | None = None) -> Generator[_Token, None, None]: ...
 def get_parent_map(context: _SelectorContext) -> dict[Element, Element]: ...
 def prepare_child(next: _Next, token: _Token) -> _Callback: ...
 def prepare_star(next: _Next, token: _Token) -> _Callback: ...
 def prepare_self(next: _Next, token: _Token) -> _Callback: ...
-def prepare_descendant(next: _Next, token: _Token) -> _Callback: ...
+def prepare_descendant(next: _Next, token: _Token) -> _Callback | None: ...
 def prepare_parent(next: _Next, token: _Token) -> _Callback: ...
-def prepare_predicate(next: _Next, token: _Token) -> _Callback: ...
+def prepare_predicate(next: _Next, token: _Token) -> _Callback | None: ...
 
-ops: dict[str, Callable[[_Next, _Token], _Callback]]
+ops: dict[str, Callable[[_Next, _Token], _Callback | None]]
 
 class _SelectorContext:
     parent_map: dict[Element, Element] | None
     root: Element
     def __init__(self, root: Element) -> None: ...
 
-_T = TypeVar("_T")
-
-def iterfind(elem: Element, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ...
-def find(elem: Element, path: str, namespaces: dict[str, str] | None = None) -> Element | None: ...
-def findall(elem: Element, path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ...
-def findtext(elem: Element, path: str, default: _T | None = None, namespaces: dict[str, str] | None = None) -> _T | str: ...
+@overload
+def iterfind(  # type: ignore[overload-overlap]
+    elem: Element[Any], path: Literal[""], namespaces: dict[str, str] | None = None
+) -> None: ...
+@overload
+def iterfind(elem: Element[Any], path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ...
+def find(elem: Element[Any], path: str, namespaces: dict[str, str] | None = None) -> Element | None: ...
+def findall(elem: Element[Any], path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ...
+@overload
+def findtext(elem: Element[Any], path: str, default: None = None, namespaces: dict[str, str] | None = None) -> str | None: ...
+@overload
+def findtext(elem: Element[Any], path: str, default: _T, namespaces: dict[str, str] | None = None) -> _T | str: ...
diff --git a/stdlib/xml/etree/ElementTree.pyi b/stdlib/xml/etree/ElementTree.pyi
index 64ebbd3ee63f..4a9113868d7e 100644
--- a/stdlib/xml/etree/ElementTree.pyi
+++ b/stdlib/xml/etree/ElementTree.pyi
@@ -2,8 +2,9 @@ import sys
 from _collections_abc import dict_keys
 from _typeshed import FileDescriptorOrPath, ReadableBuffer, SupportsRead, SupportsWrite
 from collections.abc import Callable, Generator, ItemsView, Iterable, Iterator, Mapping, Sequence
-from typing import Any, Final, Literal, SupportsIndex, TypeVar, overload
+from typing import Any, Final, Generic, Literal, Protocol, SupportsIndex, TypeVar, overload, type_check_only
 from typing_extensions import TypeAlias, TypeGuard, deprecated
+from xml.parsers.expat import XMLParserType
 
 __all__ = [
     "C14NWriterTarget",
@@ -78,13 +79,22 @@ def canonicalize(
     exclude_tags: Iterable[str] | None = None,
 ) -> None: ...
 
-class Element:
-    tag: str
+# The tag for Element can be set to the Comment or ProcessingInstruction
+# functions defined in this module. _ElementCallable could be a recursive
+# type, but defining it that way uncovered a bug in pytype.
+_ElementCallable: TypeAlias = Callable[..., Element[Any]]
+_CallableElement: TypeAlias = Element[_ElementCallable]
+
+_Tag = TypeVar("_Tag", default=str, bound=str | _ElementCallable)
+_OtherTag = TypeVar("_OtherTag", default=str, bound=str | _ElementCallable)
+
+class Element(Generic[_Tag]):
+    tag: _Tag
     attrib: dict[str, str]
     text: str | None
     tail: str | None
-    def __init__(self, tag: str, attrib: dict[str, str] = ..., **extra: str) -> None: ...
-    def append(self, subelement: Element, /) -> None: ...
+    def __init__(self, tag: _Tag, attrib: dict[str, str] = {}, **extra: str) -> None: ...
+    def append(self, subelement: Element[Any], /) -> None: ...
     def clear(self) -> None: ...
     def extend(self, elements: Iterable[Element], /) -> None: ...
     def find(self, path: str, namespaces: dict[str, str] | None = None) -> Element | None: ...
@@ -100,14 +110,17 @@ class Element:
     def insert(self, index: int, subelement: Element, /) -> None: ...
     def items(self) -> ItemsView[str, str]: ...
     def iter(self, tag: str | None = None) -> Generator[Element, None, None]: ...
+    @overload
+    def iterfind(self, path: Literal[""], namespaces: dict[str, str] | None = None) -> None: ...  # type: ignore[overload-overlap]
+    @overload
     def iterfind(self, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ...
     def itertext(self) -> Generator[str, None, None]: ...
     def keys(self) -> dict_keys[str, str]: ...
     # makeelement returns the type of self in Python impl, but not in C impl
-    def makeelement(self, tag: str, attrib: dict[str, str], /) -> Element: ...
+    def makeelement(self, tag: _OtherTag, attrib: dict[str, str], /) -> Element[_OtherTag]: ...
     def remove(self, subelement: Element, /) -> None: ...
     def set(self, key: str, value: str, /) -> None: ...
-    def __copy__(self) -> Element: ...  # returns the type of self in Python impl, but not in C impl
+    def __copy__(self) -> Element[_Tag]: ...  # returns the type of self in Python impl, but not in C impl
     def __deepcopy__(self, memo: Any, /) -> Element: ...  # Only exists in C impl
     def __delitem__(self, key: SupportsIndex | slice, /) -> None: ...
     @overload
@@ -130,8 +143,8 @@ class Element:
         def getiterator(self, tag: str | None = None) -> list[Element]: ...
 
 def SubElement(parent: Element, tag: str, attrib: dict[str, str] = ..., **extra: str) -> Element: ...
-def Comment(text: str | None = None) -> Element: ...
-def ProcessingInstruction(target: str, text: str | None = None) -> Element: ...
+def Comment(text: str | None = None) -> _CallableElement: ...
+def ProcessingInstruction(target: str, text: str | None = None) -> _CallableElement: ...
 
 PI = ProcessingInstruction
 
@@ -145,9 +158,11 @@ class QName:
     def __eq__(self, other: object) -> bool: ...
     def __hash__(self) -> int: ...
 
-class ElementTree:
+_Root = TypeVar("_Root", Element, Element | None, default=Element | None)
+
+class ElementTree(Generic[_Root]):
     def __init__(self, element: Element | None = None, file: _FileRead | None = None) -> None: ...
-    def getroot(self) -> Element | Any: ...
+    def getroot(self) -> _Root: ...
     def parse(self, source: _FileRead, parser: XMLParser | None = None) -> Element: ...
     def iter(self, tag: str | None = None) -> Generator[Element, None, None]: ...
     if sys.version_info < (3, 9):
@@ -159,6 +174,9 @@ class ElementTree:
     @overload
     def findtext(self, path: str, default: _T, namespaces: dict[str, str] | None = None) -> _T | str: ...
     def findall(self, path: str, namespaces: dict[str, str] | None = None) -> list[Element]: ...
+    @overload
+    def iterfind(self, path: Literal[""], namespaces: dict[str, str] | None = None) -> None: ...  # type: ignore[overload-overlap]
+    @overload
     def iterfind(self, path: str, namespaces: dict[str, str] | None = None) -> Generator[Element, None, None]: ...
     def write(
         self,
@@ -166,18 +184,20 @@ class ElementTree:
         encoding: str | None = None,
         xml_declaration: bool | None = None,
         default_namespace: str | None = None,
-        method: str | None = None,
+        method: Literal["xml", "html", "text", "c14n"] | None = None,
         *,
         short_empty_elements: bool = True,
     ) -> None: ...
     def write_c14n(self, file: _FileWriteC14N) -> None: ...
 
+HTML_EMPTY: set[str]
+
 def register_namespace(prefix: str, uri: str) -> None: ...
 @overload
 def tostring(
     element: Element,
     encoding: None = None,
-    method: str | None = None,
+    method: Literal["xml", "html", "text", "c14n"] | None = None,
     *,
     xml_declaration: bool | None = None,
     default_namespace: str | None = None,
@@ -187,7 +207,7 @@ def tostring(
 def tostring(
     element: Element,
     encoding: Literal["unicode"],
-    method: str | None = None,
+    method: Literal["xml", "html", "text", "c14n"] | None = None,
     *,
     xml_declaration: bool | None = None,
     default_namespace: str | None = None,
@@ -197,7 +217,7 @@ def tostring(
 def tostring(
     element: Element,
     encoding: str,
-    method: str | None = None,
+    method: Literal["xml", "html", "text", "c14n"] | None = None,
     *,
     xml_declaration: bool | None = None,
     default_namespace: str | None = None,
@@ -207,7 +227,7 @@ def tostring(
 def tostringlist(
     element: Element,
     encoding: None = None,
-    method: str | None = None,
+    method: Literal["xml", "html", "text", "c14n"] | None = None,
     *,
     xml_declaration: bool | None = None,
     default_namespace: str | None = None,
@@ -217,7 +237,7 @@ def tostringlist(
 def tostringlist(
     element: Element,
     encoding: Literal["unicode"],
-    method: str | None = None,
+    method: Literal["xml", "html", "text", "c14n"] | None = None,
     *,
     xml_declaration: bool | None = None,
     default_namespace: str | None = None,
@@ -227,21 +247,23 @@ def tostringlist(
 def tostringlist(
     element: Element,
     encoding: str,
-    method: str | None = None,
+    method: Literal["xml", "html", "text", "c14n"] | None = None,
     *,
     xml_declaration: bool | None = None,
     default_namespace: str | None = None,
     short_empty_elements: bool = True,
 ) -> list[Any]: ...
-def dump(elem: Element) -> None: ...
+def dump(elem: Element | ElementTree[Any]) -> None: ...
 
 if sys.version_info >= (3, 9):
-    def indent(tree: Element | ElementTree, space: str = "  ", level: int = 0) -> None: ...
+    def indent(tree: Element | ElementTree[Any], space: str = "  ", level: int = 0) -> None: ...
 
-def parse(source: _FileRead, parser: XMLParser | None = None) -> ElementTree: ...
+def parse(source: _FileRead, parser: XMLParser[Any] | None = None) -> ElementTree[Element]: ...
 
-class _IterParseIterator(Iterator[tuple[str, Any]]):
-    def __next__(self) -> tuple[str, Any]: ...
+# This class is defined inside the body of iterparse
+@type_check_only
+class _IterParseIterator(Iterator[tuple[str, Element]], Protocol):
+    def __next__(self) -> tuple[str, Element]: ...
     if sys.version_info >= (3, 13):
         def close(self) -> None: ...
     if sys.version_info >= (3, 11):
@@ -249,13 +271,13 @@ class _IterParseIterator(Iterator[tuple[str, Any]]):
 
 def iterparse(source: _FileRead, events: Sequence[str] | None = None, parser: XMLParser | None = None) -> _IterParseIterator: ...
 
-class XMLPullParser:
-    def __init__(self, events: Sequence[str] | None = None, *, _parser: XMLParser | None = None) -> None: ...
+_EventQueue: TypeAlias = tuple[str] | tuple[str, tuple[str, str]] | tuple[str, None]
+
+class XMLPullParser(Generic[_E]):
+    def __init__(self, events: Sequence[str] | None = None, *, _parser: XMLParser[_E] | None = None) -> None: ...
     def feed(self, data: str | ReadableBuffer) -> None: ...
     def close(self) -> None: ...
-    # Second element in the tuple could be `Element`, `tuple[str, str]` or `None`.
-    # Use `Any` to avoid false-positive errors.
-    def read_events(self) -> Iterator[tuple[str, Any]]: ...
+    def read_events(self) -> Iterator[_EventQueue | tuple[str, _E]]: ...
     def flush(self) -> None: ...
 
 def XML(text: str | ReadableBuffer, parser: XMLParser | None = None) -> Element: ...
@@ -281,12 +303,12 @@ class TreeBuilder:
     # comment_factory can take None because passing None to Comment is not an error
     def __init__(
         self,
-        element_factory: _ElementFactory | None = ...,
+        element_factory: _ElementFactory | None = None,
         *,
-        comment_factory: Callable[[str | None], Element] | None = ...,
-        pi_factory: Callable[[str, str | None], Element] | None = ...,
-        insert_comments: bool = ...,
-        insert_pis: bool = ...,
+        comment_factory: Callable[[str | None], Element[Any]] | None = None,
+        pi_factory: Callable[[str, str | None], Element[Any]] | None = None,
+        insert_comments: bool = False,
+        insert_pis: bool = False,
     ) -> None: ...
     insert_comments: bool
     insert_pis: bool
@@ -298,8 +320,8 @@ class TreeBuilder:
     def start(self, tag: Any, attrs: dict[Any, Any], /) -> Element: ...
     def end(self, tag: str, /) -> Element: ...
     # These two methods have pos-only parameters in the C implementation
-    def comment(self, text: str | None, /) -> Element: ...
-    def pi(self, target: str, text: str | None = None, /) -> Element: ...
+    def comment(self, text: str | None, /) -> Element[Any]: ...
+    def pi(self, target: str, text: str | None = None, /) -> Element[Any]: ...
 
 class C14NWriterTarget:
     def __init__(
@@ -321,13 +343,33 @@ class C14NWriterTarget:
     def comment(self, text: str) -> None: ...
     def pi(self, target: str, data: str) -> None: ...
 
-class XMLParser:
-    parser: Any
-    target: Any
+# The target type is tricky, because the implementation doesn't
+# require any particular attribute to be present. This documents the attributes
+# that can be present, but uncommenting any of them would require them.
+class _Target(Protocol):
+    # start: Callable[str, dict[str, str], Any] | None
+    # end: Callable[[str], Any] | None
+    # start_ns: Callable[[str, str], Any] | None
+    # end_ns: Callable[[str], Any] | None
+    # data: Callable[[str], Any] | None
+    # comment: Callable[[str], Any]
+    # pi: Callable[[str, str], Any] | None
+    # close: Callable[[], Any] | None
+    ...
+
+_E = TypeVar("_E", default=Element)
+
+# This is generic because the return type of close() depends on the target.
+# The default target is TreeBuilder, which returns Element.
+# C14NWriterTarget does not implement a close method, so using it results
+# in a type of XMLParser[None].
+class XMLParser(Generic[_E]):
+    parser: XMLParserType
+    target: _Target
     # TODO-what is entity used for???
-    entity: Any
+    entity: dict[str, str]
     version: str
-    def __init__(self, *, target: Any = ..., encoding: str | None = ...) -> None: ...
-    def close(self) -> Any: ...
+    def __init__(self, *, target: _Target | None = None, encoding: str | None = None) -> None: ...
+    def close(self) -> _E: ...
     def feed(self, data: str | ReadableBuffer, /) -> None: ...
     def flush(self) -> None: ...
diff --git a/stdlib/xml/sax/_exceptions.pyi b/stdlib/xml/sax/_exceptions.pyi
index 8a437a971f13..e9cc8856a9c8 100644
--- a/stdlib/xml/sax/_exceptions.pyi
+++ b/stdlib/xml/sax/_exceptions.pyi
@@ -4,15 +4,15 @@ from xml.sax.xmlreader import Locator
 class SAXException(Exception):
     def __init__(self, msg: str, exception: Exception | None = None) -> None: ...
     def getMessage(self) -> str: ...
-    def getException(self) -> Exception: ...
+    def getException(self) -> Exception | None: ...
     def __getitem__(self, ix: object) -> NoReturn: ...
 
 class SAXParseException(SAXException):
     def __init__(self, msg: str, exception: Exception | None, locator: Locator) -> None: ...
-    def getColumnNumber(self) -> int: ...
-    def getLineNumber(self) -> int: ...
-    def getPublicId(self): ...
-    def getSystemId(self): ...
+    def getColumnNumber(self) -> int | None: ...
+    def getLineNumber(self) -> int | None: ...
+    def getPublicId(self) -> str | None: ...
+    def getSystemId(self) -> str | None: ...
 
 class SAXNotRecognizedException(SAXException): ...
 class SAXNotSupportedException(SAXException): ...
diff --git a/stdlib/xml/sax/expatreader.pyi b/stdlib/xml/sax/expatreader.pyi
index 0f7bda5872c0..6a68f52f0e99 100644
--- a/stdlib/xml/sax/expatreader.pyi
+++ b/stdlib/xml/sax/expatreader.pyi
@@ -1,53 +1,82 @@
 import sys
-from _typeshed import Unused
-from xml.sax import xmlreader
+from _typeshed import ReadableBuffer
+from collections.abc import Mapping
+from typing import Any, Literal, overload
+from typing_extensions import TypeAlias
+from xml.sax import _Source, xmlreader
+from xml.sax.handler import _ContentHandlerProtocol
+
+if sys.version_info >= (3, 10):
+    from xml.sax.handler import LexicalHandler
+
+_BoolType: TypeAlias = Literal[0, 1] | bool
 
 version: str
 AttributesImpl = xmlreader.AttributesImpl
 AttributesNSImpl = xmlreader.AttributesNSImpl
 
-class _ClosedParser: ...
+class _ClosedParser:
+    ErrorColumnNumber: int
+    ErrorLineNumber: int
 
 class ExpatLocator(xmlreader.Locator):
     def __init__(self, parser: ExpatParser) -> None: ...
-    def getColumnNumber(self) -> int: ...
+    def getColumnNumber(self) -> int | None: ...
     def getLineNumber(self) -> int: ...
-    def getPublicId(self): ...
-    def getSystemId(self): ...
+    def getPublicId(self) -> str | None: ...
+    def getSystemId(self) -> str | None: ...
 
 class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator):
-    def __init__(self, namespaceHandling: int = 0, bufsize: int = 65516) -> None: ...
-    def parse(self, source) -> None: ...
-    def prepareParser(self, source) -> None: ...
-    def setContentHandler(self, handler) -> None: ...
-    def getFeature(self, name: str): ...
-    def setFeature(self, name: str, state) -> None: ...
-    def getProperty(self, name: str): ...
-    def setProperty(self, name: str, value) -> None: ...
+    def __init__(self, namespaceHandling: _BoolType = 0, bufsize: int = 65516) -> None: ...
+    def parse(self, source: xmlreader.InputSource | _Source) -> None: ...
+    def prepareParser(self, source: xmlreader.InputSource) -> None: ...
+    def setContentHandler(self, handler: _ContentHandlerProtocol) -> None: ...
+    def getFeature(self, name: str) -> _BoolType: ...
+    def setFeature(self, name: str, state: _BoolType) -> None: ...
+    if sys.version_info >= (3, 10):
+        @overload
+        def getProperty(self, name: Literal["http://xml.org/sax/properties/lexical-handler"]) -> LexicalHandler | None: ...
+
+    @overload
+    def getProperty(self, name: Literal["http://www.python.org/sax/properties/interning-dict"]) -> dict[str, Any] | None: ...
+    @overload
+    def getProperty(self, name: Literal["http://xml.org/sax/properties/xml-string"]) -> bytes | None: ...
+    @overload
+    def getProperty(self, name: str) -> object: ...
+    if sys.version_info >= (3, 10):
+        @overload
+        def setProperty(self, name: Literal["http://xml.org/sax/properties/lexical-handler"], value: LexicalHandler) -> None: ...
+
+    @overload
+    def setProperty(
+        self, name: Literal["http://www.python.org/sax/properties/interning-dict"], value: dict[str, Any]
+    ) -> None: ...
+    @overload
+    def setProperty(self, name: str, value: object) -> None: ...
     if sys.version_info >= (3, 9):
-        def feed(self, data, isFinal: bool = False) -> None: ...
+        def feed(self, data: str | ReadableBuffer, isFinal: bool = False) -> None: ...
     else:
-        def feed(self, data, isFinal: int = 0) -> None: ...
+        def feed(self, data: str | ReadableBuffer, isFinal: _BoolType = 0) -> None: ...
 
     def flush(self) -> None: ...
     def close(self) -> None: ...
     def reset(self) -> None: ...
     def getColumnNumber(self) -> int | None: ...
     def getLineNumber(self) -> int: ...
-    def getPublicId(self): ...
-    def getSystemId(self): ...
-    def start_element(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ...
+    def getPublicId(self) -> str | None: ...
+    def getSystemId(self) -> str | None: ...
+    def start_element(self, name: str, attrs: Mapping[str, str]) -> None: ...
     def end_element(self, name: str) -> None: ...
-    def start_element_ns(self, name: str, attrs) -> None: ...
+    def start_element_ns(self, name: str, attrs: Mapping[str, str]) -> None: ...
     def end_element_ns(self, name: str) -> None: ...
     def processing_instruction(self, target: str, data: str) -> None: ...
     def character_data(self, data: str) -> None: ...
     def start_namespace_decl(self, prefix: str | None, uri: str) -> None: ...
     def end_namespace_decl(self, prefix: str | None) -> None: ...
-    def start_doctype_decl(self, name: str, sysid: str | None, pubid: str | None, has_internal_subset: Unused) -> None: ...
-    def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name) -> None: ...
-    def notation_decl(self, name, base, sysid, pubid) -> None: ...
-    def external_entity_ref(self, context, base, sysid, pubid): ...
+    def start_doctype_decl(self, name: str, sysid: str | None, pubid: str | None, has_internal_subset: bool) -> None: ...
+    def unparsed_entity_decl(self, name: str, base: str | None, sysid: str, pubid: str | None, notation_name: str) -> None: ...
+    def notation_decl(self, name: str, base: str | None, sysid: str, pubid: str | None) -> None: ...
+    def external_entity_ref(self, context: str, base: str | None, sysid: str, pubid: str | None) -> int: ...
     def skipped_entity_handler(self, name: str, is_pe: bool) -> None: ...
 
 def create_parser(namespaceHandling: int = 0, bufsize: int = 65516) -> ExpatParser: ...
diff --git a/stdlib/xml/sax/handler.pyi b/stdlib/xml/sax/handler.pyi
index 7b7c69048efd..550911734596 100644
--- a/stdlib/xml/sax/handler.pyi
+++ b/stdlib/xml/sax/handler.pyi
@@ -1,14 +1,36 @@
 import sys
-from typing import NoReturn
+from typing import Literal, NoReturn, Protocol, type_check_only
 from xml.sax import xmlreader
 
 version: str
 
+@type_check_only
+class _ErrorHandlerProtocol(Protocol):  # noqa: Y046  # Protocol is not used
+    def error(self, exception: BaseException) -> NoReturn: ...
+    def fatalError(self, exception: BaseException) -> NoReturn: ...
+    def warning(self, exception: BaseException) -> None: ...
+
 class ErrorHandler:
     def error(self, exception: BaseException) -> NoReturn: ...
     def fatalError(self, exception: BaseException) -> NoReturn: ...
     def warning(self, exception: BaseException) -> None: ...
 
+@type_check_only
+class _ContentHandlerProtocol(Protocol):  # noqa: Y046  # Protocol is not used
+    def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ...
+    def startDocument(self) -> None: ...
+    def endDocument(self) -> None: ...
+    def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ...
+    def endPrefixMapping(self, prefix: str | None) -> None: ...
+    def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ...
+    def endElement(self, name: str) -> None: ...
+    def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ...
+    def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ...
+    def characters(self, content: str) -> None: ...
+    def ignorableWhitespace(self, whitespace: str) -> None: ...
+    def processingInstruction(self, target: str, data: str) -> None: ...
+    def skippedEntity(self, name: str) -> None: ...
+
 class ContentHandler:
     def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ...
     def startDocument(self) -> None: ...
@@ -17,19 +39,28 @@ class ContentHandler:
     def endPrefixMapping(self, prefix: str | None) -> None: ...
     def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ...
     def endElement(self, name: str) -> None: ...
-    def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ...
-    def endElementNS(self, name: tuple[str, str], qname: str) -> None: ...
+    def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ...
+    def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ...
     def characters(self, content: str) -> None: ...
     def ignorableWhitespace(self, whitespace: str) -> None: ...
     def processingInstruction(self, target: str, data: str) -> None: ...
     def skippedEntity(self, name: str) -> None: ...
 
+@type_check_only
+class _DTDHandlerProtocol(Protocol):  # noqa: Y046  # Protocol is not used
+    def notationDecl(self, name: str, publicId: str | None, systemId: str) -> None: ...
+    def unparsedEntityDecl(self, name: str, publicId: str | None, systemId: str, ndata: str) -> None: ...
+
 class DTDHandler:
-    def notationDecl(self, name, publicId, systemId): ...
-    def unparsedEntityDecl(self, name, publicId, systemId, ndata): ...
+    def notationDecl(self, name: str, publicId: str | None, systemId: str) -> None: ...
+    def unparsedEntityDecl(self, name: str, publicId: str | None, systemId: str, ndata: str) -> None: ...
+
+@type_check_only
+class _EntityResolverProtocol(Protocol):  # noqa: Y046  # Protocol is not used
+    def resolveEntity(self, publicId: str | None, systemId: str) -> str: ...
 
 class EntityResolver:
-    def resolveEntity(self, publicId, systemId): ...
+    def resolveEntity(self, publicId: str | None, systemId: str) -> str: ...
 
 feature_namespaces: str
 feature_namespace_prefixes: str
@@ -38,18 +69,18 @@ feature_validation: str
 feature_external_ges: str
 feature_external_pes: str
 all_features: list[str]
-property_lexical_handler: str
-property_declaration_handler: str
-property_dom_node: str
-property_xml_string: str
-property_encoding: str
-property_interning_dict: str
+property_lexical_handler: Literal["http://xml.org/sax/properties/lexical-handler"]
+property_declaration_handler: Literal["http://xml.org/sax/properties/declaration-handler"]
+property_dom_node: Literal["http://xml.org/sax/properties/dom-node"]
+property_xml_string: Literal["http://xml.org/sax/properties/xml-string"]
+property_encoding: Literal["http://www.python.org/sax/properties/encoding"]
+property_interning_dict: Literal["http://www.python.org/sax/properties/interning-dict"]
 all_properties: list[str]
 
 if sys.version_info >= (3, 10):
     class LexicalHandler:
-        def comment(self, content: str) -> object: ...
-        def startDTD(self, name: str, public_id: str | None, system_id: str | None) -> object: ...
-        def endDTD(self) -> object: ...
-        def startCDATA(self) -> object: ...
-        def endCDATA(self) -> object: ...
+        def comment(self, content: str) -> None: ...
+        def startDTD(self, name: str, public_id: str | None, system_id: str | None) -> None: ...
+        def endDTD(self) -> None: ...
+        def startCDATA(self) -> None: ...
+        def endCDATA(self) -> None: ...
diff --git a/stdlib/xml/sax/saxutils.pyi b/stdlib/xml/sax/saxutils.pyi
index 528f35963947..a29588faae2a 100644
--- a/stdlib/xml/sax/saxutils.pyi
+++ b/stdlib/xml/sax/saxutils.pyi
@@ -2,6 +2,7 @@ from _typeshed import SupportsWrite
 from codecs import StreamReaderWriter, StreamWriter
 from collections.abc import Mapping
 from io import RawIOBase, TextIOBase
+from typing import Literal, NoReturn
 from xml.sax import _Source, handler, xmlreader
 
 def escape(data: str, entities: Mapping[str, str] = {}) -> str: ...
@@ -15,23 +16,26 @@ class XMLGenerator(handler.ContentHandler):
         encoding: str = "iso-8859-1",
         short_empty_elements: bool = False,
     ) -> None: ...
+    def _qname(self, name: tuple[str | None, str]) -> str: ...
     def startDocument(self) -> None: ...
     def endDocument(self) -> None: ...
     def startPrefixMapping(self, prefix: str | None, uri: str) -> None: ...
     def endPrefixMapping(self, prefix: str | None) -> None: ...
     def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ...
     def endElement(self, name: str) -> None: ...
-    def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ...
-    def endElementNS(self, name: tuple[str, str], qname: str) -> None: ...
+    def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ...
+    def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ...
     def characters(self, content: str) -> None: ...
     def ignorableWhitespace(self, content: str) -> None: ...
     def processingInstruction(self, target: str, data: str) -> None: ...
 
 class XMLFilterBase(xmlreader.XMLReader):
     def __init__(self, parent: xmlreader.XMLReader | None = None) -> None: ...
-    def error(self, exception): ...
-    def fatalError(self, exception): ...
-    def warning(self, exception): ...
+    # ErrorHandler methods
+    def error(self, exception: BaseException) -> NoReturn: ...
+    def fatalError(self, exception: BaseException) -> NoReturn: ...
+    def warning(self, exception: BaseException) -> None: ...
+    # ContentHandler methods
     def setDocumentLocator(self, locator: xmlreader.Locator) -> None: ...
     def startDocument(self) -> None: ...
     def endDocument(self) -> None: ...
@@ -39,22 +43,26 @@ class XMLFilterBase(xmlreader.XMLReader):
     def endPrefixMapping(self, prefix: str | None) -> None: ...
     def startElement(self, name: str, attrs: xmlreader.AttributesImpl) -> None: ...
     def endElement(self, name: str) -> None: ...
-    def startElementNS(self, name: tuple[str, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ...
-    def endElementNS(self, name: tuple[str, str], qname: str) -> None: ...
+    def startElementNS(self, name: tuple[str | None, str], qname: str | None, attrs: xmlreader.AttributesNSImpl) -> None: ...
+    def endElementNS(self, name: tuple[str | None, str], qname: str | None) -> None: ...
     def characters(self, content: str) -> None: ...
     def ignorableWhitespace(self, chars: str) -> None: ...
     def processingInstruction(self, target: str, data: str) -> None: ...
     def skippedEntity(self, name: str) -> None: ...
-    def notationDecl(self, name, publicId, systemId): ...
-    def unparsedEntityDecl(self, name, publicId, systemId, ndata): ...
-    def resolveEntity(self, publicId, systemId): ...
-    def parse(self, source: _Source) -> None: ...
-    def setLocale(self, locale): ...
-    def getFeature(self, name: str) -> object: ...
-    def setFeature(self, name: str, state: object) -> None: ...
+    # DTDHandler methods
+    def notationDecl(self, name: str, publicId: str | None, systemId: str) -> None: ...
+    def unparsedEntityDecl(self, name: str, publicId: str | None, systemId: str, ndata: str) -> None: ...
+    # EntityResolver methods
+    def resolveEntity(self, publicId: str | None, systemId: str) -> str: ...
+    # XMLReader methods
+    def parse(self, source: xmlreader.InputSource | _Source) -> None: ...
+    def setLocale(self, locale: str) -> None: ...
+    def getFeature(self, name: str) -> Literal[1, 0] | bool: ...
+    def setFeature(self, name: str, state: Literal[1, 0] | bool) -> None: ...
     def getProperty(self, name: str) -> object: ...
     def setProperty(self, name: str, value: object) -> None: ...
-    def getParent(self) -> xmlreader.XMLReader: ...
+    # XMLFilter methods
+    def getParent(self) -> xmlreader.XMLReader | None: ...
     def setParent(self, parent: xmlreader.XMLReader) -> None: ...
 
-def prepare_input_source(source, base=""): ...
+def prepare_input_source(source: xmlreader.InputSource | _Source, base: str = "") -> xmlreader.InputSource: ...
diff --git a/stdlib/xml/sax/xmlreader.pyi b/stdlib/xml/sax/xmlreader.pyi
index 2ccbc95bbef0..e7d04ddeadb8 100644
--- a/stdlib/xml/sax/xmlreader.pyi
+++ b/stdlib/xml/sax/xmlreader.pyi
@@ -1,87 +1,90 @@
+from _typeshed import ReadableBuffer
 from collections.abc import Mapping
-from typing import overload
+from typing import Generic, Literal, TypeVar, overload
 from typing_extensions import Self, TypeAlias
-from xml.sax.handler import ContentHandler, DTDHandler, EntityResolver, ErrorHandler
+from xml.sax import _Source, _SupportsReadClose
+from xml.sax.handler import _ContentHandlerProtocol, _DTDHandlerProtocol, _EntityResolverProtocol, _ErrorHandlerProtocol
 
 class XMLReader:
-    def parse(self, source): ...
-    def getContentHandler(self) -> ContentHandler: ...
-    def setContentHandler(self, handler: ContentHandler) -> None: ...
-    def getDTDHandler(self) -> DTDHandler: ...
-    def setDTDHandler(self, handler: DTDHandler) -> None: ...
-    def getEntityResolver(self) -> EntityResolver: ...
-    def setEntityResolver(self, resolver: EntityResolver) -> None: ...
-    def getErrorHandler(self) -> ErrorHandler: ...
-    def setErrorHandler(self, handler: ErrorHandler) -> None: ...
-    def setLocale(self, locale): ...
-    def getFeature(self, name: str) -> object: ...
-    def setFeature(self, name: str, state: object) -> None: ...
+    def parse(self, source: InputSource | _Source) -> None: ...
+    def getContentHandler(self) -> _ContentHandlerProtocol: ...
+    def setContentHandler(self, handler: _ContentHandlerProtocol) -> None: ...
+    def getDTDHandler(self) -> _DTDHandlerProtocol: ...
+    def setDTDHandler(self, handler: _DTDHandlerProtocol) -> None: ...
+    def getEntityResolver(self) -> _EntityResolverProtocol: ...
+    def setEntityResolver(self, resolver: _EntityResolverProtocol) -> None: ...
+    def getErrorHandler(self) -> _ErrorHandlerProtocol: ...
+    def setErrorHandler(self, handler: _ErrorHandlerProtocol) -> None: ...
+    def setLocale(self, locale: str) -> None: ...
+    def getFeature(self, name: str) -> Literal[0, 1] | bool: ...
+    def setFeature(self, name: str, state: Literal[0, 1] | bool) -> None: ...
     def getProperty(self, name: str) -> object: ...
     def setProperty(self, name: str, value: object) -> None: ...
 
 class IncrementalParser(XMLReader):
     def __init__(self, bufsize: int = 65536) -> None: ...
-    def parse(self, source): ...
-    def feed(self, data): ...
-    def prepareParser(self, source): ...
-    def close(self): ...
-    def reset(self): ...
+    def parse(self, source: InputSource | _Source) -> None: ...
+    def feed(self, data: str | ReadableBuffer) -> None: ...
+    def prepareParser(self, source: InputSource) -> None: ...
+    def close(self) -> None: ...
+    def reset(self) -> None: ...
 
 class Locator:
-    def getColumnNumber(self): ...
-    def getLineNumber(self): ...
-    def getPublicId(self): ...
-    def getSystemId(self): ...
+    def getColumnNumber(self) -> int | None: ...
+    def getLineNumber(self) -> int | None: ...
+    def getPublicId(self) -> str | None: ...
+    def getSystemId(self) -> str | None: ...
 
 class InputSource:
     def __init__(self, system_id: str | None = None) -> None: ...
-    def setPublicId(self, public_id): ...
-    def getPublicId(self): ...
-    def setSystemId(self, system_id): ...
-    def getSystemId(self): ...
-    def setEncoding(self, encoding): ...
-    def getEncoding(self): ...
-    def setByteStream(self, bytefile): ...
-    def getByteStream(self): ...
-    def setCharacterStream(self, charfile): ...
-    def getCharacterStream(self): ...
+    def setPublicId(self, public_id: str | None) -> None: ...
+    def getPublicId(self) -> str | None: ...
+    def setSystemId(self, system_id: str | None) -> None: ...
+    def getSystemId(self) -> str | None: ...
+    def setEncoding(self, encoding: str | None) -> None: ...
+    def getEncoding(self) -> str | None: ...
+    def setByteStream(self, bytefile: _SupportsReadClose[bytes] | None) -> None: ...
+    def getByteStream(self) -> _SupportsReadClose[bytes] | None: ...
+    def setCharacterStream(self, charfile: _SupportsReadClose[str] | None) -> None: ...
+    def getCharacterStream(self) -> _SupportsReadClose[str] | None: ...
 
-class AttributesImpl:
-    def __init__(self, attrs: Mapping[str, str]) -> None: ...
+_AttrKey = TypeVar("_AttrKey", default=str)
+
+class AttributesImpl(Generic[_AttrKey]):
+    def __init__(self, attrs: Mapping[_AttrKey, str]) -> None: ...
     def getLength(self) -> int: ...
     def getType(self, name: str) -> str: ...
-    def getValue(self, name: str) -> str: ...
+    def getValue(self, name: _AttrKey) -> str: ...
     def getValueByQName(self, name: str) -> str: ...
-    def getNameByQName(self, name: str) -> str: ...
-    def getQNameByName(self, name: str) -> str: ...
-    def getNames(self) -> list[str]: ...
+    def getNameByQName(self, name: str) -> _AttrKey: ...
+    def getQNameByName(self, name: _AttrKey) -> str: ...
+    def getNames(self) -> list[_AttrKey]: ...
     def getQNames(self) -> list[str]: ...
     def __len__(self) -> int: ...
-    def __getitem__(self, name: str) -> str: ...
-    def keys(self) -> list[str]: ...
-    def __contains__(self, name: str) -> bool: ...
+    def __getitem__(self, name: _AttrKey) -> str: ...
+    def keys(self) -> list[_AttrKey]: ...
+    def __contains__(self, name: _AttrKey) -> bool: ...
     @overload
-    def get(self, name: str, alternative: None = None) -> str | None: ...
+    def get(self, name: _AttrKey, alternative: None = None) -> str | None: ...
     @overload
-    def get(self, name: str, alternative: str) -> str: ...
+    def get(self, name: _AttrKey, alternative: str) -> str: ...
     def copy(self) -> Self: ...
-    def items(self) -> list[tuple[str, str]]: ...
+    def items(self) -> list[tuple[_AttrKey, str]]: ...
     def values(self) -> list[str]: ...
 
 _NSName: TypeAlias = tuple[str | None, str]
 
-class AttributesNSImpl(AttributesImpl):
+class AttributesNSImpl(AttributesImpl[_NSName]):
     def __init__(self, attrs: Mapping[_NSName, str], qnames: Mapping[_NSName, str]) -> None: ...
-    def getType(self, name: _NSName) -> str: ...  # type: ignore[override]
-    def getValue(self, name: _NSName) -> str: ...  # type: ignore[override]
-    def getNameByQName(self, name: str) -> _NSName: ...  # type: ignore[override]
-    def getQNameByName(self, name: _NSName) -> str: ...  # type: ignore[override]
-    def getNames(self) -> list[_NSName]: ...  # type: ignore[override]
-    def __getitem__(self, name: _NSName) -> str: ...  # type: ignore[override]
-    def keys(self) -> list[_NSName]: ...  # type: ignore[override]
-    def __contains__(self, name: _NSName) -> bool: ...  # type: ignore[override]
-    @overload  # type: ignore[override]
+    def getValue(self, name: _NSName) -> str: ...
+    def getNameByQName(self, name: str) -> _NSName: ...
+    def getQNameByName(self, name: _NSName) -> str: ...
+    def getNames(self) -> list[_NSName]: ...
+    def __getitem__(self, name: _NSName) -> str: ...
+    def keys(self) -> list[_NSName]: ...
+    def __contains__(self, name: _NSName) -> bool: ...
+    @overload
     def get(self, name: _NSName, alternative: None = None) -> str | None: ...
     @overload
     def get(self, name: _NSName, alternative: str) -> str: ...
-    def items(self) -> list[tuple[_NSName, str]]: ...  # type: ignore[override]
+    def items(self) -> list[tuple[_NSName, str]]: ...