Path: blob/master/venv/Lib/site-packages/lxml/builder.py
811 views
# cython: language_level=212#3# Element generator factory by Fredrik Lundh.4#5# Source:6# http://online.effbot.org/2006_11_01_archive.htm#et-builder7# http://effbot.python-hosting.com/file/stuff/sandbox/elementlib/builder.py8#9# --------------------------------------------------------------------10# The ElementTree toolkit is11#12# Copyright (c) 1999-2004 by Fredrik Lundh13#14# By obtaining, using, and/or copying this software and/or its15# associated documentation, you agree that you have read, understood,16# and will comply with the following terms and conditions:17#18# Permission to use, copy, modify, and distribute this software and19# its associated documentation for any purpose and without fee is20# hereby granted, provided that the above copyright notice appears in21# all copies, and that both that copyright notice and this permission22# notice appear in supporting documentation, and that the name of23# Secret Labs AB or the author not be used in advertising or publicity24# pertaining to distribution of the software without specific, written25# prior permission.26#27# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD28# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-29# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR30# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY31# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,32# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS33# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE34# OF THIS SOFTWARE.35# --------------------------------------------------------------------3637"""38The ``E`` Element factory for generating XML documents.39"""4041from __future__ import absolute_import4243import lxml.etree as ET4445from functools import partial4647try:48basestring49except NameError:50basestring = str5152try:53unicode54except NameError:55unicode = str565758class ElementMaker(object):59"""Element generator factory.6061Unlike the ordinary Element factory, the E factory allows you to pass in62more than just a tag and some optional attributes; you can also pass in63text and other elements. The text is added as either text or tail64attributes, and elements are inserted at the right spot. Some small65examples::6667>>> from lxml import etree as ET68>>> from lxml.builder import E6970>>> ET.tostring(E("tag"))71'<tag/>'72>>> ET.tostring(E("tag", "text"))73'<tag>text</tag>'74>>> ET.tostring(E("tag", "text", key="value"))75'<tag key="value">text</tag>'76>>> ET.tostring(E("tag", E("subtag", "text"), "tail"))77'<tag><subtag>text</subtag>tail</tag>'7879For simple tags, the factory also allows you to write ``E.tag(...)`` instead80of ``E('tag', ...)``::8182>>> ET.tostring(E.tag())83'<tag/>'84>>> ET.tostring(E.tag("text"))85'<tag>text</tag>'86>>> ET.tostring(E.tag(E.subtag("text"), "tail"))87'<tag><subtag>text</subtag>tail</tag>'8889Here's a somewhat larger example; this shows how to generate HTML90documents, using a mix of prepared factory functions for inline elements,91nested ``E.tag`` calls, and embedded XHTML fragments::9293# some common inline elements94A = E.a95I = E.i96B = E.b9798def CLASS(v):99# helper function, 'class' is a reserved word100return {'class': v}101102page = (103E.html(104E.head(105E.title("This is a sample document")106),107E.body(108E.h1("Hello!", CLASS("title")),109E.p("This is a paragraph with ", B("bold"), " text in it!"),110E.p("This is another paragraph, with a ",111A("link", href="http://www.python.org"), "."),112E.p("Here are some reserved characters: <spam&egg>."),113ET.XML("<p>And finally, here is an embedded XHTML fragment.</p>"),114)115)116)117118print ET.tostring(page)119120Here's a prettyprinted version of the output from the above script::121122<html>123<head>124<title>This is a sample document</title>125</head>126<body>127<h1 class="title">Hello!</h1>128<p>This is a paragraph with <b>bold</b> text in it!</p>129<p>This is another paragraph, with <a href="http://www.python.org">link</a>.</p>130<p>Here are some reserved characters: <spam&egg>.</p>131<p>And finally, here is an embedded XHTML fragment.</p>132</body>133</html>134135For namespace support, you can pass a namespace map (``nsmap``)136and/or a specific target ``namespace`` to the ElementMaker class::137138>>> E = ElementMaker(namespace="http://my.ns/")139>>> print(ET.tostring( E.test ))140<test xmlns="http://my.ns/"/>141142>>> E = ElementMaker(namespace="http://my.ns/", nsmap={'p':'http://my.ns/'})143>>> print(ET.tostring( E.test ))144<p:test xmlns:p="http://my.ns/"/>145"""146147def __init__(self, typemap=None,148namespace=None, nsmap=None, makeelement=None):149if namespace is not None:150self._namespace = '{' + namespace + '}'151else:152self._namespace = None153154if nsmap:155self._nsmap = dict(nsmap)156else:157self._nsmap = None158159if makeelement is not None:160assert callable(makeelement)161self._makeelement = makeelement162else:163self._makeelement = ET.Element164165# initialize type map for this element factory166167if typemap:168typemap = dict(typemap)169else:170typemap = {}171172def add_text(elem, item):173try:174elem[-1].tail = (elem[-1].tail or "") + item175except IndexError:176elem.text = (elem.text or "") + item177178def add_cdata(elem, cdata):179if elem.text:180raise ValueError("Can't add a CDATA section. Element already has some text: %r" % elem.text)181elem.text = cdata182183if str not in typemap:184typemap[str] = add_text185if unicode not in typemap:186typemap[unicode] = add_text187if ET.CDATA not in typemap:188typemap[ET.CDATA] = add_cdata189190def add_dict(elem, item):191attrib = elem.attrib192for k, v in item.items():193if isinstance(v, basestring):194attrib[k] = v195else:196attrib[k] = typemap[type(v)](None, v)197if dict not in typemap:198typemap[dict] = add_dict199200self._typemap = typemap201202def __call__(self, tag, *children, **attrib):203typemap = self._typemap204205if self._namespace is not None and tag[0] != '{':206tag = self._namespace + tag207elem = self._makeelement(tag, nsmap=self._nsmap)208if attrib:209typemap[dict](elem, attrib)210211for item in children:212if callable(item):213item = item()214t = typemap.get(type(item))215if t is None:216if ET.iselement(item):217elem.append(item)218continue219for basetype in type(item).__mro__:220# See if the typemap knows of any of this type's bases.221t = typemap.get(basetype)222if t is not None:223break224else:225raise TypeError("bad argument type: %s(%r)" %226(type(item).__name__, item))227v = t(elem, item)228if v:229typemap.get(type(v))(elem, v)230231return elem232233def __getattr__(self, tag):234return partial(self, tag)235236237# create factory object238E = ElementMaker()239240241