Beispiel #1
0
    def wrap_literal(self, element):
        index = self.element.index(element)

        t = self.element.makeelement(utils.meta_attr('literal'))
        t.meta_omit = ""
        t.tail = element.tail
        t.text = unicode(element)
        for child in element.getchildren():
            t.append(child)
        self.element.remove(element)
        self.element.insert(index, t)
        t.update()
Beispiel #2
0
    def wrap_literal(self, element):
        index = self.element.index(element)

        t = self.element.makeelement(utils.meta_attr('literal'))
        t.meta_omit = ""
        t.tail = element.tail
        t.text = unicode(element)
        for child in element.getchildren():
            t.append(child)
        self.element.remove(element)
        self.element.insert(index, t)
        t.update()
Beispiel #3
0
def parse(body, parser, validator):
    """Parse XML document using expat and return an
    ElementTree-compatible document tree."""
    
    junk = ""
    tree = None
    parts = []

    offset = 0
    original_body = body
    
    while tree is None:
        expat = xml.parsers.expat.ParserCreate()
        expatparser = ExpatParser(parser, body, expat, validator)

        try:
            # attempt to parse this body; if we're not successful,
            # this may be because the document source consists of
            # several 'parts'; although this is not valid XML, we do
            # support it, being a template engine, not a XML
            # validator :-)
            if body is not None:
                expat.Parse(body, 1)

            root = expatparser.root
            if parts:
                if root is not None:
                    parts.append(root)
                root = parser.makeelement(
                    utils.meta_attr('fragments'))
                for i, part in enumerate(parts):
                    if isinstance(part, basestring):
                        parts[i-1].tail = part
                    else:
                        root.append(part)
                tree = root.getroottree()
            elif root is not None:
                tree = root.getroottree()
            else:
                raise RuntimeError("Unexpected parsing error.")

        except xml.parsers.expat.ExpatError, e:
            offset += expat.CurrentByteIndex

            # if we are not able to find a tree root, we give up and
            # let the exception through; if the error status code is 3
            # (no element found) and we've already parsed some parts,
            # we ignore the error and stop parsing.
            code = getattr(e, 'code', -1)
            if code == 3:
                body = None

                if parts:
                    continue

                raise

            if code == 2 and expatparser.root is None:
                body = "<meta:wrapper>%s</meta:wrapper>" % body
                continue

            if code == 9 and expatparser.root is not None:
                # add the root as a tree fragment and update the body
                # source to the next possible chunk
                parts.append(expatparser.root)
                body = body[:expatparser.index] + body[expat.CurrentByteIndex:]

                # a simple heuristic is used here to allow chunks of
                # 'junk' in-between the tree fragments
                m = re_tag_start.search(body)
                if m is not None:
                    pos = m.start()
                else:
                    pos = -1
                junk = body[:pos]
                body = body[pos:]
                parts.append(junk)
                offset += pos
            else:
                # the default exception won't provide correct
                # information about where in the document the
                # exception stems from; we can compute it manually
                # though.
                line = original_body[:offset].count('\n') + 1
                column = offset - original_body[:offset].rfind('\n')
                error_msg = str(e).split(':')[0]
                error_msg = "%s: line %d, column %d" % (
                    error_msg, line, column)
                raise xml.parsers.expat.ExpatError(error_msg)
