Skip to content

Commit

Permalink
Replace factory functions with classes.
Browse files Browse the repository at this point in the history
Element() and ElementTree() were factory functions until now.
These are now defined as classes.

Stop exposing _Element and _ElementTree, which are internal implementations.
Use Element and ElementTree instead.

This change is not backward compatible. Any user of types-lxml that
used _Element in their type annotations would get an error.

This change only makes sense if lxml merges
lxml/lxml#405
  • Loading branch information
udifuchs committed Feb 18, 2024
1 parent 176be26 commit d10e828
Show file tree
Hide file tree
Showing 55 changed files with 490 additions and 479 deletions.
8 changes: 4 additions & 4 deletions lxml-stubs/ElementInclude.pyi
@@ -1,6 +1,6 @@
from typing import Any, Callable, Literal, overload

from lxml.etree import LxmlSyntaxError, _Element, _ElementTree
from lxml.etree import LxmlSyntaxError, Element, ElementTree

from ._types import _ET, _FilePath

Expand All @@ -14,7 +14,7 @@ def default_loader(
href: _FilePath,
parse: Literal["xml", "text"],
encoding: str | None = None,
) -> _ElementTree[_Element] | str: ...
) -> ElementTree[Element] | str: ...
@overload
def include(
elem: _ET,
Expand All @@ -24,8 +24,8 @@ def include(
) -> _ET: ...
@overload
def include(
elem: _ElementTree[_ET],
elem: ElementTree[_ET],
loader: Callable[[_FilePath, Literal["xml", "text"]], Any] | None = None,
base_url: str | None = None,
max_depth: int = 6,
) -> _ElementTree[_ET]: ...
) -> ElementTree[_ET]: ...
16 changes: 9 additions & 7 deletions lxml-stubs/_types.pyi
Expand Up @@ -13,7 +13,7 @@ from typing import (
)
from typing_extensions import TypeAlias

from .etree import QName, _Element, _ElementTree
from .etree import QName, Element, ElementTree

# Dup but deviate from recent _typeshed
Unused: TypeAlias = Any
Expand Down Expand Up @@ -85,8 +85,8 @@ _XPathVarArg = (
| float
| str
| bytes
| _Element
| list[_Element]
| Element
| list[Element]
)
# fmt: on

Expand Down Expand Up @@ -127,8 +127,10 @@ _SaxEventNames = Literal[
"pi",
]

_ET = TypeVar("_ET", bound=_Element)
_ET_co = TypeVar("_ET_co", bound=_Element, covariant=True)
_ET = TypeVar("_ET", bound=Element)
_ET_co = TypeVar("_ET_co", bound=Element, covariant=True)
_ETT = TypeVar("_ETT", bound=ElementTree[Element])
_ETT_co = TypeVar("_ETT_co", bound=ElementTree[Element], covariant=True)

# Generic element factory function type. Because arguments are
# mostly optional, accurate typing can't be done.
Expand All @@ -138,9 +140,9 @@ _ElemFactory: TypeAlias = Callable[..., _ET]
# but checks for exact element *factory functions* instead
# (etree.Element() and friends). Python typing system doesn't
# support such outlandish usage. Use a generic callable instead.
_TagSelector: TypeAlias = _TagName | _ElemFactory[_Element]
_TagSelector: TypeAlias = _TagName | _ElemFactory[Element]

_ElementOrTree: TypeAlias = _Element | _ElementTree[_Element]
_ElementOrTree: TypeAlias = Element | ElementTree[Element]

