[IMP] make qweb into an openerp object
also attempt to improve/simplify a few implementation details bzr revid: xmo@openerp.com-20131004114155-j2639jx6dwvkz3hh
This commit is contained in:
parent
6507bdd82c
commit
81a82ca37f
|
@ -39,6 +39,7 @@ import ir_config_parameter
|
|||
import osv_memory_autovacuum
|
||||
import ir_mail_server
|
||||
import ir_fields
|
||||
import ir_qweb
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import re
|
||||
|
||||
import werkzeug.utils
|
||||
#from openerp.tools.safe_eval import safe_eval as eval
|
||||
|
||||
import xml # FIXME use lxml
|
||||
import traceback
|
||||
|
@ -32,7 +32,8 @@ BUILTINS = {
|
|||
}
|
||||
|
||||
class QWebContext(dict):
|
||||
def __init__(self, data, undefined_handler=None):
|
||||
def __init__(self, data, undefined_handler=None, loader=None):
|
||||
self.loader = loader
|
||||
self.undefined_handler = undefined_handler
|
||||
dic = BUILTINS.copy()
|
||||
dic.update(data)
|
||||
|
@ -47,7 +48,15 @@ class QWebContext(dict):
|
|||
else:
|
||||
return self.get(key, self.undefined_handler(key, self))
|
||||
|
||||
class QWebXml(object):
|
||||
def copy(self):
|
||||
return QWebContext(dict.copy(self),
|
||||
undefined_handler=self.undefined_handler,
|
||||
loader=self.loader)
|
||||
|
||||
def __copy__(self):
|
||||
return self.copy()
|
||||
|
||||
class QWeb(orm.AbstractModel):
|
||||
"""QWeb Xml templating engine
|
||||
|
||||
The templating engine use a very simple syntax, "magic" xml attributes, to
|
||||
|
@ -63,48 +72,61 @@ class QWebXml(object):
|
|||
|
||||
|
||||
"""
|
||||
def __init__(self, loader=None, undefined_handler=None):
|
||||
self.loader = loader
|
||||
self.undefined_handler = undefined_handler
|
||||
self.node = xml.dom.Node
|
||||
self._t = {}
|
||||
self._render_tag = {}
|
||||
self._format_regex = re.compile('(#\{(.*?)\})|(\{\{(.*?)\}\})')
|
||||
self._void_elements = set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
|
||||
'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'])
|
||||
prefix = 'render_tag_'
|
||||
for i in [j for j in dir(self) if j.startswith(prefix)]:
|
||||
name = i[len(prefix):].replace('_', '-')
|
||||
self._render_tag[name] = getattr(self.__class__, i)
|
||||
|
||||
self._render_att = {}
|
||||
prefix = 'render_att_'
|
||||
for i in [j for j in dir(self) if j.startswith(prefix)]:
|
||||
name = i[len(prefix):].replace('_', '-')
|
||||
self._render_att[name] = getattr(self.__class__, i)
|
||||
_name = 'ir.templating.qweb'
|
||||
|
||||
node = xml.dom.Node
|
||||
_void_elements = frozenset([
|
||||
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
|
||||
'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'])
|
||||
_format_regex = re.compile('(#\{(.*?)\})|(\{\{(.*?)\}\})')
|
||||
|
||||
def __init__(self, pool, cr):
|
||||
super(QWeb, self).__init__(pool, cr)
|
||||
self._t = {}
|
||||
|
||||
self._render_tag = self.prefixed_methods('render_tag_')
|
||||
self._render_att = self.prefixed_methods('render_att_')
|
||||
|
||||
def prefixed_methods(self, prefix):
|
||||
""" Extracts all methods prefixed by ``prefix``, and returns a mapping
|
||||
of (t-name, method) where the t-name is the method name with prefix
|
||||
removed and underscore converted to dashes
|
||||
|
||||
:param str prefix:
|
||||
:return: dict
|
||||
"""
|
||||
return dict(
|
||||
(name[len(prefix):].replace('_', '-'), getattr(type(self), name))
|
||||
for name in dir(self)
|
||||
if name.startswith(prefix))
|
||||
|
||||
def register_tag(self, tag, func):
|
||||
self._render_tag[tag] = func
|
||||
|
||||
def add_template(self, x):
|
||||
def load_document(self, x):
|
||||
"""
|
||||
Loads an XML document and installs any contained template in the engine
|
||||
"""
|
||||
if hasattr(x, 'documentElement'):
|
||||
dom = x
|
||||
elif x.startswith("<?xml"):
|
||||
dom = xml.dom.minidom.parseString(x)
|
||||
else:
|
||||
dom = xml.dom.minidom.parse(x)
|
||||
|
||||
for n in dom.documentElement.childNodes:
|
||||
if n.nodeType == 1 and n.getAttribute('t-name'):
|
||||
if n.nodeType == self.node.ELEMENT_NODE and n.getAttribute('t-name'):
|
||||
self._t[str(n.getAttribute("t-name"))] = n
|
||||
|
||||
def get_template(self, name):
|
||||
def get_template(self, name, context):
|
||||
if context.loader and name not in self._t:
|
||||
xml_doc = context.loader(name)
|
||||
self.load_document(xml_doc)
|
||||
|
||||
if name in self._t:
|
||||
return self._t[name]
|
||||
elif self.loader:
|
||||
xml = self.loader(name)
|
||||
self.add_template(xml)
|
||||
if name in self._t:
|
||||
return self._t[name]
|
||||
|
||||
raise KeyError('qweb: template "%s" not found' % name)
|
||||
|
||||
def eval(self, expr, v):
|
||||
|
@ -147,17 +169,18 @@ class QWebXml(object):
|
|||
else:
|
||||
return 0
|
||||
|
||||
def render(self, tname, v=None, out=None):
|
||||
def render(self, tname, v=None, out=None, loader=None, undefined_handler=None):
|
||||
if v is None:
|
||||
v = {}
|
||||
if not isinstance(v, QWebContext):
|
||||
v = QWebContext(v, undefined_handler=undefined_handler, loader=loader)
|
||||
v['__template__'] = tname
|
||||
stack = v.get('__stack__', [])
|
||||
if stack:
|
||||
v['__caller__'] = stack[-1]
|
||||
stack.append(tname)
|
||||
v['__stack__'] = stack
|
||||
v = QWebContext(v, self.undefined_handler)
|
||||
return self.render_node(self.get_template(tname), v)
|
||||
return self.render_node(self.get_template(tname, v), v)
|
||||
|
||||
def render_node(self, e, v):
|
||||
r = ""
|
||||
|
@ -189,8 +212,7 @@ class QWebXml(object):
|
|||
debugger = t_att.get('debug', 'pdb')
|
||||
__import__(debugger).set_trace() # pdb, ipdb, pudb, ...
|
||||
if t_render:
|
||||
if t_render in self._render_tag:
|
||||
r = self._render_tag[t_render](self, e, t_att, g_att, v)
|
||||
r = self._render_tag[t_render](self, e, t_att, g_att, v)
|
||||
else:
|
||||
r = self.render_element(e, t_att, g_att, v)
|
||||
if isinstance(r, unicode):
|
||||
|
@ -275,7 +297,7 @@ class QWebXml(object):
|
|||
enum = self.eval_object(expr, v)
|
||||
if enum is not None:
|
||||
var = t_att.get('as', expr).replace('.', '_')
|
||||
d = QWebContext(v.copy(), self.undefined_handler)
|
||||
d = v.copy()
|
||||
size = -1
|
||||
if isinstance(enum, (list, tuple)):
|
||||
size = len(enum)
|
||||
|
@ -313,11 +335,9 @@ class QWebXml(object):
|
|||
return ""
|
||||
|
||||
def render_tag_call(self, e, t_att, g_att, v):
|
||||
if "import" in t_att:
|
||||
d = v
|
||||
else:
|
||||
d = QWebContext(v.copy(), self.undefined_handler)
|
||||
d = v if 'import' in t_att else v.copy()
|
||||
d[0] = self.render_element(e, t_att, g_att, d)
|
||||
|
||||
return self.render(self.eval_format(t_att["call"], d), d)
|
||||
|
||||
def render_tag_set(self, e, t_att, g_att, v):
|
|
@ -22,20 +22,17 @@ import collections
|
|||
import copy
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import time
|
||||
|
||||
import HTMLParser
|
||||
from lxml import etree, html
|
||||
from functools import partial
|
||||
from lxml import etree
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv, orm
|
||||
from openerp.tools import graph, SKIPPED_ELEMENT_TYPES
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.view_validation import valid_view
|
||||
from openerp.tools import misc, qweb
|
||||
from openerp.tools import misc
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -764,8 +761,10 @@ class view(osv.osv):
|
|||
def loader(name):
|
||||
return self.read_template(cr, uid, name, context=context)
|
||||
|
||||
engine = qweb.QWebXml(loader=loader, undefined_handler=lambda key, v: None)
|
||||
return engine.render(id_or_xml_id, values)
|
||||
engine = self.pool['ir.templating.qweb']
|
||||
return engine.render(
|
||||
id_or_xml_id, values,
|
||||
loader=loader, undefined_handler=lambda key, v: None)
|
||||
|
||||
# maybe used to print the workflow ?
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ from xml.dom import minidom as dom
|
|||
|
||||
import common
|
||||
|
||||
from ..tools import qweb
|
||||
|
||||
impl = dom.getDOMImplementation()
|
||||
document = impl.createDocument(None, None, None)
|
||||
|
||||
|
@ -20,7 +18,7 @@ class RegistryProxy(object):
|
|||
class TestQWebTField(common.TransactionCase):
|
||||
def setUp(self):
|
||||
super(TestQWebTField, self).setUp()
|
||||
self.engine = qweb.QWebXml()
|
||||
self.engine = self.registry('ir.templating.qweb')
|
||||
|
||||
def test_trivial(self):
|
||||
field = document.createElement('span')
|
||||
|
|
Loading…
Reference in New Issue