--- a/ext/tal.py Mon Jan 04 18:40:30 2016 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,273 +0,0 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""provides simpleTAL extensions for CubicWeb
-
-"""
-
-__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 as unierror:
- LOGGER.exception(str(unierror))
- exc = simpleTALES.ContextContentException(
- "found non-unicode %r string in Context!" % unierror.args[1])
- exc.__traceback__ = sys.exc_info()[-1]
- raise exc
-
-
-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 = open(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 as ex:
- ex = ex.__class__('in %r: %s' % (expr, ex))
- ex.__traceback__ = sys.exc_info()[-1]
- raise ex
- 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.appobjects_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)