diff --git a/lxml-stubs/builder.pyi b/lxml-stubs/builder.pyi new file mode 100644 index 0000000..7590a65 --- /dev/null +++ b/lxml-stubs/builder.pyi @@ -0,0 +1,30 @@ +from typing import Any, Callable, Mapping, Optional + +from .etree import _AnyStr, _Element + +class ElementMaker: + def __init__( + self, + typemap: Optional[Mapping[Any, Callable[[_Element, Any], None]]] = ..., + namespace: Optional[str] = ..., + nsmap: Optional[Mapping[Any, _AnyStr]] = ..., + # same signature as etree.Element() + makeelement: Optional[Callable[..., _Element]] = ..., + ) -> None: ... + def __call__( + self, + tag: str, + # Although default ElementMaker only accepts _Element and types + # interpretable by default typemap (that is str, CDATA and dict) + # as children, typemap can be expanded to make sure item of any + # type is accepted. + *children: Any, + **attrib: _AnyStr, + ) -> _Element: ... + # __getattr__ here is special. ElementMaker is a factory that generates + # element of *any* tag, as long as tag name does not conflict with basic + # object methods (including python keywords like "class" and "for", + # which are common in HTML) + def __getattr__(self, name: str) -> Callable[..., _Element]: ... + +E: ElementMaker diff --git a/test-data/test-builder.yml b/test-data/test-builder.yml new file mode 100644 index 0000000..6ca8c49 --- /dev/null +++ b/test-data/test-builder.yml @@ -0,0 +1,65 @@ +- case: builder_default_element_factory + main: | + from lxml.builder import E + reveal_type(E) # N: Revealed type is "lxml.builder.ElementMaker" + +- case: builder_custom_element_factory + parametrized: + - args: namespace=ns + - args: makeelement=etree.Element + - args: namespace=ns, nsmap=map + - args: typemap=typemap + main: | + from typing import Callable, Dict, Type + from lxml.builder import ElementMaker + from lxml import etree + ns: str + map: Dict[str, str] + def add_text(elem: etree._Element, item: str) -> None: ... + typemap = {str: add_text} + E = ElementMaker({{ args }}) + reveal_type(E) # N: Revealed type is "lxml.builder.ElementMaker" + +- case: builder_create_elements + parametrized: + - method: foo + rt: lxml.etree._Element + - method: BAR + rt: lxml.etree._Element + - method: __fOoBaR_ + rt: lxml.etree._Element + - method: __str__ + rt: builtins.str + main: | + from lxml.builder import E + reveal_type(E.{{ method }}()) # N: Revealed type is "{{ rt }}" + +# Directly taken from lxml.builder docstring example +- case: builder_complex_example + main: | + from lxml.builder import E + from lxml import etree as ET + + A = E.a + I = E.i + B = E.b + + def CLASS(v): + return {'class': v} + + page = ( + E.html( + E.head( + E.title("This is a sample document") + ), + E.body( + E.h1("Hello!", CLASS("title")), + E.p("This is a paragraph with ", B("bold"), " text in it!"), + E.p("This is another paragraph, with a ", + A("link", href="http://www.python.org"), "."), + E.p("Here are some reserved characters: ."), + ET.XML("

And finally, here is an embedded XHTML fragment.

"), + ) + ) + ) + reveal_type(page) # N: Revealed type is "lxml.etree._Element"