"""
-#---------------------------------------------------------------------
+# ---------------------------------------------------------------------
# Licensed to PSF under a Contributor Agreement.
# See http://www.python.org/psf/license for licensing details.
#
# public symbols
"Comment",
"dump",
- "Element", "ElementTree",
- "fromstring", "fromstringlist",
- "iselement", "iterparse",
- "parse", "ParseError",
- "PI", "ProcessingInstruction",
+ "Element",
+ "ElementTree",
+ "fromstring",
+ "fromstringlist",
+ "iselement",
+ "iterparse",
+ "parse",
+ "ParseError",
+ "PI",
+ "ProcessingInstruction",
"QName",
"SubElement",
- "tostring", "tostringlist",
+ "tostring",
+ "tostringlist",
"TreeBuilder",
"VERSION",
- "XML", "XMLID",
+ "XML",
+ "XMLID",
"XMLParser",
"register_namespace",
- ]
+]
VERSION = "1.3.0"
'position' - the line and column of the error
"""
+
pass
+
# --------------------------------------------------------------------
def iselement(element):
"""Return True if *element* appears to be an Element."""
- return hasattr(element, 'tag')
+ return hasattr(element, "tag")
class Element:
def __init__(self, tag, attrib={}, **extra):
if not isinstance(attrib, dict):
- raise TypeError("attrib must be dict, not %s" % (
- attrib.__class__.__name__,))
+ raise TypeError(
+ "attrib must be dict, not %s" % (attrib.__class__.__name__,)
+ )
attrib = attrib.copy()
attrib.update(extra)
self.tag = tag
warnings.warn(
"The behavior of this method will change in future versions. "
"Use specific 'len(elem)' or 'elem is not None' test instead.",
- FutureWarning, stacklevel=2
- )
- return len(self._children) != 0 # emulate old behaviour, for now
+ FutureWarning,
+ stacklevel=2,
+ )
+ return len(self._children) != 0 # emulate old behaviour, for now
def __getitem__(self, index):
return self._children[index]
# Need to refer to the actual Python implementation, not the
# shadowing C implementation.
if not isinstance(e, _Element_Py):
- raise TypeError('expected an Element, not %s' % type(e).__name__)
+ raise TypeError("expected an Element, not %s" % type(e).__name__)
def remove(self, subelement):
"""Remove matching subelement.
warnings.warn(
"This method will be removed in future versions. "
"Use 'list(elem)' or iteration over elem instead.",
- DeprecationWarning, stacklevel=2
- )
+ DeprecationWarning,
+ stacklevel=2,
+ )
return self._children
def find(self, path, namespaces=None):
warnings.warn(
"This method will be removed in future versions. "
"Use 'elem.iter()' or 'list(elem.iter())' instead.",
- PendingDeprecationWarning, stacklevel=2
+ PendingDeprecationWarning,
+ stacklevel=2,
)
return list(self.iter(tag))
element.text = element.text + " " + text
return element
+
PI = ProcessingInstruction
be interpreted as a local name.
"""
+
def __init__(self, text_or_uri, tag=None):
if tag:
text_or_uri = "{%s}%s" % (text_or_uri, tag)
self.text = text_or_uri
+
def __str__(self):
return self.text
+
def __repr__(self):
- return '<QName %r>' % (self.text,)
+ return "<QName %r>" % (self.text,)
+
def __hash__(self):
return hash(self.text)
+
def __le__(self, other):
if isinstance(other, QName):
return self.text <= other.text
return self.text <= other
+
def __lt__(self, other):
if isinstance(other, QName):
return self.text < other.text
return self.text < other
+
def __ge__(self, other):
if isinstance(other, QName):
return self.text >= other.text
return self.text >= other
+
def __gt__(self, other):
if isinstance(other, QName):
return self.text > other.text
return self.text > other
+
def __eq__(self, other):
if isinstance(other, QName):
return self.text == other.text
return self.text == other
+
def __ne__(self, other):
if isinstance(other, QName):
return self.text != other.text
return self.text != other
+
# --------------------------------------------------------------------
contents will be used to initialize the tree with.
"""
+
def __init__(self, element=None, file=None):
# assert element is None or iselement(element)
- self._root = element # first node
+ self._root = element # first node
if file:
self.parse(file)
if parser is None:
# If no parser was specified, create a default XMLParser
parser = XMLParser()
- if hasattr(parser, '_parse_whole'):
+ if hasattr(parser, "_parse_whole"):
# The default XMLParser, when it comes from an accelerator,
# can define an internal _parse_whole API for efficiency.
# It can be used to parse the whole source without feeding
warnings.warn(
"This method will be removed in future versions. "
"Use 'tree.iter()' or 'list(tree.iter())' instead.",
- PendingDeprecationWarning, stacklevel=2
+ PendingDeprecationWarning,
+ stacklevel=2,
)
return list(self.iter(tag))
"This search is broken in 1.3 and earlier, and will be "
"fixed in a future version. If you rely on the current "
"behaviour, change it to %r" % path,
- FutureWarning, stacklevel=2
- )
+ FutureWarning,
+ stacklevel=2,
+ )
return self._root.find(path, namespaces)
def findtext(self, path, default=None, namespaces=None):
"This search is broken in 1.3 and earlier, and will be "
"fixed in a future version. If you rely on the current "
"behaviour, change it to %r" % path,
- FutureWarning, stacklevel=2
- )
+ FutureWarning,
+ stacklevel=2,
+ )
return self._root.findtext(path, default, namespaces)
def findall(self, path, namespaces=None):
"This search is broken in 1.3 and earlier, and will be "
"fixed in a future version. If you rely on the current "
"behaviour, change it to %r" % path,
- FutureWarning, stacklevel=2
- )
+ FutureWarning,
+ stacklevel=2,
+ )
return self._root.findall(path, namespaces)
def iterfind(self, path, namespaces=None):
"This search is broken in 1.3 and earlier, and will be "
"fixed in a future version. If you rely on the current "
"behaviour, change it to %r" % path,
- FutureWarning, stacklevel=2
- )
+ FutureWarning,
+ stacklevel=2,
+ )
return self._root.iterfind(path, namespaces)
- def write(self, file_or_filename,
- encoding=None,
- xml_declaration=None,
- default_namespace=None,
- method=None, *,
- short_empty_elements=True):
+ def write(
+ self,
+ file_or_filename,
+ encoding=None,
+ xml_declaration=None,
+ default_namespace=None,
+ method=None,
+ *,
+ short_empty_elements=True
+ ):
"""Write element tree to a file as XML.
Arguments:
encoding = "us-ascii"
enc_lower = encoding.lower()
with _get_writer(file_or_filename, enc_lower) as write:
- if method == "xml" and (xml_declaration or
- (xml_declaration is None and
- enc_lower not in ("utf-8", "us-ascii", "unicode"))):
+ if method == "xml" and (
+ xml_declaration
+ or (
+ xml_declaration is None
+ and enc_lower not in ("utf-8", "us-ascii", "unicode")
+ )
+ ):
declared_encoding = encoding
if enc_lower == "unicode":
# Retrieve the default encoding for the xml declaration
import locale
+
declared_encoding = locale.getpreferredencoding()
- write("<?xml version='1.0' encoding='%s'?>\n" % (
- declared_encoding,))
+ write("<?xml version='1.0' encoding='%s'?>\n" % (declared_encoding,))
if method == "text":
_serialize_text(write, self._root)
else:
qnames, namespaces = _namespaces(self._root, default_namespace)
serialize = _serialize[method]
- serialize(write, self._root, qnames, namespaces,
- short_empty_elements=short_empty_elements)
+ serialize(
+ write,
+ self._root,
+ qnames,
+ namespaces,
+ short_empty_elements=short_empty_elements,
+ )
def write_c14n(self, file):
# lxml.etree compatibility. use output method instead
return self.write(file, method="c14n")
+
# --------------------------------------------------------------------
# serialization support
+
@contextlib.contextmanager
def _get_writer(file_or_filename, encoding):
# returns text write method and release all resources after using
if encoding == "unicode":
file = open(file_or_filename, "w")
else:
- file = open(file_or_filename, "w", encoding=encoding,
- errors="xmlcharrefreplace")
+ file = open(
+ file_or_filename, "w", encoding=encoding, errors="xmlcharrefreplace"
+ )
with file:
yield file.write
else:
file.tell = file_or_filename.tell
except AttributeError:
pass
- file = io.TextIOWrapper(file,
- encoding=encoding,
- errors="xmlcharrefreplace",
- newline="\n")
+ file = io.TextIOWrapper(
+ file, encoding=encoding, errors="xmlcharrefreplace", newline="\n"
+ )
# Keep the original file open when the TextIOWrapper is
# destroyed
stack.callback(file.detach)
yield file.write
+
def _namespaces(elem, default_namespace=None):
# identify namespaces used in this tree
if prefix:
qnames[qname] = "%s:%s" % (prefix, tag)
else:
- qnames[qname] = tag # default element
+ qnames[qname] = tag # default element
else:
if default_namespace:
# FIXME: can this be handled in XML 1.0?
raise ValueError(
"cannot use non-qualified names with "
"default_namespace option"
- )
+ )
qnames[qname] = qname
except TypeError:
_raise_serialization_error(qname)
add_qname(text.text)
return qnames, namespaces
-def _serialize_xml(write, elem, qnames, namespaces,
- short_empty_elements, **kwargs):
+
+def _serialize_xml(write, elem, qnames, namespaces, short_empty_elements, **kwargs):
tag = elem.tag
text = elem.text
if tag is Comment:
if text:
write(_escape_cdata(text))
for e in elem:
- _serialize_xml(write, e, qnames, None,
- short_empty_elements=short_empty_elements)
+ _serialize_xml(
+ write, e, qnames, None, short_empty_elements=short_empty_elements
+ )
else:
write("<" + tag)
items = list(elem.items())
if items or namespaces:
if namespaces:
- for v, k in sorted(namespaces.items(),
- key=lambda x: x[1]): # sort on prefix
+ for v, k in sorted(
+ namespaces.items(), key=lambda x: x[1]
+ ): # sort on prefix
if k:
k = ":" + k
- write(" xmlns%s=\"%s\"" % (
- k,
- _escape_attrib(v)
- ))
+ write(' xmlns%s="%s"' % (k, _escape_attrib(v)))
for k, v in sorted(items): # lexical order
if isinstance(k, QName):
k = k.text
v = qnames[v.text]
else:
v = _escape_attrib(v)
- write(" %s=\"%s\"" % (qnames[k], v))
+ write(' %s="%s"' % (qnames[k], v))
if text or len(elem) or not short_empty_elements:
write(">")
if text:
write(_escape_cdata(text))
for e in elem:
- _serialize_xml(write, e, qnames, None,
- short_empty_elements=short_empty_elements)
+ _serialize_xml(
+ write,
+ e,
+ qnames,
+ None,
+ short_empty_elements=short_empty_elements,
+ )
write("</" + tag + ">")
else:
write(" />")
if elem.tail:
write(_escape_cdata(elem.tail))
+
# add from cvw jan 2019
-def _serialize_pretty_xml(write, elem, qnames, namespaces,
- short_empty_elements, indent=0):
+def _serialize_pretty_xml(
+ write, elem, qnames, namespaces, short_empty_elements, indent=0
+):
# print("*****pretty***** indent", elem.tag, indent)
tag = elem.tag
text = elem.text
if tag is Comment:
- write("<!--%s-->" % text)
+ write("<!--%s-->" % text)
elif tag is ProcessingInstruction:
- write("<?%s?>" % text)
+ write("<?%s?>" % text)
else:
- tag = qnames[tag]
- if tag is None:
- if text:
- write(_escape_cdata(text))
- for e in elem:
- _serialize_pretty_xml(write, e, qnames, None,
- short_empty_elements=short_empty_elements, indent=indent)
- else:
- write(" "*indent + "<" + tag)
- items = list(elem.items())
- if items or namespaces:
- if namespaces:
- for v, k in sorted(namespaces.items(),
- key=lambda x: x[1]): # sort on prefix
- if k:
- k = ":" + k
- write(" xmlns%s=\"%s\"" % (
- k,
- _escape_attrib(v)
- ))
- for k, v in sorted(items): # lexical order
- # print("atrrib ", k, v)
- if isinstance(k, QName):
- k = k.text
- if isinstance(v, QName):
- v = qnames[v.text]
- else:
- v = _escape_attrib(v)
- write(" %s=\"%s\"" % (qnames[k], v))
- if text or len(elem) or not short_empty_elements:
- if text:
- write(">")
- write(_escape_cdata(text))
- else:
- write(">\n")
-
- for e in elem:
- _serialize_pretty_xml(write, e, qnames, None,
- short_empty_elements=short_empty_elements, indent=indent+2)
- write(" "*indent + "</" + tag + ">\n")
+ tag = qnames[tag]
+ if tag is None:
+ if text:
+ write(_escape_cdata(text))
+ for e in elem:
+ _serialize_pretty_xml(
+ write,
+ e,
+ qnames,
+ None,
+ short_empty_elements=short_empty_elements,
+ indent=indent,
+ )
else:
- write(" />\n")
+ write(" " * indent + "<" + tag)
+ items = list(elem.items())
+ if items or namespaces:
+ if namespaces:
+ for v, k in sorted(
+ namespaces.items(), key=lambda x: x[1]
+ ): # sort on prefix
+ if k:
+ k = ":" + k
+ write(' xmlns%s="%s"' % (k, _escape_attrib(v)))
+ for k, v in sorted(items): # lexical order
+ # print("atrrib ", k, v)
+ if isinstance(k, QName):
+ k = k.text
+ if isinstance(v, QName):
+ v = qnames[v.text]
+ else:
+ v = _escape_attrib(v)
+ write(' %s="%s"' % (qnames[k], v))
+ if text or len(elem) or not short_empty_elements:
+ if text:
+ write(">")
+ write(_escape_cdata(text))
+ else:
+ write(">\n")
+
+ for e in elem:
+ _serialize_pretty_xml(
+ write,
+ e,
+ qnames,
+ None,
+ short_empty_elements=short_empty_elements,
+ indent=indent + 2,
+ )
+ write(" " * indent + "</" + tag + ">\n")
+ else:
+ write(" />\n")
if elem.tail:
- write(_escape_cdata(elem.tail))
+ write(_escape_cdata(elem.tail))
-HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr",
- "img", "input", "isindex", "link", "meta", "param")
+HTML_EMPTY = (
+ "area",
+ "base",
+ "basefont",
+ "br",
+ "col",
+ "frame",
+ "hr",
+ "img",
+ "input",
+ "isindex",
+ "link",
+ "meta",
+ "param",
+)
try:
HTML_EMPTY = set(HTML_EMPTY)
except NameError:
pass
+
def _serialize_html(write, elem, qnames, namespaces, **kwargs):
tag = elem.tag
text = elem.text
items = list(elem.items())
if items or namespaces:
if namespaces:
- for v, k in sorted(namespaces.items(),
- key=lambda x: x[1]): # sort on prefix
+ for v, k in sorted(
+ namespaces.items(), key=lambda x: x[1]
+ ): # sort on prefix
if k:
k = ":" + k
- write(" xmlns%s=\"%s\"" % (
- k,
- _escape_attrib(v)
- ))
+ write(' xmlns%s="%s"' % (k, _escape_attrib(v)))
for k, v in sorted(items): # lexical order
if isinstance(k, QName):
k = k.text
else:
v = _escape_attrib_html(v)
# FIXME: handle boolean attributes
- write(" %s=\"%s\"" % (qnames[k], v))
+ write(' %s="%s"' % (qnames[k], v))
write(">")
ltag = tag.lower()
if text:
if elem.tail:
write(_escape_cdata(elem.tail))
+
def _serialize_text(write, elem):
for part in elem.itertext():
write(part)
if elem.tail:
write(elem.tail)
+
_serialize = {
"xml": _serialize_xml,
"pretty_xml": _serialize_pretty_xml,
"html": _serialize_html,
"text": _serialize_text,
-# this optional method is imported at the end of the module
-# "c14n": _serialize_c14n,
+ # this optional method is imported at the end of the module
+ # "c14n": _serialize_c14n,
}
del _namespace_map[k]
_namespace_map[uri] = prefix
+
_namespace_map = {
# "well-known" namespace prefixes
"http://www.w3.org/XML/1998/namespace": "xml",
# For tests and troubleshooting
register_namespace._namespace_map = _namespace_map
+
def _raise_serialization_error(text):
- raise TypeError(
- "cannot serialize %r (type %s)" % (text, type(text).__name__)
- )
+ raise TypeError("cannot serialize %r (type %s)" % (text, type(text).__name__))
+
def _escape_cdata(text):
# escape character data
except (TypeError, AttributeError):
_raise_serialization_error(text)
+
def _escape_attrib(text):
# escape attribute value
try:
text = text.replace("<", "<")
if ">" in text:
text = text.replace(">", ">")
- if "\"" in text:
- text = text.replace("\"", """)
+ if '"' in text:
+ text = text.replace('"', """)
if "\n" in text:
text = text.replace("\n", " ")
return text
except (TypeError, AttributeError):
_raise_serialization_error(text)
+
def _escape_attrib_html(text):
# escape attribute value
try:
text = text.replace("&", "&")
if ">" in text:
text = text.replace(">", ">")
- if "\"" in text:
- text = text.replace("\"", """)
+ if '"' in text:
+ text = text.replace('"', """)
return text
except (TypeError, AttributeError):
_raise_serialization_error(text)
+
# --------------------------------------------------------------------
-def tostring(element, encoding=None, method=None, *,
- short_empty_elements=True):
+
+def tostring(element, encoding=None, method=None, *, short_empty_elements=True):
"""Generate string representation of XML element.
All subelements are included. If encoding is "unicode", a string
Returns an (optionally) encoded string containing the XML data.
"""
- stream = io.StringIO() if encoding == 'unicode' else io.BytesIO()
- ElementTree(element).write(stream, encoding, method=method,
- short_empty_elements=short_empty_elements)
+ stream = io.StringIO() if encoding == "unicode" else io.BytesIO()
+ ElementTree(element).write(
+ stream, encoding, method=method, short_empty_elements=short_empty_elements
+ )
return stream.getvalue()
+
class _ListDataStream(io.BufferedIOBase):
"""An auxiliary stream accumulating into a list reference."""
+
def __init__(self, lst):
self.lst = lst
def tell(self):
return len(self.lst)
-def tostringlist(element, encoding=None, method=None, *,
- short_empty_elements=True):
+
+def tostringlist(element, encoding=None, method=None, *, short_empty_elements=True):
lst = []
stream = _ListDataStream(lst)
- ElementTree(element).write(stream, encoding, method=method,
- short_empty_elements=short_empty_elements)
+ ElementTree(element).write(
+ stream, encoding, method=method, short_empty_elements=short_empty_elements
+ )
return lst
if not tail or tail[-1] != "\n":
sys.stdout.write("\n")
+
# --------------------------------------------------------------------
# parsing
class XMLPullParser:
-
def __init__(self, events=None, *, _parser=None):
# The _parser argument is for internal use only and must not be relied
# upon in user code. It will be removed in a future release.
class _IterParseIterator:
-
def __init__(self, source, events, parser, close_source=False):
# Use the internal, undocumented _parser argument for now; When the
# parser argument of iterparse is removed, this can be killed.
ids[id] = elem
return tree, ids
+
# Parse XML document from string constant. Alias for XML().
fromstring = XML
+
def fromstringlist(sequence, parser=None):
"""Parse XML document from sequence of string fragments.
parser.feed(text)
return parser.close()
+
# --------------------------------------------------------------------
to create new Element instances, as necessary.
"""
+
def __init__(self, element_factory=None):
- self._data = [] # data collector
- self._elem = [] # element stack
- self._last = None # last element
- self._tail = None # true if we're after an end tag
+ self._data = [] # data collector
+ self._elem = [] # element stack
+ self._last = None # last element
+ self._tail = None # true if we're after an end tag
if element_factory is None:
element_factory = Element
self._factory = element_factory
"""
self._flush()
self._last = self._elem.pop()
- assert self._last.tag == tag,\
- "end tag mismatch (expected %s, got %s)" % (
- self._last.tag, tag)
+ assert self._last.tag == tag, "end tag mismatch (expected %s, got %s)" % (
+ self._last.tag,
+ tag,
+ )
self._tail = 1
return self._last
except ImportError:
raise ImportError(
"No module named expat; use SimpleXMLTreeBuilder instead"
- )
+ )
parser = expat.ParserCreate(encoding, "}")
if target is None:
target = TreeBuilder()
self.parser = self._parser = parser
self.target = self._target = target
self._error = expat.error
- self._names = {} # name memo cache
+ self._names = {} # name memo cache
# main callbacks
parser.DefaultHandlerExpand = self._default
- if hasattr(target, 'start'):
+ if hasattr(target, "start"):
parser.StartElementHandler = self._start
- if hasattr(target, 'end'):
+ if hasattr(target, "end"):
parser.EndElementHandler = self._end
- if hasattr(target, 'data'):
+ if hasattr(target, "data"):
parser.CharacterDataHandler = target.data
# miscellaneous callbacks
- if hasattr(target, 'comment'):
+ if hasattr(target, "comment"):
parser.CommentHandler = target.comment
- if hasattr(target, 'pi'):
+ if hasattr(target, "pi"):
parser.ProcessingInstructionHandler = target.pi
# Configure pyexpat: buffering, new-style attribute handling.
parser.buffer_text = 1
try:
self.version = "Expat %d.%d.%d" % expat.version_info
except AttributeError:
- pass # unknown
+ pass # unknown
def _setevents(self, events_queue, events_to_report):
# Internal API for XMLPullParser
if event_name == "start":
parser.ordered_attributes = 1
parser.specified_attributes = 1
- def handler(tag, attrib_in, event=event_name, append=append,
- start=self._start):
+
+ def handler(
+ tag, attrib_in, event=event_name, append=append, start=self._start
+ ):
append((event, start(tag, attrib_in)))
+
parser.StartElementHandler = handler
elif event_name == "end":
- def handler(tag, event=event_name, append=append,
- end=self._end):
+
+ def handler(tag, event=event_name, append=append, end=self._end):
append((event, end(tag)))
+
parser.EndElementHandler = handler
elif event_name == "start-ns":
+
def handler(prefix, uri, event=event_name, append=append):
append((event, (prefix or "", uri or "")))
+
parser.StartNamespaceDeclHandler = handler
elif event_name == "end-ns":
+
def handler(prefix, event=event_name, append=append):
append((event, None))
+
parser.EndNamespaceDeclHandler = handler
else:
raise ValueError("unknown event %r" % event_name)
attrib = {}
if attr_list:
for i in range(0, len(attr_list), 2):
- attrib[fixname(attr_list[i])] = attr_list[i+1]
+ attrib[fixname(attr_list[i])] = attr_list[i + 1]
return self.target.start(tag, attrib)
def _end(self, tag):
data_handler(self.entity[text[1:-1]])
except KeyError:
from xml.parsers import expat
+
err = expat.error(
- "undefined entity %s: line %d, column %d" %
- (text, self.parser.ErrorLineNumber,
- self.parser.ErrorColumnNumber)
- )
- err.code = 11 # XML_ERROR_UNDEFINED_ENTITY
+ "undefined entity %s: line %d, column %d"
+ % (text, self.parser.ErrorLineNumber, self.parser.ErrorColumnNumber)
+ )
+ err.code = 11 # XML_ERROR_UNDEFINED_ENTITY
err.lineno = self.parser.ErrorLineNumber
err.offset = self.parser.ErrorColumnNumber
raise err
elif prefix == "<" and text[:9] == "<!DOCTYPE":
- self._doctype = [] # inside a doctype declaration
+ self._doctype = [] # inside a doctype declaration
elif self._doctype is not None:
# parse doctype contents
if prefix == ">":
"This method of XMLParser is deprecated. Define doctype() "
"method on the TreeBuilder target.",
DeprecationWarning,
- )
+ )
# sentinel, if doctype is redefined in a subclass
__doctype = doctype
def close(self):
"""Finish feeding data to parser and return element structure."""
try:
- self.parser.Parse("", 1) # end of data
+ self.parser.Parse("", 1) # end of data
except self._error as v:
self._raiseerror(v)
try:
# Element, SubElement, ParseError, TreeBuilder, XMLParser
from _elementtree import *
except ImportError:
- pass
\ No newline at end of file
+ pass