[Zope3-checkins] CVS: Zope3/src/zope/pagetemplate - __init__.py:1.1.2.1 engine.py:1.1.2.1 expressions.py:1.1.2.1 interfaces.py:1.1.2.1 iterator.py:1.1.2.1 pagetemplate.py:1.1.2.1 pagetemplatefile.py:1.1.2.1 pythonexpr.py:1.1.2.1 readme.txt:1.1.2.1 safemapping.py:1.1.2.1 tales.py:1.1.2.1
Jim Fulton
jim@zope.com
Mon, 23 Dec 2002 14:33:00 -0500
Update of /cvs-repository/Zope3/src/zope/pagetemplate
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/pagetemplate
Added Files:
Tag: NameGeddon-branch
__init__.py engine.py expressions.py interfaces.py iterator.py
pagetemplate.py pagetemplatefile.py pythonexpr.py readme.txt
safemapping.py tales.py
Log Message:
Initial renaming before debugging
=== Added File Zope3/src/zope/pagetemplate/__init__.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Zope 3 compatible ZTUtils."""
from zope.pagetemplate.iterator import Iterator
=== Added File Zope3/src/zope/pagetemplate/engine.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Expression engine configuration and registration.
Each expression engine can have its own expression types and base names.
$Id: engine.py,v 1.1.2.1 2002/12/23 19:32:58 jim Exp $
"""
from zope.pagetemplate.tales import ExpressionEngine, RegistrationError
from zope.pagetemplate.expressions import PathExpr, StringExpr, NotExpr, DeferExpr
from zope.pagetemplate.expressions import SimpleModuleImporter
from zope.pagetemplate.pythonexpr import PythonExpr
def Engine():
e = ExpressionEngine()
reg = e.registerType
for pt in PathExpr._default_type_names:
reg(pt, PathExpr)
reg('string', StringExpr)
reg('python', PythonExpr)
reg('not', NotExpr)
reg('defer', DeferExpr)
e.registerBaseName('modules', SimpleModuleImporter())
return e
Engine = Engine()
=== Added File Zope3/src/zope/pagetemplate/expressions.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Basic Page Template expression types.
$Id: expressions.py,v 1.1.2.1 2002/12/23 19:32:58 jim Exp $
"""
__metaclass__ = type # All classes are new style when run with Python 2.2+
import re, sys
from types import StringTypes
from zope.pagetemplate.tales import ExpressionEngine, CompilerError, RegistrationError
from zope.pagetemplate.tales import _valid_name, _parse_expr, NAME_RE, Undefined
from zope.pagetemplate.pythonexpr import PythonExpr
Undefs = (Undefined, AttributeError, KeyError,
TypeError, IndexError)
_marker = object()
def simpleTraverse(object, path_items, econtext):
"""Traverses a sequence of names, first trying attributes then items.
"""
for name in path_items:
next = getattr(object, name, _marker)
if next is not _marker:
object = next
elif hasattr(object, '__getitem__'):
object = object[name]
else:
raise NameError, name
return object
class SubPathExpr:
def __init__(self, path, traverser):
self._path = path = str(path).strip().split('/')
self._base = base = path.pop(0)
self._traverser = traverser
if not _valid_name(base):
raise CompilerError, 'Invalid variable name "%s"' % base
# Parse path
self._dp = dp = []
for i in range(len(path)):
e = path[i]
if e[:1] == '?' and _valid_name(e[1:]):
dp.append((i, e[1:]))
dp.reverse()
def _eval(self, econtext,
list=list, isinstance=isinstance):
vars = econtext.vars
path = self._path
if self._dp:
path = list(path) # Copy!
for i, varname in self._dp:
val = vars[varname]
if isinstance(val, StringTypes):
path[i] = val
else:
# If the value isn't a string, assume it's a sequence
# of path names.
path[i:i+1] = list(val)
base = self._base
if base == 'CONTEXTS': # Special base name
ob = econtext.contexts
else:
ob = vars[base]
if isinstance(ob, DeferWrapper):
ob = ob()
if path:
ob = self._traverser(ob, path, econtext)
return ob
class PathExpr:
"""One or more subpath expressions, separated by '|'.
"""
# _default_type_names contains the expression type names this
# class is usually registered for.
_default_type_names = (
'standard',
'path',
'exists',
'nocall',
)
def __init__(self, name, expr, engine, traverser=simpleTraverse):
self._s = expr
self._name = name
paths = expr.split('|')
self._subexprs = []
add = self._subexprs.append
for i in range(len(paths)):
path = paths[i].lstrip()
if _parse_expr(path):
# This part is the start of another expression type,
# so glue it back together and compile it.
add(engine.compile('|'.join(paths[i:]).lstrip()))
break
add(SubPathExpr(path, traverser)._eval)
def _exists(self, econtext):
for expr in self._subexprs:
try:
expr(econtext)
except Undefs:
pass
else:
return 1
return 0
def _eval(self, econtext):
for expr in self._subexprs[:-1]:
# Try all but the last subexpression, skipping undefined ones.
try:
ob = expr(econtext)
except Undefs:
pass
else:
break
else:
# On the last subexpression allow exceptions through.
ob = self._subexprs[-1](econtext)
if self._name == 'nocall':
return ob
# Call the object if it is callable.
if hasattr(ob, '__call__'):
return ob()
return ob
def __call__(self, econtext):
if self._name == 'exists':
return self._exists(econtext)
return self._eval(econtext)
def __str__(self):
return '%s expression (%s)' % (self._name, `self._s`)
def __repr__(self):
return '<PathExpr %s:%s>' % (self._name, `self._s`)
_interp = re.compile(r'\$(%(n)s)|\${(%(n)s(?:/[^}]*)*)}' % {'n': NAME_RE})
class StringExpr:
def __init__(self, name, expr, engine):
self._s = expr
if '%' in expr:
expr = expr.replace('%', '%%')
self._vars = vars = []
if '$' in expr:
# Use whatever expr type is registered as "path".
path_type = engine.getTypes()['path']
parts = []
for exp in expr.split('$$'):
if parts: parts.append('$')
m = _interp.search(exp)
while m is not None:
parts.append(exp[:m.start()])
parts.append('%s')
vars.append(path_type(
'path', m.group(1) or m.group(2), engine))
exp = exp[m.end():]
m = _interp.search(exp)
if '$' in exp:
raise CompilerError, (
'$ must be doubled or followed by a simple path')
parts.append(exp)
expr = ''.join(parts)
self._expr = expr
def __call__(self, econtext):
vvals = []
for var in self._vars:
v = var(econtext)
vvals.append(v)
return self._expr % tuple(vvals)
def __str__(self):
return 'string expression (%s)' % `self._s`
def __repr__(self):
return '<StringExpr %s>' % `self._s`
class NotExpr:
def __init__(self, name, expr, engine):
self._s = expr = expr.lstrip()
self._c = engine.compile(expr)
def __call__(self, econtext):
return int(not econtext.evaluateBoolean(self._c))
def __repr__(self):
return '<NotExpr %s>' % `self._s`
class DeferWrapper:
def __init__(self, expr, econtext):
self._expr = expr
self._econtext = econtext
def __str__(self):
return str(self())
def __call__(self):
return self._expr(self._econtext)
class DeferExpr:
def __init__(self, name, expr, compiler):
self._s = expr = expr.lstrip()
self._c = compiler.compile(expr)
def __call__(self, econtext):
return DeferWrapper(self._c, econtext)
def __repr__(self):
return '<DeferExpr %s>' % `self._s`
class SimpleModuleImporter:
"""Minimal module importer with no security."""
def __getitem__(self, module):
mod = __import__(module)
path = module.split('.')
for name in path[1:]:
mod = getattr(mod, name)
return mod
=== Added File Zope3/src/zope/pagetemplate/interfaces.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interface that describes the 'macros' attribute of a PageTemplate.
$Id: interfaces.py,v 1.1.2.1 2002/12/23 19:32:58 jim Exp $
"""
from zope.interface import Interface
from zope.interface.element import Attribute
class IMacrosAttribute(Interface):
macros = Attribute("An object that implements the __getitem__ "
"protocol, containing page template macros.")
=== Added File Zope3/src/zope/pagetemplate/iterator.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
__doc__='''Iterator class
$Id: iterator.py,v 1.1.2.1 2002/12/23 19:32:58 jim Exp $'''
__version__='$Revision: 1.1.2.1 $'[11:-2]
class Iterator:
'''Simple Iterator class'''
__allow_access_to_unprotected_subobjects__ = 1
def __init__(self, seq):
self.seq = seq
self.nextIndex = 0
def next(self):
i = self.nextIndex
try:
self.seq[i]
except IndexError:
return 0
self.index = i
self.nextIndex = i+1
return 1
def number(self): return self.nextIndex
def even(self): return not self.index % 2
def odd(self): return self.index % 2
def letter(self, base=ord('a'), radix=26):
index = self.index
s = ''
while 1:
index, off = divmod(index, radix)
s = chr(base + off) + s
if not index: return s
def Letter(self):
return self.letter(base=ord('A'))
def Roman(self, rnvalues=(
(1000,'M'),(900,'CM'),(500,'D'),(400,'CD'),
(100,'C'),(90,'XC'),(50,'L'),(40,'XL'),
(10,'X'),(9,'IX'),(5,'V'),(4,'IV'),(1,'I')) ):
n = self.index + 1
s = ''
for v, r in rnvalues:
rct, n = divmod(n, v)
s = s + r * rct
return s
def roman(self):
return self.Roman.lower()
def start(self): return self.nextIndex == 1
def end(self):
try: self.seq[self.nextIndex]
except IndexError: return 1
return 0
def item(self):
return self.seq[self.index]
def length(self):
return len(self.seq)
=== Added File Zope3/src/zope/pagetemplate/pagetemplate.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Page Template module
HTML- and XML-based template objects using TAL, TALES, and METAL.
$Id: pagetemplate.py,v 1.1.2.1 2002/12/23 19:32:58 jim Exp $
"""
__metaclass__ = type # All classes are new style when run with Python 2.2+
import sys
from zope.tal.talparser import TALParser
from zope.tal.htmltalparser import HTMLTALParser
from zope.tal.talgenerator import TALGenerator
from zope.tal.talinterpreter import TALInterpreter
from zope.pagetemplate.engine import Engine
from zope.pagetemplate.interfaces import IMacrosAttribute
# Don't use cStringIO here! It's not unicode aware.
from StringIO import StringIO
class MacroCollection:
def __get__(self, parent, type=None):
parent._cook_check()
return parent._v_macros
_default_options = {}
class PageTemplate:
"""Page Templates using TAL, TALES, and METAL.
Subclassing
-----------
The following methods have certain internal responsibilities.
pt_getContext(**keywords)
Should ignore keyword arguments that it doesn't care about,
and construct the namespace passed to the TALES expression
engine. This method is free to use the keyword arguments it
receives.
pt_render(namespace, source=0)
Responsible the TAL interpreter to perform the rendering. The
namespace argument is a mapping which defines the top-level
namespaces passed to the TALES expression engine.
__call__(*args, **keywords)
Calls pt_getContext() to construct the top-level namespace
passed to the TALES expression engine, then calls pt_render()
to perform the rendering.
"""
# XXX this breaks something in the Zope3 views registries.
# Temporarily removed. SteveA 2002-10-22
#__implements__ = IMacrosAttribute
content_type = 'text/html'
expand = 1
_v_errors = ()
_v_warnings = ()
_v_program = None
_v_macros = None
_v_cooked = 0
_text = ''
_engine_name = 'default'
_error_start = '<!-- Page Template Diagnostics'
macros = MacroCollection()
def pt_edit(self, text, content_type):
if content_type:
self.content_type = str(content_type)
if hasattr(text, 'read'):
text = text.read()
self.write(text)
def pt_getContext(self, args=(), options=_default_options, **ignored):
rval = {'template': self,
'options': options,
'args': args,
'nothing': None,
}
rval.update(self.pt_getEngine().getBaseNames())
return rval
def __call__(self, *args, **kwargs):
return self.pt_render(self.pt_getContext(args, kwargs))
pt_getEngineContext = Engine.getContext
def pt_getEngine(self):
return Engine
def pt_render(self, namespace, source=0):
"""Render this Page Template"""
self._cook_check()
__traceback_supplement__ = (PageTemplateTracebackSupplement,
self, namespace)
if self._v_errors:
raise PTRuntimeError(str(self._v_errors))
output = StringIO(u'')
context = self.pt_getEngineContext(namespace)
TALInterpreter(self._v_program, self._v_macros,
context, output, tal=not source, strictinsert=0)()
return output.getvalue()
def pt_errors(self, namespace):
self._cook_check()
err = self._v_errors
if err:
return err
try:
self.pt_render(namespace, source=1)
except:
return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
def pt_warnings(self):
self._cook_check()
return self._v_warnings
def write(self, text):
assert isinstance(text, str)
if text.startswith(self._error_start):
errend = text.find('-->')
if errend >= 0:
text = text[errend + 4:]
if self._text != text:
self._text = text
# XXX can this be done only if we changed self._text?
self._cook()
def read(self):
"""Gets the source, sometimes with macros expanded."""
self._cook_check()
if not self._v_errors:
if not self.expand:
return self._text
try:
# XXX not clear how this ever gets called, but the
# first arg to pt_render() needs to change if it ever does.
return self.pt_render({}, source=1)
except:
return ('%s\n Macro expansion failed\n %s\n-->\n%s' %
(self._error_start, "%s: %s" % sys.exc_info()[:2],
self._text) )
return ('%s\n %s\n-->\n%s' % (self._error_start,
'\n'.join(self._v_errors),
self._text))
def pt_source_file(self):
"""To be overridden."""
return None
def _cook_check(self):
if not self._v_cooked:
self._cook()
def _cook(self):
"""Compile the TAL and METAL statments.
Cooking must not fail due to compilation errors in templates.
"""
engine = self.pt_getEngine()
source_file = self.pt_source_file()
if self.html():
gen = TALGenerator(engine, xml=0, source_file=source_file)
parser = HTMLTALParser(gen)
else:
gen = TALGenerator(engine, source_file=source_file)
parser = TALParser(gen)
self._v_errors = ()
try:
parser.parseString(self._text)
self._v_program, self._v_macros = parser.getCode()
except:
self._v_errors = ["Compilation failed",
"%s: %s" % sys.exc_info()[:2]]
self._v_warnings = parser.getWarnings()
self._v_cooked = 1
def html(self):
if not hasattr(self, 'is_html'):
return self.content_type == 'text/html'
return self.is_html
class PTRuntimeError(RuntimeError):
'''The Page Template has template errors that prevent it from rendering.'''
pass
class PageTemplateTracebackSupplement:
#__implements__ = ITracebackSupplement
def __init__(self, pt, namespace):
self.manageable_object = pt
try:
w = pt.pt_warnings()
except: # We're already trying to report an error, don't make another.
w = ()
e = pt.pt_errors(namespace)
if e:
w = list(w) + list(e)
self.warnings = w
=== Added File Zope3/src/zope/pagetemplate/pagetemplatefile.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Filesystem Page Template module
Zope object encapsulating a Page Template from the filesystem.
"""
__metaclass__ = type
__version__ = '$Revision: 1.1.2.1 $'[11:-2]
import os, sys
import logging
from zope.pagetemplate.pagetemplate import PageTemplate
def package_home(gdict):
filename = gdict["__file__"]
return os.path.dirname(filename)
class PageTemplateFile(PageTemplate):
"Zope wrapper for filesystem Page Template using TAL, TALES, and METAL"
_v_last_read = 0
def __init__(self, filename, _prefix=None):
if not isinstance(_prefix, str):
if _prefix is None:
_prefix = sys._getframe(1).f_globals
_prefix = package_home(_prefix)
self.filename = os.path.join(_prefix, filename)
def _cook_check(self):
if self._v_last_read and not __debug__:
return
__traceback_info__ = self.filename
try:
mtime = os.path.getmtime(self.filename)
except OSError:
mtime = 0
if self._v_program is not None and mtime == self._v_last_read:
return
self.pt_edit(open(self.filename), None)
self._cook()
if self._v_errors:
logging.error('PageTemplateFile: Error in template: %s',
'\n'.join(self._v_errors))
return
self._v_last_read = mtime
def document_src(self, REQUEST=None):
"""Return expanded document source."""
if REQUEST is not None:
REQUEST.response.setHeader('Content-Type', self.content_type)
return self.read()
def pt_source_file(self):
return self.filename
def __getstate__(self):
raise TypeError("non-picklable object")
=== Added File Zope3/src/zope/pagetemplate/pythonexpr.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Generic Python Expression Handler"""
__version__ = '$Revision: 1.1.2.1 $'[11:-2]
class PythonExpr:
def __init__(self, name, expr, engine):
text = expr.replace('\n', ' ').strip()
self.text = text
# The next line can legally raise SyntaxError.
self._code = code = compile(text, '<string>', 'eval')
self._varnames = code.co_names
def _bind_used_names(self, econtext, builtins):
# Bind template variables
names = {}
vars = econtext.vars
marker = self
for vname in self._varnames:
val = vars.get(vname, marker)
if val is not marker:
names[vname] = val
elif vname not in builtins:
# Fall back to using expression types as variable values.
val = econtext._engine.getTypes().get(vname, marker)
if val is not marker:
val = ExprTypeProxy(vname, val, econtext)
names[vname] = val
names['__builtins__'] = builtins
return names
def __call__(self, econtext):
__traceback_info__ = self.text
vars = self._bind_used_names(econtext, __builtins__)
return eval(self._code, vars)
def __str__(self):
return 'Python expression "%s"' % self.text
def __repr__(self):
return '<PythonExpr %s>' % self.text
class ExprTypeProxy:
'''Class that proxies access to an expression type handler'''
def __init__(self, name, handler, econtext):
self._name = name
self._handler = handler
self._econtext = econtext
def __call__(self, text):
return self._handler(self._name, text,
self._econtext._engine)(self._econtext)
=== Added File Zope3/src/zope/pagetemplate/readme.txt ===
Page Templates
Introduction
Page Templates provide an elegant templating mechanism that
achieves a clean separation of presentation and application
logic while allowing for designers to work with templates
in their visual editing tools (FrontPage, Dreamweaver, GoLive,
etc.)
This document focuses on usage of Page Templates outside of
a Zope context, it does *not* explain how to write page templates
as there are several resources on the web which do so.
Dependencies
Zope3 Package Dependencies
- TAL (Template Attribute Language)
- Interface
- ZTUtils (batching utilities for zpt)
- The standard logging package ("logging") from Python 2.3.
Simple Usage
Using PageTemplates outside of Zope3 is very easy and straight
forward. a quick example::
> python
>> from Zope.PageTemplate.PageTemplateFile import PageTemplateFile
>> my_pt = PageTemplateFile('hello_world.pt')
>> my_pt()
u'<html><body>Hello World</body></html>'
Setting Up Contexts
Rendering a page template without binding data to is not very
interesting. By default keyword arguments you pass in page
templates appear in the options namespace.
pt_getContext(**keywords)
Should ignore keyword arguments that it doesn't care about,
and construct the namespace passed to the TALES expression
engine. This method is free to use the keyword arguments it
receives.
pt_render(namespace, source=0)
Responsible the TAL interpreter to perform the rendering. The
namespace argument is a mapping which defines the top-level
namespaces passed to the TALES expression engine.
Narrative ()
Narrative (Subclassing PageTemplates)
Lets say we want to alter page templates such that keyword
arguments appear as top level items in the namespace. we can
subclass page template and alter the default behavior of
pt_getContext to add them in::
from Zope.PageTemplate.PageTemplate import PageTemplate
class mypt(PageTemplate):
def pt_getContext(self, args=(), options={}, **kw):
rval = PageTemplate.pt_getContext(self, args=args)
options.update(rval)
return options
class foo:
def getContents(self): return 'hi'
So now we can bind objects in a more arbitrary fashion, like
the following::
template = """
<html>
<body>
<b tal:replace="das_object/getContents">Good Stuff Here</b>
</body>
</html>
"""
pt = mypt()
pt.write(template)
pt(das_object=foo())
Author
Kapil Thangavelu <hazmat at objectrealms.net>
=== Added File Zope3/src/zope/pagetemplate/safemapping.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Simple variation of MultiMapping used to support layers of variable
declarations in TAL."""
class SafeMapping:
def __init__(self, *dicts):
self._mappings = list(dicts)
self._mappings.reverse()
def __getitem__(self, key):
for d in self._mappings:
if key in d:
return d[key]
raise KeyError, key
def __contains__(self, key):
for d in self._mappings:
if key in d:
return 1
return 0
has_key = __contains__
def get(self, key, default=None):
for d in self._mappings:
if key in d:
return d[key]
return default
def _push(self, dict):
self._mappings.insert(0, dict)
def _pop(self, count=1):
del self._mappings[:count]
=== Added File Zope3/src/zope/pagetemplate/tales.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""TALES
An implementation of a generic TALES engine
"""
__metaclass__ = type # All classes are new style when run with Python 2.2+
__version__ = '$Revision: 1.1.2.1 $'[11:-2]
import re
import sys
from types import StringType, StringTypes
import zope.pagetemplate
from zope.pagetemplate.safemapping import SafeMapping
from zope.tal.interfaces import ITALESCompiler, ITALESEngine, ITALESErrorInfo
NAME_RE = r"[a-zA-Z][a-zA-Z0-9_]*"
_parse_expr = re.compile(r"(%s):" % NAME_RE).match
_valid_name = re.compile('%s$' % NAME_RE).match
class TALESError(Exception):
"""Error during TALES evaluation"""
class Undefined(TALESError):
'''Exception raised on traversal of an undefined path'''
class CompilerError(Exception):
'''TALES Compiler Error'''
class RegistrationError(Exception):
'''Expression type or base name registration Error'''
_default = object()
_marker = object()
class Iterator(Zope.ZTUtils.Iterator):
def __init__(self, name, seq, context):
Zope.ZTUtils.Iterator.__init__(self, seq)
self.name = name
self._context = context
def __iter__(self):
return self
def next(self):
if Zope.ZTUtils.Iterator.next(self):
self._context.setLocal(self.name, self.seq[self.index])
return 1
return 0
class ErrorInfo:
"""Information about an exception passed to an on-error handler."""
__implements__ = ITALESErrorInfo
def __init__(self, err, position=(None, None)):
if isinstance(err, Exception):
self.type = err.__class__
self.value = err
else:
self.type = err
self.value = None
self.lineno = position[0]
self.offset = position[1]
class ExpressionEngine:
'''Expression Engine
An instance of this class keeps a mutable collection of expression
type handlers. It can compile expression strings by delegating to
these handlers. It can provide an expression Context, which is
capable of holding state and evaluating compiled expressions.
'''
__implements__ = ITALESCompiler
def __init__(self):
self.types = {}
self.base_names = {}
self.iteratorFactory = Iterator
def registerType(self, name, handler):
if not _valid_name(name):
raise RegistrationError, (
'Invalid expression type name "%s".' % name)
types = self.types
if name in types:
raise RegistrationError, (
'Multiple registrations for Expression type "%s".' %
name)
types[name] = handler
def getTypes(self):
return self.types
def registerBaseName(self, name, object):
if not _valid_name(name):
raise RegistrationError, 'Invalid base name "%s".' % name
base_names = self.base_names
if name in base_names:
raise RegistrationError, (
'Multiple registrations for base name "%s".' % name)
base_names[name] = object
def getBaseNames(self):
return self.base_names
def compile(self, expression):
m = _parse_expr(expression)
if m:
type = m.group(1)
expr = expression[m.end():]
else:
type = "standard"
expr = expression
try:
handler = self.types[type]
except KeyError:
raise CompilerError, (
'Unrecognized expression type "%s".' % type)
return handler(type, expr, self)
def getContext(self, contexts=None, **kwcontexts):
if contexts is not None:
if kwcontexts:
kwcontexts.update(contexts)
else:
kwcontexts = contexts
return Context(self, kwcontexts)
def getCompilerError(self):
return CompilerError
class Context:
'''Expression Context
An instance of this class holds context information that it can
use to evaluate compiled expressions.
'''
__implements__ = ITALESEngine
_context_class = SafeMapping
position = (None, None)
source_file = None
def __init__(self, engine, contexts):
self._engine = engine
self.contexts = contexts
contexts['nothing'] = None
contexts['default'] = _default
self.repeat_vars = rv = {}
# Wrap this, as it is visible to restricted code
contexts['repeat'] = rep = self._context_class(rv)
contexts['loop'] = rep # alias
self.global_vars = gv = contexts.copy()
self.local_vars = lv = {}
self.vars = self._context_class(gv, lv)
# Keep track of what needs to be popped as each scope ends.
self._scope_stack = []
def beginScope(self):
self._scope_stack.append([self.local_vars.copy()])
def endScope(self):
scope = self._scope_stack.pop()
self.local_vars = lv = scope[0]
v = self.vars
v._pop()
v._push(lv)
# Pop repeat variables, if any
i = len(scope) - 1
while i:
name, value = scope[i]
if value is None:
del self.repeat_vars[name]
else:
self.repeat_vars[name] = value
i = i - 1
def setLocal(self, name, value):
self.local_vars[name] = value
def setGlobal(self, name, value):
self.global_vars[name] = value
def setRepeat(self, name, expr):
expr = self.evaluate(expr)
if not expr:
return self._engine.iteratorFactory(name, (), self)
it = self._engine.iteratorFactory(name, expr, self)
old_value = self.repeat_vars.get(name)
self._scope_stack[-1].append((name, old_value))
self.repeat_vars[name] = it
return it
def evaluate(self, expression,
isinstance=isinstance, StringType=StringType):
if isinstance(expression, StringType):
expression = self._engine.compile(expression)
__traceback_supplement__ = (
TALESTracebackSupplement, self, expression)
return expression(self)
evaluateValue = evaluate
def evaluateBoolean(self, expr):
return not not self.evaluate(expr)
def evaluateText(self, expr):
text = self.evaluate(expr)
if text is _default or text is None:
return text
if not isinstance(text, StringTypes):
text = unicode(text)
return text
def evaluateStructure(self, expr):
return self.evaluate(expr)
evaluateStructure = evaluate
def evaluateMacro(self, expr):
# XXX Should return None or a macro definition
return self.evaluate(expr)
evaluateMacro = evaluate
def createErrorInfo(self, err, position):
return ErrorInfo(err, position)
def getDefault(self):
return _default
def setSourceFile(self, source_file):
self.source_file = source_file
def setPosition(self, position):
self.position = position
class TALESTracebackSupplement:
"""Implementation of Zope.Exceptions.ITracebackSupplement"""
def __init__(self, context, expression):
self.context = context
self.source_url = context.source_file
self.line = context.position[0]
self.column = context.position[1]
self.expression = repr(expression)
def getInfo(self, as_html=0):
import pprint
data = self.context.contexts.copy()
if 'modules' in data:
del data['modules'] # the list is really long and boring
s = pprint.pformat(data)
if not as_html:
return ' - Names:\n %s' % s.replace('\n', '\n ')
else:
from cgi import escape
return '<b>Names:</b><pre>%s</pre>' % (escape(s))
return None
class SimpleExpr:
'''Simple example of an expression type handler'''
def __init__(self, name, expr, engine):
self._name = name
self._expr = expr
def __call__(self, econtext):
return self._name, self._expr
def __repr__(self):
return '<SimpleExpr %s %s>' % (self._name, `self._expr`)