class SupportsLaxedItems(Protocol[_KT_co, _VT_co]):
"""Relaxed form of SupportsItems
Expand Down
10 changes: 5 additions & 5 deletions lxml-stubs/builder.pyi
@@ -1,9 +1,9 @@
from typing import Any, Callable, Generic, Mapping, overload

from ._types import _ElemFactory, _ET_co, _NSMapArg
from .etree import QName, _Element
from .etree import QName, Element

_TypeMapArg = Mapping[Any, Callable[[_Element, Any], None]]
_TypeMapArg = Mapping[Any, Callable[[Element, Any], None]]

class ElementMaker(Generic[_ET_co]):
@overload
Expand All @@ -30,11 +30,11 @@ class ElementMaker(Generic[_ET_co]):
namespace: str | None = ...,
nsmap: _NSMapArg | None = ...,
makeelement: None = ...,
) -> ElementMaker[_Element]: ...
) -> ElementMaker[Element]: ...
def __call__(
self,
tag: str | QName, # No bytes here
# Although, by default, the ElementMaker only accepts _Element and types
# Although, by default, the ElementMaker only accepts Element and types
# interpretable by the default typemap (that is str, CDATA and dict)
# as children, the typemap can be expanded to make sure items of any
# type are accepted.
Expand All @@ -47,4 +47,4 @@ class ElementMaker(Generic[_ET_co]):
# like "class" and "for", which are common in HTML)
def __getattr__(self, name: str) -> _ElemFactory[_ET_co]: ...

E: ElementMaker[_Element]
E: ElementMaker[Element]
4 changes: 2 additions & 2 deletions lxml-stubs/cssselect.pyi
@@ -1,7 +1,7 @@
from typing import Literal, overload

from ._types import _ET, _NonDefaultNSMapArg, _XPathVarArg
from .etree import XPath, _ElementTree
from .etree import XPath, ElementTree

_CSSTransArg = LxmlTranslator | Literal["xml", "html", "xhtml"]

Expand Down Expand Up @@ -47,7 +47,7 @@ class CSSSelector(XPath):
@overload
def __call__( # pyright: ignore[reportIncompatibleMethodOverride]
self,
_etree_or_element: _ElementTree[_ET],
_etree_or_element: ElementTree[_ET],
/,
**_variables: _XPathVarArg,
) -> list[_ET]: ...
6 changes: 2 additions & 4 deletions lxml-stubs/etree/__init__.pyi
Expand Up @@ -28,16 +28,14 @@ from ._dtd import (
from ._element import (
_Attrib as _Attrib,
_Comment as _Comment,
_Element as _Element,
_ElementTree as _ElementTree,
Element as Element,
ElementTree as ElementTree,
_Entity as _Entity,
_ProcessingInstruction as _ProcessingInstruction,
)
from ._factory_func import (
PI as PI,
Comment as Comment,
Element as Element,
ElementTree as ElementTree,
Entity as Entity,
ProcessingInstruction as ProcessingInstruction,
SubElement as SubElement,
Expand Down
16 changes: 8 additions & 8 deletions lxml-stubs/etree/_classlookup.pyi
Expand Up @@ -12,12 +12,12 @@ from .._types import (
_ElemClsLookupArg,
_NSMapArg,
)
from ._element import _Comment, _Element, _Entity, _ProcessingInstruction
from ._element import _Comment, Element, _Entity, _ProcessingInstruction

#
# Public element classes
#
class ElementBase(_Element):
class ElementBase(Element):
"""The public Element class
Original Docstring
Expand Down Expand Up @@ -146,7 +146,7 @@ class ElementDefaultClassLookup(ElementClassLookup):
accept the respective Element classes."""

@property
def element_class(self) -> type[_Element]: ...
def element_class(self) -> type[Element]: ...
@property
def comment_class(self) -> type[_Comment]: ...
@property
Expand Down Expand Up @@ -180,8 +180,8 @@ class AttributeBasedElementClassLookup(FallbackElementClassLookup):
def __init__(
self,
attribute_name: _AttrName,
class_mapping: Mapping[str, type[_Element]]
| Mapping[str | None, type[_Element]],
class_mapping: Mapping[str, type[Element]]
| Mapping[str | None, type[Element]],
fallback: ElementClassLookup | None = ...,
) -> None: ...

Expand Down Expand Up @@ -218,7 +218,7 @@ class CustomElementClassLookup(FallbackElementClassLookup, metaclass=ABCMeta):
doc: object, # Internal doc object
namespace: str | None,
name: str | None,
) -> type[_Element] | None: ...
) -> type[Element] | None: ...

class PythonElementClassLookup(FallbackElementClassLookup, metaclass=ABCMeta):
"""Element class lookup based on a subclass method
Expand Down Expand Up @@ -272,8 +272,8 @@ class PythonElementClassLookup(FallbackElementClassLookup, metaclass=ABCMeta):
def lookup(
self,
doc: object,
element: _Element, # quasi-Element with all attributes read-only
) -> type[_Element] | None: ...
element: Element, # quasi-Element with all attributes read-only
) -> type[Element] | None: ...

