root/livinglogic.python.xist/src/ll/xist/xsc.py @ 4533:35aca9b50120

Revision 4533:35aca9b50120, 120.3 KB (checked in by Walter Doerwald <walter@…>, 8 years ago)

Replace ll.xist.xsc.AttrProcInst? with ll.xist.xsc.AttrElement?.

This makes it possible to have a useful output for the new element outside of
attributes.

Make ll.xist.ns.ul4.attr_if an AttrElement? subclass.

Remove ll.xist.ns.ul4.attr_ifnn.

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 Null(CharacterData):
2125    """
2126    node that does not contain anything.
2127    """
2128
2129    @classmethod
2130    def _str(cls, fullname=True, xml=True, decorate=True):
2131        s = cls._strbase(fullname=fullname, xml=xml)
2132        if decorate:
2133            s = "<{}>".format(s)
2134        return s
2135
2136    def convert(self, converter):
2137        return self
2138
2139    def publish(self, publisher):
2140        if False:
2141            yield ""
2142
2143    def present(self, presenter):
2144        return presenter.presentNull(self) # return a generator-iterator
2145
2146    def __unicode__(self):
2147        return u""
2148
2149    def __repr__(self):
2150        return "ll.xist.xsc.Null"
2151
2152
2153Null = Null() # Singleton, the Python way
2154
2155
2156class _Attr_Meta(Frag.__metaclass__):
2157    def __new__(cls, name, bases, dict):
2158        # can be overwritten in subclasses, to specify that this attributes is required
2159        if "required" in dict:
2160            dict["required"] = bool(dict["required"])
2161        # convert the default to a Frag
2162        if "default" in dict:
2163            dict["default"] = Frag(dict["default"])
2164        # convert the entries in values to unicode
2165        if "values" in dict:
2166            values = dict["values"]
2167            if values is not None:
2168                dict["values"] = tuple(unicode(entry) for entry in values)
2169        self = super(_Attr_Meta, cls).__new__(cls, name, bases, dict)
2170        if self.xmlns is not None:
2171            threadlocalpool.pool.register(self)
2172        return self
2173
2174    def __repr__(self):
2175        return "<attribute class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
2176
2177
2178class Attr(Frag):
2179    """
2180    Base class of all attribute classes.
2181
2182    The content of an attribute may be any other XIST node. This is different
2183    from a normal DOM, where only text and character references are allowed.
2184    The reason for this is to allow dynamic content (implemented as elements or
2185    processing instructions) to be put into attributes.
2186
2187    Of course, this dynamic content when finally converted to HTML should
2188    normally result in a fragment consisting only of text and character
2189    references. But note that it is allowed to have elements and processing
2190    instructions inside of attributes even when publishing. Processing
2191    instructions will be published as is and for elements their content will be
2192    published::
2193
2194        >>> from ll.xist.ns import html, php
2195        >>> node = html.img(
2196        ...    src=php.php("echo 'eggs.gif'"),
2197        ...    alt=html.abbr(
2198        ...       "EGGS",
2199        ...       title="Extensible Graphics Generation System",
2200        ...       lang="en"
2201        ...    )
2202        ... )
2203        >>> print node.bytes()
2204        <img alt="EGGS" src="<?php echo 'eggs.gif'?>" />
2205    """
2206    __metaclass__ = _Attr_Meta
2207    required = False
2208    default = None
2209    values = None
2210
2211    def isfancy(self):
2212        """
2213        Return whether :var:`self` contains nodes other than :class:`Text`.
2214        """
2215        for child in self:
2216            if not isinstance(child, Text):
2217                return True
2218        return False
2219
2220    @classmethod
2221    def _str(cls, fullname=True, xml=True, decorate=True):
2222        return cls._strbase(fullname=fullname, xml=xml)
2223
2224    def present(self, presenter):
2225        return presenter.presentAttr(self) # return a generator-iterator
2226
2227    def checkvalid(self):
2228        """
2229        Check whether :var:`self` has an allowed value, i.e. one that is specified
2230        in the class attribute ``values``. If the value is not allowed a warning
2231        will be issued through the Python warning framework.
2232
2233        If :var:`self` is "fancy" (i.e. contains non-:class:`Text` nodes), no
2234        check will be done.
2235        """
2236        values = self.__class__.values
2237        if self and isinstance(values, tuple) and not self.isfancy():
2238            value = unicode(self)
2239            if value not in values:
2240                warnings.warn(IllegalAttrValueWarning(self))
2241
2242    def _publishname(self, publisher):
2243        if self.xmlns is not None:
2244            prefix = publisher._ns2prefix.get(self.xmlns) if self.xmlns != xml_xmlns else u"xml"
2245            if prefix is not None:
2246                return u"{}:{}".format(prefix, self.xmlname)
2247        return self.xmlname
2248
2249    def _publishattrvalue(self, publisher):
2250        # Internal helper that is used to publish the attribute value
2251        # (can be overwritten in subclass (done by e.g. :class:`StyleAttr` and
2252        # :class:`URLAttr`)
2253        return Frag.publish(self, publisher)
2254
2255    def publish(self, publisher):
2256        if publisher.validate:
2257            self.checkvalid()
2258        if len(self)==1 and isinstance(self[0], AttrElement):
2259            for part in self[0].publishattr(publisher, self):
2260                yield part
2261        else:
2262            publisher.inattr += 1
2263            yield publisher.encode(u' {}="'.format(self._publishname(publisher)))
2264            publisher.pushtextfilter(misc.xmlescape_attr)
2265            for part in self._publishattrvalue(publisher):
2266                yield part
2267            publisher.poptextfilter()
2268            yield publisher.encode(u'"')
2269            publisher.inattr -= 1
2270
2271    def pretty(self, level=0, indent="\t"):
2272        return self.clone()
2273
2274    def __repr__(self):
2275        l = len(self)
2276        if l==0:
2277            info = u"no children"
2278        elif l==1:
2279            info = u"1 child"
2280        else:
2281            info = u"{} children".format(l)
2282        loc = " (from {})".format(self.startloc) if self.startloc is not None else ""
2283        return "<{0.__class__.__module__}.{0.__fullname__} attr object ({1}){2} at {3:#x}>".format(self, info, loc, id(self))
2284
2285
2286class TextAttr(Attr):
2287    """
2288    Attribute class that is used for normal text attributes.
2289    """
2290
2291
2292class IDAttr(Attr):
2293    """
2294    Attribute used for ids.
2295    """
2296
2297
2298class NumberAttr(Attr):
2299    """
2300    Attribute class that is used for when the attribute value may be any kind
2301    of number.
2302    """
2303
2304
2305class IntAttr(NumberAttr):
2306    """
2307    Attribute class that is used when the attribute value may be an integer.
2308    """
2309
2310
2311class FloatAttr(NumberAttr):
2312    """
2313    Attribute class that is used when the attribute value may be a
2314    floating point value.
2315    """
2316
2317
2318class BoolAttr(Attr):
2319    """
2320    Attribute class that is used for boolean attributes. When publishing
2321    the value will always be the attribute name, regardless of the real value.
2322    """
2323
2324    # We can't simply overwrite :meth:`_publishattrvalue`, because for ``xhtml==0`` we don't output a "proper" attribute
2325    def publish(self, publisher):
2326        if publisher.validate:
2327            self.checkvalid()
2328        if len(self)==1 and isinstance(self[0], AttrElement):
2329            for part in self[0].publishboolattr(publisher):
2330                yield part
2331        else:
2332            publisher.inattr += 1
2333            name = self._publishname(publisher)
2334            yield publisher.encode(u" {}".format(name))
2335            if publisher.xhtml>0:
2336                yield publisher.encode(u'="')
2337                publisher.pushtextfilter(misc.xmlescape)
2338                yield publisher.encode(name)
2339                publisher.poptextfilter()
2340                yield publisher.encode(u'"')
2341            publisher.inattr -= 1
2342
2343
2344class ColorAttr(Attr):
2345    """
2346    Attribute class that is used for a color attributes.
2347    """
2348
2349
2350class StyleAttr(Attr):
2351    """
2352    Attribute class that is used for CSS style attributes.
2353    """
2354
2355    def _transform(self, replacer):
2356        from ll.xist import css
2357        stylesheet = cssutils.parseString(u"a{{{}}}".format(self))
2358        css.replaceurls(stylesheet, replacer)
2359        return stylesheet.cssRules[0].style.getCssText(separator=" ")
2360
2361    def replaceurls(self, replacer):
2362        """
2363        Replace each URL in the style. Each URL will be passed to the callable
2364        :var:`replacer` and replaced with the returned value.
2365        """
2366        self[:] = self._transform(replacer)
2367
2368    def parsed(self, parser, event):
2369        if event == "leaveattrns" and not self.isfancy() and parser.base is not None:
2370            from ll.xist import css
2371            def prependbase(u):
2372                return parser.base/u
2373            self.replaceurls(prependbase)
2374
2375    def _publishattrvalue(self, publisher):
2376        if not self.isfancy() and publisher.base is not None:
2377            from ll.xist import css
2378            def reltobase(u):
2379                return u.relative(publisher.base)
2380            for part in Frag(self._transform(reltobase)).publish(publisher):
2381                yield part
2382        else:
2383            for part in super(StyleAttr, self)._publishattrvalue(publisher):
2384                yield part
2385
2386    def urls(self, base=None):
2387        """
2388        Return a list of all the URLs (as :class:`URL` objects) found in the style
2389        attribute.
2390        """
2391        from ll.xist import css
2392        urls = []
2393        def collect(u):
2394            urls.append(u)
2395            return u
2396        s = cssutils.parseString(u"a{{{}}}".format(self))
2397        css.replaceurls(s, collect)
2398        return urls
2399
2400
2401class URLAttr(Attr):
2402    """
2403    Attribute class that is used for URLs. See the module :mod:`ll.url` for more
2404    information about URL handling.
2405    """
2406
2407    def parsed(self, parser, event):
2408        if event == "leaveattrns" and not self.isfancy() and parser.base is not None:
2409            self[:] = (url_.URL(parser.base/unicode(self)),)
2410
2411    def _publishattrvalue(self, publisher):
2412        if self.isfancy():
2413            return Attr._publishattrvalue(self, publisher)
2414        else:
2415            new = Attr(url_.URL(unicode(self)).relative(publisher.base))
2416            return new._publishattrvalue(publisher)
2417
2418    def asURL(self):
2419        """
2420        Return :var:`self` as a :class:`URL` object (note that non-:class:`Text`
2421        content will be filtered out).
2422        """
2423        return url_.URL(Attr.__unicode__(self))
2424
2425    def forInput(self, root=None):
2426        """
2427        return a :class:`URL` pointing to the real location of the referenced
2428        resource. :var:`root` must be the root URL relative to which :var:`self`
2429        will be interpreted and usually comes from the ``root`` attribute of the
2430        :var:`converter` argument in :meth:`convert`.
2431        """
2432        u = self.asURL()
2433        if u.scheme == "root":
2434            u.scheme = None
2435        u = url_.URL(root)/u
2436        return u
2437
2438    def imagesize(self, root=None):
2439        """
2440        Return the size of an image as a tuple.
2441        """
2442        return self.openread(root).imagesize
2443
2444    def contentlength(self, root=None):
2445        """
2446        Return the size of a file in bytes.
2447        """
2448        return self.openread(root).contentlength
2449
2450    def lastmodified(self, root=None):
2451        """
2452        returns the timestamp for the last modification to the file
2453        """
2454        return self.openread(root).lastmodified
2455
2456    def openread(self, root=None):
2457        """
2458        Return a :class:`Resource` for reading from the URL.
2459        """
2460        return self.forInput(root).openread()
2461
2462    def openwrite(self, root=None):
2463        """
2464        Return a :class:`Resource` for writing to the URL.
2465        """
2466        return self.forInput(root).openwrite()
2467
2468
2469class _Attrs_Meta(Node.__metaclass__):
2470    def __new__(cls, name, bases, dict):
2471        self = super(_Attrs_Meta, cls).__new__(cls, name, bases, dict)
2472        self._byxmlname = weakref.WeakValueDictionary() # map XML name to attribute class
2473        self._bypyname = weakref.WeakValueDictionary() # map Python name to attribute class
2474        self._defaultattrsxml = weakref.WeakValueDictionary() # map XML name to attribute class with default value
2475        self._defaultattrspy = weakref.WeakValueDictionary() # map Python name to attribute class with default value
2476
2477        # go through the attributes and register them in the cache
2478        for key in dir(self):
2479            value = getattr(self, key)
2480            if isinstance(value, _Attr_Meta):
2481                self.add(value)
2482        return self
2483
2484    def __repr__(self):
2485        return "<attrs class {0.__module__}:{0.__fullname__} with {1} attrs at {2:#x}>".format(self, len(self._bypyname), id(self))
2486
2487    def __contains__(self, key):
2488        if isinstance(key, basestring):
2489            return key in self._bypyname
2490        if key.xmlns is not None:
2491            return True
2492        return self._bypyname.get(key.__name__, None) is key
2493
2494
2495class Attrs(Node, dict):
2496    """
2497    An attribute map. Allowed entries are specified through nested subclasses
2498    of :class:`Attr`.
2499    """
2500    __metaclass__ = _Attrs_Meta
2501
2502    def __init__(self, _content=None, **attrs):
2503        dict.__init__(self)
2504        # set default attribute values
2505        for (key, value) in self._defaultattrspy.iteritems():
2506            self[key] = value.default.clone()
2507        # set attributes, this might overwrite (or delete) default attributes
2508        self.update(_content, **attrs)
2509
2510    def __eq__(self, other):
2511        return self.__class__ is other.__class__ and dict.__eq__(self, other)
2512
2513    @classmethod
2514    def _str(cls, fullname=True, xml=True, decorate=True):
2515        return cls._strbase(fullname=fullname, xml=xml)
2516
2517    @classmethod
2518    def add(cls, value):
2519        cls._byxmlname[value.xmlname] = value
2520        cls._bypyname[value.__name__] = value
2521        if value.default:
2522            cls._defaultattrsxml[value.xmlname] = value
2523            cls._defaultattrspy[value.__name__] = value
2524        # fix classname (but don't patch inherited attributes)
2525        if "." not in value.__fullname__:
2526            value.__fullname__ = "{}.{}".format(cls.__fullname__, value.__fullname__)
2527
2528    def _create(self):
2529        node = self.__class__() # "virtual" constructor
2530        node.clear()
2531        return node
2532
2533    def clone(self):
2534        node = self._create()
2535        for value in dict.values(self):
2536            dict.__setitem__(node, value.__class__, value.clone())
2537        return self._decoratenode(node)
2538
2539    def __copy__(self):
2540        node = self._create()
2541        for value in dict.values(self):
2542            dict.__setitem__(node, value.__class__, value)
2543        return self._decoratenode(node)
2544
2545    def __deepcopy__(self, memo=None):
2546        node = self._create()
2547        if memo is None:
2548            memo = {}
2549        memo[id(self)] = node
2550        for value in dict.values(self):
2551            dict.__setitem__(node, value.__class__, copy.deepcopy(value, memo))
2552        return self._decoratenode(node)
2553
2554    def convert(self, converter):
2555        node = self._create()
2556        for value in self.values():
2557            newvalue = value.convert(converter)
2558            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)
2559            node[value.__class__] = newvalue
2560        return node
2561
2562    def compact(self):
2563        node = self._create()
2564        for value in self.values():
2565            newvalue = value.compact()
2566            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)
2567            node[value.__class__] = newvalue
2568        return node
2569
2570    def normalized(self):
2571        node = self._create()
2572        for value in self.values():
2573            newvalue = value.normalized()
2574            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)
2575            node[value.__class__] = newvalue
2576        return node
2577
2578    def present(self, presenter):
2579        return presenter.presentAttrs(self) # return a generator-iterator
2580
2581    def checkvalid(self):
2582        # collect required attributes
2583        attrs = {value for value in self.allowedattrs() if value.required}
2584        # Check each existing attribute and remove it from the list of required ones
2585        for value in self.values():
2586            value.checkvalid()
2587            try:
2588                attrs.remove(value.__class__)
2589            except KeyError:
2590                pass
2591        # are there any required attributes remaining that haven't been specified? => warn about it
2592        if attrs:
2593            warnings.warn(RequiredAttrMissingWarning(self, list(attrs)))
2594
2595    def publish(self, publisher):
2596        if publisher.validate:
2597            self.checkvalid()
2598        for value in self.values():
2599            for part in value.publish(publisher):
2600                yield part
2601
2602    def __unicode__(self):
2603        return u""
2604
2605    @classmethod
2606    def isallowed(cls, name):
2607        if isinstance(name, basestring):
2608            return name in cls._bypyname
2609        if name.xmlns is not None:
2610            return True
2611        try:
2612            candidate = cls._bypyname[name.__name__]
2613        except KeyError:
2614            return False
2615        # make sure that both Python name and XML name match
2616        return candidate.xmlname == name.xmlname
2617
2618    @classmethod
2619    def isallowed_xml(cls, name, xmlns=None):
2620        if isinstance(name, basestring):
2621            return name in cls._byxmlname
2622        if name.xmlns is not None:
2623            return True
2624        try:
2625            candidate = cls._bypyname[name.__name__]
2626        except KeyError:
2627            return False
2628        # make sure that both Python name and XML name match
2629        return candidate.xmlname == name.xmlname
2630
2631    def __getattribute__(self, name):
2632        sup = super(Attrs, self)
2633        if name in sup.__getattribute__("_bypyname"): # avoid recursion
2634            return self.__getitem__(name)
2635        else:
2636            return sup.__getattribute__(name)
2637
2638    def __setattr__(self, name, value):
2639        sup = super(Attrs, self)
2640        if name in sup.__getattribute__("_bypyname"): # avoid recursion
2641            return self.__setitem__(name, value)
2642        else:
2643            return sup.__setattr__(name, value)
2644
2645    def __delattr__(self, name):
2646        sup = super(Attrs, self)
2647        if name in sup.__getattribute__("_bypyname"): # avoid recursion
2648            return self.__detitem__(name)
2649        else:
2650            return sup.__delattr__(name)
2651
2652    def __getitem__(self, name):
2653        if isinstance(name, list):
2654            node = self
2655            for subname in name:
2656                node = node[subname]
2657            return node
2658        return self.attr(name)
2659
2660    def __setitem__(self, name, value):
2661        if isinstance(name, list):
2662            if not name:
2663                raise ValueError("can't replace self")
2664            node = self
2665            for subname in name[:-1]:
2666                node = node[subname]
2667            node[name[-1]] = value
2668        return self.set(name, value)
2669
2670    def __delitem__(self, name):
2671        if isinstance(name, list):
2672            if not name:
2673                raise ValueError("can't delete self")
2674            node = self
2675            for subname in name[:-1]:
2676                node = node[subname]
2677            del node[name[-1]]
2678        dict.__delitem__(self, self.allowedattr(name))
2679
2680    def has(self, name):
2681        """
2682        Return whether :var:`self` has an attribute with a Python name :var:`name`.
2683        :var:`name` may also be an attribute class (either from ``self.Attrs``
2684        or a global attribute).
2685        """
2686        try:
2687            attr = dict.__getitem__(self, self.allowedattr(name))
2688        except KeyError:
2689            return False
2690        return len(attr)>0
2691
2692    def has_xml(self, name):
2693        """
2694        Similar to :meth:`has`, but :var:`name` is treated as the XML name
2695        instead of the Python name.
2696        """
2697        try:
2698            attr = dict.__getitem__(self, self.allowedattr_xml(name))
2699        except KeyError:
2700            return False
2701        return len(attr)>0
2702
2703    def __contains__(self, name):
2704        return self.has(name)
2705
2706    def get(self, name, default=None):
2707        """
2708        works like the dictionary method :meth:`get`, it returns the attribute
2709        with the Python name :var:`name`, or :var:`default` if :var:`self` has no
2710        such attribute. :var:`name` may also be an attribute class (either from
2711        ``self.Attrs`` or a global attribute).
2712        """
2713        attr = self.attr(name)
2714        if not attr:
2715            attr = self.allowedattr(name)(default) # pack the attribute into an attribute object
2716        return attr
2717
2718    def get_xml(self, name, default=None):
2719        """
2720        Similar to :meth:`get`, but :var:`name` is treated as the XML name
2721        instead of the Python name.
2722        """
2723        attr = self.attr_xml(name)
2724        if not attr:
2725            attr = self.allowedattr_xml(name)(default) # pack the attribute into an attribute object
2726        return attr
2727
2728    def set(self, name, value):
2729        """
2730        Set the attribute with the Python :var:`name` to the value :var:`value`.
2731        :var:`name` may be a string or an attribute class. The newly set attribute
2732        will be returned.
2733        """
2734        attr = self.allowedattr(name)
2735        value = attr(value)
2736        dict.__setitem__(self, attr, value) # put the attribute in our dict
2737        return value
2738
2739    def set_xml(self, name, value):
2740        """
2741        Similar to :meth:`set`, but :var:`name` is treated as the XML name
2742        instead of the Python name.
2743        """
2744        attr = self.allowedattr_xml(name)
2745        value = attr(value)
2746        dict.__setitem__(self, attr, value) # put the attribute in our dict
2747        return value
2748
2749    def setdefault(self, name, default):
2750        """
2751        Works like the dictionary method :meth:`setdefault`, it returns the
2752        attribute with the Python name :var:`name`. If :var:`self` has no such
2753        attribute, it will be set to :var:`default` and :var:`default` will be
2754        returned as the new attribute value.
2755        """
2756        value = self.attr(name)
2757        if not value:
2758            attr = self.allowedattr(name)
2759            value = attr(default) # pack the attribute into an attribute object
2760            dict.__setitem__(self, attr, value)
2761        return value
2762
2763    def setdefault_xml(self, name, default):
2764        """
2765        Similar to :meth:`setdefault`, but :var:`name` is treated as the XML name
2766        instead of the Python name.
2767        """
2768        value = self.attr_xml(name)
2769        if not value:
2770            attr = self.allowedattr(name)
2771            value = attr(default) # pack the attribute into an attribute object
2772            dict.__setitem__(self, attr, value)
2773        return value
2774
2775    def update(self, *args, **kwargs):
2776        """
2777        Copies attributes over from all mappings in :var:`args` and from
2778        :var:`kwargs`.
2779        """
2780        for mapping in args + (kwargs,):
2781            if mapping is not None:
2782                if isinstance(mapping, Attrs):
2783                    # This makes sure that global attributes are copied properly
2784                    for value in mapping._iterallvalues():
2785                        self[value.__class__] = value
2786                else:
2787                    for (attrname, attrvalue) in mapping.iteritems():
2788                        self[attrname] = attrvalue
2789
2790    @classmethod
2791    def allowedattrs(cls):
2792        """
2793        Return an iterator over all allowed attribute classes.
2794        """
2795        return cls._bypyname.itervalues()
2796
2797    @classmethod
2798    def allowedattr(cls, name):
2799        if isinstance(name, basestring):
2800            try:
2801                return cls._bypyname[name]
2802            except KeyError:
2803                raise IllegalAttrError(name, cls, False)
2804        # name is an attribute class
2805        if name.xmlns is not None: # if the attribute class is for a global attribute we accept it
2806            return name
2807        try:
2808            candidate = cls._bypyname[name.__name__]
2809        except KeyError:
2810            raise IllegalAttrError(name, cls, False)
2811        else:
2812            # make sure that both Python name and XML name match
2813            if candidate.xmlname == name.xmlname:
2814                return candidate
2815            raise IllegalAttrError(name, cls, False)
2816
2817    @classmethod
2818    def allowedattr_xml(cls, name):
2819        if isinstance(name, basestring):
2820            try:
2821                return cls._byxmlname[name]
2822            except KeyError:
2823                raise IllegalAttrError(name, cls, True)
2824        # name is an attribute class
2825        if name.xmlns is not None: # if the attribute class is for a global attribute we accept it
2826            return name
2827        try:
2828            candidate = cls._bypyname[name.__name__]
2829        except KeyError:
2830            raise IllegalAttrError(name, cls, False)
2831        else:
2832            # make sure that both Python name and XML name match
2833            if candidate.xmlname == name.xmlname:
2834                return candidate
2835            raise IllegalAttrError(name, cls, True)
2836
2837    def __len__(self):
2838        return misc.count(self.values())
2839
2840    def keys(self):
2841        for value in dict.itervalues(self):
2842            if value:
2843                yield value.__class__
2844
2845    iterkeys = __iter__ = keys
2846
2847    def values(self):
2848        for value in dict.itervalues(self):
2849            if value:
2850                yield value
2851
2852    itervalues = values
2853
2854    def items(self):
2855        for value in dict.itervalues(self):
2856            if value:
2857                yield (value.__class__, value)
2858
2859    iteritems = items
2860
2861    def _iterallvalues(self):
2862        """
2863        Iterate through all values, even the unset ones.
2864        """
2865        return dict.itervalues(self)
2866
2867    def attr(self, name):
2868        attr = self.allowedattr(name)
2869        try:
2870            value = dict.__getitem__(self, attr)
2871        except KeyError: # if the attribute is not there generate a new empty one
2872            value = attr()
2873            dict.__setitem__(self, attr, value)
2874        return value
2875
2876    def attr_xml(self, name):
2877        attr = self.allowedattr_xml(name)
2878        try:
2879            value = dict.__getitem__(self, attr)
2880        except KeyError: # if the attribute is not there generate a new empty one
2881            value = attr()
2882            dict.__setitem__(self, attr, value)
2883        return value
2884
2885    def filtered(self, function):
2886        """
2887        Return a filtered version of :var:`self`.
2888        """
2889        node = self._create()
2890        for (name, value) in self.items():
2891            if function(value):
2892                node[name] = value
2893        return node
2894
2895    def _fixnames(self, names):
2896        newnames = []
2897        for name in names:
2898            if isinstance(name, basestring):
2899                try:
2900                    name = self.allowedattr(name)
2901                except IllegalAttrError:
2902                    continue
2903            newnames.append(name)
2904        return tuple(newnames)
2905
2906    def _fixnames_xml(self, names):
2907        newnames = []
2908        for name in names:
2909            if isinstance(name, basestring):
2910                try:
2911                    name = self.allowedattr_xml(name)
2912                except IllegalAttrError:
2913                    continue
2914            newnames.append(name)
2915        return tuple(newnames)
2916
2917    def withnames(self, *names):
2918        """
2919        Return a copy of :var:`self` where only the attributes with Python names
2920        in :var:`names` are kept, all others are removed.
2921        """
2922        def isok(node):
2923            return isinstance(node, names)
2924
2925        names = self._fixnames(names)
2926        return self.filtered(isok)
2927
2928    def withnames_xml(self, *names):
2929        """
2930        Return a copy of :var:`self` where only the attributes with XML names
2931        in :var:`names` are kept, all others are removed.
2932        """
2933        def isok(node):
2934            return isinstance(node, names)
2935
2936        names = self._fixnames_xml(names)
2937        return self.filtered(isok)
2938
2939    def withoutnames(self, *names):
2940        """
2941        Return a copy of :var:`self` where all the attributes with Python names
2942        in :var:`names` are removed.
2943        """
2944        def isok(node):
2945            return not isinstance(node, names)
2946
2947        names = self._fixnames(names)
2948        return self.filtered(isok)
2949
2950    def withoutnames_xml(self, *names):
2951        """
2952        Return a copy of :var:`self` where all the attributes with XML names
2953        in :var:`names` are removed.
2954        """
2955        def isok(node):
2956            return not isinstance(node, names)
2957
2958        names = self._fixnames_xml(names)
2959        return self.filtered(isok)
2960
2961    def __repr__(self):
2962        l = len(self)
2963        if l==0:
2964            info = "(no attrs)"
2965        elif l==1:
2966            info = "(1 attr)"
2967        else:
2968            info = "({} attrs)".format(l)
2969        if self.startloc is not None:
2970            loc = " (from {})".format(self.startloc)
2971        else:
2972            loc = ""
2973        return "<{0.__class__.__module__}.{0.__fullname__} attrs {1}{2} at {3:#x}>".format(self, info, loc, id(self))
2974
2975
2976def _patchclassnames(dict, name):
2977    # If an :class:`Attrs` class has been provided patch up its class names
2978    try:
2979        attrs = dict["Attrs"]
2980    except KeyError:
2981        pass
2982    else:
2983        attrs.__fullname__ = "{}.Attrs".format(name)
2984        for (key, value) in attrs.__dict__.iteritems():
2985            if isinstance(value, _Attr_Meta):
2986                value.__fullname__ = "{}.{}".format(name, value.__fullname__)
2987
2988    # If a Context has been provided patch up its class names
2989    try:
2990        context = dict["Context"]
2991    except KeyError:
2992        pass
2993    else:
2994        context.__fullname__ = "{}.{}".format(name, context.__fullname__)
2995
2996
2997class _Element_Meta(Node.__metaclass__):
2998    def __new__(cls, name, bases, dict):
2999        if "model" in dict and isinstance(dict["model"], bool):
3000            from ll.xist import sims
3001            dict["model"] = sims.Any() if dict["model"] else sims.Empty()
3002        _patchclassnames(dict, name)
3003        self = super(_Element_Meta, cls).__new__(cls, name, bases, dict)
3004        if dict.get("register") is not None:
3005            threadlocalpool.pool.register(self)
3006        return self
3007
3008    def __repr__(self):
3009        return "<element class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
3010
3011
3012class Element(Node):
3013    """
3014    This class represents XML/XIST elements. All elements implemented by the
3015    user must be derived from this class.
3016
3017    Elements support the following class variables:
3018
3019    :attr:`model` : object with :meth:`checkvalid` method
3020        This is an object that is used for validating the content of the element.
3021        See the module :mod:`ll.xist.sims` for more info. If :attr:`model` is
3022        :const:`None` validation will be skipped, otherwise it will be performed
3023        when parsing or publishing.
3024
3025    :attr:`Attrs` : :class:`Element.Attrs` subclass
3026        This is a class derived from :class:`Element.Attrs` and must define all
3027        attributes as classes nested inside this :class:`Attrs` class.
3028
3029    :attr:`xmlns` : string
3030        This is the name of the namespace this element belong to.
3031
3032    :attr:`register` : bool
3033        If :attr:`register` is false the element will never be registered in a
3034        :class:`Pool`. The default is :const:`True`.
3035
3036    :attr:`xmlname` : string
3037        If the class name has to be different from the XML name (e.g. because the
3038        XML name is not a valid Python identifier) :attr:`xmlname` can be used to
3039        specify the real XML name. Otherwise the XML name will be the Python name.
3040    """
3041    __metaclass__ = _Element_Meta
3042
3043    model = None
3044    register = None
3045
3046    Attrs = Attrs
3047
3048    def __init__(self, *content, **attrs):
3049        """
3050        Create a new :class:`Element` instance.
3051
3052        Positional arguments are treated as content nodes. Keyword arguments and
3053        dictionaries are treated as attributes.
3054        """
3055        self.attrs = self.Attrs()
3056        newcontent = []
3057        for child in content:
3058            if isinstance(child, dict):
3059                self.attrs.update(child)
3060            else:
3061                newcontent.append(child)
3062        self.content = Frag(*newcontent)
3063        self.attrs.update(attrs)
3064
3065    def __getstate__(self):
3066        attrs = {}
3067        for (key, value) in self.attrs.iteritems():
3068            if key.xmlns is None:
3069                key = key.__name__
3070            else:
3071                key = (key.__module__, key.__fullname__)
3072            attrs[key] = Frag(value)
3073        return (self.content, attrs)
3074
3075    def __setstate__(self, (content, attrs)):
3076        self.content = content
3077        self.attrs = self.Attrs()
3078        for (key, value) in attrs.iteritems():
3079            if not isinstance(key, basestring):
3080                obj = __import__(key[0])
3081                for name in key[0].split(".")[1:]:
3082                    obj = getattr(obj, name)
3083                for name in key[1].split("."):
3084                    obj = getattr(obj, name)
3085                key = obj
3086            self.attrs[key] = value
3087
3088    def __enter__(self):
3089        """
3090        :class:`Element` nodes can be used in ``with`` blocks to build XIST trees.
3091        Inside a ``with`` block ``+`` and :func:`add` can be used to append node
3092        to the currently active element in the ``with`` block::
3093
3094            with xsc.build():
3095                with html.ul() as node:
3096                    +html.li("I hear and I forget.")
3097                    +html.li("I see and I believe.")
3098                    +html.li("I do and I understand.")
3099                    xsc.add(class_="quote")
3100            print node.bytes()
3101        """
3102        threadlocalnodehandler.handler.enter(self)
3103        return self
3104
3105    def __exit__(self, type, value, traceback):
3106        threadlocalnodehandler.handler.exit()
3107
3108    def __call__(self, *content, **attrs):
3109        """
3110        Calling an element add items in :var:`content` to the element content
3111        and set attributes from :var:`attrs`. The element itself will be returned.
3112        """
3113        for child in content:
3114            if isinstance(child, dict):
3115                self.attrs.update(child)
3116            else:
3117                self.content.append(child)
3118        for (attrname, attrvalue) in attrs.iteritems():
3119            self.attrs[attrname] = attrvalue
3120        return self
3121
3122    def __eq__(self, other):
3123        return self.__class__ is other.__class__ and self.content==other.content and self.attrs==other.attrs
3124
3125    @classmethod
3126    def _str(cls, fullname=True, xml=True, decorate=True):
3127        s = cls._strbase(fullname=fullname, xml=xml)
3128        if decorate:
3129            if cls.model is not None and cls.model.empty:
3130                s = "<{}/>".format(s)
3131            else:
3132                s = "<{}>".format(s)
3133        return s
3134
3135    def checkvalid(self):
3136        if self.model is not None:
3137            self.model.checkvalid(self)
3138        self.attrs.checkvalid()
3139
3140    def append(self, *items):
3141        """
3142        Append every item in :var:`items` to the elements content.
3143        """
3144        self.content.append(*items)
3145
3146    def extend(self, items):
3147        """
3148        Append all items in :var:`items` to the elements content.
3149        """
3150        self.content.extend(items)
3151
3152    def insert(self, index, *items):
3153        """
3154        Insert every item in :var:`items` at the position :var:`index`.
3155        """
3156        self.content.insert(index, *items)
3157
3158    def convert(self, converter):
3159        node = self.__class__() # "virtual" constructor
3160        node.content = self.content.convert(converter)
3161        node.attrs = self.attrs.convert(converter)
3162        return self._decoratenode(node)
3163
3164    def clone(self):
3165        node = self.__class__() # "virtual" constructor
3166        node.content = self.content.clone() # this is faster than passing it in the constructor (no :func:`tonode` call)
3167        node.attrs = self.attrs.clone()
3168        return self._decoratenode(node)
3169
3170    def __copy__(self):
3171        node = self.__class__()
3172        node.content = copy.copy(self.content)
3173        node.attrs = copy.copy(self.attrs)
3174        return self._decoratenode(node)
3175
3176    def __deepcopy__(self, memo=None):
3177        node = self.__class__()
3178        if memo is None:
3179            memo = {}
3180        memo[id(self)] = node
3181        node.content = copy.deepcopy(self.content, memo)
3182        node.attrs = copy.deepcopy(self.attrs, memo)
3183        return self._decoratenode(node)
3184
3185    def __unicode__(self):
3186        return unicode(self.content)
3187
3188    def _addimagesizeattributes(self, url, widthattr=None, heightattr=None):
3189        """
3190        Automatically set image width and height attributes.
3191
3192        The size of the image with the URL :var:`url` will be determined and the
3193        width of the image will be put into the attribute with the name
3194        :var:`widthattr` if :var:`widthattr` is not :const:`None` and the
3195        attribute is not set already. The same will happen for the height, which
3196        will be put into the attribute named :var:`heighattr`.
3197        """
3198        try:
3199            size = url.imagesize()
3200        except IOError, exc:
3201            warnings.warn(FileNotFoundWarning("can't read image", url, exc))
3202        else:
3203            for attr in (heightattr, widthattr):
3204                if attr is not None: # do something to the width/height
3205                    if not self.attrs.has(attr):
3206                        self[attr] = size[attr==heightattr]
3207
3208    def present(self, presenter):
3209        return presenter.presentElement(self) # return a generator-iterator
3210
3211    def _publishname(self, publisher):
3212        if self.xmlns is not None:
3213            prefix = publisher._ns2prefix.get(self.xmlns)
3214            if prefix is not None:
3215                return u"{}:{}".format(prefix, self.xmlname)
3216        return self.xmlname
3217
3218    def _publishfull(self, publisher):
3219        """
3220        Does the full publication of the element. If you need full elements
3221        inside attributes (e.g. for JSP tag libraries), you can overwrite
3222        :meth:`publish` and simply call this method.
3223        """
3224        name = self._publishname(publisher)
3225        yield publisher.encode(u"<")
3226        yield publisher.encode(name)
3227        # we're the first element to be published, so we have to create the xmlns attributes
3228        if publisher._publishxmlns:
3229            for (xmlns, prefix) in publisher._ns2prefix.iteritems():
3230                if xmlns not in publisher.hidexmlns:
3231                    yield publisher.encode(u" xmlns")
3232                    if prefix is not None:
3233                        yield publisher.encode(u":")
3234                        yield publisher.encode(prefix)
3235                    yield publisher.encode(u'="')
3236                    yield publisher.encode(xmlns)
3237                    yield publisher.encode('"')
3238            # reset the note, so the next element won't create the attributes again
3239            publisher._publishxmlns = False
3240        for part in self.attrs.publish(publisher):
3241            yield part
3242        if len(self):
3243            yield publisher.encode(u">")
3244            for part in self.content.publish(publisher):
3245                yield part
3246            yield publisher.encode(u"</")
3247            yield publisher.encode(name)
3248            yield publisher.encode(u">")
3249        else:
3250            if publisher.xhtml in (0, 1):
3251                if self.model is not None and self.model.empty:
3252                    if publisher.xhtml==1:
3253                        yield publisher.encode(u" /")
3254                    yield publisher.encode(u">")
3255                else:
3256                    yield publisher.encode(u"></")
3257                    yield publisher.encode(name)
3258                    yield publisher.encode(u">")
3259            elif publisher.xhtml == 2:
3260                yield publisher.encode(u"/>")
3261
3262    def publish(self, publisher):
3263        if publisher.validate:
3264            self.checkvalid()
3265        if publisher.inattr:
3266            # publish the content only when we are inside an attribute. This works much like using the plain string value,
3267            # but even works with processing instructions, or what the abbreviation entities return
3268            return self.content.publish(publisher) # return a generator-iterator
3269        else:
3270            return self._publishfull(publisher) # return a generator-iterator
3271
3272    def __getitem__(self, index):
3273        """
3274        If :var:`index` is a string, return the attribute with this (Python)
3275        name. If :var:`index` is an attribute class, return the attribute
3276        that is an instance of this class. If :var:`index` is a number or slice
3277        return the appropriate content node. :var:`index` may also be a list, in
3278        with case :meth:`__getitem__` will be applied recusively.
3279        :meth:`__getitem__` also supports walk filters.
3280
3281        """
3282        if isinstance(index, (basestring, _Attr_Meta)):
3283            return self.attrs[index]
3284        elif isinstance(index, (list, int, long, slice)):
3285            return self.content[index]
3286        else:
3287            from ll.xist import xfind
3288            def iterate(matcher):
3289                path = [self, None]
3290                for child in self:
3291                    path[-1] = child
3292                    if matcher(path):
3293                        yield child
3294            return misc.Iterator(iterate(xfind.makewalkfilter(index).matchpath))
3295
3296    def __setitem__(self, index, value):
3297        """
3298        Set an attribute or content node to the value :var:`value`. For possible
3299        types for :var:`index` see :meth:`__getitem__`.
3300        """
3301        if isinstance(index, (basestring, _Attr_Meta)):
3302            self.attrs[index] = value
3303        elif isinstance(index, (list, int, long, slice)):
3304            self.content[index] = value
3305        else:
3306            from ll.xist import xfind
3307            matcher = xfind.makewalkfilter(index).matchpath
3308            value = Frag(value)
3309            newcontent = []
3310            path = [self, None]
3311            for child in self:
3312                path[-1] = child
3313                if matcher(path):
3314                    newcontent.extend(value)
3315                else:
3316                    newcontent.append(child)
3317            self.content[:] = newcontent
3318
3319    def __delitem__(self, index):
3320        """
3321        Remove an attribute or content node. For possible types for :var:`index`
3322        see :meth:`__getitem__`.
3323        """
3324        if isinstance(index, (basestring, _Attr_Meta)):
3325            del self.attrs[index]
3326        elif isinstance(index, (list, int, long, slice)):
3327            del self.content[index]
3328        else:
3329            from ll.xist import xfind
3330            matcher = xfind.makewalkfilter(index).matchpath
3331            self.content = Frag(child for child in self if not matcher([self, child]))
3332
3333    def __getslice__(self, index1, index2):
3334        """
3335        Return a copy of the element that contains a slice of the content.
3336        """
3337        return self.content[index1:index2]
3338
3339    def __setslice__(self, index1, index2, sequence):
3340        """
3341        Replace a slice of the content of the element.
3342        """
3343        self.content[index1:index2] = sequence
3344
3345    def __delslice__(self, index1, index2):
3346        """
3347        Remove a slice of the content of the element.
3348        """
3349        del self.content[index1:index2]
3350
3351    def __iadd__(self, other):
3352        self.extend(other)
3353        return self
3354
3355    def __len__(self):
3356        """
3357        Return the number of children.
3358        """
3359        return len(self.content)
3360
3361    def __iter__(self):
3362        return iter(self.content)
3363
3364    def compact(self):
3365        node = self.__class__()
3366        node.content = self.content.compact()
3367        node.attrs = self.attrs.compact()
3368        return self._decoratenode(node)
3369
3370    def withsep(self, separator, clone=False):
3371        """
3372        Return a version of :var:`self` with a separator node between the child
3373        nodes of :var:`self`. For more info see :meth:`Frag.withsep`.
3374        """
3375        node = self.__class__()
3376        node.attrs = self.attrs.clone()
3377        node.content = self.content.withsep(separator, clone)
3378        return node
3379
3380    def sorted(self, cmp=None, key=None, reverse=False):
3381        """
3382        Return a sorted version of :var:`self`. :var:`compare` is a comparison
3383        function. The arguments :var:`cmp`, :var:`key` and :var:`reverse` have
3384        the same meaning as fot the builtin :func:`sorted` function.
3385        """
3386        node = self.__class__()
3387        node.attrs = self.attrs.clone()
3388        node.content = self.content.sorted(cmp, key, reverse)
3389        return node
3390
3391    def reversed(self):
3392        """
3393        Return a reversed version of :var:`self`.
3394        """
3395        node = self.__class__()
3396        node.attrs = self.attrs.clone()
3397        node.content = self.content.reversed()
3398        return node
3399
3400    def filtered(self, function):
3401        """
3402        Return a filtered version of the :var:`self`.
3403        """
3404        node = self.__class__()
3405        node.attrs = self.attrs.clone()
3406        node.content = self.content.filtered(function)
3407        return node
3408
3409    def shuffled(self):
3410        """
3411        Return a shuffled version of the :var:`self`.
3412        """
3413        node = self.__class__()
3414        node.attrs = self.attrs.clone()
3415        node.content = self.content.shuffled()
3416        return node
3417
3418    def mapped(self, function, converter=None, **converterargs):
3419        if converter is None:
3420            converter = Converter(**converterargs)
3421        node = function(self, converter)
3422        assert isinstance(node, Node), "the mapped method returned the illegal object {!r} (type {!r}) when mapping {!r}".format(node, type(node), self)
3423        if node is self:
3424            node = self.__class__(self.content.mapped(function, converter))
3425            node.attrs = self.attrs.clone()
3426        return node
3427
3428    def normalized(self):
3429        node = self.__class__()
3430        node.attrs = self.attrs.normalized()
3431        node.content = self.content.normalized()
3432        return node
3433
3434    def pretty(self, level=0, indent="\t"):
3435        orglevel = level # Remember the original indent level, so that any misconfiguration inside the element doesn't mess with the indentation
3436        node = self.__class__(self.attrs)
3437        if len(self):
3438            # search for text content
3439            for child in self:
3440                if isinstance(child, Text):
3441                    # leave content alone
3442                    node.append(self.content.clone())
3443                    break
3444            else:
3445                level += 1
3446                for child in self:
3447                    level += child.prettyindentbefore
3448                    node.append("\n", child.pretty(level, indent))
3449                    level += child.prettyindentafter
3450                node.append("\n", indent*orglevel)
3451        if orglevel>0:
3452            node = Frag(indent*orglevel, node)
3453        return node
3454
3455    def __repr__(self):
3456        lc = len(self.content)
3457        if lc==0:
3458            infoc = "no children"
3459        elif lc==1:
3460            infoc = "1 child"
3461        else:
3462            infoc = "{} children".format(lc)
3463        la = len(self.attrs)
3464        if la==0:
3465            infoa = "no attrs"
3466        elif la==1:
3467            infoa = "1 attr"
3468        else:
3469            infoa = "{} attrs".format(la)
3470        if self.startloc is not None:
3471            loc = " (from {})".format(self.startloc)
3472        else:
3473            loc = ""
3474        return "<{0.__class__.__module__}.{0.__fullname__} element object ({1}/{2}){3} at {4:#x}>".format(self, infoc, infoa, loc, id(self))
3475
3476
3477class AttrElement(Element):
3478    """
3479    Special subclass of :class:`Element`.
3480
3481    When an :class:`AttrElement` node is the only node in an attribute, it
3482    takes over publishing of the attribute (via the methods :meth:`publishattr`
3483    and :meth:`publishboolattr`). In all other cases publishing is done in the
3484    normal way (and must be overwritten with the :meth:`publish` method).
3485    """
3486
3487    register = None
3488
3489    @misc.notimplemented
3490    def publish(self, publisher):
3491        """
3492        Publish ``self`` to the publisher :var:`publisher` (outside of any
3493        attribute)
3494        """
3495
3496    @misc.notimplemented
3497    def publishattr(self, publisher, attr):
3498        """
3499        Publish the attribute :var:`attr` to the publisher :var:`publisher`.
3500        """
3501
3502    @misc.notimplemented
3503    def publishboolattr(self, publisher, attr):
3504        """
3505        Publish the boolean attribute :var:`attr` to the publisher
3506        """
3507
3508
3509class _Entity_Meta(Node.__metaclass__):
3510    def __new__(cls, name, bases, dict):
3511        self = super(_Entity_Meta, cls).__new__(cls, name, bases, dict)
3512        if dict.get("register") is not None:
3513            threadlocalpool.pool.register(self)
3514        return self
3515
3516    def __repr__(self):
3517        return "<entity class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
3518
3519
3520class Entity(Node):
3521    """
3522    Class for entities. Derive your own entities from it and overwrite
3523    :meth:`convert`.
3524    """
3525    __metaclass__ = _Entity_Meta
3526
3527    register = None
3528
3529    @classmethod
3530    def _str(cls, fullname=True, xml=True, decorate=True):
3531        s = cls._strbase(fullname=fullname, xml=xml)
3532        if decorate:
3533            s = "&{};".format(s)
3534        return s
3535
3536    def __eq__(self, other):
3537        return self.__class__ is other.__class__
3538
3539    def compact(self):
3540        return self
3541
3542    def present(self, presenter):
3543        return presenter.presentEntity(self) # return a generator-iterator
3544
3545    def publish(self, publisher):
3546        yield publisher.encode(u"&")
3547        yield publisher.encode(self.xmlname)
3548        yield publisher.encode(u";")
3549
3550    def __repr__(self):
3551        if self.startloc is not None:
3552            loc = " (from {})".format(self.startloc)
3553        else:
3554            loc = ""
3555        return "<{0.__class__.__module__}.{0.__fullname__} entity object{1} at {2:#x}>".format(self, loc, id(self))
3556
3557
3558class _CharRef_Meta(Entity.__metaclass__): # don't subclass Text.__metaclass__, as this is redundant
3559    def __repr__(self):
3560        return "<charref class {0.__module__}:{0.__fullname__} at {1:#x}>".format(self, id(self))
3561
3562
3563class CharRef(Text, Entity):
3564    """
3565    A simple named character reference, the codepoint is in the class attribute
3566    :attr:`codepoint`.
3567    """
3568    __metaclass__ = _CharRef_Meta
3569    register = None
3570
3571    def __init__(self):
3572        Text.__init__(self, unichr(self.codepoint))
3573        Entity.__init__(self)
3574
3575    def __getnewargs__(self):
3576        return ()
3577
3578    def present(self, presenter):
3579        return presenter.presentEntity(self) # return a generator-iterator
3580
3581    # The rest is the same as for Text, but does not return CharRefs, but Texts
3582    def __getitem__(self, index):
3583        return Text(self.content.__getitem__(index))
3584
3585    def __add__(self, other):
3586        return Text(self.content + other)
3587
3588    def __radd__(self, other):
3589        return Text(unicode(other) + self.content)
3590
3591    def __mul__(self, n):
3592        return Text(n * self.content)
3593
3594    def __rmul__(self, n):
3595        return Text(n * self.content)
3596
3597    def __getslice__(self, index1, index2):
3598        return Text(self.content.__getslice__(index1, index2))
3599
3600    def capitalize(self):
3601        return Text(self.content.capitalize())
3602
3603    def center(self, width):
3604        return Text(self.content.center(width))
3605
3606    def ljust(self, width, fill=u" "):
3607        return Text(self.content.ljust(width, fill))
3608
3609    def lower(self):
3610        return Text(self.content.lower())
3611
3612    def lstrip(self, chars=None):
3613        return Text(self.content.lstrip(chars))
3614
3615    def replace(self, old, new, maxsplit=-1):
3616        return Text(self.content.replace(old, new, maxsplit))
3617
3618    def rjust(self, width, fill=u" "):
3619        return Text(self.content.rjust(width, fill))
3620
3621    def rstrip(self, chars=None):
3622        return Text(self.content.rstrip(chars))
3623
3624    def strip(self, chars=None):
3625        return Text(self.content.strip(chars))
3626
3627    def swapcase(self):
3628        return Text(self.content.swapcase())
3629
3630    def title(self):
3631        return Text(self.content.title())
3632
3633    def translate(self, table):
3634        return Text(self.content.translate(table))
3635
3636    def upper(self):
3637        return Text(self.content.upper())
3638
3639
3640###
3641### XML class pool
3642###
3643
3644class Pool(misc.Pool):
3645    """
3646    A :class:`Pool` stores a collection of XIST classes and can be passed to a
3647    parser. The parser will ask the pool which classes to use when elements,
3648    processing instructions etc. have to be instantiated.
3649    """
3650
3651    def __init__(self, *objects):
3652        """
3653        Create a :class:`Pool` object. All items in :var:`objects` will be
3654        registered in the pool.
3655        """
3656        self._elementsbyxmlname = {}
3657        self._elementsbypyname = {}
3658        self._procinstsbyxmlname = {}
3659        self._procinstsbypyname = {}
3660        self._entitiesbyxmlname = {}
3661        self._entitiesbypyname = {}
3662        self._charrefsbyxmlname = {}
3663        self._charrefsbypyname = {}
3664        self._charrefsbycodepoint = {}
3665        self._attrsbyxmlname = {}
3666        self._attrsbypyname = {}
3667        misc.Pool.__init__(self, *objects)
3668
3669    def register(self, object):
3670        """
3671        Register :var:`object` in the pool. :var:`object` can be:
3672
3673        *   a :class:`Element`, :class:`ProcInst`, :class:`Entity`, or
3674            :class:`CharRef` class;
3675
3676        *   an :class:`Attr` class for a global attribute;
3677
3678        *   an :class:`Attrs` class containing global attributes;
3679
3680        *   a :class:`dict` (all values will be registered, this makes it possible
3681            to e.g. register all local variables by passing ``vars()``);
3682
3683        *   a module (all attributes in the module will be registered).
3684        """
3685        # Note that the following is a complete reimplementation, otherwise the interactions would be too complicated.
3686        if isinstance(object, type):
3687            if issubclass(object, Element):
3688                if object.register:
3689                    self._elementsbyxmlname[(object.xmlname, object.xmlns)] = object
3690                    self._elementsbypyname[(object.__name__, object.xmlns)] = object
3691            elif issubclass(object, ProcInst):
3692                if object.register:
3693                    self._procinstsbyxmlname[object.xmlname] = object
3694                    self._procinstsbypyname[object.__name__] = object
3695            elif issubclass(object, Entity):
3696                if object.register:
3697                    self._entitiesbyxmlname[object.xmlname] = object
3698                    self._entitiesbypyname[object.__name__] = object
3699                    if issubclass(object, CharRef):
3700                        self._charrefsbyxmlname[object.xmlname] = object
3701                        self._charrefsbypyname[object.__name__] = object
3702                        self._charrefsbycodepoint[object.codepoint] = object
3703            elif issubclass(object, Attr):
3704                if object.xmlns is not None and object.register:
3705                    self._attrsbyxmlname[(object.xmlname, object.xmlns)] = object
3706                    self._attrsbypyname[(object.__name__, object.xmlns)] = object
3707            elif issubclass(object, Attrs):
3708                for attr in object.allowedattrs():
3709                    self.register(attr)
3710            self._attrs[object.__name__] = object
3711        elif isinstance(object, types.ModuleType):
3712            self.register(object.__dict__)
3713        elif isinstance(object, dict):
3714            for (key, value) in object.iteritems():
3715                if key == "__bases__":
3716                    for base in value:
3717                        if not isinstance(base, Pool):
3718                            base = self.__class__(base)
3719                        self.bases.append(base)
3720                elif isinstance(value, type):
3721                    self.register(value)
3722                elif not isinstance(value, (types.ModuleType, dict)):
3723                    try:
3724                        self._attrs[key] = value
3725                    except TypeError:
3726                        pass
3727        elif isinstance(object, Pool):
3728            self.bases.append(object)
3729
3730    def __enter__(self):
3731        self.prev = threadlocalpool.pool
3732        threadlocalpool.pool = self
3733        return self
3734
3735    def __exit__(self, type, value, traceback):
3736        threadlocalpool.pool = self.prev
3737        del self.prev
3738
3739    def clear(self):
3740        """
3741        Make :var:`self` empty.
3742        """
3743        self._elementsbyxmlname.clear()
3744        self._elementsbypyname.clear()
3745        self._procinstsbyxmlname.clear()
3746        self._procinstsbypyname.clear()
3747        self._entitiesbyxmlname.clear()
3748        self._entitiesbypyname.clear()
3749        self._charrefsbyxmlname.clear()
3750        self._charrefsbypyname.clear()
3751        self._charrefsbycodepoint.clear()
3752        self._attrsbyxmlname.clear()
3753        self._attrsbypyname.clear()
3754        misc.Pool.clear(self)
3755
3756    def clone(self):
3757        """
3758        Return a copy of :var:`self`.
3759        """
3760        copy = misc.Pool.clone(self)
3761        copy._elementsbyxmlname = self._elementsbyxmlname.copy()
3762        copy._elementsbypyname = self._elementsbypyname.copy()
3763        copy._procinstsbyxmlname = self._procinstsbyxmlname.copy()
3764        copy._procinstsbypyname = self._procinstsbypyname.copy()
3765        copy._entitiesbyxmlname = self._entitiesbyxmlname.copy()
3766        copy._entitiesbypyname = self._entitiesbypyname.copy()
3767        copy._charrefsbyxmlname = self._charrefsbyxmlname.copy()
3768        copy._charrefsbypyname = self._charrefsbypyname.copy()
3769        copy._charrefsbycodepoint = self._charrefsbycodepoint.copy()
3770        copy._attrsbyxmlname = self._attrsbyxmlname.copy()
3771        copy._attrsbypyname = self._attrsbypyname.copy()
3772        return copy
3773
3774    def elements(self):
3775        """
3776        Return an iterator for all registered element classes.
3777        """
3778        seen = set()
3779        for element in self._elementsbypyname.itervalues():
3780            yield element
3781            seen.add((element.xmlname, element.xmlns))
3782        for base in self.bases:
3783            for element in base.elements():
3784                if (element.xmlname, element.xmlns) not in seen:
3785                    yield element
3786                    seen.add((element.xmlname, element.xmlns))
3787
3788    def elementclass(self, name, xmlns):
3789        """
3790        Return the element class for the element with the Python name :var:`name`
3791        and the namespace :var:`xmlns`. If the element can't be found an
3792        :exc:`IllegalElementError` will be raised.
3793        """
3794        if isinstance(xmlns, (list, tuple)):
3795            for xmlns in xmlns:
3796                xmlns = nsname(xmlns)
3797                try:
3798                    return self._elementsbypyname[(name, xmlns)]
3799                except KeyError:
3800                    pass
3801        else:
3802            xmlns = nsname(xmlns)
3803            try:
3804                return self._elementsbypyname[(name, xmlns)]
3805            except KeyError:
3806                pass
3807        for base in self.bases:
3808            try:
3809                return base.elementclass(name, xmlns)
3810            except IllegalElementError:
3811                pass
3812        raise IllegalElementError(name, xmlns, False)
3813
3814    def elementclass_xml(self, name, xmlns):
3815        """
3816        Return the element class for the element type with the XML name
3817        :var:`name` and the namespace :var:`xmlns`. If the element can't
3818        be found an :exc:`IllegalElementError` will be raised.
3819        """
3820        if isinstance(xmlns, (list, tuple)):
3821            for xmlns in xmlns:
3822                xmlns = nsname(xmlns)
3823                try:
3824                    return self._elementsbyxmlname[(name, xmlns)]
3825                except KeyError:
3826                    pass
3827        else:
3828            xmlns = nsname(xmlns)
3829            try:
3830                return self._elementsbyxmlname[(name, xmlns)]
3831            except KeyError:
3832                pass
3833        for base in self.bases:
3834            try:
3835                return base.elementclass_xml(name, xmlns)
3836            except IllegalElementError:
3837                pass
3838        raise IllegalElementError(name, xmlns, True)
3839
3840    def element(self, name, xmlns):
3841        """
3842        Return an element object for the element type with the Python name
3843        :var:`name` and the namespace :var:`xmlns`.
3844        """
3845        return self.elementclass(name, xmlns)()
3846
3847    def element_xml(self, name, xmlns):
3848        """
3849        Return an element object for the element type with the XML name
3850        :var:`name` and the namespace :var:`xmlns`.
3851        """
3852        return self.elementclass_xml(name, xmlns)()
3853
3854    def haselement(self, name, xmlns):
3855        """
3856        Is there a registered element class in :var:`self` for the element type
3857        with the Python name :var:`name` and the namespace :var:`xmlns`?
3858        """
3859        return (name, nsname(xmlns)) in self._elementsbypyname or any(base.haselement(name, xmlns) for base in self.bases)
3860
3861    def haselement_xml(self, name, xmlns):
3862        """
3863        Is there a registered element class in :var:`self` for the element type
3864        with the XML name :var:`name` and the namespace :var:`xmlns`?
3865        """
3866        return (name, nsname(xmlns)) in self._elementsbyxmlname or any(base.haselement_xml(name, xmlns) for base in self.bases)
3867
3868    def procinsts(self):
3869        """
3870        Return an iterator for all registered processing instruction classes.
3871        """
3872        seen = set()
3873        for procinst in self._procinstsbypyname.itervalues():
3874            yield procinst
3875            seen.add(procinst.xmlname)
3876        for base in self.bases:
3877            for procinst in base.procinsts():
3878                if procinst.xmlname not in seen:
3879                    yield procinst
3880                    seen.add(procinst.xmlname)
3881
3882    def procinstclass(self, name):
3883        """
3884        Return the processing instruction class for the PI with the Python name
3885        :var:`name`. If the processing instruction can't be found an
3886        :exc:`IllegalProcInstError` will be raised.
3887        """
3888        try:
3889            return self._procinstsbypyname[name]
3890        except KeyError:
3891            for base in self.bases:
3892                try:
3893                    return base.procinstclass(name)
3894                except IllegalProcInstError:
3895                    pass
3896            raise IllegalProcInstError(name, False)
3897
3898    def procinstclass_xml(self, name):
3899        """
3900        Return the processing instruction class for the PI with the XML name
3901        :var:`name`. If the processing instruction can't be found an
3902        :exc:`IllegalProcInstError` will be raised.
3903        """
3904        try:
3905            return self._procinstsbyxmlname[name]
3906        except KeyError:
3907            for base in self.bases:
3908                try:
3909                    return base.procinstclass_xml(name)
3910                except IllegalProcInstError:
3911                    pass
3912            raise IllegalProcInstError(name, True)
3913
3914    def procinst(self, name, content):
3915        """
3916        Return a processing instruction object for the PI type with the Python
3917        target name :var:`name`.
3918        """
3919        return self.procinstclass(name)(content)
3920
3921    def procinst_xml(self, name, content):
3922        """
3923        Return a processing instruction object for the PI type with the XML
3924        target name :var:`name`.
3925        """
3926        return self.procinstclass_xml(name)(content)
3927
3928    def hasprocinst(self, name):
3929        """
3930        Is there a registered processing instruction class in :var:`self` for the
3931        PI with the Python name :var:`name`?
3932        """
3933        return name in self._procinstsbypyname or any(base.hasprocinst(name) for base in self.bases)
3934
3935    def hasprocinst_xml(self, name):
3936        """
3937        Is there a registered processing instruction class in :var:`self` for the
3938        PI with the XML name :var:`name`?
3939        """
3940        return name in self._procinstsbyxmlname or any(base.hasprocinst_xml(name) for base in self.bases)
3941
3942    def entities(self):
3943        """
3944        Return an iterator for all registered entity classes.
3945        """
3946        seen = set()
3947        for entity in self._entitiesbypyname.itervalues():
3948            yield entity
3949            seen.add(entity.xmlname)
3950        for base in self.bases:
3951            for entity in base.entities():
3952                if entity.xmlname not in seen:
3953                    yield entity
3954                    seen.add(entity.xmlname)
3955
3956    def entityclass(self, name):
3957        """
3958        Return the entity class for the entity with the Python name :var:`name`.
3959        If the entity can't be found an :exc:`IllegalEntityError` will be raised.
3960        """
3961        try:
3962            return self._entitiesbypyname[name]
3963        except KeyError:
3964            for base in self.bases:
3965                try:
3966                    return base.entityclass(name)
3967                except IllegalEntityError:
3968                    pass
3969            raise IllegalEntityError(name, False)
3970
3971    def entityclass_xml(self, name):
3972        """
3973        Return the entity class for the entity with the XML name :var:`name`.
3974        If the entity can't be found an :exc:`IllegalEntityError` will be raised.
3975        """
3976        try:
3977            return self._entitiesbyxmlname[name]
3978        except KeyError:
3979            for base in self.bases:
3980                try:
3981                    return base.entityclass_xml(name)
3982                except IllegalEntityError:
3983                    pass
3984            raise IllegalEntityError(name, True)
3985
3986    def entity(self, name):
3987        """
3988        Return an entity object for the entity with the Python name :var:`name`.
3989        """
3990        return self.entityclass(name)()
3991
3992    def entity_xml(self, name):
3993        """
3994        Return an entity object for the entity with the XML name :var:`name`.
3995        """
3996        return self.entityclass_xml(name)()
3997
3998    def hasentity(self, name):
3999        """
4000        Is there a registered entity class in :var:`self` for the entity with the
4001        Python name :var:`name`?
4002        """
4003        return name in self._entitiesbypyname or any(base.hasentity(name) for base in self.bases)
4004
4005    def hasentity_xml(self, name):
4006        """
4007        Is there a registered entity class in :var:`self` for the entity with the
4008        XML name :var:`name`?
4009        """
4010        return name in self._entitiesbyxmlname or any(base.hasentity_xml(name) for base in self.bases)
4011
4012    def charrefs(self):
4013        """
4014        Return an iterator for all character entity classes.
4015        """
4016        seen = set()
4017        for charref in self._charrefsbypyname.itervalues():
4018            yield charref
4019            seen.add(charref.xmlname)
4020        for base in self.bases:
4021            for charref in base.charrefs():
4022                if charref.xmlname not in seen:
4023                    yield charref
4024                    seen.add(charref.xmlname)
4025
4026    def charrefclass(self, name):
4027        """
4028        Return the character entity with the Python name :var:`name`.
4029        :var:`name` can also be an :class:`int` specifying the codepoint.
4030        If the character entity can't be found a :exc:`IllegalEntityError`
4031        will be raised.
4032        """
4033        try:
4034            if isinstance(name, (int, long)):
4035                return self._charrefsbycodepoint[name]
4036            return self._charrefsbypyname[name]
4037        except KeyError:
4038            for base in self.bases:
4039                try:
4040                    return base.charrefclass(name)
4041                except IllegalEntityError:
4042                    pass
4043            raise IllegalEntityError(name, False)
4044
4045    def charrefclass_xml(self, name):
4046        """
4047        Return the character entity with the XML name :var:`name`.
4048        :var:`name` can also be an :class:`int` specifying the codepoint.
4049        If the character entity can't be found a :exc:`IllegalEntityError`
4050        will be raised.
4051        """
4052        try:
4053            if isinstance(name, (int, long)):
4054                return self._charrefsbycodepoint[name]
4055            return self._charrefsbyxmlname[name]
4056        except KeyError:
4057            for base in self.bases:
4058                try:
4059                    return base.charrefclass_xml(name)
4060                except IllegalEntityError:
4061                    pass
4062            raise IllegalEntityError(name, True)
4063
4064    def charref(self, name):
4065        """
4066        Return a character entity object for the character with the Python name
4067        or codepoint :var:`name`.
4068        """
4069        return self.charrefclass(name)()
4070
4071    def charref_xml(self, name):
4072        """
4073        Return a character entity object for the character with the XML name
4074        or codepoint :var:`name`.
4075        """
4076        return self.charrefclass_xml(name)()
4077
4078    def hascharref(self, name):
4079        """
4080        Is there a registered character entity class in :var:`self` with the
4081        Python name or codepoint :var:`name`?
4082        """
4083        if isinstance(name, (int, long)):
4084            has = name in self._charrefsbycodepoint
4085        else:
4086            has = name in self._charrefsbypyname
4087        return has or any(base.hascharref(name) for base in self.bases)
4088
4089    def hascharref_xml(self, name):
4090        """
4091        Is there a registered character entity class in :var:`self` with the XML
4092        name or codepoint :var:`name`?
4093        """
4094        if isinstance(name, (int, long)):
4095            has = name in self._charrefsbycodepoint
4096        else:
4097            has = name in self._charrefsbypyname
4098        return has or any(base.hascharref_xml(name) for base in self.bases)
4099
4100    def attrclass(self, name, xmlns):
4101        """
4102        Return the attribute class for the global attribute with the Python name
4103        :var:`name` and the namespace :var:`xmlns`. If the attribute can't
4104        be found a :exc:`IllegalAttrError` will be raised.
4105        """
4106        if not isinstance(xmlns, (list, tuple)):
4107            xmlns = (xmlns,)
4108        for xmlns in xmlns:
4109            xmlns = nsname(xmlns)
4110            try:
4111                return self._attrsbypyname[(name, xmlns)]
4112            except KeyError:
4113                pass
4114        for base in self.bases:
4115            try:
4116                return base.attrclass(name, xmlns)
4117            except IllegalAttrError:
4118                pass
4119        raise IllegalAttrError(name, xmlns, False)
4120
4121    def attrclass_xml(self, name, xmlns):
4122        """
4123        Return the attribute class for the global attribute with the XML name
4124        :var:`name` and the namespace :var:`xmlns`. If the attribute can't
4125        be found a :exc:`IllegalAttrError` will be raised.
4126        """
4127        if not isinstance(xmlns, (list, tuple)):
4128            xmlns = (xmlns,)
4129        for xmlns in xmlns:
4130            xmlns = nsname(xmlns)
4131            try:
4132                return self._attrsbyxmlname[(name, xmlns)]
4133            except KeyError:
4134                pass
4135        for base in self.bases:
4136            try:
4137                return base.attrclass_xml(name, xmlns)
4138            except IllegalAttrError:
4139                pass
4140        raise IllegalAttrError(name, xmlns, True)
4141
4142    def text(self, content):
4143        """
4144        Create a text node with the content :var:`content`.
4145        """
4146        return Text(content)
4147
4148    def comment(self, content):
4149        """
4150        Create a comment node with the content :var:`content`.
4151        """
4152        return Comment(content)
4153
4154    def __getattr__(self, key):
4155        try:
4156            return self._attrs[key]
4157        except KeyError:
4158            for base in self.bases:
4159                return getattr(base, key)
4160            raise AttributeError(key)
4161
4162
4163# Default pool (can be temporarily changed via ``with xsc.Pool() as pool:``)
4164class ThreadLocalPool(threading.local):
4165    pool = Pool()
4166
4167threadlocalpool = ThreadLocalPool()
4168
4169
4170###
4171### Functions for namespace handling
4172###
4173
4174def docpool():
4175    """
4176    Return a pool suitable for parsing XIST docstrings.
4177    """
4178    from ll.xist.ns import html, chars, abbr, doc, specials
4179    return Pool(doc, specials, html, chars, abbr)
4180
4181
4182def nsname(xmlns):
4183    """
4184    If :var:`xmlns` is a module, return ``xmlns.xmlns``, else return
4185    :var:`xmlns` unchanged.
4186    """
4187    if xmlns is not None and not isinstance(xmlns, basestring):
4188        xmlns = xmlns.xmlns
4189    return xmlns
4190
4191
4192def nsclark(xmlns):
4193    """
4194    Return a namespace name in Clark notation. :var:`xmlns` can be :const:`None`,
4195    a string or a module.
4196    """
4197    if xmlns is None:
4198        return "{}"
4199    elif not isinstance(xmlns, basestring):
4200        xmlns = xmlns.xmlns
4201    return "{{{}}}".format(xmlns)
4202
4203
4204# C0 Controls and Basic Latin
4205class quot(CharRef): "quotation mark = APL quote, U+0022 ISOnum"; codepoint = 34
4206class amp(CharRef): "ampersand, U+0026 ISOnum"; codepoint = 38
4207class lt(CharRef): "less-than sign, U+003C ISOnum"; codepoint = 60
4208class gt(CharRef): "greater-than sign, U+003E ISOnum"; codepoint = 62
4209class apos(CharRef): "apostrophe mark, U+0027 ISOnum"; codepoint = 39
4210
4211
4212###
4213### Location information
4214###
4215
4216class Location(object):
4217    """
4218    Represents a location in an XML entity.
4219    """
4220    __slots__ = ("url", "line", "col", "char")
4221
4222    def __init__(self, url=None, line=None, col=None):
4223        """
4224        Create a new :class:`Location` object using the arguments passed in.
4225        :var:`url` is the URL/filename. :var:`line` is the line number and
4226        :var:`col` is the column number (both starting at 0).
4227        """
4228        self.url = url
4229        self.line = line
4230        self.col = col
4231
4232    def offset(self, offset):
4233        """
4234        Return a location where the line number is incremented by offset
4235        (and the column number is reset to 0).
4236        """
4237        if offset==0:
4238            return self
4239        elif self.line is None:
4240            return Location(url=self.url, col=0)
4241        return Location(url=self.url, line=self.line+offset, col=0)
4242
4243    def __str__(self):
4244        url = str(self.url) if self.url is not None else "???"
4245        line = str(self.line) if self.line is not None else "?"
4246        col = str(self.col) if self.col is not None else "?"
4247        return "{}:{}:{}".format(url, line, col)
4248
4249    def __repr__(self):
4250        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))
4251
4252    def __eq__(self, other):
4253        return self.__class__ is other.__class__ and self.url==other.url and self.line==other.line and self.col==other.col
4254
4255    def __ne__(self, other):
4256        return not self==other
4257
4258    def __xattrs__(self, mode="default"):
4259        return ("url", "line", "col")
4260
4261    def __xrepr__(self, mode="default"):
4262        for part in ipipe.xrepr(self.url, mode):
4263            yield part
4264        yield (astyle.style_default, ":")
4265        for part in ipipe.xrepr(self.line, mode):
4266            yield part
4267        yield (astyle.style_default, ":")
4268        for part in ipipe.xrepr(self.col, mode):
4269            yield part
Note: See TracBrowser for help on using the browser.