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()
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)
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
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)
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
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'))