Skip to content

Commit

Permalink
refactor: Create aliases for element attribute mapping and tuple
Browse files Browse the repository at this point in the history
  • Loading branch information
abelcheung committed Nov 14, 2024
1 parent 921e569 commit 233adc9
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 56 deletions.
41 changes: 33 additions & 8 deletions src/lxml-stubs/_types.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,34 @@ _AnyStr = str | bytes
_TagName = str | bytes | bytearray | QName
_AttrName = str | bytes | bytearray | QName
_AttrVal = str | bytes | bytearray | QName
_AttrNameKey = str | bytes | QName
"""Types supported in attribute name, sans bytearray"""

# On the other hand, Elementpath API doesn't do str/byte canonicalization,
# only unicode accepted for py3
_ElemPathArg = str | QName

_AttrMapping = SupportsLaxedItems[_AttrNameKey, _AttrVal]
"""Attribute dict-like mapping
Used in attrib argument of various factories and methods.
Bytearray not supported as key (not hashable).
Internal stuff
--------------
Anything that delves into `_initNodeAttributes()`
(in `apihelper.pxi`) should be able to use it.
Need to make sure `_Attrib` and `dict` are supported in
places wherever this alias is used.
"""

_AttrTuples = Iterable[tuple[_AttrNameKey, _AttrVal]]
"""Tuple form of attribute key/value pairs
Used in attrib argument where tuple form is accepted,
in place of or in addition to `_AttrMapping`.
"""

# Due to Mapping having invariant key types, Mapping[A | B, ...]
# would fail to validate against either Mapping[A, ...] or Mapping[B, ...]
# Try to settle for simpler solution, assuming python3 users would not
Expand Down Expand Up @@ -131,24 +154,24 @@ class _ElementFactory(Protocol, Generic[_ET_co]):
This is callback protocol for `makeelement()` method of
various element objects, with following signature (which
is identical to `etree.Element()` function):
is identical to `etree.Element()` factory):
```python
(_tag, attrib=..., nsmap=..., **_extra)
```
The mapping in `attrib` argument and all `_extra` keyword
arguments would be merged together. The result is usually
`{**attrib, **_extra}`, but some places may deviate.
arguments would be merged together, with `_extra` taking
precedence over `attrib`.
"""

def __call__(
self,
_tag: _TagName,
/,
attrib: SupportsLaxedItems[str, _AnyStr] | None = None,
attrib: _AttrMapping | None = None,
nsmap: _NSMapArg | None = None,
**_extra: _AnyStr,
**_extra: _AttrVal,
) -> _ET_co: ...

# Note that _TagSelector filters element type not by classes,
Expand All @@ -165,9 +188,11 @@ _DefEtreeParsers = XMLParser[_ET_co] | HTMLParser[_ET_co]
class SupportsLaxedItems(Protocol[_KT_co, _VT_co]):
"""Relaxed form of SupportsItems
Original SupportsItems from typeshed returns generic set which
is compatible with ItemsView. However, _Attrib doesn't conform
and returns list instead. Gotta find a common ground here.
Original `SupportsItems` from typeshed returns
generic set which is compatible with `ItemsView`.
However, `_Attrib.items()` returns `list` instead.
Here we find a common ground that satisfies both
and avoid the mapping invariance culprit.
"""

def items(self) -> Collection[tuple[_KT_co, _VT_co]]: ...
Expand Down
8 changes: 4 additions & 4 deletions src/lxml-stubs/etree/_classlookup.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ from abc import ABCMeta, abstractmethod
from typing import Mapping, final

from .._types import (
SupportsLaxedItems,
_AnyStr,
_AttrMapping,
_AttrName,
_AttrVal,
_ElemClsLookupArg,
_NSMapArg,
)
Expand Down Expand Up @@ -59,9 +59,9 @@ class ElementBase(_Element):
def __init__(
self,
*children: object,
attrib: SupportsLaxedItems[str, _AnyStr] | None = None,
attrib: _AttrMapping | None = None,
nsmap: _NSMapArg | None = None,
**_extra: _AnyStr,
**_extra: _AttrVal,
) -> None: ...
def _init(self) -> None: ...

Expand Down
3 changes: 2 additions & 1 deletion src/lxml-stubs/etree/_element.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,8 @@ class _ElementTree(Generic[_t._ET_co]):
class _Attrib:
def __setitem__(self, __k: _t._AttrName, __v: _t._AttrVal) -> None: ...
def __delitem__(self, __k: _t._AttrName) -> None: ...
# explicitly checks for dict and _Attrib
# _only_ checks for dict and _Attrib to do
# .items() conversion, not any Mapping
def update(
self,
sequence_or_dict: (
Expand Down
16 changes: 8 additions & 8 deletions src/lxml-stubs/etree/_factory_func.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ from typing import overload

from .._types import (
_ET,
SupportsLaxedItems,
_AnyStr,
_AttrMapping,
_AttrVal,
_DefEtreeParsers,
_ElementFactory,
_ET_co,
Expand Down Expand Up @@ -45,27 +45,27 @@ def SubElement(
_parent: ObjectifiedElement,
_tag: _TagName,
/,
attrib: SupportsLaxedItems[str, _AnyStr] | None = None,
attrib: _AttrMapping | None = None,
nsmap: _NSMapArg | None = None,
**_extra: _AnyStr,
**_extra: _AttrVal,
) -> StringElement: ...
@overload
def SubElement(
_parent: HtmlElement,
_tag: _TagName,
/,
attrib: SupportsLaxedItems[str, _AnyStr] | None = None,
attrib: _AttrMapping | None = None,
nsmap: _NSMapArg | None = None,
**_extra: _AnyStr,
**_extra: _AttrVal,
) -> HtmlElement: ...
@overload
def SubElement(
_parent: _ET,
_tag: _TagName,
/,
attrib: SupportsLaxedItems[str, _AnyStr] | None = None,
attrib: _AttrMapping | None = None,
nsmap: _NSMapArg | None = None,
**_extra: _AnyStr,
**_extra: _AttrVal,
) -> _ET: ...
@overload # from element, parser ignored
def ElementTree(element: _ET) -> _ElementTree[_ET]: ...
Expand Down
10 changes: 6 additions & 4 deletions src/lxml-stubs/etree/_serializer.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ from _typeshed import SupportsWrite
from .._types import (
SupportsLaxedItems,
_AnyStr,
_AttrMapping,
_AttrVal,
_ElementOrTree,
_FileReadSource,
_FileWriteSource,
Expand Down Expand Up @@ -139,10 +141,10 @@ class _IncrementalFileWriter:
def element(
self,
tag: _TagName,
attrib: SupportsLaxedItems[str, _AnyStr] | None = None,
attrib: _AttrMapping | None = None,
nsmap: _NSMapArg | None = None,
method: _OutputMethodArg | None = None,
**_extra: _AnyStr,
**_extra: _AttrVal,
) -> ContextManager[None]: ...

@final
Expand All @@ -166,10 +168,10 @@ class _AsyncIncrementalFileWriter:
def element(
self,
tag: _TagName,
attrib: SupportsLaxedItems[str, _AnyStr] | None = None,
attrib: _AttrMapping | None = None,
nsmap: _NSMapArg | None = None,
method: _OutputMethodArg | None = None,
**_extra: _AnyStr,
**_extra: _AttrVal,
) -> AsyncContextManager[None]: ...

class xmlfile(
Expand Down
Loading

0 comments on commit 233adc9

Please sign in to comment.