Skip to content

Commit 7c08258

Browse files
committed
feat(stub): fromstring(), parse() and _ElementTree.parse() support custom target parser
1 parent 070fcd0 commit 7c08258

2 files changed

Lines changed: 118 additions & 16 deletions

File tree

src/lxml-stubs/etree/_element.pyi

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ from typing import (
1111
overload,
1212
)
1313

14+
from .. import _types as _t
15+
from ..cssselect import _CSSTransArg
16+
from ._module_misc import CDATA, DocInfo, QName
17+
from ._parser import CustomTargetParser
18+
from ._xslt import XSLTAccessControl, XSLTExtension, _Stylesheet_Param, _XSLTResultTree
19+
1420
if sys.version_info >= (3, 11):
1521
from typing import Never, Self
1622
else:
@@ -21,11 +27,6 @@ if sys.version_info >= (3, 13):
2127
else:
2228
from typing_extensions import deprecated
2329

24-
from .. import _types as _t
25-
from ..cssselect import _CSSTransArg
26-
from ._module_misc import CDATA, DocInfo, QName
27-
from ._xslt import XSLTAccessControl, XSLTExtension, _Stylesheet_Param, _XSLTResultTree
28-
2930
_T = TypeVar("_T")
3031

3132
# The base of _Element is *almost* an amalgam of MutableSequence[_Element]
@@ -661,6 +662,8 @@ class _Element:
661662
self, tag: _t._TagSelector | None = None, *tags: _t._TagSelector
662663
) -> Iterator[Self]: ...
663664

665+
_ET2_co = TypeVar("_ET2_co", bound=_Element, default=_Element, covariant=True)
666+
664667
# ET class notation is specialized, indicating the type of element
665668
# it is holding (e.g. XML element, HTML element or Objectified
666669
# Element).
@@ -673,13 +676,63 @@ class _ElementTree(Generic[_t._ET_co]):
673676
def parser(self) -> _t._DefEtreeParsers[_t._ET_co] | None: ...
674677
@property
675678
def docinfo(self) -> DocInfo: ...
679+
@overload # common parser
676680
def parse(
677681
self,
678682
source: _t._FileReadSource,
679-
parser: _t._DefEtreeParsers[_t._ET_co] | None = None,
683+
parser: _t._DefEtreeParsers[_ET2_co],
680684
*,
681-
base_url: _t._AnyStr | None = None,
682-
) -> None: ...
685+
base_url: str | bytes | None = None,
686+
) -> _ET2_co:
687+
"""Updates self with the content of source and returns its root.
688+
689+
Annotation
690+
----------
691+
This overload handles the case where a common parser is supplied.
692+
693+
See Also
694+
--------
695+
- [API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree._ElementTree.parse)
696+
"""
697+
@overload # custom target parser
698+
def parse(
699+
self,
700+
source: _t._FileReadSource,
701+
parser: CustomTargetParser[_ET2_co],
702+
*,
703+
base_url: str | bytes | None = None,
704+
) -> _ET2_co:
705+
"""Updates self with the content of source and returns its root.
706+
707+
Annotation
708+
----------
709+
This overload handles the case where a custom target parser is supplied.
710+
Note that target object must return an element, i.e. compatible with
711+
`etree.TreeBuilder`.
712+
713+
See Also
714+
--------
715+
- [API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree._ElementTree.parse)
716+
"""
717+
@overload # parser not supplied
718+
def parse(
719+
self,
720+
source: _t._FileReadSource,
721+
parser: None = None,
722+
*,
723+
base_url: str | bytes | None = None,
724+
) -> _Element:
725+
"""Updates self with the content of source and returns its root.
726+
727+
Annotation
728+
----------
729+
This overload handles the case where no parser is supplied (thus
730+
default parser is utilised).
731+
732+
See Also
733+
--------
734+
- [API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree._ElementTree.parse)
735+
"""
683736
# Changes root node; in terms of typing, this means changing
684737
# specialization of ElementTree. This is not expressible in
685738
# current typing system.

