From fa9dc6dd69edb718857bfd970ec7d10d6d114e3c Mon Sep 17 00:00:00 2001 From: danieleades <33452915+danieleades@users.noreply.github.com> Date: Sat, 16 Mar 2024 16:08:00 +0000 Subject: [PATCH] improve type annotations in 'docutils.nodes.Element' (#11539) --- stubs/docutils/@tests/stubtest_allowlist.txt | 2 +- stubs/docutils/docutils/nodes.pyi | 112 ++++++++++++++----- 2 files changed, 86 insertions(+), 28 deletions(-) diff --git a/stubs/docutils/@tests/stubtest_allowlist.txt b/stubs/docutils/@tests/stubtest_allowlist.txt index 75de2ad81..0ff154a5a 100644 --- a/stubs/docutils/@tests/stubtest_allowlist.txt +++ b/stubs/docutils/@tests/stubtest_allowlist.txt @@ -4,8 +4,8 @@ docutils.frontend.OptionParser.__getattr__ docutils.io.FileOutput.__getattr__ docutils.io.FileOutput.__init__ docutils.languages.LanguageImporter.__getattr__ -docutils.nodes.Element.__getattr__ docutils.nodes.Element.__iter__ # doesn't exist at runtime, but the class is iterable due to __getitem__ +docutils.nodes.Element.tagname # class variable is overridden in __init__ method docutils.nodes.GenericNodeVisitor.__getattr__ # these methods take a rawsource parameter that has been deprecated and is completely ignored, so we omit it from the stub docutils.nodes.Text.__new__ diff --git a/stubs/docutils/docutils/nodes.pyi b/stubs/docutils/docutils/nodes.pyi index 5bd899485..0e5b726ad 100644 --- a/stubs/docutils/docutils/nodes.pyi +++ b/stubs/docutils/docutils/nodes.pyi @@ -104,15 +104,25 @@ class Text(Node, str): def rstrip(self, chars: str | None = None) -> str: ... def lstrip(self, chars: str | None = None) -> str: ... +_T = TypeVar("_T") + class Element(Node): + local_attributes: ClassVar[Sequence[str]] + basic_attributes: ClassVar[Sequence[str]] + list_attributes: ClassVar[Sequence[str]] + known_attributes: ClassVar[Sequence[str]] + tagname: str + child_text_separator: ClassVar[str] + attributes: dict[str, Any] children: list[Node] rawsource: str - def __init__(self, rawsource: str = "", *children: Node, **attributes): ... + def __init__(self, rawsource: str = "", *children: Node, **attributes: Any) -> None: ... + def shortrepr(self) -> str: ... + def starttag(self, quoteattr: Callable[[str], str] | None = None) -> str: ... + def endtag(self) -> str: ... + def emptytag(self) -> str: ... def __len__(self) -> int: ... def __contains__(self, key: str | Node) -> bool: ... - # '__iter__' is added as workaround, since mypy doesn't support classes that are iterable via '__getitem__' - # see https://github.com/python/typeshed/pull/10099#issuecomment-1528789395 - def __iter__(self) -> Iterator[Node]: ... @overload def __getitem__(self, key: str) -> Any: ... @overload @@ -129,19 +139,78 @@ class Element(Node): def __add__(self, other: list[Node]) -> list[Node]: ... def __radd__(self, other: list[Node]) -> list[Node]: ... def __iadd__(self, other: Node | Iterable[Node]) -> Self: ... + def astext(self) -> str: ... + def non_default_attributes(self) -> dict[str, Any]: ... + def attlist(self) -> list[tuple[str, Any]]: ... + @overload + def get(self, key: str) -> Any: ... + @overload + def get(self, key: str, failobj: _T) -> _T: ... + def hasattr(self, attr: str) -> bool: ... + def delattr(self, attr: str) -> None: ... + @overload + def setdefault(self, key: str) -> Any: ... + @overload + def setdefault(self, key: str, failobj: _T) -> Any | _T: ... + has_key = hasattr + def get_language_code(self, fallback: str = "") -> str: ... + def append(self, item: Node) -> None: ... + def extend(self, item: Iterable[Node]) -> None: ... + def insert(self, index: SupportsIndex, item: Node | Iterable[Node] | None) -> None: ... + def pop(self, i: int = -1) -> Node: ... + def remove(self, item: Node) -> None: ... + def index(self, item: Node, start: int = 0, stop: int = sys.maxsize) -> int: ... + def previous_sibling(self) -> Node | None: ... + def is_not_default(self, key: str) -> bool: ... + def update_basic_atts(self, dict_: Mapping[str, Any] | Node) -> None: ... + def append_attr_list(self, attr: str, values: Iterable[Any]) -> None: ... + def coerce_append_attr_list(self, attr: str, value) -> None: ... + def replace_attr(self, attr: str, value: Any, force: bool = True) -> None: ... + def copy_attr_convert(self, attr: str, value: Any, replace: bool = True) -> None: ... + def copy_attr_coerce(self, attr: str, value: Any, replace: bool) -> None: ... + def copy_attr_concatenate(self, attr: str, value: Any, replace: bool) -> None: ... + def copy_attr_consistent(self, attr: str, value: Any, replace: bool) -> None: ... + def update_all_atts( + self, + dict_: Mapping[str, Any] | Node, + update_fun: Callable[[Element, str, Any, bool], object] = ..., + replace: bool = True, + and_source: bool = False, + ) -> None: ... + def update_all_atts_consistantly( + self, dict_: Mapping[str, Any] | Node, replace: bool = True, and_source: bool = False + ) -> None: ... + def update_all_atts_concatenating( + self, dict_: dict[str, Any] | Node, replace: bool = True, and_source: bool = False + ) -> None: ... + def update_all_atts_coercion( + self, dict_: Mapping[str, Any] | Node, replace: bool = True, and_source: bool = False + ) -> None: ... + def update_all_atts_convert(self, dict_: Mapping[str, Any] | Node, and_source: bool = False) -> None: ... + def clear(self) -> None: ... + def replace(self, old: Node, new: Node | Sequence[Node]) -> None: ... + def replace_self(self, new: Node | Sequence[Node]) -> None: ... + def first_child_matching_class( + self, childclass: type[Node] | tuple[type[Node], ...], start: int = 0, end: int = sys.maxsize + ) -> int | None: ... + def first_child_not_matching_class( + self, childclass: type[Node] | tuple[type[Node], ...], start: int = 0, end: int = sys.maxsize + ) -> int | None: ... + def pformat(self, indent: str = " ", level: int = 0) -> str: ... def copy(self) -> Self: ... def deepcopy(self) -> Self: ... - def pformat(self, indent: str = " ", level: int = 0) -> str: ... - def astext(self) -> str: ... - def index(self, item: Node, start: int = 0, stop: int = sys.maxsize) -> int: ... - def remove(self, item: Node) -> None: ... - def insert(self, index: SupportsIndex, item: Node | Iterable[Node] | None) -> None: ... - def previous_sibling(self) -> Node | None: ... - def __getattr__(self, name: str, /) -> Incomplete: ... + def set_class(self, name: str) -> None: ... + def note_referenced_by(self, name: str | None = None, id: str | None = None) -> None: ... + @classmethod + def is_not_list_attribute(cls, attr: str) -> bool: ... + @classmethod + def is_not_known_attribute(cls, attr: str) -> bool: ... + + # '__iter__' is added as workaround, since mypy doesn't support classes that are iterable via '__getitem__' + # see https://github.com/python/typeshed/pull/10099#issuecomment-1528789395 + def __iter__(self) -> Iterator[Node]: ... class TextElement(Element): - # A few classes not subclassing TextElement have this, too - child_text_separator: ClassVar[str] def __init__(self, rawsource: str = "", text: str = "", *children: Node, **attributes) -> None: ... class FixedTextElement(TextElement): ... @@ -299,22 +368,11 @@ class field_list(Sequential, Element): ... class field(Part, Element): ... class field_name(Part, TextElement): ... class field_body(Part, Element): ... - -# child_text_separator in some option* classes mirrors TextElement, -# but these classes do not subclass TextElement -class option(Part, Element): - child_text_separator: str - +class option(Part, Element): ... class option_argument(Part, TextElement): ... - -class option_group(Part, Element): - child_text_separator: str # see above - +class option_group(Part, Element): ... class option_list(Sequential, Element): ... - -class option_list_item(Part, Element): - child_text_separator: str # see above - +class option_list_item(Part, Element): ... class option_string(Part, TextElement): ... class description(Part, Element): ... class literal_block(General, FixedTextElement): ...