1.1 --- a/libxml2macro.py Sun Jan 16 01:15:01 2005 +0000
1.2 +++ b/libxml2macro.py Sun Jan 16 01:15:37 2005 +0000
1.3 @@ -1,12 +1,43 @@
1.4 #!/usr/bin/env python
1.5
1.6 import compiler
1.7 +import marshal, imp, os, stat, struct
1.8 +
1.9 +# Originally from Analysis/Writers/Python.
1.10 +
1.11 +def write_module(module, source_filename, target_filename, syntax_check=1):
1.12 +
1.13 + """
1.14 + Write the given 'module', associated with the given 'source_filename', to a
1.15 + file with the given 'target_filename'. The optional 'syntax_check' flag (set
1.16 + by default) ensures that the module is syntactically correct.
1.17 + """
1.18 +
1.19 + if syntax_check:
1.20 + compiler.syntax.check(module)
1.21 + compiler.misc.set_filename(source_filename, module)
1.22 + generator = compiler.pycodegen.ModuleCodeGenerator(module)
1.23 + f = open(target_filename, "wb")
1.24 + f.write(get_header(source_filename))
1.25 + marshal.dump(generator.getCode(), f)
1.26 + f.close()
1.27 +
1.28 +def get_header(filename):
1.29 +
1.30 + "Taken from compiler.pycodegen. Prepare the compiled module header."
1.31 +
1.32 + MAGIC = imp.get_magic()
1.33 + mtime = os.stat(filename)[stat.ST_MTIME]
1.34 + mtime = struct.pack('<i', mtime)
1.35 + return MAGIC + mtime
1.36 +
1.37 +# Processing functions.
1.38
1.39 def process_nodes(root_node, prefix):
1.40
1.41 """
1.42 Under the 'root_node', process all suitable expression nodes which employ
1.43 - Name nodes using the given 'prefix'.
1.44 + special names using the given 'prefix'.
1.45 """
1.46
1.47 for node in root_node.getChildNodes():
1.48 @@ -19,24 +50,77 @@
1.49 process_nodes(node, prefix)
1.50
1.51 def process_callfunc(node, prefix):
1.52 +
1.53 + """
1.54 + Process the CallFunc 'node' searching for special names with the given
1.55 + 'prefix'.
1.56 + """
1.57 +
1.58 # Check the target.
1.59 target = node.node
1.60 if isinstance(target, compiler.ast.Getattr):
1.61 - process_getattr(target, prefix, node)
1.62 + if process_getattr(target, prefix, node):
1.63 +
1.64 + # Process all sibling arguments of the new first argument, too.
1.65 +
1.66 + process_callfunc_args(node, prefix, start_index=1)
1.67 +
1.68 + else:
1.69 + process_callfunc_args(node, prefix)
1.70 +
1.71 else:
1.72 - process_nodes(node, prefix)
1.73 + process_callfunc_args(node, prefix)
1.74 +
1.75 +def process_callfunc_args(node, prefix, start_index=0):
1.76 +
1.77 + """
1.78 + Process the arguments of the given CallFunc 'node' searching for special
1.79 + names with the given 'prefix'. The optional 'start_index' is used to
1.80 + indicate the first argument to be processed.
1.81 + """
1.82
1.83 -def process_getattr(node, prefix, parent):
1.84 - # Check the target.
1.85 - target = node.expr
1.86 - if isinstance(target, compiler.ast.Name) and target.name.startswith(prefix):
1.87 + for index in range(start_index, len(node.args)):
1.88 + arg = node.args[index]
1.89 + if isinstance(arg, compiler.ast.Getattr):
1.90 + process_getattr(arg, prefix, node, index=index)
1.91 +
1.92 +def process_getattr(node, prefix, parent, index=None):
1.93 +
1.94 + """
1.95 + Process the Getattr 'node' searching for special names with the given
1.96 + 'prefix', using the given 'parent' to add transformed expressions in place
1.97 + of the original ones.
1.98 +
1.99 + The optional 'index' is used when arguments of CallFunc nodes are being
1.100 + processed and where a replacement of such arguments is occurring.
1.101 + """
1.102 +
1.103 + # Detected cases:
1.104 + # node.attr plus node.attr.attr
1.105 + # (obj.node).attr plus (obj.node).attr.attr
1.106 + # Note that the deep cases are dealt with first using a recursive call.
1.107 +
1.108 + if getattr_has_prefix(node, prefix) or \
1.109 + isinstance(node.expr, compiler.ast.Getattr) and propagated_prefix(process_getattr(node.expr, prefix, node)):
1.110
1.111 # Replace CallFunc plus Getattr occurrences:
1.112 # node.attr(args) -> Node_attr(node, args)
1.113 + # fn(node.attr) -> fn(Node_attr(node))
1.114
1.115 if isinstance(parent, compiler.ast.CallFunc):
1.116 - parent.node = compiler.ast.Name("Node_%s" % node.attrname)
1.117 - parent.args.insert(0, compiler.ast.Name(target.name))
1.118 +
1.119 + # If this node is not an argument, transform the call.
1.120 +
1.121 + if index is None:
1.122 + parent.node = compiler.ast.Name("Node_%s" % node.attrname)
1.123 + parent.args.insert(0, node.expr)
1.124 +
1.125 + else:
1.126 + replacement = compiler.ast.CallFunc(
1.127 + compiler.ast.Name("Node_%s" % node.attrname),
1.128 + [node.expr]
1.129 + )
1.130 + parent.args[index] = replacement
1.131
1.132 # Replace plain Getattr nodes:
1.133 # node.attr -> Node_attr(node)
1.134 @@ -56,8 +140,41 @@
1.135 except TypeError:
1.136 if value is node:
1.137 parent.__dict__[key] = replacement
1.138 +
1.139 + # Propagate whether the kind of result might need transforming itself.
1.140 +
1.141 + return node.attrname
1.142 +
1.143 else:
1.144 process_nodes(node, prefix)
1.145 + return None
1.146 +
1.147 +def propagated_prefix(attrname):
1.148 +
1.149 + """
1.150 + Return whether the given 'attrname' used in a transformation should be
1.151 + considered significant at the parent level.
1.152 + """
1.153 +
1.154 + return attrname in ("ownerElement", "ownerDocument")
1.155 +
1.156 +def getattr_has_prefix(node, prefix):
1.157 +
1.158 + """
1.159 + Determine whether the given Getattr 'node' employs the special 'prefix' in a
1.160 + number of ways.
1.161 + """
1.162 +
1.163 + # Check the expression as a simple name:
1.164 + # node.attr
1.165 + if isinstance(node.expr, compiler.ast.Name) and node.expr.name.startswith(prefix):
1.166 + return 1
1.167 + # Check the attribute name of child expressions:
1.168 + # (obj.node).attr
1.169 + elif isinstance(node.expr, compiler.ast.Getattr) and node.expr.attrname.startswith(prefix):
1.170 + return 1
1.171 + else:
1.172 + return 0
1.173
1.174 def process_file(filename, prefix="x2_"):
1.175
1.176 @@ -73,6 +190,17 @@
1.177 # Find references to special variables.
1.178
1.179 process_nodes(module, prefix)
1.180 +
1.181 + # Write the module.
1.182 +
1.183 + write_module(module, filename, os.path.splitext(filename)[0] + ".pyc")
1.184 return module
1.185
1.186 +if __name__ == "__main__":
1.187 + import sys
1.188 + if len(sys.argv) < 2:
1.189 + print "libxml2macro.py <module-filename>"
1.190 + sys.exit(1)
1.191 + process_file(sys.argv[1])
1.192 +
1.193 # vim: tabstop=4 expandtab shiftwidth=4
2.1 --- a/macrotest.py Sun Jan 16 01:15:01 2005 +0000
2.2 +++ b/macrotest.py Sun Jan 16 01:15:37 2005 +0000
2.3 @@ -5,15 +5,79 @@
2.4 from libxml2dom.macrolib import *
2.5 import libxml2
2.6 import xml.dom
2.7 -import sys
2.8 +
2.9 +class Container:
2.10 + pass
2.11 +
2.12 +doc = """<?xml version="1.0"?>
2.13 +<doc>
2.14 + <element attr="value">
2.15 + <subelement/>
2.16 + </element>
2.17 +</doc>
2.18 +"""
2.19 +
2.20 +def find_root(x2_d):
2.21 + x2_root = None
2.22 +
2.23 + # Property access should be transformed.
2.24 +
2.25 + for x2_n in x2_d.childNodes:
2.26 + if x2_n.nodeType == xml.dom.Node.ELEMENT_NODE:
2.27 + x2_root = x2_n
2.28 + break
2.29 +
2.30 + return x2_root
2.31 +
2.32 +def test():
2.33 + global doc
2.34 +
2.35 + # Assignment should not be transformed.
2.36 +
2.37 + x2_d = parseString(doc)
2.38 +
2.39 + # Not even within containers, and not special names alone.
2.40 +
2.41 + c = Container()
2.42 + c.x2_d = x2_d
2.43
2.44 -x2_d = libxml2.parseFile(sys.argv[1])
2.45 -x2_root = None
2.46 -for x2_n in x2_d.childNodes:
2.47 - if x2_n.nodeType == xml.dom.Node.ELEMENT_NODE:
2.48 - x2_root = x2_n
2.49 - break
2.50 + # Find the root element.
2.51 +
2.52 + x2_root = find_root(x2_d)
2.53 + c.x2_root = x2_root
2.54 +
2.55 + # Create new attributes.
2.56 + # Method access should be transformed.
2.57 +
2.58 + x2_root.setAttributeNS("ns", "xxx:yyy", "zzz")
2.59 + c.x2_root.setAttributeNS("ns", "XXX:YYY", "ZZZ")
2.60 +
2.61 + # Create new elements.
2.62 + # Method access should be transformed.
2.63 +
2.64 + x2_new = x2_d.createElementNS("ns2", "ppp:qqq")
2.65 + x2_root.appendChild(x2_new)
2.66 + x2_new2 = c.x2_d.createElementNS("ns2", "PPP:QQQ")
2.67 + c.x2_root.appendChild(x2_new2)
2.68
2.69 -print x2_root.localName, x2_root.namespaceURI, x2_root.getAttributeNS(None, "xmlns")
2.70 + # Create new elements using ownerDocument.
2.71 + # Chaining properties is not
2.72 +
2.73 + x2_new3 = x2_new.ownerDocument.createElement("fff")
2.74 + x2_new.appendChild(x2_new3)
2.75 + x2_new4 = x2_new2.ownerDocument.createElement("FFF")
2.76 + x2_new2.appendChild(x2_new4)
2.77 +
2.78 + # Create a new document.
2.79 +
2.80 + x2_d2 = createDocument("nsD", "newdoc", None)
2.81 + x2_root2 = find_root(x2_d2)
2.82 +
2.83 + # Attempt to import nodes from the original document.
2.84 +
2.85 + x2_imported = x2_d2.importNode(x2_root, 1)
2.86 + x2_d2.replaceChild(x2_imported, x2_root2)
2.87 +
2.88 + return x2_d, x2_d2
2.89
2.90 # vim: tabstop=4 expandtab shiftwidth=4