src/lxml-stubs/etree/_module_func.pyi

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def XML(
171171
[API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree.XML)
172172
"""
173173

174-
@overload
174+
@overload # common parser
175175
def parse(
176176
source: _FileReadSource,
177177
parser: _DefEtreeParsers[_ET_co],
@@ -189,15 +189,35 @@ def parse(
189189
Please [refer to wiki](https://github.com/abelcheung/types-lxml/wiki/Using-specialised-class-directly#no-automatic-change-of-subscript)
190190
on how to create such annotation-only specialized parsers.
191191
192-
Returning result of custom parser target is unsupported in stubs.
193-
Please use `typing.cast()` directly in such case.
192+
See Also
193+
--------
194+
[API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree.parse)
195+
"""
196+
197+
@overload # custom target parser
198+
def parse(
199+
source: _FileReadSource,
200+
parser: CustomTargetParser[_T],
201+
*,
202+
base_url: str | bytes | None = None,
203+
) -> _T:
204+
"""Return an ElementTree object loaded with source elements.
205+
206+
Annotation
207+
----------
208+
When specially constructed parser with custom parser target is supplied,
209+
`parse()` returns that value dictated in parser target definition,
210+
that is the parser target `.close()` method return value.
211+
212+
Please [refer to wiki](https://github.com/abelcheung/types-lxml/wiki/Custom-target-parser)
213+
on how to create fully annotated parser with custom target object.
194214
195215
See Also
196216
--------
197217
[API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree.parse)
198218
"""
199219

200-
@overload
220+
@overload # parser not supplied
201221
def parse(
202222
source: _FileReadSource,
203223
parser: None = None,
@@ -206,12 +226,16 @@ def parse(
206226
) -> _ElementTree:
207227
"""Return an ElementTree object loaded with source elements.
208228
229+
Annotation
230+
----------
231+
This overload handles usage of `parse()` with default parser.
232+
209233
See Also
210234
--------
211235
[API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree.parse)
212236
"""
213237

214-
@overload
238+
@overload # common parser
215239
def fromstring(
216240
text: str | Buffer,
217241
parser: _DefEtreeParsers[_ET_co],
@@ -230,15 +254,36 @@ def fromstring(
230254
Please [refer to wiki](https://github.com/abelcheung/types-lxml/wiki/Using-specialised-class-directly#no-automatic-change-of-subscript)
231255
on how to create such annotation-only specialized parsers.
232256
233-
Returning result of custom parser target is unsupported in stubs.
234-
Please use `typing.cast()` directly in such case.
257+
See Also
258+
--------
259+
[API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree.fromstring)
260+
"""
261+
262+
@overload # custom target parser
263+
def fromstring(
264+
text: str | Buffer,
265+
parser: CustomTargetParser[_T],
266+
*,
267+
base_url: str | bytes | None = None,
268+
) -> _T:
269+
"""Parses an XML document or fragment from a string.
270+
Returns the root node (or the result returned by a parser target).
271+
272+
Annotation
273+
----------
274+
When specially constructed parser with custom parser target is supplied,
275+
`fromstring()` returns that value dictated in parser target definition,
276+
that is the parser target `.close()` method return value.
277+
278+
Please [refer to wiki](https://github.com/abelcheung/types-lxml/wiki/Custom-target-parser)
279+
on how to create fully annotated parser with custom target object.
235280
236281
See Also
237282
--------
238283
[API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree.fromstring)
239284
"""
240285

241-
@overload
286+
@overload # parser not supplied
242287
def fromstring(
243288
text: str | Buffer,
244289
parser: None = None,
@@ -248,6 +293,10 @@ def fromstring(
248293
"""Parses an XML document or fragment from a string.
249294
Returns the root node (or the result returned by a parser target).
250295
296+
Annotation
297+
----------
298+
This overload handles usage of `fromstring()` with default parser.
299+
251300
See Also
252301
--------
253302
[API Documentation](https://lxml.de/apidoc/lxml.etree.html#lxml.etree.fromstring)

0 commit comments

Comments
 (0)