Beispiel #4
0
    def __call__(self, macro, global_scope=True, debug=False):
        root = copy.deepcopy(self.tree).getroot()

        if not isinstance(root, Element):
            raise ValueError(
                "Must define valid namespace for tag: '%s.'" % root.tag)

        # if macro is non-trivial, start compilation at the element
        # where the macro is defined
        if macro:
            for element in root.walk():
                node = element.node
                if node is not None and node.define_macro == macro:
                    element.meta_translator = root.meta_translator

                    # if element is the document root, render as a normal
                    # template, e.g. unset the `macro` mode
                    if root is element:
                        macro = None
                    else:
                        root = element

                    break
            else:
                raise KeyError(macro)

        # initialize code stream object
        stream = generation.CodeIO(
            root.node.symbols, encoding=self.encoding,
            indentation=0, indentation_string="\t")

        # transient symbols are added to the primary scope to exclude
        # them from being carried over when using macros
        for name, value in stream.symbols.as_dict().items():
            if value is config.TRANSIENT_SYMBOL:
                stream.scope[0].add(name)

        # initialize variable scope
        stream.scope.append(set(
            (stream.symbols.out,
             stream.symbols.write,
             stream.symbols.scope,
             stream.symbols.domain,
             stream.symbols.language)))

        if global_scope is False:
            stream.scope[-1].add(stream.symbols.remote_scope)

        # set up initialization code
        stream.symbol_mapping['_init_stream'] = generation.initialize_stream
        stream.symbol_mapping['_init_scope'] = generation.initialize_scope
        stream.symbol_mapping['_init_tal'] = generation.initialize_tal
        stream.symbol_mapping['_init_default'] = generation.initialize_default

        # add code-generation lookup globals
        if debug:
            lookup = codegen.lookup_attr_debug
        else:
            lookup = codegen.lookup_attr
        stream.symbol_mapping['_lookup_attr'] = lookup

        if global_scope:
            assignments = (
                clauses.Assign(
                    types.value("_init_stream()"), ("%(out)s", "%(write)s")),
                clauses.Assign(
                     types.value("_init_tal()"), ("%(attributes)s", "%(repeat)s")),
                clauses.Assign(
                    types.value("_init_default()"), '%(default_marker_symbol)s'),
                clauses.Assign(
                    types.value(repr(None)), '%(default)s'),
                clauses.Assign(
                     types.template("None"), "%(domain)s"))
        else:
            assignments = (
                clauses.Assign(
                    types.template(
                        "%(scope)s['%(out)s'], %(scope)s['%(write)s']"),
                    ("%(out)s", "%(write)s")),
                clauses.Assign(
                    types.value("_init_tal()"), ("%(attributes)s", "%(repeat)s")),
                clauses.Assign(
                    types.value("_init_default()"), '%(default_marker_symbol)s'),
                clauses.Assign(
                    types.value(repr(None)), '%(default)s'),
                clauses.Assign(
                     types.template("None"), "%(domain)s"))

        for clause in assignments:
            clause.begin(stream)
            clause.end(stream)

        if macro is not None:
            nsmap = {}
            namespaces = set()

            for tag in root.attrib:
                if '}' not in tag:
                    continue

                namespace = tag[1:].split('}')[0]
                namespaces.add(namespace)

            for prefix, namespace in root.nsmap.items():
                if namespace in namespaces:
                    nsmap[prefix] = namespace

            wrapper = self.tree.parser.makeelement(
                utils.meta_attr('wrapper'),
                utils.get_attributes_from_namespace(root, config.META_NS),
                nsmap=nsmap)
            wrapper.append(root)
            root = wrapper

        # output XML headers, if applicable
        if global_scope is True or macro is "":
            header = ""
            if self.xml_declaration is not None:
                header += self.xml_declaration + '\n'
            if self.doctype:
                doctype = self.doctype + '\n'
                if self.encoding:
                    doctype = doctype.encode(self.encoding)
                header += doctype
            if header:
                out = clauses.Out(header)
                stream.scope.append(set())
                stream.begin([out])
                stream.end([out])
                stream.scope.pop()

        # add meta settings
        root.meta_omit_default_prefix = self.omit_default_prefix

        # start generation
        root.start(stream)
        body = stream.getvalue()

        # symbols dictionary
        __dict__ = stream.symbols.as_dict()

        # prepare globals
        _globals = ["from cPickle import loads as _loads"]
        for symbol, value in stream.symbol_mapping.items():
            _globals.append(
                "%s = _loads(%s)" % (symbol, repr(dumps(value))))

        transient = []
        for name, value in stream.symbols.as_dict().items():
            if value is config.TRANSIENT_SYMBOL:
                transient.append("%s = econtext.get('%s')" % (name, name))
        transient = "; ".join(transient)

        # wrap generated Python-code in function definition
        if global_scope:
            source = generation.function_wrap(
                'render', _globals, body, transient,
                "%(out)s.getvalue()" % __dict__)
        else:
            source = generation.function_wrap(
                'render', _globals, body, transient)

        suite = codegen.Suite(source)
        return suite.source
