From 49b717ca52bf0781a538b04c0d76a5513f7119b8 Mon Sep 17 00:00:00 2001 From: Screwtapello Date: Sat, 23 Sep 2023 13:08:13 +1000 Subject: [PATCH] stdlib/xml/sax: Add type annotations (#10606) * stdlib/xml/sax: Type annotations for commonly used methods. * stdlib/xml/sax: More annotations. It turns out SAX's definition of a "qname" is exactly the opposite of ElementTree's. With that understanding, let's annotate the Attributes*Impl classes too. * stdlib/xml/sax: I better understand what AttributesNSImpl is doing now. * Update third-party library stubs to agree with the new SAX annotations. --- stdlib/xml/sax/__init__.pyi | 21 ++++---- stdlib/xml/sax/handler.pyi | 27 +++++----- stdlib/xml/sax/saxutils.pyi | 64 ++++++++++++------------ stdlib/xml/sax/xmlreader.pyi | 82 ++++++++++++++++++------------- stubs/netaddr/netaddr/ip/iana.pyi | 4 +- stubs/untangle/untangle.pyi | 4 +- 6 files changed, 109 insertions(+), 93 deletions(-) diff --git a/stdlib/xml/sax/__init__.pyi b/stdlib/xml/sax/__init__.pyi index 8bcf902df..f726eae05 100644 --- a/stdlib/xml/sax/__init__.pyi +++ b/stdlib/xml/sax/__init__.pyi @@ -2,12 +2,18 @@ import sys from _typeshed import ReadableBuffer, StrPath, SupportsRead, _T_co from collections.abc import Iterable from typing import Any, NoReturn, Protocol +from typing_extensions import TypeAlias from xml.sax.handler import ContentHandler as ContentHandler, ErrorHandler as ErrorHandler from xml.sax.xmlreader import Locator, XMLReader class _SupportsReadClose(SupportsRead[_T_co], Protocol[_T_co]): def close(self) -> None: ... +if sys.version_info >= (3, 8): + _Source: TypeAlias = StrPath | _SupportsReadClose[bytes] | _SupportsReadClose[str] +else: + _Source: TypeAlias = str | _SupportsReadClose[bytes] | _SupportsReadClose[str] + class SAXException(Exception): def __init__(self, msg: str, exception: Exception | None = None) -> None: ... def getMessage(self) -> str: ... @@ -28,20 +34,13 @@ class SAXReaderNotAvailable(SAXNotSupportedException): ... default_parser_list: list[str] if sys.version_info >= (3, 8): + def make_parser(parser_list: Iterable[str] = ()) -> XMLReader: ... - def parse( - source: StrPath | _SupportsReadClose[bytes] | _SupportsReadClose[str], - handler: ContentHandler, - errorHandler: ErrorHandler = ..., - ) -> None: ... else: - def make_parser(parser_list: list[str] = []) -> XMLReader: ... - def parse( - source: str | _SupportsReadClose[bytes] | _SupportsReadClose[str], - handler: ContentHandler, - errorHandler: ErrorHandler = ..., - ) -> None: ... + def make_parser(parser_list: list[str] = []) -> XMLReader: ... + +def parse(source: _Source, handler: ContentHandler, errorHandler: ErrorHandler = ...) -> None: ... def parseString(string: ReadableBuffer | str, handler: ContentHandler, errorHandler: ErrorHandler | None = ...) -> None: ... def _create_parser(parser_name: str) -> XMLReader: ... diff --git a/stdlib/xml/sax/handler.pyi b/stdlib/xml/sax/handler.pyi index 63b725bd6..30fe31d51 100644 --- a/stdlib/xml/sax/handler.pyi +++ b/stdlib/xml/sax/handler.pyi @@ -1,5 +1,6 @@ import sys from typing import NoReturn +from xml.sax import xmlreader version: str @@ -9,19 +10,19 @@ class ErrorHandler: def warning(self, exception: BaseException) -> None: ... class ContentHandler: - def setDocumentLocator(self, locator): ... - def startDocument(self): ... - def endDocument(self): ... - def startPrefixMapping(self, prefix, uri): ... - def endPrefixMapping(self, prefix): ... - def startElement(self, name, attrs): ... - def endElement(self, name): ... - def startElementNS(self, name, qname, attrs): ... - def endElementNS(self, name, qname): ... - def characters(self, content): ... - def ignorableWhitespace(self, whitespace): ... - def processingInstruction(self, target, data): ... - def skippedEntity(self, name): ... + 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) -> 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 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 DTDHandler: def notationDecl(self, name, publicId, systemId): ... diff --git a/stdlib/xml/sax/saxutils.pyi b/stdlib/xml/sax/saxutils.pyi index 0d9223770..06e03a1e4 100644 --- a/stdlib/xml/sax/saxutils.pyi +++ b/stdlib/xml/sax/saxutils.pyi @@ -2,7 +2,7 @@ from _typeshed import SupportsWrite from codecs import StreamReaderWriter, StreamWriter from collections.abc import Mapping from io import RawIOBase, TextIOBase -from xml.sax import handler, xmlreader +from xml.sax import _Source, handler, xmlreader def escape(data: str, entities: Mapping[str, str] = {}) -> str: ... def unescape(data: str, entities: Mapping[str, str] = {}) -> str: ... @@ -15,46 +15,46 @@ class XMLGenerator(handler.ContentHandler): encoding: str = "iso-8859-1", short_empty_elements: bool = False, ) -> None: ... - def startDocument(self): ... - def endDocument(self): ... - def startPrefixMapping(self, prefix, uri): ... - def endPrefixMapping(self, prefix): ... - def startElement(self, name, attrs): ... - def endElement(self, name): ... - def startElementNS(self, name, qname, attrs): ... - def endElementNS(self, name, qname): ... - def characters(self, content): ... - def ignorableWhitespace(self, content): ... - def processingInstruction(self, target, data): ... + 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 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): ... - def setDocumentLocator(self, locator): ... - def startDocument(self): ... - def endDocument(self): ... - def startPrefixMapping(self, prefix, uri): ... - def endPrefixMapping(self, prefix): ... - def startElement(self, name, attrs): ... - def endElement(self, name): ... - def startElementNS(self, name, qname, attrs): ... - def endElementNS(self, name, qname): ... - def characters(self, content): ... - def ignorableWhitespace(self, chars): ... - def processingInstruction(self, target, data): ... - def skippedEntity(self, name): ... + 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, str], qname: str, attrs: xmlreader.AttributesNSImpl) -> None: ... + def endElementNS(self, name: tuple[str, str], qname: str) -> 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): ... + def parse(self, source: _Source) -> None: ... def setLocale(self, locale): ... - def getFeature(self, name): ... - def setFeature(self, name, state): ... - def getProperty(self, name): ... - def setProperty(self, name, value): ... - def getParent(self): ... - def setParent(self, parent): ... + def getFeature(self, name: str) -> object: ... + def setFeature(self, name: str, state: object) -> None: ... + def getProperty(self, name: str) -> object: ... + def setProperty(self, name: str, value: object) -> None: ... + def getParent(self) -> xmlreader.XMLReader: ... + def setParent(self, parent: xmlreader.XMLReader) -> None: ... def prepare_input_source(source, base=""): ... diff --git a/stdlib/xml/sax/xmlreader.pyi b/stdlib/xml/sax/xmlreader.pyi index 0bf167b04..74d2efb01 100644 --- a/stdlib/xml/sax/xmlreader.pyi +++ b/stdlib/xml/sax/xmlreader.pyi @@ -1,20 +1,23 @@ from collections.abc import Mapping +from typing import overload +from typing_extensions import Self, TypeAlias +from xml.sax.handler import ContentHandler, DTDHandler, EntityResolver, ErrorHandler class XMLReader: def parse(self, source): ... - def getContentHandler(self): ... - def setContentHandler(self, handler): ... - def getDTDHandler(self): ... - def setDTDHandler(self, handler): ... - def getEntityResolver(self): ... - def setEntityResolver(self, resolver): ... - def getErrorHandler(self): ... - def setErrorHandler(self, handler): ... + 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): ... - def setFeature(self, name, state): ... - def getProperty(self, name): ... - def setProperty(self, name, value): ... + def getFeature(self, name: str) -> object: ... + def setFeature(self, name: str, state: object) -> 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: ... @@ -45,27 +48,40 @@ class InputSource: class AttributesImpl: def __init__(self, attrs: Mapping[str, str]) -> None: ... - def getLength(self): ... - def getType(self, name): ... - def getValue(self, name): ... - def getValueByQName(self, name): ... - def getNameByQName(self, name): ... - def getQNameByName(self, name): ... - def getNames(self): ... - def getQNames(self): ... + def getLength(self) -> int: ... + def getType(self, name: str) -> str: ... + def getValue(self, name: str) -> 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 getQNames(self) -> list[str]: ... def __len__(self) -> int: ... - def __getitem__(self, name): ... - def keys(self): ... - def __contains__(self, name): ... - def get(self, name, alternative=None): ... - def copy(self): ... - def items(self): ... - def values(self): ... + def __getitem__(self, name: str) -> str: ... + def keys(self) -> list[str]: ... + def __contains__(self, name: str) -> bool: ... + @overload + def get(self, name: str, alternative: None = None) -> str | None: ... + @overload + def get(self, name: str, alternative: str) -> str: ... + def copy(self) -> Self: ... + def items(self) -> list[tuple[str, str]]: ... + def values(self) -> list[str]: ... + +_NSName: TypeAlias = tuple[str | None, str] class AttributesNSImpl(AttributesImpl): - def __init__(self, attrs: Mapping[tuple[str, str], str], qnames: Mapping[tuple[str, str], str]) -> None: ... - def getValueByQName(self, name): ... - def getNameByQName(self, name): ... - def getQNameByName(self, name): ... - def getQNames(self): ... - def copy(self): ... + 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 get(self, name: _NSName, alternative: None = None) -> str | None: ... + @overload # type: ignore[override] + def get(self, name: _NSName, alternative: str) -> str: ... + def items(self) -> list[tuple[_NSName, str]]: ... # type: ignore[override] diff --git a/stubs/netaddr/netaddr/ip/iana.pyi b/stubs/netaddr/netaddr/ip/iana.pyi index 268023950..76e5f9990 100644 --- a/stubs/netaddr/netaddr/ip/iana.pyi +++ b/stubs/netaddr/netaddr/ip/iana.pyi @@ -3,7 +3,7 @@ from collections.abc import Callable, Mapping, MutableMapping from typing import Any from typing_extensions import TypeAlias from xml.sax import handler -from xml.sax.xmlreader import XMLReader +from xml.sax.xmlreader import AttributesImpl, XMLReader from netaddr.core import Publisher, Subscriber from netaddr.ip import IPAddress, IPNetwork, IPRange @@ -14,7 +14,7 @@ IANA_INFO: dict[str, dict[_IanaInfoKey, dict[str, str]]] class SaxRecordParser(handler.ContentHandler): def __init__(self, callback: Callable[[Mapping[str, object] | None], object] | None = None) -> None: ... - def startElement(self, name: str, attrs: Mapping[str, object]) -> None: ... + def startElement(self, name: str, attrs: AttributesImpl) -> None: ... def endElement(self, name: str) -> None: ... def characters(self, content: str) -> None: ... diff --git a/stubs/untangle/untangle.pyi b/stubs/untangle/untangle.pyi index f706aee02..3df430b10 100644 --- a/stubs/untangle/untangle.pyi +++ b/stubs/untangle/untangle.pyi @@ -1,7 +1,7 @@ from collections.abc import Iterator, Mapping from typing import Any from typing_extensions import Self -from xml.sax import handler +from xml.sax import handler, xmlreader def is_string(x: object) -> bool: ... @@ -29,7 +29,7 @@ class Handler(handler.ContentHandler): root: Element elements: list[Element] def __init__(self) -> None: ... - def startElement(self, name: str, attributes: Mapping[str, Any]) -> None: ... + def startElement(self, name: str, attributes: xmlreader.AttributesImpl) -> None: ... def endElement(self, name: str) -> None: ... def characters(self, cdata: str) -> None: ...