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
Binary file imgs/search_var.dia has changed
Binary file imgs/usecase.dia has changed
--- /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('&', '&amp;')
+	data = data.replace('<', '&lt;')
+	return str(data.replace('"', '&#22;'))
+    
+    def _esc_comm(self, data):
+	import types
+	if not (isinstance(data, types.StringTypes) and self.safe):
+	    return str(data)
+	return str(data.replace('--', '-&#2d;'))
+    
+    def _esc_text(self, data):
+	import types
+	if not (isinstance(data, types.StringTypes) and self.safe):
+	    return str(data)
+	return str(data.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;'))
+    
+    def _esc_cdata(self, data):
+	import types
+	if not (isinstance(data, types.StringTypes) and self.safe):
+	    return str(data)
+	return str(data.replace(']]>', ']]&gt;'))
+    
+    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
+