Mercurial > mez_xml
changeset 0:3679d2d8443a
Import from CVS and goto mez_xml-0.4
author | Thinker K.F. Li <thinker@branda.to> |
---|---|
date | Wed, 13 Feb 2008 22:33:51 +0800 |
parents | |
children | aa384b04f316 |
files | __init__.py imgs/search_var.dia imgs/usecase.dia mez_xml.py setup.py test/test01.py test/test_temp01.xhtml tools.py |
diffstat | 7 files changed, 510 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mez_xml.py Wed Feb 13 22:33:51 2008 +0800 @@ -0,0 +1,304 @@ +from xml.dom.minidom import parse +import re + +reo_attr_rep = re.compile('\\$\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}') +reserved_keywords = {'commit': None, 'gen_doc': None} + +def clz_name(fn): + from os import path + return path.basename(fn).split('.')[0] + +def _check_reserved(name): + if name.startswith('_') or reserved_keywords.has_key(name): + raise NameError, '%s is a reserved keyword' % (name,) + pass + +class mez_xml(object): + def __init__(self): + super(mez_xml, self).__init__() + pass + + def template_head(self): + self.output_cmd_line('from mez_xml.tools import nez_web') + self.output_cmd_line('') + self.output_cmd_line('class %s(nez_web):' % (self.cname,)) + self.dig() + self.output_cmd_line('def __init__(self, ofo):') + self.dig() + self.output_cmd_line('super(%s, self).__init__()' % (self.cname,)) + self.output_cmd_line('self.ofo = ofo') + self.output_cmd_line('pass') + self.back() + self.output_cmd_line('') + pass + + def template_tail(self): + self.output_cmd_line('pass') + self.back() + self.output_cmd_line('') + pass + + def subtree_start(self, cname): + self.output_cmd_line('def %s(self, pdata, cdata):' % (cname)) + self.dig() + self.output_cmd_line('def temp(data):') + self.dig() + self.frag_start() + pass + + def subtree_stop(self, cname): + self.frag_stop() + self.output_cmd_line('pass') + self.back() + self.output_cmd_line('self._feed_subtree(temp, pdata, cdata)') + self.output_cmd_line('pass') + self.back() + self.output_cmd_line('') + pass + + def _expand_vars(self, data, _tp): + mo = reo_attr_rep.search(data) + pos = 0 + while mo: + if pos != mo.start(): + self.output(data[pos:mo.start()]) + pass + self.frag_stop() + name = mo.group(1) + subnames = name.split('.') + first = subnames[0] + self.output_cmd_line('odata = data.setdefault(\'%s\', {})' % (first)) + for subname in subnames[1:]: + self.output_cmd_line('odata = odata.setdefault(\'%s\', {})' % (subname)) + pass + self.output_cmd_line('self.ofo.write(self._esc_%s(odata))' % (_tp)) + self.frag_start() + pos = mo.end() + for subname in subnames: + _check_reserved(subname) + pass + #if not self.all_names.has_key(name): + #if not self.all_attrnames.has_key(name): + # self.travel_q.append((name, None)) + # self.all_attrnames[name] = None + # pass + #pass + #else: + #raise NameError, '%s is redefined' % (name,) + mo = reo_attr_rep.search(data, pos) + pass + self.output(data[pos:]) + pass + + def _expand_comm(self, data): + self._expand_vars(data, 'comm') + pass + + def _expand_param(self, data): + self._expand_vars(data, 'param') + pass + + def _expand_text(self, data,): + self._expand_vars(data, 'text') + pass + + def _expand_cdata(self, data): + self._expand_vars(data, 'cdata') + pass + + def gen_attrs(self, attrs): + for i in range(attrs.length): + attr = attrs.item(i) + self.output(' ' + attr.name) + val = attr.nodeValue + if val: + self.output('="') + self._expand_param(val) + self.output('"') + pass + pass + pass + + def tag_start(self, no): + from xml.dom import Node + nt = no.nodeType + if nt == Node.CDATA_SECTION_NODE: + self.output('<![CDATA[') + self._expand_cdata(no.data) + self.output(']]>') + elif nt == Node.COMMENT_NODE: + self.output('<!--') + self._expand_comm(no.data); + self.output('-->') + elif nt == Node.TEXT_NODE: + self._expand_text(no.data); + elif nt == Node.ELEMENT_NODE: + self.output('<' + no.tagName) + self.gen_attrs(no.attributes) + self.output('>') + pass + + def tag_stop(self, no): + from xml.dom import Node + nt = no.nodeType + if nt == Node.ELEMENT_NODE: + self.output('</%s>' % (no.tagName,)) + pass + pass + + def frag_start(self): + self.output_cmd('self.ofo.write(\'\'\'') + pass + + def frag_stop(self): + self.output_raw('\'\'\')\n') + pass + + def call_subtree(self, no): + name = no.getAttribute('ezid') + self.frag_stop() + subnames = name.split('.') + first = subnames[0] + self.output_cmd_line('cdata = data.setdefault(\'%s\', {})' % (first)) + for subname in subnames[1:]: + self.output_cmd_line('cdata = cdata.setdefault(\'%s\', {})' % (subname)) + pass + last = subnames[-1] + self.output_cmd_line('self.%s(data, cdata)' % (last)) + self.frag_start() + pass + + def gen_node_template(self, name, node): + from xml.dom import Node + fw_q = [node] + + def trackback(no): + while no != node and no.parentNode and not no.nextSibling: + no = no.parentNode + self.tag_stop(no) + pass + pass + + first = True + while fw_q: + no = fw_q.pop() + if (not first) and no.nodeType == Node.ELEMENT_NODE and no.hasAttributes() and no.hasAttribute('ezid'): + name = no.getAttribute('ezid').split('.')[-1] + _check_reserved(name) + if not self.all_names.has_key(name) and not self.all_attrnames.has_key(name): + self.travel_q.append((name, no)) + self.all_names[name] = None + else: + raise NameError, '%s is redefined' % (name,) + self.call_subtree(no) + trackback(no) + else: + self.tag_start(no) + if not no.hasChildNodes(): + self.tag_stop(no) + trackback(no) + else: + children = [no.childNodes.item(i) for i in range(no.childNodes.length)] + children.reverse() + fw_q.extend(children) + pass + pass + first = False + pass + pass + + def dig(self): + self.indent = self.indent + 4 + pass + + def back(self): + self.indent = self.indent - 4 + pass + + def output_cmd(self, msg): + self.ofo.write(' ' * self.indent) + self.ofo.write(msg) + pass + + def output_cmd_line(self, msg): + self.ofo.write(' ' * self.indent) + self.ofo.write(msg) + self.ofo.write('\n') + pass + + def output_raw(self, msg): + self.ofo.write(msg) + pass + + def output(self, msg): + parts = msg.split('\\') + if len(parts) > 0: + msg = '\\\\'.join(parts) + pass + if len(msg) > 0 and msg[-1] == '\'': + msg = msg[:-1] + '\\\'' + pass + parts = msg.split('\'\'\'') + if len(parts) > 1: + msg = '\\\'\\\'\\\''.join(parts) + pass + self.ofo.write(msg) + pass + + def start(self, fn, ifo, ofo): + self.ofo = ofo + self.indent = 0 + + cname = clz_name(fn) + dom = parse(ifo) + self.travel_q = [('_root', dom)] + self.cname = cname + self.all_names = {} + self.all_attrnames = {} + + self.travel_tree() + pass + + def travel_tree(self): + self.template_head() + + travel_q = self.travel_q + while travel_q: + name, node = travel_q[0] + self.subtree_start(name) + del travel_q[0] + if node: + self.gen_node_template(name, node) + pass + self.subtree_stop(name) + pass + + self.template_tail() + pass + pass + +if __name__ == '__main__': + import sys + import locale + + if len(sys.argv) != 2: + sys.exit(1) + pass + + class fakefile(object): + pass + + fn = sys.argv[1] + fo = file(fn, 'r') + encoding = locale.getpreferredencoding() + stdout = fakefile() + oldwrite = sys.stdout.write + stdout.write = lambda x: oldwrite(x.encode(encoding)) + + if encoding != 'ascii': + print >> stdout, '# -*- coding: %s' % (encoding,) + pass + + mex = mez_xml() + mex.start(fn, fo, stdout) + pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup.py Wed Feb 13 22:33:51 2008 +0800 @@ -0,0 +1,19 @@ +from distutils.core import setup + +setup(name='mez_xml', + version='0.4', + description='A template engine.', + long_description=''' + mez_xm is a template engine. + It generates a Python template module for a XML or XHTML file.''', + author='Thinker K.F. Li', + author_email='thinker@branda.to', + url='https://trac-hg.assembla.com/mez_xml/', + packages=['mez_xml'], + package_dir={'mez_xml': '.'}, + classifiers=['License :: OSI Approved :: BSD License', + 'Programming Language :: Python', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries', + 'Topic :: Software Development :: Libraries'], + license='BSD' +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test01.py Wed Feb 13 22:33:51 2008 +0800 @@ -0,0 +1,16 @@ +from __future__ import with_statement +from test_temp01 import test_temp01 +from sys import stdout + +temp = test_temp01(stdout) +with temp: + temp.NAME = 'foo' + temp.item = 'product' + for i in range(5): + with temp.LOOP: + temp.cost = '$' + str(i) + temp.number = str(i) + pass + pass + pass +temp.commit()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test_temp01.xhtml Wed Feb 13 22:33:51 2008 +0800 @@ -0,0 +1,21 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>test.xhtml</title> +</head> + +<body> +Hello Mr/Miss <span ezid="NAME">NAME</span> +<table> + +<tr><th>Item</th><th>Cost</th><th>Number</th></tr> + +<tr ezid="LOOP"> +<td><span ezid="item">XXX</span></td> +<td>${cost}</td> +<td><input type="text" value="${number}"/></td> +</tr> + +</table> + +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools.py Wed Feb 13 22:33:51 2008 +0800 @@ -0,0 +1,150 @@ + +class scope(object): + def __init__(self, temp, func, name): + super(scope, self).__init__() + self.temp = temp + self.name = name + self.func = func + pass + + def __enter__(self): + temp = self.temp + name = self.name + top = temp._nss[-1] + if top.has_key(name): + if isinstance(top[name], dict): + top[name] = [top[name]] + pass + if isinstance(top[name], list): + ntop = {} + top[name].append(ntop) + pass + pass + else: + ntop = top[name] = {} + pass + temp._nss.append(ntop) + return temp + + def __exit__(self, exc_type, exc_value, traceback): + self.temp._nss.pop() + pass + + def __call__(self, *args, **kws): + return self.func(self.temp, *args, **kws) + + def set_var(self, var): + if not isinstance(var, (dict, list, tuple)): + raise TypeError, 'var should be type of dict, list or tuple' + ntop = self.temp._nss[-1] + ntop[self.name] = var + pass + pass + +class scope_deco(object): + def __init__(self, name, func): + super(scope_deco, self).__init__() + self.func = func + self.name = name + pass + + def __get__(self, temp, owner): + return scope(temp, self.func, self.name) + + def __set__(self, temp, value): + top = temp._nss[-1] + top[self.name] = value + pass + pass + +class new_web_meta(type): + def __init__(clz, name, bases, dict): + super(new_web_meta, clz).__init__(name, bases, dict) + for name in dict.keys(): + if not name.startswith('_'): + func = dict[name] + setattr(clz, name, scope_deco(name, func)) + pass + pass + pass + +class nez_web(object): + __metaclass__ = new_web_meta + + def __init__(self): + super(nez_web, self).__init__() + self.safe = False + pass + + def __enter__(self): + self._rootns = {} + self._nss = [self._rootns] + self._committed = False + self._ready = False + pass + + def __exit__(self, exc_type, exc_value, traceback): + if self._committed: + self._root({}, self._rootns) + pass + self._ready = True + pass + + def _feed_subtree(self, temp, pdata, cdata): + if isinstance(cdata, dict): + data = dict(pdata) + data.update(cdata) + temp(data) + elif isinstance(cdata, list): + for d in cdata: + data = dict(pdata) + data.update(d) + temp(data) + pass + pass + else: + if self.safe: + pass + self.ofo.write(cdata) + pass + pass + + def _esc_param(self, data): + import types + if not (isinstance(data, types.StringTypes) and self.safe): + return str(data) + data = data.replace('&', '&') + data = data.replace('<', '<') + return str(data.replace('"', '')) + + def _esc_comm(self, data): + import types + if not (isinstance(data, types.StringTypes) and self.safe): + return str(data) + return str(data.replace('--', '-d;')) + + def _esc_text(self, data): + import types + if not (isinstance(data, types.StringTypes) and self.safe): + return str(data) + return str(data.replace('&', '&').replace('<', '<').replace('>', '>')) + + def _esc_cdata(self, data): + import types + if not (isinstance(data, types.StringTypes) and self.safe): + return str(data) + return str(data.replace(']]>', ']]>')) + + def commit(self): + assert not self._committed, '%s was committed more than one time.' % (repr(self),) + if self._ready: + self._root({}, self._rootns) + pass + self._committed = True + pass + + def gen_doc(self, data): + self._root({}, data) + pass + pass +