common/tal.py
changeset 1808 aa09e20dd8c0
parent 1693 49075f57cf2c
parent 1807 6d541c610165
child 1810 e95e876be17c
--- a/common/tal.py	Tue May 05 17:18:49 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,256 +0,0 @@
-"""provides simpleTAL extensions for CubicWeb
-
-:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-
-__docformat__ = "restructuredtext en"
-
-import sys
-import re
-from os.path import exists, isdir, join
-from logging import getLogger
-from StringIO import StringIO
-        
-from simpletal import simpleTAL, simpleTALES
-
-from logilab.common.decorators import cached
-
-LOGGER = getLogger('cubicweb.tal')
-
-
-class LoggerAdapter(object):
-    def __init__(self, tal_logger):
-        self.tal_logger = tal_logger
-        
-    def debug(self, msg):
-        LOGGER.debug(msg)
-
-    def warn(self, msg):
-        LOGGER.warning(msg)
-
-    def __getattr__(self, attrname):
-        return getattr(self.tal_logger, attrname)
-
-
-class CubicWebContext(simpleTALES.Context):
-    """add facilities to access entity / resultset"""
-
-    def __init__(self, options=None, allowPythonPath=1):
-        simpleTALES.Context.__init__(self, options, allowPythonPath)
-        self.log = LoggerAdapter(self.log)
-
-    def update(self, context):
-        for varname, value in context.items():
-            self.addGlobal(varname, value)
-
-    def addRepeat(self, name, var, initialValue):
-        simpleTALES.Context.addRepeat(self, name, var, initialValue)
-
-# XXX FIXME need to find a clean to define OPCODE values for extensions
-I18N_CONTENT = 18  
-I18N_REPLACE = 19
-RQL_EXECUTE  = 20
-# simpleTAL uses the OPCODE values to define priority over commands.
-# TAL_ITER should have the same priority than TAL_REPEAT (i.e. 3), but
-# we can't use the same OPCODE for two different commands without changing
-# the simpleTAL implementation. Another solution would be to totally override
-# the REPEAT implementation with the ITER one, but some specific operations
-# (involving len() for instance) are not implemented for ITER, so we prefer
-# to keep both implementations for now, and to fool simpleTAL by using a float
-# number between 3 and 4
-TAL_ITER     = 3.1
-
-
-# FIX simpleTAL HTML 4.01 stupidity
-# (simpleTAL never closes tags like INPUT, IMG, HR ...)
-simpleTAL.HTML_FORBIDDEN_ENDTAG.clear()
-
-class CubicWebTemplateCompiler(simpleTAL.HTMLTemplateCompiler):
-    """extends default compiler by adding i18n:content commands"""
-
-    def __init__(self):
-        simpleTAL.HTMLTemplateCompiler.__init__(self)
-        self.commandHandler[I18N_CONTENT] = self.compile_cmd_i18n_content
-        self.commandHandler[I18N_REPLACE] = self.compile_cmd_i18n_replace
-        self.commandHandler[RQL_EXECUTE] = self.compile_cmd_rql
-        self.commandHandler[TAL_ITER] = self.compile_cmd_tal_iter
-
-    def setTALPrefix(self, prefix):
-        simpleTAL.TemplateCompiler.setTALPrefix(self, prefix)
-        self.tal_attribute_map['i18n:content'] = I18N_CONTENT
-        self.tal_attribute_map['i18n:replace'] = I18N_REPLACE
-        self.tal_attribute_map['rql:execute'] = RQL_EXECUTE
-        self.tal_attribute_map['tal:iter'] = TAL_ITER
-
-    def compile_cmd_i18n_content(self, argument):
-        # XXX tal:content structure=, text= should we support this ?
-        structure_flag = 0
-        return (I18N_CONTENT, (argument, False, structure_flag, self.endTagSymbol))
-
-    def compile_cmd_i18n_replace(self, argument):
-        # XXX tal:content structure=, text= should we support this ?
-        structure_flag = 0
-        return (I18N_CONTENT, (argument, True, structure_flag, self.endTagSymbol))
-
-    def compile_cmd_rql(self, argument):
-        return (RQL_EXECUTE, (argument, self.endTagSymbol))
-
-    def compile_cmd_tal_iter(self, argument):
-        original_id, (var_name, expression, end_tag_symbol) = \
-                     simpleTAL.HTMLTemplateCompiler.compileCmdRepeat(self, argument)
-        return (TAL_ITER, (var_name, expression, self.endTagSymbol))
-
-    def getTemplate(self):
-        return CubicWebTemplate(self.commandList, self.macroMap, self.symbolLocationTable)
-
-    def compileCmdAttributes (self, argument):
-        """XXX modified to support single attribute
-        definition ending by a ';'
-
-        backport this to simpleTAL
-        """
-        # Compile tal:attributes into attribute command
-        # Argument: [(attributeName, expression)]
-        
-        # Break up the list of attribute settings first
-        commandArgs = []
-        # We only want to match semi-colons that are not escaped
-        argumentSplitter =  re.compile(r'(?<!;);(?!;)')
-        for attributeStmt in argumentSplitter.split(argument):
-            if not attributeStmt.strip():
-                continue
-            #  remove any leading space and un-escape any semi-colons
-            attributeStmt = attributeStmt.lstrip().replace(';;', ';')
-            # Break each attributeStmt into name and expression
-            stmtBits = attributeStmt.split(' ')
-            if (len (stmtBits) < 2):
-                # Error, badly formed attributes command
-                msg = "Badly formed attributes command '%s'.  Attributes commands must be of the form: 'name expression[;name expression]'" % argument
-                self.log.error(msg)
-                raise simpleTAL.TemplateParseException(self.tagAsText(self.currentStartTag), msg)
-            attName = stmtBits[0]
-            attExpr = " ".join(stmtBits[1:])
-            commandArgs.append((attName, attExpr))
-        return (simpleTAL.TAL_ATTRIBUTES, commandArgs)
-
-
-class CubicWebTemplateInterpreter(simpleTAL.TemplateInterpreter):
-    """provides implementation for interpreting cubicweb extensions"""
-    def __init__(self):
-        simpleTAL.TemplateInterpreter.__init__(self)
-        self.commandHandler[I18N_CONTENT] = self.cmd_i18n
-        self.commandHandler[TAL_ITER] = self.cmdRepeat
-        # self.commandHandler[RQL_EXECUTE] = self.cmd_rql
-
-    def cmd_i18n(self, command, args):
-        """i18n:content and i18n:replace implementation"""
-        string, replace_flag, structure_flag, end_symbol = args
-        if replace_flag:
-            self.outputTag = 0
-        result = self.context.globals['_'](string)
-        self.tagContent = (0, result)
-        self.movePCForward = self.symbolTable[end_symbol]
-        self.programCounter += 1
-
-
-class CubicWebTemplate(simpleTAL.HTMLTemplate):
-    """overrides HTMLTemplate.expand() to systematically use CubicWebInterpreter
-    """
-    def expand(self, context, outputFile):
-        interpreter = CubicWebTemplateInterpreter()
-        interpreter.initialise(context, outputFile)
-        simpleTAL.HTMLTemplate.expand(self, context, outputFile,# outputEncoding='unicode',
-                                      interpreter=interpreter)
-
-    def expandInline(self, context, outputFile, interpreter):
-        """ Internally used when expanding a template that is part of a context."""
-        try:
-            interpreter.execute(self)
-        except UnicodeError, unierror:
-            LOGGER.exception(str(unierror))
-            raise simpleTALES.ContextContentException("found non-unicode %r string in Context!" % unierror.args[1]), None, sys.exc_info()[-1]
-
-
-def compile_template(template):
-    """compiles a TAL template string
-    :type template: unicode
-    :param template: a TAL-compliant template string
-    """
-    string_buffer = StringIO(template)
-    compiler = CubicWebTemplateCompiler()
-    compiler.parseTemplate(string_buffer) # , inputEncoding='unicode')
-    return compiler.getTemplate()
-
-
-def compile_template_file(filepath):
-    """compiles a TAL template file
-    :type filepath: str
-    :param template: path of the file to compile 
-    """
-    fp = file(filepath)
-    file_content = unicode(fp.read()) # template file should be pure ASCII
-    fp.close()
-    return compile_template(file_content)
-
-
-def evaluatePython (self, expr):
-    if not self.allowPythonPath:
-        return self.false
-    globals = {}
-    for name, value in self.globals.items():
-        if isinstance (value, simpleTALES.ContextVariable):
-            value = value.rawValue()
-        globals[name] = value
-    globals['path'] = self.pythonPathFuncs.path
-    globals['string'] = self.pythonPathFuncs.string
-    globals['exists'] = self.pythonPathFuncs.exists
-    globals['nocall'] = self.pythonPathFuncs.nocall
-    globals['test'] = self.pythonPathFuncs.test
-    locals = {}
-    for name, value in self.locals.items():
-        if (isinstance (value, simpleTALES.ContextVariable)):
-            value = value.rawValue()
-        locals[name] = value
-    # XXX precompile expr will avoid late syntax error
-    try:
-        result = eval(expr, globals, locals)
-    except Exception, ex:
-        ex = ex.__class__('in %r: %s' % (expr, ex))
-        raise ex, None, sys.exc_info()[-1]
-    if (isinstance (result, simpleTALES.ContextVariable)):
-        return result.value()
-    return result
-
-simpleTALES.Context.evaluatePython = evaluatePython
-
-
-class talbased(object):
-    def __init__(self, filename, write=True):
-##         if not osp.isfile(filepath):
-##             # print "[tal.py] just for tests..."
-##             # get parent frame
-##             directory = osp.abspath(osp.dirname(sys._getframe(1).f_globals['__file__']))
-##             filepath = osp.join(directory, filepath)
-        self.filename = filename
-        self.write = write
-
-    def __call__(self, viewfunc):
-        def wrapped(instance, *args, **kwargs):
-            variables = viewfunc(instance, *args, **kwargs)
-            html = instance.tal_render(self._compiled_template(instance), variables)
-            if self.write:
-                instance.w(html)
-            else:
-                return html
-        return wrapped
-
-    def _compiled_template(self, instance):
-        for fileordirectory in instance.config.vregistry_path():
-            filepath = join(fileordirectory, self.filename)
-            if isdir(fileordirectory) and exists(filepath):
-                return compile_template_file(filepath)
-        raise Exception('no such template %s' % self.filename)
-    _compiled_template = cached(_compiled_template, 0)
-