Skip to content
This repository has been archived by the owner on Apr 6, 2022. It is now read-only.

Commit

Permalink
Implement lxml.builder, fixes lxml#30
Browse files Browse the repository at this point in the history
  • Loading branch information
abelcheung committed Feb 19, 2022
1 parent 0aa4365 commit 277e8e9
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 0 deletions.
30 changes: 30 additions & 0 deletions lxml-stubs/builder.pyi
Original file line number Diff line number Diff line change
@@ -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
65 changes: 65 additions & 0 deletions test-data/test-builder.yml
Original file line number Diff line number Diff line change
@@ -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: <spam&egg>."),
ET.XML("<p>And finally, here is an embedded XHTML fragment.</p>"),
)
)
)
reveal_type(page) # N: Revealed type is "lxml.etree._Element"

0 comments on commit 277e8e9

Please sign in to comment.