def set_element_class_lookup(lookup: ElementClassLookup | None = ...) -> None:
"""Set the global element class lookup method
Expand Down
56 changes: 41 additions & 15 deletions lxml-stubs/etree/_element.pyi
Expand Up @@ -8,10 +8,18 @@ from ._module_misc import CDATA, DocInfo, QName
from ._parser import _DefEtreeParsers
from ._xslt import XSLTAccessControl, XSLTExtension, _Stylesheet_Param, _XSLTResultTree

# The base of _Element is *almost* an amalgam of MutableSequence[_Element]
# The base of Element is *almost* an amalgam of MutableSequence[Element]
# plus mixin methods for _Attrib.
# Extra methods follow the order of _Element source approximately
class _Element:
# Extra methods follow the order of Element source approximately
class Element:
def __new__( # Args identical to Element.makeelement
cls,
_tag: _t._TagName,
/,
attrib: _t.SupportsLaxedItems[str, _t._AnyStr] | None = ...,
nsmap: _t._NSMapArg | None = ...,
**_extra: _t._AnyStr,
) -> Self: ...
#
# Common properties
#
Expand All @@ -30,7 +38,7 @@ class _Element:
@tail.setter
def tail(self, value: _t._AnyStr | CDATA | None) -> None: ...
#
# _Element-only properties
# Element-only properties
# Following props are marked as read-only in comment,
# but 'sourceline' and 'base' provide __set__ method.
# However, we only implement rw property for base, as
Expand Down Expand Up @@ -89,7 +97,7 @@ class _Element:
def getparent(self) -> Self | None: ...
def getnext(self) -> Self | None: ...
def getprevious(self) -> Self | None: ...
def getroottree(self) -> _ElementTree[Self]: ...
def getroottree(self) -> ElementTree[Self]: ...
@overload
def itersiblings(
self, *tags: _t._TagSelector, preceding: bool = ...
Expand Down Expand Up @@ -200,7 +208,25 @@ class _Element:
# element, the absolute majority of lxml API will fail to work.
# It is considered harmful to support such corner case, which
# adds much complexity without any benefit.
class _ElementTree(Generic[_t._ET_co]):
class ElementTree(Generic[_t._ET_co]):
@overload # from element, parser ignored
def __new__(cls: type[_t._ETT], element: _t._ET) -> _t._ETT: ...
@overload # from file source, custom parser
def __new__(
cls: type[_t._ETT_co],
element: None = ...,
*,
file: _t._FileReadSource,
parser: _DefEtreeParsers[_t._ET_co],
) -> _t._ETT_co: ...
@overload # from file source, default parser
def __new__(
cls: type[_t._ETT],
element: None = ...,
*,
file: _t._FileReadSource,
parser: None = ...,
) -> _t._ETT: ...
@property
def parser(self) -> _DefEtreeParsers[_t._ET_co] | None: ...
@property
Expand All @@ -215,7 +241,7 @@ class _ElementTree(Generic[_t._ET_co]):
# Changes root node; in terms of typing, this means changing
# specialization of ElementTree. This is not expressible in
# current typing system.
def _setroot(self, root: _Element) -> None: ...
def _setroot(self, root: Element) -> None: ...
def getroot(self) -> _t._ET_co: ...
# Special notes for write()
# For write(), there are quite many combination of keyword
Expand Down Expand Up @@ -271,15 +297,15 @@ class _ElementTree(Generic[_t._ET_co]):
doctype: str | None = ...,
compression: int | None = ...,
) -> None: ...
def getpath(self: _ElementTree[_t._ET], element: _t._ET) -> str: ...
def getelementpath(self: _ElementTree[_t._ET], element: _t._ET) -> str: ...
def getpath(self: ElementTree[_t._ET], element: _t._ET) -> str: ...
def getelementpath(self: ElementTree[_t._ET], element: _t._ET) -> str: ...
@overload
def iter(self, *tags: _t._TagSelector) -> Iterator[_t._ET_co]: ...
@overload
def iter(self, *, tag: _t._TagSelector | None = ...) -> Iterator[_t._ET_co]: ...
#
# ElementPath methods calls the same method on root node,
# so signature should be the same as _Element ones
# so signature should be the same as Element ones
#
def find(
self, path: _t._ElemPathArg, namespaces: _t._NSMapArg | None = ...
Expand Down Expand Up @@ -348,7 +374,7 @@ class _ElementTree(Generic[_t._ET_co]):
# Behaves like MutableMapping but deviates a lot in details
class _Attrib:
@property
def _element(self) -> _Element: ...
def _element(self) -> Element: ...
def __setitem__(self, __k: _t._AttrName, __v: _t._AttrVal) -> None: ...
def __delitem__(self, __k: _t._AttrName) -> None: ...
# explicitly checks for dict and _Attrib
Expand Down Expand Up @@ -398,13 +424,13 @@ class _Attrib:
# So here we are.
#
# It is decided to not decouple other content only elements
# from _Element, even though their interfaces are vastly different
# from _Element. The notion of or'ing different kind of elements
# from Element, even though their interfaces are vastly different
# from Element. The notion of or'ing different kind of elements
# throughout all element methods would cause great inconvenience
# for me and all users alike -- using some _AnyHtmlElement alias
# to represent union of all elements was a failure for users.
# We opt for convenience and ease of use in the future.
class __ContentOnlyElement(_Element):
class __ContentOnlyElement(Element):
#
# Useful properties
#
Expand Down Expand Up @@ -441,7 +467,7 @@ class _Comment(__ContentOnlyElement):
@property # type: ignore[misc]
def tag(self) -> _t._ElemFactory[_Comment]: ... # type: ignore[override]

# signature of .get() for _PI and _Element are the same
# signature of .get() for _PI and Element are the same
class _ProcessingInstruction(__ContentOnlyElement):
@property # type: ignore[misc]
def tag(self) -> _t._ElemFactory[_ProcessingInstruction]: ... # type: ignore[override]
Expand Down
31 changes: 6 additions & 25 deletions lxml-stubs/etree/_factory_func.pyi
@@ -1,15 +1,18 @@
from typing import overload
from typing import overload, TypeVar
from typing_extensions import Self

from .._types import (
_ET,
SupportsLaxedItems,
_AnyStr,
_ET_co,
_ETT,
_ETT_co,
_FileReadSource,
_NSMapArg,
_TagName,
)
from ._element import _Comment, _Element, _ElementTree, _Entity, _ProcessingInstruction
from ._element import _Comment, _Entity, _ProcessingInstruction
from ._parser import _DefEtreeParsers

def Comment(text: _AnyStr | None = ...) -> _Comment: ...
Expand All @@ -20,13 +23,7 @@ def ProcessingInstruction(
PI = ProcessingInstruction

def Entity(name: _AnyStr) -> _Entity: ...
def Element( # Args identical to _Element.makeelement
_tag: _TagName,
/,
attrib: SupportsLaxedItems[str, _AnyStr] | None = ...,
nsmap: _NSMapArg | None = ...,
**_extra: _AnyStr,
) -> _Element: ...

def SubElement(
_parent: _ET,
_tag: _TagName,
Expand All @@ -35,19 +32,3 @@ def SubElement(
nsmap: _NSMapArg | None = ...,
**_extra: _AnyStr,
) -> _ET: ...
@overload # from element, parser ignored
def ElementTree(element: _ET) -> _ElementTree[_ET]: ...
@overload # from file source, custom parser
def ElementTree(
element: None = ...,
*,
file: _FileReadSource,
parser: _DefEtreeParsers[_ET_co],
) -> _ElementTree[_ET_co]: ...
@overload # from file source, default parser
def ElementTree(
element: None = ...,
*,
file: _FileReadSource,
parser: None = ...,
) -> _ElementTree[_Element]: ...

0 comments on commit d10e828

Please sign in to comment.