Improve types for xml.etree.ElementTree (#930)

* Improve types for xml.etree.ElementTree

Update signatures to reflect the following peculiarities of the
ElementTree library:

- The elementtree library accepts unicode or bytes for most xml values
  in python2, and coerces everywhere -- but in python3, only str makes
  sense.
- In python 2, the library produces str or unicode instances
  unpredictably, depending on whether the xml is decodeable as ascii or
  not. In python 3, it always produces str instances.
- The parser functions accept unicode or bytes in 2 and 3 -- again, will
  coerce individual instances so heterogeneous lists are ok.
- In python 3, the tostring functions produce bytes or str, depending on
  the value of the 'encoding' parameter.

* improve docs
* Improve ElementFactory type by specifying dict of 2nd arg
This commit is contained in:
lincolnq
2017-02-23 07:43:09 +03:00
committed by Łukasz Langa
parent 91ff50ad7a
commit 59789b8a3e

View File

@@ -1,6 +1,6 @@
# Stubs for xml.etree.ElementTree (Python 3.4)
# Stubs for xml.etree.ElementTree
from typing import Any, AnyStr, Union, IO, Callable, Dict, List, Tuple, Sequence, Iterator, TypeVar, Optional, KeysView, ItemsView, Generator
from typing import Any, Union, IO, Callable, Dict, List, Tuple, Sequence, Iterator, TypeVar, Optional, KeysView, ItemsView, Generator, Text
import io
import sys
@@ -10,53 +10,80 @@ class ParseError(SyntaxError): ...
def iselement(element: 'Element') -> bool: ...
_Ss = TypeVar('_Ss', str, bytes)
_T = TypeVar('_T')
_str_or_bytes = Union[str, bytes]
# Type for parser inputs. Parser will accept any unicode/str/bytes and coerce,
# and this is true in py2 and py3 (even fromstringlist() in python3 can be
# called with a heterogeneous list)
_parser_input_type = Union[bytes, Text]
# Type for individual tag/attr/ns/text values in args to most functions.
# In py2, the library accepts str or unicode everywhere and coerces
# aggressively.
# In py3, bytes is not coerced to str and so use of bytes is probably an error,
# so we exclude it. (why? the parser never produces bytes when it parses XML,
# so e.g., element.get(b'name') will always return None for parsed XML, even if
# there is a 'name' attribute.)
_str_argument_type = Union[str, Text]
# Type for return values from individual tag/attr/text values and serialization
if sys.version_info >= (3,):
# note: in python3, everything comes out as str, yay:
_str_result_type = str
# unfortunately, tostring and tostringlist can return either bytes or str
# depending on the value of `encoding` parameter. Client code knows best:
_tostring_result_type = Any
else:
# in python2, if the tag/attribute/text wasn't decode-able as ascii, it
# comes out as a unicode string; otherwise it comes out as str. (see
# _fixtext function in the source). Client code knows best:
_str_result_type = Any
# On the bright side, tostring and tostringlist always return bytes:
_tostring_result_type = bytes
class Element(Sequence['Element']):
tag = ... # type: _str_or_bytes
attrib = ... # type: Dict[_str_or_bytes, _str_or_bytes]
text = ... # type: Optional[_str_or_bytes]
tail = ... # type: Optional[_str_or_bytes]
def __init__(self, tag: Union[AnyStr, Callable[..., 'Element']], attrib: Dict[AnyStr, AnyStr]=..., **extra: AnyStr) -> None: ...
tag = ... # type: _str_result_type
attrib = ... # type: Dict[_str_result_type, _str_result_type]
text = ... # type: Optional[_str_result_type]
tail = ... # type: Optional[_str_result_type]
def __init__(self, tag: Union[_str_argument_type, Callable[..., 'Element']], attrib: Dict[_str_argument_type, _str_argument_type]=..., **extra: _str_argument_type) -> None: ...
def append(self, subelement: 'Element') -> None: ...
def clear(self) -> None: ...
def copy(self) -> 'Element': ...
def extend(self, elements: Sequence['Element']) -> None: ...
def find(self, path: str, namespaces: Dict[str, str]=...) -> Optional['Element']: ...
def findall(self, path: str, namespaces: Dict[str, str]=...) -> List['Element']: ...
def findtext(self, path: str, default: _T=..., namespaces: Dict[str, str]=...) -> Union[_T, str]: ...
def get(self, key: AnyStr, default: _T=...) -> Union[AnyStr, _T]: ...
def find(self, path: _str_argument_type, namespaces: Dict[_str_argument_type, _str_argument_type]=...) -> Optional['Element']: ...
def findall(self, path: _str_argument_type, namespaces: Dict[_str_argument_type, _str_argument_type]=...) -> List['Element']: ...
def findtext(self, path: _str_argument_type, default: _T=..., namespaces: Dict[_str_argument_type, _str_argument_type]=...) -> Union[_T, _str_result_type]: ...
def get(self, key: _str_argument_type, default: _T=...) -> Union[_str_result_type, _T]: ...
def getchildren(self) -> List['Element']: ...
def getiterator(self, tag: Union[str, AnyStr]=...) -> List['Element']: ...
def getiterator(self, tag: _str_argument_type=...) -> List['Element']: ...
if sys.version_info >= (3, 2):
def insert(self, index: int, subelement: 'Element') -> None: ...
else:
def insert(self, index: int, element: 'Element') -> None: ...
def items(self) -> ItemsView[AnyStr, AnyStr]: ...
def iter(self, tag: Union[str, AnyStr]=...) -> Generator['Element', None, None]: ...
def iterfind(self, path: str, namespaces: Dict[str, str]=...) -> List['Element']: ...
def itertext(self) -> Generator[str, None, None]: ...
def keys(self) -> KeysView[AnyStr]: ...
def makeelement(self, tag: _Ss, attrib: Dict[_Ss, _Ss]) -> 'Element': ...
def items(self) -> ItemsView[_str_result_type, _str_result_type]: ...
def iter(self, tag: _str_argument_type=...) -> Generator['Element', None, None]: ...
def iterfind(self, path: _str_argument_type, namespaces: Dict[_str_argument_type, _str_argument_type]=...) -> List['Element']: ...
def itertext(self) -> Generator[_str_result_type, None, None]: ...
def keys(self) -> KeysView[_str_result_type]: ...
def makeelement(self, tag: _str_argument_type, attrib: Dict[_str_argument_type, _str_argument_type]) -> 'Element': ...
def remove(self, subelement: 'Element') -> None: ...
def set(self, key: AnyStr, value: AnyStr) -> None: ...
def set(self, key: _str_argument_type, value: _str_argument_type) -> None: ...
def __bool__(self) -> bool: ...
def __delitem__(self, index: int) -> None: ...
def __getitem__(self, index) -> 'Element': ...
def __len__(self) -> int: ...
def __setitem__(self, index: int, element: 'Element') -> None: ...
def SubElement(parent: Element, tag: AnyStr, attrib: Dict[AnyStr, AnyStr]=..., **extra: AnyStr) -> Element: ...
def Comment(text: _str_or_bytes=...) -> Element: ...
def ProcessingInstruction(target: str, text: str=...) -> Element: ...
def SubElement(parent: Element, tag: _str_argument_type, attrib: Dict[_str_argument_type, _str_argument_type]=..., **extra: _str_argument_type) -> Element: ...
def Comment(text: _str_argument_type=...) -> Element: ...
def ProcessingInstruction(target: _str_argument_type, text: _str_argument_type=...) -> Element: ...
PI = ... # type: Callable[..., Element]
class QName:
text = ... # type: str
def __init__(self, text_or_uri: str, tag: str=...) -> None: ...
def __init__(self, text_or_uri: _str_argument_type, tag: _str_argument_type=...) -> None: ...
_file_or_filename = Union[str, bytes, int, IO[Any]]
@@ -65,25 +92,25 @@ class ElementTree:
def __init__(self, element: Element=..., file: _file_or_filename=...) -> None: ...
def getroot(self) -> Element: ...
def parse(self, source: _file_or_filename, parser: 'XMLParser'=...) -> Element: ...
def iter(self, tag: Union[str, AnyStr]=...) -> Generator[Element, None, None]: ...
def getiterator(self, tag: Union[str, AnyStr]=...) -> List[Element]: ...
def find(self, path: str, namespaces: Dict[str, str]=...) -> Optional[Element]: ...
def findtext(self, path: str, default: _T=..., namespaces: Dict[str, str]=...) -> Union[_T, str]: ...
def findall(self, path: str, namespaces: Dict[str, str]=...) -> List[Element]: ...
def iterfind(self, path: str, namespaces: Dict[str, str]=...) -> List[Element]: ...
def iter(self, tag: _str_argument_type=...) -> Generator[Element, None, None]: ...
def getiterator(self, tag: _str_argument_type=...) -> List[Element]: ...
def find(self, path: _str_argument_type, namespaces: Dict[_str_argument_type, _str_argument_type]=...) -> Optional[Element]: ...
def findtext(self, path: _str_argument_type, default: _T=..., namespaces: Dict[_str_argument_type, _str_argument_type]=...) -> Union[_T, _str_result_type]: ...
def findall(self, path: _str_argument_type, namespaces: Dict[_str_argument_type, _str_argument_type]=...) -> List[Element]: ...
def iterfind(self, path: _str_argument_type, namespaces: Dict[_str_argument_type, _str_argument_type]=...) -> List[Element]: ...
if sys.version_info >= (3, 4):
def write(self, file_or_filename: _file_or_filename, encoding: str=..., xml_declaration: Optional[bool]=..., default_namespace: str=..., method: str=..., *, short_empty_elements: bool=...) -> None: ...
def write(self, file_or_filename: _file_or_filename, encoding: str=..., xml_declaration: Optional[bool]=..., default_namespace: _str_argument_type=..., method: str=..., *, short_empty_elements: bool=...) -> None: ...
else:
def write(self, file_or_filename: _file_or_filename, encoding: str=..., xml_declaration: Optional[bool]=..., default_namespace: str=..., method: str=...) -> None: ...
def write(self, file_or_filename: _file_or_filename, encoding: str=..., xml_declaration: Optional[bool]=..., default_namespace: _str_argument_type=..., method: str=...) -> None: ...
def write_c14n(self, file: _file_or_filename) -> None: ...
def register_namespace(prefix: str, uri: str) -> None: ...
def register_namespace(prefix: _str_argument_type, uri: _str_argument_type) -> None: ...
if sys.version_info >= (3, 4):
def tostring(element: Element, encoding: str=..., method: str=..., *, short_empty_elements: bool=...) -> str: ...
def tostringlist(element: Element, encoding: str=..., method: str=..., *, short_empty_elements: bool=...) -> List[str]: ...
def tostring(element: Element, encoding: str=..., method: str=..., *, short_empty_elements: bool=...) -> _tostring_result_type: ...
def tostringlist(element: Element, encoding: str=..., method: str=..., *, short_empty_elements: bool=...) -> List[_tostring_result_type]: ...
else:
def tostring(element: Element, encoding: str=..., method: str=...) -> str: ...
def tostringlist(element: Element, encoding: str=..., method: str=...) -> List[str]: ...
def tostring(element: Element, encoding: str=..., method: str=...) -> _tostring_result_type: ...
def tostringlist(element: Element, encoding: str=..., method: str=...) -> List[_tostring_result_type]: ...
def dump(elem: Element) -> None: ...
def parse(source: _file_or_filename, parser: 'XMLParser'=...) -> ElementTree: ...
def iterparse(source: _file_or_filename, events: Sequence[str]=..., parser: 'XMLParser'=...) -> Iterator[Tuple[str, Element]]: ...
@@ -95,20 +122,31 @@ if sys.version_info >= (3, 4):
def close(self) -> None: ...
def read_events(self) -> Iterator[Tuple[str, Element]]: ...
def XML(text: AnyStr, parser: 'XMLParser'=...) -> Element: ...
def XMLID(text: AnyStr, parser: 'XMLParser'=...) -> Tuple[Element, Dict[str, Element]]: ...
def XML(text: _parser_input_type, parser: 'XMLParser'=...) -> Element: ...
def XMLID(text: _parser_input_type, parser: 'XMLParser'=...) -> Tuple[Element, Dict[_str_result_type, Element]]: ...
# TODO-improve this type
fromstring = ... # type: Callable[..., Element]
# This is aliased to XML in the source.
fromstring = XML
def fromstringlist(sequence: Sequence[AnyStr], parser: 'XMLParser'=...) -> Element: ...
def fromstringlist(sequence: Sequence[_parser_input_type], parser: 'XMLParser'=...) -> Element: ...
# This type is both not precise enough and too precise. The TreeBuilder
# requires the elementfactory to accept tag and attrs in its args and produce
# some kind of object that has .text and .tail properties.
# I've chosen to constrain the ElementFactory to always produce an Element
# because that is how almost everyone will use it.
# Unfortunately, the type of the factory arguments is dependent on how
# TreeBuilder is called by client code (they could pass strs, bytes or whatever);
# but we don't want to use a too-broad type, or it would be too hard to write
# elementfactories.
_ElementFactory = Callable[[Any, Dict[Any, Any]], Element]
class TreeBuilder:
def __init__(self, element_factory: Callable[[AnyStr, Dict[AnyStr, AnyStr]], Element]=...) -> None: ...
def __init__(self, element_factory: _ElementFactory=...) -> None: ...
def close(self) -> Element: ...
def data(self, data: AnyStr) -> None: ...
def start(self, tag: AnyStr, attrs: Dict[AnyStr, AnyStr]) -> Element: ...
def end(self, tag: AnyStr) -> Element: ...
def data(self, data: _parser_input_type) -> None: ...
def start(self, tag: _parser_input_type, attrs: Dict[_parser_input_type, _parser_input_type]) -> Element: ...
def end(self, tag: _parser_input_type) -> Element: ...
class XMLParser:
parser = ... # type: Any
@@ -118,5 +156,5 @@ class XMLParser:
version = ... # type: str
def __init__(self, html: int=..., target: TreeBuilder=..., encoding: str=...) -> None: ...
def doctype(self, name: str, pubid: str, system: str) -> None: ...
def close(self) -> Any: ... # TODO-most of the time, this will be Element, but it can be anything target.close() returns
def feed(self, data: AnyStr)-> None: ...
def close(self) -> Element: ...
def feed(self, data: _parser_input_type) -> None: ...