root/livinglogic.python.xist/src/ll/xist/xsc.py @ 4498:9abeb7654261

Revision 4498:9abeb7654261, 120.3 KB (checked in by Walter Doerwald <walter@…>, 8 years ago)

Don't call misc.Pool.register() in xsc.Pool.register().

Line 
1# -*- coding: utf-8 -*-
2
3## Copyright 1999-2011 by LivingLogic AG, Bayreuth/Germany
4## Copyright 1999-2011 by Walter Dörwald
5##
6## All Rights Reserved
7##
8## See ll/__init__.py for the license
9
10
11"""
12This module contains all the central XML tree classes, the namespace classes,
13exception and warning classes and a few helper classes and functions.
14"""
15
16
17__docformat__ = "reStructuredText"
18
19
20import sys, os, random, copy, warnings, cPickle, threading, weakref, types, codecs
21
22import cssutils
23
24from ll import misc, url as url_, xml_codec
25
26try:
27    import astyle
28except ImportError:
29    astyle = None
30
31# IPython/ipipe support
32try:
33    import ipipe
34except ImportError:
35    ipipe = None
36
37
38__docformat__ = "reStructuredText"
39
40
41xml_xmlns = "http://www.w3.org/XML/1998/namespace"
42
43
44###
45### helpers
46###
47
48def tonode(value):
49    """
50    convert :var:`value` to an XIST :class:`Node`.
51
52    If :var:`value` is a tuple or list, it will be (recursively) converted to a
53    :class:`Frag`. Integers, strings, etc. will be converted to a :class:`Text`.
54    If :var:`value` is a :class:`Node` already, it will be returned unchanged.
55    In the case of :const:`None` the XIST Null (:data:`ll.xist.xsc.Null`) will be
56    returned. If :var:`value` is iterable, a :class:`Frag` will be generated
57    from the items. Anything else will raise an :exc:`IllegalObjectError`
58    exception.
59    """
60    if isinstance(value, Node):
61        if isinstance(value, Attrs):
62            raise IllegalObjectError(value)
63        # we don't have to turn an Attr into a Frag, because this will be done once the Attr is put back into the tree
64        return value
65    elif isinstance(value, (basestring, int, long, float)):
66        return Text(value)
67    elif value is None:
68        return Null
69    elif isinstance(value, (list, tuple)):
70        return Frag(*value)
71    elif isinstance(value, url_.URL):
72        return Text(value)
73    elif not isinstance(value, _Node_Meta): # avoid Node classes (whose __getitem__() returns an xfind selector)
74        # Maybe it's an iterator/generator?
75        try:
76            value = iter(value)
77        except TypeError:
78            pass
79        else:
80            return Frag(*list(value))
81    raise IllegalObjectError(value) # none of the above => bail out
82
83
84class ThreadLocalNodeHander(threading.local):
85    handler = None
86
87threadlocalnodehandler = ThreadLocalNodeHander()
88
89
90class build(object):
91    """
92    A :class:`build` object can be used as a ``with`` block handler to create a
93    new XIST tree::
94
95        with xsc.build():
96            with html.ul() as e:
97                +html.li("gurk")
98                +html.li("hurz")
99    """
100
101    def __init__(self):
102        self.stack = []
103
104    def __enter__(self):
105        self.prev = threadlocalnodehandler.handler
106        threadlocalnodehandler.handler = self
107
108    def __exit__(self, type, value, traceback):
109        threadlocalnodehandler.handler = self.prev
110        del self.prev
111
112    def enter(self, node):
113        if self.stack:
114            self.stack[-1](node)
115        self.stack.append(node)
116        return node
117
118    def exit(self):
119        self.stack.pop()
120
121    def add(self, *args, **kwargs):
122        self.stack[-1](*args, **kwargs)
123
124
125class addattr(object):
126    """
127    An :class:`addattr` object can be used as a ``with`` block handler to modify
128    an attribute of an element::
129
130        with xsc.build():
131            with html.div() as e:
132                with xsc.addattr("align"):
133                    +xsc.Text("right")
134    """
135    def __init__(self, attrname):
136        """
137        Create an :class:`addattr` object for adding to the attribute named
138        :var:`attrname` (which can be the Python name of an attribute or an
139        attribute class).
140        """
141        self.attr = threadlocalnodehandler.handler.stack[-1][attrname]
142
143    def __enter__(self):
144        threadlocalnodehandler.handler.stack.append(self.attr)
145        return self.attr
146
147    def __exit__(self, type, value, traceback):
148        threadlocalnodehandler.handler.stack.pop()
149
150    def add(self, *args):
151        self.attr(*args)
152
153
154def add(*args, **kwargs):
155    """
156    :func:`add` appends items in :var:`args` and sets attributes in
157    :var:`kwargs` in the currenly active node in the ``with`` stack.
158    """
159    threadlocalnodehandler.handler.add(*args, **kwargs)
160
161
162###
163### Conversion context
164###
165
166class Context(object):
167    """
168    This is an empty class that can be used by the :meth:`convert` method to
169    hold element or namespace specific data during the :meth:`convert` call.
170    The method :meth:`Converter.__getitem__` will return a unique instance of
171    this class.
172    """
173    __fullname__ = "Context"
174
175
176###
177### Exceptions and warnings
178###
179
180class Error(Exception):
181    """
182    Base class for all XIST exceptions
183    """
184    pass
185
186
187class Warning(UserWarning):
188    """
189    Base class for all warning exceptions (i.e. those that won't result in a
190    program termination.)
191    """
192    pass
193
194
195class IllegalAttrValueWarning(Warning):
196    """
197    Warning that is issued when an attribute has an illegal value when parsing
198    or publishing.
199    """
200
201    def __init__(self, attr):
202        self.attr = attr
203
204    def __str__(self):
205        attr = self.attr
206        return "Attribute value {!r} not allowed for {}".format(unicode(attr), attr._str(fullname=True, xml=False, decorate=False))
207
208
209class RequiredAttrMissingWarning(Warning):
210    """
211    Warning that is issued when a required attribute is missing during parsing
212    or publishing.
213    """
214
215    def __init__(self, attrs, reqattrs):
216        self.attrs = attrs
217        self.reqattrs = reqattrs
218
219    def __str__(self):
220        return "Required attribute{} {} missing in {}".format(("s" if len(self.reqattrs) > 1 else ""), ", ".join(repr(attr) for attr in self.reqattrs), self.attrs._str(fullname=True, xml=False, decorate=False))
221
222
223class IllegalPrefixError(Error, LookupError):
224    """
225    Exception that is raised when a namespace prefix is undefined.
226    """
227    def __init__(self, prefix):
228        self.prefix = prefix
229
230    def __str__(self):
231        return "namespace prefix {} is undefined".format(self.prefix)
232
233
234class IllegalElementError(Error, LookupError):
235    """
236    Exception that is raised when an illegal element class is requested
237    """
238
239    def __init__(self, name, xmlns, xml=False):
240        self.name = name
241        self.xmlns = xmlns
242        self.xml = xml
243
244    def __str__(self):
245        xmlns = self.xmlns
246        if isinstance(xmlns, (list, tuple)):
247            if len(xmlns) > 1:
248                return "no element with {} name {} in namespaces {}".format("XML" if self.xml else "Python", self.name, ", ".join(nsclark(xmlns) for xmlns in xmlns))
249            xmlns = xmlns[0]
250        return "no element with {} name {}{}".format("XML" if self.xml else "Python", nsclark(xmlns), self.name)
251
252
253class IllegalProcInstError(Error, LookupError):
254    """
255    Exception that is raised when an illegal procinst class is requested
256    """
257
258    def __init__(self, name, xml=False):
259        self.name = name
260        self.xml = xml
261
262    def __str__(self):
263        return "no procinst with {} name {}".format("XML" if self.xml else "Python", self.name)
264
265
266class IllegalEntityError(Error, LookupError):
267    """
268    Exception that is raised when an illegal entity class is requested
269    """
270
271    def __init__(self, name, xml=False):
272        self.name = name
273        self.xml = xml
274
275    def __str__(self):
276        return "no entity with {} name {}".format("XML" if self.xml else "Python", self.name)
277
278
279class IllegalCharRefError(Error, LookupError):
280    """
281    Exception that is raised when an illegal charref class is requested
282    """
283
284    def __init__(self, name, xml=False):
285        self.name = name
286        self.xml = xml
287
288    def __str__(self):
289        if isinstance(self.name, (int, long)):
290            return "no charref with codepoint {}".format(self.name)
291        return "no charref with {} name {}".format("XML" if self.xml else "Python", self.name)
292
293
294class IllegalAttrError(Error, LookupError):
295    """
296    Exception that is raised when an illegal attribute is requested.
297    """
298
299    def __init__(self, name, cls, xml=False):
300        self.name = name
301        self.cls = cls
302        self.xml = xml
303
304    def __str__(self):
305        if isinstance(self.name, basestring):
306            return "no local attribute with {} name {!r} in {!r}".format("XML" if self.xml else "Python", self.name, self.cls)
307        elif self.name.xmlns is None:
308            return "no local attribute with class {!r} in {!r}".format(self.name, self.cls)
309        else:
310            return "no global attribute with class {!r}".format(self.name)
311
312
313class MultipleRootsError(Error):
314    def __str__(self):
315        return "can't add namespace attributes: XML tree has multiple roots"
316
317
318class ElementNestingError(Error):
319    """
320    Exception that is raised when an element has an illegal nesting
321    (e.g. ``<a><b></a></b>``)
322    """
323
324    def __init__(self, expectedelement, foundelement):
325        self.expectedelement = expectedelement
326        self.foundelement = foundelement
327
328    def __str__(self):
329        return "mismatched element nesting (close tag for {} expected; close tag for {} found)".format(self.expectedelement._str(fullname=True, xml=False, decorate=True), self.foundelement._str(fullname=True, xml=False, decorate=True))
330
331
332class FileNotFoundWarning(Warning):
333    """
334    Warning that is issued when a file can't be found.
335    """
336    def __init__(self, message, filename, exc):
337        Warning.__init__(self, message, filename, exc)
338        self.message = message
339        self.filename = filename
340        self.exc = exc
341
342    def __str__(self):
343        return "{0.message}: {0.filename!r} not found ({0.exc})".format(self)
344
345
346class IllegalObjectError(Error, TypeError):
347    """
348    Exception that is raised when an XIST constructor gets passed an
349    unconvertable object.
350    """
351
352    def __init__(self, object):
353        self.object = object
354
355    def __str__(self):
356        return "can't convert object {!r} of type {} to an XIST node".format(self.object, type(self.object).__name__)
357
358
359class IllegalCommentContentWarning(Warning):
360    """
361    Warning that is issued when there is an illegal comment, i.e. one containing
362    ``--`` or ending in ``-``. (This can only happen when the comment was created
363    by code, not when parsed from an XML file.)
364    """
365
366    def __init__(self, comment):
367        self.comment = comment
368
369    def __str__(self):
370        return "comment with content {!r} is illegal, as it contains '--' or ends in '-'".format(self.comment.content)
371
372
373class IllegalProcInstFormatError(Error):
374    """
375    Exception that is raised when there is an illegal processing instruction,
376    i.e. one containing ``?>``. (This can only happen when the processing
377    instruction was created by code, not when parsed from an XML file.)
378    """
379
380    def __init__(self, procinst):
381        self.procinst = procinst
382
383    def __str__(self):
384        return "processing instruction with content {!r} is illegal, as it contains '?>'".format(self.procinst.content)
385
386
387###
388### Context containing state during :meth:`convert` calls
389###
390
391class ConverterState(object):
392    def __init__(self, node, root, mode, stage, target, lang, makeaction, makeproject):
393        self.node = node
394        self.root = root
395        self.mode = mode
396        self.stage = stage
397        if target is None:
398            from ll.xist.ns import html
399            target = html
400        self.target = target
401        self.lang = lang
402        self.makeaction = makeaction
403        self.makeproject = makeproject
404
405
406class Converter(object):
407    """
408    An instance of this class is passed around in calls to the :meth:`convert`
409    method. A :class:`Converter` object can be used when some element needs to
410    keep state across a nested :meth:`convert` call. A typical example are nested
411    chapter/subchapter elements with automatic numbering. For an example see the
412    element :class:`ll.xist.ns.doc.section`.
413    """
414
415    def __init__(self, node=None, root=None, mode=None, stage=None, target=None, lang=None, makeaction=None, makeproject=None):
416        """
417        Create a :class:`Converter`. Arguments are used to initialize the
418        :class:`Converter` properties of the same name.
419        """
420        self.states = [ ConverterState(node=node, root=root, mode=mode, stage=stage, target=target, lang=lang, makeaction=makeaction, makeproject=makeproject) ]
421        self.contexts = {}
422
423    class node(misc.propclass):
424        """
425        The root node for which conversion has been called. This is automatically
426        set by the :meth:`conv` method of :class:`Node` objects.
427        """
428        def __get__(self):
429            return self.states[-1].node
430
431        def __set__(self, node):
432            self.states[-1].node = node
433
434        def __delete__(self):
435            self.states[-1].node = None
436
437    class root(misc.propclass):
438        """
439        The root URL for the conversion. Resolving URLs during the conversion
440        process should be done relative to :prop:`root`.
441        """
442        def __get__(self):
443            return self.states[-1].root
444
445        def __set__(self, root):
446            self.states[-1].root = root
447
448        def __delete__(self):
449            self.states[-1].root = None
450
451    class mode(misc.propclass):
452        """
453        The conversion mode. This corresponds directly to the mode in XSLT.
454        The default is :const:`None`.
455        """
456        def __get__(self):
457            return self.states[-1].mode
458
459        def __set__(self, mode):
460            self.states[-1].mode = mode
461
462        def __delete__(self):
463            self.states[-1].mode = None
464
465    class stage(misc.propclass):
466        """
467        If your conversion is done in multiple steps or stages you can use this
468        property to specify in which stage the conversion process currently is.
469        The default is :const:`"deliver"`.
470        """
471        def __get__(self):
472            if self.states[-1].stage is None:
473                return "deliver"
474            else:
475                return self.states[-1].stage
476
477        def __set__(self, stage):
478            self.states[-1].stage = stage
479
480        def __delete__(self):
481            self.states[-1].stage = None
482
483    class target(misc.propclass):
484        """
485        Specifies the conversion target. This must be a namespace module or
486        similar object.
487        """
488        def __get__(self):
489            return self.states[-1].target
490
491        def __set__(self, target):
492            self.states[-1].target = target
493
494        def __delete__(self):
495            self.states[-1].target = None
496
497    class lang(misc.propclass):
498        """
499        The target language. The default is :const:`None`.
500        """
501        def __get__(self):
502            return self.states[-1].lang
503
504        def __set__(self, lang):
505            self.states[-1].lang = lang
506
507        def __delete__(self):
508            self.states[-1].lang = None
509
510    class makeaction(misc.propclass):
511        """
512        If an XIST conversion is done by an :class:`ll.make.XISTConvertAction`
513        this property will hold the action object during that conversion. If
514        you're not using the :mod:`ll.make` module you can simply ignore this
515        property. The default is :const:`None`.
516        """
517        def __get__(self):
518            return self.states[-1].makeaction
519
520        def __set__(self, makeaction):
521            self.states[-1].makeaction = makeaction
522
523        def __delete__(self):
524            self.states[-1].makeaction = None
525
526    class makeproject(misc.propclass):
527        """
528        If an XIST conversion is done by an :class:`ll.make.XISTConvertAction`
529        this property will hold the :class:`Project` object during that conversion.
530        If you're not using the :mod:`ll.make` module you can simply ignore this
531        property.
532        """
533        def __get__(self):
534            return self.states[-1].makeproject
535
536        def __set__(self, makeproject):
537            self.states[-1].makeproject = makeproject
538
539        def __delete__(self):
540            self.states[-1].makeproject = None
541
542    def push(self, node=None, root=None, mode=None, stage=None, target=None, lang=None, makeaction=None, makeproject=None):
543        self.lastnode = None
544        if node is None:
545            node = self.node
546        if root is None:
547            root = self.root
548        if mode is None:
549            mode = self.mode
550        if stage is None:
551            stage = self.stage
552        if target is None:
553            target = self.target
554        if lang is None:
555            lang = self.lang
556        if makeaction is None:
557            makeaction = self.makeaction
558        if makeproject is None:
559            makeproject = self.makeproject
560        self.states.append(ConverterState(node=node, root=root, mode=mode, stage=stage, target=target, lang=lang, makeaction=makeaction, makeproject=makeproject))
561
562    def pop(self):
563        if len(self.states) == 1:
564            raise IndexError("can't pop last state")
565        state = self.states.pop()
566        self.lastnode = state.node
567        return state
568
569    def __getitem__(self, obj):
570        """
571        Return a context object for :var:`obj`, which should be an
572        :class:`ll.xist.xsc.Node` instance or subclass. Each of these classes
573        that defines its own :class:`Context` class gets a unique instance of
574        this class. This instance will be created on the first access and the
575        element can store information there that needs to be available across
576        calls to :meth:`convert`.
577        """
578        contextclass = obj.Context
579        # don't use :meth:`setdefault`, as constructing the context object might involve some overhead
580        try:
581            return self.contexts[contextclass]
582        except KeyError:
583            context = contextclass()
584            self.contexts[contextclass] = context
585            return context
586
587
588###
589### Publisher for serializing XML trees to strings
590###
591
592class Publisher(object):
593    """
594    A :class:`Publisher` object is used for serializing an XIST tree into a byte
595    sequence.
596    """
597
598    def __init__(self, encoding=None, xhtml=1, validate=True, prefixes={}, prefixdefault=False, hidexmlns=(), showxmlns=()):
599        """
600        Create a publisher. Arguments have the following meaning:
601
602        :var:`encoding` : string or :const:`None`
603            Specifies the encoding to be used for the byte sequence. If
604            :const:`None` is used the encoding in the XML declaration will be used.
605            If there is no XML declaration, UTF-8 will be used.
606
607        :var:`xhtml` : int
608            With the parameter :var:`xhtml` you can specify if you want HTML
609            output:
610
611            HTML (``xhtml==0``)
612                Elements with a empty content model will be published as ``<foo>``.
613
614            HTML browser compatible XML (``xhtml==1``)
615                Elements with an empty content model will be published as ``<foo />``
616                and others that just happen to be empty as ``<foo></foo>``. This is
617                the default.
618
619            Pure XML (``xhtml==2``)
620                All empty elements will be published as ``<foo/>``.
621
622        :var:`validate` : bool
623            Specifies whether validation should be done before publishing.
624
625        :var:`prefixes` : mapping
626            A dictionary that specifies which namespace prefixes should be used
627            for publishing. Keys in the dictionary are either namespace names or
628            objects that have an ``xmlns`` attribute which is the namespace name.
629            Values can be:
630
631            :const:`False`
632                Treat elements in this namespace as if they are not in any
633                namespace (if global attributes from this namespace are encountered,
634                a non-empty prefix will be used nonetheless).
635
636            :const:`None`
637                Treat the namespace as the default namespaces (i.e. use unprefixed
638                element names). Global attributes will again result in a non-empty
639                prefix.
640
641            :const:`True`
642                The publisher uses a unique non-empty prefix for this namespace.
643
644            A string
645                Use this prefix for the namespace.
646
647        :var:`prefixdefault` : string or :const:`None`
648            If an element or attribute is encountered whose namespace name is not
649            in :var:`prefixes` :var:`prefixdefault` is used as the fallback.
650
651        :var:`hidexmlns` : list or set
652            :var:`hidexmlns` can be a list or set that contains namespace names
653            for which no ``xmlns`` attributes should be published. (This can be
654            used to hide the namespace declarations for e.g. Java taglibs.)
655
656        :var:`showxmlns` : list or set
657            :var:`showxmlns` can be a list or set that contains namespace names
658            for which ``xmlns`` attributes *will* be published, even if there are
659            no elements from this namespace in the tree.
660        """
661        self.base = None
662        self.encoding = encoding
663        self.encoder = None
664        self.xhtml = xhtml
665        self.validate = validate
666        self.prefixes = {nsname(xmlns): prefix for (xmlns, prefix) in prefixes.iteritems()}
667        self.prefixdefault = prefixdefault
668        self.hidexmlns = {nsname(xmlns) for xmlns in hidexmlns}
669        self.showxmlns = {nsname(xmlns) for xmlns in showxmlns}
670        self._ns2prefix = {}
671        self._prefix2ns = {}
672
673    def encode(self, text):
674        """
675        Encode :var:`text` with the encoding and error handling currently active
676        and return the resulting byte string.
677        """
678        return self.encoder.encode(text)
679
680    def encodetext(self, text):
681        """
682        Encode :var:`test` as text data. :var:`text` must be a :class:`unicode`
683        object. The publisher will apply the configured encoding, error handling
684        and the current text filter (which escapes characters that can't appear
685        in text data (like ``<`` etc.)) and returns the resulting :class:`str`
686        object.
687        """
688        self.encoder.errors = self.__errors[-1]
689        result = self.encoder.encode(self.__textfilters[-1](text))
690        self.encoder.errors = "strict"
691        return result
692
693    def pushtextfilter(self, filter):
694        """
695        Pushes a new text filter function ontp the text filter stack. This
696        function is responsible for escaping characters that can't appear in text
697        data (like ``<``)). This is used to switch on escaping of ``"`` inside
698        attribute values.
699        """
700        self.__textfilters.append(filter)
701
702    def poptextfilter(self):
703        """
704        Pops the current text filter function from the stack.
705        """
706        self.__textfilters.pop()
707
708    def pusherrors(self, errors):
709        """
710        Pushes a new error handling scheme onto the error handling stack.
711        """
712        self.__errors.append(errors)
713
714    def poperrors(self):
715        """
716        Pop the current error handling scheme from the error handling stack.
717        """
718        self.__errors.pop()
719
720    def _newprefix(self):
721        prefix = "ns"
722        suffix = 2
723        while True:
724            if prefix not in self._prefix2ns:
725                return prefix
726            prefix = "ns{}".format(suffix)
727            suffix += 1
728
729    def getencoding(self):
730        """
731        Return the encoding currently in effect.
732        """
733        if self.encoding is not None:
734            # The encoding has been prescribed, so this *will* be used.
735            return self.encoding
736        elif self.encoder is not None:
737            # The encoding is determined by the XML declaration in the output,
738            # so use that if it has been determined already. If the encoder hasn't
739            # determined the encoding yet (e.g. because nothing has been output
740            # yet) use utf-8 (which will be what the encoder eventually will decide
741            # to use too). Note that this will not work if nothing has been output
742            # yet, but later an XML declaration (using a different encoding) will
743            # be output, but this shouldn't happen anyway.
744            return self.encoder.encoding or "utf-8"
745        return "utf-8"
746
747    def getnamespaceprefix(self, xmlns):
748        """
749        Return (and register) a namespace prefix for the namespace name
750        :var:`xmlns`. This honors the namespace configuration from ``self.prefixes``
751        and ``self.prefixdefault``. Furthermore the same prefix will be returned
752        from now on (except when the empty prefix becomes invalid once global
753        attributes are encountered)
754        """
755        if xmlns is None:
756            return None
757
758        if xmlns == xml_xmlns: # We don't need a namespace mapping for the xml namespace
759            prefix = "xml"
760        else:
761            try:
762                prefix = self._ns2prefix[xmlns]
763            except KeyError: # A namespace we haven't encountered yet
764                prefix = self.prefixes.get(xmlns, self.prefixdefault)
765                if prefix is True:
766                    prefix = self._newprefix()
767                if prefix is not False:
768                    try:
769                        oldxmlns = self._prefix2ns[prefix]
770                    except KeyError:
771                        pass
772                    else:
773                        # If this prefix has already been used for another namespace, we need a new one
774                        if oldxmlns != xmlns:
775                            prefix = self._newprefix()
776                    self._ns2prefix[xmlns] = prefix
777                    self._prefix2ns[prefix] = xmlns
778        return prefix
779
780    def getobjectprefix(self, object):
781        """
782        Get and register a namespace prefix for the namespace :var:`object` lives
783        in (specified by the :attr:`xmlns` attribute of :var:`object`). Similar
784        to :meth:`getnamespaceprefix` this honors the namespace configuration from
785        ``self.prefixes`` and ``self.prefixdefault`` (except when a global
786        attribute requires a non-empty prefix).
787        """
788        xmlns = getattr(object, "xmlns")
789        if xmlns is None:
790            return None
791
792        if xmlns == xml_xmlns: # We don't need a namespace mapping for the xml namespace
793            prefix = "xml"
794        else:
795            emptyok = isinstance(object, Element) # If it's e.g. a procinst assume we need a non-empty prefix
796            try:
797                prefix = self._ns2prefix[xmlns]
798            except KeyError: # A namespace we haven't encountered yet
799                prefix = self.prefixes.get(xmlns, self.prefixdefault)
800                # global attributes always require prefixed names
801                if prefix is True or ((prefix is None or prefix is False) and not emptyok):
802                    prefix = self._newprefix()
803                if prefix is not False:
804                    try:
805                        oldxmlns = self._prefix2ns[prefix]
806                    except KeyError:
807                        pass
808                    else:
809                        # If this prefix has already been used for another namespace, we need a new one
810                        if oldxmlns != xmlns:
811                            prefix = self._newprefix()
812                    self._ns2prefix[xmlns] = prefix
813                    self._prefix2ns[prefix] = xmlns
814            else:
815                # We can't use the unprefixed names for global attributes
816                if (prefix is None or prefix is False) and not emptyok:
817                    # Use a new one
818                    prefix = self._newprefix()
819                    self._ns2prefix[xmlns] = prefix
820                    self._prefix2ns[prefix] = xmlns
821        return prefix
822
823    def iterbytes(self, node, base=None):
824        """
825        Output the node :var:`node`. This method is a generator that will yield
826        the resulting XML byte sequence in fragments.
827        """
828        self._ns2prefix.clear()
829        self._prefix2ns.clear()
830        # iterate through every node in the tree
831        for n in node.walknodes(Node):
832            self.getobjectprefix(n)
833        # Add the prefixes forced by ``self.showxmlns``
834        for xmlns in self.showxmlns:
835            self.getnamespaceprefix(xmlns)
836
837        # Do we have to publish xmlns attributes?
838        self._publishxmlns = False
839        if self._ns2prefix:
840            # Determine if we have multiple roots
841            if isinstance(node, Frag):
842                count = 0
843                for child in node:
844                    if isinstance(node, Element) and node.xmlns not in self.hidexmlns:
845                        count += 1
846                if count > 1:
847                    raise MultipleRootsError()
848            self._publishxmlns = True
849
850        self.inattr = 0
851        self.__textfilters = [ misc.xmlescape_text ]
852
853        self.__errors = [ "xmlcharrefreplace" ]
854
855        self.base = url_.URL(base)
856        self.node = node
857
858        self.encoder = codecs.getincrementalencoder("xml")(encoding=self.encoding)
859
860        for part in self.node.publish(self):
861            yield part
862        rest = self.encoder.encode(u"", True) # finish encoding and flush buffers
863        if rest:
864            yield rest
865
866        self.inattr = 0
867        self.__textfilters = [ misc.xmlescape_text ]
868
869        self.__errors = [ "xmlcharrefreplace" ]
870
871        self.publishxmlns = False
872        self._ns2prefix.clear()
873        self._prefix2ns.clear()
874
875        self.encoder = None
876
877    def bytes(self, node, base=None):
878        """
879        Return a byte string in XML format for the XIST node :var:`node`.
880        """
881        return "".join(self.iterbytes(node, base))
882
883    def iterstring(self, node, base=None):
884        """
885        A generator that will produce a serialized string of :var:`node`.
886        """
887        decoder = codecs.getincrementaldecoder("xml")(encoding=self.encoding)
888        for part in self.iterbytes(node, base):
889            part = decoder.decode(part, False)
890            if part:
891                yield part
892        part = decoder.decode("", True)
893        if part:
894            yield part
895
896    def string(self, node, base=None):
897        """
898        Return a unicode string for :var:`node`.
899        """
900        decoder = codecs.getdecoder("xml")
901        result = self.bytes(node, base)
902        return decoder(result, encoding=self.encoding)[0]
903
904    def write(self, stream, node, base=None):
905        """
906        Write :var:`node` to the file-like object :var:`stream` (which must
907        provide a :meth:`write` method).
908        """
909        for part in self.iterbytes(node, base):
910            stream.write(part)
911
912
913###
914### The DOM classes
915###
916
917class _Node_Meta(type):
918    def __new__(cls, name, bases, dict):
919        dict["__fullname__"] = name
920        if "register" not in dict:
921            dict["register"] = True
922        if "xmlname" not in dict:
923            dict["xmlname"] = name.rsplit(".", 1)[-1]
924        return type.__new__(cls, name, bases, dict)
925
926    def __repr__(self):
927        return "<class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
928
929    def __div__(self, other):
930        from ll.xist import xfind
931        return xfind.IsInstanceSelector(self) / other
932
933    def __floordiv__(self, other):
934        from ll.xist import xfind
935        return xfind.IsInstanceSelector(self) // other
936
937    def __mul__(self, other):
938        from ll.xist import xfind
939        return xfind.IsInstanceSelector(self) * other
940
941    def __pow__(self, other):
942        from ll.xist import xfind
943        return xfind.IsInstanceSelector(self) ** other
944
945    def __and__(self, other):
946        from ll.xist import xfind
947        return xfind.IsInstanceSelector(self) & other
948
949    def __or__(self, other):
950        from ll.xist import xfind
951        return xfind.IsInstanceSelector(self) | other
952
953    def __getitem__(self, index):
954        from ll.xist import xfind
955        return xfind.IsInstanceSelector(self)[index]
956
957    def __invert__(self):
958        from ll.xist import xfind
959        return ~xfind.IsInstanceSelector(self)
960
961
962class Node(object):
963    """
964    base class for nodes in the document tree. Derived classes may
965    overwrite :meth:`convert` or :meth:`publish`.
966    """
967    __metaclass__ = _Node_Meta
968
969    # location of this node in the XML file (will be hidden in derived classes,
970    # but is specified here, so that no special tests are required. In derived
971    # classes this will be set by the parser)
972    startloc = None
973    endloc = None
974
975    # Subclasses relevant for parsing (i.e. Element, ProcInst, Entity and CharRef)
976    # have an additional class attribute named register. This attribute may have
977    # three values:
978    # :const:`False`: Don't register for parsing.
979    # :const:`True`:  Use for parsing.
980    # If register is not set it defaults to :const:`True`
981
982    Context = Context
983
984    prettyindentbefore = 0
985    prettyindentafter = 0
986
987    def __repr__(self):
988        return "<{0.__module__}:{0.__fullname__} object at {1:#x}>".format(self, id(self))
989
990    def __ne__(self, other):
991        return not self == other
992
993    xmlname = None
994    xmlns = None
995
996    @classmethod
997    def _strbase(cls, fullname, xml):
998        v = []
999        if fullname:
1000            ns = cls.xmlns if xml else cls.__module__
1001            if ns is not None:
1002                v.append(ns)
1003                v.append(":")
1004        if xml:
1005            name = cls.xmlname
1006        elif fullname:
1007            name = cls.__fullname__
1008        else:
1009            name = cls.__name__
1010        v.append(name)
1011        return "".join(v)
1012
1013    def __pos__(self):
1014        threadlocalnodehandler.handler.add(self)
1015
1016    def __div__(self, other):
1017        from ll.xist import xfind
1018        return xfind.IsSelector(self) / other
1019
1020    def __floordiv__(self, other):
1021        from ll.xist import xfind
1022        return xfind.IsSelector(self) // other
1023
1024    def __mul__(self, other):
1025        from ll.xist import xfind
1026        return xfind.IsSelector(self) * other
1027
1028    def __pow__(self, other):
1029        from ll.xist import xfind
1030        return xfind.IsSelector(self) ** other
1031
1032    def __and__(self, other):
1033        from ll.xist import xfind
1034        return xfind.IsSelector(self) & other
1035
1036    def __or__(self, other):
1037        from ll.xist import xfind
1038        return xfind.IsSelector(self) | other
1039
1040    def clone(self):
1041        """
1042        return a clone of :var:`self`. Compared to :meth:`deepcopy` :meth:`clone`
1043        will create multiple instances of objects that can be found in the tree
1044        more than once. :meth:`clone` can't clone trees that contain cycles.
1045        """
1046        return self
1047
1048    def copy(self):
1049        """
1050        Return a shallow copy of :var:`self`.
1051        """
1052        return self.__copy__()
1053
1054    def __copy__(self):
1055        return self
1056
1057    def deepcopy(self):
1058        """
1059        Return a deep copy of :var:`self`.
1060        """
1061        return self.__deepcopy__()
1062
1063    def __deepcopy__(self, memo=None):
1064        return self
1065
1066    @misc.notimplemented
1067    def present(self, presenter):
1068        """
1069        :meth:`present` is used as a central dispatch method for the
1070        presenter classes. Normally it is not called by the user, but internally
1071        by the presenter. The user should use the appropriate presenter class
1072        directly.
1073        """
1074        # Subclasses of ``Node`` implement this method by calling the appropriate
1075        # ``present*`` method in the publisher (i.e. double dispatch)
1076
1077    def conv(self, converter=None, root=None, mode=None, stage=None, target=None, lang=None, function=None, makeaction=None, makeproject=None):
1078        """
1079        Convenience method for calling :meth:`convert`.
1080
1081        :meth:`conv` will automatically set ``:var:`converter`.node`` to :var:`self`
1082        to remember the "document root node" for which :meth:`conv` has been
1083        called, this means that you should not call :meth:`conv` in any of the
1084        recursive calls, as you would loose this information. Call :meth:`convert`
1085        directly instead.
1086        """
1087        if converter is None:
1088            converter = Converter(node=self, root=root, mode=mode, stage=stage, target=target, lang=lang, makeaction=makeaction, makeproject=makeproject)
1089            return self.convert(converter)
1090        else:
1091            converter.push(node=self, root=root, mode=mode, stage=stage, target=target, lang=lang, makeaction=makeaction, makeproject=makeproject)
1092            node = self.convert(converter)
1093            converter.pop()
1094            return node
1095
1096    @misc.notimplemented
1097    def convert(self, converter):
1098        """
1099        implementation of the conversion method. When you define your own element
1100        classes you have to overwrite this method and implement the desired
1101        conversion.
1102
1103        This method must return an instance of :class:`Node`. It may *not* change
1104        :var:`self`.
1105        """
1106
1107    @misc.notimplemented
1108    def __unicode__(self):
1109        """
1110        Return the character content of :var:`self` as a unicode string. This
1111        means that comments and processing instructions will be filtered out.
1112        For elements you'll get the element content.
1113
1114        :meth:`__unicode__` can be used everywhere where a plain string
1115        representation of the node is required.
1116        """
1117        pass
1118
1119    def __str__(self):
1120        """
1121        Return the character content of :var:`self` as a string (if possible, i.e.
1122        there are no characters that are unencodable in the default encoding).
1123        """
1124        return str(unicode(self))
1125
1126    def __int__(self):
1127        """
1128        Convert the character content of :var:`self` to an :class:`int`.
1129        """
1130        return int(unicode(self))
1131
1132    def __long__(self):
1133        """
1134        Convert the character content of :var:`self` to an :class:`long`.
1135        """
1136        return long(unicode(self))
1137
1138    def asFloat(self, decimal=".", ignore=""):
1139        """
1140        Convert the character content of :var:`self` to an :class:`float`.
1141        :var:`decimal` specifies which decimal separator is used in the value
1142        (e.g. ``"."`` (the default) or ``","``). :var:`ignore` specifies which
1143        characters will be ignored.
1144        """
1145        s = unicode(self)
1146        for c in ignore:
1147            s = s.replace(c, u"")
1148        if decimal != u".":
1149            s = s.replace(decimal, u".")
1150        return float(s)
1151
1152    def __float__(self):
1153        """
1154        Convert the character content of :var:`self` to an :class:`float`.
1155        """
1156        return self.asFloat()
1157
1158    def __complex__(self):
1159        """
1160        Convert the character content of :var:`self` to an :class:`complex`.
1161        """
1162        return complex(unicode(self))
1163
1164    def parsed(self, parser, event):
1165        """
1166        This method will be called by the parser :var:`parser` once after
1167        :var:`self` is created by the parser (This is used e.g. by
1168        :class:`URLAttr` to incorporate the base URL into the attribute).
1169
1170        :var:`event` is the parser event that initiated the call.
1171        """
1172
1173    def checkvalid(self):
1174        """
1175        This method will be called when parsing or publishing to check whether
1176        :var`self` is valid.
1177
1178        If :var:`self` is found to be invalid a warning should be issued through
1179        the Python warning framework.
1180        """
1181
1182    @misc.notimplemented
1183    def publish(self, publisher):
1184        """
1185        Generate unicode strings for the node. :var:`publisher` must be an
1186        instance of :class:`ll.xist.xsc.Publisher`.
1187
1188        The encoding and xhtml specification are taken from the :var:`publisher`.
1189        """
1190
1191    def iterbytes(self, base=None, publisher=None, **publishargs):
1192        """
1193        A generator that will produce this node as a serialized byte string.
1194
1195        For the possible parameters see the :class:`ll.xist.xsc.Publisher`
1196        constructor.
1197        """
1198        if publisher is None:
1199            publisher = Publisher(**publishargs)
1200
1201        return publisher.iterbytes(self, base) # return a generator-iterator
1202
1203    def bytes(self, base=None, publisher=None, **publishargs):
1204        """
1205        Return :var:`self` as a serialized byte string.
1206
1207        For the possible parameters see the :class:`ll.xist.xsc.Publisher`
1208        constructor.
1209        """
1210        if publisher is None:
1211            publisher = Publisher(**publishargs)
1212
1213        return publisher.bytes(self, base)
1214
1215    def iterstring(self, base=None, publisher=None, **publishargs):
1216        """
1217        A generator that will produce a serialized byte string of :var:`self`.
1218
1219        For the possible parameters see the :class:`ll.xist.xsc.Publisher`
1220        constructor.
1221        """
1222        if publisher is None:
1223            publisher = Publisher(**publishargs)
1224
1225        return publisher.iterstring(self, base) # return a generator-iterator
1226
1227    def string(self, base=None, publisher=None, **publishargs):
1228        """
1229        Return a serialized unicode string for :var:`self`.
1230
1231        For the possible parameters see the :class:`ll.xist.xsc.Publisher`
1232        constructor.
1233        """
1234        if publisher is None:
1235            publisher = Publisher(**publishargs)
1236        return publisher.string(self, base)
1237
1238    def write(self, stream, base=None, publisher=None, **publishargs):
1239        """
1240        Write :var:`self` to the file-like object :var:`stream` (which must provide
1241        a :meth:`write` method).
1242
1243        For the rest of the parameters see the :class:`ll.xist.xsc.Publisher`
1244        constructor.
1245        """
1246        if publisher is None:
1247            publisher = Publisher(**publishargs)
1248        return publisher.write(stream, self, base)
1249
1250    def walk(self, walkfilter=None):
1251        """
1252        Return an iterator for traversing the tree rooted at :var:`self`.
1253
1254        :var:`walkfilter` is used for specifying whether or not a node should be
1255        yielded and when the children of this node should be traversed. If
1256        :var:`walkfilter` is callable, it will be called for each node visited
1257        during the traversal. A path (i.e. a list of all nodes from the root to
1258        the current node) will be passed to the filter on each call and the
1259        filter must return a sequence of "node handling options". If
1260        :var:`walkfilter` is not callable, it must be a sequence of node
1261        handling options that will be used for all visited nodes.
1262
1263        Entries in this returned sequence can be the following:
1264
1265        :const:`True`
1266            This tells :meth:`walk` to yield this node from the iterator.
1267
1268        :const:`False`
1269            Don't yield this node from the iterator.
1270
1271        :const:`xfind.enterattrs`
1272            This is a global constant in :mod:`ll.xist.xfind` and tells :meth:`walk`
1273            to traverse the attributes of this node (if it's an :class:`Element`,
1274            otherwise this option will be ignored).
1275
1276        :const:`xfind.entercontent`
1277            This is a global constant in :mod:`ll.xist.xfind` and tells :meth:`walk`
1278            to traverse the child nodes of this node (if it's an :class:`Element`,
1279            otherwise this option will be ignored).
1280
1281        These options will be executed in the order they are specified in the
1282        sequence, so to get a top down traversal of a tree (without entering
1283        attributes), ``(True, xfind.entercontent)`` can be used. For a bottom up
1284        traversal ``(xfind.entercontent, True)`` can be used.
1285
1286        Each item produced by the iterator is a path list. :meth:`walk` reuses
1287        this list, so you can't rely on the value of the list being the same
1288        across calls to :meth:`next`.
1289        """
1290        from ll.xist import xfind
1291        return xfind.makewalkfilter(walkfilter).walk(self)
1292
1293    def walknodes(self, walkfilter=None):
1294        """
1295        Return an iterator for traversing the tree. :var:`filter` works the same
1296        as the :var:`walkfilter` argument for :meth:`walk`. The items produced
1297        by the iterator are the nodes themselves.
1298        """
1299        from ll.xist import xfind
1300        return xfind.makewalkfilter(walkfilter).walknodes(self)
1301
1302    def walkpaths(self, walkfilter=None):
1303        """
1304        Return an iterator for traversing the tree. :var:`walkfilter` works
1305        the same as the :var:`walkfilter` argument for :meth:`walk`. The items
1306        produced by the iterator are copies of the path.
1307        """
1308        from ll.xist import xfind
1309        return xfind.makewalkfilter(walkfilter).walkpaths(self)
1310
1311    def compact(self):
1312        """
1313        Return a version of :var:`self`, where textnodes or character references
1314        that contain only linefeeds are removed, i.e. potentially useless
1315        whitespace is removed.
1316        """
1317        return self
1318
1319    def _decoratenode(self, node):
1320        # Decorate the :class:`Node` :var:`node` with the same location
1321        # information as :var:`self`.
1322
1323        node.startloc = self.startloc
1324        node.endloc = self.endloc
1325        return node
1326
1327    def mapped(self, function, converter=None, **converterargs):
1328        """
1329        Return the node mapped through the function :var:`function`. This call
1330        works recursively (for :class:`Frag` and :class:`Element`).
1331
1332        When you want an unmodified node you simply can return :var:`self`.
1333        :meth:`mapped` will make a copy of it and fill the content recursively.
1334        Note that element attributes will not be mapped. When you return a
1335        different node from :func:`function` this node will be incorporated
1336        into the result as-is.
1337        """
1338        if converter is None:
1339            converter = Converter(**converterargs)
1340        node = function(self, converter)
1341        assert isinstance(node, Node), "the mapped method returned the illegal object {!r} (type {!r}) when mapping {!r}".format(node, type(node), self)
1342        return node
1343
1344    def normalized(self):
1345        """
1346        Return a normalized version of :var:`self`, which means that consecutive
1347        :class:`Text` nodes are merged.
1348        """
1349        return self
1350
1351    def __mul__(self, factor):
1352        """
1353        Return a :class:`Frag` with :var:`factor` times the node as an entry.
1354        Note that the node will not be copied, i.e. this is a
1355        "shallow :meth:`__mul__`".
1356        """
1357        return Frag(*factor*[self])
1358
1359    def __rmul__(self, factor):
1360        """
1361        Return a :class:`Frag` with :var:`factor` times the node as an entry.
1362        """
1363        return Frag(*[self]*factor)
1364
1365    def pretty(self, level=0, indent="\t"):
1366        """
1367        Return a prettyfied version of :var:`self`, i.e. one with properly nested
1368        and indented tags (as far as possible). If an element has mixed content
1369        (i.e. :class:`Text` and non-:class:`Text` nodes) the content will be
1370        returned as is.
1371
1372        Note that whitespace will prevent pretty printing too, so you might want
1373        to call :meth:`normalized` and :meth:`compact` before calling
1374        :meth:`pretty` to remove whitespace.
1375        """
1376        if level:
1377            return Frag(indent*level, self)
1378        else:
1379            return self
1380
1381
1382###
1383### Helper functions for ipipe
1384###
1385
1386
1387if ipipe is not None:
1388    def _ipipe_type(node):
1389        "The type of the node"
1390        if node is Null:
1391            return "null"
1392        elif isinstance(node, Element):
1393            return "element"
1394        elif isinstance(node, ProcInst):
1395            return "procinst"
1396        elif isinstance(node, CharRef):
1397            return "charref"
1398        elif isinstance(node, Entity):
1399            return "entity"
1400        elif isinstance(node, Text):
1401            return "text"
1402        elif isinstance(node, Comment):
1403            return "comment"
1404        elif isinstance(node, DocType):
1405            return "doctype"
1406        elif isinstance(node, Attr):
1407            return "attr"
1408        elif isinstance(node, Frag):
1409            return "frag"
1410        return ipipe.noitem
1411    _ipipe_type.__xname__ = "type"
1412
1413
1414    def _ipipe_ns(node):
1415        "The namespace"
1416        return node.xmlns
1417    _ipipe_ns.__xname__ = "ns"
1418
1419
1420    def _ipipe_name(node):
1421        "The element/procinst/entity/attribute name of the node"
1422        if isinstance(node, (Element, ProcInst, Entity, Attr)):
1423            return "{0.__class__.__module__}.{0.__fullname__}".format(node)
1424        return ipipe.noitem
1425    _ipipe_name.__xname__ = "name"
1426
1427
1428    def _ipipe_childrencount(node):
1429        "The number of child nodes"
1430        if isinstance(node, Element):
1431            return len(node.content)
1432        elif isinstance(node, Frag):
1433            return len(node)
1434        return ipipe.noitem
1435    _ipipe_childrencount.__xname__ = "# children"
1436
1437
1438    def _ipipe_attrscount(node):
1439        "The number of attribute nodes"
1440        if isinstance(node, Element):
1441            return len(node.attrs)
1442        return ipipe.noitem
1443    _ipipe_attrscount.__xname__ = "# attrs"
1444
1445
1446    def _ipipe_content(node):
1447        "The text content"
1448        if isinstance(node, CharacterData):
1449            return unicode(node.content)
1450        elif isinstance(node, Attr):
1451            return unicode(node)
1452        return ipipe.noitem
1453    _ipipe_content.__xname__ = "content"
1454
1455
1456    @ipipe.xrepr.when_type(_Node_Meta)
1457    def repr_nodeclass(self, mode="default"):
1458        yield (astyle.style_type_type, "{0.__module__}.{0.__fullname__}".format(self))
1459
1460
1461    @ipipe.xattrs.when_type(Node)
1462    def xattrs_nodeclass(self, mode="default"):
1463        yield ipipe.AttributeDescriptor("startloc", doc="the location in the XML file")
1464        yield ipipe.FunctionDescriptor(_ipipe_type)
1465        yield ipipe.AttributeDescriptor("xmlns", doc="the XML namespace of the node")
1466        yield ipipe.FunctionDescriptor(_ipipe_name, "name")
1467        yield ipipe.FunctionDescriptor(_ipipe_content, "content")
1468        if mode == "detail":
1469            yield ipipe.IterAttributeDescriptor("content", "the element content")
1470            yield ipipe.IterAttributeDescriptor("attrs", "the element attributes")
1471        else:
1472            yield ipipe.FunctionDescriptor(_ipipe_childrencount, "children")
1473            yield ipipe.FunctionDescriptor(_ipipe_attrscount, "attrs")
1474
1475
1476class CharacterData(Node):
1477    """
1478    Base class for XML character data (:class:`Text`, :class:`ProcInst`,
1479    :class:`Comment` and :class:`DocType`).
1480
1481    (Provides nearly the same functionality as :class:`UserString`,
1482    but omits a few methods.)
1483    """
1484    __slots__ = ("_content",)
1485
1486    def __init__(self, *content):
1487        self._content = u"".join(unicode(x) for x in content)
1488
1489    def __getstate__(self):
1490        return self._content
1491
1492    def __setstate__(self, content):
1493        self._content = content
1494
1495    class content(misc.propclass):
1496        """
1497        The text content of the node as a :class:`unicode` object.
1498        """
1499        def __get__(self):
1500            return self._content
1501
1502    def __hash__(self):
1503        return self._content.__hash__()
1504
1505    def __eq__(self, other):
1506        return self.__class__ is other.__class__ and self._content==other._content
1507
1508    def __len__(self):
1509        return self._content.__len__()
1510
1511    def __getitem__(self, index):
1512        return self.__class__(self._content.__getitem__(index))
1513
1514    def __add__(self, other):
1515        return self.__class__(self._content + other)
1516
1517    def __radd__(self, other):
1518        return self.__class__(unicode(other) + self._content)
1519
1520    def __mul__(self, n):
1521        return self.__class__(n * self._content)
1522
1523    def __rmul__(self, n):
1524        return self.__class__(n * self._content)
1525
1526    def __getslice__(self, index1, index2):
1527        return self.__class__(self._content.__getslice__(index1, index2))
1528
1529    def capitalize(self):
1530        return self.__class__(self._content.capitalize())
1531
1532    def center(self, width):
1533        return self.__class__(self._content.center(width))
1534
1535    def count(self, sub, start=0, end=sys.maxint):
1536        return self._content.count(sub, start, end)
1537
1538    def endswith(self, suffix, start=0, end=sys.maxint):
1539        return self._content.endswith(suffix, start, end)
1540
1541    def index(self, sub, start=0, end=sys.maxint):
1542        return self._content.index(sub, start, end)
1543
1544    def isalpha(self):
1545        return self._content.isalpha()
1546
1547    def isalnum(self):
1548        return self._content.isalnum()
1549
1550    def isdecimal(self):
1551        return self._content.isdecimal()
1552
1553    def isdigit(self):
1554        return self._content.isdigit()
1555
1556    def islower(self):
1557        return self._content.islower()
1558
1559    def isnumeric(self):
1560        return self._content.isnumeric()
1561
1562    def isspace(self):
1563        return self._content.isspace()
1564
1565    def istitle(self):
1566        return self._content.istitle()
1567
1568    def isupper(self):
1569        return self._content.isupper()
1570
1571    def join(self, frag):
1572        return frag.withsep(self)
1573
1574    def ljust(self, width, fill=u" "):
1575        return self.__class__(self._content.ljust(width, fill))
1576
1577    def lower(self):
1578        return self.__class__(self._content.lower())
1579
1580    def lstrip(self, chars=None):
1581        return self.__class__(self._content.lstrip(chars))
1582
1583    def replace(self, old, new, maxsplit=-1):
1584        return self.__class__(self._content.replace(old, new, maxsplit))
1585
1586    def rjust(self, width, fill=u" "):
1587        return self.__class__(self._content.rjust(width, fill))
1588
1589    def rstrip(self, chars=None):
1590        return self.__class__(self._content.rstrip(chars))
1591
1592    def rfind(self, sub, start=0, end=sys.maxint):
1593        return self._content.rfind(sub, start, end)
1594
1595    def rindex(self, sub, start=0, end=sys.maxint):
1596        return self._content.rindex(sub, start, end)
1597
1598    def split(self, sep=None, maxsplit=-1):
1599        return Frag(self._content.split(sep, maxsplit))
1600
1601    def splitlines(self, keepends=0):
1602        return Frag(self._content.splitlines(keepends))
1603
1604    def startswith(self, prefix, start=0, end=sys.maxint):
1605        return self._content.startswith(prefix, start, end)
1606
1607    def strip(self, chars=None):
1608        return self.__class__(self._content.strip(chars))
1609
1610    def swapcase(self):
1611        return self.__class__(self._content.swapcase())
1612
1613    def title(self):
1614        return self.__class__(self._content.title())
1615
1616    def translate(self, table):
1617        return self.__class__(self._content.translate(table))
1618
1619    def upper(self):
1620        return self.__class__(self._content.upper())
1621
1622    def __repr__(self):
1623        if self.startloc is not None:
1624            loc = " (from {})".format(self.startloc)
1625        else:
1626            loc = ""
1627        return "<{0.__class__.__module__}.{0.__fullname__} content={0.content!r}{1} at {2:#x}>".format(self, loc, id(self))
1628
1629
1630class Text(CharacterData):
1631    """
1632    A text node. The characters ``<``, ``>``, ``&`` (and ``"`` inside
1633    attributes) will be "escaped" with the appropriate character entities when
1634    this node is published.
1635    """
1636
1637    def convert(self, converter):
1638        return self
1639
1640    def __unicode__(self):
1641        return self._content
1642
1643    def publish(self, publisher):
1644        yield publisher.encodetext(self._content)
1645
1646    def present(self, presenter):
1647        return presenter.presentText(self) # return a generator-iterator
1648
1649    def compact(self):
1650        if self.content.isspace():
1651            return Null
1652        else:
1653            return self
1654
1655    def pretty(self, level=0, indent="\t"):
1656        return self
1657
1658
1659class Frag(Node, list):
1660    """
1661    A fragment contains a list of nodes and can be used for dynamically
1662    constructing content. The attribute :attr:`content` of an :class:`Element`
1663    is a :class:`Frag`.
1664    """
1665
1666    def __init__(self, *content):
1667        list.__init__(self)
1668        for child in content:
1669            child = tonode(child)
1670            if isinstance(child, Frag):
1671                list.extend(self, child)
1672            elif child is not Null:
1673                list.append(self, child)
1674
1675    def __enter__(self):
1676        return threadlocalnodehandler.handler.enter(self)
1677
1678    def __exit__(self, type, value, traceback):
1679        threadlocalnodehandler.handler.exit()
1680
1681    def __call__(self, *content):
1682        self.extend(content)
1683        return self
1684
1685    @classmethod
1686    def _str(cls, fullname=True, xml=True, decorate=True):
1687        s = cls._strbase(fullname=fullname, xml=xml)
1688        if decorate:
1689            s = "<{}>".format(s)
1690        return s
1691
1692    def _create(self):
1693        """
1694        internal helper that is used to create an empty clone of :var:`self`.
1695        """
1696        # This is overwritten by :class:`Attr` to insure that attributes don't
1697        # get initialized with the default value when used in various methods
1698        # that create new attributes.
1699        return self.__class__()
1700
1701    def clear(self):
1702        """
1703        Make :var:`self` empty.
1704        """
1705        del self[:]
1706
1707    def convert(self, converter):
1708        node = self._create()
1709        for child in self:
1710            convertedchild = child.convert(converter)
1711            assert isinstance(convertedchild, Node), "the convert method returned the illegal object {!r} (type {!r}) when converting {!r}".format(convertedchild, type(convertedchild), self)
1712            node.append(convertedchild)
1713        return self._decoratenode(node)
1714
1715    def clone(self):
1716        node = self._create()
1717        list.extend(node, (child.clone() for child in self))
1718        return self._decoratenode(node)
1719
1720    def __copy__(self):
1721        """
1722        helper for the :mod:`copy` module.
1723        """
1724        node = self._create()
1725        list.extend(node, self)
1726        return self._decoratenode(node)
1727
1728    def __deepcopy__(self, memo=None):
1729        """
1730        helper for the :mod:`copy` module.
1731        """
1732        node = self._create()
1733        if memo is None:
1734            memo = {}
1735        memo[id(self)] = node
1736        list.extend(node, (copy.deepcopy(child, memo) for child in self))
1737        return self._decoratenode(node)
1738
1739    def present(self, presenter):
1740        return presenter.presentFrag(self) # return a generator-iterator
1741
1742    def __unicode__(self):
1743        return u"".join(unicode(child) for child in self)
1744
1745    def __eq__(self, other):
1746        return self.__class__ is other.__class__ and list.__eq__(self, other)
1747
1748    def publish(self, publisher):
1749        for child in self:
1750            for part in child.publish(publisher):
1751                yield part
1752
1753    def __getitem__(self, index):
1754        """
1755        Return the :var:`index`'th node of the content of the fragment. If
1756        :var:`index` is a list :meth:`__getitem__` will work recursively.
1757        If :var:`index` is an empty list, :var:`self` will be returned.
1758        :meth:`__getitem__` also supports walk filters.
1759        """
1760        if isinstance(index, list):
1761            node = self
1762            for subindex in index:
1763                node = node[subindex]
1764            return node
1765        elif isinstance(index, (int, long)):
1766            return list.__getitem__(self, index)
1767        elif isinstance(index, slice):
1768            return self.__class__(list.__getitem__(self, index))
1769        else:
1770            from ll.xist import xfind
1771            def iterate(matcher):
1772                path = [self, None]
1773                for child in self:
1774                    path[-1] = child
1775                    if matcher(path):
1776                        yield child
1777            return misc.Iterator(iterate(xfind.makewalkfilter(index).matchpath))
1778
1779    def __setitem__(self, index, value):
1780        """
1781        Allows you to replace the :var:`index`'th content node of the fragment
1782        with the new value :var:`value` (which will be converted to a node).
1783        If  :var:`index` is a list :meth:`__setitem__` will be applied to the
1784        innermost index after traversing the rest of :var:`index` recursively.
1785        If :var:`index` is an empty list, an exception will be raised.
1786        :meth:`__setitem__` also supports walk filters.
1787        """
1788        if isinstance(index, list):
1789            if not index:
1790                raise ValueError("can't replace self")
1791            node = self
1792            for subindex in index[:-1]:
1793                node = node[subindex]
1794            node[index[-1]] = value
1795        elif isinstance(index, (int, long)):
1796            value = Frag(value)
1797            if index==-1:
1798                l = len(self)
1799                list.__setslice__(self, l-1, l, value)
1800            else:
1801                list.__setslice__(self, index, index+1, value)
1802        elif isinstance(index, slice):
1803            list.__setitem__(self, index, Frag(value))
1804        else:
1805            from ll.xist import xfind
1806            matcher = xfind.makewalkfilter(index).matchpath
1807            value = Frag(value)
1808            newcontent = []
1809            path = [self, None]
1810            for child in self:
1811                path[-1] = child
1812                if matcher(path):
1813                    newcontent.extend(value)
1814                else:
1815                    newcontent.append(child)
1816            list.__setslice__(self, 0, len(self), newcontent)
1817
1818    def __delitem__(self, index):
1819        """
1820        Remove the :var:`index`'th content node from the fragment. If :var:`index`
1821        is a list, the innermost index will be deleted, after traversing the rest
1822        of :var:`index` recursively. If :var:`index` is an empty list, an
1823        exception will be raised. Anything except :class:`list`, :class:`int` and
1824        :class:`slice` objects will be turned into a walk filter and any child
1825        node matching this filter will be deleted from :var:`self`.
1826        """
1827        if isinstance(index, list):
1828            if not index:
1829                raise ValueError("can't delete self")
1830            node = self
1831            for subindex in index[:-1]:
1832                node = node[subindex]
1833            del node[index[-1]]
1834        elif isinstance(index, (int, long, slice)):
1835            list.__delitem__(self, index)
1836        else:
1837            from ll.xist import xfind
1838            matcher = xfind.makewalkfilter(index).matchpath
1839            list.__setslice__(self, 0, len(self), [child for child in self if not matcher([self, child])])
1840
1841    def __getslice__(self, index1, index2):
1842        """
1843        Returns a slice of the content of the fragment.
1844        """
1845        node = self._create()
1846        list.extend(node, list.__getslice__(self, index1, index2))
1847        return node
1848
1849    def __setslice__(self, index1, index2, sequence):
1850        """
1851        Replace a slice of the content of the fragment
1852        """
1853        list.__setslice__(self, index1, index2, Frag(sequence))
1854
1855    # no need to implement __delslice__
1856
1857    def __mul__(self, factor):
1858        """
1859        Return a :class:`Frag` with :var:`factor` times the content of :var:`self`.
1860        Note that no copies of the content will be generated, so this is a
1861        "shallow :meth:`__mul__`".
1862        """
1863        node = self._create()
1864        list.extend(node, list.__mul__(self, factor))
1865        return node
1866
1867    __rmul__ = __mul__
1868
1869    def __iadd__(self, other):
1870        self.extend(other)
1871        return self
1872
1873    # no need to implement __len__ or __nonzero__
1874
1875    def append(self, *others):
1876        """
1877        Append every item in :var:`others` to :var:`self`.
1878        """
1879        for other in others:
1880            other = tonode(other)
1881            if isinstance(other, Frag):
1882                list.extend(self, other)
1883            elif other is not Null:
1884                list.append(self, other)
1885
1886    def extend(self, items):
1887        """
1888        Append all items from the sequence :var:`items` to :var:`self`.
1889        """
1890        self.append(items)
1891
1892    def insert(self, index, *others):
1893        """
1894        Insert all items in :var:`others` at the position :var:`index`. (this is
1895        the same as ``self[index:index] = others``)
1896        """
1897        other = Frag(*others)
1898        list.__setslice__(self, index, index, other)
1899
1900    def compact(self):
1901        node = self._create()
1902        for child in self:
1903            compactedchild = child.compact()
1904            assert isinstance(compactedchild, Node), "the compact method returned the illegal object {!r} (type {!r}) when compacting {!r}".format(compactedchild, type(compactedchild), child)
1905            if compactedchild is not Null:
1906                list.append(node, compactedchild)
1907        return self._decoratenode(node)
1908
1909    def withsep(self, separator, clone=False):
1910        """
1911        Return a version of :var:`self` with a separator node between the nodes of
1912        :var:`self`.
1913
1914        if :var:`clone` is false, one node will be inserted several times, if
1915        :var:`clone` is true, clones of this node will be used.
1916        """
1917        node = self._create()
1918        newseparator = tonode(separator)
1919        for child in self:
1920            if len(node):
1921                node.append(newseparator)
1922                if clone:
1923                    newseparator = newseparator.clone()
1924            node.append(child)
1925        return node
1926
1927    def sorted(self, cmp=None, key=None, reverse=False):
1928        """
1929        Return a sorted version of the :var:`self`. :var:`cmp`, :var:`key` and
1930        :var:`reverse` have to same meaning as for the builtin function
1931        :func:`sorted`.
1932        """
1933        return self.__class__(sorted(self, cmp, key, reverse))
1934
1935    def reversed(self):
1936        """
1937        Return a reversed version of the :var:`self`.
1938        """
1939        node = list(self)
1940        node.reverse()
1941        return self.__class__(node)
1942
1943    def filtered(self, function):
1944        """
1945        Return a filtered version of the :var:`self`, i.e. a copy of :var:`self`,
1946        where only content nodes for which :func:`function` returns true will
1947        be copied.
1948        """
1949        node = self._create()
1950        list.extend(node, (child for child in self if function(child)))
1951        return node
1952
1953    def shuffled(self):
1954        """
1955        Return a shuffled version of :var:`self`, i.e. a copy of :var:`self` where the
1956        content nodes are randomly reshuffled.
1957        """
1958        content = list(self)
1959        node = self._create()
1960        while content:
1961            index = random.randrange(len(content))
1962            list.append(node, content[index])
1963            del content[index]
1964        return node
1965
1966    def mapped(self, function, converter=None, **converterargs):
1967        if converter is None:
1968            converter = Converter(**converterargs)
1969        node = function(self, converter)
1970        assert isinstance(node, Node), "the mapped method returned the illegal object {!r} (type {!r}) when mapping {!r}".format(node, type(node), self)
1971        if node is self:
1972            node = self._create()
1973            for child in self:
1974                node.append(child.mapped(function, converter))
1975        return node
1976
1977    def normalized(self):
1978        node = self._create()
1979        lasttypeOK = False
1980        for child in self:
1981            normalizedchild = child.normalized()
1982            thistypeOK = isinstance(normalizedchild, Text)
1983            if thistypeOK and lasttypeOK:
1984                node[-1] += normalizedchild
1985            else:
1986                list.append(node, normalizedchild)
1987            lasttypeOK = thistypeOK
1988        return node
1989
1990    def pretty(self, level=0, indent="\t"):
1991        node = self._create()
1992        for (i, child) in enumerate(self):
1993            if i:
1994                node.append("\n")
1995            level += child.prettyindentbefore
1996            node.append(child.pretty(level, indent))
1997            level += child.prettyindentafter
1998        return node
1999
2000    def __repr__(self):
2001        l = len(self)
2002        if l==0:
2003            info = "no children"
2004        elif l==1:
2005            info = "1 child"
2006        else:
2007            info = "{} children".format(l)
2008        loc = " (from {})".format(self.startloc) if self.startloc is not None else ""
2009        return "<{0.__class__.__module__}.{0.__fullname__} object ({1}){2} at {3:#x}>".format(self, info, loc, id(self))
2010
2011
2012class Comment(CharacterData):
2013    """
2014    An XML comment.
2015    """
2016
2017    def convert(self, converter):
2018        return self
2019
2020    def __unicode__(self):
2021        return u""
2022
2023    def present(self, presenter):
2024        return presenter.presentComment(self)  # return a generator-iterator
2025
2026    def publish(self, publisher):
2027        if not publisher.inattr:
2028            content = self.content
2029            if u"--" in content or content.endswith(u"-"):
2030                warnings.warn(IllegalCommentContentWarning(self))
2031            yield publisher.encode(u"<!--")
2032            yield publisher.encode(content)
2033            yield publisher.encode(u"-->")
2034
2035
2036class _DocType_Meta(Node.__metaclass__):
2037    def __repr__(self):
2038        return "<doctype class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
2039
2040
2041class DocType(CharacterData):
2042    """
2043    An XML document type declaration.
2044    """
2045
2046    __metaclass__ = _DocType_Meta
2047
2048    def convert(self, converter):
2049        return self
2050
2051    def present(self, presenter):
2052        return presenter.presentDocType(self) # return a generator-iterator
2053
2054    def publish(self, publisher):
2055        if not publisher.inattr:
2056            yield publisher.encode(u"<!DOCTYPE ")
2057            yield publisher.encode(self.content)
2058            yield publisher.encode(u">")
2059
2060    def __unicode__(self):
2061        return u""
2062
2063
2064class _ProcInst_Meta(Node.__metaclass__):
2065    def __new__(cls, name, bases, dict):
2066        self = super(_ProcInst_Meta, cls).__new__(cls, name, bases, dict)
2067        if dict.get("register") is not None: # check here as the pool isn't defined yet
2068            threadlocalpool.pool.register(self)
2069        return self
2070
2071    def __repr__(self):
2072        return "<procinst class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
2073
2074
2075class ProcInst(CharacterData):
2076    """
2077    Base class for processing instructions.
2078
2079    Processing instructions for specific targets must be implemented as
2080    subclasses of :class:`ProcInst`.
2081    """
2082    __metaclass__ = _ProcInst_Meta
2083
2084    register = None
2085
2086    @classmethod
2087    def _str(cls, fullname=True, xml=True, decorate=True):
2088        s = cls._strbase(fullname=fullname, xml=xml)
2089        if decorate:
2090            s = "<{}>".format(s)
2091        return s
2092
2093    def convert(self, converter):
2094        return self
2095
2096    def present(self, presenter):
2097        return presenter.presentProcInst(self) # return a generator-iterator
2098
2099    def publish(self, publisher):
2100        if publisher.validate:
2101            self.checkvalid()
2102        content = self.content
2103        if u"?>" in content:
2104            raise IllegalProcInstFormatError(self)
2105        yield publisher.encode(u"<?{} {}?>".format(self.xmlname, content))
2106
2107    def __unicode__(self):
2108        return u""
2109
2110    def __repr__(self):
2111        if self.startloc is not None:
2112            loc = " (from {})".format(self.startloc)
2113        else:
2114            loc = ""
2115        return "<{0.__class__.__module__}.{0.__fullname__} procinst content={0.content!r}{1} at {2:#x}>".format(self, loc, id(self))
2116
2117    def __mul__(self, n):
2118        return Node.__mul__(self, n) # don't inherit ``CharacterData.__mul__``
2119
2120    def __rmul__(self, n):
2121        return Node.__rmul__(self, n) # don't inherit ``CharacterData.__rmul__``
2122
2123
2124class AttrProcInst(ProcInst):
2125    """
2126    Special subclass of :class:`ProcInst`.
2127
2128    When an :class:`AttrProcInst` node is the first node in an attribute, it
2129    takes over publishing of the attribute (via the methods :meth:`publishattr`
2130    and :meth:`publishboolattr`). In all other cases the processing instruction
2131    disappears completely.
2132    """
2133
2134    register = None
2135
2136    def publish(self, publisher):
2137        if False:
2138            yield ""
2139
2140    @misc.notimplemented
2141    def publishattr(self, publisher, attr):
2142        """
2143        Publish the attribute :var:`attr` to the publisher :var:`publisher`.
2144        (Note that ``attr[0]`` is :var:`self`).
2145        """
2146
2147    @misc.notimplemented
2148    def publishboolattr(self, publisher, attr):
2149        """
2150        Publish the boolean attribute :var:`attr` to the publisher
2151        :var:`publisher`. (Note that ``attr[0]`` is :var:`self`).
2152        """
2153
2154
2155class Null(CharacterData):
2156    """
2157    node that does not contain anything.
2158    """
2159
2160    @classmethod
2161    def _str(cls, fullname=True, xml=True, decorate=True):
2162        s = cls._strbase(fullname=fullname, xml=xml)
2163        if decorate:
2164            s = "<{}>".format(s)
2165        return s
2166
2167    def convert(self, converter):
2168        return self
2169
2170    def publish(self, publisher):
2171        if False:
2172            yield ""
2173
2174    def present(self, presenter):
2175        return presenter.presentNull(self) # return a generator-iterator
2176
2177    def __unicode__(self):
2178        return u""
2179
2180    def __repr__(self):
2181        return "ll.xist.xsc.Null"
2182
2183
2184Null = Null() # Singleton, the Python way
2185
2186
2187class _Attr_Meta(Frag.__metaclass__):
2188    def __new__(cls, name, bases, dict):
2189        # can be overwritten in subclasses, to specify that this attributes is required
2190        if "required" in dict:
2191            dict["required"] = bool(dict["required"])
2192        # convert the default to a Frag
2193        if "default" in dict:
2194            dict["default"] = Frag(dict["default"])
2195        # convert the entries in values to unicode
2196        if "values" in dict:
2197            values = dict["values"]
2198            if values is not None:
2199                dict["values"] = tuple(unicode(entry) for entry in values)
2200        self = super(_Attr_Meta, cls).__new__(cls, name, bases, dict)
2201        if self.xmlns is not None:
2202            threadlocalpool.pool.register(self)
2203        return self
2204
2205    def __repr__(self):
2206        return "<attribute class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
2207
2208
2209class Attr(Frag):
2210    """
2211    Base class of all attribute classes.
2212
2213    The content of an attribute may be any other XIST node. This is different
2214    from a normal DOM, where only text and character references are allowed.
2215    The reason for this is to allow dynamic content (implemented as elements or
2216    processing instructions) to be put into attributes.
2217
2218    Of course, this dynamic content when finally converted to HTML should
2219    normally result in a fragment consisting only of text and character
2220    references. But note that it is allowed to have elements and processing
2221    instructions inside of attributes even when publishing. Processing
2222    instructions will be published as is and for elements their content will be
2223    published::
2224
2225        >>> from ll.xist.ns import html, php
2226        >>> node = html.img(
2227        ...    src=php.php("echo 'eggs.gif'"),
2228        ...    alt=html.abbr(
2229        ...       "EGGS",
2230        ...       title="Extensible Graphics Generation System",
2231        ...       lang="en"
2232        ...    )
2233        ... )
2234        >>> print node.bytes()
2235        <img alt="EGGS" src="<?php echo 'eggs.gif'?>" />
2236    """
2237    __metaclass__ = _Attr_Meta
2238    required = False
2239    default = None
2240    values = None
2241
2242    def isfancy(self):
2243        """
2244        Return whether :var:`self` contains nodes other than :class:`Text`.
2245        """
2246        for child in self:
2247            if not isinstance(child, Text):
2248                return True
2249        return False
2250
2251    @classmethod
2252    def _str(cls, fullname=True, xml=True, decorate=True):
2253        return cls._strbase(fullname=fullname, xml=xml)
2254
2255    def present(self, presenter):
2256        return presenter.presentAttr(self) # return a generator-iterator
2257
2258    def checkvalid(self):
2259        """
2260        Check whether :var:`self` has an allowed value, i.e. one that is specified
2261        in the class attribute ``values``. If the value is not allowed a warning
2262        will be issued through the Python warning framework.
2263
2264        If :var:`self` is "fancy" (i.e. contains non-:class:`Text` nodes), no
2265        check will be done.
2266        """
2267        values = self.__class__.values
2268        if self and isinstance(values, tuple) and not self.isfancy():
2269            value = unicode(self)
2270            if value not in values:
2271                warnings.warn(IllegalAttrValueWarning(self))
2272
2273    def _publishname(self, publisher):
2274        if self.xmlns is not None:
2275            prefix = publisher._ns2prefix.get(self.xmlns) if self.xmlns != xml_xmlns else u"xml"
2276            if prefix is not None:
2277                return u"{}:{}".format(prefix, self.xmlname)
2278        return self.xmlname
2279
2280    def _publishattrvalue(self, publisher):
2281        # Internal helper that is used to publish the attribute value
2282        # (can be overwritten in subclass (done by e.g. :class:`StyleAttr` and
2283        # :class:`URLAttr`)
2284        return Frag.publish(self, publisher)
2285
2286    def publish(self, publisher):
2287        if publisher.validate:
2288            self.checkvalid()
2289        if self and isinstance(self[0], AttrProcInst):
2290            for part in self[0].publishattr(publisher, self):
2291                yield part
2292        else:
2293            publisher.inattr += 1
2294            yield publisher.encode(u' {}="'.format(self._publishname(publisher)))
2295            publisher.pushtextfilter(misc.xmlescape_attr)
2296            for part in self._publishattrvalue(publisher):
2297                yield part
2298            publisher.poptextfilter()
2299            yield publisher.encode(u'"')
2300            publisher.inattr -= 1
2301
2302    def pretty(self, level=0, indent="\t"):
2303        return self.clone()
2304
2305    def __repr__(self):
2306        l = len(self)
2307        if l==0:
2308            info = u"no children"
2309        elif l==1:
2310            info = u"1 child"
2311        else:
2312            info = u"{} children".format(l)
2313        loc = " (from {})".format(self.startloc) if self.startloc is not None else ""
2314        return "<{0.__class__.__module__}.{0.__fullname__} attr object ({1}){2} at {3:#x}>".format(self, info, loc, id(self))
2315
2316
2317class TextAttr(Attr):
2318    """
2319    Attribute class that is used for normal text attributes.
2320    """
2321
2322
2323class IDAttr(Attr):
2324    """
2325    Attribute used for ids.
2326    """
2327
2328
2329class NumberAttr(Attr):
2330    """
2331    Attribute class that is used for when the attribute value may be any kind
2332    of number.
2333    """
2334
2335
2336class IntAttr(NumberAttr):
2337    """
2338    Attribute class that is used when the attribute value may be an integer.
2339    """
2340
2341
2342class FloatAttr(NumberAttr):
2343    """
2344    Attribute class that is used when the attribute value may be a
2345    floating point value.
2346    """
2347
2348
2349class BoolAttr(Attr):
2350    """
2351    Attribute class that is used for boolean attributes. When publishing
2352    the value will always be the attribute name, regardless of the real value.
2353    """
2354
2355    # We can't simply overwrite :meth:`_publishattrvalue`, because for ``xhtml==0`` we don't output a "proper" attribute
2356    def publish(self, publisher):
2357        if publisher.validate:
2358            self.checkvalid()
2359        if self and isinstance(self[0], AttrProcInst):
2360            for part in self[0].publishboolattr(publisher, self):
2361                yield part
2362        else:
2363            publisher.inattr += 1
2364            name = self._publishname(publisher)
2365            yield publisher.encode(u" {}".format(name))
2366            if publisher.xhtml>0:
2367                yield publisher.encode(u'="')
2368                publisher.pushtextfilter(misc.xmlescape)
2369                yield publisher.encode(name)
2370                publisher.poptextfilter()
2371                yield publisher.encode(u'"')
2372            publisher.inattr -= 1
2373
2374
2375class ColorAttr(Attr):
2376    """
2377    Attribute class that is used for a color attributes.
2378    """
2379
2380
2381class StyleAttr(Attr):
2382    """
2383    Attribute class that is used for CSS style attributes.
2384    """
2385
2386    def _transform(self, replacer):
2387        from ll.xist import css
2388        stylesheet = cssutils.parseString(u"a{{{}}}".format(self))
2389        css.replaceurls(stylesheet, replacer)
2390        return stylesheet.cssRules[0].style.getCssText(separator=" ")
2391
2392    def replaceurls(self, replacer):
2393        """
2394        Replace each URL in the style. Each URL will be passed to the callable
2395        :var:`replacer` and replaced with the returned value.
2396        """
2397        self[:] = self._transform(replacer)
2398
2399    def parsed(self, parser, event):
2400        if event == "leaveattrns" and not self.isfancy() and parser.base is not None:
2401            from ll.xist import css
2402            def prependbase(u):
2403                return parser.base/u
2404            self.replaceurls(prependbase)
2405
2406    def _publishattrvalue(self, publisher):
2407        if not self.isfancy() and publisher.base is not None:
2408            from ll.xist import css
2409            def reltobase(u):
2410                return u.relative(publisher.base)
2411            for part in Frag(self._transform(reltobase)).publish(publisher):
2412                yield part
2413        else:
2414            for part in super(StyleAttr, self)._publishattrvalue(publisher):
2415                yield part
2416
2417    def urls(self, base=None):
2418        """
2419        Return a list of all the URLs (as :class:`URL` objects) found in the style
2420        attribute.
2421        """
2422        from ll.xist import css
2423        urls = []
2424        def collect(u):
2425            urls.append(u)
2426            return u
2427        s = cssutils.parseString(u"a{{{}}}".format(self))
2428        css.replaceurls(s, collect)
2429        return urls
2430
2431
2432class URLAttr(Attr):
2433    """
2434    Attribute class that is used for URLs. See the module :mod:`ll.url` for more
2435    information about URL handling.
2436    """
2437
2438    def parsed(self, parser, event):
2439        if event == "leaveattrns" and not self.isfancy() and parser.base is not None:
2440            self[:] = (url_.URL(parser.base/unicode(self)),)
2441
2442    def _publishattrvalue(self, publisher):
2443        if self.isfancy():
2444            return Attr._publishattrvalue(self, publisher)
2445        else:
2446            new = Attr(url_.URL(unicode(self)).relative(publisher.base))
2447            return new._publishattrvalue(publisher)
2448
2449    def asURL(self):
2450        """
2451        Return :var:`self` as a :class:`URL` object (note that non-:class:`Text`
2452        content will be filtered out).
2453        """
2454        return url_.URL(Attr.__unicode__(self))
2455
2456    def forInput(self, root=None):
2457        """
2458        return a :class:`URL` pointing to the real location of the referenced
2459        resource. :var:`root` must be the root URL relative to which :var:`self`
2460        will be interpreted and usually comes from the ``root`` attribute of the
2461        :var:`converter` argument in :meth:`convert`.
2462        """
2463        u = self.asURL()
2464        if u.scheme == "root":
2465            u.scheme = None
2466        u = url_.URL(root)/u
2467        return u
2468
2469    def imagesize(self, root=None):
2470        """
2471        Return the size of an image as a tuple.
2472        """
2473        return self.openread(root).imagesize
2474
2475    def contentlength(self, root=None):
2476        """
2477        Return the size of a file in bytes.
2478        """
2479        return self.openread(root).contentlength
2480
2481    def lastmodified(self, root=None):
2482        """
2483        returns the timestamp for the last modification to the file
2484        """
2485        return self.openread(root).lastmodified
2486
2487    def openread(self, root=None):
2488        """
2489        Return a :class:`Resource` for reading from the URL.
2490        """
2491        return self.forInput(root).openread()
2492
2493    def openwrite(self, root=None):
2494        """
2495        Return a :class:`Resource` for writing to the URL.
2496        """
2497        return self.forInput(root).openwrite()
2498
2499
2500class _Attrs_Meta(Node.__metaclass__):
2501    def __new__(cls, name, bases, dict):
2502        self = super(_Attrs_Meta, cls).__new__(cls, name, bases, dict)
2503        self._byxmlname = weakref.WeakValueDictionary() # map XML name to attribute class
2504        self._bypyname = weakref.WeakValueDictionary() # map Python name to attribute class
2505        self._defaultattrsxml = weakref.WeakValueDictionary() # map XML name to attribute class with default value
2506        self._defaultattrspy = weakref.WeakValueDictionary() # map Python name to attribute class with default value
2507
2508        # go through the attributes and register them in the cache
2509        for key in dir(self):
2510            value = getattr(self, key)
2511            if isinstance(value, _Attr_Meta):
2512                self.add(value)
2513        return self
2514
2515    def __repr__(self):
2516        return "<attrs class {0.__module__}:{0.__fullname__} with {1} attrs at {2:#x}>".format(self, len(self._bypyname), id(self))
2517
2518    def __contains__(self, key):
2519        if isinstance(key, basestring):
2520            return key in self._bypyname
2521        if key.xmlns is not None:
2522            return True
2523        return self._bypyname.get(key.__name__, None) is key
2524
2525
2526class Attrs(Node, dict):
2527    """
2528    An attribute map. Allowed entries are specified through nested subclasses
2529    of :class:`Attr`.
2530    """
2531    __metaclass__ = _Attrs_Meta
2532
2533    def __init__(self, _content=None, **attrs):
2534        dict.__init__(self)
2535        # set default attribute values
2536        for (key, value) in self._defaultattrspy.iteritems():
2537            self[key] = value.default.clone()
2538        # set attributes, this might overwrite (or delete) default attributes
2539        self.update(_content, **attrs)
2540
2541    def __eq__(self, other):
2542        return self.__class__ is other.__class__ and dict.__eq__(self, other)
2543
2544    @classmethod
2545    def _str(cls, fullname=True, xml=True, decorate=True):
2546        return cls._strbase(fullname=fullname, xml=xml)
2547
2548    @classmethod
2549    def add(cls, value):
2550        cls._byxmlname[value.xmlname] = value
2551        cls._bypyname[value.__name__] = value
2552        if value.default:
2553            cls._defaultattrsxml[value.xmlname] = value
2554            cls._defaultattrspy[value.__name__] = value
2555        # fix classname (but don't patch inherited attributes)
2556        if "." not in value.__fullname__:
2557            value.__fullname__ = "{}.{}".format(cls.__fullname__, value.__fullname__)
2558
2559    def _create(self):
2560        node = self.__class__() # "virtual" constructor
2561        node.clear()
2562        return node
2563
2564    def clone(self):
2565        node = self._create()
2566        for value in dict.values(self):
2567            dict.__setitem__(node, value.__class__, value.clone())
2568        return self._decoratenode(node)
2569
2570    def __copy__(self):
2571        node = self._create()
2572        for value in dict.values(self):
2573            dict.__setitem__(node, value.__class__, value)
2574        return self._decoratenode(node)
2575
2576    def __deepcopy__(self, memo=None):
2577        node = self._create()
2578        if memo is None:
2579            memo = {}
2580        memo[id(self)] = node
2581        for value in dict.values(self):
2582            dict.__setitem__(node, value.__class__, copy.deepcopy(value, memo))
2583        return self._decoratenode(node)
2584
2585    def convert(self, converter):
2586        node = self._create()
2587        for value in self.values():
2588            newvalue = value.convert(converter)
2589            assert isinstance(newvalue, Node), "the convert method returned the illegal object {0!r} (type {1!r}) when converting the attribute {2.__class__.__name__} with the value {2!r}".format(newvalue, type(newvalue), value)
2590            node[value.__class__] = newvalue
2591        return node
2592
2593    def compact(self):
2594        node = self._create()
2595        for value in self.values():
2596            newvalue = value.compact()
2597            assert isinstance(newvalue, Node), "the compact method returned the illegal object {0!r} (type {1!r}) when compacting the attribute {2.__class__.__name__} with the value {2!r}".format(newvalue, type(newvalue), value)
2598            node[value.__class__] = newvalue
2599        return node
2600
2601    def normalized(self):
2602        node = self._create()
2603        for value in self.values():
2604            newvalue = value.normalized()
2605            assert isinstance(newvalue, Node), "the normalized method returned the illegal object {0!r} (type {1!r}) when normalizing the attribute {2.__class__.__name__} with the value {2!r}".format(newvalue, type(newvalue), value)
2606            node[value.__class__] = newvalue
2607        return node
2608
2609    def present(self, presenter):
2610        return presenter.presentAttrs(self) # return a generator-iterator
2611
2612    def checkvalid(self):
2613        # collect required attributes
2614        attrs = {value for value in self.allowedattrs() if value.required}
2615        # Check each existing attribute and remove it from the list of required ones
2616        for value in self.values():
2617            value.checkvalid()
2618            try:
2619                attrs.remove(value.__class__)
2620            except KeyError:
2621                pass
2622        # are there any required attributes remaining that haven't been specified? => warn about it
2623        if attrs:
2624            warnings.warn(RequiredAttrMissingWarning(self, list(attrs)))
2625
2626    def publish(self, publisher):
2627        if publisher.validate:
2628            self.checkvalid()
2629        for value in self.values():
2630            for part in value.publish(publisher):
2631                yield part
2632
2633    def __unicode__(self):
2634        return u""
2635
2636    @classmethod
2637    def isallowed(cls, name):
2638        if isinstance(name, basestring):
2639            return name in cls._bypyname
2640        if name.xmlns is not None:
2641            return True
2642        try:
2643            candidate = cls._bypyname[name.__name__]
2644        except KeyError:
2645            return False
2646        # make sure that both Python name and XML name match
2647        return candidate.xmlname == name.xmlname
2648
2649    @classmethod
2650    def isallowed_xml(cls, name, xmlns=None):
2651        if isinstance(name, basestring):
2652            return name in cls._byxmlname
2653        if name.xmlns is not None:
2654            return True
2655        try:
2656            candidate = cls._bypyname[name.__name__]
2657        except KeyError:
2658            return False
2659        # make sure that both Python name and XML name match
2660        return candidate.xmlname == name.xmlname
2661
2662    def __getattribute__(self, name):
2663        sup = super(Attrs, self)
2664        if name in sup.__getattribute__("_bypyname"): # avoid recursion
2665            return self.__getitem__(name)
2666        else:
2667            return sup.__getattribute__(name)
2668
2669    def __setattr__(self, name, value):
2670        sup = super(Attrs, self)
2671        if name in sup.__getattribute__("_bypyname"): # avoid recursion
2672            return self.__setitem__(name, value)
2673        else:
2674            return sup.__setattr__(name, value)
2675
2676    def __delattr__(self, name):
2677        sup = super(Attrs, self)
2678        if name in sup.__getattribute__("_bypyname"): # avoid recursion
2679            return self.__detitem__(name)
2680        else:
2681            return sup.__delattr__(name)
2682
2683    def __getitem__(self, name):
2684        if isinstance(name, list):
2685            node = self
2686            for subname in name:
2687                node = node[subname]
2688            return node
2689        return self.attr(name)
2690
2691    def __setitem__(self, name, value):
2692        if isinstance(name, list):
2693            if not name:
2694                raise ValueError("can't replace self")
2695            node = self
2696            for subname in name[:-1]:
2697                node = node[subname]
2698            node[name[-1]] = value
2699        return self.set(name, value)
2700
2701    def __delitem__(self, name):
2702        if isinstance(name, list):
2703            if not name:
2704                raise ValueError("can't delete self")
2705            node = self
2706            for subname in name[:-1]:
2707                node = node[subname]
2708            del node[name[-1]]
2709        dict.__delitem__(self, self.allowedattr(name))
2710
2711    def has(self, name):
2712        """
2713        Return whether :var:`self` has an attribute with a Python name :var:`name`.
2714        :var:`name` may also be an attribute class (either from ``self.Attrs``
2715        or a global attribute).
2716        """
2717        try:
2718            attr = dict.__getitem__(self, self.allowedattr(name))
2719        except KeyError:
2720            return False
2721        return len(attr)>0
2722
2723    def has_xml(self, name):
2724        """
2725        Similar to :meth:`has`, but :var:`name` is treated as the XML name
2726        instead of the Python name.
2727        """
2728        try:
2729            attr = dict.__getitem__(self, self.allowedattr_xml(name))
2730        except KeyError:
2731            return False
2732        return len(attr)>0
2733
2734    def __contains__(self, name):
2735        return self.has(name)
2736
2737    def get(self, name, default=None):
2738        """
2739        works like the dictionary method :meth:`get`, it returns the attribute
2740        with the Python name :var:`name`, or :var:`default` if :var:`self` has no
2741        such attribute. :var:`name` may also be an attribute class (either from
2742        ``self.Attrs`` or a global attribute).
2743        """
2744        attr = self.attr(name)
2745        if not attr:
2746            attr = self.allowedattr(name)(default) # pack the attribute into an attribute object
2747        return attr
2748
2749    def get_xml(self, name, default=None):
2750        """
2751        Similar to :meth:`get`, but :var:`name` is treated as the XML name
2752        instead of the Python name.
2753        """
2754        attr = self.attr_xml(name)
2755        if not attr:
2756            attr = self.allowedattr_xml(name)(default) # pack the attribute into an attribute object
2757        return attr
2758
2759    def set(self, name, value):
2760        """
2761        Set the attribute with the Python :var:`name` to the value :var:`value`.
2762        :var:`name` may be a string or an attribute class. The newly set attribute
2763        will be returned.
2764        """
2765        attr = self.allowedattr(name)
2766        value = attr(value)
2767        dict.__setitem__(self, attr, value) # put the attribute in our dict
2768        return value
2769
2770    def set_xml(self, name, value):
2771        """
2772        Similar to :meth:`set`, but :var:`name` is treated as the XML name
2773        instead of the Python name.
2774        """
2775        attr = self.allowedattr_xml(name)
2776        value = attr(value)
2777        dict.__setitem__(self, attr, value) # put the attribute in our dict
2778        return value
2779
2780    def setdefault(self, name, default):
2781        """
2782        Works like the dictionary method :meth:`setdefault`, it returns the
2783        attribute with the Python name :var:`name`. If :var:`self` has no such
2784        attribute, it will be set to :var:`default` and :var:`default` will be
2785        returned as the new attribute value.
2786        """
2787        value = self.attr(name)
2788        if not value:
2789            attr = self.allowedattr(name)
2790            value = attr(default) # pack the attribute into an attribute object
2791            dict.__setitem__(self, attr, value)
2792        return value
2793
2794    def setdefault_xml(self, name, default):
2795        """
2796        Similar to :meth:`setdefault`, but :var:`name` is treated as the XML name
2797        instead of the Python name.
2798        """
2799        value = self.attr_xml(name)
2800        if not value:
2801            attr = self.allowedattr(name)
2802            value = attr(default) # pack the attribute into an attribute object
2803            dict.__setitem__(self, attr, value)
2804        return value
2805
2806    def update(self, *args, **kwargs):
2807        """
2808        Copies attributes over from all mappings in :var:`args` and from
2809        :var:`kwargs`.
2810        """
2811        for mapping in args + (kwargs,):
2812            if mapping is not None:
2813                if isinstance(mapping, Attrs):
2814                    # This makes sure that global attributes are copied properly
2815                    for value in mapping._iterallvalues():
2816                        self[value.__class__] = value
2817                else:
2818                    for (attrname, attrvalue) in mapping.iteritems():
2819                        self[attrname] = attrvalue
2820
2821    @classmethod
2822    def allowedattrs(cls):
2823        """
2824        Return an iterator over all allowed attribute classes.
2825        """
2826        return cls._bypyname.itervalues()
2827
2828    @classmethod
2829    def allowedattr(cls, name):
2830        if isinstance(name, basestring):
2831            try:
2832                return cls._bypyname[name]
2833            except KeyError:
2834                raise IllegalAttrError(name, cls, False)
2835        # name is an attribute class
2836        if name.xmlns is not None: # if the attribute class is for a global attribute we accept it
2837            return name
2838        try:
2839            candidate = cls._bypyname[name.__name__]
2840        except KeyError:
2841            raise IllegalAttrError(name, cls, False)
2842        else:
2843            # make sure that both Python name and XML name match
2844            if candidate.xmlname == name.xmlname:
2845                return candidate
2846            raise IllegalAttrError(name, cls, False)
2847
2848    @classmethod
2849    def allowedattr_xml(cls, name):
2850        if isinstance(name, basestring):
2851            try:
2852                return cls._byxmlname[name]
2853            except KeyError:
2854                raise IllegalAttrError(name, cls, True)
2855        # name is an attribute class
2856        if name.xmlns is not None: # if the attribute class is for a global attribute we accept it
2857            return name
2858        try:
2859            candidate = cls._bypyname[name.__name__]
2860        except KeyError:
2861            raise IllegalAttrError(name, cls, False)
2862        else:
2863            # make sure that both Python name and XML name match
2864            if candidate.xmlname == name.xmlname:
2865                return candidate
2866            raise IllegalAttrError(name, cls, True)
2867
2868    def __len__(self):
2869        return misc.count(self.values())
2870
2871    def keys(self):
2872        for value in dict.itervalues(self):
2873            if value:
2874                yield value.__class__
2875
2876    iterkeys = __iter__ = keys
2877
2878    def values(self):
2879        for value in dict.itervalues(self):
2880            if value:
2881                yield value
2882
2883    itervalues = values
2884
2885    def items(self):
2886        for value in dict.itervalues(self):
2887            if value:
2888                yield (value.__class__, value)
2889
2890    iteritems = items
2891
2892    def _iterallvalues(self):
2893        """
2894        Iterate through all values, even the unset ones.
2895        """
2896        return dict.itervalues(self)
2897
2898    def attr(self, name):
2899        attr = self.allowedattr(name)
2900        try:
2901            value = dict.__getitem__(self, attr)
2902        except KeyError: # if the attribute is not there generate a new empty one
2903            value = attr()
2904            dict.__setitem__(self, attr, value)
2905        return value
2906
2907    def attr_xml(self, name):
2908        attr = self.allowedattr_xml(name)
2909        try:
2910            value = dict.__getitem__(self, attr)
2911        except KeyError: # if the attribute is not there generate a new empty one
2912            value = attr()
2913            dict.__setitem__(self, attr, value)
2914        return value
2915
2916    def filtered(self, function):
2917        """
2918        Return a filtered version of :var:`self`.
2919        """
2920        node = self._create()
2921        for (name, value) in self.items():
2922            if function(value):
2923                node[name] = value
2924        return node
2925
2926    def _fixnames(self, names):
2927        newnames = []
2928        for name in names:
2929            if isinstance(name, basestring):
2930                try:
2931                    name = self.allowedattr(name)
2932                except IllegalAttrError:
2933                    continue
2934            newnames.append(name)
2935        return tuple(newnames)
2936
2937    def _fixnames_xml(self, names):
2938        newnames = []
2939        for name in names:
2940            if isinstance(name, basestring):
2941                try:
2942                    name = self.allowedattr_xml(name)
2943                except IllegalAttrError:
2944                    continue
2945            newnames.append(name)
2946        return tuple(newnames)
2947
2948    def withnames(self, *names):
2949        """
2950        Return a copy of :var:`self` where only the attributes with Python names
2951        in :var:`names` are kept, all others are removed.
2952        """
2953        def isok(node):
2954            return isinstance(node, names)
2955
2956        names = self._fixnames(names)
2957        return self.filtered(isok)
2958
2959    def withnames_xml(self, *names):
2960        """
2961        Return a copy of :var:`self` where only the attributes with XML names
2962        in :var:`names` are kept, all others are removed.
2963        """
2964        def isok(node):
2965            return isinstance(node, names)
2966
2967        names = self._fixnames_xml(names)
2968        return self.filtered(isok)
2969
2970    def withoutnames(self, *names):
2971        """
2972        Return a copy of :var:`self` where all the attributes with Python names
2973        in :var:`names` are removed.
2974        """
2975        def isok(node):
2976            return not isinstance(node, names)
2977
2978        names = self._fixnames(names)
2979        return self.filtered(isok)
2980
2981    def withoutnames_xml(self, *names):
2982        """
2983        Return a copy of :var:`self` where all the attributes with XML names
2984        in :var:`names` are removed.
2985        """
2986        def isok(node):
2987            return not isinstance(node, names)
2988
2989        names = self._fixnames_xml(names)
2990        return self.filtered(isok)
2991
2992    def __repr__(self):
2993        l = len(self)
2994        if l==0:
2995            info = "(no attrs)"
2996        elif l==1:
2997            info = "(1 attr)"
2998        else:
2999            info = "({} attrs)".format(l)
3000        if self.startloc is not None:
3001            loc = " (from {})".format(self.startloc)
3002        else:
3003            loc = ""
3004        return "<{0.__class__.__module__}.{0.__fullname__} attrs {1}{2} at {3:#x}>".format(self, info, loc, id(self))
3005
3006
3007def _patchclassnames(dict, name):
3008    # If an :class:`Attrs` class has been provided patch up its class names
3009    try:
3010        attrs = dict["Attrs"]
3011    except KeyError:
3012        pass
3013    else:
3014        attrs.__fullname__ = "{}.Attrs".format(name)
3015        for (key, value) in attrs.__dict__.iteritems():
3016            if isinstance(value, _Attr_Meta):
3017                value.__fullname__ = "{}.{}".format(name, value.__fullname__)
3018
3019    # If a Context has been provided patch up its class names
3020    try:
3021        context = dict["Context"]
3022    except KeyError:
3023        pass
3024    else:
3025        context.__fullname__ = "{}.{}".format(name, context.__fullname__)
3026
3027
3028class _Element_Meta(Node.__metaclass__):
3029    def __new__(cls, name, bases, dict):
3030        if "model" in dict and isinstance(dict["model"], bool):
3031            from ll.xist import sims
3032            dict["model"] = sims.Any() if dict["model"] else sims.Empty()
3033        _patchclassnames(dict, name)
3034        self = super(_Element_Meta, cls).__new__(cls, name, bases, dict)
3035        if dict.get("register") is not None:
3036            threadlocalpool.pool.register(self)
3037        return self
3038
3039    def __repr__(self):
3040        return "<element class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
3041
3042
3043class Element(Node):
3044    """
3045    This class represents XML/XIST elements. All elements implemented by the
3046    user must be derived from this class.
3047
3048    Elements support the following class variables:
3049
3050    :attr:`model` : object with :meth:`checkvalid` method
3051        This is an object that is used for validating the content of the element.
3052        See the module :mod:`ll.xist.sims` for more info. If :attr:`model` is
3053        :const:`None` validation will be skipped, otherwise it will be performed
3054        when parsing or publishing.
3055
3056    :attr:`Attrs` : :class:`Element.Attrs` subclass
3057        This is a class derived from :class:`Element.Attrs` and must define all
3058        attributes as classes nested inside this :class:`Attrs` class.
3059
3060    :attr:`xmlns` : string
3061        This is the name of the namespace this element belong to.
3062
3063    :attr:`register` : bool
3064        If :attr:`register` is false the element will never be registered in a
3065        :class:`Pool`. The default is :const:`True`.
3066
3067    :attr:`xmlname` : string
3068        If the class name has to be different from the XML name (e.g. because the
3069        XML name is not a valid Python identifier) :attr:`xmlname` can be used to
3070        specify the real XML name. Otherwise the XML name will be the Python name.
3071    """
3072    __metaclass__ = _Element_Meta
3073
3074    model = None
3075    register = None
3076
3077    Attrs = Attrs
3078
3079    def __init__(self, *content, **attrs):
3080        """
3081        Create a new :class:`Element` instance.
3082
3083        Positional arguments are treated as content nodes. Keyword arguments and
3084        dictionaries are treated as attributes.
3085        """
3086        self.attrs = self.Attrs()
3087        newcontent = []
3088        for child in content:
3089            if isinstance(child, dict):
3090                self.attrs.update(child)
3091            else:
3092                newcontent.append(child)
3093        self.content = Frag(*newcontent)
3094        self.attrs.update(attrs)
3095
3096    def __getstate__(self):
3097        attrs = {}
3098        for (key, value) in self.attrs.iteritems():
3099            if key.xmlns is None:
3100                key = key.__name__
3101            else:
3102                key = (key.__module__, key.__fullname__)
3103            attrs[key] = Frag(value)
3104        return (self.content, attrs)
3105
3106    def __setstate__(self, (content, attrs)):
3107        self.content = content
3108        self.attrs = self.Attrs()
3109        for (key, value) in attrs.iteritems():
3110            if not isinstance(key, basestring):
3111                obj = __import__(key[0])
3112                for name in key[0].split(".")[1:]:
3113                    obj = getattr(obj, name)
3114                for name in key[1].split("."):
3115                    obj = getattr(obj, name)
3116                key = obj
3117            self.attrs[key] = value
3118
3119    def __enter__(self):
3120        """
3121        :class:`Element` nodes can be used in ``with`` blocks to build XIST trees.
3122        Inside a ``with`` block ``+`` and :func:`add` can be used to append node
3123        to the currently active element in the ``with`` block::
3124
3125            with xsc.build():
3126                with html.ul() as node:
3127                    +html.li("I hear and I forget.")
3128                    +html.li("I see and I believe.")
3129                    +html.li("I do and I understand.")
3130                    xsc.add(class_="quote")
3131            print node.bytes()
3132        """
3133        threadlocalnodehandler.handler.enter(self)
3134        return self
3135
3136    def __exit__(self, type, value, traceback):
3137        threadlocalnodehandler.handler.exit()
3138
3139    def __call__(self, *content, **attrs):
3140        """
3141        Calling an element add items in :var:`content` to the element content
3142        and set attributes from :var:`attrs`. The element itself will be returned.
3143        """
3144        for child in content:
3145            if isinstance(child, dict):
3146                self.attrs.update(child)
3147            else:
3148                self.content.append(child)
3149        for (attrname, attrvalue) in attrs.iteritems():
3150            self.attrs[attrname] = attrvalue
3151        return self
3152
3153    def __eq__(self, other):
3154        return self.__class__ is other.__class__ and self.content==other.content and self.attrs==other.attrs
3155
3156    @classmethod
3157    def _str(cls, fullname=True, xml=True, decorate=True):
3158        s = cls._strbase(fullname=fullname, xml=xml)
3159        if decorate:
3160            if cls.model is not None and cls.model.empty:
3161                s = "<{}/>".format(s)
3162            else:
3163                s = "<{}>".format(s)
3164        return s
3165
3166    def checkvalid(self):
3167        if self.model is not None:
3168            self.model.checkvalid(self)
3169        self.attrs.checkvalid()
3170
3171    def append(self, *items):
3172        """
3173        Append every item in :var:`items` to the elements content.
3174        """
3175        self.content.append(*items)
3176
3177    def extend(self, items):
3178        """
3179        Append all items in :var:`items` to the elements content.
3180        """
3181        self.content.extend(items)
3182
3183    def insert(self, index, *items):
3184        """
3185        Insert every item in :var:`items` at the position :var:`index`.
3186        """
3187        self.content.insert(index, *items)
3188
3189    def convert(self, converter):
3190        node = self.__class__() # "virtual" constructor
3191        node.content = self.content.convert(converter)
3192        node.attrs = self.attrs.convert(converter)
3193        return self._decoratenode(node)
3194
3195    def clone(self):
3196        node = self.__class__() # "virtual" constructor
3197        node.content = self.content.clone() # this is faster than passing it in the constructor (no :func:`tonode` call)
3198        node.attrs = self.attrs.clone()
3199        return self._decoratenode(node)
3200
3201    def __copy__(self):
3202        node = self.__class__()
3203        node.content = copy.copy(self.content)
3204        node.attrs = copy.copy(self.attrs)
3205        return self._decoratenode(node)
3206
3207    def __deepcopy__(self, memo=None):
3208        node = self.__class__()
3209        if memo is None:
3210            memo = {}
3211        memo[id(self)] = node
3212        node.content = copy.deepcopy(self.content, memo)
3213        node.attrs = copy.deepcopy(self.attrs, memo)
3214        return self._decoratenode(node)
3215
3216    def __unicode__(self):
3217        return unicode(self.content)
3218
3219    def _addimagesizeattributes(self, url, widthattr=None, heightattr=None):
3220        """
3221        Automatically set image width and height attributes.
3222
3223        The size of the image with the URL :var:`url` will be determined and the
3224        width of the image will be put into the attribute with the name
3225        :var:`widthattr` if :var:`widthattr` is not :const:`None` and the
3226        attribute is not set already. The same will happen for the height, which
3227        will be put into the attribute named :var:`heighattr`.
3228        """
3229        try:
3230            size = url.imagesize()
3231        except IOError, exc:
3232            warnings.warn(FileNotFoundWarning("can't read image", url, exc))
3233        else:
3234            for attr in (heightattr, widthattr):
3235                if attr is not None: # do something to the width/height
3236                    if not self.attrs.has(attr):
3237                        self[attr] = size[attr==heightattr]
3238
3239    def present(self, presenter):
3240        return presenter.presentElement(self) # return a generator-iterator
3241
3242    def _publishname(self, publisher):
3243        if self.xmlns is not None:
3244            prefix = publisher._ns2prefix.get(self.xmlns)
3245            if prefix is not None:
3246                return u"{}:{}".format(prefix, self.xmlname)
3247        return self.xmlname
3248
3249    def _publishfull(self, publisher):
3250        """
3251        Does the full publication of the element. If you need full elements
3252        inside attributes (e.g. for JSP tag libraries), you can overwrite
3253        :meth:`publish` and simply call this method.
3254        """
3255        name = self._publishname(publisher)
3256        yield publisher.encode(u"<")
3257        yield publisher.encode(name)
3258        # we're the first element to be published, so we have to create the xmlns attributes
3259        if publisher._publishxmlns:
3260            for (xmlns, prefix) in publisher._ns2prefix.iteritems():
3261                if xmlns not in publisher.hidexmlns:
3262                    yield publisher.encode(u" xmlns")
3263                    if prefix is not None:
3264                        yield publisher.encode(u":")
3265                        yield publisher.encode(prefix)
3266                    yield publisher.encode(u'="')
3267                    yield publisher.encode(xmlns)
3268                    yield publisher.encode('"')
3269            # reset the note, so the next element won't create the attributes again
3270            publisher._publishxmlns = False
3271        for part in self.attrs.publish(publisher):
3272            yield part
3273        if len(self):
3274            yield publisher.encode(u">")
3275            for part in self.content.publish(publisher):
3276                yield part
3277            yield publisher.encode(u"</")
3278            yield publisher.encode(name)
3279            yield publisher.encode(u">")
3280        else:
3281            if publisher.xhtml in (0, 1):
3282                if self.model is not None and self.model.empty:
3283                    if publisher.xhtml==1:
3284                        yield publisher.encode(u" /")
3285                    yield publisher.encode(u">")
3286                else:
3287                    yield publisher.encode(u"></")
3288                    yield publisher.encode(name)
3289                    yield publisher.encode(u">")
3290            elif publisher.xhtml == 2:
3291                yield publisher.encode(u"/>")
3292
3293    def publish(self, publisher):
3294        if publisher.validate:
3295            self.checkvalid()
3296        if publisher.inattr:
3297            # publish the content only when we are inside an attribute. This works much like using the plain string value,
3298            # but even works with processing instructions, or what the abbreviation entities return
3299            return self.content.publish(publisher) # return a generator-iterator
3300        else:
3301            return self._publishfull(publisher) # return a generator-iterator
3302
3303    def __getitem__(self, index):
3304        """
3305        If :var:`index` is a string, return the attribute with this (Python)
3306        name. If :var:`index` is an attribute class, return the attribute
3307        that is an instance of this class. If :var:`index` is a number or slice
3308        return the appropriate content node. :var:`index` may also be a list, in
3309        with case :meth:`__getitem__` will be applied recusively.
3310        :meth:`__getitem__` also supports walk filters.
3311
3312        """
3313        if isinstance(index, (basestring, _Attr_Meta)):
3314            return self.attrs[index]
3315        elif isinstance(index, (list, int, long, slice)):
3316            return self.content[index]
3317        else:
3318            from ll.xist import xfind
3319            def iterate(matcher):
3320                path = [self, None]
3321                for child in self:
3322                    path[-1] = child
3323                    if matcher(path):
3324                        yield child
3325            return misc.Iterator(iterate(xfind.makewalkfilter(index).matchpath))
3326
3327    def __setitem__(self, index, value):
3328        """
3329        Set an attribute or content node to the value :var:`value`. For possible
3330        types for :var:`index` see :meth:`__getitem__`.
3331        """
3332        if isinstance(index, (basestring, _Attr_Meta)):
3333            self.attrs[index] = value
3334        elif isinstance(index, (list, int, long, slice)):
3335            self.content[index] = value
3336        else:
3337            from ll.xist import xfind
3338            matcher = xfind.makewalkfilter(index).matchpath
3339            value = Frag(value)
3340            newcontent = []
3341            path = [self, None]
3342            for child in self:
3343                path[-1] = child
3344                if matcher(path):
3345                    newcontent.extend(value)
3346                else:
3347                    newcontent.append(child)
3348            self.content[:] = newcontent
3349
3350    def __delitem__(self, index):
3351        """
3352        Remove an attribute or content node. For possible types for :var:`index`
3353        see :meth:`__getitem__`.
3354        """
3355        if isinstance(index, (basestring, _Attr_Meta)):
3356            del self.attrs[index]
3357        elif isinstance(index, (list, int, long, slice)):
3358            del self.content[index]
3359        else:
3360            from ll.xist import xfind
3361            matcher = xfind.makewalkfilter(index).matchpath
3362            self.content = Frag(child for child in self if not matcher([self, child]))
3363
3364    def __getslice__(self, index1, index2):
3365        """
3366        Return a copy of the element that contains a slice of the content.
3367        """
3368        return self.content[index1:index2]
3369
3370    def __setslice__(self, index1, index2, sequence):
3371        """
3372        Replace a slice of the content of the element.
3373        """
3374        self.content[index1:index2] = sequence
3375
3376    def __delslice__(self, index1, index2):
3377        """
3378        Remove a slice of the content of the element.
3379        """
3380        del self.content[index1:index2]
3381
3382    def __iadd__(self, other):
3383        self.extend(other)
3384        return self
3385
3386    def __len__(self):
3387        """
3388        Return the number of children.
3389        """
3390        return len(self.content)
3391
3392    def __iter__(self):
3393        return iter(self.content)
3394
3395    def compact(self):
3396        node = self.__class__()
3397        node.content = self.content.compact()
3398        node.attrs = self.attrs.compact()
3399        return self._decoratenode(node)
3400
3401    def withsep(self, separator, clone=False):
3402        """
3403        Return a version of :var:`self` with a separator node between the child
3404        nodes of :var:`self`. For more info see :meth:`Frag.withsep`.
3405        """
3406        node = self.__class__()
3407        node.attrs = self.attrs.clone()
3408        node.content = self.content.withsep(separator, clone)
3409        return node
3410
3411    def sorted(self, cmp=None, key=None, reverse=False):
3412        """
3413        Return a sorted version of :var:`self`. :var:`compare` is a comparison
3414        function. The arguments :var:`cmp`, :var:`key` and :var:`reverse` have
3415        the same meaning as fot the builtin :func:`sorted` function.
3416        """
3417        node = self.__class__()
3418        node.attrs = self.attrs.clone()
3419        node.content = self.content.sorted(cmp, key, reverse)
3420        return node
3421
3422    def reversed(self):
3423        """
3424        Return a reversed version of :var:`self`.
3425        """
3426        node = self.__class__()
3427        node.attrs = self.attrs.clone()
3428        node.content = self.content.reversed()
3429        return node
3430
3431    def filtered(self, function):
3432        """
3433        Return a filtered version of the :var:`self`.
3434        """
3435        node = self.__class__()
3436        node.attrs = self.attrs.clone()
3437        node.content = self.content.filtered(function)
3438        return node
3439
3440    def shuffled(self):
3441        """
3442        Return a shuffled version of the :var:`self`.
3443        """
3444        node = self.__class__()
3445        node.attrs = self.attrs.clone()
3446        node.content = self.content.shuffled()
3447        return node
3448
3449    def mapped(self, function, converter=None, **converterargs):
3450        if converter is None:
3451            converter = Converter(**converterargs)
3452        node = function(self, converter)
3453        assert isinstance(node, Node), "the mapped method returned the illegal object {!r} (type {!r}) when mapping {!r}".format(node, type(node), self)
3454        if node is self:
3455            node = self.__class__(self.content.mapped(function, converter))
3456            node.attrs = self.attrs.clone()
3457        return node
3458
3459    def normalized(self):
3460        node = self.__class__()
3461        node.attrs = self.attrs.normalized()
3462        node.content = self.content.normalized()
3463        return node
3464
3465    def pretty(self, level=0, indent="\t"):
3466        orglevel = level # Remember the original indent level, so that any misconfiguration inside the element doesn't mess with the indentation
3467        node = self.__class__(self.attrs)
3468        if len(self):
3469            # search for text content
3470            for child in self:
3471                if isinstance(child, Text):
3472                    # leave content alone
3473                    node.append(self.content.clone())
3474                    break
3475            else:
3476                level += 1
3477                for child in self:
3478                    level += child.prettyindentbefore
3479                    node.append("\n", child.pretty(level, indent))
3480                    level += child.prettyindentafter
3481                node.append("\n", indent*orglevel)
3482        if orglevel>0:
3483            node = Frag(indent*orglevel, node)
3484        return node
3485
3486    def __repr__(self):
3487        lc = len(self.content)
3488        if lc==0:
3489            infoc = "no children"
3490        elif lc==1:
3491            infoc = "1 child"
3492        else:
3493            infoc = "{} children".format(lc)
3494        la = len(self.attrs)
3495        if la==0:
3496            infoa = "no attrs"
3497        elif la==1:
3498            infoa = "1 attr"
3499        else:
3500            infoa = "{} attrs".format(la)
3501        if self.startloc is not None:
3502            loc = " (from {})".format(self.startloc)
3503        else:
3504            loc = ""
3505        return "<{0.__class__.__module__}.{0.__fullname__} element object ({1}/{2}){3} at {4:#x}>".format(self, infoc, infoa, loc, id(self))
3506
3507
3508class _Entity_Meta(Node.__metaclass__):
3509    def __new__(cls, name, bases, dict):
3510        self = super(_Entity_Meta, cls).__new__(cls, name, bases, dict)
3511        if dict.get("register") is not None:
3512            threadlocalpool.pool.register(self)
3513        return self
3514
3515    def __repr__(self):
3516        return "<entity class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
3517
3518
3519class Entity(Node):
3520    """
3521    Class for entities. Derive your own entities from it and overwrite
3522    :meth:`convert`.
3523    """
3524    __metaclass__ = _Entity_Meta
3525
3526    register = None
3527
3528    @classmethod
3529    def _str(cls, fullname=True, xml=True, decorate=True):
3530        s = cls._strbase(fullname=fullname, xml=xml)
3531        if decorate:
3532            s = "&{};".format(s)
3533        return s
3534
3535    def __eq__(self, other):
3536        return self.__class__ is other.__class__
3537
3538    def compact(self):
3539        return self
3540
3541    def present(self, presenter):
3542        return presenter.presentEntity(self) # return a generator-iterator
3543
3544    def publish(self, publisher):
3545        yield publisher.encode(u"&")
3546        yield publisher.encode(self.xmlname)
3547        yield publisher.encode(u";")
3548
3549    def __repr__(self):
3550        if self.startloc is not None:
3551            loc = " (from {})".format(self.startloc)
3552        else:
3553            loc = ""
3554        return "<{0.__class__.__module__}.{0.__fullname__} entity object{1} at {2:#x}>".format(self, loc, id(self))
3555
3556
3557class _CharRef_Meta(Entity.__metaclass__): # don't subclass Text.__metaclass__, as this is redundant
3558    def __repr__(self):
3559        return "<charref class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
3560
3561
3562class CharRef(Text, Entity):
3563    """
3564    A simple named character reference, the codepoint is in the class attribute
3565    :attr:`codepoint`.
3566    """
3567    __metaclass__ = _CharRef_Meta
3568    register = None
3569
3570    def __init__(self):
3571        Text.__init__(self, unichr(self.codepoint))
3572        Entity.__init__(self)
3573
3574    def __getnewargs__(self):
3575        return ()
3576
3577    def present(self, presenter):
3578        return presenter.presentEntity(self) # return a generator-iterator
3579
3580    # The rest is the same as for Text, but does not return CharRefs, but Texts
3581    def __getitem__(self, index):
3582        return Text(self.content.__getitem__(index))
3583
3584    def __add__(self, other):
3585        return Text(self.content + other)
3586
3587    def __radd__(self, other):
3588        return Text(unicode(other) + self.content)
3589
3590    def __mul__(self, n):
3591        return Text(n * self.content)
3592
3593    def __rmul__(self, n):
3594        return Text(n * self.content)
3595
3596    def __getslice__(self, index1, index2):
3597        return Text(self.content.__getslice__(index1, index2))
3598
3599    def capitalize(self):
3600        return Text(self.content.capitalize())
3601
3602    def center(self, width):
3603        return Text(self.content.center(width))
3604
3605    def ljust(self, width, fill=u" "):
3606        return Text(self.content.ljust(width, fill))
3607
3608    def lower(self):
3609        return Text(self.content.lower())
3610
3611    def lstrip(self, chars=None):
3612        return Text(self.content.lstrip(chars))
3613
3614    def replace(self, old, new, maxsplit=-1):
3615        return Text(self.content.replace(old, new, maxsplit))
3616
3617    def rjust(self, width, fill=u" "):
3618        return Text(self.content.rjust(width, fill))
3619
3620    def rstrip(self, chars=None):
3621        return Text(self.content.rstrip(chars))
3622
3623    def strip(self, chars=None):
3624        return Text(self.content.strip(chars))
3625
3626    def swapcase(self):
3627        return Text(self.content.swapcase())
3628
3629    def title(self):
3630        return Text(self.content.title())
3631
3632    def translate(self, table):
3633        return Text(self.content.translate(table))
3634
3635    def upper(self):
3636        return Text(self.content.upper())
3637
3638
3639###
3640### XML class pool
3641###
3642
3643class Pool(misc.Pool):
3644    """
3645    A :class:`Pool` stores a collection of XIST classes and can be passed to a
3646    parser. The parser will ask the pool which classes to use when elements,
3647    processing instructions etc. have to be instantiated.
3648    """
3649
3650    def __init__(self, *objects):
3651        """
3652        Create a :class:`Pool` object. All items in :var:`objects` will be
3653        registered in the pool.
3654        """
3655        self._elementsbyxmlname = {}
3656        self._elementsbypyname = {}
3657        self._procinstsbyxmlname = {}
3658        self._procinstsbypyname = {}
3659        self._entitiesbyxmlname = {}
3660        self._entitiesbypyname = {}
3661        self._charrefsbyxmlname = {}
3662        self._charrefsbypyname = {}
3663        self._charrefsbycodepoint = {}
3664        self._attrsbyxmlname = {}
3665        self._attrsbypyname = {}
3666        misc.Pool.__init__(self, *objects)
3667
3668    def register(self, object):
3669        """
3670        Register :var:`object` in the pool. :var:`object` can be:
3671
3672        *   a :class:`Element`, :class:`ProcInst`, :class:`Entity`, or
3673            :class:`CharRef` class;
3674
3675        *   an :class:`Attr` class for a global attribute;
3676
3677        *   an :class:`Attrs` class containing global attributes;
3678
3679        *   a :class:`dict` (all values will be registered, this makes it possible
3680            to e.g. register all local variables by passing ``vars()``);
3681
3682        *   a module (all attributes in the module will be registered).
3683        """
3684        # Note that the following is a complete reimplementation, otherwise the interactions would be too complicated.
3685        if isinstance(object, type):
3686            if issubclass(object, Element):
3687                if object.register:
3688                    self._elementsbyxmlname[(object.xmlname, object.xmlns)] = object
3689                    self._elementsbypyname[(object.__name__, object.xmlns)] = object
3690            elif issubclass(object, ProcInst):
3691                if object.register:
3692                    self._procinstsbyxmlname[object.xmlname] = object
3693                    self._procinstsbypyname[object.__name__] = object
3694            elif issubclass(object, Entity):
3695                if object.register:
3696                    self._entitiesbyxmlname[object.xmlname] = object
3697                    self._entitiesbypyname[object.__name__] = object
3698                    if issubclass(object, CharRef):
3699                        self._charrefsbyxmlname[object.xmlname] = object
3700                        self._charrefsbypyname[object.__name__] = object
3701                        self._charrefsbycodepoint[object.codepoint] = object
3702            elif issubclass(object, Attr):
3703                if object.xmlns is not None and object.register:
3704                    self._attrsbyxmlname[(object.xmlname, object.xmlns)] = object
3705                    self._attrsbypyname[(object.__name__, object.xmlns)] = object
3706            elif issubclass(object, Attrs):
3707                for attr in object.allowedattrs():
3708                    self.register(attr)
3709            self._attrs[object.__name__] = object
3710        elif isinstance(object, types.ModuleType):
3711            self.register(object.__dict__)
3712        elif isinstance(object, dict):
3713            for (key, value) in object.iteritems():
3714                if key == "__bases__":
3715                    for base in value:
3716                        if not isinstance(base, Pool):
3717                            base = self.__class__(base)
3718                        self.bases.append(base)
3719                elif isinstance(value, type):
3720                    self.register(value)
3721                elif not isinstance(value, (types.ModuleType, dict)):
3722                    try:
3723                        self._attrs[key] = value
3724                    except TypeError:
3725                        pass
3726        elif isinstance(object, Pool):
3727            self.bases.append(object)
3728
3729    def __enter__(self):
3730        self.prev = threadlocalpool.pool
3731        threadlocalpool.pool = self
3732        return self
3733
3734    def __exit__(self, type, value, traceback):
3735        threadlocalpool.pool = self.prev
3736        del self.prev
3737
3738    def clear(self):
3739        """
3740        Make :var:`self` empty.
3741        """
3742        self._elementsbyxmlname.clear()
3743        self._elementsbypyname.clear()
3744        self._procinstsbyxmlname.clear()
3745        self._procinstsbypyname.clear()
3746        self._entitiesbyxmlname.clear()
3747        self._entitiesbypyname.clear()
3748        self._charrefsbyxmlname.clear()
3749        self._charrefsbypyname.clear()
3750        self._charrefsbycodepoint.clear()
3751        self._attrsbyxmlname.clear()
3752        self._attrsbypyname.clear()
3753        misc.Pool.clear(self)
3754
3755    def clone(self):
3756        """
3757        Return a copy of :var:`self`.
3758        """
3759        copy = misc.Pool.clone(self)
3760        copy._elementsbyxmlname = self._elementsbyxmlname.copy()
3761        copy._elementsbypyname = self._elementsbypyname.copy()
3762        copy._procinstsbyxmlname = self._procinstsbyxmlname.copy()
3763        copy._procinstsbypyname = self._procinstsbypyname.copy()
3764        copy._entitiesbyxmlname = self._entitiesbyxmlname.copy()
3765        copy._entitiesbypyname = self._entitiesbypyname.copy()
3766        copy._charrefsbyxmlname = self._charrefsbyxmlname.copy()
3767        copy._charrefsbypyname = self._charrefsbypyname.copy()
3768        copy._charrefsbycodepoint = self._charrefsbycodepoint.copy()
3769        copy._attrsbyxmlname = self._attrsbyxmlname.copy()
3770        copy._attrsbypyname = self._attrsbypyname.copy()
3771        return copy
3772
3773    def elements(self):
3774        """
3775        Return an iterator for all registered element classes.
3776        """
3777        seen = set()
3778        for element in self._elementsbypyname.itervalues():
3779            yield element
3780            seen.add((element.xmlname, element.xmlns))
3781        for base in self.bases:
3782            for element in base.elements():
3783                if (element.xmlname, element.xmlns) not in seen:
3784                    yield element
3785                    seen.add((element.xmlname, element.xmlns))
3786
3787    def elementclass(self, name, xmlns):
3788        """
3789        Return the element class for the element with the Python name :var:`name`
3790        and the namespace :var:`xmlns`. If the element can't be found an
3791        :exc:`IllegalElementError` will be raised.
3792        """
3793        if isinstance(xmlns, (list, tuple)):
3794            for xmlns in xmlns:
3795                xmlns = nsname(xmlns)
3796                try:
3797                    return self._elementsbypyname[(name, xmlns)]
3798                except KeyError:
3799                    pass
3800        else:
3801            xmlns = nsname(xmlns)
3802            try:
3803                return self._elementsbypyname[(name, xmlns)]
3804            except KeyError:
3805                pass
3806        for base in self.bases:
3807            try:
3808                return base.elementclass(name, xmlns)
3809            except IllegalElementError:
3810                pass
3811        raise IllegalElementError(name, xmlns, False)
3812
3813    def elementclass_xml(self, name, xmlns):
3814        """
3815        Return the element class for the element type with the XML name
3816        :var:`name` and the namespace :var:`xmlns`. If the element can't
3817        be found an :exc:`IllegalElementError` will be raised.
3818        """
3819        if isinstance(xmlns, (list, tuple)):
3820            for xmlns in xmlns:
3821                xmlns = nsname(xmlns)
3822                try:
3823                    return self._elementsbyxmlname[(name, xmlns)]
3824                except KeyError:
3825                    pass
3826        else:
3827            xmlns = nsname(xmlns)
3828            try:
3829                return self._elementsbyxmlname[(name, xmlns)]
3830            except KeyError:
3831                pass
3832        for base in self.bases:
3833            try:
3834                return base.elementclass_xml(name, xmlns)
3835            except IllegalElementError:
3836                pass
3837        raise IllegalElementError(name, xmlns, True)
3838
3839    def element(self, name, xmlns):
3840        """
3841        Return an element object for the element type with the Python name
3842        :var:`name` and the namespace :var:`xmlns`.
3843        """
3844        return self.elementclass(name, xmlns)()
3845
3846    def element_xml(self, name, xmlns):
3847        """
3848        Return an element object for the element type with the XML name
3849        :var:`name` and the namespace :var:`xmlns`.
3850        """
3851        return self.elementclass_xml(name, xmlns)()
3852
3853    def haselement(self, name, xmlns):
3854        """
3855        Is there a registered element class in :var:`self` for the element type
3856        with the Python name :var:`name` and the namespace :var:`xmlns`?
3857        """
3858        return (name, nsname(xmlns)) in self._elementsbypyname or any(base.haselement(name, xmlns) for base in self.bases)
3859
3860    def haselement_xml(self, name, xmlns):
3861        """
3862        Is there a registered element class in :var:`self` for the element type
3863        with the XML name :var:`name` and the namespace :var:`xmlns`?
3864        """
3865        return (name, nsname(xmlns)) in self._elementsbyxmlname or any(base.haselement_xml(name, xmlns) for base in self.bases)
3866
3867    def procinsts(self):
3868        """
3869        Return an iterator for all registered processing instruction classes.
3870        """
3871        seen = set()
3872        for procinst in self._procinstsbypyname.itervalues():
3873            yield procinst
3874            seen.add(procinst.xmlname)
3875        for base in self.bases:
3876            for procinst in base.procinsts():
3877                if procinst.xmlname not in seen:
3878                    yield procinst
3879                    seen.add(procinst.xmlname)
3880
3881    def procinstclass(self, name):
3882        """
3883        Return the processing instruction class for the PI with the Python name
3884        :var:`name`. If the processing instruction can't be found an
3885        :exc:`IllegalProcInstError` will be raised.
3886        """
3887        try:
3888            return self._procinstsbypyname[name]
3889        except KeyError:
3890            for base in self.bases:
3891                try:
3892                    return base.procinstclass(name)
3893                except IllegalProcInstError:
3894                    pass
3895            raise IllegalProcInstError(name, False)
3896
3897    def procinstclass_xml(self, name):
3898        """
3899        Return the processing instruction class for the PI with the XML name
3900        :var:`name`. If the processing instruction can't be found an
3901        :exc:`IllegalProcInstError` will be raised.
3902        """
3903        try:
3904            return self._procinstsbyxmlname[name]
3905        except KeyError:
3906            for base in self.bases:
3907                try:
3908                    return base.procinstclass_xml(name)
3909                except IllegalProcInstError:
3910                    pass
3911            raise IllegalProcInstError(name, True)
3912
3913    def procinst(self, name, content):
3914        """
3915        Return a processing instruction object for the PI type with the Python
3916        target name :var:`name`.
3917        """
3918        return self.procinstclass(name)(content)
3919
3920    def procinst_xml(self, name, content):
3921        """
3922        Return a processing instruction object for the PI type with the XML
3923        target name :var:`name`.
3924        """
3925        return self.procinstclass_xml(name)(content)
3926
3927    def hasprocinst(self, name):
3928        """
3929        Is there a registered processing instruction class in :var:`self` for the
3930        PI with the Python name :var:`name`?
3931        """
3932        return name in self._procinstsbypyname or any(base.hasprocinst(name) for base in self.bases)
3933
3934    def hasprocinst_xml(self, name):
3935        """
3936        Is there a registered processing instruction class in :var:`self` for the
3937        PI with the XML name :var:`name`?
3938        """
3939        return name in self._procinstsbyxmlname or any(base.hasprocinst_xml(name) for base in self.bases)
3940
3941    def entities(self):
3942        """
3943        Return an iterator for all registered entity classes.
3944        """
3945        seen = set()
3946        for entity in self._entitiesbypyname.itervalues():
3947            yield entity
3948            seen.add(entity.xmlname)
3949        for base in self.bases:
3950            for entity in base.entities():
3951                if entity.xmlname not in seen:
3952                    yield entity
3953                    seen.add(entity.xmlname)
3954
3955    def entityclass(self, name):
3956        """
3957        Return the entity class for the entity with the Python name :var:`name`.
3958        If the entity can't be found an :exc:`IllegalEntityError` will be raised.
3959        """
3960        try:
3961            return self._entitiesbypyname[name]
3962        except KeyError:
3963            for base in self.bases:
3964                try:
3965                    return base.entityclass(name)
3966                except IllegalEntityError:
3967                    pass
3968            raise IllegalEntityError(name, False)
3969
3970    def entityclass_xml(self, name):
3971        """
3972        Return the entity class for the entity with the XML name :var:`name`.
3973        If the entity can't be found an :exc:`IllegalEntityError` will be raised.
3974        """
3975        try:
3976            return self._entitiesbyxmlname[name]
3977        except KeyError:
3978            for base in self.bases:
3979                try:
3980                    return base.entityclass_xml(name)
3981                except IllegalEntityError:
3982                    pass
3983            raise IllegalEntityError(name, True)
3984
3985    def entity(self, name):
3986        """
3987        Return an entity object for the entity with the Python name :var:`name`.
3988        """
3989        return self.entityclass(name)()
3990
3991    def entity_xml(self, name):
3992        """
3993        Return an entity object for the entity with the XML name :var:`name`.
3994        """
3995        return self.entityclass_xml(name)()
3996
3997    def hasentity(self, name):
3998        """
3999        Is there a registered entity class in :var:`self` for the entity with the
4000        Python name :var:`name`?
4001        """
4002        return name in self._entitiesbypyname or any(base.hasentity(name) for base in self.bases)
4003
4004    def hasentity_xml(self, name):
4005        """
4006        Is there a registered entity class in :var:`self` for the entity with the
4007        XML name :var:`name`?
4008        """
4009        return name in self._entitiesbyxmlname or any(base.hasentity_xml(name) for base in self.bases)
4010
4011    def charrefs(self):
4012        """
4013        Return an iterator for all character entity classes.
4014        """
4015        seen = set()
4016        for charref in self._charrefsbypyname.itervalues():
4017            yield charref
4018            seen.add(charref.xmlname)
4019        for base in self.bases:
4020            for charref in base.charrefs():
4021                if charref.xmlname not in seen:
4022                    yield charref
4023                    seen.add(charref.xmlname)
4024
4025    def charrefclass(self, name):
4026        """
4027        Return the character entity with the Python name :var:`name`.
4028        :var:`name` can also be an :class:`int` specifying the codepoint.
4029        If the character entity can't be found a :exc:`IllegalEntityError`
4030        will be raised.
4031        """
4032        try:
4033            if isinstance(name, (int, long)):
4034                return self._charrefsbycodepoint[name]
4035            return self._charrefsbypyname[name]
4036        except KeyError:
4037            for base in self.bases:
4038                try:
4039                    return base.charrefclass(name)
4040                except IllegalEntityError:
4041                    pass
4042            raise IllegalEntityError(name, False)
4043
4044    def charrefclass_xml(self, name):
4045        """
4046        Return the character entity with the XML name :var:`name`.
4047        :var:`name` can also be an :class:`int` specifying the codepoint.
4048        If the character entity can't be found a :exc:`IllegalEntityError`
4049        will be raised.
4050        """
4051        try:
4052            if isinstance(name, (int, long)):
4053                return self._charrefsbycodepoint[name]
4054            return self._charrefsbyxmlname[name]
4055        except KeyError:
4056            for base in self.bases:
4057                try:
4058                    return base.charrefclass_xml(name)
4059                except IllegalEntityError:
4060                    pass
4061            raise IllegalEntityError(name, True)
4062
4063    def charref(self, name):
4064        """
4065        Return a character entity object for the character with the Python name
4066        or codepoint :var:`name`.
4067        """
4068        return self.charrefclass(name)()
4069
4070    def charref_xml(self, name):
4071        """
4072        Return a character entity object for the character with the XML name
4073        or codepoint :var:`name`.
4074        """
4075        return self.charrefclass_xml(name)()
4076
4077    def hascharref(self, name):
4078        """
4079        Is there a registered character entity class in :var:`self` with the
4080        Python name or codepoint :var:`name`?
4081        """
4082        if isinstance(name, (int, long)):
4083            has = name in self._charrefsbycodepoint
4084        else:
4085            has = name in self._charrefsbypyname
4086        return has or any(base.hascharref(name) for base in self.bases)
4087
4088    def hascharref_xml(self, name):
4089        """
4090        Is there a registered character entity class in :var:`self` with the XML
4091        name or codepoint :var:`name`?
4092        """
4093        if isinstance(name, (int, long)):
4094            has = name in self._charrefsbycodepoint
4095        else:
4096            has = name in self._charrefsbypyname
4097        return has or any(base.hascharref_xml(name) for base in self.bases)
4098
4099    def attrclass(self, name, xmlns):
4100        """
4101        Return the attribute class for the global attribute with the Python name
4102        :var:`name` and the namespace :var:`xmlns`. If the attribute can't
4103        be found a :exc:`IllegalAttrError` will be raised.
4104        """
4105        if not isinstance(xmlns, (list, tuple)):
4106            xmlns = (xmlns,)
4107        for xmlns in xmlns:
4108            xmlns = nsname(xmlns)
4109            try:
4110                return self._attrsbypyname[(name, xmlns)]
4111            except KeyError:
4112                pass
4113        for base in self.bases:
4114            try:
4115                return base.attrclass(name, xmlns)
4116            except IllegalAttrError:
4117                pass
4118        raise IllegalAttrError(name, xmlns, False)
4119
4120    def attrclass_xml(self, name, xmlns):
4121        """
4122        Return the attribute class for the global attribute with the XML name
4123        :var:`name` and the namespace :var:`xmlns`. If the attribute can't
4124        be found a :exc:`IllegalAttrError` will be raised.
4125        """
4126        if not isinstance(xmlns, (list, tuple)):
4127            xmlns = (xmlns,)
4128        for xmlns in xmlns:
4129            xmlns = nsname(xmlns)
4130            try:
4131                return self._attrsbyxmlname[(name, xmlns)]
4132            except KeyError:
4133                pass
4134        for base in self.bases:
4135            try:
4136                return base.attrclass_xml(name, xmlns)
4137            except IllegalAttrError:
4138                pass
4139        raise IllegalAttrError(name, xmlns, True)
4140
4141    def text(self, content):
4142        """
4143        Create a text node with the content :var:`content`.
4144        """
4145        return Text(content)
4146
4147    def comment(self, content):
4148        """
4149        Create a comment node with the content :var:`content`.
4150        """
4151        return Comment(content)
4152
4153    def __getattr__(self, key):
4154        try:
4155            return self._attrs[key]
4156        except KeyError:
4157            for base in self.bases:
4158                return getattr(base, key)
4159            raise AttributeError(key)
4160
4161
4162# Default pool (can be temporarily changed via ``with xsc.Pool() as pool:``)
4163class ThreadLocalPool(threading.local):
4164    pool = Pool()
4165
4166threadlocalpool = ThreadLocalPool()
4167
4168
4169###
4170### Functions for namespace handling
4171###
4172
4173def docpool():
4174    """
4175    Return a pool suitable for parsing XIST docstrings.
4176    """
4177    from ll.xist.ns import html, chars, abbr, doc, specials
4178    return Pool(doc, specials, html, chars, abbr)
4179
4180
4181def nsname(xmlns):
4182    """
4183    If :var:`xmlns` is a module, return ``xmlns.xmlns``, else return
4184    :var:`xmlns` unchanged.
4185    """
4186    if xmlns is not None and not isinstance(xmlns, basestring):
4187        xmlns = xmlns.xmlns
4188    return xmlns
4189
4190
4191def nsclark(xmlns):
4192    """
4193    Return a namespace name in Clark notation. :var:`xmlns` can be :const:`None`,
4194    a string or a module.
4195    """
4196    if xmlns is None:
4197        return "{}"
4198    elif not isinstance(xmlns, basestring):
4199        xmlns = xmlns.xmlns
4200    return "{{{}}}".format(xmlns)
4201
4202
4203# C0 Controls and Basic Latin
4204class quot(CharRef): "quotation mark = APL quote, U+0022 ISOnum"; codepoint = 34
4205class amp(CharRef): "ampersand, U+0026 ISOnum"; codepoint = 38
4206class lt(CharRef): "less-than sign, U+003C ISOnum"; codepoint = 60
4207class gt(CharRef): "greater-than sign, U+003E ISOnum"; codepoint = 62
4208class apos(CharRef): "apostrophe mark, U+0027 ISOnum"; codepoint = 39
4209
4210
4211###
4212### Location information
4213###
4214
4215class Location(object):
4216    """
4217    Represents a location in an XML entity.
4218    """
4219    __slots__ = ("url", "line", "col", "char")
4220
4221    def __init__(self, url=None, line=None, col=None):
4222        """
4223        Create a new :class:`Location` object using the arguments passed in.
4224        :var:`url` is the URL/filename. :var:`line` is the line number and
4225        :var:`col` is the column number (both starting at 0).
4226        """
4227        self.url = url
4228        self.line = line
4229        self.col = col
4230
4231    def offset(self, offset):
4232        """
4233        Return a location where the line number is incremented by offset
4234        (and the column number is reset to 0).
4235        """
4236        if offset==0:
4237            return self
4238        elif self.line is None:
4239            return Location(url=self.url, col=0)
4240        return Location(url=self.url, line=self.line+offset, col=0)
4241
4242    def __str__(self):
4243        url = str(self.url) if self.url is not None else "???"
4244        line = str(self.line) if self.line is not None else "?"
4245        col = str(self.col) if self.col is not None else "?"
4246        return "{}:{}:{}".format(url, line, col)
4247
4248    def __repr__(self):
4249        return "{0.__class__.__name__}({1})".format(self, ", ".join("{}={!r}".format(attr, getattr(self, attr)) for attr in ("url", "line", "col") if getattr(self, attr) is not None))
4250
4251    def __eq__(self, other):
4252        return self.__class__ is other.__class__ and self.url==other.url and self.line==other.line and self.col==other.col
4253
4254    def __ne__(self, other):
4255        return not self==other
4256
4257    def __xattrs__(self, mode="default"):
4258        return ("url", "line", "col")
4259
4260    def __xrepr__(self, mode="default"):
4261        for part in ipipe.xrepr(self.url, mode):
4262            yield part
4263        yield (astyle.style_default, ":")
4264        for part in ipipe.xrepr(self.line, mode):
4265            yield part
4266        yield (astyle.style_default, ":")
4267        for part in ipipe.xrepr(self.col, mode):
4268            yield part
Note: See TracBrowser for help on using the browser.