Beispiel #5
0
def parse(body, parser, validator):
    """Parse XML document using expat and return an
    ElementTree-compatible document tree."""

    junk = ""
    tree = None
    parts = []

    offset = 0
    original_body = body

    while tree is None:
        expat = xml.parsers.expat.ParserCreate()
        expatparser = ExpatParser(parser, body, expat, validator)

        try:
            # attempt to parse this body; if we're not successful,
            # this may be because the document source consists of
            # several 'parts'; although this is not valid XML, we do
            # support it, being a template engine, not a XML
            # validator :-)
            if body is not None:
                expat.Parse(body, 1)

            root = expatparser.root
            if parts:
                if root is not None:
                    parts.append(root)
                root = parser.makeelement(utils.meta_attr('fragments'))
                for i, part in enumerate(parts):
                    if isinstance(part, basestring):
                        parts[i - 1].tail = part
                    else:
                        root.append(part)
                tree = root.getroottree()
            elif root is not None:
                tree = root.getroottree()
            else:
                raise RuntimeError("Unexpected parsing error.")

        except xml.parsers.expat.ExpatError, e:
            offset += expat.CurrentByteIndex

            # if we are not able to find a tree root, we give up and
            # let the exception through; if the error status code is 3
            # (no element found) and we've already parsed some parts,
            # we ignore the error and stop parsing.
            code = getattr(e, 'code', -1)
            if code == 3:
                body = None

                if parts:
                    continue

                raise

            if code == 2 and expatparser.root is None:
                body = "<meta:wrapper>%s</meta:wrapper>" % body
                continue

            if code == 9 and expatparser.root is not None:
                # add the root as a tree fragment and update the body
                # source to the next possible chunk
                parts.append(expatparser.root)
                body = body[:expatparser.index] + body[expat.CurrentByteIndex:]

                # a simple heuristic is used here to allow chunks of
                # 'junk' in-between the tree fragments
                m = re_tag_start.search(body)
                if m is not None:
                    pos = m.start()
                else:
                    pos = -1
                junk = body[:pos]
                body = body[pos:]
                parts.append(junk)
                offset += pos
            else:
                # the default exception won't provide correct
                # information about where in the document the
                # exception stems from; we can compute it manually
                # though.
                line = original_body[:offset].count('\n') + 1
                column = offset - original_body[:offset].rfind('\n')
                error_msg = str(e).split(':')[0]
                error_msg = "%s: line %d, column %d" % (error_msg, line,
                                                        column)
                raise xml.parsers.expat.ExpatError(error_msg)
Beispiel #6
0
    def __call__(self, macro, global_scope=True, debug=False):
        root = copy.deepcopy(self.tree).getroot()

        if not isinstance(root, Element):
            raise ValueError("Must define valid namespace for tag: '%s.'" %
                             root.tag)

        # if macro is non-trivial, start compilation at the element
        # where the macro is defined
        if macro:
            for element in root.walk():
                node = element.node
                if node is not None and node.define_macro == macro:
                    element.meta_translator = root.meta_translator

                    # if element is the document root, render as a normal
                    # template, e.g. unset the `macro` mode
                    if root is element:
                        macro = None
                    else:
                        root = element

                    break
            else:
                raise KeyError(macro)

        # initialize code stream object
        stream = generation.CodeIO(root.node.symbols,
                                   encoding=self.encoding,
                                   indentation=0,
                                   indentation_string="\t")

        # transient symbols are added to the primary scope to exclude
        # them from being carried over when using macros
        for name, value in stream.symbols.as_dict().items():
            if value is config.TRANSIENT_SYMBOL:
                stream.scope[0].add(name)

        # initialize variable scope
        stream.scope.append(
            set((stream.symbols.out, stream.symbols.write,
                 stream.symbols.scope, stream.symbols.domain,
                 stream.symbols.language)))

        if global_scope is False:
            stream.scope[-1].add(stream.symbols.remote_scope)

        # set up initialization code
        stream.symbol_mapping['_init_stream'] = generation.initialize_stream
        stream.symbol_mapping['_init_scope'] = generation.initialize_scope
        stream.symbol_mapping['_init_tal'] = generation.initialize_tal
        stream.symbol_mapping['_init_default'] = generation.initialize_default

        # add code-generation lookup globals
        if debug:
            lookup = codegen.lookup_attr_debug
        else:
            lookup = codegen.lookup_attr
        stream.symbol_mapping['_lookup_attr'] = lookup

        if global_scope:
            assignments = (clauses.Assign(types.value("_init_stream()"),
                                          ("%(out)s", "%(write)s")),
                           clauses.Assign(types.value("_init_tal()"),
                                          ("%(attributes)s", "%(repeat)s")),
                           clauses.Assign(types.value("_init_default()"),
                                          '%(default_marker_symbol)s'),
                           clauses.Assign(types.value(repr(None)),
                                          '%(default)s'),
                           clauses.Assign(types.template("None"),
                                          "%(domain)s"))
        else:
            assignments = (clauses.Assign(
                types.template("%(scope)s['%(out)s'], %(scope)s['%(write)s']"),
                ("%(out)s", "%(write)s")),
                           clauses.Assign(types.value("_init_tal()"),
                                          ("%(attributes)s", "%(repeat)s")),
                           clauses.Assign(types.value("_init_default()"),
                                          '%(default_marker_symbol)s'),
                           clauses.Assign(types.value(repr(None)),
                                          '%(default)s'),
                           clauses.Assign(types.template("None"),
                                          "%(domain)s"))

        for clause in assignments:
            clause.begin(stream)
            clause.end(stream)

        if macro is not None:
            nsmap = {}
            namespaces = set()

            for tag in root.attrib:
                if '}' not in tag:
                    continue

                namespace = tag[1:].split('}')[0]
                namespaces.add(namespace)

            for prefix, namespace in root.nsmap.items():
                if namespace in namespaces:
                    nsmap[prefix] = namespace

            wrapper = self.tree.parser.makeelement(
                utils.meta_attr('wrapper'),
                utils.get_attributes_from_namespace(root, config.META_NS),
                nsmap=nsmap)
            wrapper.append(root)
            root = wrapper

        # output XML headers, if applicable
        if global_scope is True or macro is "":
            header = ""
            if self.xml_declaration is not None:
                header += self.xml_declaration + '\n'
            if self.doctype:
                doctype = self.doctype + '\n'
                if self.encoding:
                    doctype = doctype.encode(self.encoding)
                header += doctype
            if header:
                out = clauses.Out(header)
                stream.scope.append(set())
                stream.begin([out])
                stream.end([out])
                stream.scope.pop()

        # add meta settings
        root.meta_omit_default_prefix = self.omit_default_prefix

        # start generation
        root.start(stream)
        body = stream.getvalue()

        # symbols dictionary
        __dict__ = stream.symbols.as_dict()

        # prepare globals
        _globals = ["from cPickle import loads as _loads"]
        for symbol, value in stream.symbol_mapping.items():
            _globals.append("%s = _loads(%s)" % (symbol, repr(dumps(value))))

        transient = []
        for name, value in stream.symbols.as_dict().items():
            if value is config.TRANSIENT_SYMBOL:
                transient.append("%s = econtext.get('%s')" % (name, name))
        transient = "; ".join(transient)

        # wrap generated Python-code in function definition
        if global_scope:
            source = generation.function_wrap('render', _globals, body,
                                              transient,
                                              "%(out)s.getvalue()" % __dict__)
        else:
            source = generation.function_wrap('render', _globals, body,
                                              transient)

        suite = codegen.Suite(source)
        return suite.source
Beispiel #7
0
class Element(etree.ElementBase):
    """Template element class.

    To start translation at this element, use the ``start`` method,
    providing a code stream object.
    """

    translator = None

    class node(Node):
        @property
        def omit(self):
            if self.element.meta_omit is not None:
                return self.element.meta_omit or True
            if self.element.meta_replace:
                return True

        @property
        def content(self):
            return self.element.meta_replace

    node = property(node)

    def start(self, stream):
        self._stream = stream
        self.node.visit()

    def update(self):
        pass

    @property
    def root(self):
        tree = self.getroottree()
        root = tree.getroot()

        while root is not None:
            parent = root.getparent()
            if parent is None:
                break
            root = parent

        return root

    @property
    def stream(self):
        try:
            return self.root._stream
        except AttributeError:
            raise AttributeError("Can't locate stream object.")

    meta_cdata = etree.Annotation(utils.meta_attr('cdata'))

    meta_omit = etree.Annotation(utils.meta_attr('omit-tag'))

    meta_omit_default_prefix = etree.Annotation(
        utils.meta_attr('omit-default-prefix'))

    meta_structure = etree.Annotation(utils.meta_attr('structure'))

    meta_attributes = etree.Annotation(utils.meta_attr('attributes'))

    meta_replace = etree.Annotation(utils.meta_attr('replace'))