Chameleon-2.6.1/0000755000175000001440000000000011665422242014050 5ustar mborchusers00000000000000Chameleon-2.6.1/distribute_setup.py0000644000175000001440000003661511476415473020043 0ustar mborchusers00000000000000#!python """Bootstrap distribute installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from distribute_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import os import sys import time import fnmatch import tempfile import tarfile from distutils import log try: from site import USER_SITE except ImportError: USER_SITE = None try: import subprocess def _python_cmd(*args): args = (sys.executable,) + args return subprocess.call(args) == 0 except ImportError: # will be used for python 2.3 def _python_cmd(*args): args = (sys.executable,) + args # quoting arguments if windows if sys.platform == 'win32': def quote(arg): if ' ' in arg: return '"%s"' % arg return arg args = [quote(arg) for arg in args] return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 DEFAULT_VERSION = "0.6.14" DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" SETUPTOOLS_FAKED_VERSION = "0.6c11" SETUPTOOLS_PKG_INFO = """\ Metadata-Version: 1.0 Name: setuptools Version: %s Summary: xxxx Home-page: xxx Author: xxx Author-email: xxx License: xxx Description: xxx """ % SETUPTOOLS_FAKED_VERSION def _install(tarball): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # installing log.warn('Installing Distribute') if not _python_cmd('setup.py', 'install'): log.warn('Something went wrong during the installation.') log.warn('See the error message above.') finally: os.chdir(old_wd) def _build_egg(egg, tarball, to_dir): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # building an egg log.warn('Building a Distribute egg in %s', to_dir) _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) finally: os.chdir(old_wd) # returning the result log.warn(egg) if not os.path.exists(egg): raise IOError('Could not build the egg.') def _do_download(version, download_base, to_dir, download_delay): egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' % (version, sys.version_info[0], sys.version_info[1])) if not os.path.exists(egg): tarball = download_setuptools(version, download_base, to_dir, download_delay) _build_egg(egg, tarball, to_dir) sys.path.insert(0, egg) import setuptools setuptools.bootstrap_install_from = egg def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15, no_fake=True): # making sure we use the absolute path to_dir = os.path.abspath(to_dir) was_imported = 'pkg_resources' in sys.modules or \ 'setuptools' in sys.modules try: try: import pkg_resources if not hasattr(pkg_resources, '_distribute'): if not no_fake: _fake_setuptools() raise ImportError except ImportError: return _do_download(version, download_base, to_dir, download_delay) try: pkg_resources.require("distribute>="+version) return except pkg_resources.VersionConflict: e = sys.exc_info()[1] if was_imported: sys.stderr.write( "The required version of distribute (>=%s) is not available,\n" "and can't be installed while this script is running. Please\n" "install a more recent version first, using\n" "'easy_install -U distribute'." "\n\n(Currently using %r)\n" % (version, e.args[0])) sys.exit(2) else: del pkg_resources, sys.modules['pkg_resources'] # reload ok return _do_download(version, download_base, to_dir, download_delay) except pkg_resources.DistributionNotFound: return _do_download(version, download_base, to_dir, download_delay) finally: if not no_fake: _create_fake_setuptools_pkg_info(to_dir) def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay=15): """Download distribute from a specified location and return its filename `version` should be a valid distribute version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ # making sure we use the absolute path to_dir = os.path.abspath(to_dir) try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen tgz_name = "distribute-%s.tar.gz" % version url = download_base + tgz_name saveto = os.path.join(to_dir, tgz_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: log.warn("Downloading %s", url) src = urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = src.read() dst = open(saveto, "wb") dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def _no_sandbox(function): def __no_sandbox(*args, **kw): try: from setuptools.sandbox import DirectorySandbox if not hasattr(DirectorySandbox, '_old'): def violation(*args): pass DirectorySandbox._old = DirectorySandbox._violation DirectorySandbox._violation = violation patched = True else: patched = False except ImportError: patched = False try: return function(*args, **kw) finally: if patched: DirectorySandbox._violation = DirectorySandbox._old del DirectorySandbox._old return __no_sandbox def _patch_file(path, content): """Will backup the file then patch it""" existing_content = open(path).read() if existing_content == content: # already patched log.warn('Already patched.') return False log.warn('Patching...') _rename_path(path) f = open(path, 'w') try: f.write(content) finally: f.close() return True _patch_file = _no_sandbox(_patch_file) def _same_content(path, content): return open(path).read() == content def _rename_path(path): new_name = path + '.OLD.%s' % time.time() log.warn('Renaming %s into %s', path, new_name) os.rename(path, new_name) return new_name def _remove_flat_installation(placeholder): if not os.path.isdir(placeholder): log.warn('Unkown installation at %s', placeholder) return False found = False for file in os.listdir(placeholder): if fnmatch.fnmatch(file, 'setuptools*.egg-info'): found = True break if not found: log.warn('Could not locate setuptools*.egg-info') return log.warn('Removing elements out of the way...') pkg_info = os.path.join(placeholder, file) if os.path.isdir(pkg_info): patched = _patch_egg_dir(pkg_info) else: patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) if not patched: log.warn('%s already patched.', pkg_info) return False # now let's move the files out of the way for element in ('setuptools', 'pkg_resources.py', 'site.py'): element = os.path.join(placeholder, element) if os.path.exists(element): _rename_path(element) else: log.warn('Could not find the %s element of the ' 'Setuptools distribution', element) return True _remove_flat_installation = _no_sandbox(_remove_flat_installation) def _after_install(dist): log.warn('After install bootstrap.') placeholder = dist.get_command_obj('install').install_purelib _create_fake_setuptools_pkg_info(placeholder) def _create_fake_setuptools_pkg_info(placeholder): if not placeholder or not os.path.exists(placeholder): log.warn('Could not find the install location') return pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) setuptools_file = 'setuptools-%s-py%s.egg-info' % \ (SETUPTOOLS_FAKED_VERSION, pyver) pkg_info = os.path.join(placeholder, setuptools_file) if os.path.exists(pkg_info): log.warn('%s already exists', pkg_info) return log.warn('Creating %s', pkg_info) f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() pth_file = os.path.join(placeholder, 'setuptools.pth') log.warn('Creating %s', pth_file) f = open(pth_file, 'w') try: f.write(os.path.join(os.curdir, setuptools_file)) finally: f.close() _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) def _patch_egg_dir(path): # let's check if it's already patched pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') if os.path.exists(pkg_info): if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): log.warn('%s already patched.', pkg_info) return False _rename_path(path) os.mkdir(path) os.mkdir(os.path.join(path, 'EGG-INFO')) pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() return True _patch_egg_dir = _no_sandbox(_patch_egg_dir) def _before_install(): log.warn('Before install bootstrap.') _fake_setuptools() def _under_prefix(location): if 'install' not in sys.argv: return True args = sys.argv[sys.argv.index('install')+1:] for index, arg in enumerate(args): for option in ('--root', '--prefix'): if arg.startswith('%s=' % option): top_dir = arg.split('root=')[-1] return location.startswith(top_dir) elif arg == option: if len(args) > index: top_dir = args[index+1] return location.startswith(top_dir) if arg == '--user' and USER_SITE is not None: return location.startswith(USER_SITE) return True def _fake_setuptools(): log.warn('Scanning installed packages') try: import pkg_resources except ImportError: # we're cool log.warn('Setuptools or Distribute does not seem to be installed.') return ws = pkg_resources.working_set try: setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', replacement=False)) except TypeError: # old distribute API setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) if setuptools_dist is None: log.warn('No setuptools distribution found') return # detecting if it was already faked setuptools_location = setuptools_dist.location log.warn('Setuptools installation detected at %s', setuptools_location) # if --root or --preix was provided, and if # setuptools is not located in them, we don't patch it if not _under_prefix(setuptools_location): log.warn('Not patching, --root or --prefix is installing Distribute' ' in another location') return # let's see if its an egg if not setuptools_location.endswith('.egg'): log.warn('Non-egg installation') res = _remove_flat_installation(setuptools_location) if not res: return else: log.warn('Egg installation') pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') if (os.path.exists(pkg_info) and _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): log.warn('Already patched.') return log.warn('Patching...') # let's create a fake egg replacing setuptools one res = _patch_egg_dir(setuptools_location) if not res: return log.warn('Patched done.') _relaunch() def _relaunch(): log.warn('Relaunching...') # we have to relaunch the process # pip marker to avoid a relaunch bug if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: sys.argv[0] = 'setup.py' args = [sys.executable] + sys.argv sys.exit(subprocess.call(args)) def _extractall(self, path=".", members=None): """Extract all members from the archive to the current working directory and set owner, modification time and permissions on directories afterwards. `path' specifies a different directory to extract to. `members' is optional and must be a subset of the list returned by getmembers(). """ import copy import operator from tarfile import ExtractError directories = [] if members is None: members = self for tarinfo in members: if tarinfo.isdir(): # Extract directories with a safe mode. directories.append(tarinfo) tarinfo = copy.copy(tarinfo) tarinfo.mode = 448 # decimal for oct 0700 self.extract(tarinfo, path) # Reverse sort directories. if sys.version_info < (2, 4): def sorter(dir1, dir2): return cmp(dir1.name, dir2.name) directories.sort(sorter) directories.reverse() else: directories.sort(key=operator.attrgetter('name'), reverse=True) # Set correct owner, mtime and filemode on directories. for tarinfo in directories: dirpath = os.path.join(path, tarinfo.name) try: self.chown(tarinfo, dirpath) self.utime(tarinfo, dirpath) self.chmod(tarinfo, dirpath) except ExtractError: e = sys.exc_info()[1] if self.errorlevel > 1: raise else: self._dbg(1, "tarfile: %s" % e) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" tarball = download_setuptools() _install(tarball) if __name__ == '__main__': main(sys.argv[1:]) Chameleon-2.6.1/Makefile0000644000175000001440000000607711532666537015534 0ustar mborchusers00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = docs SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Chameleon.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Chameleon.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." Chameleon-2.6.1/tox.ini0000644000175000001440000000134711612276035015367 0ustar mborchusers00000000000000[tox] envlist = py25,py26,py27,py32,pypy,cover [testenv:py25] commands = python setup.py test -q deps = ordereddict unittest2 distribute [testenv:py26] commands = python setup.py test -q deps = ordereddict unittest2 distribute [testenv:py27] commands = python setup.py test -q deps = distribute [testenv:py32] commands = python setup.py test -q deps = distribute [testenv:pypy] commands = python setup.py test -q deps = ordereddict unittest2 distribute [testenv:cover] basepython = python2.6 commands = python setup.py nosetests --with-xunit --with-xcoverage deps = nose coverage==3.4 nosexcover ordereddict unittest2 distribute Chameleon-2.6.1/benchmarks/0000755000175000001440000000000011665422242016165 5ustar mborchusers00000000000000Chameleon-2.6.1/benchmarks/bm_mako.py0000644000175000001440000000744411641056037020154 0ustar mborchusers00000000000000#!/usr/bin/python """ Benchmark for test the performance of Mako templates engine. Includes: -two template inherences -HTML escaping, XML escaping, URL escaping, whitespace trimming -function defitions and calls -forloops """ __author__ = "virhilo@gmail.com (Lukasz Fidosz)" # Python imports import os import sys import optparse import time # Local imports import util def relative(*args): return os.path.join(os.path.dirname(os.path.abspath(__file__)), *args) sys.path.insert(0, relative('..', 'lib')) # Mako imports from mako.template import Template from mako.lookup import TemplateLookup LOREM_IPSUM = """Quisque lobortis hendrerit posuere. Curabitur aliquet consequat sapien molestie pretium. Nunc adipiscing luc tus mi, viverra porttitor lorem vulputate et. Ut at purus sem, sed tincidunt ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent pulvinar sodales justo at congue. Praesent aliquet facilisis nisl a molestie. Sed tempus nisl ut augue eleifend tincidunt. Sed a lacinia nulla. Cras tortor est, mollis et consequat at, vulputate et orci. Nulla sollicitudin""" BASE_TEMPLATE = """ <%def name="render_table(table)"> % for row in table: % for col in row: % endfor % endfor
${col|h}
<%def name="img(src, alt)"> ${alt} ${title|h,trim} ${next.body()} """ PAGE_TEMPLATE = """ <%inherit file="base.mako"/> % for row in table: % for col in row: % endfor % endfor
${col}
% for nr in xrange(img_count): ${parent.img('/foo/bar/baz.png', 'no image :o')} % endfor ${next.body()} % for nr in paragraphs:

${lorem|x}

% endfor ${parent.render_table(table)} """ CONTENT_TEMPLATE = """ <%inherit file="page.mako"/> <%def name="fun1()"> fun1 <%def name="fun2()"> fun2 <%def name="fun3()"> foo3 <%def name="fun4()"> foo4 <%def name="fun5()"> foo5 <%def name="fun6()"> foo6

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam laoreet justo in velit faucibus lobortis. Sed dictum sagittis volutpat. Sed adipiscing vestibulum consequat. Nullam laoreet, ante nec pretium varius, libero arcu porttitor orci, id cursus odio nibh nec leo. Vestibulum dapibus pellentesque purus, sed bibendum tortor laoreet id. Praesent quis sodales ipsum. Fusce ut ligula sed diam pretium sagittis vel at ipsum. Nulla sagittis sem quam, et volutpat velit. Fusce dapibus ligula quis lectus ultricies tempor. Pellente

${fun1()} ${fun2()} ${fun3()} ${fun4()} ${fun5()} ${fun6()} """ def test_mako(count): lookup = TemplateLookup() lookup.put_string('base.mako', BASE_TEMPLATE) lookup.put_string('page.mako', PAGE_TEMPLATE) template = Template(CONTENT_TEMPLATE, lookup=lookup) table = [xrange(150) for i in xrange(150)] paragraphs = xrange(50) title = 'Hello world!' times = [] for i in range(count): t0 = time.time() data = template.render(table=table, paragraphs=paragraphs, lorem=LOREM_IPSUM, title=title, img_count=50) t1 = time.time() times.append(t1-t0) return times if __name__ == "__main__": parser = optparse.OptionParser( usage="%prog [options]", description=("Test the performance of Mako templates.")) util.add_standard_options_to(parser) (options, args) = parser.parse_args() util.run_benchmark(options, options.num_runs, test_mako) Chameleon-2.6.1/benchmarks/util.py0000644000175000001440000000340711641053656017523 0ustar mborchusers00000000000000#!/usr/bin/env python """Utility code for benchmark scripts.""" __author__ = "collinwinter@google.com (Collin Winter)" import math import operator def run_benchmark(options, num_runs, bench_func, *args): """Run the given benchmark, print results to stdout. Args: options: optparse.Values instance. num_runs: number of times to run the benchmark bench_func: benchmark function. `num_runs, *args` will be passed to this function. This should return a list of floats (benchmark execution times). """ if options.profile: import cProfile prof = cProfile.Profile() prof.runcall(bench_func, num_runs, *args) prof.print_stats(sort=options.profile_sort) else: data = bench_func(num_runs, *args) if options.take_geo_mean: product = reduce(operator.mul, data, 1) print math.pow(product, 1.0 / len(data)) else: for x in data: print x def add_standard_options_to(parser): """Add a bunch of common command-line flags to an existing OptionParser. This function operates on `parser` in-place. Args: parser: optparse.OptionParser instance. """ parser.add_option("-n", action="store", type="int", default=100, dest="num_runs", help="Number of times to run the test.") parser.add_option("--profile", action="store_true", help="Run the benchmark through cProfile.") parser.add_option("--profile_sort", action="store", type="str", default="time", help="Column to sort cProfile output by.") parser.add_option("--take_geo_mean", action="store_true", help="Return the geo mean, rather than individual data.") Chameleon-2.6.1/benchmarks/bm_chameleon.py0000644000175000001440000000771311641064376021164 0ustar mborchusers00000000000000#!/usr/bin/python2 """ Benchmark for test the performance of Chameleon page template engine. """ __author__ = "mborch@gmail.com (Malthe Borch)" # Python imports import os import sys import optparse import time # Local imports import util def relative(*args): return os.path.join(os.path.dirname(os.path.abspath(__file__)), *args) sys.path.insert(0, relative('..', 'src')) # Chameleon imports from chameleon import PageTemplate LOREM_IPSUM = """Quisque lobortis hendrerit posuere. Curabitur aliquet consequat sapien molestie pretium. Nunc adipiscing luc tus mi, viverra porttitor lorem vulputate et. Ut at purus sem, sed tincidunt ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent pulvinar sodales justo at congue. Praesent aliquet facilisis nisl a molestie. Sed tempus nisl ut augue eleifend tincidunt. Sed a lacinia nulla. Cras tortor est, mollis et consequat at, vulputate et orci. Nulla sollicitudin""" BASE_TEMPLATE = '''
${col}
${alt}
${title.strip()} ''' PAGE_TEMPLATE = ''' images:

${lorem}

''' CONTENT_TEMPLATE = ''' fun1fun2fun3fun4fun5fun6

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam laoreet justo in velit faucibus lobortis. Sed dictum sagittis volutpat. Sed adipiscing vestibulum consequat. Nullam laoreet, ante nec pretium varius, libero arcu porttitor orci, id cursus odio nibh nec leo. Vestibulum dapibus pellentesque purus, sed bibendum tortor laoreet id. Praesent quis sodales ipsum. Fusce ut ligula sed diam pretium sagittis vel at ipsum. Nulla sagittis sem quam, et volutpat velit. Fusce dapibus ligula quis lectus ultricies tempor. Pellente

''' def test_mako(count): template = PageTemplate(CONTENT_TEMPLATE) base = PageTemplate(BASE_TEMPLATE) page = PageTemplate(PAGE_TEMPLATE) table = [xrange(150) for i in xrange(150)] paragraphs = xrange(50) title = 'Hello world!' times = [] for i in range(count): t0 = time.time() data = template.render( table=table, paragraphs=paragraphs, lorem=LOREM_IPSUM, title=title, img_count=50, base=base, page=page, ) t1 = time.time() times.append(t1-t0) return times if __name__ == "__main__": parser = optparse.OptionParser( usage="%prog [options]", description=("Test the performance of Chameleon templates.")) util.add_standard_options_to(parser) (options, args) = parser.parse_args() util.run_benchmark(options, options.num_runs, test_mako) Chameleon-2.6.1/LICENSE.txt0000644000175000001440000002046311531701545015676 0ustar mborchusers00000000000000The majority of the code in Chameleon is supplied under this license: A copyright notice accompanies this license document that identifies the copyright holders. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Portions of the code in Chameleon are supplied under the ZPL (headers within individiual files indicate that these portions are licensed under the ZPL): Zope Public License (ZPL) Version 2.1 ------------------------------------- A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Portions of the code in Chameleon are supplied under the BSD license (headers within individiual files indicate that these portions are licensed under this license): All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Portions of the code in Chameleon are supplied under the Python License (headers within individiual files indicate that these portions are licensed under this license): PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python. 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this License Agreement. Chameleon-2.6.1/setup.cfg0000644000175000001440000000042411665422242015671 0ustar mborchusers00000000000000[easy_install] zip_ok = false [nosetests] match = ^test nocapture = 1 cover-package = tree.codegen, tree.lexer, tree.parser, tree.nodes, tree.translation, tree.language, tree.tales, tree.expressions cover-erase = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 Chameleon-2.6.1/PKG-INFO0000644000175000001440000007516411665422242015162 0ustar mborchusers00000000000000Metadata-Version: 1.0 Name: Chameleon Version: 2.6.1 Summary: Fast HTML/XML Template Compiler. Home-page: http://www.pagetemplates.org/ Author: Malthe Borch Author-email: mborch@gmail.com License: BSD-like (http://repoze.org/license.html) Description: Overview ======== Chameleon is an HTML/XML template engine for `Python `_. It uses the *page templates* language. You can use it in any Python web application with just about any version of Python (2.5 and up, including 3.x and `pypy `_). Visit the `website `_ for more information or the `documentation `_. License and Copyright --------------------- This software is made available as-is under a BSD-like license [1]_ (see included copyright notice). Notes ----- .. [1] This software is licensed under the `Repoze `_ license. Changes ======= 2.6.1 (2011-11-30) ------------------ Bugfixes: - Decode HTML entities in expression interpolation strings. This fixes issue #74. - Allow ``xml`` and ``xmlns`` attributes on TAL, I18N and METAL namespace elements. This fixes issue #73. 2.6.0 (2011-11-24) ------------------ Features: - Added support for implicit translation: The ``implicit_i18n_translate`` option enables implicit translation of text. The ``implicit_i18n_attributes`` enables implicit translation of attributes. The latter must be a set and for an attribute to be implicitly translated, its lowercase string value must be included in the set. - Added option ``strict`` (enabled by default) which decides whether expressions are required to be valid at compile time. That is, if not set, an exception is only raised for an invalid expression at evaluation time. - An expression error now results in an exception only if the expression is attempted evaluated during a rendering. - Added a configuration option ``prepend_relative_search_path`` which decides whether the path relative to a file-based template is prepended to the load search path. The default is ``True``. - Added a configuration option ``search_path`` to the file-based template class, which adds additional paths to the template load instance bound to the ``load:`` expression. The option takes a string path or an iterable yielding string paths. The default value is the empty set. Bugfixes: - Exception instances now support pickle/unpickle. - An attributes in i18n:attributes no longer needs to match an existing or dynamic attribute in order to appear in the element. This fixes issue #66. 2.5.3 (2011-10-23) ------------------ Bugfixes: - Fixed an issue where a nested macro slot definition would fail even though there existed a parent macro definition. This fixes issue #69. 2.5.2 (2011-10-12) ------------------ Bugfixes: - Fixed an issue where technically invalid input would result in a compiler error. Features: - The markup class now inherits from the unicode string type such that it's compatible with the string interface. 2.5.1 (2011-09-29) ------------------ Bugfixes: - The symbol names "convert", "decode" and "translate" are now no longer set as read-only *compiler internals*. This fixes issue #65. - Fixed an issue where a macro extension chain nested two levels (a template uses a macro that extends a macro) would lose the middle slot definitions if slots were defined nested. The compiler now throws an error if a nested slot definition is used outside a macro extension context. 2.5.0 (2011-09-23) ------------------ Features: - An expression type ``structure:`` is now available which wraps the expression result as *structure* such that it is not escaped on insertion, e.g.::
${structure: context.body}
This also means that the ``structure`` keyword for ``tal:content`` and ``tal:replace`` now has an alternative spelling via the expression type ``structure:``. - The string-based template constructor now accepts encoded input. 2.4.6 (2011-09-23) ------------------ Bugfixes: - The ``tal:on-error`` statement should catch all exceptions. - Fixed issue that would prevent escaping of interpolation expression values appearing in text. 2.4.5 (2011-09-21) ------------------ Bugfixes: - The ``tal:on-error`` handler should have a ``error`` variable defined that has the value of the exception thrown. - The ``tal:on-error`` statement is a substitution statement and should support the "text" and "structure" insertion methods. 2.4.4 (2011-09-15) ------------------ Bugfixes: - An encoding specified in the XML document preamble is now read and used to decode the template input to unicode. This fixes issue #55. - Encoded expression input on Python 3 is now correctly decoded. Previously, the string representation output would be included instead of an actually decoded string. - Expression result conversion steps are now correctly included in error handling such that the exception output points to the expression location. 2.4.3 (2011-09-13) ------------------ Features: - When an encoding is provided, pass the 'ignore' flag to avoid decoding issues with bad input. Bugfixes: - Fixed pypy compatibility issue (introduced in previous release). 2.4.2 (2011-09-13) ------------------ Bugfixes: - Fixed an issue in the compiler where an internal variable (such as a translation default value) would be cached, resulting in variable scope corruption (see issue #49). 2.4.1 (2011-09-08) ------------------ Bugfixes: - Fixed an issue where a default value for an attribute would sometimes spill over into another attribute. - Fixed issue where the use of the ``default`` name in an attribute interpolation expression would print the attribute value. This is unexpected, because it's an expression, not a static text suitable for output. An attribute value of ``default`` now correctly drops the attribute. 2.4.0 (2011-08-22) ------------------ Features: - Added an option ``boolean_attributes`` to evaluate and render a provided set of attributes using a boolean logic: if the attribute is a true value, the value will be the attribute name, otherwise the attribute is dropped. In the reference implementation, the following attributes are configured as boolean values when the template is rendered in HTML-mode:: "compact", "nowrap", "ismap", "declare", "noshade", "checked", "disabled", "readonly", "multiple", "selected", "noresize", "defer" Note that in Chameleon, these attributes must be manually provided. Bugfixes: - The carriage return character (used on Windows platforms) would incorrectly be included in Python comments. It is now replaced with a line break. This fixes issue #44. 2.3.8 (2011-08-19) ------------------ - Fixed import error that affected Python 2.5 only. 2.3.7 (2011-08-19) ------------------ Features: - Added an option ``literal_false`` that disables the default behavior of dropping an attribute for a value of ``False`` (in addition to ``None``). This modified behavior is the behavior exhibited in reference implementation. Bugfixes: - Undo attribute special HTML attribute behavior (see previous release). This turned out not to be a compatible behavior; rather, boolean values should simply be coerced to a string. Meanwhile, the reference implementation does support an HTML mode in which the special attribute behavior is exhibited. We do not currently support this mode. 2.3.6 (2011-08-18) ------------------ Features: - Certain HTML attribute names now have a special behavior for a attribute value of ``True`` (or ``default`` if no default is defined). For these attributes, this return value will result in the name being printed as the value:: will be rendered as:: This behavior is compatible with the reference implementation. 2.3.5 (2011-08-18) ------------------ Features: - Added support for the set operator (``{item, item, ...}``). Bugfixes: - If macro is defined on the same element as a translation name, this no longer results in a "translation name not allowed outside translation" error. This fixes issue #43. - Attribute fallback to dictionary lookup now works on multiple items (e.g. ``d1.d2.d2``). This fixes issue #42. 2.3.4 (2011-08-16) ------------------ Features: - When inserting content in either attributes or text, a value of ``True`` (like ``False`` and ``None``) will result in no action. - Use statically assigned variables for ``"attrs"`` and ``"default"``. This change yields a performance improvement of 15-20%. - The template loader class now accepts an optional argument ``default_extension`` which accepts a filename extension which will be appended to the filename if there's not already an extension. Bugfixes: - The default symbol is now ``True`` for an attribute if the attribute default is not provided. Note that the result is that the attribute is dropped. This fixes issue #41. - Fixed an issue where assignment to a variable ``"type"`` would fail. This fixes issue #40. - Fixed an issue where an (unsuccesful) assignment for a repeat loop to a compiler internal name would not result in an error. - If the translation function returns the identical object, manually coerce it to string. This fixes a compatibility issue with translation functions which do not convert non-string objects to a string value, but simply return them unchanged. 2.3.3 (2011-08-15) ------------------ Features: - The ``load:`` expression now passes the initial keyword arguments to its template loader (e.g. ``auto_reload`` and ``encoding``). - In the exception output, string variable values are now limited to a limited output of characters, single line only. Bugfixes: - Fixed horizontal alignment of exception location info (i.e. 'String:', 'Filename:' and 'Location:') such that they match the template exception formatter. 2.3.2 (2011-08-11) ------------------ Bugfixes: - Fixed issue where i18n:domain would not be inherited through macros and slots. This fixes issue #37. 2.3.1 (2011-08-11) ------------------ Features: - The ``Builtin`` node type may now be used to represent any Python local or global name. This allows expression compilers to refer to e.g. ``get`` or ``getitem``, or to explicit require a builtin object such as one from the ``extra_builtins`` dictionary. Bugfixes: - Builtins which are not explicitly disallowed may now be redefined and used as variables (e.g. ``nothing``). - Fixed compiler issue with circular node annotation loop. 2.3 (2011-08-10) ---------------- Features: - Added support for the following syntax to disable inline evaluation in a comment: Note that the initial question mark character (?) will be omitted from output. - The parser now accepts '<' and '>' in attributes. Note that this is invalid markup. Previously, the '<' would not be accepted as a valid attribute value, but this would result in an 'unexpected end tag' error elsewhere. This fixes issue #38. - The expression compiler now provides methods ``assign_text`` and ``assign_value`` such that a template engine might configure this value conversion to support e.g. encoded strings. Note that currently, the only client for the ``assign_text`` method is the string expression type. - Enable template loader for string-based template classes. Note that the ``filename`` keyword argument may be provided on initialization to identify the template source by filename. This fixes issue #36. - Added ``extra_builtins`` option to the page template class. These builtins are added to the default builtins dictionary at cook time and may be provided at initialization using the ``extra_builtins`` keyword argument. Bugfixes: - If a translation domain is set for a fill slot, use this setting instead of the macro template domain. - The Python expression compiler now correctly decodes HTML entities ``'gt'`` and ``'lt'``. This fixes issue #32. - The string expression compiler now correctly handles encoded text (when support for encoded strings is enabled). This fixes issue #35. - Fixed an issue where setting the ``filename`` attribute on a file-based template would not automatically cause an invalidation. - Exceptions raised by Chameleon can now be copied via ``copy.copy``. This fixes issue #36. [leorochael] - If copying the exception fails in the exception handler, simply re-raise the original exception and log a warning. 2.2 (2011-07-28) ---------------- Features: - Added new expression type ``load:`` that allows loading a template. Both relative and absolute paths are supported. If the path given is relative, then it will be resolved with respect to the directory of the template. - Added support for dynamic evaluation of expressions. Note that this is to support legacy applications. It is not currently wired into the provided template classes. - Template classes now have a ``builtins`` attribute which may be used to define built-in variables always available in the template variable scope. Incompatibilities: - The file-based template class no longer accepts a parameter ``loader``. This parameter would be used to load a template from a relative path, using a ``find(filename)`` method. This was however, undocumented, and probably not very useful since we have the ``TemplateLoader`` mechanism already. - The compiled template module now contains an ``initialize`` function which takes values that map to the template builtins. The return value of this function is a dictionary that contains the render functions. Bugfixes: - The file-based template class no longer verifies the existance of a template file (using ``os.lstat``). This now happens implicitly if eager parsing is enabled, or otherwise when first needed (e.g. at render time). This is classified as a bug fix because the previous behavior was probably not what you'd expect, especially if an application initializes a lot of templates without needing to render them immediately. 2.1.1 (2011-07-28) ------------------ Features: - Improved exception display. The expression string is now shown in the context of the original source (if available) with a marker string indicating the location of the expression in the template source. Bugfixes: - The ``structure`` insertion mode now correctly decodes entities for any expression type (including ``string:``). This fixes issue #30. - Don't show internal variables in the exception formatter variable listing. 2.1 (2011-07-25) ---------------- Features: - Expression interpolation (using the ``${...}`` operator and previously also ``$identifier``) now requires braces everywhere except inside the ``string:`` expression type. This change is motivated by a number of legacy templates in which the interpolation format without braces ``$identifier`` appears as text. 2.0.2 (2011-07-25) ------------------ Bugfixes: - Don't use dynamic variable scope for lambda-scoped variables (#27). - Avoid duplication of exception class and message in traceback. - Fixed issue where a ``metal:fill-slot`` would be ignored if a macro was set to be used on the same element (#16). 2.0.1 (2011-07-23) ------------------ Bugfixes: - Fixed issue where global variable definition from macro slots would fail (they would instead be local). This also affects error reporting from inside slots because this would be recorded internally as a global. - Fixed issue with template cache digest (used for filenames); modules are now invalidated whenever any changes are made to the distribution set available (packages on ``sys.path``). - Fixed exception handler to better let exceptions propagate through the renderer. - The disk-based module compiler now mangles template source filenames such that the output Python module is valid and at root level (dots and hyphens are replaced by an underscore). This fixes issue #17. - Fixed translations (i18n) on Python 2.5. 2.0 (2011-07-14) ---------------- - Point release. 2.0-rc14 (2011-07-13) --------------------- Bugfixes: - The tab character (``\t``) is now parsed correctly when used inside tags. Features: - The ``RepeatDict`` class now works as a proxy behind a seperate dictionary instance. - Added template constructor option ``keep_body`` which is a flag (also available as a class attribute) that controls whether to save the template body input in the ``body`` attribute. This is disabled by default, unless debug-mode is enabled. - The page template loader class now accepts an optional ``formats`` argument which can be used to select an alternative template class. 2.0-rc13 (2011-07-07) --------------------- Bugfixes: - The backslash character (followed by optional whitespace and a line break) was not correctly interpreted as a continuation for Python expressions. Features: - The Python expression implementation is now more flexible for external subclassing via a new ``parse`` method. 2.0-rc12 (2011-07-04) --------------------- Bugfixes: - Initial keyword arguments passed to a template now no longer "leak" into the template variable space after a macro call. - An unexpected end tag is now an unrecoverable error. Features: - Improve exception output. 2.0-rc11 (2011-05-26) --------------------- Bugfixes: - Fixed issue where variable names that begin with an underscore were seemingly allowed, but their use resulted in a compiler error. Features: - Template variable names are now allowed to be prefixed with a single underscore, but not two or more (reserved for internal use). Examples of valid names:: item ITEM _item camelCase underscore_delimited help - Added support for Genshi's comment "drop" syntax:: Note the additional exclamation (!) character. This fixes addresses issue #10. 2.0-rc10 (2011-05-24) --------------------- Bugfixes: - The ``tal:attributes`` statement now correctly operates case-insensitive. The attribute name given in the statement will replace an existing attribute with the same name, without respect to case. Features: - Added ``meta:interpolation`` statement to control expression interpolation setting. Strings that disable the setting: ``"off"`` and ``"false"``. Strings that enable the setting: ``"on"`` and ``"true"``. - Expression interpolation now works inside XML comments. 2.0-rc9 (2011-05-05) -------------------- Features: - Better debugging support for string decode and conversion. If a naive join fails, each element in the output will now be attempted coerced to unicode to try and trigger the failure near to the bad string. 2.0-rc8 (2011-04-11) -------------------- Bugfixes: - If a macro defines two slots with the same name, a caller will now fill both with a single usage. - If a valid of ``None`` is provided as the translation function argument, we now fall back to the class default. 2.0-rc7 (2011-03-29) -------------------- Bugfixes: - Fixed issue with Python 2.5 compatibility AST. This affected at least PyPy 1.4. Features: - The ``auto_reload`` setting now defaults to the class value; the base template class gives a default value of ``chameleon.config.AUTO_RELOAD``. This change allows a subclass to provide a custom default value (such as an application-specific debug mode setting). 2.0-rc6 (2011-03-19) -------------------- Features: - Added support for ``target_language`` keyword argument to render method. If provided, the argument will be curried onto the translation function. Bugfixes: - The HTML entities 'lt', 'gt' and 'quot' appearing inside content subtition expressions are now translated into their native character values. This fixes an issue where you could not dynamically create elements using the ``structure`` (which is possible in ZPT). The need to create such structure stems from the lack of an expression interpolation operator in ZPT. - Fixed duplicate file pointer issue with test suite (affected Windows platforms only). This fixes issue #9. [oliora] - Use already open file using ``os.fdopen`` when trying to write out the module source. This fixes LP #731803. 2.0-rc5 (2011-03-07) -------------------- Bugfixes: - Fixed a number of issues concerning the escaping of attribute values: 1) Static attribute values are now included as they appear in the source. This means that invalid attribute values such as ``"true && false"`` are now left alone. It's not the job of the template engine to correct such markup, at least not in the default mode of operation. 2) The string expression compiler no longer unescapes values. Instead, this is left to each expression compiler. Currently only the Python expression compiler unescapes its input. 3) The dynamic escape code sequence now correctly only replaces ampersands that are part of an HTML escape format. Imports: - The page template classes and the loader class can now be imported directly from the ``chameleon`` module. Features: - If a custom template loader is not provided, relative paths are now resolved using ``os.abspath`` (i.e. to the current working directory). - Absolute paths are normalized using ``os.path.normpath`` and ``os.path.expanduser``. This ensures that all paths are kept in their "canonical" form. 2.0-rc4 (2011-03-03) -------------------- Bugfixes: - Fixed an issue where the output of an end-to-end string expression would raise an exception if the expression evaluated to ``None`` (it should simply output nothing). - The ``convert`` function (which is configurable on the template class level) now defaults to the ``translate`` function (at run-time). This fixes an issue where message objects were not translated (and thus converted to a string) using the a provided ``translate`` function. - Fixed string interpolation issue where an expression immediately succeeded by a right curly bracket would not parse. This fixes issue #5. - Fixed error where ``tal:condition`` would be evaluated after ``tal:repeat``. Features: - Python expression is now a TALES expression. That means that the pipe operator can be used to chain two or more expressions in a try-except sequence. This behavior was ported from the 1.x series. Note that while it's still possible to use the pipe character ("|") in an expression, it must now be escaped. - The template cache can now be shared by multiple processes. 2.0-rc3 (2011-03-02) -------------------- Bugfixes: - Fixed ``atexit`` handler. This fixes issue #3. - If a cache directory is specified, it will now be used even when not in debug mode. - Allow "comment" attribute in the TAL namespace. This fixes an issue in the sense that the reference engine allows any attribute within the TAL namespace. However, only "comment" is in common use. - The template constructor now accepts a flag ``debug`` which puts the template *instance* into debug-mode regardless of the global setting. This fixes issue #1. Features: - Added exception handler for exceptions raised while evaluating an expression. This handler raises (or attempts to) a new exception of the type ``RenderError``, with an additional base class of the original exception class. The string value of the exception is a formatted error message which includes the expression that caused the exception. If we are unable to create the exception class, the original exception is re-raised. 2.0-rc2 (2011-02-28) -------------------- - Fixed upload issue. 2.0-rc1 (2011-02-28) -------------------- - Initial public release. See documentation for what's new in this series. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.1 Classifier: Programming Language :: Python :: 3.2 Chameleon-2.6.1/docs/0000755000175000001440000000000011665422242015000 5ustar mborchusers00000000000000Chameleon-2.6.1/docs/reference.rst0000644000175000001440000014024011663366023017473 0ustar mborchusers00000000000000:tocdepth: 4 .. _language-reference: .. highlight:: xml Language Reference ================== The language reference is structured such that it can be read as a general introduction to the *page templates* language. It's split into parts that correspond to each of the main language features. Syntax ###### You can safely :ref:`skip this section ` if you're familiar with how template languages work or just want to learn by example. An *attribute language* is a programming language designed to render documents written in XML or HTML markup. The input must be a well-formed document. The output from the template is usually XML-like but isn't required to be well-formed. The statements of the language are document tags with special attributes, and look like this::

...

In the above example, the attribute ``namespace-prefix:command="argument"`` is the statement, and the entire paragraph tag is the statement's element. The statement's element is the portion of the document on which this statement operates. The namespace prefixes are typically declared once, at the top of a template (note that prefix declarations for the template language namespaces are omitted from the template output):: ... Thankfully, sane namespace prefix defaults are in place to let us skip most of the boilerplate::

...

Note how ``tal`` is used without an explicit namespace declaration. Chameleon sets up defaults for ``metal`` and ``i18n`` as well. .. note:: Default prefixes are a special feature of Chameleon. .. _tal: Basics (TAL) ############ The *template attribute language* is used to create dynamic XML-like content. It allows elements of a document to be replaced, repeated, or omitted. Statements ---------- These are the available statements: ================== ============== Statement Description ================== ============== ``tal:define`` Define variables. ``tal:switch`` Defines a switch condition ``tal:condition`` Include element only if expression is true. ``tal:repeat`` Repeat an element. ``tal:case`` Includes element only if expression is equal to parent switch. ``tal:content`` Substitute the content of an element. ``tal:replace`` Replace the element with dynamic content. ``tal:omit-tag`` Omit the element tags, leaving only the inner content. ``tal:attributes`` Dynamically change or insert element attributes. ``tal:on-error`` Substitute the content of an element if processing fails. ================== ============== When there is only one TAL statement per element, the order in which they are executed is simple. Starting with the root element, each element's statements are executed, then each of its child elements is visited, in order, to do the same:: </meta> <body> <div tal:condition="items"> <p>These are your items:</p> <ul> <li tal:repeat="item items" tal:content="item" /> </ul> </div> </body> </html> Any combination of statements may appear on the same element, except that the ``tal:content`` and ``tal:replace`` statements may not be used on the same element. .. note:: The ``tal:case`` and ``tal:switch`` statements are available in Chameleon only. TAL does not use use the order in which statements are written in the tag to determine the order in which they are executed. When an element has multiple statements, they are executed in the order printed in the table above. There is a reasoning behind this ordering. Because users often want to set up variables for use in other statements contained within this element or subelements, ``tal:define`` is executed first. Then any switch statement. ``tal:condition`` follows, then ``tal:repeat``, then ``tal:case``. We are now rendering an element; first ``tal:content`` or ``tal:replace``. Finally, before ``tal:attributes``, we have ``tal:omit-tag`` (which is implied with ``tal:replace``). .. note:: *TALES* is used as the expression language for the "stuff in the quotes". The default syntax is simply Python, but other inputs are possible --- see the section on :ref:`expressions <tales>`. ``tal:attributes`` ^^^^^^^^^^^^^^^^^^ Updates or inserts element attributes. :: tal:attributes="href request.url" Syntax ~~~~~~ ``tal:attributes`` syntax:: argument ::= attribute_statement [';' attribute_statement]* attribute_statement ::= attribute_name expression attribute_name ::= [namespace-prefix ':'] Name namespace-prefix ::= Name Description ~~~~~~~~~~~ The ``tal:attributes`` statement replaces the value of an attribute (or creates an attribute) with a dynamic value. The value of each expression is converted to a string, if necessary. .. note:: You can qualify an attribute name with a namespace prefix, for example ``html:table``, if you are generating an XML document with multiple namespaces. If an attribute expression evaluates to ``None``, the attribute is deleted from the statement element (or simply not inserted). If the expression evaluates to the symbol ``default`` (a symbol which is always available when evaluating attributes), its value is defined as the default static attribute value. If there is no such default value, a return value of ``default`` will drop the attribute. If you use ``tal:attributes`` on an element with an active ``tal:replace`` command, the ``tal:attributes`` statement is ignored. If you use ``tal:attributes`` on an element with a ``tal:repeat`` statement, the replacement is made on each repetition of the element, and the replacement expression is evaluated fresh for each repetition. .. note:: If you want to include a semicolon (";") in an expression, it must be escaped by doubling it (";;") [1]_. Examples ~~~~~~~~ Replacing a link:: <a href="/sample/link.html" tal:attributes="href context.url()" > ... </a> Replacing two attributes:: <textarea rows="80" cols="20" tal:attributes="rows request.rows();cols request.cols()" /> A checkbox input:: <input type="input" tal:attributes="checked True" /> ``tal:condition`` ^^^^^^^^^^^^^^^^^ Conditionally includes or omits an element:: <div tal:condition="comments"> ... </div> Syntax ~~~~~~ ``tal:condition`` syntax:: argument ::= expression Description ~~~~~~~~~~~ The ``tal:condition`` statement includes the statement element in the template only if the condition is met, and omits it otherwise. If its expression evaluates to a *true* value, then normal processing of the element continues, otherwise the statement element is immediately removed from the template. For these purposes, the value ``nothing`` is false, and ``default`` has the same effect as returning a true value. .. note:: Like Python itself, ZPT considers None, zero, empty strings, empty sequences, empty dictionaries, and instances which return a nonzero value from ``__len__`` or ``__nonzero__`` false; all other values are true, including ``default``. Examples ~~~~~~~~ Test a variable before inserting it:: <p tal:condition="request.message" tal:content="request.message" /> Testing for odd/even in a repeat-loop:: <div tal:repeat="item range(10)"> <p tal:condition="repeat.item.even">Even</p> <p tal:condition="repeat.item.odd">Odd</p> </div> ``tal:content`` ^^^^^^^^^^^^^^^ Replaces the content of an element. Syntax ~~~~~~ ``tal:content`` syntax:: argument ::= (['text'] | 'structure') expression Description ~~~~~~~~~~~ Rather than replacing an entire element, you can insert text or structure in place of its children with the ``tal:content`` statement. The statement argument is exactly like that of ``tal:replace``, and is interpreted in the same fashion. If the expression evaluates to ``nothing``, the statement element is left childless. If the expression evaluates to ``default``, then the element's contents are evaluated. The default replacement behavior is ``text``, which replaces angle-brackets and ampersands with their HTML entity equivalents. The ``structure`` keyword passes the replacement text through unchanged, allowing HTML/XML markup to be inserted. This can break your page if the text contains unanticipated markup (eg. text submitted via a web form), which is the reason that it is not the default. .. note:: The ``structure`` keyword exists to provide backwards compatibility. In Chameleon, the ``structure:`` expression type provides the same functionality (also for inline expressions). Examples ~~~~~~~~ Inserting the user name:: <p tal:content="user.getUserName()">Fred Farkas</p> Inserting HTML/XML:: <p tal:content="structure context.getStory()"> Marked <b>up</b> content goes here. </p> ``tal:define`` ^^^^^^^^^^^^^^ Defines local variables. Syntax ~~~~~~ ``tal:define`` syntax:: argument ::= define_scope [';' define_scope]* define_scope ::= (['local'] | 'global') define_var define_var ::= variable_name expression variable_name ::= Name Description ~~~~~~~~~~~ The ``tal:define`` statement defines variables. When you define a local variable in a statement element, you can use that variable in that element and the elements it contains. If you redefine a variable in a contained element, the new definition hides the outer element's definition within the inner element. Note that valid variable names are any Python identifier string including underscore, although two or more leading underscores are disallowed (used internally by the compiler). Further, names are case-sensitive. Python builtins are always "in scope", but most of them may be redefined (such as ``help``). Exceptions are:: ``float``, ``int``, ``len``, ``long``, ``str``, ``None``, ``True`` and ``False``. In addition, the following names are reserved: ``econtext``, ``rcontext``, ``translate``, ``decode`` and ``convert``. If the expression associated with a variable evaluates to ``nothing``, then that variable has the value ``nothing``, and may be used as such in further expressions. Likewise, if the expression evaluates to ``default``, then the variable has the value ``default``, and may be used as such in further expressions. You can define two different kinds of variables: *local* and *global*. When you define a local variable in a statement element, you can only use that variable in that element and the elements it contains. If you redefine a local variable in a contained element, the new definition hides the outer element's definition within the inner element. When you define a global variables, you can use it in any element processed after the defining element. If you redefine a global variable, you replace its definition for the rest of the template. To set the definition scope of a variable, use the keywords ``local`` or ``global`` in front of the assignment. The default setting is ``local``; thus, in practice, only the ``global`` keyword is used. .. note:: If you want to include a semicolon (";") in an expression, it must be escaped by doubling it (";;") [1]_. Examples ~~~~~~~~ Defining a variable:: tal:define="company_name 'Zope Corp, Inc.'" Defining two variables, where the second depends on the first:: tal:define="mytitle context.title; tlen len(mytitle)" ``tal:switch`` and ``tal:case`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Defines a switch clause. :: <ul tal:switch="len(items) % 2"> <li tal:case="True">odd</li> <li tal:case="False">even</li> </ul> Syntax ~~~~~~ ``tal:case`` and ``tal:switch`` syntax:: argument ::= expression Description ~~~~~~~~~~~ The *switch* and *case* construct is a short-hand syntax for evaluating a set of expressions against a parent value. The ``tal:switch`` statement is used to set a new parent value and the ``tal:case`` statement works like a condition and only allows content if the expression matches the value. Note that if the case expression is the symbol ``default``, it always matches the switch. .. note:: These statements are only available in Chameleon 2.x and not part of the ZPT specification. Examples ~~~~~~~~ :: <ul tal:switch="item.type"> <li tal:case="'document'"> Document </li> <li tal:case="'folder'"> Folder </li> </ul> Note that any and all cases that match the switch will be included. ``tal:omit-tag`` ^^^^^^^^^^^^^^^^ Removes an element, leaving its contents. Syntax ~~~~~~ ``tal:omit-tag`` syntax:: argument ::= [ expression ] Description ~~~~~~~~~~~ The ``tal:omit-tag`` statement leaves the contents of an element in place while omitting the surrounding start and end tags. If the expression evaluates to a *false* value, then normal processing of the element continues and the tags are not omitted. If the expression evaluates to a *true* value, or no expression is provided, the statement element is replaced with its contents. .. note:: Like Python itself, ZPT considers None, zero, empty strings, empty sequences, empty dictionaries, and instances which return a nonzero value from ``__len__`` or ``__nonzero__`` false; all other values are true, including ``default``. Examples ~~~~~~~~ Unconditionally omitting a tag:: <div tal:omit-tag="" comment="This tag will be removed"> <i>...but this text will remain.</i> </div> Conditionally omitting a tag:: <b tal:omit-tag="not:bold">I may be bold.</b> The above example will omit the ``b`` tag if the variable ``bold`` is false. Creating ten paragraph tags, with no enclosing tag:: <span tal:repeat="n range(10)" tal:omit-tag=""> <p tal:content="n">1</p> </span> .. _tal_repeat: ``tal:repeat`` ^^^^^^^^^^^^^^ Repeats an element. Syntax ~~~~~~ ``tal:repeat`` syntax:: argument ::= variable_name expression variable_name ::= Name Description ~~~~~~~~~~~ The ``tal:repeat`` statement replicates a sub-tree of your document once for each item in a sequence. The expression should evaluate to a sequence. If the sequence is empty, then the statement element is deleted, otherwise it is repeated for each value in the sequence. If the expression is ``default``, then the element is left unchanged, and no new variables are defined. The ``variable_name`` is used to define a local variable and a repeat variable. For each repetition, the local variable is set to the current sequence element, and the repeat variable is set to an iteration object. Repeat variables ~~~~~~~~~~~~~~~~~ You use repeat variables to access information about the current repetition (such as the repeat index). The repeat variable has the same name as the local variable, but is only accessible through the built-in variable named ``repeat``. The following information is available from the repeat variable: ================== ============== Attribute Description ================== ============== ``index`` Repetition number, starting from zero. ``number`` Repetition number, starting from one. ``even`` True for even-indexed repetitions (0, 2, 4, ...). ``odd`` True for odd-indexed repetitions (1, 3, 5, ...). ``start`` True for the starting repetition (index 0). ``end`` True for the ending, or final, repetition. ``first`` True for the first item in a group - see note below ``last`` True for the last item in a group - see note below ``length`` Length of the sequence, which will be the total number of repetitions. ``letter`` Repetition number as a lower-case letter: "a" - "z", "aa" - "az", "ba" - "bz", ..., "za" - "zz", "aaa" - "aaz", and so forth. ``Letter`` Upper-case version of *letter*. ``roman`` Repetition number as a lower-case roman numeral: "i", "ii", "iii", "iv", "v", etc. ``Roman`` Upper-case version of *roman*. ================== ============== You can access the contents of the repeat variable using either dictionary- or attribute-style access, e.g. ``repeat['item'].start`` or ``repeat.item.start``. .. note:: For legacy compatibility, the attributes ``odd``, ``even``, ``number``, ``letter``, ``Letter``, ``roman``, and ``Roman`` are callable (returning ``self``). Note that ``first`` and ``last`` are intended for use with sorted sequences. They try to divide the sequence into group of items with the same value. Examples ~~~~~~~~ Iterating over a sequence of strings:: <p tal:repeat="txt ('one', 'two', 'three')"> <span tal:replace="txt" /> </p> Inserting a sequence of table rows, and using the repeat variable to number the rows:: <table> <tr tal:repeat="item here.cart"> <td tal:content="repeat.item.number">1</td> <td tal:content="item.description">Widget</td> <td tal:content="item.price">$1.50</td> </tr> </table> Nested repeats:: <table border="1"> <tr tal:repeat="row range(10)"> <td tal:repeat="column range(10)"> <span tal:define="x repeat.row.number; y repeat.column.number; z x * y" tal:replace="string:$x * $y = $z">1 * 1 = 1</span> </td> </tr> </table> Insert objects. Separates groups of objects by type by drawing a rule between them:: <div tal:repeat="object objects"> <h2 tal:condition="repeat.object.first.meta_type" tal:content="object.type">Meta Type</h2> <p tal:content="object.id">Object ID</p> <hr tal:condition="object.last.meta_type" /> </div> .. note:: the objects in the above example should already be sorted by type. ``tal:replace`` ^^^^^^^^^^^^^^^ Replaces an element. Syntax ~~~~~~ ``tal:replace`` syntax:: argument ::= ['structure'] expression Description ~~~~~~~~~~~ The ``tal:replace`` statement replaces an element with dynamic content. It replaces the statement element with either text or a structure (unescaped markup). The body of the statement is an expression with an optional type prefix. The value of the expression is converted into an escaped string unless you provide the 'structure' prefix. Escaping consists of converting ``&`` to ``&amp;``, ``<`` to ``&lt;``, and ``>`` to ``&gt;``. .. note:: If the inserted object provides an ``__html__`` method, that method is called with the result inserted as structure. This feature is not implemented by ZPT. If the expression evaluates to ``None``, the element is simply removed. If the value is ``default``, then the element is left unchanged. Examples ~~~~~~~~ Inserting a title:: <span tal:replace="context.title">Title</span> Inserting HTML/XML:: <div tal:replace="structure table" /> .. _tales: Expressions (TALES) ################### The *Template Attribute Language Expression Syntax* (TALES) standard describes expressions that supply :ref:`tal` and :ref:`metal` with data. TALES is *one* possible expression syntax for these languages, but they are not bound to this definition. Similarly, TALES could be used in a context having nothing to do with TAL or METAL. TALES expressions are described below with any delimiter or quote markup from higher language layers removed. Here is the basic definition of TALES syntax:: Expression ::= [type_prefix ':'] String type_prefix ::= Name Here are some simple examples:: 1 + 2 None string:Hello, ${view.user_name} The optional *type prefix* determines the semantics and syntax of the *expression string* that follows it. A given implementation of TALES can define any number of expression types, with whatever syntax you like. It also determines which expression type is indicated by omitting the prefix. Types ----- These are the available TALES expression types: ============= ============== Prefix Description ============= ============== ``exists`` Evaluate the result inside an exception handler; if one of the exceptions ``AttributeError``, ``LookupError``, ``TypeError``, ``NameError``, or ``KeyError`` is raised during evaluation, the result is ``False``, otherwise ``True``. Note that the original result is discarded in any case. ``import`` Import a global symbol using dotted notation. ``load`` Load a template relative to the current template or absolute. ``not`` Negate the expression result ``python`` Evaluate a Python expression ``string`` Format a string ``structure`` Wraps the expression result as *structure*. ============= ============== .. note:: The default expression type is ``python``. .. warning:: The Zope reference engine defaults to a ``path`` expression type, which is closely tied to the Zope framework. This expression is not implemented in Chameleon (but it's available in a Zope framework compatibility package). There's a mechanism to allow fallback to alternative expressions, if one should fail (raise an exception). The pipe character ('|') is used to separate two expressions:: <div tal:define="page request.GET['page'] | 0"> This mechanism applies only to the ``python`` expression type, and by derivation ``string``. .. _tales_built_in_names: ``python`` ^^^^^^^^^^ Evaluates a Python expression. Syntax ~~~~~~ Python expression syntax:: Any valid Python language expression Description ~~~~~~~~~~~ Python expressions are executed natively within the translated template source code. There is no built-in security apparatus. ``string`` ^^^^^^^^^^ Syntax ~~~~~~ String expression syntax:: string_expression ::= ( plain_string | [ varsub ] )* varsub ::= ( '$' Variable ) | ( '${ Expression }' ) plain_string ::= ( '$$' | non_dollar )* non_dollar ::= any character except '$' Description ~~~~~~~~~~~ String expressions interpret the expression string as text. If no expression string is supplied the resulting string is *empty*. The string can contain variable substitutions of the form ``$name`` or ``${expression}``, where ``name`` is a variable name, and ``expression`` is a TALES-expression. The escaped string value of the expression is inserted into the string. .. note:: To prevent a ``$`` from being interpreted this way, it must be escaped as ``$$``. Examples ~~~~~~~~ Basic string formatting:: <span tal:replace="string:$this and $that"> Spam and Eggs </span> <p tal:content="string:${request.form['total']}"> total: 12 </p> Including a dollar sign:: <p tal:content="string:$$$cost"> cost: $42.00 </p> .. _import-expression: ``import`` ^^^^^^^^^^ Imports a module global. .. _structure-expression: ``structure`` ^^^^^^^^^^^^^ Wraps the expression result as *structure*: The replacement text is inserted into the document without escaping, allowing HTML/XML markup to be inserted. This can break your page if the text contains unanticipated markup (eg. text submitted via a web form), which is the reason that it is not the default. .. _load-expression: ``load`` ^^^^^^^^ Loads a template instance. Syntax ~~~~~~ Load expression syntax:: Relative or absolute file path Description ~~~~~~~~~~~ The template will be loaded using the same template class as the calling template. Examples ~~~~~~~~ Loading a template and using it as a macro:: <div tal:define="master load: ../master.pt" metal:use-macro="master" /> Built-in names -------------- These are the names always available in the TALES expression namespace: - ``default`` - special value used to specify that existing text or attributes should not be replaced. See the documentation for individual TAL statements for details on how they interpret *default*. - ``repeat`` - the *repeat* variables; see :ref:`tal_repeat` for more information. - ``template`` - reference to the template which was first called; this symbol is carried over when using macros. - ``macros`` - reference to the macros dictionary that corresponds to the current template. .. _metal: Macros (METAL) ############## The *Macro Expansion Template Attribute Language* (METAL) standard is a facility for HTML/XML macro preprocessing. It can be used in conjunction with or independently of TAL and TALES. Macros provide a way to define a chunk of presentation in one template, and share it in others, so that changes to the macro are immediately reflected in all of the places that share it. Additionally, macros are always fully expanded, even in a template's source text, so that the template appears very similar to its final rendering. A single Page Template can accomodate multiple macros. Namespace --------- The METAL namespace URI and recommended alias are currently defined as:: xmlns:metal="http://xml.zope.org/namespaces/metal" Just like the TAL namespace URI, this URI is not attached to a web page; it's just a unique identifier. This identifier must be used in all templates which use METAL. Statements ---------- METAL defines a number of statements: * ``metal:define-macro`` Define a macro. * ``metal:use-macro`` Use a macro. * ``metal:extend-macro`` Extend a macro. * ``metal:define-slot`` Define a macro customization point. * ``metal:fill-slot`` Customize a macro. Although METAL does not define the syntax of expression non-terminals, leaving that up to the implementation, a canonical expression syntax for use in METAL arguments is described in TALES Specification. ``define-macro`` ^^^^^^^^^^^^^^^^ Defines a macro. Syntax ~~~~~~ ``metal:define-macro`` syntax:: argument ::= Name Description ~~~~~~~~~~~ The ``metal:define-macro`` statement defines a macro. The macro is named by the statement expression, and is defined as the element and its sub-tree. Examples ~~~~~~~~ Simple macro definition:: <p metal:define-macro="copyright"> Copyright 2011, <em>Foobar</em> Inc. </p> ``define-slot`` ^^^^^^^^^^^^^^^ Defines a macro customization point. Syntax ~~~~~~ ``metal:define-slot`` syntax:: argument ::= Name Description ~~~~~~~~~~~ The ``metal:define-slot`` statement defines a macro customization point or *slot*. When a macro is used, its slots can be replaced, in order to customize the macro. Slot definitions provide default content for the slot. You will get the default slot contents if you decide not to customize the macro when using it. The ``metal:define-slot`` statement must be used inside a ``metal:define-macro`` statement. Slot names must be unique within a macro. Examples ~~~~~~~~ Simple macro with slot:: <p metal:define-macro="hello"> Hello <b metal:define-slot="name">World</b> </p> This example defines a macro with one slot named ``name``. When you use this macro you can customize the ``b`` element by filling the ``name`` slot. ``fill-slot`` ^^^^^^^^^^^^^ Customize a macro. Syntax ~~~~~~ ``metal:fill-slot`` syntax:: argument ::= Name Description ~~~~~~~~~~~ The ``metal:fill-slot`` statement customizes a macro by replacing a *slot* in the macro with the statement element (and its content). The ``metal:fill-slot`` statement must be used inside a ``metal:use-macro`` statement. Slot names must be unique within a macro. If the named slot does not exist within the macro, the slot contents will be silently dropped. Examples ~~~~~~~~ Given this macro:: <p metal:define-macro="hello"> Hello <b metal:define-slot="name">World</b> </p> You can fill the ``name`` slot like so:: <p metal:use-macro="container['master.html'].macros.hello"> Hello <b metal:fill-slot="name">Kevin Bacon</b> </p> ``use-macro`` ^^^^^^^^^^^^^ Use a macro. Syntax ~~~~~~ ``metal:use-macro`` syntax:: argument ::= expression Description ~~~~~~~~~~~ The ``metal:use-macro`` statement replaces the statement element with a macro. The statement expression describes a macro definition. .. note:: In Chameleon the expression may point to a template instance; in this case it will be rendered in its entirety. ``extend-macro`` ^^^^^^^^^^^^^^^^ Extends a macro. Syntax ~~~~~~ ``metal:extend-macro`` syntax:: argument ::= expression Description ~~~~~~~~~~~ To extend an existing macro, choose a name for the macro and add a define-macro attribute to a document element with the name as the argument. Add an extend-macro attribute to the document element with an expression referencing the base macro as the argument. The extend-macro must be used in conjunction with define-macro, and must not be used with use-macro. The element's subtree is the macro body. Examples ~~~~~~~~ :: <div metal:define-macro="page-header" metal:extend-macro="standard_macros['page-header']"> <div metal:fill-slot="breadcrumbs"> You are here: <div metal:define-slot="breadcrumbs"/> </div> </div> .. _i18n: Translation (I18N) ################## Translation of template contents and attributes is supported via the ``i18n`` namespace and message objects. Messages -------- The translation machinery defines a message as *any object* which is not a string or a number and which does not provide an ``__html__`` method. When any such object is inserted into the template, the translate function is invoked first to see if it needs translation. The result is always coerced to a native string before it's inserted into the template. Translation function -------------------- The simplest way to hook into the translation machinery is to provide a translation function to the template constructor or at render-time. In either case it should be passed as the keyword argument ``translate``. The function has the following signature: .. code-block:: python def translate(msgid, domain=None, mapping=None, context=None, target_language=None, default=None): ... The result should be a string or ``None``. If another type of object is returned, it's automatically coerced into a string. If `zope.i18n <http://pypi.python.org/pypi/zope.i18n>`_ is available, the translation machinery defaults to using its translation function. Note that this function requires messages to conform to the message class from `zope.i18nmessageid <http://pypi.python.org/pypi/zope.i18nmessageid>`_; specifically, messages must have attributes ``domain``, ``mapping`` and ``default``. Example use: .. code-block:: python from zope.i18nmessageid import MessageFactory _ = MessageFactory("food") apple = _(u"Apple") There's currently no further support for other translation frameworks. Using Zope's translation framework ----------------------------------- The translation function from ``zope.i18n`` relies on *translation domains* to provide translations. These are components that are registered for some translation domain identifier and which implement a ``translate`` method that translates messages for that domain. .. note:: To register translation domain components, the Zope Component Architecture must be used (see `zope.component <http://pypi.python.org/pypi/zope.component>`_). The easiest way to configure translation domains is to use the the ``registerTranslations`` ZCML-directive; this requires the use of the `zope.configuration <http://pypi.python.org/pypi/zope.configuration>`_ package. This will set up translation domains and gettext catalogs automatically: .. code-block:: xml <configure xmlns="http://namespaces.zope.org/zope" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> <i18n:registerTranslations directory="locales" /> </configure> The ``./locales`` directory must follow a particular directory structure: .. code-block:: bash ./locales/en/LC_MESSAGES ./locales/de/LC_MESSAGES ... In each of the ``LC_MESSAGES`` directories, one `GNU gettext <http://en.wikipedia.org/wiki/GNU_gettext>`_ file in the ``.po`` format must be present per translation domain: .. code-block:: po # ./locales/de/LC_MESSAGES/food.po msgid "" msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "Apple" msgstr "Apfel" It may be necessary to compile the message catalog using the ``msgfmt`` utility. This will produce a ``.mo`` file. Translation domains without gettext ----------------------------------- The following example demonstrates how to manually set up and configure a translation domain for which messages are provided directly:: from zope import component from zope.i18n.simpletranslationdomain import SimpleTranslationDomain food = SimpleTranslationDomain("food", { ('de', u'Apple'): u'Apfel', }) component.provideUtility(food, food.domain) An example of a custom translation domain class:: from zope import interface class TranslationDomain(object): interface.implements(ITranslationDomain) def translate(self, msgid, mapping=None, context=None, target_language=None, default=None): ... component.provideUtility(TranslationDomain(), name="custom") This approach can be used to integrate other translation catalog implementations. .. highlight:: xml Namespace --------- The ``i18n`` namespace URI and recommended prefix are currently defined as:: xmlns:i18n="http://xml.zope.org/namespaces/i18n" This is not a URL, but merely a unique identifier. Do not expect a browser to resolve it successfully. Statements ---------- The allowable ``i18n`` statements are: - ``i18n:translate`` - ``i18n:domain`` - ``i18n:source`` - ``i18n:target`` - ``i18n:name`` - ``i18n:attributes`` - ``i18n:data`` ``i18n:translate`` ^^^^^^^^^^^^^^^^^^ This attribute is used to mark units of text for translation. If this attribute is specified with an empty string as the value, the message ID is computed from the content of the element bearing this attribute. Otherwise, the value of the element gives the message ID. ``i18n:domain`` ^^^^^^^^^^^^^^^ The ``i18n:domain`` attribute is used to specify the domain to be used to get the translation. If not specified, the translation services will use a default domain. The value of the attribute is used directly; it is not a TALES expression. ``i18n:source`` ^^^^^^^^^^^^^^^ The ``i18n:source`` attribute specifies the language of the text to be translated. The default is ``nothing``, which means we don't provide this information to the translation services. ``i18n:target`` ^^^^^^^^^^^^^^^ The ``i18n:target`` attribute specifies the language of the translation we want to get. If the value is ``default``, the language negotiation services will be used to choose the destination language. If the value is ``nothing``, no translation will be performed; this can be used to suppress translation within a larger translated unit. Any other value must be a language code. The attribute value is a TALES expression; the result of evaluating the expression is the language code or one of the reserved values. .. note:: ``i18n:target`` is primarily used for hints to text extraction tools and translation teams. If you had some text that should only be translated to e.g. German, then it probably shouldn't be wrapped in an ``i18n:translate`` span. ``i18n:name`` ^^^^^^^^^^^^^ Name the content of the current element for use in interpolation within translated content. This allows a replaceable component in content to be re-ordered by translation. For example:: <span i18n:translate=''> <span tal:replace='context.name' i18n:name='name' /> was born in <span tal:replace='context.country_of_birth' i18n:name='country' />. </span> would cause this text to be passed to the translation service:: "${name} was born in ${country}." ``i18n:attributes`` ^^^^^^^^^^^^^^^^^^^ This attribute will allow us to translate attributes of HTML tags, such as the ``alt`` attribute in the ``img`` tag. The ``i18n:attributes`` attribute specifies a list of attributes to be translated with optional message IDs for each; if multiple attribute names are given, they must be separated by semicolons. Message IDs used in this context must not include whitespace. Note that the value of the particular attributes come either from the HTML attribute value itself or from the data inserted by ``tal:attributes``. If an attibute is to be both computed using ``tal:attributes`` and translated, the translation service is passed the result of the TALES expression for that attribute. An example:: <img src="http://foo.com/logo" alt="Visit us" tal:attributes="alt context.greeting" i18n:attributes="alt" > In this example, we let ``tal:attributes`` set the value of the ``alt`` attribute to the text "Stop by for a visit!". This text will be passed to the translation service, which uses the result of language negotiation to translate "Stop by for a visit!" into the requested language. The example text in the template, "Visit us", will simply be discarded. Another example, with explicit message IDs:: <img src="../icons/uparrow.png" alt="Up" i18n:attributes="src up-arrow-icon; alt up-arrow-alttext" > Here, the message ID ``up-arrow-icon`` will be used to generate the link to an icon image file, and the message ID 'up-arrow-alttext' will be used for the "alt" text. ``i18n:data`` ^^^^^^^^^^^^^ Since TAL always returns strings, we need a way in ZPT to translate objects, one of the most obvious cases being ``datetime`` objects. The ``data`` attribute will allow us to specify such an object, and ``i18n:translate`` will provide us with a legal format string for that object. If ``data`` is used, ``i18n:translate`` must be used to give an explicit message ID, rather than relying on a message ID computed from the content. Relation with TAL processing ---------------------------- The attributes defined in the ``i18n`` namespace modify the behavior of the TAL interpreter for the ``tal:attributes``, ``tal:content``, ``tal:repeat``, and ``tal:replace`` attributes, but otherwise do not affect TAL processing. Since these attributes only affect TAL processing by causing translations to occur at specific times, using these with a TAL processor which does not support the ``i18n`` namespace degrades well; the structural expectations for a template which uses the ``i18n`` support is no different from those for a page which does not. The only difference is that translations will not be performed in a legacy processor. Relation with METAL processing ------------------------------- When using translation with METAL macros, the internationalization context is considered part of the specific documents that page components are retrieved from rather than part of the combined page. This makes the internationalization context lexical rather than dynamic, making it easier for a site builder to understand the behavior of each element with respect to internationalization. Let's look at an example to see what this means:: <html i18n:translate='' i18n:domain='EventsCalendar' metal:use-macro="container['master.html'].macros.thismonth"> <div metal:fill-slot='additional-notes'> <ol tal:condition="context.notes"> <li tal:repeat="note context.notes"> <tal:block tal:omit-tag="" tal:condition="note.heading"> <strong tal:content="note.heading"> Note heading goes here </strong> <br /> </tal:block> <span tal:replace="note/description"> Some longer explanation for the note goes here. </span> </li> </ol> </div> </html> And the macro source:: <html i18n:domain='CalendarService'> <div tal:replace='python:DateTime().Month()' i18n:translate=''>January</div> <!-- really hairy TAL code here ;-) --> <div define-slot="additional-notes"> Place for the application to add additional notes if desired. </div> </html> Note that the macro is using a different domain than the application (which it should be). With lexical scoping, no special markup needs to be applied to cause the slot-filler in the application to be part of the same domain as the rest of the application's page components. If dynamic scoping were used, the internationalization context would need to be re-established in the slot-filler. Extracting translatable message ------------------------------- Translators use `PO files <http://www.gnu.org/software/hello/manual/gettext/PO-Files.html>`_ when translating messages. To create and update PO files you need to do two things: *extract* all messages from python and templates files and store them in a ``.pot`` file, and for each language *update* its ``.po`` file. Chameleon facilitates this by providing extractors for `Babel <http://babel.edgewall.org/>`_. To use this you need modify ``setup.py``. For example: .. code-block:: python from setuptools import setup setup(name="mypackage", install_requires = [ "Babel", ], message_extractors = { "src": [ ("**.py", "chameleon_python", None ), ("**.pt", "chameleon_xml", None ), ]}, ) This tells Babel to scan the ``src`` directory while using the ``chameleon_python`` extractor for all ``.py`` files and the ``chameleon_xml`` extractor for all ``.pt`` files. You can now use Babel to manage your PO files: .. code-block:: bash python setup.py extract_messages --output-file=i18n/mydomain.pot python setup.py update_catalog \ -l nl \ -i i18n/mydomain.pot \ -o i18n/nl/LC_MESSAGES/mydomain.po python setup.py compile_catalog \ --directory i18n --locale nl You can also configure default options in a ``setup.cfg`` file. For example:: [compile_catalog] domain = mydomain directory = i18n [extract_messages] copyright_holder = Acme Inc. output_file = i18n/mydomain.pot charset = UTF-8 [init_catalog] domain = mydomain input_file = i18n/mydomain.pot output_dir = i18n [update_catalog] domain = mydomain input_file = i18n/mydomain.pot output_dir = i18n previous = true You can now use the Babel commands directly:: python setup.py extract_messages python setup.py update_catalog python setup.py compile_catalog ${...} operator ############### The ``${...}`` notation is short-hand for text insertion. The Python-expression inside the braces is evaluated and the result included in the output (all inserted text is escaped by default): .. code-block:: html <div id="section-${index + 1}"> ${content} </div> To escape this behavior, prefix the notation with a backslash character: ``\${...}``. Note that if an object implements the ``__html__`` method, the result of this method will be inserted as-is (without XML escaping). Markup comments ############### You can apply the "!" and "?" modifiers to change how comments are processed: Drop ``<!--! This comment will be dropped from output -->`` Verbatim ``<!--? This comment will be included verbatim -->`` That is, evaluation of ``${...}`` expressions is disabled if the comment opens with the "?" character. .. _new-features: Language extensions ################### The page template language as implemented in the Chameleon library comes with a number of new features. Some take inspiration from `Genshi <http://genshi.edgewall.org/>`_. *New expression types* The :ref:`structure <structure-expression>` expression wraps an expression result as *structure*:: <div>${structure: body.text}</div> The :ref:`import <import-expression>` expression imports module globals:: <div tal:define="compile import: re.compile"> ... </div> This :ref:`load <load-expression>` expression loads templates relative to the current template:: <div tal:define="compile load: main.pt"> ... </div> *Tuple unpacking* The ``tal:define`` and ``tal:repeat`` statements supports tuple unpacking:: tal:define="(a, b, c) [1, 2, 3]" Extended `iterable unpacking <http://www.python.org/dev/peps/pep-3132/>`_ using the asterisk character is not currently supported (even for versions of Python that support it natively). *Dictionary lookup as fallback after attribute error* If attribute lookup (using the ``obj.<name>`` syntax) raises an ``AttributeError`` exception, a secondary lookup is attempted using dictionary lookup --- ``obj['<name>']``. Behind the scenes, this is done by rewriting all attribute-lookups to a custom lookup call: .. code-block:: python def lookup_attr(obj, key): try: return getattr(obj, key) except AttributeError as exc: try: get = obj.__getitem__ except AttributeError: raise exc try: return get(key) except KeyError: raise exc *Inline string substitution* In element attributes and in the text or tail of an element, string expression interpolation is available using the ``${...}`` syntax:: <span class="content-${item_type}"> ${title or item_id} </span> *Literal content* While the ``tal:content`` and ``tal:repeat`` attributes both support the ``structure`` keyword which inserts the content as a literal (without XML-escape), an object may also provide an ``__html__`` method to the same effect. The result of the method will be inserted as *structure*. This is particularly useful for content which is substituted using the expression operator: ``"${...}"`` since the ``structure`` keyword is not allowed here. *Switches* Two new attributes have been added: ``tal:switch`` and ``tal:case``. A case attribute works like a condition and only allows content if the value matches that of the nearest parent switch value. Incompatibilities and differences ################################# There are a number of incompatibilities and differences between the Chameleon language implementation and the Zope reference implementation (ZPT): *Default expression* The default expression type is Python. *Template arguments* Arguments passed by keyword to the render- or call method are inserted directly into the template execution namespace. This is different from ZPT where these are only available through the ``options`` dictionary. Zope:: <div tal:content="options/title" /> Chameleon:: <div tal:content="title" /> *Special symbols* The ``CONTEXTS`` symbol is not available. The `z3c.pt <http://pypi.python.org/pypi/z3c.pt>`_ package works as a compatibility layer. The template classes in this package provide a implementation which is fully compatible with ZPT. Notes ##### .. [1] This has been changed in 2.x. Previously, it was up to the expression engine to parse the expression values including any semicolons and since for instance Python-expressions can never end in a semicolon, it was possible to clearly distinguish between the different uses of the symbol, e.g. :: tal:define="text 'Hello world; goodbye world'" The semicolon appearing in the definition above is part of the Python-expression simply because it makes the expression valid. Meanwhile: :: tal:define="text1 'Hello world'; text2 'goodbye world'" The semicolon here must denote a second variable definition because there is no valid Python-expression that includes it. While this behavior works well in practice, it is incompatible with the reference specification, and also blurs the interface between the compiler and the expression engine. In 2.x we therefore have to escape the semicolon by doubling it (as defined by the specification): :: tal:define="text 'Hello world;; goodbye world'" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon-2.6.1/docs/index.rst����������������������������������������������������������������������0000644�0001750�0000144�00000016316�11640343073�016645� 0����������������������������������������������������������������������������������������������������ustar �mborch��������������������������users���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon ========= Chameleon is an HTML/XML template engine for `Python <http://www.python.org>`_. It's designed to generate the document output of a web application, typically HTML markup or XML. The language used is *page templates*, originally a `Zope <http://www.zope.org>`_ invention [1]_, but available here as a :ref:`standalone library <no-dependencies>` that you can use in any script or application running Python 2.5 and up (including 3.x and `pypy <http://pypy.org>`_). It comes with a set of :ref:`new features <new-features>`, too. The template engine compiles templates into Python byte-code and is optimized for speed. For a complex template language, the performance is :ref:`very good <fast>`. *Found a bug?* Please report issues to the `issue tracker <http://github.com/malthe/chameleon/issues>`_. *Need help?* Post to the Pylons `discussion list <http://groups.google.com/group/pylons-discuss/>`_ or join the ``#pyramid`` channel on `Freenode IRC <http://freenode.net/>`_. Getting the code ---------------- You can `download <http://pypi.python.org/pypi/Chameleon#downloads>`_ the package from the Python package index or install the latest release using setuptools or the newer `distribute <http://packages.python.org/distribute/>`_ (required for Python 3.x):: $ easy_install Chameleon .. _no-dependencies: There are no required library dependencies on Python 2.7 and up [2]_. On 2.5 and 2.6, the `ordereddict <http://pypi.python.org/pypi/ordereddict>`_ and `unittest2 <http://pypi.python.org/pypi/unittest2>`_ packages are set as dependencies. The project is hosted in a `GitHub repository <http://github.com/malthe/chameleon>`_. Code contributions are welcome. The easiest way is to use the `pull request <http://help.github.com/pull-requests/>`_ interface. Introduction ------------ The *page templates* language is used within your document structure as special element attributes and text markup. Using a set of simple language constructs, you control the document flow, element repetition, text replacement and translation. .. note:: If you've used page templates in a Zope environment previously, note that Chameleon uses Python as the default expression language (instead of *path* expressions). The basic language (known as the *template attribute language* or TAL) is simple enough to grasp from an example: .. code-block:: genshi <html> <body> <h1>Hello, ${'world'}!</h1> <table> <tr tal:repeat="row 'apple', 'banana', 'pineapple'"> <td tal:repeat="col 'juice', 'muffin', 'pie'"> ${row.capitalize()} ${col} </td> </tr> </table> </body> </html> The ``${...}`` notation is short-hand for text insertion [3]_. The Python-expression inside the braces is evaluated and the result included in the output. By default, the string is escaped before insertion. To avoid this, use the ``structure:`` prefix: .. code-block:: genshi <div>${structure: ...}</div> Note that if the expression result is an object that implements an ``__html__()`` method [4]_, this method will be called and the result treated as "structure". An example of such an object is the ``Markup`` class that's included as a utility:: from chameleon.utils import Markup username = "<tt>%s</tt>" % username The macro language (known as the *macro expansion language* or METAL) provides a means of filling in portions of a generic template. On the left, the macro template; on the right, a template that loads and uses the macro, filling in the "content" slot: .. code-block:: genshi <html xmlns="http://www.w3.org/1999/xhtml"> <metal:main use-macro="load: main.pt"> <head> <p metal:fill-slot="content">${structure: document.body}<p/> <title>Example — ${document.title}

${document.title}

In the example, the expression type :ref:`load ` is used to retrieve a template from the file system using a path relative to the calling template. The METAL system works with TAL such that you can for instance fill in a slot that appears in a ``tal:repeat`` loop, or refer to variables defined using ``tal:define``. The third language subset is the translation system (known as the *internationalization language* or I18N): .. code-block:: genshi ...
You have ${round(amount, 2)} dollars in your account.
... Each translation message is marked up using ``i18n:translate`` and values can be mapped using ``i18n:name``. Attributes are marked for translation using ``i18n:attributes``. The template engine generates `gettext `_ translation strings from the markup:: "You have ${amount} dollars in your account." If you use a web framework such as `Pyramid `_, the translation system is set up automatically and will negotiate on a *target language* based on the HTTP request or other parameter. If not, then you need to configure this manually. Next steps ---------- This was just an introduction. There are a number of other basic statements that you need to know in order to use the language. This is all covered in the :ref:`language reference `. If you're already familiar with the page template language, you can skip ahead to the :ref:`getting started ` section to learn how to use the template engine in your code. To learn about integration with your favorite web framework see the section on :ref:`framework integration `. License ------- This software is made available under a BSD-like license. Contents ======== .. toctree:: :maxdepth: 2 library.rst reference.rst integration.rst configuration.rst Indices and Tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` Notes ===== .. [1] The template language specifications and API for the Page Templates engine are based on Zope Page Templates (see in particular `zope.pagetemplate `_). However, the Chameleon compiler and Page Templates engine is an entirely new codebase, packaged as a standalone distribution. It does require a Zope software environment. .. [2] The translation system in Chameleon is pluggable and based on `gettext `_. There is built-in support for the `zope.i18n `_ package. If this package is installed, it will be used by default. The `translationstring `_ package offers some of the same helper and utility classes, without the Zope application interface. .. [3] This syntax was taken from `Genshi `_. .. [4] See the `WebHelpers `_ library which provide a simple wrapper around this method. Chameleon-2.6.1/docs/conf.py0000644000175000001440000001436711663360110016303 0ustar mborchusers00000000000000# -*- coding: utf-8 -*- # # Chameleon documentation build configuration file, created by # sphinx-quickstart on Sun Nov 1 16:08:00 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Chameleon' copyright = u'2008-2011 by Malthe Borch and the Repoze Community' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '2.6' # The full version, including alpha/beta/rc tags. release = '2.6.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = "Chameleon %s documentation" % version # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bchameleonm, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'chameleondoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'chameleon.tex', u'Chameleon Documentation', u'Malthe Borch et. al', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True Chameleon-2.6.1/docs/configuration.rst0000644000175000001440000000211511622523414020374 0ustar mborchusers00000000000000Configuration ============= Most settings can be provided as keyword-arguments to the template constructor classes. There are certain settings which are required at environment level. Acceptable values are ``"0"``, ``"1"``, or the literals ``"true"`` or ``"false"`` (case-insensitive). General usage ------------- The following settings are useful in general. ``CHAMELEON_EAGER`` Parse and compile templates on instantiation. ``CHAMELEON_CACHE`` When set to a file system path, the template compiler will write its output to files in this directory and use it as a cache. This not only enables you to see the compiler output, but also speeds up startup. ``CHAMELEON_RELOAD`` This setting controls the default value of the ``auto_reload`` parameter. Development ----------- The following settings are mostly useful during development or debugging of the library itself. ``CHAMELEON_DEBUG`` Enables a set of debugging settings which make it easier to discover and research issues with the engine itself. This implicitly enables auto-reload for any template. Chameleon-2.6.1/docs/integration.rst0000644000175000001440000000241711622474733020066 0ustar mborchusers00000000000000.. _framework-integration: Integration =========== Integration with Chameleon is available for a number of popular web frameworks. The framework will usually provide loading mechanisms and translation (internationalization) configuration. Pyramid ------- Chameleon is the default template engine for the `Pyramid `_ framework. See the section on `Page Templates `_ for a complete reference. Zope 2 / Plone -------------- Install the `five.pt `_ package to replace the reference template engine (globally). Zope Toolkit (ZTK) ------------------ Install the `z3c.pt `_ package for applications based on the `Zope Toolkit `_ (ZTK). Note that you need to explicit use the template classes from this package. Grok ---- Support for the `Grok `_ framework is available in the `grokcore.chameleon `_ package. This package will setup Grok's policy for templating integration and associate the Chameleon template components for the ``.cpt`` template filename extension. Chameleon-2.6.1/docs/library.rst0000644000175000001440000001502511634052671017202 0ustar mborchusers00000000000000Library Documentation ===================== This section documents the package as a Python library. To learn about the page template language, consult the :ref:`language reference `. .. _getting-started-with-cpt: Getting started --------------- There are several template constructor classes available, one for each of the combinations *text* or *xml*, and *string* or *file*. The file-based constructor requires an absolute path. To set up a templates directory *once*, use the template loader class:: import os path = os.path.basedir(__file__) from chameleon import PageTemplateLoader templates = PageTemplateLoader(os.path.join(path, "templates")) Then, to load a template relative to the provided path, use dictionary syntax:: template = templates['hello.pt'] Alternatively, use the appropriate template class directly. Let's try with a string input:: from chameleon import PageTemplate template = PageTemplate("
Hello, ${name}.
") All template instances are callable. Provide variables by keyword argument:: >>> template(name='John') '
Hello, John.
' .. _fast: Performance ----------- The template engine compiles (or *translates*) template source code into Python byte-code. In simple templates this yields an increase in performance of about 7 times in comparison to the reference implementation. In benchmarks for the content management system `Plone `_, switching to Chameleon yields a request to response improvement of 20-50%. Extension --------- You can extend the language through the expression engine by writing your own expression compiler. Let's try and write an expression compiler for an expression type that will simply uppercase the supplied value. We'll call it ``upper``. You can write such a compiler as a closure: .. code-block:: python import ast def uppercase_expression(string): def compiler(target, engine): uppercased = self.string.uppercase() value = ast.Str(uppercased) return [ast.Assign(targets=[target], value=value)] return compiler To make it available under a certain prefix, we'll add it to the expression types dictionary. .. code-block:: python from chameleon import PageTemplate PageTemplate.expression_types['upper'] = uppercase_expression Alternatively, you could subclass the template class and set the attribute ``expression_types`` to a dictionary that includes your expression: .. code-block:: python from chameleon import PageTemplateFile from chameleon.tales import PythonExpr class MyPageTemplateFile(PageTemplateFile): expression_types = { 'python': PythonExpr, 'upper': uppercase_expression } You can now uppercase strings *natively* in your templates::
It's probably best to stick with a Python expression::
.. _whats-new: Changes between 1.x and 2.x --------------------------- This sections describes new features, improvements and changes from 1.x to 2.x. New parser ~~~~~~~~~~ This series features a new, custom-built parser, implemented in pure Python. It parses both HTML and XML inputs (the previous parser relied on the expat system library and was more strict about its input). The main benefit of the new parser is that the compiler is now able to point to the source location of parse- and compilation errors much more accurately. This should be a great aid in debugging these errors. Compatible output ~~~~~~~~~~~~~~~~~ The 2.x engine matches the output of the reference implementation more closely (usually exactly). There are less differences altogether; for instance, the method of escaping TALES expression (usually a semicolon) has been changed to match that of the reference implementation. New language features ~~~~~~~~~~~~~~~~~~~~~ This series also introduces a number of new language features: 1. Support for the ``tal:on-error`` from the reference specification has been added. 2. Two new attributes ``tal:switch`` and ``tal:case`` have been added to make element conditions more flexible. Code improvements ~~~~~~~~~~~~~~~~~ The template classes have been refactored and simplified allowing better reuse of code and more intuitive APIs on the lower levels. Expression engine ~~~~~~~~~~~~~~~~~ The expression engine has been redesigned to make it easier to understand and extend. The new engine is based on the ``ast`` module (available since Python 2.6; backports included for Python 2.5). This means that expression compilers now need to return a valid list of AST statements that include an assignment to the target node. Compiler ~~~~~~~~ The new compiler has been optimized for complex templates. As a result, in the benchmark suite included with the package, this compiler scores about half of the 1.x series. For most real world applications, the engine should still perform as well as the 1.x series. API reference ------------- This section describes the documented API of the library. Template classes ~~~~~~~~~~~~~~~~ Use the ``PageTemplate*`` template classes to define a template from a string or file input: .. automodule:: chameleon .. autoclass:: chameleon.PageTemplate Note: The remaining classes take the same general configuration arguments. .. automethod:: render .. autoclass:: chameleon.PageTemplateFile(filename, **config) .. autoclass:: chameleon.PageTextTemplate .. autoclass:: chameleon.PageTextTemplateFile Template loader ~~~~~~~~~~~~~~~ Some systems have framework support for loading templates from files. The following loader class is directly compatible with the Pylons framework and may be adapted to other frameworks: .. class:: chameleon.PageTemplateLoader(search_path=None, default_extension=None, **config) Load templates from ``search_path`` (must be a string or a list of strings):: templates = PageTemplateLoader(path) example = templates['example.pt'] If ``default_extension`` is provided, this will be added to inputs that do not already have an extension:: templates = PageTemplateLoader(path, ".pt") example = templates['example'] Any additional keyword arguments will be passed to the template constructor:: templates = PageTemplateLoader(path, debug=True, encoding="utf-8") .. automethod:: load Expression engine ~~~~~~~~~~~~~~~~~ For advanced integration, the compiler module provides support for dynamic expression evaluation: .. automodule:: chameleon.compiler .. autoclass:: chameleon.compiler.ExpressionEvaluator Chameleon-2.6.1/src/0000755000175000001440000000000011665422242014637 5ustar mborchusers00000000000000Chameleon-2.6.1/src/Chameleon.egg-info/0000755000175000001440000000000011665422242020224 5ustar mborchusers00000000000000Chameleon-2.6.1/src/Chameleon.egg-info/SOURCES.txt0000644000175000001440000003256211665422237022124 0ustar mborchusers00000000000000.gitignore CHANGES.rst COPYRIGHT.txt LICENSE.txt Makefile README.rst distribute_setup.py setup.cfg setup.py tox.ini benchmarks/bm_chameleon.py benchmarks/bm_mako.py benchmarks/util.py docs/conf.py docs/configuration.rst docs/index.rst docs/integration.rst docs/library.rst docs/reference.rst src/Chameleon.egg-info/PKG-INFO src/Chameleon.egg-info/SOURCES.txt src/Chameleon.egg-info/dependency_links.txt src/Chameleon.egg-info/not-zip-safe src/Chameleon.egg-info/top_level.txt src/chameleon/__init__.py src/chameleon/ast24.py src/chameleon/astutil.py src/chameleon/benchmark.py src/chameleon/codegen.py src/chameleon/compiler.py src/chameleon/config.py src/chameleon/exc.py src/chameleon/i18n.py src/chameleon/interfaces.py src/chameleon/loader.py src/chameleon/metal.py src/chameleon/namespaces.py src/chameleon/nodes.py src/chameleon/parser.py src/chameleon/program.py src/chameleon/py25.py src/chameleon/py26.py src/chameleon/tal.py src/chameleon/tales.py src/chameleon/template.py src/chameleon/tokenize.py src/chameleon/utils.py src/chameleon/tests/__init__.py src/chameleon/tests/test_doctests.py src/chameleon/tests/test_loader.py src/chameleon/tests/test_parser.py src/chameleon/tests/test_sniffing.py src/chameleon/tests/test_templates.py src/chameleon/tests/test_tokenizer.py src/chameleon/tests/inputs/001-interpolation.txt src/chameleon/tests/inputs/001-variable-scope.html src/chameleon/tests/inputs/001-variable-scope.pt src/chameleon/tests/inputs/001.xml src/chameleon/tests/inputs/002-repeat-scope.pt src/chameleon/tests/inputs/002.xml src/chameleon/tests/inputs/003-content.pt src/chameleon/tests/inputs/003.xml src/chameleon/tests/inputs/004-attributes.pt src/chameleon/tests/inputs/004.xml src/chameleon/tests/inputs/005-default.pt src/chameleon/tests/inputs/005.xml src/chameleon/tests/inputs/006-attribute-interpolation.pt src/chameleon/tests/inputs/006.xml src/chameleon/tests/inputs/007-content-interpolation.pt src/chameleon/tests/inputs/007.xml src/chameleon/tests/inputs/008-builtins.pt src/chameleon/tests/inputs/008.xml src/chameleon/tests/inputs/009-literals.pt src/chameleon/tests/inputs/009.xml src/chameleon/tests/inputs/010-structure.pt src/chameleon/tests/inputs/010.xml src/chameleon/tests/inputs/011-messages.pt src/chameleon/tests/inputs/011.xml src/chameleon/tests/inputs/012-translation.pt src/chameleon/tests/inputs/012.xml src/chameleon/tests/inputs/013-repeat-nested.pt src/chameleon/tests/inputs/013.xml src/chameleon/tests/inputs/014-repeat-nested-similar.pt src/chameleon/tests/inputs/014.xml src/chameleon/tests/inputs/015-translation-nested.pt src/chameleon/tests/inputs/015.xml src/chameleon/tests/inputs/016-explicit-translation.pt src/chameleon/tests/inputs/016.xml src/chameleon/tests/inputs/017-omit-tag.pt src/chameleon/tests/inputs/017.xml src/chameleon/tests/inputs/018-translation-nested-dynamic.pt src/chameleon/tests/inputs/018.xml src/chameleon/tests/inputs/019-replace.pt src/chameleon/tests/inputs/019.xml src/chameleon/tests/inputs/020-on-error.pt src/chameleon/tests/inputs/020.xml src/chameleon/tests/inputs/021-translation-domain.pt src/chameleon/tests/inputs/021.xml src/chameleon/tests/inputs/022-switch.pt src/chameleon/tests/inputs/022.xml src/chameleon/tests/inputs/023-condition.pt src/chameleon/tests/inputs/023.xml src/chameleon/tests/inputs/024-namespace-elements.pt src/chameleon/tests/inputs/024.xml src/chameleon/tests/inputs/025-repeat-whitespace.pt src/chameleon/tests/inputs/025.xml src/chameleon/tests/inputs/026-repeat-variable.pt src/chameleon/tests/inputs/026.xml src/chameleon/tests/inputs/027-attribute-replacement.pt src/chameleon/tests/inputs/027.xml src/chameleon/tests/inputs/028-attribute-toggle.pt src/chameleon/tests/inputs/028.xml src/chameleon/tests/inputs/029-attribute-ordering.pt src/chameleon/tests/inputs/029.xml src/chameleon/tests/inputs/030-repeat-tuples.pt src/chameleon/tests/inputs/030.xml src/chameleon/tests/inputs/031-namespace-with-tal.pt src/chameleon/tests/inputs/031.xml src/chameleon/tests/inputs/032-master-template.pt src/chameleon/tests/inputs/032.xml src/chameleon/tests/inputs/033-use-macro-trivial.pt src/chameleon/tests/inputs/033.xml src/chameleon/tests/inputs/034-use-template-as-macro.pt src/chameleon/tests/inputs/034.xml src/chameleon/tests/inputs/035-use-macro-with-fill-slot.pt src/chameleon/tests/inputs/035.xml src/chameleon/tests/inputs/036-use-macro-inherits-dynamic-scope.pt src/chameleon/tests/inputs/036.xml src/chameleon/tests/inputs/037-use-macro-local-variable-scope.pt src/chameleon/tests/inputs/037.xml src/chameleon/tests/inputs/038-use-macro-globals.pt src/chameleon/tests/inputs/038.xml src/chameleon/tests/inputs/039-globals.pt src/chameleon/tests/inputs/039.xml src/chameleon/tests/inputs/040-macro-using-template-symbol.pt src/chameleon/tests/inputs/040.xml src/chameleon/tests/inputs/041-translate-nested-names.pt src/chameleon/tests/inputs/041.xml src/chameleon/tests/inputs/042-use-macro-fill-footer.pt src/chameleon/tests/inputs/042.xml src/chameleon/tests/inputs/043-macro-nested-dynamic-vars.pt src/chameleon/tests/inputs/043.xml src/chameleon/tests/inputs/044-tuple-define.pt src/chameleon/tests/inputs/044.xml src/chameleon/tests/inputs/045-namespaces.pt src/chameleon/tests/inputs/045.xml src/chameleon/tests/inputs/046-extend-macro.pt src/chameleon/tests/inputs/046.xml src/chameleon/tests/inputs/047-use-extended-macro.pt src/chameleon/tests/inputs/047.xml src/chameleon/tests/inputs/048-use-extended-macro-fill-original.pt src/chameleon/tests/inputs/048.xml src/chameleon/tests/inputs/049-entities-in-attributes.pt src/chameleon/tests/inputs/049.xml src/chameleon/tests/inputs/050-define-macro-and-use-not-extend.pt src/chameleon/tests/inputs/050.xml src/chameleon/tests/inputs/051-use-non-extended-macro.pt src/chameleon/tests/inputs/051.xml src/chameleon/tests/inputs/052-i18n-domain-inside-filled-slot.pt src/chameleon/tests/inputs/052.xml src/chameleon/tests/inputs/053-special-characters-in-attributes.pt src/chameleon/tests/inputs/053.xml src/chameleon/tests/inputs/054-import-expression.pt src/chameleon/tests/inputs/054.xml src/chameleon/tests/inputs/055-attribute-fallback-to-dict-lookup.pt src/chameleon/tests/inputs/055.xml src/chameleon/tests/inputs/056-comment-attribute.pt src/chameleon/tests/inputs/056.xml src/chameleon/tests/inputs/057-order.pt src/chameleon/tests/inputs/057.xml src/chameleon/tests/inputs/058-script.pt src/chameleon/tests/inputs/058.xml src/chameleon/tests/inputs/059-embedded-javascript.pt src/chameleon/tests/inputs/059.xml src/chameleon/tests/inputs/060-macro-with-multiple-same-slots.pt src/chameleon/tests/inputs/060.xml src/chameleon/tests/inputs/061-fill-one-slot-but-two-defined.pt src/chameleon/tests/inputs/061.xml src/chameleon/tests/inputs/062-comments-and-expressions.pt src/chameleon/tests/inputs/062.xml src/chameleon/tests/inputs/063-continuation.pt src/chameleon/tests/inputs/063.xml src/chameleon/tests/inputs/064-tags-and-special-characters.pt src/chameleon/tests/inputs/064.xml src/chameleon/tests/inputs/065-use-macro-in-fill.pt src/chameleon/tests/inputs/065.xml src/chameleon/tests/inputs/066-load-expression.pt src/chameleon/tests/inputs/066.xml src/chameleon/tests/inputs/067-attribute-decode.pt src/chameleon/tests/inputs/067.xml src/chameleon/tests/inputs/068-less-than-greater-than-in-attributes.pt src/chameleon/tests/inputs/068.xml src/chameleon/tests/inputs/069-translation-domain-and-macro.pt src/chameleon/tests/inputs/069.xml src/chameleon/tests/inputs/070-translation-domain-and-use-macro.pt src/chameleon/tests/inputs/070.xml src/chameleon/tests/inputs/071-html-attribute-defaults.pt src/chameleon/tests/inputs/071.xml src/chameleon/tests/inputs/072-repeat-interpolation.pt src/chameleon/tests/inputs/072.xml src/chameleon/tests/inputs/073-utf8-encoded.pt src/chameleon/tests/inputs/073.xml src/chameleon/tests/inputs/074-encoded-template.pt src/chameleon/tests/inputs/074.xml src/chameleon/tests/inputs/075-nested-macros.pt src/chameleon/tests/inputs/075.xml src/chameleon/tests/inputs/076-nested-macro-override.pt src/chameleon/tests/inputs/076.xml src/chameleon/tests/inputs/077-i18n-attributes.pt src/chameleon/tests/inputs/077.xml src/chameleon/tests/inputs/078-tags-and-newlines.pt src/chameleon/tests/inputs/078.xml src/chameleon/tests/inputs/079-implicit-i18n.pt src/chameleon/tests/inputs/079.xml src/chameleon/tests/inputs/080-xmlns-namespace-on-tal.pt src/chameleon/tests/inputs/080.xml src/chameleon/tests/inputs/081.xml src/chameleon/tests/inputs/082.xml src/chameleon/tests/inputs/083.xml src/chameleon/tests/inputs/084.xml src/chameleon/tests/inputs/085.xml src/chameleon/tests/inputs/086.xml src/chameleon/tests/inputs/087.xml src/chameleon/tests/inputs/088.xml src/chameleon/tests/inputs/089.xml src/chameleon/tests/inputs/090.xml src/chameleon/tests/inputs/091.xml src/chameleon/tests/inputs/092.xml src/chameleon/tests/inputs/093.xml src/chameleon/tests/inputs/094.xml src/chameleon/tests/inputs/095.xml src/chameleon/tests/inputs/096.xml src/chameleon/tests/inputs/097.xml src/chameleon/tests/inputs/098.xml src/chameleon/tests/inputs/099.xml src/chameleon/tests/inputs/100.xml src/chameleon/tests/inputs/101-unclosed-tags.html src/chameleon/tests/inputs/101.xml src/chameleon/tests/inputs/102-unquoted-attributes.html src/chameleon/tests/inputs/102.xml src/chameleon/tests/inputs/103-simple-attribute.html src/chameleon/tests/inputs/103.xml src/chameleon/tests/inputs/104.xml src/chameleon/tests/inputs/105.xml src/chameleon/tests/inputs/106.xml src/chameleon/tests/inputs/107.xml src/chameleon/tests/inputs/108.xml src/chameleon/tests/inputs/109.xml src/chameleon/tests/inputs/110.xml src/chameleon/tests/inputs/111.xml src/chameleon/tests/inputs/112.xml src/chameleon/tests/inputs/113.xml src/chameleon/tests/inputs/114.xml src/chameleon/tests/inputs/115.xml src/chameleon/tests/inputs/116.xml src/chameleon/tests/inputs/117.xml src/chameleon/tests/inputs/118.xml src/chameleon/tests/inputs/119.xml src/chameleon/tests/inputs/greeting.pt src/chameleon/tests/inputs/hello_world.pt src/chameleon/tests/inputs/hello_world.txt src/chameleon/tests/outputs/001.html src/chameleon/tests/outputs/001.pt src/chameleon/tests/outputs/001.txt src/chameleon/tests/outputs/002.pt src/chameleon/tests/outputs/003.pt src/chameleon/tests/outputs/004.pt src/chameleon/tests/outputs/005.pt src/chameleon/tests/outputs/006.pt src/chameleon/tests/outputs/007.pt src/chameleon/tests/outputs/008.pt src/chameleon/tests/outputs/009.pt src/chameleon/tests/outputs/010.pt src/chameleon/tests/outputs/011-en.pt src/chameleon/tests/outputs/011.pt src/chameleon/tests/outputs/012-en.pt src/chameleon/tests/outputs/012.pt src/chameleon/tests/outputs/013.pt src/chameleon/tests/outputs/014.pt src/chameleon/tests/outputs/015-en.pt src/chameleon/tests/outputs/015.pt src/chameleon/tests/outputs/016-en.pt src/chameleon/tests/outputs/016.pt src/chameleon/tests/outputs/017.pt src/chameleon/tests/outputs/018-en.pt src/chameleon/tests/outputs/018.pt src/chameleon/tests/outputs/019.pt src/chameleon/tests/outputs/020.pt src/chameleon/tests/outputs/021-en.pt src/chameleon/tests/outputs/021.pt src/chameleon/tests/outputs/022.pt src/chameleon/tests/outputs/023.pt src/chameleon/tests/outputs/024.pt src/chameleon/tests/outputs/025.pt src/chameleon/tests/outputs/026.pt src/chameleon/tests/outputs/027.pt src/chameleon/tests/outputs/028.pt src/chameleon/tests/outputs/029.pt src/chameleon/tests/outputs/030.pt src/chameleon/tests/outputs/031.pt src/chameleon/tests/outputs/032.pt src/chameleon/tests/outputs/033.pt src/chameleon/tests/outputs/034.pt src/chameleon/tests/outputs/035.pt src/chameleon/tests/outputs/036.pt src/chameleon/tests/outputs/037.pt src/chameleon/tests/outputs/038.pt src/chameleon/tests/outputs/039.pt src/chameleon/tests/outputs/040.pt src/chameleon/tests/outputs/041.pt src/chameleon/tests/outputs/042.pt src/chameleon/tests/outputs/043.pt src/chameleon/tests/outputs/044.pt src/chameleon/tests/outputs/045.pt src/chameleon/tests/outputs/046.pt src/chameleon/tests/outputs/047.pt src/chameleon/tests/outputs/048.pt src/chameleon/tests/outputs/049.pt src/chameleon/tests/outputs/050.pt src/chameleon/tests/outputs/051.pt src/chameleon/tests/outputs/052.pt src/chameleon/tests/outputs/053.pt src/chameleon/tests/outputs/054.pt src/chameleon/tests/outputs/055.pt src/chameleon/tests/outputs/056.pt src/chameleon/tests/outputs/057.pt src/chameleon/tests/outputs/058.pt src/chameleon/tests/outputs/059.pt src/chameleon/tests/outputs/060.pt src/chameleon/tests/outputs/061.pt src/chameleon/tests/outputs/062.pt src/chameleon/tests/outputs/063.pt src/chameleon/tests/outputs/064.pt src/chameleon/tests/outputs/065.pt src/chameleon/tests/outputs/066.pt src/chameleon/tests/outputs/067.pt src/chameleon/tests/outputs/068.pt src/chameleon/tests/outputs/069-en.pt src/chameleon/tests/outputs/069.pt src/chameleon/tests/outputs/070-en.pt src/chameleon/tests/outputs/070.pt src/chameleon/tests/outputs/071.pt src/chameleon/tests/outputs/072.pt src/chameleon/tests/outputs/073.pt src/chameleon/tests/outputs/074.pt src/chameleon/tests/outputs/075.pt src/chameleon/tests/outputs/076.pt src/chameleon/tests/outputs/077-en.pt src/chameleon/tests/outputs/077.pt src/chameleon/tests/outputs/078.pt src/chameleon/tests/outputs/079-en.pt src/chameleon/tests/outputs/079.pt src/chameleon/tests/outputs/080.pt src/chameleon/tests/outputs/101.html src/chameleon/tests/outputs/102.html src/chameleon/tests/outputs/103.html src/chameleon/tests/outputs/greeting.pt src/chameleon/tests/outputs/hello_world.pt src/chameleon/tests/outputs/hello_world.txt src/chameleon/zpt/__init__.py src/chameleon/zpt/loader.py src/chameleon/zpt/program.py src/chameleon/zpt/template.pyChameleon-2.6.1/src/Chameleon.egg-info/not-zip-safe0000644000175000001440000000000111520513763022451 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/Chameleon.egg-info/PKG-INFO0000644000175000001440000007516411665422237021342 0ustar mborchusers00000000000000Metadata-Version: 1.0 Name: Chameleon Version: 2.6.1 Summary: Fast HTML/XML Template Compiler. Home-page: http://www.pagetemplates.org/ Author: Malthe Borch Author-email: mborch@gmail.com License: BSD-like (http://repoze.org/license.html) Description: Overview ======== Chameleon is an HTML/XML template engine for `Python `_. It uses the *page templates* language. You can use it in any Python web application with just about any version of Python (2.5 and up, including 3.x and `pypy `_). Visit the `website `_ for more information or the `documentation `_. License and Copyright --------------------- This software is made available as-is under a BSD-like license [1]_ (see included copyright notice). Notes ----- .. [1] This software is licensed under the `Repoze `_ license. Changes ======= 2.6.1 (2011-11-30) ------------------ Bugfixes: - Decode HTML entities in expression interpolation strings. This fixes issue #74. - Allow ``xml`` and ``xmlns`` attributes on TAL, I18N and METAL namespace elements. This fixes issue #73. 2.6.0 (2011-11-24) ------------------ Features: - Added support for implicit translation: The ``implicit_i18n_translate`` option enables implicit translation of text. The ``implicit_i18n_attributes`` enables implicit translation of attributes. The latter must be a set and for an attribute to be implicitly translated, its lowercase string value must be included in the set. - Added option ``strict`` (enabled by default) which decides whether expressions are required to be valid at compile time. That is, if not set, an exception is only raised for an invalid expression at evaluation time. - An expression error now results in an exception only if the expression is attempted evaluated during a rendering. - Added a configuration option ``prepend_relative_search_path`` which decides whether the path relative to a file-based template is prepended to the load search path. The default is ``True``. - Added a configuration option ``search_path`` to the file-based template class, which adds additional paths to the template load instance bound to the ``load:`` expression. The option takes a string path or an iterable yielding string paths. The default value is the empty set. Bugfixes: - Exception instances now support pickle/unpickle. - An attributes in i18n:attributes no longer needs to match an existing or dynamic attribute in order to appear in the element. This fixes issue #66. 2.5.3 (2011-10-23) ------------------ Bugfixes: - Fixed an issue where a nested macro slot definition would fail even though there existed a parent macro definition. This fixes issue #69. 2.5.2 (2011-10-12) ------------------ Bugfixes: - Fixed an issue where technically invalid input would result in a compiler error. Features: - The markup class now inherits from the unicode string type such that it's compatible with the string interface. 2.5.1 (2011-09-29) ------------------ Bugfixes: - The symbol names "convert", "decode" and "translate" are now no longer set as read-only *compiler internals*. This fixes issue #65. - Fixed an issue where a macro extension chain nested two levels (a template uses a macro that extends a macro) would lose the middle slot definitions if slots were defined nested. The compiler now throws an error if a nested slot definition is used outside a macro extension context. 2.5.0 (2011-09-23) ------------------ Features: - An expression type ``structure:`` is now available which wraps the expression result as *structure* such that it is not escaped on insertion, e.g.::
${structure: context.body}
This also means that the ``structure`` keyword for ``tal:content`` and ``tal:replace`` now has an alternative spelling via the expression type ``structure:``. - The string-based template constructor now accepts encoded input. 2.4.6 (2011-09-23) ------------------ Bugfixes: - The ``tal:on-error`` statement should catch all exceptions. - Fixed issue that would prevent escaping of interpolation expression values appearing in text. 2.4.5 (2011-09-21) ------------------ Bugfixes: - The ``tal:on-error`` handler should have a ``error`` variable defined that has the value of the exception thrown. - The ``tal:on-error`` statement is a substitution statement and should support the "text" and "structure" insertion methods. 2.4.4 (2011-09-15) ------------------ Bugfixes: - An encoding specified in the XML document preamble is now read and used to decode the template input to unicode. This fixes issue #55. - Encoded expression input on Python 3 is now correctly decoded. Previously, the string representation output would be included instead of an actually decoded string. - Expression result conversion steps are now correctly included in error handling such that the exception output points to the expression location. 2.4.3 (2011-09-13) ------------------ Features: - When an encoding is provided, pass the 'ignore' flag to avoid decoding issues with bad input. Bugfixes: - Fixed pypy compatibility issue (introduced in previous release). 2.4.2 (2011-09-13) ------------------ Bugfixes: - Fixed an issue in the compiler where an internal variable (such as a translation default value) would be cached, resulting in variable scope corruption (see issue #49). 2.4.1 (2011-09-08) ------------------ Bugfixes: - Fixed an issue where a default value for an attribute would sometimes spill over into another attribute. - Fixed issue where the use of the ``default`` name in an attribute interpolation expression would print the attribute value. This is unexpected, because it's an expression, not a static text suitable for output. An attribute value of ``default`` now correctly drops the attribute. 2.4.0 (2011-08-22) ------------------ Features: - Added an option ``boolean_attributes`` to evaluate and render a provided set of attributes using a boolean logic: if the attribute is a true value, the value will be the attribute name, otherwise the attribute is dropped. In the reference implementation, the following attributes are configured as boolean values when the template is rendered in HTML-mode:: "compact", "nowrap", "ismap", "declare", "noshade", "checked", "disabled", "readonly", "multiple", "selected", "noresize", "defer" Note that in Chameleon, these attributes must be manually provided. Bugfixes: - The carriage return character (used on Windows platforms) would incorrectly be included in Python comments. It is now replaced with a line break. This fixes issue #44. 2.3.8 (2011-08-19) ------------------ - Fixed import error that affected Python 2.5 only. 2.3.7 (2011-08-19) ------------------ Features: - Added an option ``literal_false`` that disables the default behavior of dropping an attribute for a value of ``False`` (in addition to ``None``). This modified behavior is the behavior exhibited in reference implementation. Bugfixes: - Undo attribute special HTML attribute behavior (see previous release). This turned out not to be a compatible behavior; rather, boolean values should simply be coerced to a string. Meanwhile, the reference implementation does support an HTML mode in which the special attribute behavior is exhibited. We do not currently support this mode. 2.3.6 (2011-08-18) ------------------ Features: - Certain HTML attribute names now have a special behavior for a attribute value of ``True`` (or ``default`` if no default is defined). For these attributes, this return value will result in the name being printed as the value:: will be rendered as:: This behavior is compatible with the reference implementation. 2.3.5 (2011-08-18) ------------------ Features: - Added support for the set operator (``{item, item, ...}``). Bugfixes: - If macro is defined on the same element as a translation name, this no longer results in a "translation name not allowed outside translation" error. This fixes issue #43. - Attribute fallback to dictionary lookup now works on multiple items (e.g. ``d1.d2.d2``). This fixes issue #42. 2.3.4 (2011-08-16) ------------------ Features: - When inserting content in either attributes or text, a value of ``True`` (like ``False`` and ``None``) will result in no action. - Use statically assigned variables for ``"attrs"`` and ``"default"``. This change yields a performance improvement of 15-20%. - The template loader class now accepts an optional argument ``default_extension`` which accepts a filename extension which will be appended to the filename if there's not already an extension. Bugfixes: - The default symbol is now ``True`` for an attribute if the attribute default is not provided. Note that the result is that the attribute is dropped. This fixes issue #41. - Fixed an issue where assignment to a variable ``"type"`` would fail. This fixes issue #40. - Fixed an issue where an (unsuccesful) assignment for a repeat loop to a compiler internal name would not result in an error. - If the translation function returns the identical object, manually coerce it to string. This fixes a compatibility issue with translation functions which do not convert non-string objects to a string value, but simply return them unchanged. 2.3.3 (2011-08-15) ------------------ Features: - The ``load:`` expression now passes the initial keyword arguments to its template loader (e.g. ``auto_reload`` and ``encoding``). - In the exception output, string variable values are now limited to a limited output of characters, single line only. Bugfixes: - Fixed horizontal alignment of exception location info (i.e. 'String:', 'Filename:' and 'Location:') such that they match the template exception formatter. 2.3.2 (2011-08-11) ------------------ Bugfixes: - Fixed issue where i18n:domain would not be inherited through macros and slots. This fixes issue #37. 2.3.1 (2011-08-11) ------------------ Features: - The ``Builtin`` node type may now be used to represent any Python local or global name. This allows expression compilers to refer to e.g. ``get`` or ``getitem``, or to explicit require a builtin object such as one from the ``extra_builtins`` dictionary. Bugfixes: - Builtins which are not explicitly disallowed may now be redefined and used as variables (e.g. ``nothing``). - Fixed compiler issue with circular node annotation loop. 2.3 (2011-08-10) ---------------- Features: - Added support for the following syntax to disable inline evaluation in a comment: Note that the initial question mark character (?) will be omitted from output. - The parser now accepts '<' and '>' in attributes. Note that this is invalid markup. Previously, the '<' would not be accepted as a valid attribute value, but this would result in an 'unexpected end tag' error elsewhere. This fixes issue #38. - The expression compiler now provides methods ``assign_text`` and ``assign_value`` such that a template engine might configure this value conversion to support e.g. encoded strings. Note that currently, the only client for the ``assign_text`` method is the string expression type. - Enable template loader for string-based template classes. Note that the ``filename`` keyword argument may be provided on initialization to identify the template source by filename. This fixes issue #36. - Added ``extra_builtins`` option to the page template class. These builtins are added to the default builtins dictionary at cook time and may be provided at initialization using the ``extra_builtins`` keyword argument. Bugfixes: - If a translation domain is set for a fill slot, use this setting instead of the macro template domain. - The Python expression compiler now correctly decodes HTML entities ``'gt'`` and ``'lt'``. This fixes issue #32. - The string expression compiler now correctly handles encoded text (when support for encoded strings is enabled). This fixes issue #35. - Fixed an issue where setting the ``filename`` attribute on a file-based template would not automatically cause an invalidation. - Exceptions raised by Chameleon can now be copied via ``copy.copy``. This fixes issue #36. [leorochael] - If copying the exception fails in the exception handler, simply re-raise the original exception and log a warning. 2.2 (2011-07-28) ---------------- Features: - Added new expression type ``load:`` that allows loading a template. Both relative and absolute paths are supported. If the path given is relative, then it will be resolved with respect to the directory of the template. - Added support for dynamic evaluation of expressions. Note that this is to support legacy applications. It is not currently wired into the provided template classes. - Template classes now have a ``builtins`` attribute which may be used to define built-in variables always available in the template variable scope. Incompatibilities: - The file-based template class no longer accepts a parameter ``loader``. This parameter would be used to load a template from a relative path, using a ``find(filename)`` method. This was however, undocumented, and probably not very useful since we have the ``TemplateLoader`` mechanism already. - The compiled template module now contains an ``initialize`` function which takes values that map to the template builtins. The return value of this function is a dictionary that contains the render functions. Bugfixes: - The file-based template class no longer verifies the existance of a template file (using ``os.lstat``). This now happens implicitly if eager parsing is enabled, or otherwise when first needed (e.g. at render time). This is classified as a bug fix because the previous behavior was probably not what you'd expect, especially if an application initializes a lot of templates without needing to render them immediately. 2.1.1 (2011-07-28) ------------------ Features: - Improved exception display. The expression string is now shown in the context of the original source (if available) with a marker string indicating the location of the expression in the template source. Bugfixes: - The ``structure`` insertion mode now correctly decodes entities for any expression type (including ``string:``). This fixes issue #30. - Don't show internal variables in the exception formatter variable listing. 2.1 (2011-07-25) ---------------- Features: - Expression interpolation (using the ``${...}`` operator and previously also ``$identifier``) now requires braces everywhere except inside the ``string:`` expression type. This change is motivated by a number of legacy templates in which the interpolation format without braces ``$identifier`` appears as text. 2.0.2 (2011-07-25) ------------------ Bugfixes: - Don't use dynamic variable scope for lambda-scoped variables (#27). - Avoid duplication of exception class and message in traceback. - Fixed issue where a ``metal:fill-slot`` would be ignored if a macro was set to be used on the same element (#16). 2.0.1 (2011-07-23) ------------------ Bugfixes: - Fixed issue where global variable definition from macro slots would fail (they would instead be local). This also affects error reporting from inside slots because this would be recorded internally as a global. - Fixed issue with template cache digest (used for filenames); modules are now invalidated whenever any changes are made to the distribution set available (packages on ``sys.path``). - Fixed exception handler to better let exceptions propagate through the renderer. - The disk-based module compiler now mangles template source filenames such that the output Python module is valid and at root level (dots and hyphens are replaced by an underscore). This fixes issue #17. - Fixed translations (i18n) on Python 2.5. 2.0 (2011-07-14) ---------------- - Point release. 2.0-rc14 (2011-07-13) --------------------- Bugfixes: - The tab character (``\t``) is now parsed correctly when used inside tags. Features: - The ``RepeatDict`` class now works as a proxy behind a seperate dictionary instance. - Added template constructor option ``keep_body`` which is a flag (also available as a class attribute) that controls whether to save the template body input in the ``body`` attribute. This is disabled by default, unless debug-mode is enabled. - The page template loader class now accepts an optional ``formats`` argument which can be used to select an alternative template class. 2.0-rc13 (2011-07-07) --------------------- Bugfixes: - The backslash character (followed by optional whitespace and a line break) was not correctly interpreted as a continuation for Python expressions. Features: - The Python expression implementation is now more flexible for external subclassing via a new ``parse`` method. 2.0-rc12 (2011-07-04) --------------------- Bugfixes: - Initial keyword arguments passed to a template now no longer "leak" into the template variable space after a macro call. - An unexpected end tag is now an unrecoverable error. Features: - Improve exception output. 2.0-rc11 (2011-05-26) --------------------- Bugfixes: - Fixed issue where variable names that begin with an underscore were seemingly allowed, but their use resulted in a compiler error. Features: - Template variable names are now allowed to be prefixed with a single underscore, but not two or more (reserved for internal use). Examples of valid names:: item ITEM _item camelCase underscore_delimited help - Added support for Genshi's comment "drop" syntax:: Note the additional exclamation (!) character. This fixes addresses issue #10. 2.0-rc10 (2011-05-24) --------------------- Bugfixes: - The ``tal:attributes`` statement now correctly operates case-insensitive. The attribute name given in the statement will replace an existing attribute with the same name, without respect to case. Features: - Added ``meta:interpolation`` statement to control expression interpolation setting. Strings that disable the setting: ``"off"`` and ``"false"``. Strings that enable the setting: ``"on"`` and ``"true"``. - Expression interpolation now works inside XML comments. 2.0-rc9 (2011-05-05) -------------------- Features: - Better debugging support for string decode and conversion. If a naive join fails, each element in the output will now be attempted coerced to unicode to try and trigger the failure near to the bad string. 2.0-rc8 (2011-04-11) -------------------- Bugfixes: - If a macro defines two slots with the same name, a caller will now fill both with a single usage. - If a valid of ``None`` is provided as the translation function argument, we now fall back to the class default. 2.0-rc7 (2011-03-29) -------------------- Bugfixes: - Fixed issue with Python 2.5 compatibility AST. This affected at least PyPy 1.4. Features: - The ``auto_reload`` setting now defaults to the class value; the base template class gives a default value of ``chameleon.config.AUTO_RELOAD``. This change allows a subclass to provide a custom default value (such as an application-specific debug mode setting). 2.0-rc6 (2011-03-19) -------------------- Features: - Added support for ``target_language`` keyword argument to render method. If provided, the argument will be curried onto the translation function. Bugfixes: - The HTML entities 'lt', 'gt' and 'quot' appearing inside content subtition expressions are now translated into their native character values. This fixes an issue where you could not dynamically create elements using the ``structure`` (which is possible in ZPT). The need to create such structure stems from the lack of an expression interpolation operator in ZPT. - Fixed duplicate file pointer issue with test suite (affected Windows platforms only). This fixes issue #9. [oliora] - Use already open file using ``os.fdopen`` when trying to write out the module source. This fixes LP #731803. 2.0-rc5 (2011-03-07) -------------------- Bugfixes: - Fixed a number of issues concerning the escaping of attribute values: 1) Static attribute values are now included as they appear in the source. This means that invalid attribute values such as ``"true && false"`` are now left alone. It's not the job of the template engine to correct such markup, at least not in the default mode of operation. 2) The string expression compiler no longer unescapes values. Instead, this is left to each expression compiler. Currently only the Python expression compiler unescapes its input. 3) The dynamic escape code sequence now correctly only replaces ampersands that are part of an HTML escape format. Imports: - The page template classes and the loader class can now be imported directly from the ``chameleon`` module. Features: - If a custom template loader is not provided, relative paths are now resolved using ``os.abspath`` (i.e. to the current working directory). - Absolute paths are normalized using ``os.path.normpath`` and ``os.path.expanduser``. This ensures that all paths are kept in their "canonical" form. 2.0-rc4 (2011-03-03) -------------------- Bugfixes: - Fixed an issue where the output of an end-to-end string expression would raise an exception if the expression evaluated to ``None`` (it should simply output nothing). - The ``convert`` function (which is configurable on the template class level) now defaults to the ``translate`` function (at run-time). This fixes an issue where message objects were not translated (and thus converted to a string) using the a provided ``translate`` function. - Fixed string interpolation issue where an expression immediately succeeded by a right curly bracket would not parse. This fixes issue #5. - Fixed error where ``tal:condition`` would be evaluated after ``tal:repeat``. Features: - Python expression is now a TALES expression. That means that the pipe operator can be used to chain two or more expressions in a try-except sequence. This behavior was ported from the 1.x series. Note that while it's still possible to use the pipe character ("|") in an expression, it must now be escaped. - The template cache can now be shared by multiple processes. 2.0-rc3 (2011-03-02) -------------------- Bugfixes: - Fixed ``atexit`` handler. This fixes issue #3. - If a cache directory is specified, it will now be used even when not in debug mode. - Allow "comment" attribute in the TAL namespace. This fixes an issue in the sense that the reference engine allows any attribute within the TAL namespace. However, only "comment" is in common use. - The template constructor now accepts a flag ``debug`` which puts the template *instance* into debug-mode regardless of the global setting. This fixes issue #1. Features: - Added exception handler for exceptions raised while evaluating an expression. This handler raises (or attempts to) a new exception of the type ``RenderError``, with an additional base class of the original exception class. The string value of the exception is a formatted error message which includes the expression that caused the exception. If we are unable to create the exception class, the original exception is re-raised. 2.0-rc2 (2011-02-28) -------------------- - Fixed upload issue. 2.0-rc1 (2011-02-28) -------------------- - Initial public release. See documentation for what's new in this series. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.1 Classifier: Programming Language :: Python :: 3.2 Chameleon-2.6.1/src/Chameleon.egg-info/top_level.txt0000644000175000001440000000001211665422237022753 0ustar mborchusers00000000000000chameleon Chameleon-2.6.1/src/Chameleon.egg-info/dependency_links.txt0000644000175000001440000000000111665422237024276 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/0000755000175000001440000000000011665422242016572 5ustar mborchusers00000000000000Chameleon-2.6.1/src/chameleon/i18n.py0000644000175000001440000000563011663637637017745 0ustar mborchusers00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## from .exc import CompilationError from .namespaces import I18N_NS as ZOPE_I18N_NS WHITELIST = frozenset([ "translate", "domain", "target", "source", "attributes", "data", "name", "mode", "xmlns", "xml" ]) try: # pragma: no cover str = unicode except NameError: pass try: # pragma: no cover # optional: `zope.i18n`, `zope.i18nmessageid` from zope.i18n import interpolate from zope.i18n import translate from zope.i18nmessageid import Message except ImportError: # pragma: no cover def fast_translate(msgid, domain=None, mapping=None, context=None, target_language=None, default=None): if default is None: return msgid return default else: # pragma: no cover def fast_translate(msgid, domain=None, mapping=None, context=None, target_language=None, default=None): if msgid is None: return if target_language is not None: result = translate( msgid, domain=domain, mapping=mapping, context=context, target_language=target_language, default=default) if result != msgid: return result if isinstance(msgid, Message): default = msgid.default mapping = msgid.mapping if default is None: default = str(msgid) if not isinstance(default, basestring): return default return interpolate(default, mapping) def parse_attributes(attrs, xml=True): d = {} # filter out empty items, eg: # i18n:attributes="value msgid; name msgid2;" # would result in 3 items where the last one is empty attrs = [spec for spec in attrs.split(";") if spec] for spec in attrs: parts = spec.split() if len(parts) == 2: attr, msgid = parts elif len(parts) == 1: attr = parts[0] msgid = None else: raise CompilationError( "Illegal i18n:attributes specification.", spec) if not xml: attr = attr.lower() if attr in d: raise CompilationError( "Attribute may only be specified once in i18n:attributes", attr) d[attr] = msgid return d Chameleon-2.6.1/src/chameleon/compiler.py0000644000175000001440000013234411665421700020763 0ustar mborchusers00000000000000import re import sys import itertools import logging import threading import functools import collections import pickle from .astutil import load from .astutil import store from .astutil import param from .astutil import swap from .astutil import subscript from .astutil import node_annotations from .astutil import annotated from .astutil import NameLookupRewriteVisitor from .astutil import Comment from .astutil import Symbol from .astutil import Builtin from .codegen import TemplateCodeGenerator from .codegen import template from .tal import ErrorInfo from .tal import NAME from .i18n import fast_translate from .nodes import Text from .nodes import Value from .nodes import Substitution from .nodes import Assignment from .nodes import Module from .nodes import Context from .tokenize import Token from .config import DEBUG_MODE from .exc import TranslationError from .exc import ExpressionError from .parser import groupdict from .utils import DebuggingOutputStream from .utils import char2entity from .utils import ListDictProxy from .utils import native_string from .utils import byte_string from .utils import string_type from .utils import unicode_string from .utils import version from .utils import ast from .utils import safe_native from .utils import builtins from .utils import decode_htmlentities if version >= (3, 0, 0): long = int log = logging.getLogger('chameleon.compiler') COMPILER_INTERNALS_OR_DISALLOWED = set([ "econtext", "rcontext", "str", "int", "float", "long", "len", "None", "True", "False", "RuntimeError", ]) RE_MANGLE = re.compile('[\-: ]') RE_NAME = re.compile('^%s$' % NAME) if DEBUG_MODE: LIST = template("cls()", cls=DebuggingOutputStream, mode="eval") else: LIST = template("[]", mode="eval") def identifier(prefix, suffix=None): return "__%s_%s" % (prefix, mangle(suffix or id(prefix))) def mangle(string): return RE_MANGLE.sub('_', str(string)).replace('\n', '') def load_econtext(name): return template("getitem(KEY)", KEY=ast.Str(s=name), mode="eval") def store_econtext(name): name = native_string(name) return subscript(name, load("econtext"), ast.Store()) def store_rcontext(name): name = native_string(name) return subscript(name, load("rcontext"), ast.Store()) def set_error(token, exception): try: line, column = token.location filename = token.filename except AttributeError: line, column = 0, 0 filename = "" string = safe_native(token) return template( "rcontext.setdefault('__error__', [])." "append((string, line, col, src, exc))", string=ast.Str(s=string), line=ast.Num(n=line), col=ast.Num(n=column), src=ast.Str(s=filename), sys=Symbol(sys), exc=exception, ) def try_except_wrap(stmts, token): exception = template( "exc_info()[1]", exc_info=Symbol(sys.exc_info), mode="eval" ) body = set_error(token, exception) + template("raise") return ast.TryExcept( body=stmts, handlers=[ast.ExceptHandler(body=body)], ) @template def emit_node(node): # pragma: no cover __append(node) @template def emit_node_if_non_trivial(node): # pragma: no cover if node is not None: __append(node) @template def emit_bool(target, s, default_marker=None, default=None): # pragma: no cover if target is default_marker: target = default elif target: target = s else: target = None @template def emit_convert( target, encoded=byte_string, str=unicode_string, long=long, type=type, default_marker=None, default=None): # pragma: no cover if target is None: pass elif target is default_marker: target = default else: __tt = type(target) if __tt is int or __tt is float or __tt is long: target = str(target) elif __tt is encoded: target = decode(target) elif __tt is not str: try: target = target.__html__ except AttributeError: __converted = convert(target) target = str(target) if target is __converted else __converted else: target = target() @template def emit_translate(target, msgid, default=None): # pragma: no cover target = translate(msgid, default=default, domain=__i18n_domain) @template def emit_convert_and_escape( target, quote=None, quote_entity=None, str=unicode_string, long=long, type=type, encoded=byte_string, default_marker=None, default=None): # pragma: no cover if target is None: pass elif target is default_marker: target = default else: __tt = type(target) if __tt is int or __tt is float or __tt is long: target = str(target) else: try: if __tt is encoded: target = decode(target) elif __tt is not str: try: target = target.__html__ except: __converted = convert(target) target = str(target) if target is __converted \ else __converted else: raise RuntimeError except RuntimeError: target = target() else: if target is not None: try: escape = __re_needs_escape(target) is not None except TypeError: pass else: if escape: # Character escape if '&' in target: target = target.replace('&', '&') if '<' in target: target = target.replace('<', '<') if '>' in target: target = target.replace('>', '>') if quote is not None and quote in target: target = target.replace(quote, quote_entity) class Interpolator(object): braces_required_regex = re.compile( r'(?.*)})') braces_optional_regex = re.compile( r'(?.*)}|(?P[A-Za-z][A-Za-z0-9_]*))') def __init__(self, expression, braces_required, translate=False): self.expression = expression self.regex = self.braces_required_regex if braces_required else \ self.braces_optional_regex self.translate = translate def __call__(self, name, engine): """The strategy is to find possible expression strings and call the ``validate`` function of the parser to validate. For every possible starting point, the longest possible expression is tried first, then the second longest and so forth. Example 1: ${'expressions use the ${} format'} The entire expression is attempted first and it is also the only one that validates. Example 2: ${'Hello'} ${'world!'} Validation of the longest possible expression (the entire string) will fail, while the second round of attempts, ``${'Hello'}`` and ``${'world!'}`` respectively, validate. """ body = [] nodes = [] text = self.expression expr_map = {} translate = self.translate while text: matched = text m = self.regex.search(matched) if m is None: nodes.append(ast.Str(s=text)) break part = text[:m.start()] text = text[m.start():] if part: node = ast.Str(s=part) nodes.append(node) if not body: target = name else: target = store("%s_%d" % (name.id, text.pos)) while True: d = groupdict(m, matched) string = d["expression"] or d["variable"] or "" string = decode_htmlentities(string) try: compiler = engine.parse(string) body += compiler.assign_text(target) except ExpressionError: matched = matched[m.start():m.end() - 1] m = self.regex.search(matched) if m is None: raise else: break # If one or more expressions are not simple names, we # disable translation. if RE_NAME.match(string) is None: translate = False # if this is the first expression, use the provided # assignment name; otherwise, generate one (here based # on the string position) node = load(target.id) nodes.append(node) expr_map[node] = safe_native(string) text = text[len(m.group()):] if len(nodes) == 1: target = nodes[0] if translate and isinstance(target, ast.Str): target = template( "translate(msgid, domain=__i18n_domain)", msgid=target, mode="eval", ) else: if translate: formatting_string = "" keys = [] values = [] for node in nodes: if isinstance(node, ast.Str): formatting_string += node.s else: string = expr_map[node] formatting_string += "${%s}" % string keys.append(ast.Str(s=string)) values.append(node) target = template( "translate(msgid, mapping=mapping, domain=__i18n_domain)", msgid=ast.Str(s=formatting_string), mapping=ast.Dict(keys=keys, values=values), mode="eval" ) else: nodes = [ template( "NODE if NODE is not None else ''", NODE=node, mode="eval" ) for node in nodes ] target = ast.BinOp( left=ast.Str(s="%s" * len(nodes)), op=ast.Mod(), right=ast.Tuple(elts=nodes, ctx=ast.Load())) body += [ast.Assign(targets=[name], value=target)] return body class ExpressionEngine(object): """Expression engine. This test demonstrates how to configure and invoke the engine. >>> from chameleon import tales >>> parser = tales.ExpressionParser({ ... 'python': tales.PythonExpr, ... 'not': tales.NotExpr, ... 'exists': tales.ExistsExpr, ... 'string': tales.StringExpr, ... }, 'python') >>> engine = ExpressionEngine(parser) An expression evaluation function: >>> eval = lambda expression: tales.test( ... tales.IdentityExpr(expression), engine) We have provided 'python' as the default expression type. This means that when no prefix is given, the expression is evaluated as a Python expression: >>> eval('not False') True Note that the ``type`` prefixes bind left. If ``not`` and ``exits`` are two expression type prefixes, consider the following:: >>> eval('not: exists: int(None)') True The pipe operator binds right. In the following example, but arguments are evaluated against ``not: exists: ``. >>> eval('not: exists: help') False >>> eval('string:test ${1}${2}') 'test 12' """ supported_char_escape_set = set(('&', '<', '>')) def __init__(self, parser, char_escape=(), default=None, default_marker=None): self._parser = parser self._char_escape = char_escape self._default = default self._default_marker = default_marker def __call__(self, string, target): # BBB: This method is deprecated. Instead, a call should first # be made to ``parse`` and then one of the assignment methods # ("value" or "text"). compiler = self.parse(string) return compiler(string, target) def parse(self, string): expression = self._parser(string) compiler = self.get_compiler(expression, string) return ExpressionCompiler(compiler, self) def get_compiler(self, expression, string): def compiler(target, engine, result_type=None, *args): stmts = expression(target, engine) if result_type is not None: method = getattr(self, '_convert_%s' % result_type) steps = method(target, *args) stmts.extend(steps) return [try_except_wrap(stmts, string)] return compiler def _convert_bool(self, target, s): """Converts value given by ``target`` to a string ``s`` if the target is a true value, otherwise ``None``. """ return emit_bool( target, ast.Str(s=s), default=self._default, default_marker=self._default_marker ) def _convert_text(self, target): """Converts value given by ``target`` to text.""" if self._char_escape: # This is a cop-out - we really only support a very select # set of escape characters other = set(self._char_escape) - self.supported_char_escape_set if other: for supported in '"', '\'', '': if supported in self._char_escape: quote = supported break else: raise RuntimeError( "Unsupported escape set: %s." % repr(self._char_escape) ) else: quote = '\0' entity = char2entity(quote or '\0') return emit_convert_and_escape( target, quote=ast.Str(s=quote), quote_entity=ast.Str(s=entity), default=self._default, default_marker=self._default_marker, ) return emit_convert( target, default=self._default, default_marker=self._default_marker, ) class ExpressionCompiler(object): def __init__(self, compiler, engine): self.compiler = compiler self.engine = engine def assign_bool(self, target, s): return self.compiler(target, self.engine, "bool", s) def assign_text(self, target): return self.compiler(target, self.engine, "text") def assign_value(self, target): return self.compiler(target, self.engine) class ExpressionEvaluator(object): """Evaluates dynamic expression. This is not particularly efficient, but supported for legacy applications. >>> from chameleon import tales >>> parser = tales.ExpressionParser({'python': tales.PythonExpr}, 'python') >>> engine = functools.partial(ExpressionEngine, parser) >>> evaluate = ExpressionEvaluator(engine, { ... 'foo': 'bar', ... }) The evaluation function is passed the local and remote context, the expression type and finally the expression. >>> evaluate({'boo': 'baz'}, {}, 'python', 'foo + boo') 'barbaz' The cache is now primed: >>> evaluate({'boo': 'baz'}, {}, 'python', 'foo + boo') 'barbaz' Note that the call method supports currying of the expression argument: >>> python = evaluate({'boo': 'baz'}, {}, 'python') >>> python('foo + boo') 'barbaz' """ __slots__ = "_engine", "_cache", "_names", "_builtins" def __init__(self, engine, builtins): self._engine = engine self._names, self._builtins = zip(*builtins.items()) self._cache = {} def __call__(self, econtext, rcontext, expression_type, string=None): if string is None: return functools.partial( self.__call__, econtext, rcontext, expression_type ) expression = "%s:%s" % (expression_type, string) try: evaluate = self._cache[expression] except KeyError: assignment = Assignment(["_result"], expression, True) module = Module("evaluate", Context(assignment)) compiler = Compiler( self._engine, module, ('econtext', 'rcontext') + self._names ) env = {} exec(compiler.code, env) evaluate = self._cache[expression] = env["evaluate"] evaluate(econtext, rcontext, *self._builtins) return econtext['_result'] class NameTransform(object): """ >>> nt = NameTransform( ... set(('foo', 'bar', )), {'boo': 'boz'}, ... ('econtext', ), ... ) >>> def test(node): ... rewritten = nt(node) ... module = ast.Module([ast.fix_missing_locations(rewritten)]) ... codegen = TemplateCodeGenerator(module) ... return codegen.code Any odd name: >>> test(load('frobnitz')) "getitem('frobnitz')" A 'builtin' name will first be looked up via ``get`` allowing fall back to the global builtin value: >>> test(load('foo')) "get('foo', foo)" Internal names (with two leading underscores) are left alone: >>> test(load('__internal')) '__internal' Compiler internals or disallowed names: >>> test(load('econtext')) 'econtext' Aliased names: >>> test(load('boo')) 'boz' """ def __init__(self, builtins, aliases, internals): self.builtins = builtins self.aliases = aliases self.internals = internals def __call__(self, node): name = node.id # Don't rewrite names that begin with an underscore; they are # internal and can be assumed to be locally defined. This # policy really should be part of the template program, not # defined here in the compiler. if name.startswith('__') or name in self.internals: return node if isinstance(node.ctx, ast.Store): return store_econtext(name) aliased = self.aliases.get(name) if aliased is not None: return load(aliased) # If the name is a Python global, first try acquiring it from # the dynamic context, then fall back to the global. if name in self.builtins: return template( "get(key, name)", mode="eval", key=ast.Str(s=name), name=load(name), ) # Otherwise, simply acquire it from the dynamic context. return load_econtext(name) class ExpressionTransform(object): """Internal wrapper to transform expression nodes into assignment statements. The node input may use the provided expression engine, but other expression node types are supported such as ``Builtin`` which simply resolves a built-in name. Used internally be the compiler. """ loads_symbol = Symbol(pickle.loads) def __init__(self, engine_factory, cache, transform, strict=True): self.engine_factory = engine_factory self.cache = cache self.transform = transform self.strict = strict def __call__(self, expression, target): if isinstance(target, string_type): target = store(target) try: stmts = self.translate(expression, target) except ExpressionError: if self.strict: raise exc = sys.exc_info()[1] p = pickle.dumps(exc) stmts = template( "__exc = loads(p)", loads=self.loads_symbol, p=ast.Str(s=p) ) token = Token(exc.token, exc.offset, filename=exc.filename) stmts += set_error(token, load("__exc")) stmts += [ast.Raise(exc=load("__exc"))] # Apply dynamic name rewrite transform to each statement visitor = NameLookupRewriteVisitor(self.transform) for stmt in stmts: visitor(stmt) return stmts def translate(self, expression, target): if isinstance(target, string_type): target = store(target) cached = self.cache.get(expression) if cached is not None: stmts = [ast.Assign(targets=[target], value=cached)] elif isinstance(expression, ast.expr): stmts = [ast.Assign(targets=[target], value=expression)] else: # The engine interface supports simple strings, which # default to expression nodes if isinstance(expression, string_type): expression = Value(expression, True) kind = type(expression).__name__ visitor = getattr(self, "visit_%s" % kind) stmts = visitor(expression, target) # Add comment target_id = getattr(target, "id", target) comment = Comment(" %r -> %s" % (expression, target_id)) stmts.insert(0, comment) return stmts def visit_Value(self, node, target): engine = self.engine_factory() compiler = engine.parse(node.value) return compiler.assign_value(target) def visit_Default(self, node, target): value = annotated(node.marker) return [ast.Assign(targets=[target], value=value)] def visit_Substitution(self, node, target): engine = self.engine_factory( char_escape=node.char_escape, default=node.default, ) compiler = engine.parse(node.value) return compiler.assign_text(target) def visit_Negate(self, node, target): return self.translate(node.value, target) + \ template("TARGET = not TARGET", TARGET=target) def visit_Identity(self, node, target): expression = self.translate(node.expression, "__expression") value = self.translate(node.value, "__value") return expression + value + \ template("TARGET = __expression is __value", TARGET=target) def visit_Equality(self, node, target): expression = self.translate(node.expression, "__expression") value = self.translate(node.value, "__value") return expression + value + \ template("TARGET = __expression == __value", TARGET=target) def visit_Boolean(self, node, target): engine = self.engine_factory() compiler = engine.parse(node.value) return compiler.assign_bool(target, node.s) def visit_Interpolation(self, node, target): expr = node.value if isinstance(expr, Substitution): engine = self.engine_factory( char_escape=expr.char_escape, default=expr.default, ) elif isinstance(expr, Value): engine = self.engine_factory() else: raise RuntimeError("Bad value: %r." % node.value) interpolator = Interpolator( expr.value, node.braces_required, node.translation ) compiler = engine.get_compiler(interpolator, expr.value) return compiler(target, engine) def visit_Translate(self, node, target): if node.msgid is not None: msgid = ast.Str(s=node.msgid) else: msgid = target return self.translate(node.node, target) + \ emit_translate(target, msgid, default=target) def visit_Static(self, node, target): value = annotated(node) return [ast.Assign(targets=[target], value=value)] def visit_Builtin(self, node, target): value = annotated(node) return [ast.Assign(targets=[target], value=value)] class Compiler(object): """Generic compiler class. Iterates through nodes and yields Python statements which form a template program. """ exceptions = NameError, \ ValueError, \ AttributeError, \ LookupError, \ TypeError defaults = { 'translate': Symbol(fast_translate), 'decode': Builtin("str"), 'convert': Builtin("str"), } lock = threading.Lock() global_builtins = set(builtins.__dict__) def __init__(self, engine_factory, node, builtins={}, strict=True): self._scopes = [set()] self._expression_cache = {} self._translations = [] self._builtins = builtins self._aliases = [{}] self._macros = [] self._current_slot = [] internals = COMPILER_INTERNALS_OR_DISALLOWED | \ set(self.defaults) transform = NameTransform( self.global_builtins | set(builtins), ListDictProxy(self._aliases), internals, ) self._engine = ExpressionTransform( engine_factory, self._expression_cache, transform, strict=strict, ) if isinstance(node_annotations, dict): self.lock.acquire() backup = node_annotations.copy() else: backup = None try: module = ast.Module([]) module.body += self.visit(node) ast.fix_missing_locations(module) generator = TemplateCodeGenerator(module) finally: if backup is not None: node_annotations.clear() node_annotations.update(backup) self.lock.release() self.code = generator.code def visit(self, node): if node is None: return () kind = type(node).__name__ visitor = getattr(self, "visit_%s" % kind) iterator = visitor(node) return list(iterator) def visit_Sequence(self, node): for item in node.items: for stmt in self.visit(item): yield stmt def visit_Element(self, node): self._aliases.append(self._aliases[-1].copy()) for stmt in self.visit(node.start): yield stmt for stmt in self.visit(node.content): yield stmt if node.end is not None: for stmt in self.visit(node.end): yield stmt self._aliases.pop() def visit_Module(self, node): body = [] body += template("import re") body += template("import functools") body += template("__marker = object()") body += template( r"g_re_amp = re.compile(r'&(?!([A-Za-z]+|#[0-9]+);)')" ) body += template( r"g_re_needs_escape = re.compile(r'[&<>\"\']').search") body += template( r"__re_whitespace = " r"functools.partial(re.compile('\s+').sub, ' ')", ) # Visit module content program = self.visit(node.program) body += [ast.FunctionDef( name=node.name, args=ast.arguments( args=[param(b) for b in self._builtins], defaults=(), ), body=program )] return body def visit_MacroProgram(self, node): functions = [] # Visit defined macros macros = getattr(node, "macros", ()) names = [] for macro in macros: stmts = self.visit(macro) function = stmts[-1] names.append(function.name) functions += stmts # Return function dictionary functions += [ast.Return(value=ast.Dict( keys=[ast.Str(s=name) for name in names], values=[load(name) for name in names], ))] return functions def visit_Context(self, node): return template("getitem = econtext.__getitem__") + \ template("get = econtext.get") + \ self.visit(node.node) def visit_Macro(self, node): body = [] # Initialization body += template("__append = __stream.append") body += template("__re_amp = g_re_amp") body += template("__re_needs_escape = g_re_needs_escape") # Resolve defaults for name in self.defaults: body += template( "NAME = econtext[KEY]", NAME=name, KEY=ast.Str(s="__" + name) ) # Internal set of defined slots self._slots = set() # Visit macro body nodes = itertools.chain(*tuple(map(self.visit, node.body))) # Slot resolution for name in self._slots: body += template( "try: NAME = econtext[KEY].pop()\n" "except: NAME = None", KEY=ast.Str(s=name), NAME=store(name)) # Append visited nodes body += nodes function_name = "render" if node.name is None else \ "render_%s" % mangle(node.name) function = ast.FunctionDef( name=function_name, args=ast.arguments( args=[ param("__stream"), param("econtext"), param("rcontext"), param("__i18n_domain"), ], defaults=[load("None")], ), body=body ) yield function def visit_Text(self, node): return emit_node(ast.Str(s=node.value)) def visit_Domain(self, node): backup = "__previous_i18n_domain_%d" % id(node) return template("BACKUP = __i18n_domain", BACKUP=backup) + \ template("__i18n_domain = NAME", NAME=ast.Str(s=node.name)) + \ self.visit(node.node) + \ template("__i18n_domain = BACKUP", BACKUP=backup) def visit_OnError(self, node): body = [] fallback = identifier("__fallback") body += template("fallback = len(__stream)", fallback=fallback) self._enter_assignment((node.name, )) fallback_body = self.visit(node.fallback) self._leave_assignment((node.name, )) error_assignment = template( "econtext[key] = cls(__exc, rcontext['__error__'][-1][1:3])", cls=ErrorInfo, key=ast.Str(s=node.name), ) body += [ast.TryExcept( body=self.visit(node.node), handlers=[ast.ExceptHandler( type=ast.Tuple(elts=[Builtin("Exception")], ctx=ast.Load()), name=store("__exc"), body=(error_assignment + \ template("del __stream[fallback:]", fallback=fallback) + \ fallback_body ), )] )] return body def visit_Content(self, node): name = "__content" body = self._engine(node.expression, store(name)) if node.translate: body += emit_translate(name, name) if node.char_escape: body += emit_convert_and_escape(name) else: body += emit_convert(name) body += template("if NAME is not None: __append(NAME)", NAME=name) return body def visit_Interpolation(self, node): name = identifier("content") return self._engine(node, name) + \ emit_node_if_non_trivial(name) def visit_Alias(self, node): assert len(node.names) == 1 name = node.names[0] target = self._aliases[-1][name] = identifier(name, id(node)) return self._engine(node.expression, target) def visit_Assignment(self, node): for name in node.names: if name in COMPILER_INTERNALS_OR_DISALLOWED: raise TranslationError( "Name disallowed by compiler.", name ) if name.startswith('__'): raise TranslationError( "Name disallowed by compiler (double underscore).", name ) assignment = self._engine(node.expression, store("__value")) if len(node.names) != 1: target = ast.Tuple( elts=[store_econtext(name) for name in node.names], ctx=ast.Store(), ) else: target = store_econtext(node.names[0]) assignment.append(ast.Assign(targets=[target], value=load("__value"))) for name in node.names: if not node.local: assignment += template( "rcontext[KEY] = __value", KEY=ast.Str(s=native_string(name)) ) return assignment def visit_Define(self, node): scope = set(self._scopes[-1]) self._scopes.append(scope) for assignment in node.assignments: if assignment.local: for stmt in self._enter_assignment(assignment.names): yield stmt for stmt in self.visit(assignment): yield stmt for stmt in self.visit(node.node): yield stmt for assignment in node.assignments: if assignment.local: for stmt in self._leave_assignment(assignment.names): yield stmt self._scopes.pop() def visit_Omit(self, node): return self.visit_Condition(node) def visit_Condition(self, node): target = "__condition" assignment = self._engine(node.expression, target) assert assignment for stmt in assignment: yield stmt body = self.visit(node.node) or [ast.Pass()] orelse = getattr(node, "orelse", None) if orelse is not None: orelse = self.visit(orelse) test = load(target) yield ast.If(test=test, body=body, orelse=orelse) def visit_Translate(self, node): """Translation. Visit items and assign output to a default value. Finally, compile a translation expression and use either result or default. """ body = [] # Track the blocks of this translation self._translations.append(set()) # Prepare new stream append = identifier("append", id(node)) stream = identifier("stream", id(node)) body += template("s = new_list", s=stream, new_list=LIST) + \ template("a = s.append", a=append, s=stream) # Visit body to generate the message body code = self.visit(node.node) swap(ast.Suite(body=code), load(append), "__append") body += code # Reduce white space and assign as message id msgid = identifier("msgid", id(node)) body += template( "msgid = __re_whitespace(''.join(stream)).strip()", msgid=msgid, stream=stream ) default = msgid # Compute translation block mapping if applicable names = self._translations[-1] if names: keys = [] values = [] for name in names: stream, append = self._get_translation_identifiers(name) keys.append(ast.Str(s=name)) values.append(load(stream)) # Initialize value body.insert( 0, ast.Assign( targets=[store(stream)], value=ast.Str(s=native_string("")))) mapping = ast.Dict(keys=keys, values=values) else: mapping = None # if this translation node has a name, use it as the message id if node.msgid: msgid = ast.Str(s=node.msgid) # emit the translation expression body += template( "__append(translate(" "msgid, mapping=mapping, default=default, domain=__i18n_domain))", msgid=msgid, default=default, mapping=mapping ) # pop away translation block reference self._translations.pop() return body def visit_Start(self, node): try: line, column = node.prefix.location except AttributeError: line, column = 0, 0 yield Comment( " %s%s ... (%d:%d)\n" " --------------------------------------------------------" % ( node.prefix, node.name, line, column)) if node.attributes: for stmt in emit_node(ast.Str(s=node.prefix + node.name)): yield stmt for attribute in node.attributes: for stmt in self.visit(attribute): yield stmt for stmt in emit_node(ast.Str(s=node.suffix)): yield stmt else: for stmt in emit_node( ast.Str(s=node.prefix + node.name + node.suffix)): yield stmt def visit_End(self, node): for stmt in emit_node(ast.Str( s=node.prefix + node.name + node.space + node.suffix)): yield stmt def visit_Attribute(self, node): f = node.space + node.name + node.eq + node.quote + "%s" + node.quote # Static attributes are just outputted directly if isinstance(node.expression, ast.Str): s = f % node.expression.s return template("__append(S)", S=ast.Str(s=s)) target = identifier("attr", node.name) body = self._engine(node.expression, store(target)) return body + template( "if TARGET is not None: __append(FORMAT % TARGET)", FORMAT=ast.Str(s=f), TARGET=target, ) def visit_Cache(self, node): body = [] for expression in node.expressions: name = identifier("cache", id(expression)) target = store(name) # Skip re-evaluation if self._expression_cache.get(expression): continue body += self._engine(expression, target) self._expression_cache[expression] = target body += self.visit(node.node) return body def visit_UseInternalMacro(self, node): if node.name is None: render = "render" else: render = "render_%s" % mangle(node.name) return template( "f(__stream, econtext.copy(), rcontext, __i18n_domain)", f=render) + \ template("econtext.update(rcontext)") def visit_DefineSlot(self, node): name = "__slot_%s" % mangle(node.name) body = self.visit(node.node) self._slots.add(name) orelse = template( "SLOT(__stream, econtext.copy(), rcontext, __i18n_domain)", SLOT=name) test = ast.Compare( left=load(name), ops=[ast.Is()], comparators=[load("None")] ) return [ ast.If(test=test, body=body or [ast.Pass()], orelse=orelse) ] def visit_Name(self, node): """Translation name.""" if not self._translations: raise TranslationError( "Not allowed outside of translation.", node.name) if node.name in self._translations[-1]: raise TranslationError( "Duplicate translation name: %s." % node.name) self._translations[-1].add(node.name) body = [] # prepare new stream stream, append = self._get_translation_identifiers(node.name) body += template("s = new_list", s=stream, new_list=LIST) + \ template("a = s.append", a=append, s=stream) # generate code code = self.visit(node.node) swap(ast.Suite(body=code), load(append), "__append") body += code # output msgid text = Text('${%s}' % node.name) body += self.visit(text) # Concatenate stream body += template("stream = ''.join(stream)", stream=stream) return body def visit_UseExternalMacro(self, node): self._macros.append(node.extend) callbacks = [] for slot in node.slots: key = "__slot_%s" % mangle(slot.name) fun = "__fill_%s" % mangle(slot.name) self._current_slot.append(slot.name) body = template("getitem = econtext.__getitem__") + \ template("get = econtext.get") + \ self.visit(slot.node) assert self._current_slot.pop() == slot.name callbacks.append( ast.FunctionDef( name=fun, args=ast.arguments( args=[ param("__stream"), param("econtext"), param("rcontext"), param("__i18n_domain"), ], defaults=[load("__i18n_domain")], ), body=body or [ast.Pass()], )) key = ast.Str(s=key) assignment = template( "_slots = econtext[KEY] = DEQUE((NAME,))", KEY=key, NAME=fun, DEQUE=Symbol(collections.deque), ) if node.extend: append = template("_slots.appendleft(NAME)", NAME=fun) assignment = [ast.TryExcept( body=template("_slots = getitem(KEY)", KEY=key), handlers=[ast.ExceptHandler(body=assignment)], orelse=append, )] callbacks.extend(assignment) assert self._macros.pop() == node.extend assignment = self._engine(node.expression, store("__macro")) return ( callbacks + \ assignment + \ template( "__macro.include(__stream, econtext.copy(), " \ "rcontext, __i18n_domain)") + \ template("econtext.update(rcontext)") ) def visit_Repeat(self, node): # Used for loop variable definition and restore self._scopes.append(set()) # Variable assignment and repeat key for single- and # multi-variable repeat clause if node.local: contexts = "econtext", else: contexts = "econtext", "rcontext" for name in node.names: if name in COMPILER_INTERNALS_OR_DISALLOWED: raise TranslationError( "Name disallowed by compiler.", name ) if len(node.names) > 1: targets = [ ast.Tuple(elts=[ subscript(native_string(name), load(context), ast.Store()) for name in node.names], ctx=ast.Store()) for context in contexts ] key = ast.Tuple( elts=[ast.Str(s=name) for name in node.names], ctx=ast.Load()) else: name = node.names[0] targets = [ subscript(native_string(name), load(context), ast.Store()) for context in contexts ] key = ast.Str(s=node.names[0]) index = identifier("__index", id(node)) assignment = [ast.Assign(targets=targets, value=load("__item"))] # Make repeat assignment in outer loop names = node.names local = node.local outer = self._engine(node.expression, store("__iterator")) if local: outer[:] = list(self._enter_assignment(names)) + outer outer += template( "__iterator, INDEX = getitem('repeat')(key, __iterator)", key=key, INDEX=index ) # Set a trivial default value for each name assigned to make # sure we assign a value even if the iteration is empty outer += [ast.Assign( targets=[store_econtext(name) for name in node.names], value=load("None")) ] # Compute inner body inner = self.visit(node.node) # After each iteration, decrease the index inner += template("index -= 1", index=index) # For items up to N - 1, emit repeat whitespace inner += template( "if INDEX > 0: __append(WHITESPACE)", INDEX=index, WHITESPACE=ast.Str(s=node.whitespace) ) # Main repeat loop outer += [ast.For( target=store("__item"), iter=load("__iterator"), body=assignment + inner, )] # Finally, clean up assignment if it's local if outer: outer += self._leave_assignment(names) self._scopes.pop() return outer def _get_translation_identifiers(self, name): assert self._translations prefix = id(self._translations[-1]) stream = identifier("stream_%d" % prefix, name) append = identifier("append_%d" % prefix, name) return stream, append def _enter_assignment(self, names): for name in names: for stmt in template( "BACKUP = get(KEY, __marker)", BACKUP=identifier("backup_%s" % name, id(names)), KEY=ast.Str(s=native_string(name)), ): yield stmt def _leave_assignment(self, names): for name in names: for stmt in template( "if BACKUP is __marker: del econtext[KEY]\n" "else: econtext[KEY] = BACKUP", BACKUP=identifier("backup_%s" % name, id(names)), KEY=ast.Str(s=native_string(name)), ): yield stmt Chameleon-2.6.1/src/chameleon/program.py0000644000175000001440000000162611641327540020617 0ustar mborchusers00000000000000try: str = unicode except NameError: long = int from .tokenize import iter_xml from .tokenize import iter_text from .parser import ElementParser from .namespaces import XML_NS from .namespaces import XMLNS_NS class ElementProgram(object): DEFAULT_NAMESPACES = { 'xmlns': XMLNS_NS, 'xml': XML_NS, } tokenizers = { 'xml': iter_xml, 'text': iter_text, } def __init__(self, source, mode="xml", filename=None): tokenizer = self.tokenizers[mode] tokens = tokenizer(source, filename) parser = ElementParser(tokens, self.DEFAULT_NAMESPACES) self.body = [] for kind, args in parser: node = self.visit(kind, args) if node is not None: self.body.append(node) def visit(self, kind, args): visitor = getattr(self, "visit_%s" % kind) return visitor(*args) Chameleon-2.6.1/src/chameleon/nodes.py0000644000175000001440000000661511663206221020257 0ustar mborchusers00000000000000from .astutil import Node class UseExternalMacro(Node): """Extend external macro.""" _fields = "expression", "slots", "extend" class Sequence(Node): """Element sequence.""" _fields = "items", class Content(Node): """Content substitution.""" _fields = "expression", "char_escape", "translate" class Default(Node): """Represents a default value.""" _fields = "marker", class Value(Node): """Expression object value.""" _fields = "value", def __repr__(self): try: line, column = self.value.location except AttributeError: line, column = 0, 0 return "<%s %r (%d:%d)>" % ( type(self).__name__, self.value, line, column ) class Substitution(Value): """Expression value for text substitution.""" _fields = "value", "char_escape", "default" default = None class Boolean(Value): _fields = "value", "s" class Negate(Node): """Wraps an expression with a negation.""" _fields = "value", class Element(Node): """XML element.""" _fields = "start", "end", "content" class Attribute(Node): """Element attribute.""" _fields = "name", "expression", "quote", "eq", "space" class Start(Node): """Start-tag.""" _fields = "name", "prefix", "suffix", "attributes" class End(Node): """End-tag.""" _fields = "name", "space", "prefix", "suffix" class Condition(Node): """Node visited only if some condition holds.""" _fields = "expression", "node", "orelse" class Identity(Node): """Condition expression that is true on identity.""" _fields = "expression", "value" class Equality(Node): """Condition expression that is true on equality.""" _fields = "expression", "value" class Cache(Node): """Cache (evaluate only once) the value of ``expression`` inside ``node``. """ _fields = "expressions", "node" class Assignment(Node): """Variable assignment.""" _fields = "names", "expression", "local" class Alias(Assignment): """Alias assignment. Note that ``expression`` should be a cached or global value. """ local = False class Define(Node): """Variable definition in scope.""" _fields = "assignments", "node" class Repeat(Assignment): """Iterate over provided assignment and repeat body.""" _fields = "names", "expression", "local", "whitespace", "node" class Macro(Node): """Macro definition.""" _fields = "name", "body" class Program(Node): _fields = "name", "body" class Module(Node): _fields = "name", "program", class Context(Node): _fields = "node", class Text(Node): """Static text output.""" _fields = "value", class Interpolation(Text): """String interpolation output.""" _fields = "value", "braces_required", "translation" class Translate(Node): """Translate node.""" _fields = "msgid", "node" class Name(Node): """Translation name.""" _fields = "name", "node" class Domain(Node): """Update translation domain.""" _fields = "name", "node" class OnError(Node): _fields = "fallback", "name", "node" class UseInternalMacro(Node): """Use internal macro (defined inside same program).""" _fields = "name", class FillSlot(Node): """Fill a macro slot.""" _fields = "name", "node" class DefineSlot(Node): """Define a macro slot.""" _fields = "name", "node" Chameleon-2.6.1/src/chameleon/codegen.py0000644000175000001440000001337311636356733020570 0ustar mborchusers00000000000000try: import ast except ImportError: from chameleon import ast24 as ast import inspect import textwrap import types import copy try: import __builtin__ as builtins except ImportError: import builtins reverse_builtin_map = dict( (value, name) for (name, value) in builtins.__dict__.items() ) try: basestring except NameError: basestring = str from .astutil import ASTCodeGenerator from .astutil import load from .astutil import store from .astutil import parse from .astutil import Builtin from .astutil import Symbol from .astutil import node_annotations from .exc import CompilationError try: NATIVE_NUMBERS = int, float, long, bool except NameError: NATIVE_NUMBERS = int, float, bool def template(function, mode='exec', **kw): def wrapper(*vargs, **kwargs): symbols = dict(zip(args, vargs + defaults)) symbols.update(kwargs) class Visitor(ast.NodeVisitor): def visit_Name(self, node): value = symbols.get(node.id, self) if value is not self: if isinstance(value, basestring): value = load(value) if isinstance(value, type) or value in reverse_builtin_map: name = reverse_builtin_map.get(value) if name is not None: value = Builtin(name) else: value = Symbol(value) assert node not in node_annotations assert hasattr(value, '_fields') node_annotations[node] = value expr = parse(source, mode=mode) if not isinstance(function, basestring): expr = expr.body[0] Visitor().visit(expr) return expr.body if isinstance(function, basestring): source = function defaults = args = () return wrapper(**kw) source = textwrap.dedent(inspect.getsource(function)) argspec = inspect.getargspec(function) args = argspec[0] defaults = argspec[3] or () return wrapper class TemplateCodeGenerator(ASTCodeGenerator): """Extends the standard Python code generator class with handlers for the helper node classes: - Symbol (an importable value) - Static (value that can be made global) - Builtin (from the builtins module) - Marker (short-hand for a unique static object) """ names = () def __init__(self, tree): self.imports = {} self.defines = {} self.markers = {} # Generate code super(TemplateCodeGenerator, self).__init__(tree) def visit_Module(self, node): super(TemplateCodeGenerator, self).visit_Module(node) # Make sure we terminate the line printer self.flush() # Clear lines array for import visits body = self.lines self.lines = [] while self.defines: name, node = self.defines.popitem() assignment = ast.Assign(targets=[store(name)], value=node) self.visit(assignment) # Make sure we terminate the line printer self.flush() # Clear lines array for import visits defines = self.lines self.lines = [] while self.imports: value, node = self.imports.popitem() if isinstance(value, types.ModuleType): stmt = ast.Import( names=[ast.alias(name=value.__name__, asname=node.id)]) elif hasattr(value, '__name__'): path = reverse_builtin_map.get(value) if path is None: path = value.__module__ name = value.__name__ stmt = ast.ImportFrom( module=path, names=[ast.alias(name=name, asname=node.id)], level=0, ) else: raise TypeError(value) self.visit(stmt) # Clear last import self.flush() # Stich together lines self.lines += defines + body def define(self, name, node): assert node is not None value = self.defines.get(name) if value is node: pass elif value is None: self.defines[name] = node else: raise CompilationError( "Duplicate symbol name for define.", name) return load(name) def require(self, value): if value is None: return load("None") if isinstance(value, NATIVE_NUMBERS): return ast.Num(value) node = self.imports.get(value) if node is None: # we come up with a unique symbol based on the class name name = "_%s" % getattr(value, '__name__', str(value)) node = load(name) self.imports[value] = store(node.id) return node def visit(self, node): annotation = node_annotations.get(node) if annotation is None: super(TemplateCodeGenerator, self).visit(node) else: self.visit(annotation) def visit_Comment(self, node): if node.stmt is None: self._new_line() else: self.visit(node.stmt) for line in node.text.replace('\r', '\n').split('\n'): self._new_line() self._write("%s#%s" % (node.space, line)) def visit_Builtin(self, node): name = load(node.id) self.visit(name) def visit_Symbol(self, node): node = self.require(node.value) self.visit(node) def visit_Static(self, node): if node.name is None: name = "_static_%d" % id(node.value) else: name = node.name node = self.define(name, node.value) self.visit(node) Chameleon-2.6.1/src/chameleon/zpt/0000755000175000001440000000000011665422242017407 5ustar mborchusers00000000000000Chameleon-2.6.1/src/chameleon/zpt/program.py0000644000175000001440000005062011665421423021433 0ustar mborchusers00000000000000import re try: import ast except ImportError: from chameleon import ast24 as ast try: str = unicode except NameError: long = int from functools import partial from ..program import ElementProgram from ..namespaces import XML_NS from ..namespaces import XMLNS_NS from ..namespaces import I18N_NS as I18N from ..namespaces import TAL_NS as TAL from ..namespaces import METAL_NS as METAL from ..namespaces import META_NS as META from ..astutil import Static from ..astutil import parse from ..astutil import marker from .. import tal from .. import metal from .. import i18n from .. import nodes from ..exc import LanguageError from ..exc import ParseError from ..exc import CompilationError from ..utils import decode_htmlentities try: str = unicode except NameError: long = int missing = object() def skip(node): return node def wrap(node, *wrappers): for wrapper in reversed(wrappers): node = wrapper(node) return node def validate_attributes(attributes, namespace, whitelist): for ns, name in attributes: if ns == namespace and name not in whitelist: raise CompilationError( "Bad attribute for namespace '%s'" % ns, name ) class MacroProgram(ElementProgram): """Visitor class that generates a program for the ZPT language.""" DEFAULT_NAMESPACES = { 'xmlns': XMLNS_NS, 'xml': XML_NS, 'tal': TAL, 'metal': METAL, 'i18n': I18N, 'meta': META, } DROP_NS = TAL, METAL, I18N, META VARIABLE_BLACKLIST = "default", "repeat", "nothing", \ "convert", "decode", "translate" _interpolation_enabled = True _whitespace = "\n" _last = "" # Macro name (always trivial for a macro program) name = None # This default marker value has the semantics that if an # expression evaluates to that value, the expression default value # is returned. For an attribute, if there is no default, this # means that the attribute is dropped. default_marker = None # Escape mode (true value means XML-escape) escape = True # Attributes which should have boolean behavior (on true, the # value takes the attribute name, on false, the attribute is # dropped) boolean_attributes = set() # If provided, this should be a set of attributes for implicit # translation. Any attribute whose name is included in the set # will be translated even without explicit markup. Note that all # values should be lowercase strings. implicit_i18n_attributes = set() # If set, text will be translated even without explicit markup. implicit_i18n_translate = False def __init__(self, *args, **kwargs): # Internal array for switch statements self._switches = [] # Internal array for current use macro level self._use_macro = [] # Internal array for current interpolation status self._interpolation = [True] # Internal dictionary of macro definitions self._macros = {} # Apply default values from **kwargs to self self._pop_defaults( kwargs, 'boolean_attributes', 'default_marker', 'escape', 'implicit_i18n_translate', 'implicit_i18n_attributes', ) super(MacroProgram, self).__init__(*args, **kwargs) @property def macros(self): macros = list(self._macros.items()) macros.append((None, nodes.Sequence(self.body))) return tuple( nodes.Macro(name, [nodes.Context(node)]) for name, node in macros ) def visit_default(self, node): return nodes.Text(node) def visit_element(self, start, end, children): ns = start['ns_attrs'] for (prefix, attr), encoded in tuple(ns.items()): if prefix == TAL: ns[prefix, attr] = decode_htmlentities(encoded) # Validate namespace attributes validate_attributes(ns, TAL, tal.WHITELIST) validate_attributes(ns, METAL, metal.WHITELIST) validate_attributes(ns, I18N, i18n.WHITELIST) # Check attributes for language errors self._check_attributes(start['namespace'], ns) # Remember whitespace for item repetition if self._last is not None: self._whitespace = "\n" + " " * len(self._last.rsplit('\n', 1)[-1]) # Set element-local whitespace whitespace = self._whitespace # Set up switch try: clause = ns[TAL, 'switch'] except KeyError: switch = None else: switch = nodes.Value(clause) self._switches.append(switch) body = [] # Include macro use_macro = ns.get((METAL, 'use-macro')) extend_macro = ns.get((METAL, 'extend-macro')) if use_macro or extend_macro: slots = [] self._use_macro.append(slots) if use_macro: inner = nodes.UseExternalMacro( nodes.Value(use_macro), slots, False ) else: inner = nodes.UseExternalMacro( nodes.Value(extend_macro), slots, True ) # -or- include tag else: content = nodes.Sequence(body) # tal:content try: clause = ns[TAL, 'content'] except KeyError: pass else: key, value = tal.parse_substitution(clause) xlate = True if ns.get((I18N, 'translate')) == '' else False content = self._make_content_node(value, content, key, xlate) if end is None: # Make sure start-tag has opening suffix. start['suffix'] = ">" # Explicitly set end-tag. end = { 'prefix': '' } # i18n:translate try: clause = ns[I18N, 'translate'] except KeyError: pass else: dynamic = ns.get((TAL, 'content')) or ns.get((TAL, 'replace')) if not dynamic: content = nodes.Translate(clause, content) # tal:attributes try: clause = ns[TAL, 'attributes'] except KeyError: TAL_ATTRIBUTES = {} else: TAL_ATTRIBUTES = tal.parse_attributes(clause) # i18n:attributes try: clause = ns[I18N, 'attributes'] except KeyError: I18N_ATTRIBUTES = {} else: I18N_ATTRIBUTES = i18n.parse_attributes(clause) # Prepare attributes from TAL language prepared = tal.prepare_attributes( start['attrs'], TAL_ATTRIBUTES, I18N_ATTRIBUTES, ns, self.DROP_NS ) # Create attribute nodes STATIC_ATTRIBUTES = self._create_static_attributes(prepared) ATTRIBUTES = self._create_attributes_nodes( prepared, I18N_ATTRIBUTES ) # Start- and end nodes start_tag = nodes.Start( start['name'], start['prefix'], start['suffix'], ATTRIBUTES ) end_tag = nodes.End( end['name'], end['space'], end['prefix'], end['suffix'], ) if end is not None else None # tal:omit-tag try: clause = ns[TAL, 'omit-tag'] except KeyError: omit = False else: clause = clause.strip() if clause == "": omit = True else: expression = nodes.Negate(nodes.Value(clause)) omit = expression # Wrap start- and end-tags in condition start_tag = nodes.Condition(expression, start_tag) if end_tag is not None: end_tag = nodes.Condition(expression, end_tag) if omit is True or start['namespace'] in self.DROP_NS: inner = content else: inner = nodes.Element( start_tag, end_tag, content, ) # Assign static attributes dictionary to "attrs" value inner = nodes.Define( [nodes.Alias(["attrs"], STATIC_ATTRIBUTES)], inner, ) if omit is not False: inner = nodes.Cache([omit], inner) # tal:replace try: clause = ns[TAL, 'replace'] except KeyError: pass else: key, value = tal.parse_substitution(clause) xlate = True if ns.get((I18N, 'translate')) == '' else False inner = self._make_content_node(value, inner, key, xlate) # metal:define-slot try: clause = ns[METAL, 'define-slot'] except KeyError: DEFINE_SLOT = skip else: DEFINE_SLOT = partial(nodes.DefineSlot, clause) # tal:define try: clause = ns[TAL, 'define'] except KeyError: DEFINE = skip else: defines = tal.parse_defines(clause) if defines is None: raise ParseError("Invalid define syntax.", clause) DEFINE = partial( nodes.Define, [nodes.Assignment( names, nodes.Value(expr), context == "local") for (context, names, expr) in defines], ) # tal:case try: clause = ns[TAL, 'case'] except KeyError: CASE = skip else: value = nodes.Value(clause) for switch in reversed(self._switches): if switch is not None: break else: raise LanguageError( "Must define switch on a parent element.", clause ) CASE = lambda node: nodes.Define( [nodes.Assignment(["default"], switch, True)], nodes.Condition( nodes.Equality(switch, value), node, ) ) # tal:repeat try: clause = ns[TAL, 'repeat'] except KeyError: REPEAT = skip else: defines = tal.parse_defines(clause) assert len(defines) == 1 context, names, expr = defines[0] expression = nodes.Value(expr) REPEAT = partial( nodes.Repeat, names, expression, context == "local", whitespace ) # tal:condition try: clause = ns[TAL, 'condition'] except KeyError: CONDITION = skip else: expression = nodes.Value(clause) CONDITION = partial(nodes.Condition, expression) # tal:switch if switch is None: SWITCH = skip else: SWITCH = partial(nodes.Cache, [switch]) # i18n:domain try: clause = ns[I18N, 'domain'] except KeyError: DOMAIN = skip else: DOMAIN = partial(nodes.Domain, clause) # i18n:name try: clause = ns[I18N, 'name'] except KeyError: NAME = skip else: NAME = partial(nodes.Name, clause) # The "slot" node next is the first node level that can serve # as a macro slot slot = wrap( inner, DEFINE_SLOT, DEFINE, CASE, CONDITION, REPEAT, SWITCH, DOMAIN, ) # metal:fill-slot try: clause = ns[METAL, 'fill-slot'] except KeyError: pass else: index = -(1 + int(bool(use_macro or extend_macro))) try: slots = self._use_macro[index] except IndexError: raise LanguageError( "Cannot use metal:fill-slot without metal:use-macro.", clause ) slots = self._use_macro[index] slots.append(nodes.FillSlot(clause, slot)) # metal:define-macro try: clause = ns[METAL, 'define-macro'] except KeyError: pass else: self._macros[clause] = slot slot = nodes.UseInternalMacro(clause) slot = wrap( slot, NAME ) # tal:on-error try: clause = ns[TAL, 'on-error'] except KeyError: ON_ERROR = skip else: key, value = tal.parse_substitution(clause) translate = True if ns.get((I18N, 'translate')) == '' else False fallback = nodes.Element( start_tag, end_tag, self._make_content_node(value, None, key, translate) ) ON_ERROR = partial(nodes.OnError, fallback, 'error') clause = ns.get((META, 'interpolation')) if clause in ('false', 'off'): INTERPOLATION = False elif clause in ('true', 'on'): INTERPOLATION = True elif clause is None: INTERPOLATION = self._interpolation[-1] else: raise LanguageError("Bad interpolation setting.", clause) self._interpolation.append(INTERPOLATION) # Visit content body for child in children: body.append(self.visit(*child)) self._switches.pop() self._interpolation.pop() if use_macro: self._use_macro.pop() return wrap( slot, ON_ERROR ) def visit_start_tag(self, start): return self.visit_element(start, None, []) def visit_comment(self, node): if node.startswith(' Chameleon-2.6.1/src/chameleon/tests/inputs/017.xml0000644000175000001440000000012311445675021022304 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/015.xml0000644000175000001440000000022611445675021022306 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/028.xml0000644000175000001440000000012311445675021022306 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/005.xml0000644000175000001440000000015011445675021022301 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/098.xml0000644000175000001440000000010711445675021022317 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/094.xml0000644000175000001440000000016011445675021022312 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/057-order.pt0000644000175000001440000000031211533661135023243 0ustar mborchusers00000000000000
Chameleon-2.6.1/src/chameleon/tests/inputs/055.xml0000644000175000001440000000011211445675021022304 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/115.xml0000644000175000001440000000014711445675021022311 0ustar mborchusers00000000000000 ]> &e1; Chameleon-2.6.1/src/chameleon/tests/inputs/109.xml0000644000175000001440000000014211445675021022307 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/054-import-expression.pt0000644000175000001440000000014411532704766025646 0ustar mborchusers00000000000000
${datetime(1984, 12, 31).isoformat()}
Chameleon-2.6.1/src/chameleon/tests/inputs/001.xml0000644000175000001440000000007411445675021022302 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/088.xml0000644000175000001440000000012711445675021022320 0ustar mborchusers00000000000000 "> ]> &e; Chameleon-2.6.1/src/chameleon/tests/inputs/086.xml0000644000175000001440000000014411445675021022315 0ustar mborchusers00000000000000 "> ]> &e; Chameleon-2.6.1/src/chameleon/tests/inputs/090.xml0000644000175000001440000000022611445675021022311 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/033.xml0000644000175000001440000000016511445675021022310 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/051-use-non-extended-macro.pt0000644000175000001440000000026711531163410026403 0ustar mborchusers00000000000000 Filled content Chameleon-2.6.1/src/chameleon/tests/inputs/009-literals.pt0000644000175000001440000000006111445675021023746 0ustar mborchusers00000000000000 ${literal} Chameleon-2.6.1/src/chameleon/tests/inputs/007.xml0000644000175000001440000000010111445675021022277 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/046.xml0000644000175000001440000000017011445675021022310 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/020-on-error.pt0000644000175000001440000000024511636355675023703 0ustar mborchusers00000000000000
Chameleon-2.6.1/src/chameleon/tests/inputs/001-variable-scope.html0000644000175000001440000000016311450127612025331 0ustar mborchusers00000000000000 ${text} $text ${text | 'Goodbye world!'} Chameleon-2.6.1/src/chameleon/tests/inputs/013.xml0000644000175000001440000000017411445675021022306 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/108.xml0000644000175000001440000000017111445675021022310 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/015-translation-nested.pt0000644000175000001440000000031211445675021025741 0ustar mborchusers00000000000000
Price: Per kilo ${12.5}
Chameleon-2.6.1/src/chameleon/tests/inputs/068.xml0000644000175000001440000000012411445675021022313 0ustar mborchusers00000000000000 ]> &e; Chameleon-2.6.1/src/chameleon/tests/inputs/042-use-macro-fill-footer.pt0000644000175000001440000000022011501223221026214 0ustar mborchusers00000000000000 New footer Chameleon-2.6.1/src/chameleon/tests/inputs/001-variable-scope.pt0000644000175000001440000000031711613337133025013 0ustar mborchusers00000000000000 ${text} bad ok Chameleon-2.6.1/src/chameleon/tests/inputs/096.xml0000644000175000001440000000014311445675021022315 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/008-builtins.pt0000644000175000001440000000033511620700251023750 0ustar mborchusers00000000000000 ${nothing}
${attrs['class']}
${nothing}
Chameleon-2.6.1/src/chameleon/tests/inputs/022-switch.pt0000644000175000001440000000047511445675021023434 0ustar mborchusers00000000000000
bad ok ok
bad ok
Chameleon-2.6.1/src/chameleon/tests/inputs/003-content.pt0000644000175000001440000000077211622441723023601 0ustar mborchusers00000000000000
1 2
3
4
5 6
Chameleon-2.6.1/src/chameleon/tests/inputs/014-repeat-nested-similar.pt0000644000175000001440000000020211445675021026316 0ustar mborchusers00000000000000 [${i},${j}] Chameleon-2.6.1/src/chameleon/tests/inputs/023.xml0000644000175000001440000000011711445675021022304 0ustar mborchusers00000000000000 ]> &e; Chameleon-2.6.1/src/chameleon/tests/inputs/016-explicit-translation.pt0000644000175000001440000000062611445675021026311 0ustar mborchusers00000000000000
Hello world!
${'Hello world!'} ${'Hello world!'} Chameleon-2.6.1/src/chameleon/tests/inputs/038.xml0000644000175000001440000000012011445675021022304 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/036-use-macro-inherits-dynamic-scope.pt0000644000175000001440000000016311450644604030400 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/inputs/107.xml0000644000175000001440000000015111445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/002.xml0000644000175000001440000000007511445675021022304 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/075-nested-macros.pt0000644000175000001440000000043211650737465024711 0ustar mborchusers00000000000000 foo Chameleon-2.6.1/src/chameleon/tests/inputs/105.xml0000644000175000001440000000015011445675021022302 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/025.xml0000644000175000001440000000014411445675021022306 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/050-define-macro-and-use-not-extend.pt0000644000175000001440000000027411641065557030104 0ustar mborchusers00000000000000 Default content Chameleon-2.6.1/src/chameleon/tests/inputs/024.xml0000644000175000001440000000016411445675021022307 0ustar mborchusers00000000000000 "> ]> &e; Chameleon-2.6.1/src/chameleon/tests/inputs/029.xml0000644000175000001440000000012311445675021022307 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/009.xml0000644000175000001440000000010211445675021022302 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/111.xml0000644000175000001440000000017311445675021022304 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/103.xml0000644000175000001440000000010511445675021022300 0ustar mborchusers00000000000000 ]> <doc> Chameleon-2.6.1/src/chameleon/tests/inputs/043-macro-nested-dynamic-vars.pt0000644000175000001440000000061411504611130027070 0ustar mborchusers00000000000000 ${title}
Chameleon-2.6.1/src/chameleon/tests/inputs/003.xml0000644000175000001440000000007511445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/064.xml0000644000175000001440000000011711445675021022311 0ustar mborchusers00000000000000 ]> 𐀀􏿽 Chameleon-2.6.1/src/chameleon/tests/inputs/092.xml0000644000175000001440000000014611445675021022314 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/067-attribute-decode.pt0000644000175000001440000000031111620445601025347 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/inputs/043.xml0000644000175000001440000000015411445675021022307 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/042.xml0000644000175000001440000000014211445675021022303 0ustar mborchusers00000000000000 ]> A Chameleon-2.6.1/src/chameleon/tests/inputs/063.xml0000644000175000001440000000015411445675021022311 0ustar mborchusers00000000000000 ]> <เจมส์> Chameleon-2.6.1/src/chameleon/tests/inputs/044.xml0000644000175000001440000000027311445675021022312 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/110.xml0000644000175000001440000000020111445675021022273 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/070.xml0000644000175000001440000000012111445675021022301 0ustar mborchusers00000000000000"> %e; ]> Chameleon-2.6.1/src/chameleon/tests/inputs/012.xml0000644000175000001440000000014411445675021022302 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/038-use-macro-globals.pt0000644000175000001440000000021411450666037025450 0ustar mborchusers00000000000000 ok Chameleon-2.6.1/src/chameleon/tests/inputs/078.xml0000644000175000001440000000014411445675021022316 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/103-simple-attribute.html0000644000175000001440000000032011474712662025740 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/inputs/033-use-macro-trivial.pt0000644000175000001440000000011111450436720025460 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/inputs/101-unclosed-tags.html0000644000175000001440000000010311474663644025220 0ustar mborchusers00000000000000



Hello world

Chameleon-2.6.1/src/chameleon/tests/inputs/117.xml0000644000175000001440000000012611445675021022310 0ustar mborchusers00000000000000 ]> ] Chameleon-2.6.1/src/chameleon/tests/inputs/062-comments-and-expressions.pt0000644000175000001440000000056411620466453027105 0ustar mborchusers00000000000000
Chameleon-2.6.1/src/chameleon/tests/inputs/069-translation-domain-and-macro.pt0000644000175000001440000000024711620714244027601 0ustar mborchusers00000000000000 Title Chameleon-2.6.1/src/chameleon/tests/inputs/045-namespaces.pt0000644000175000001440000000104711535132161024244 0ustar mborchusers00000000000000 ]> ZZZ YYY XXX Chameleon-2.6.1/src/chameleon/tests/inputs/005-default.pt0000644000175000001440000000054211631410432023541 0ustar mborchusers00000000000000 Default Default Default ${'Computed default'} Chameleon-2.6.1/src/chameleon/tests/inputs/032.xml0000644000175000001440000000014411445675021022304 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/019-replace.pt0000644000175000001440000000057511445675021023555 0ustar mborchusers00000000000000
1 2
3
4
5 6
Chameleon-2.6.1/src/chameleon/tests/inputs/078-tags-and-newlines.pt0000644000175000001440000000143611663637540025472 0ustar mborchusers00000000000000 , Chameleon-2.6.1/src/chameleon/tests/inputs/031.xml0000644000175000001440000000014411445675021022303 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/104.xml0000644000175000001440000000014511445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/006-attribute-interpolation.pt0000644000175000001440000000061711637044266027027 0ustar mborchusers00000000000000 copyright (c) ${2010} copyright (c) ${2010} copyright (c) ${2010} $ignored ${'%stype \'str\'%s' % (chr(60), chr(62))} Chameleon-2.6.1/src/chameleon/tests/inputs/047.xml0000644000175000001440000000010011445675021022302 0ustar mborchusers00000000000000 ]> X Y Chameleon-2.6.1/src/chameleon/tests/inputs/048-use-extended-macro-fill-original.pt0000644000175000001440000000023611641065271030353 0ustar mborchusers00000000000000 Extended footer Chameleon-2.6.1/src/chameleon/tests/inputs/020.xml0000644000175000001440000000011511445675021022277 0ustar mborchusers00000000000000 ]> ]]]> Chameleon-2.6.1/src/chameleon/tests/inputs/054.xml0000644000175000001440000000011011445675021022301 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/047-use-extended-macro.pt0000644000175000001440000000017211530505174025621 0ustar mborchusers00000000000000 Extended Chameleon-2.6.1/src/chameleon/tests/inputs/037.xml0000644000175000001440000000012011445675021022303 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/061-fill-one-slot-but-two-defined.pt0000644000175000001440000000023011547545735027615 0ustar mborchusers00000000000000 My document Chameleon-2.6.1/src/chameleon/tests/inputs/118.xml0000644000175000001440000000012711445675021022312 0ustar mborchusers00000000000000 ]> ] Chameleon-2.6.1/src/chameleon/tests/inputs/060-macro-with-multiple-same-slots.pt0000644000175000001440000000033711547545705030132 0ustar mborchusers00000000000000 <metal:title define-slot="title">Untitled</metal:title>

Untitled

Chameleon-2.6.1/src/chameleon/tests/inputs/085.xml0000644000175000001440000000014611445675021022316 0ustar mborchusers00000000000000 "> ]> &e; Chameleon-2.6.1/src/chameleon/tests/inputs/101.xml0000644000175000001440000000012111445675021022274 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/073.xml0000644000175000001440000000013611445675021022312 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/044-tuple-define.pt0000644000175000001440000000011611530322743024503 0ustar mborchusers00000000000000 ${a}, ${b} Chameleon-2.6.1/src/chameleon/tests/inputs/041-translate-nested-names.pt0000644000175000001440000000076511470435213026507 0ustar mborchusers00000000000000
Hello world!
Hello world!
Goodbye world!
Chameleon-2.6.1/src/chameleon/tests/inputs/099.xml0000644000175000001440000000014411445675021022321 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/069.xml0000644000175000001440000000013511445675021022316 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/102-unquoted-attributes.html0000644000175000001440000000012411474722745026502 0ustar mborchusers00000000000000

Hello world

Chameleon-2.6.1/src/chameleon/tests/inputs/035.xml0000644000175000001440000000007011445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/036.xml0000644000175000001440000000011111445675021022302 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/116.xml0000644000175000001440000000011211445675021022302 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/010.xml0000644000175000001440000000014711445675021022303 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/041.xml0000644000175000001440000000015111445675021022302 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/016.xml0000644000175000001440000000010211445675021022300 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/028-attribute-toggle.pt0000644000175000001440000000040011623427755025416 0ustar mborchusers00000000000000
Chameleon-2.6.1/src/chameleon/tests/inputs/102.xml0000644000175000001440000000014711445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/112.xml0000644000175000001440000000013111445675021022277 0ustar mborchusers00000000000000 ]>
Chameleon-2.6.1/src/chameleon/tests/inputs/013-repeat-nested.pt0000644000175000001440000000025511445675021024667 0ustar mborchusers00000000000000
[${i},${j}]
Chameleon-2.6.1/src/chameleon/tests/inputs/045.xml0000644000175000001440000000017011445675021022307 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/050.xml0000644000175000001440000000020411445675021022301 0ustar mborchusers00000000000000ÿþ<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc>@!*L</doc> Chameleon-2.6.1/src/chameleon/tests/inputs/071.xml0000644000175000001440000000013211445675021022304 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/080-xmlns-namespace-on-tal.pt0000644000175000001440000000037211663637075026427 0ustar mborchusers00000000000000 Hello world Chameleon-2.6.1/src/chameleon/tests/inputs/026.xml0000644000175000001440000000014011445675021022303 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/001-interpolation.txt0000644000175000001440000000002611531710252025173 0ustar mborchusers00000000000000${''}<&> Chameleon-2.6.1/src/chameleon/tests/inputs/067.xml0000644000175000001440000000010111445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/greeting.pt0000644000175000001440000000005111634317676023435 0ustar mborchusers00000000000000
Hello, ${name | 'undefined'}.
Chameleon-2.6.1/src/chameleon/tests/inputs/072-repeat-interpolation.pt0000644000175000001440000000054711634104364026302 0ustar mborchusers00000000000000
  • ${i}
  • ${i}
  • ${i}
Chameleon-2.6.1/src/chameleon/tests/inputs/080.xml0000644000175000001440000000013711445675021022311 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/022.xml0000644000175000001440000000012011445675021022275 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/113.xml0000644000175000001440000000013311445675021022302 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/051.xml0000644000175000001440000000021411445675021022303 0ustar mborchusers00000000000000ÿþ<!DOCTYPE @!*L [ <!ELEMENT @!*L (#PCDATA)> ]> <@!*L></@!*L> Chameleon-2.6.1/src/chameleon/tests/inputs/002-repeat-scope.pt0000644000175000001440000000033111474511104024500 0ustar mborchusers00000000000000
${text}${char}
ok Chameleon-2.6.1/src/chameleon/tests/inputs/089.xml0000644000175000001440000000015411445675021022321 0ustar mborchusers00000000000000 ]> &e; Chameleon-2.6.1/src/chameleon/tests/inputs/065.xml0000644000175000001440000000012111445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/011-messages.pt0000644000175000001440000000035311445675021023733 0ustar mborchusers00000000000000
${message} Chameleon-2.6.1/src/chameleon/tests/inputs/004.xml0000644000175000001440000000014611445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/114.xml0000644000175000001440000000014011445675021022301 0ustar mborchusers00000000000000 "> ]> &e; Chameleon-2.6.1/src/chameleon/tests/inputs/017-omit-tag.pt0000644000175000001440000000055511447161567023666 0ustar mborchusers00000000000000
Hello world!
1 Hello world! 2
3 4
Hello world!
Hello world!
Hello world!
Hello world!
Chameleon-2.6.1/src/chameleon/tests/inputs/063-continuation.pt0000644000175000001440000000010311613337164024636 0ustar mborchusers00000000000000
${foo}
Chameleon-2.6.1/src/chameleon/tests/inputs/059.xml0000644000175000001440000000034311445675021022316 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/068-less-than-greater-than-in-attributes.pt0000644000175000001440000000032211620447275031204 0ustar mborchusers00000000000000 0 < 1 or 0 > 1 Chameleon-2.6.1/src/chameleon/tests/inputs/011.xml0000644000175000001440000000020011445675021022272 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/055-attribute-fallback-to-dict-lookup.pt0000644000175000001440000000014211622651153030535 0ustar mborchusers00000000000000
${obj.foo} ${obj.boo.bar}
Chameleon-2.6.1/src/chameleon/tests/inputs/030.xml0000644000175000001440000000012511445675021022301 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/034-use-template-as-macro.pt0000644000175000001440000000007111450412655026231 0ustar mborchusers00000000000000Chameleon-2.6.1/src/chameleon/tests/inputs/073-utf8-encoded.pt0000644000175000001440000000022311634410073024407 0ustar mborchusers00000000000000 ${'my title'} — ${'my site'} Chameleon-2.6.1/src/chameleon/tests/inputs/058.xml0000644000175000001440000000015711445675021022320 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/052.xml0000644000175000001440000000010411445675021022302 0ustar mborchusers00000000000000 ]> ð€€ô¿½ Chameleon-2.6.1/src/chameleon/tests/inputs/052-i18n-domain-inside-filled-slot.pt0000644000175000001440000000040211531247362027634 0ustar mborchusers00000000000000 Translated content Chameleon-2.6.1/src/chameleon/tests/inputs/091.xml0000644000175000001440000000026511445675021022315 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/034.xml0000644000175000001440000000006711445675021022312 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/060.xml0000644000175000001440000000010311445675021022300 0ustar mborchusers00000000000000 ]> X Y Chameleon-2.6.1/src/chameleon/tests/inputs/048.xml0000644000175000001440000000007511445675021022316 0ustar mborchusers00000000000000 ]> ] Chameleon-2.6.1/src/chameleon/tests/inputs/018-translation-nested-dynamic.pt0000644000175000001440000000065611447165360027403 0ustar mborchusers00000000000000
monthname year
Chameleon-2.6.1/src/chameleon/tests/inputs/057.xml0000644000175000001440000000006711445675021022317 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/018.xml0000644000175000001440000000011511445675021022306 0ustar mborchusers00000000000000 ]> ]]> Chameleon-2.6.1/src/chameleon/tests/inputs/070-translation-domain-and-use-macro.pt0000644000175000001440000000024711620714335030364 0ustar mborchusers00000000000000 Title Chameleon-2.6.1/src/chameleon/tests/inputs/084.xml0000644000175000001440000000006611445675021022316 0ustar mborchusers00000000000000]> Chameleon-2.6.1/src/chameleon/tests/inputs/023-condition.pt0000644000175000001440000000025211527750224024113 0ustar mborchusers00000000000000 bad ok Chameleon-2.6.1/src/chameleon/tests/inputs/040-macro-using-template-symbol.pt0000644000175000001440000000153711450701313027461 0ustar mborchusers00000000000000 ${foo}
Chameleon-2.6.1/src/chameleon/tests/inputs/095.xml0000644000175000001440000000021511445675021022314 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/071-html-attribute-defaults.pt0000644000175000001440000000074111623433350026700 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/inputs/006.xml0000644000175000001440000000014611445675021022307 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/077.xml0000644000175000001440000000013511445675021022315 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/083.xml0000644000175000001440000000014511445675021022313 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/012-translation.pt0000644000175000001440000000102511470434755024465 0ustar mborchusers00000000000000
Hello world!
Hello world!
Hello world!
Hello ${'world'}! Goodbye ${'planet'}!
Hello ${'world'}! Goodbye ${'planet'}!
Chameleon-2.6.1/src/chameleon/tests/inputs/037-use-macro-local-variable-scope.pt0000644000175000001440000000030311450651744030006 0ustar mborchusers00000000000000 ok Chameleon-2.6.1/src/chameleon/tests/inputs/014.xml0000644000175000001440000000022611445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/031-namespace-with-tal.pt0000644000175000001440000000035411450407017025604 0ustar mborchusers00000000000000
True
Chameleon-2.6.1/src/chameleon/tests/inputs/049-entities-in-attributes.pt0000644000175000001440000000070711665421275026562 0ustar mborchusers00000000000000
    
    
  
  
    
  
Chameleon-2.6.1/src/chameleon/tests/inputs/087.xml0000644000175000001440000000015311445675021022316 0ustar  mborchusers00000000000000


]>
&e;
Chameleon-2.6.1/src/chameleon/tests/inputs/024-namespace-elements.pt0000644000175000001440000000040211450103772025664 0ustar  mborchusers00000000000000
  
    
      
        ${'first'}
      
      second
    
    
      ok
    
    
      bad
    
  

Chameleon-2.6.1/src/chameleon/tests/inputs/093.xml0000644000175000001440000000007411445675021022315 0ustar  mborchusers00000000000000
]>




Chameleon-2.6.1/src/chameleon/tests/inputs/074.xml0000644000175000001440000000013611445675021022313 0ustar  mborchusers00000000000000

]>

Chameleon-2.6.1/src/chameleon/tests/inputs/046-extend-macro.pt0000644000175000001440000000034511530504607024517 0ustar  mborchusers00000000000000
  
    New footer
  
Chameleon-2.6.1/src/chameleon/tests/inputs/007-content-interpolation.pt0000644000175000001440000000037611665422017026474 0ustar  mborchusers00000000000000
  
    ${'Hello world!'}
    ${literal}
    ${structure: literal.s}
    ${"%stype 'str'%s" % (chr(60), chr(62))}
    &&
    ${None}
    $leftalone
    
${None}
${1 < 2 and 'Hello world' or None}
Chameleon-2.6.1/src/chameleon/tests/inputs/079.xml0000644000175000001440000000014511445675021022320 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/032-master-template.pt0000644000175000001440000000112411613211562025221 0ustar mborchusers00000000000000 Master template
Chameleon-2.6.1/src/chameleon/tests/inputs/082.xml0000644000175000001440000000013211445675021022306 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/106.xml0000644000175000001440000000015111445675021022304 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/079-implicit-i18n.pt0000644000175000001440000000063611663216641024536 0ustar mborchusers00000000000000 Welcome

Welcome

An edge case: ${. Site logo Site logo
foo.
bar.
Chameleon-2.6.1/src/chameleon/tests/inputs/119.xml0000644000175000001440000000010211445675021022304 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/029-attribute-ordering.pt0000644000175000001440000000031611450375624025750 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/inputs/097.xml0000644000175000001440000000023511445675021022320 0ustar mborchusers00000000000000 %e; ]> Chameleon-2.6.1/src/chameleon/tests/inputs/100.xml0000644000175000001440000000014511445675021022301 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/021-translation-domain.pt0000644000175000001440000000060311445675021025726 0ustar mborchusers00000000000000
Hello world!
Hello world!
Hello world!
Hello world!
Chameleon-2.6.1/src/chameleon/tests/inputs/039.xml0000644000175000001440000000011111445675021022305 0ustar mborchusers00000000000000 ]> Chameleon-2.6.1/src/chameleon/tests/inputs/066-load-expression.pt0000644000175000001440000000012511614215717025247 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/inputs/061.xml0000644000175000001440000000010211445675021022300 0ustar mborchusers00000000000000 ]> £ Chameleon-2.6.1/src/chameleon/tests/inputs/064-tags-and-special-characters.pt0000644000175000001440000000010511607240251027351 0ustar mborchusers00000000000000
Chameleon-2.6.1/src/chameleon/tests/outputs/0000755000175000001440000000000011665422242021457 5ustar mborchusers00000000000000Chameleon-2.6.1/src/chameleon/tests/outputs/059.pt0000644000175000001440000000021611620242115022326 0ustar mborchusers00000000000000 test test Chameleon-2.6.1/src/chameleon/tests/outputs/053.pt0000644000175000001440000000013511531477155022337 0ustar mborchusers00000000000000
test test Chameleon-2.6.1/src/chameleon/tests/outputs/052.pt0000644000175000001440000000030211531247677022336 0ustar mborchusers00000000000000 Master template
Translated content
Chameleon-2.6.1/src/chameleon/tests/outputs/hello_world.txt0000644000175000001440000000001511532701100024510 0ustar mborchusers00000000000000Hello world! Chameleon-2.6.1/src/chameleon/tests/outputs/004.pt0000644000175000001440000000065411620437315022332 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/outputs/014.pt0000644000175000001440000000026411445675021022333 0ustar mborchusers00000000000000 [3,3] [3,4] [4,3] [4,4] Chameleon-2.6.1/src/chameleon/tests/outputs/001.txt0000644000175000001440000000002111531710247022506 0ustar mborchusers00000000000000<&> Chameleon-2.6.1/src/chameleon/tests/outputs/019.pt0000644000175000001440000000024211623430401022321 0ustar mborchusers00000000000000 Hello world! Hello world!1 2Hello world! Hello world!3 Hello world!5 6Hello world! 1 1.0 True Chameleon-2.6.1/src/chameleon/tests/outputs/060.pt0000644000175000001440000000014711547546013022335 0ustar mborchusers00000000000000 Untitled

Untitled

Chameleon-2.6.1/src/chameleon/tests/outputs/015-en.pt0000644000175000001440000000026311447145253022734 0ustar mborchusers00000000000000
Price: Per kilo 12.5 ('Per kilo ${amount}' translation into 'en') ('Price: ${price}' translation into 'en')
Chameleon-2.6.1/src/chameleon/tests/outputs/013.pt0000644000175000001440000000042511445675021022331 0ustar mborchusers00000000000000
[1,1] [1,2]
[2,1] [2,2]
Chameleon-2.6.1/src/chameleon/tests/outputs/045.pt0000644000175000001440000000071711535132165022337 0ustar mborchusers00000000000000 ]> ZZZ YYY XXX Chameleon-2.6.1/src/chameleon/tests/outputs/005.pt0000644000175000001440000000030711623430361022323 0ustar mborchusers00000000000000 Default True False Computed default Chameleon-2.6.1/src/chameleon/tests/outputs/070.pt0000644000175000001440000000026111620714410022321 0ustar mborchusers00000000000000 Title
Chameleon-2.6.1/src/chameleon/tests/outputs/072.pt0000644000175000001440000000047411634104417022336 0ustar mborchusers00000000000000
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
Chameleon-2.6.1/src/chameleon/tests/outputs/054.pt0000644000175000001440000000004211532704236022327 0ustar mborchusers00000000000000
1984-12-31T00:00:00
Chameleon-2.6.1/src/chameleon/tests/outputs/037.pt0000644000175000001440000000026211530207463022332 0ustar mborchusers00000000000000 Master template
ok
Chameleon-2.6.1/src/chameleon/tests/outputs/003.pt0000644000175000001440000000054311623430232022320 0ustar mborchusers00000000000000
Hello world!
Hello world!
1 2
Hello world!
Hello world!
3
Hello world!
5 6
Hello world!
1
1.0
True
False
0
<div>Hello world!</div> Chameleon-2.6.1/src/chameleon/tests/outputs/010.pt0000644000175000001440000000024411614001244022311 0ustar mborchusers00000000000000
1 < 2
2 < 3, 2&3, 2<3, 2>3
3 < 4
4 < 5
Hello world!
Chameleon-2.6.1/src/chameleon/tests/outputs/033.pt0000644000175000001440000000027411530177642022336 0ustar mborchusers00000000000000 Master template
Chameleon-2.6.1/src/chameleon/tests/outputs/071.pt0000644000175000001440000000042511623433341022330 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/outputs/029.pt0000644000175000001440000000015311530420217022324 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/outputs/021-en.pt0000644000175000001440000000063611447150716022735 0ustar mborchusers00000000000000
Hello world! ('Hello world!' translation into 'en' with domain 'new')
Hello world! ('Hello world!' translation into 'en' with domain 'old')
Hello world!
Hello world!
Chameleon-2.6.1/src/chameleon/tests/outputs/056.pt0000644000175000001440000000006711533406634022342 0ustar mborchusers00000000000000 Namespace tag Chameleon-2.6.1/src/chameleon/tests/outputs/065.pt0000644000175000001440000000025211613214603022326 0ustar mborchusers00000000000000 Title
Content
Chameleon-2.6.1/src/chameleon/tests/outputs/101.html0000644000175000001440000000010311474655747022660 0ustar mborchusers00000000000000



Hello world

Chameleon-2.6.1/src/chameleon/tests/outputs/018.pt0000644000175000001440000000010111530420146022314 0ustar mborchusers00000000000000
october 1982
Chameleon-2.6.1/src/chameleon/tests/outputs/063.pt0000644000175000001440000000002011604560332022320 0ustar mborchusers00000000000000
2
Chameleon-2.6.1/src/chameleon/tests/outputs/036.pt0000644000175000001440000000026611530206360022330 0ustar mborchusers00000000000000 New title
Chameleon-2.6.1/src/chameleon/tests/outputs/021.pt0000644000175000001440000000030411447150735022326 0ustar mborchusers00000000000000
Hello world!
Hello world!
Hello world!
Hello world!
Chameleon-2.6.1/src/chameleon/tests/outputs/077.pt0000644000175000001440000000010511653732207022337 0ustar mborchusers00000000000000Chameleon-2.6.1/src/chameleon/tests/outputs/077-en.pt0000644000175000001440000000017011653732243022741 0ustar mborchusers00000000000000Chameleon-2.6.1/src/chameleon/tests/outputs/057.pt0000644000175000001440000000006511533661164022342 0ustar mborchusers00000000000000 1 2 3 Chameleon-2.6.1/src/chameleon/tests/outputs/001.html0000644000175000001440000000012511450131562022635 0ustar mborchusers00000000000000 Hello world! Hello world! Goodbye world! Chameleon-2.6.1/src/chameleon/tests/outputs/031.pt0000644000175000001440000000011511530160736022322 0ustar mborchusers00000000000000
Hello World! Hello World! Hello World! 0 1 2 True
Chameleon-2.6.1/src/chameleon/tests/outputs/047.pt0000644000175000001440000000033111530505232022323 0ustar mborchusers00000000000000 Master template
Chameleon-2.6.1/src/chameleon/tests/outputs/027.pt0000644000175000001440000000026311530420176022330 0ustar mborchusers00000000000000
abcghi Hello World! Hello World!
Chameleon-2.6.1/src/chameleon/tests/outputs/055.pt0000644000175000001440000000003111622651153022325 0ustar mborchusers00000000000000
bar baz
Chameleon-2.6.1/src/chameleon/tests/outputs/040.pt0000644000175000001440000000071211530420223022313 0ustar mborchusers00000000000000 foo Chameleon-2.6.1/src/chameleon/tests/outputs/066.pt0000644000175000001440000000006311614327666022346 0ustar mborchusers00000000000000 Hello world! Chameleon-2.6.1/src/chameleon/tests/outputs/012-en.pt0000644000175000001440000000074111447145063022731 0ustar mborchusers00000000000000
Hello world! ('Hello world!' translation into 'en')
Hello world! ('hello_world' translation into 'en')
Hello world! ('Hello world!' translation into 'en')
Hello world! Goodbye planet! ('Hello ${first}! Goodbye ${second}!' translation into 'en')
Hello world! Goodbye planet! ('hello_goodbye' translation into 'en')
Chameleon-2.6.1/src/chameleon/tests/outputs/020.pt0000644000175000001440000000007011636363403022323 0ustar mborchusers00000000000000 NameError thrown at 3:22. Chameleon-2.6.1/src/chameleon/tests/outputs/061.pt0000644000175000001440000000015411547570257022343 0ustar mborchusers00000000000000 My document

My document

Chameleon-2.6.1/src/chameleon/tests/outputs/078.pt0000644000175000001440000000045111663637443022353 0ustar mborchusers00000000000000 1, 2, 3 Chameleon-2.6.1/src/chameleon/tests/outputs/011.pt0000644000175000001440000000021211445675021022321 0ustar mborchusers00000000000000
Message
Message
Message
Message
Message Chameleon-2.6.1/src/chameleon/tests/outputs/046.pt0000644000175000001440000000033011530504512022321 0ustar mborchusers00000000000000 Master template
Chameleon-2.6.1/src/chameleon/tests/outputs/016-en.pt0000644000175000001440000000057311445675021022740 0ustar mborchusers00000000000000
Hello world! ('Hello world!' translation into 'en')
Hello world! ('Hello world!' translation into 'en') Hello world! ('hello_world' translation into 'en') Hello world! ('Hello world!' translation into 'en') Hello world! ('hello_world' translation into 'en') Chameleon-2.6.1/src/chameleon/tests/outputs/017.pt0000644000175000001440000000024011445675021022330 0ustar mborchusers00000000000000 Hello world! 1 Hello world! 23 4Hello world!
Hello world!
Hello world! Hello world! Chameleon-2.6.1/src/chameleon/tests/outputs/039.pt0000644000175000001440000000000011450666100022320 0ustar mborchusers00000000000000Chameleon-2.6.1/src/chameleon/tests/outputs/006.pt0000644000175000001440000000037411637044304022333 0ustar mborchusers00000000000000 copyright (c) 2010 copyright (c) 2010 copyright (c) 2010 $ignored <type 'str'> Chameleon-2.6.1/src/chameleon/tests/outputs/012.pt0000644000175000001440000000037511447145110022325 0ustar mborchusers00000000000000
Hello world!
Hello world!
Hello world!
Hello world! Goodbye planet!
Hello world! Goodbye planet!
Chameleon-2.6.1/src/chameleon/tests/outputs/051.pt0000644000175000001440000000026211641065670022333 0ustar mborchusers00000000000000 Master template
Default content
Chameleon-2.6.1/src/chameleon/tests/outputs/022.pt0000644000175000001440000000022011445675021022322 0ustar mborchusers00000000000000
ok ok
ok
Chameleon-2.6.1/src/chameleon/tests/outputs/064.pt0000644000175000001440000000005111607240261022324 0ustar mborchusers00000000000000
Chameleon-2.6.1/src/chameleon/tests/outputs/009.pt0000644000175000001440000000007611445675021022340 0ustar mborchusers00000000000000
Hello world!
Chameleon-2.6.1/src/chameleon/tests/outputs/073.pt0000644000175000001440000000021111634332673022333 0ustar mborchusers00000000000000 my title — my site Chameleon-2.6.1/src/chameleon/tests/outputs/023.pt0000644000175000001440000000006711527750230022331 0ustar mborchusers00000000000000 ok Chameleon-2.6.1/src/chameleon/tests/outputs/002.pt0000644000175000001440000000027711474511115022327 0ustar mborchusers00000000000000
Hello! Hello.
Goodbye! Goodbye.
ok Chameleon-2.6.1/src/chameleon/tests/outputs/079.pt0000644000175000001440000000047511663215706022354 0ustar mborchusers00000000000000 Welcome

Welcome

An edge case: ${. Site logo Site logo
foo.
bar.
Chameleon-2.6.1/src/chameleon/tests/outputs/048.pt0000644000175000001440000000032011530505245022326 0ustar mborchusers00000000000000 Master template
Chameleon-2.6.1/src/chameleon/tests/outputs/018-en.pt0000644000175000001440000000026111530420157022725 0ustar mborchusers00000000000000
october ('october' translation into 'en') 1982 ('1982' translation into 'en') ('${monthname} ${year}' translation into 'en')
Chameleon-2.6.1/src/chameleon/tests/outputs/025.pt0000644000175000001440000000032011527750442022330 0ustar mborchusers00000000000000
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 1, 2, 3 .
Chameleon-2.6.1/src/chameleon/tests/outputs/103.html0000644000175000001440000000032011474712674022654 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/outputs/greeting.pt0000644000175000001440000000003511634317703023627 0ustar mborchusers00000000000000
Hello, undefined.
Chameleon-2.6.1/src/chameleon/tests/outputs/043.pt0000644000175000001440000000007011530211567022324 0ustar mborchusers00000000000000 My title Chameleon-2.6.1/src/chameleon/tests/outputs/030.pt0000644000175000001440000000015011450404371022315 0ustar mborchusers00000000000000
1, 1, 2
2, 3, 4
Chameleon-2.6.1/src/chameleon/tests/outputs/032.pt0000644000175000001440000000027411530164370022327 0ustar mborchusers00000000000000 Master template
Chameleon-2.6.1/src/chameleon/tests/outputs/038.pt0000644000175000001440000000006711450666064022345 0ustar mborchusers00000000000000 ok Chameleon-2.6.1/src/chameleon/tests/outputs/016.pt0000644000175000001440000000027211445675021022334 0ustar mborchusers00000000000000
Hello world!
Hello world! Hello world! Hello world! Hello world! Chameleon-2.6.1/src/chameleon/tests/outputs/001.pt0000644000175000001440000000007511613337140022321 0ustar mborchusers00000000000000 Hello world! ok Chameleon-2.6.1/src/chameleon/tests/outputs/041.pt0000644000175000001440000000021311474531133022322 0ustar mborchusers00000000000000
Hello world!
Hello world!
Goodbye
Chameleon-2.6.1/src/chameleon/tests/outputs/008.pt0000644000175000001440000000017011620700263022322 0ustar mborchusers00000000000000
static
nothing
Chameleon-2.6.1/src/chameleon/tests/outputs/079-en.pt0000644000175000001440000000106311663220411022733 0ustar mborchusers00000000000000 Welcome ('Welcome' translation into 'en')

Welcome ('Welcome' translation into 'en')

An edge case: ${. ('An edge case: ${.' translation into 'en') Site logo ('Site logo' translation into 'en') Site logo ('Site logo' translation into 'en')
foo. ('foo.' translation into 'en')
bar. ('bar.' translation into 'en')
Chameleon-2.6.1/src/chameleon/tests/outputs/070-en.pt0000644000175000001440000000034411620714435022732 0ustar mborchusers00000000000000 Title ('title' translation into 'en' with domain 'test')
Chameleon-2.6.1/src/chameleon/tests/outputs/007.pt0000644000175000001440000000030411665422044022327 0ustar mborchusers00000000000000 Hello world!
Hello world!
Hello world!
<type 'str'> && $leftalone
Hello world
Chameleon-2.6.1/src/chameleon/tests/outputs/102.html0000644000175000001440000000012411474663732022655 0ustar mborchusers00000000000000

Hello world

Chameleon-2.6.1/src/chameleon/tests/outputs/011-en.pt0000644000175000001440000000046411445675021022732 0ustar mborchusers00000000000000
Message ('message' translation into 'en')
Message ('message' translation into 'en')
Message ('message' translation into 'en')
Message ('message' translation into 'en')
Message ('message' translation into 'en') Chameleon-2.6.1/src/chameleon/tests/outputs/049.pt0000644000175000001440000000032411665421726022345 0ustar mborchusers00000000000000
amp=&amp; lt=&lt;
amp=& lt=<
Chameleon-2.6.1/src/chameleon/tests/outputs/069-en.pt0000644000175000001440000000034411620463317022742 0ustar mborchusers00000000000000 Title ('title' translation into 'en' with domain 'test')
Chameleon-2.6.1/src/chameleon/tests/outputs/035.pt0000644000175000001440000000027511530206353022331 0ustar mborchusers00000000000000 New title
Chameleon-2.6.1/src/chameleon/tests/outputs/hello_world.pt0000644000175000001440000000006211532677465024350 0ustar mborchusers00000000000000 Hello world! Chameleon-2.6.1/src/chameleon/tests/outputs/067.pt0000644000175000001440000000014211620445571022336 0ustar mborchusers00000000000000 Chameleon-2.6.1/src/chameleon/tests/outputs/062.pt0000644000175000001440000000043211620466455022337 0ustar mborchusers00000000000000
Chameleon-2.6.1/src/chameleon/tests/outputs/044.pt0000644000175000001440000000005311530322766022332 0ustar mborchusers00000000000000 a, b Chameleon-2.6.1/src/chameleon/tests/outputs/068.pt0000644000175000001440000000025411620447301022334 0ustar mborchusers00000000000000 0 < 1 or 0 > 1 0 < 1 or 0 > 1 0 < 1 or 0 > 1 Chameleon-2.6.1/src/chameleon/tests/test_parser.py0000644000175000001440000000561111640111541022632 0ustar mborchusers00000000000000from __future__ import with_statement import sys from unittest import TestCase from ..namespaces import XML_NS from ..namespaces import XMLNS_NS from ..namespaces import PY_NS class ParserTest(TestCase): def test_sample_files(self): import os import traceback path = os.path.join(os.path.dirname(__file__), "inputs") for filename in os.listdir(path): if not filename.endswith('.html'): continue with open(os.path.join(path, filename), 'rb') as f: source = f.read() from ..utils import read_encoded try: want = read_encoded(source) except UnicodeDecodeError: exc = sys.exc_info()[1] self.fail("%s - %s" % (exc, filename)) from ..tokenize import iter_xml from ..parser import ElementParser try: tokens = iter_xml(want) parser = ElementParser(tokens, { 'xmlns': XMLNS_NS, 'xml': XML_NS, 'py': PY_NS, }) elements = tuple(parser) except: self.fail(traceback.format_exc()) output = [] def render(kind, args): if kind == 'element': # start tag tag, end, children = args output.append("%(prefix)s%(name)s" % tag) for attr in tag['attrs']: output.append( "%(space)s%(name)s%(eq)s%(quote)s%(value)s%(quote)s" % \ attr ) output.append("%(suffix)s" % tag) # children for item in children: render(*item) # end tag output.append( "%(prefix)s%(name)s%(space)s%(suffix)s" % end ) elif kind == 'text': text = args[0] output.append(text) elif kind == 'start_tag': node = args[0] output.append( "%(prefix)s%(name)s%(space)s%(suffix)s" % node ) else: raise RuntimeError("Not implemented: %s." % kind) for kind, args in elements: render(kind, args) got = "".join(output) from doctest import OutputChecker checker = OutputChecker() if checker.check_output(want, got, 0) is False: from doctest import Example example = Example(f.name, want) diff = checker.output_difference( example, got, 0) self.fail("(%s) - \n%s" % (f.name, diff)) Chameleon-2.6.1/src/chameleon/tests/__init__.py0000644000175000001440000000000211447151137022035 0ustar mborchusers00000000000000# Chameleon-2.6.1/src/chameleon/tests/test_loader.py0000644000175000001440000000541011611511114022577 0ustar mborchusers00000000000000import unittest class LoadTests: def _makeOne(self, search_path=None, **kwargs): klass = self._getTargetClass() return klass(search_path, **kwargs) def _getTargetClass(self): from chameleon.loader import TemplateLoader return TemplateLoader def test_load_relative(self): import os here = os.path.join(os.path.dirname(__file__), "inputs") loader = self._makeOne(search_path=[here]) result = self._load(loader, 'hello_world.pt') self.assertEqual(result.filename, os.path.join(here, 'hello_world.pt')) def test_consecutive_loads(self): import os here = os.path.join(os.path.dirname(__file__), "inputs") loader = self._makeOne(search_path=[here]) self.assertTrue( self._load(loader, 'hello_world.pt') is \ self._load(loader, 'hello_world.pt')) def test_load_relative_badpath_in_searchpath(self): import os here = os.path.join(os.path.dirname(__file__), "inputs") loader = self._makeOne(search_path=[os.path.join(here, 'none'), here]) result = self._load(loader, 'hello_world.pt') self.assertEqual(result.filename, os.path.join(here, 'hello_world.pt')) def test_load_abs(self): import os here = os.path.join(os.path.dirname(__file__), "inputs") loader = self._makeOne() abs = os.path.join(here, 'hello_world.pt') result = self._load(loader, abs) self.assertEqual(result.filename, abs) class LoadPageTests(unittest.TestCase, LoadTests): def _load(self, loader, filename): from chameleon.zpt import template return loader.load(filename, template.PageTemplateFile) class ZPTLoadTests(unittest.TestCase): def _makeOne(self, *args, **kwargs): import os here = os.path.join(os.path.dirname(__file__), "inputs") from chameleon.zpt import loader return loader.TemplateLoader(here, **kwargs) def test_load_xml(self): loader = self._makeOne() template = loader.load("hello_world.pt", "xml") from chameleon.zpt.template import PageTemplateFile self.assertTrue(isinstance(template, PageTemplateFile)) def test_load_text(self): loader = self._makeOne() template = loader.load("hello_world.txt", "text") from chameleon.zpt.template import PageTextTemplateFile self.assertTrue(isinstance(template, PageTextTemplateFile)) def test_load_getitem_gets_xml_file(self): loader = self._makeOne() template = loader["hello_world.pt"] from chameleon.zpt.template import PageTemplateFile self.assertTrue(isinstance(template, PageTemplateFile)) def test_suite(): import sys return unittest.findTestCases(sys.modules[__name__]) Chameleon-2.6.1/src/chameleon/tales.py0000644000175000001440000003527211663211426020263 0ustar mborchusers00000000000000import re import sys try: import ast except ImportError: from chameleon import ast24 as ast from .astutil import parse from .astutil import store from .astutil import load from .astutil import ItemLookupOnAttributeErrorVisitor from .codegen import TemplateCodeGenerator from .codegen import template from .codegen import reverse_builtin_map from .astutil import Builtin from .astutil import Symbol from .exc import ExpressionError from .utils import resolve_dotted from .utils import Markup from .tokenize import Token from .parser import substitute from .compiler import Interpolator try: from .py26 import lookup_attr except SyntaxError: from .py25 import lookup_attr split_parts = re.compile(r'(?', 'exec') exec(code, env) result = env["result"] if isinstance(result, basestring): result = str(result) return result def transform_attribute(node): return template( "lookup(object, name)", lookup=Symbol(lookup_attr), object=node.value, name=ast.Str(s=node.attr), mode="eval" ) class TalesExpr(object): """Base class. This class helps implementations for the Template Attribute Language Expression Syntax (TALES). The syntax evaluates one or more expressions, separated by '|' (pipe). The first expression that succeeds, is returned. Expression: expression := (type ':')? line ('|' expression)? line := .* Expression lines may not contain the pipe character unless escaped. It has a special meaning: If the expression to the left of the pipe fails (raises one of the exceptions listed in ``catch_exceptions``), evaluation proceeds to the expression(s) on the right. Subclasses must implement ``translate`` which assigns a value for a given expression. >>> class PythonPipeExpr(TalesExpr): ... def translate(self, expression, target): ... compiler = PythonExpr(expression) ... return compiler(target, None) >>> test(PythonPipeExpr('foo | bar | 42')) 42 >>> test(PythonPipeExpr('foo|42')) 42 """ exceptions = NameError, \ ValueError, \ AttributeError, \ LookupError, \ TypeError def __init__(self, expression): self.expression = expression def __call__(self, target, engine): remaining = self.expression assignments = [] while remaining: if match_prefix(remaining) is not None: compiler = engine.parse(remaining) assignment = compiler.assign_value(target) remaining = "" else: for m in split_parts.finditer(remaining): expression = remaining[:m.start()] remaining = remaining[m.end():] break else: expression = remaining remaining = "" expression = expression.replace('\\|', '|') assignment = self.translate(expression, target) assignments.append(assignment) if not assignments: assignments.append( self.translate(remaining, target) ) for i, assignment in enumerate(reversed(assignments)): if i == 0: body = assignment else: body = [ast.TryExcept( body=assignment, handlers=[ast.ExceptHandler( type=ast.Tuple( elts=map(resolve_global, self.exceptions), ctx=ast.Load()), name=None, body=body, )], )] return body def translate(self, expression, target): """Return statements that assign a value to ``target``.""" raise NotImplementedError( "Must be implemented by a subclass.") class PathExpr(TalesExpr): """Path expression compiler. Syntax:: PathExpr ::= Path [ '|' Path ]* Path ::= variable [ '/' URL_Segment ]* variable ::= Name For example:: request/cookies/oatmeal nothing here/some-file 2001_02.html.tar.gz/foo root/to/branch | default When a path expression is evaluated, it attempts to traverse each path, from left to right, until it succeeds or runs out of paths. To traverse a path, it first fetches the object stored in the variable. For each path segment, it traverses from the current object to the subobject named by the path segment. Once a path has been successfully traversed, the resulting object is the value of the expression. If it is a callable object, such as a method or class, it is called. The semantics of traversal (and what it means to be callable) are implementation-dependent (see the ``translate`` method). """ def translate(self, expression, target): raise NotImplementedError( "Path expressions are not yet implemented. " "It's unclear whether a general implementation " "can be devised.") class PythonExpr(TalesExpr): """Python expression compiler. >>> test(PythonExpr('2 + 2')) 4 The Python expression is a TALES expression. That means we can use the pipe operator: >>> test(PythonExpr('foo | 2 + 2 | 5')) 4 To include a pipe character, use a backslash escape sequence: >>> test(PythonExpr('\"\|\"')) '|' """ transform = ItemLookupOnAttributeErrorVisitor(transform_attribute) def parse(self, string): return parse(string, 'eval').body def translate(self, expression, target): # Strip spaces string = expression.strip() # Conver line continuations to newlines string = substitute(re_continuation, '\n', string) # Convert newlines to spaces string = string.replace('\n', ' ') try: value = self.parse(string) except SyntaxError: exc = sys.exc_info()[1] raise ExpressionError(exc.msg, string) # Transform attribute lookups to allow fallback to item lookup self.transform.visit(value) return [ast.Assign(targets=[target], value=value)] class ProxyExpr(TalesExpr): def __init__(self, name, *args): super(ProxyExpr, self).__init__(*args) self.name = name def translate(self, expression, target): path = expression.strip() return [ast.Assign(targets=[target], value=ast.Call( func=load(self.name), args=[ast.Str(s=path)], keywords=[], starargs=None, kwargs=None ))] class ImportExpr(object): re_dotted = re.compile(r'^[A-Za-z.]+$') def __init__(self, expression): self.expression = expression def __call__(self, target, engine): string = self.expression.strip().replace('\n', ' ') value = template( "RESOLVE(NAME)", RESOLVE=Symbol(resolve_dotted), NAME=ast.Str(s=string), mode="eval", ) return [ast.Assign(targets=[target], value=value)] class NotExpr(object): """Negates the expression. >>> engine = SimpleEngine(PythonExpr) >>> test(NotExpr('False'), engine) True >>> test(NotExpr('True'), engine) False """ def __init__(self, expression): self.expression = expression def __call__(self, target, engine): compiler = engine.parse(self.expression) body = compiler.assign_value(target) return body + template("target = not target", target=target) class StructureExpr(object): """Wraps the expression result as 'structure'. >>> engine = SimpleEngine(PythonExpr) >>> test(StructureExpr('\"foo\"'), engine) 'foo' """ wrapper_class = Symbol(Markup) def __init__(self, expression): self.expression = expression def __call__(self, target, engine): compiler = engine.parse(self.expression) body = compiler.assign_value(target) return body + template( "target = wrapper(target)", target=target, wrapper=self.wrapper_class ) class IdentityExpr(object): """Identity expression. Exists to demonstrate the interface. >>> test(IdentityExpr('42')) 42 """ def __init__(self, expression): self.expression = expression def __call__(self, target, engine): compiler = engine.parse(self.expression) return compiler.assign_value(target) class StringExpr(object): """Similar to the built-in ``string.Template``, but uses an expression engine to support pluggable string substitution expressions. Expr string: string := (text | substitution) (string)? substitution := ('$' variable | '${' expression '}') text := .* In other words, an expression string can contain multiple substitutions. The text- and substitution parts will be concatenated back into a string. >>> test(StringExpr('Hello ${name}!'), name='world') 'Hello world!' In the default configuration, braces may be omitted if the expression is an identifier. >>> test(StringExpr('Hello $name!'), name='world') 'Hello world!' The ``braces_required`` flag changes this setting: >>> test(StringExpr('Hello $name!', True)) 'Hello $name!' We can escape interpolation using the standard escaping syntax: >>> test(StringExpr('\\${name}')) '\\\${name}' Multiple interpolations in one: >>> test(StringExpr(\"Hello ${'a'}${'b'}${'c'}!\")) 'Hello abc!' Here's a more involved example taken from a javascript source: >>> result = test(StringExpr(\"\"\" ... function(oid) { ... $('#' + oid).autocomplete({source: ${'source'}}); ... } ... \"\"\")) >>> 'source: source' in result True In the above examples, the expression is evaluated using the dummy engine which just returns the input as a string. As an example, we'll implement an expression engine which instead counts the number of characters in the expresion and returns an integer result. >>> class engine: ... @staticmethod ... def parse(expression): ... class compiler: ... @staticmethod ... def assign_text(target): ... return [ ... ast.Assign( ... targets=[target], ... value=ast.Num(n=len(expression)) ... )] ... ... return compiler This will demonstrate how the string expression coerces the input to a string. >>> expr = StringExpr( ... 'There are ${hello world} characters in \"hello world\"') We evaluate the expression using the new engine: >>> test(expr, engine) 'There are 11 characters in \"hello world\"' """ def __init__(self, expression, braces_required=False): # The code relies on the expression being a token string if not isinstance(expression, Token): expression = Token(expression, 0) self.translator = Interpolator(expression, braces_required) def __call__(self, name, engine): return self.translator(name, engine) class ExistsExpr(object): """Boolean wrapper. Return 0 if the expression results in an exception, otherwise 1. As a means to generate exceptions, we set up an expression engine which evaluates the provided expression using Python: >>> engine = SimpleEngine(PythonExpr) >>> test(ExistsExpr('int(0)'), engine) 1 >>> test(ExistsExpr('int(None)'), engine) 0 """ exceptions = AttributeError, LookupError, TypeError, NameError, KeyError def __init__(self, expression): self.expression = expression def __call__(self, target, engine): ignore = store("_ignore") compiler = engine.parse(self.expression) body = compiler.assign_value(ignore) classes = map(resolve_global, self.exceptions) return [ ast.TryExcept( body=body, handlers=[ast.ExceptHandler( type=ast.Tuple(elts=classes, ctx=ast.Load()), name=None, body=template("target = 0", target=target), )], orelse=template("target = 1", target=target) ) ] class ExpressionParser(object): def __init__(self, factories, default): self.factories = factories self.default = default def __call__(self, expression): m = match_prefix(expression) if m is not None: prefix = m.group(1) expression = expression[m.end():] else: prefix = self.default try: factory = self.factories[prefix] except KeyError: exc = sys.exc_info()[1] raise LookupError( "Unknown expression type: %s." % str(exc) ) return factory(expression) class SimpleEngine(object): expression = PythonExpr def __init__(self, expression=None): if expression is not None: self.expression = expression def parse(self, string): compiler = self.expression(string) return SimpleCompiler(compiler, self) class SimpleCompiler(object): def __init__(self, compiler, engine): self.compiler = compiler self.engine = engine def assign_text(self, target): """Assign expression string as a text value.""" return self._assign_value_and_coerce(target, "str") def assign_value(self, target): """Assign expression string as object value.""" return self.compiler(target, self.engine) def _assign_value_and_coerce(self, target, builtin): return self.assign_value(target) + template( "target = builtin(target)", target=target, builtin=builtin ) Chameleon-2.6.1/src/chameleon/tal.py0000644000175000001440000002635711663637626017755 0ustar mborchusers00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (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. # ############################################################################## import re import copy from .exc import LanguageError from .utils import descriptorint from .utils import descriptorstr from .namespaces import XMLNS_NS from .parser import groups try: next except NameError: from chameleon.py25 import next try: # optional library: `zope.interface` import interfaces import zope.interface except ImportError: interfaces = None NAME = r"[a-zA-Z_][-a-zA-Z0-9_]*" DEFINE_RE = re.compile(r"(?s)\s*(?:(global|local)\s+)?" + r"(%s|\(%s(?:,\s*%s)*\))\s+(.*)\Z" % (NAME, NAME, NAME)) SUBST_RE = re.compile(r"\s*(?:(text|structure)\s+)?(.*)\Z", re.S) ATTR_RE = re.compile(r"\s*([^\s]+)\s+([^\s].*)\Z", re.S) ENTITY_RE = re.compile(r'(&(#?)(x?)(\d{1,5}|\w{1,8});)') WHITELIST = frozenset([ "define", "comment", "condition", "content", "replace", "repeat", "attributes", "on-error", "omit-tag", "script", "switch", "case", "xmlns", "xml" ]) def split_parts(arg): # Break in pieces at undoubled semicolons and # change double semicolons to singles: i = 0 while i < len(arg): m = ENTITY_RE.search(arg[i:]) if m is None: break arg = arg[:i + m.end()] + ';' + arg[i + m.end():] i += m.end() arg = arg.replace(";;", "\0") parts = arg.split(';') parts = [p.replace("\0", ";") for p in parts] if len(parts) > 1 and not parts[-1].strip(): del parts[-1] # It ended in a semicolon return parts def parse_attributes(clause): attrs = {} for part in split_parts(clause): m = ATTR_RE.match(part) if not m: raise LanguageError( "Bad syntax in attributes.", clause) name, expr = groups(m, part) if name in attrs: raise LanguageError( "Duplicate attribute name in attributes.", part) attrs[name] = expr return attrs def parse_substitution(clause): m = SUBST_RE.match(clause) if m is None: raise LanguageError( "Invalid content substitution syntax.", clause) key, expression = groups(m, clause) if not key: key = "text" return key, expression def parse_defines(clause): defines = [] for part in split_parts(clause): m = DEFINE_RE.match(part) if m is None: return context, name, expr = groups(m, part) context = context or "local" if name.startswith('('): names = [n.strip() for n in name.strip('()').split(',')] else: names = (name,) defines.append((context, names, expr)) return defines def prepare_attributes(attrs, dyn_attributes, i18n_attributes, ns_attributes, drop_ns): drop = set([attribute['name'] for attribute, (ns, value) in zip(attrs, ns_attributes) if ns in drop_ns or ( ns == XMLNS_NS and attribute['value'] in drop_ns ) ]) attributes = [] normalized = {} for attribute in attrs: name = attribute['name'] if name in drop: continue attributes.append(( name, attribute['value'], attribute['quote'], attribute['space'], attribute['eq'], None, )) normalized[name.lower()] = len(attributes) - 1 for name, expr in dyn_attributes.items(): index = normalized.get(name.lower()) if index is not None: _, text, quote, space, eq, _ = attributes[index] add = attributes.__setitem__ else: text = None quote = '"' space = " " eq = "=" index = len(attributes) add = attributes.insert normalized[name.lower()] = len(attributes) - 1 attribute = name, text, quote, space, eq, expr add(index, attribute) for name in i18n_attributes: attr = name.lower() if attr not in normalized: attributes.append((name, name, '"', " ", "=", None)) normalized[attr] = len(attributes) - 1 return attributes class RepeatItem(object): if interfaces is not None: zope.interface.implements(interfaces.ITALESIterator) __slots__ = "length", "_iterator" def __init__(self, iterator, length): self.length = length self._iterator = iterator def __iter__(self): return self._iterator try: iter(()).__len__ except AttributeError: @property def index(self): try: remaining = self._iterator.__length_hint__() except AttributeError: remaining = len(tuple(copy.copy(self._iterator))) return self.length - remaining - 1 else: @property def index(self): remaining = self._iterator.__len__() return self.length - remaining - 1 @property def start(self): return self.index == 0 @property def end(self): return self.index == self.length - 1 @descriptorint def number(self): return self.index + 1 @descriptorstr def odd(self): """Returns a true value if the item index is odd. >>> it = RepeatItem(iter(("apple", "pear")), 2) >>> next(it._iterator) 'apple' >>> it.odd() '' >>> next(it._iterator) 'pear' >>> it.odd() 'odd' """ return self.index % 2 == 1 and 'odd' or '' @descriptorstr def even(self): """Returns a true value if the item index is even. >>> it = RepeatItem(iter(("apple", "pear")), 2) >>> next(it._iterator) 'apple' >>> it.even() 'even' >>> next(it._iterator) 'pear' >>> it.even() '' """ return self.index % 2 == 0 and 'even' or '' def next(self): raise NotImplementedError( "Method not implemented (can't update local variable).") def _letter(self, base=ord('a'), radix=26): """Get the iterator position as a lower-case letter >>> it = RepeatItem(iter(("apple", "pear", "orange")), 3) >>> next(it._iterator) 'apple' >>> it.letter() 'a' >>> next(it._iterator) 'pear' >>> it.letter() 'b' >>> next(it._iterator) 'orange' >>> it.letter() 'c' """ index = self.index if index < 0: raise TypeError("No iteration position") s = "" while 1: index, off = divmod(index, radix) s = chr(base + off) + s if not index: return s letter = descriptorstr(_letter) @descriptorstr def Letter(self): """Get the iterator position as an upper-case letter >>> it = RepeatItem(iter(("apple", "pear", "orange")), 3) >>> next(it._iterator) 'apple' >>> it.Letter() 'A' >>> next(it._iterator) 'pear' >>> it.Letter() 'B' >>> next(it._iterator) 'orange' >>> it.Letter() 'C' """ return self._letter(base=ord('A')) @descriptorstr 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'))): """Get the iterator position as an upper-case roman numeral >>> it = RepeatItem(iter(("apple", "pear", "orange")), 3) >>> next(it._iterator) 'apple' >>> it.Roman() 'I' >>> next(it._iterator) 'pear' >>> it.Roman() 'II' >>> next(it._iterator) 'orange' >>> it.Roman() 'III' """ n = self.index + 1 s = "" for v, r in rnvalues: rct, n = divmod(n, v) s = s + r * rct return s @descriptorstr def roman(self): """Get the iterator position as a lower-case roman numeral >>> it = RepeatItem(iter(("apple", "pear", "orange")), 3) >>> next(it._iterator) 'apple' >>> it.roman() 'i' >>> next(it._iterator) 'pear' >>> it.roman() 'ii' >>> next(it._iterator) 'orange' >>> it.roman() 'iii' """ return self.Roman().lower() class RepeatDict(dict): """Repeat dictionary implementation. >>> repeat = RepeatDict({}) >>> iterator, length = repeat('numbers', range(5)) >>> length 5 >>> repeat['numbers'] """ __slots__ = "__setitem__", "__getitem__", "__getattr__" def __init__(self, d): self.__setitem__ = d.__setitem__ self.__getitem__ = d.__getitem__ self.__getattr__ = d.__getitem__ def __call__(self, key, iterable): """We coerce the iterable to a tuple and return an iterator after registering it in the repeat dictionary.""" try: iterable = tuple(iterable) except TypeError: if iterable is None: iterable = () else: # The message below to the TypeError is the Python # 2.5-style exception message. Python 2.4.X also # raises a TypeError, but with a different message. # ("TypeError: iteration over non-sequence"). The # Python 2.5 error message is more helpful. We # construct the 2.5-style message explicitly here so # that both Python 2.4.X and Python 2.5+ will raise # the same error. This makes writing the tests eaiser # and makes the output easier to understand. raise TypeError("%r object is not iterable" % type(iterable).__name__) length = len(iterable) iterator = iter(iterable) # Insert as repeat item self[key] = RepeatItem(iterator, length) return iterator, length class ErrorInfo(object): """Information about an exception passed to an on-error handler.""" if interfaces is not None: zope.interface.implements(interfaces.ITALExpressionErrorInfo) 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] Chameleon-2.6.1/src/chameleon/astutil.py0000644000175000001440000006205111663175456020647 0ustar mborchusers00000000000000# -*- coding: utf-8 -*- # # Copyright (C) 2008-2009 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://genshi.edgewall.org/wiki/License. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at http://genshi.edgewall.org/log/. """Support classes for generating code from abstract syntax trees.""" try: import ast except ImportError: from chameleon import ast24 as ast import sys import logging import weakref node_annotations = weakref.WeakKeyDictionary() try: node_annotations[ast.Name()] = None except TypeError: logging.debug( "Unable to create weak references to AST nodes. " \ "A lock will be used around compilation loop." ) node_annotations = {} __docformat__ = 'restructuredtext en' def annotated(value): node = load("annotation") node_annotations[node] = value return node def parse(source, mode='eval'): return compile(source, '', mode, ast.PyCF_ONLY_AST) def load(name): return ast.Name(id=name, ctx=ast.Load()) def store(name): return ast.Name(id=name, ctx=ast.Store()) def param(name): return ast.Name(id=name, ctx=ast.Param()) def delete(name): return ast.Name(id=name, ctx=ast.Del()) def subscript(name, value, ctx): return ast.Subscript( value=value, slice=ast.Index(value=ast.Str(s=name)), ctx=ctx, ) def walk_names(target, mode): for node in ast.walk(target): if isinstance(node, ast.Name) and \ isinstance(node.ctx, mode): yield node.id def copy(source, target): target.__class__ = source.__class__ target.__dict__ = source.__dict__ def swap(root, replacement, name): for node in ast.walk(root): if (isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load) and node.id == name): assert hasattr(replacement, '_fields') node_annotations.setdefault(node, replacement) def marker(name): return ast.Str(s="__%s" % name) class Node(object): """AST baseclass that gives us a convenient initialization method. We explicitly declare and use the ``_fields`` attribute.""" _fields = () def __init__(self, *args, **kwargs): assert isinstance(self._fields, tuple) self.__dict__.update(kwargs) for name, value in zip(self._fields, args): setattr(self, name, value) def __repr__(self): """Poor man's single-line pretty printer.""" name = type(self).__name__ return '<%s%s at %x>' % ( name, "".join(" %s=%r" % (name, getattr(self, name, "\"?\"")) for name in self._fields), id(self) ) class Builtin(Node): """Represents a Python builtin. Used when a builtin is used internally by the compiler, to avoid clashing with a user assignment (e.g. ``help`` is a builtin, but also commonly assigned in templates). """ _fields = "id", "ctx" ctx = ast.Load() class Symbol(Node): """Represents an importable symbol.""" _fields = "value", class Static(Node): """Represents a static value.""" _fields = "value", "name" name = None class Comment(Node): _fields = "text", "space", "stmt" stmt = None space = "" class ASTCodeGenerator(object): """General purpose base class for AST transformations. Every visitor method can be overridden to return an AST node that has been altered or replaced in some way. """ def __init__(self, tree): self.lines_info = [] self.line_info = [] self.lines = [] self.line = "" self.last = None self.indent = 0 self.blame_stack = [] self.visit(tree) if self.line.strip(): self._new_line() self.line = None self.line_info = None # strip trivial lines self.code = "\n".join( line.strip() and line or "" for line in self.lines ) def _change_indent(self, delta): self.indent += delta def _new_line(self): if self.line is not None: self.lines.append(self.line) self.lines_info.append(self.line_info) self.line = ' ' * 4 * self.indent if len(self.blame_stack) == 0: self.line_info = [] self.last = None else: self.line_info = [(0, self.blame_stack[-1],)] self.last = self.blame_stack[-1] def _write(self, s): if len(s) == 0: return if len(self.blame_stack) == 0: if self.last is not None: self.last = None self.line_info.append((len(self.line), self.last)) else: if self.last != self.blame_stack[-1]: self.last = self.blame_stack[-1] self.line_info.append((len(self.line), self.last)) self.line += s def flush(self): if self.line: self._new_line() def visit(self, node): if node is None: return None if type(node) is tuple: return tuple([self.visit(n) for n in node]) try: self.blame_stack.append((node.lineno, node.col_offset,)) info = True except AttributeError: info = False visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None) if visitor is None: raise Exception('No handler for ``%s`` (%s).' % ( node.__class__.__name__, repr(node))) ret = visitor(node) if info: self.blame_stack.pop() return ret def visit_Module(self, node): for n in node.body: self.visit(n) visit_Interactive = visit_Module visit_Suite = visit_Module def visit_Expression(self, node): return self.visit(node.body) # arguments = (expr* args, identifier? vararg, # identifier? kwarg, expr* defaults) def visit_arguments(self, node): first = True no_default_count = len(node.args) - len(node.defaults) for i, arg in enumerate(node.args): if not first: self._write(', ') else: first = False self.visit(arg) if i >= no_default_count: self._write('=') self.visit(node.defaults[i - no_default_count]) if getattr(node, 'vararg', None): if not first: self._write(', ') else: first = False self._write('*' + node.vararg) if getattr(node, 'kwarg', None): if not first: self._write(', ') else: first = False self._write('**' + node.kwarg) # FunctionDef(identifier name, arguments args, # stmt* body, expr* decorators) def visit_FunctionDef(self, node): self._new_line() for decorator in getattr(node, 'decorator_list', ()): self._new_line() self._write('@') self.visit(decorator) self._new_line() self._write('def ' + node.name + '(') self.visit(node.args) self._write('):') self._change_indent(1) for statement in node.body: self.visit(statement) self._change_indent(-1) # ClassDef(identifier name, expr* bases, stmt* body) def visit_ClassDef(self, node): self._new_line() self._write('class ' + node.name) if node.bases: self._write('(') self.visit(node.bases[0]) for base in node.bases[1:]: self._write(', ') self.visit(base) self._write(')') self._write(':') self._change_indent(1) for statement in node.body: self.visit(statement) self._change_indent(-1) # Return(expr? value) def visit_Return(self, node): self._new_line() self._write('return') if getattr(node, 'value', None): self._write(' ') self.visit(node.value) # Delete(expr* targets) def visit_Delete(self, node): self._new_line() self._write('del ') self.visit(node.targets[0]) for target in node.targets[1:]: self._write(', ') self.visit(target) # Assign(expr* targets, expr value) def visit_Assign(self, node): self._new_line() for target in node.targets: self.visit(target) self._write(' = ') self.visit(node.value) # AugAssign(expr target, operator op, expr value) def visit_AugAssign(self, node): self._new_line() self.visit(node.target) self._write(' ' + self.binary_operators[node.op.__class__] + '= ') self.visit(node.value) # Print(expr? dest, expr* values, bool nl) def visit_Print(self, node): self._new_line() self._write('print') if getattr(node, 'dest', None): self._write(' >> ') self.visit(node.dest) if getattr(node, 'values', None): self._write(', ') else: self._write(' ') if getattr(node, 'values', None): self.visit(node.values[0]) for value in node.values[1:]: self._write(', ') self.visit(value) if not node.nl: self._write(',') # For(expr target, expr iter, stmt* body, stmt* orelse) def visit_For(self, node): self._new_line() self._write('for ') self.visit(node.target) self._write(' in ') self.visit(node.iter) self._write(':') self._change_indent(1) for statement in node.body: self.visit(statement) self._change_indent(-1) if getattr(node, 'orelse', None): self._new_line() self._write('else:') self._change_indent(1) for statement in node.orelse: self.visit(statement) self._change_indent(-1) # While(expr test, stmt* body, stmt* orelse) def visit_While(self, node): self._new_line() self._write('while ') self.visit(node.test) self._write(':') self._change_indent(1) for statement in node.body: self.visit(statement) self._change_indent(-1) if getattr(node, 'orelse', None): self._new_line() self._write('else:') self._change_indent(1) for statement in node.orelse: self.visit(statement) self._change_indent(-1) # If(expr test, stmt* body, stmt* orelse) def visit_If(self, node): self._new_line() self._write('if ') self.visit(node.test) self._write(':') self._change_indent(1) for statement in node.body: self.visit(statement) self._change_indent(-1) if getattr(node, 'orelse', None): self._new_line() self._write('else:') self._change_indent(1) for statement in node.orelse: self.visit(statement) self._change_indent(-1) # With(expr context_expr, expr? optional_vars, stmt* body) def visit_With(self, node): self._new_line() self._write('with ') self.visit(node.context_expr) if getattr(node, 'optional_vars', None): self._write(' as ') self.visit(node.optional_vars) self._write(':') self._change_indent(1) for statement in node.body: self.visit(statement) self._change_indent(-1) # Raise(expr? type, expr? inst, expr? tback) def visit_Raise(self, node): self._new_line() self._write('raise') if not getattr(node, "type", None): exc = getattr(node, "exc", None) if exc is None: return self._write(' ') return self.visit(exc) self._write(' ') self.visit(node.type) if not node.inst: return self._write(', ') self.visit(node.inst) if not node.tback: return self._write(', ') self.visit(node.tback) # TryExcept(stmt* body, excepthandler* handlers, stmt* orelse) def visit_TryExcept(self, node): self._new_line() self._write('try:') self._change_indent(1) for statement in node.body: self.visit(statement) self._change_indent(-1) if getattr(node, 'handlers', None): for handler in node.handlers: self.visit(handler) self._new_line() if getattr(node, 'orelse', None): self._write('else:') self._change_indent(1) for statement in node.orelse: self.visit(statement) self._change_indent(-1) # excepthandler = (expr? type, expr? name, stmt* body) def visit_ExceptHandler(self, node): self._new_line() self._write('except') if getattr(node, 'type', None): self._write(' ') self.visit(node.type) if getattr(node, 'name', None): if sys.version_info[0] == 2: assert getattr(node, 'type', None) self._write(', ') else: self._write(' as ') self.visit(node.name) self._write(':') self._change_indent(1) for statement in node.body: self.visit(statement) self._change_indent(-1) visit_excepthandler = visit_ExceptHandler # TryFinally(stmt* body, stmt* finalbody) def visit_TryFinally(self, node): self._new_line() self._write('try:') self._change_indent(1) for statement in node.body: self.visit(statement) self._change_indent(-1) if getattr(node, 'finalbody', None): self._new_line() self._write('finally:') self._change_indent(1) for statement in node.finalbody: self.visit(statement) self._change_indent(-1) # Assert(expr test, expr? msg) def visit_Assert(self, node): self._new_line() self._write('assert ') self.visit(node.test) if getattr(node, 'msg', None): self._write(', ') self.visit(node.msg) def visit_alias(self, node): self._write(node.name) if getattr(node, 'asname', None): self._write(' as ') self._write(node.asname) # Import(alias* names) def visit_Import(self, node): self._new_line() self._write('import ') self.visit(node.names[0]) for name in node.names[1:]: self._write(', ') self.visit(name) # ImportFrom(identifier module, alias* names, int? level) def visit_ImportFrom(self, node): self._new_line() self._write('from ') if node.level: self._write('.' * node.level) self._write(node.module) self._write(' import ') self.visit(node.names[0]) for name in node.names[1:]: self._write(', ') self.visit(name) # Exec(expr body, expr? globals, expr? locals) def visit_Exec(self, node): self._new_line() self._write('exec ') self.visit(node.body) if not node.globals: return self._write(', ') self.visit(node.globals) if not node.locals: return self._write(', ') self.visit(node.locals) # Global(identifier* names) def visit_Global(self, node): self._new_line() self._write('global ') self.visit(node.names[0]) for name in node.names[1:]: self._write(', ') self.visit(name) # Expr(expr value) def visit_Expr(self, node): self._new_line() self.visit(node.value) # Pass def visit_Pass(self, node): self._new_line() self._write('pass') # Break def visit_Break(self, node): self._new_line() self._write('break') # Continue def visit_Continue(self, node): self._new_line() self._write('continue') ### EXPRESSIONS def with_parens(f): def _f(self, node): self._write('(') f(self, node) self._write(')') return _f bool_operators = {ast.And: 'and', ast.Or: 'or'} # BoolOp(boolop op, expr* values) @with_parens def visit_BoolOp(self, node): joiner = ' ' + self.bool_operators[node.op.__class__] + ' ' self.visit(node.values[0]) for value in node.values[1:]: self._write(joiner) self.visit(value) binary_operators = { ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/', ast.Mod: '%', ast.Pow: '**', ast.LShift: '<<', ast.RShift: '>>', ast.BitOr: '|', ast.BitXor: '^', ast.BitAnd: '&', ast.FloorDiv: '//' } # BinOp(expr left, operator op, expr right) @with_parens def visit_BinOp(self, node): self.visit(node.left) self._write(' ' + self.binary_operators[node.op.__class__] + ' ') self.visit(node.right) unary_operators = { ast.Invert: '~', ast.Not: 'not', ast.UAdd: '+', ast.USub: '-', } # UnaryOp(unaryop op, expr operand) def visit_UnaryOp(self, node): self._write(self.unary_operators[node.op.__class__] + ' ') self.visit(node.operand) # Lambda(arguments args, expr body) @with_parens def visit_Lambda(self, node): self._write('lambda ') self.visit(node.args) self._write(': ') self.visit(node.body) # IfExp(expr test, expr body, expr orelse) @with_parens def visit_IfExp(self, node): self.visit(node.body) self._write(' if ') self.visit(node.test) self._write(' else ') self.visit(node.orelse) # Dict(expr* keys, expr* values) def visit_Dict(self, node): self._write('{') for key, value in zip(node.keys, node.values): self.visit(key) self._write(': ') self.visit(value) self._write(', ') self._write('}') def visit_Set(self, node): self._write('{') elts = list(node.elts) last = elts.pop() for elt in elts: self.visit(elt) self._write(', ') self.visit(last) self._write('}') # ListComp(expr elt, comprehension* generators) def visit_ListComp(self, node): self._write('[') self.visit(node.elt) for generator in node.generators: # comprehension = (expr target, expr iter, expr* ifs) self._write(' for ') self.visit(generator.target) self._write(' in ') self.visit(generator.iter) for ifexpr in generator.ifs: self._write(' if ') self.visit(ifexpr) self._write(']') # GeneratorExp(expr elt, comprehension* generators) def visit_GeneratorExp(self, node): self._write('(') self.visit(node.elt) for generator in node.generators: # comprehension = (expr target, expr iter, expr* ifs) self._write(' for ') self.visit(generator.target) self._write(' in ') self.visit(generator.iter) for ifexpr in generator.ifs: self._write(' if ') self.visit(ifexpr) self._write(')') # Yield(expr? value) def visit_Yield(self, node): self._write('yield') if getattr(node, 'value', None): self._write(' ') self.visit(node.value) comparison_operators = { ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=', ast.Gt: '>', ast.GtE: '>=', ast.Is: 'is', ast.IsNot: 'is not', ast.In: 'in', ast.NotIn: 'not in', } # Compare(expr left, cmpop* ops, expr* comparators) @with_parens def visit_Compare(self, node): self.visit(node.left) for op, comparator in zip(node.ops, node.comparators): self._write(' ' + self.comparison_operators[op.__class__] + ' ') self.visit(comparator) # Call(expr func, expr* args, keyword* keywords, # expr? starargs, expr? kwargs) def visit_Call(self, node): self.visit(node.func) self._write('(') first = True for arg in node.args: if not first: self._write(', ') first = False self.visit(arg) for keyword in node.keywords: if not first: self._write(', ') first = False # keyword = (identifier arg, expr value) self._write(keyword.arg) self._write('=') self.visit(keyword.value) if getattr(node, 'starargs', None): if not first: self._write(', ') first = False self._write('*') self.visit(node.starargs) if getattr(node, 'kwargs', None): if not first: self._write(', ') first = False self._write('**') self.visit(node.kwargs) self._write(')') # Repr(expr value) def visit_Repr(self, node): self._write('`') self.visit(node.value) self._write('`') # Num(object n) def visit_Num(self, node): self._write(repr(node.n)) # Str(string s) def visit_Str(self, node): self._write(repr(node.s)) # Attribute(expr value, identifier attr, expr_context ctx) def visit_Attribute(self, node): self.visit(node.value) self._write('.') self._write(node.attr) # Subscript(expr value, slice slice, expr_context ctx) def visit_Subscript(self, node): self.visit(node.value) self._write('[') def _process_slice(node): if isinstance(node, ast.Ellipsis): self._write('...') elif isinstance(node, ast.Slice): if getattr(node, 'lower', 'None'): self.visit(node.lower) self._write(':') if getattr(node, 'upper', None): self.visit(node.upper) if getattr(node, 'step', None): self._write(':') self.visit(node.step) elif isinstance(node, ast.Index): self.visit(node.value) elif isinstance(node, ast.ExtSlice): self.visit(node.dims[0]) for dim in node.dims[1:]: self._write(', ') self.visit(dim) else: raise NotImplemented('Slice type not implemented') _process_slice(node.slice) self._write(']') # Name(identifier id, expr_context ctx) def visit_Name(self, node): self._write(node.id) # List(expr* elts, expr_context ctx) def visit_List(self, node): self._write('[') for elt in node.elts: self.visit(elt) self._write(', ') self._write(']') # Tuple(expr *elts, expr_context ctx) def visit_Tuple(self, node): self._write('(') for elt in node.elts: self.visit(elt) self._write(', ') self._write(')') class AnnotationAwareVisitor(ast.NodeVisitor): def visit(self, node): annotation = node_annotations.get(node) if annotation is not None: assert hasattr(annotation, '_fields') node = annotation super(AnnotationAwareVisitor, self).visit(node) def apply_transform(self, node): if node not in node_annotations: result = self.transform(node) if result is not None and result is not node: node_annotations[node] = result class NameLookupRewriteVisitor(AnnotationAwareVisitor): def __init__(self, transform): self.transform = transform self.transformed = set() self.scopes = [set()] def __call__(self, node): self.visit(node) return self.transformed def visit_Name(self, node): scope = self.scopes[-1] if isinstance(node.ctx, ast.Param): scope.add(node.id) elif node.id not in scope: self.transformed.add(node.id) self.apply_transform(node) def visit_Lambda(self, node): self.scopes.append(set()) try: self.visit(node.args) self.visit(node.body) finally: self.scopes.pop() class ItemLookupOnAttributeErrorVisitor(AnnotationAwareVisitor): def __init__(self, transform): self.transform = transform def visit_Attribute(self, node): self.generic_visit(node) self.apply_transform(node) Chameleon-2.6.1/src/chameleon/template.py0000644000175000001440000002302211663356765020774 0ustar mborchusers00000000000000from __future__ import with_statement import os import sys import copy import atexit import hashlib import shutil import logging import tempfile import inspect pkg_digest = hashlib.sha1(__name__.encode('utf-8')) try: import pkg_resources except ImportError: logging.info("Setuptools not installed. Unable to determine version.") else: for path in sys.path: for distribution in pkg_resources.find_distributions(path): if distribution.has_version(): version = distribution.version.encode('utf-8') pkg_digest.update(version) from .exc import TemplateError from .exc import ExceptionFormatter from .compiler import Compiler from .config import DEBUG_MODE from .config import AUTO_RELOAD from .config import EAGER_PARSING from .config import CACHE_DIRECTORY from .loader import ModuleLoader from .loader import MemoryLoader from .nodes import Module from .utils import DebuggingOutputStream from .utils import Scope from .utils import join from .utils import mangle from .utils import derive_formatted_exception from .utils import read_bytes from .utils import raise_with_traceback from .utils import byte_string log = logging.getLogger('chameleon.template') def _make_module_loader(): if CACHE_DIRECTORY: path = CACHE_DIRECTORY else: path = tempfile.mkdtemp() @atexit.register def cleanup(path=path, shutil=shutil): shutil.rmtree(path) return ModuleLoader(path) class BaseTemplate(object): """Template base class. Takes a string input which must be one of the following: - a unicode string (or string on Python 3); - a utf-8 encoded byte string; - a byte string for an XML document that defines an encoding in the document premamble; - an HTML document that specifies the encoding via the META tag. Note that the template input is decoded, parsed and compiled on initialization. """ default_encoding = "utf-8" # This attribute is strictly informational in this template class # and is used in exception formatting. It may be set on # initialization using the optional ``filename`` keyword argument. filename = '' _cooked = False if DEBUG_MODE or CACHE_DIRECTORY: loader = _make_module_loader() else: loader = MemoryLoader() if DEBUG_MODE: output_stream_factory = DebuggingOutputStream else: output_stream_factory = list debug = DEBUG_MODE # The ``builtins`` dictionary can be used by a template class to # add symbols which may not be redefined and which are (cheaply) # available in the template variable scope builtins = {} # The ``builtins`` dictionary is updated with this dictionary at # cook time. Note that it can be provided at class initialization # using the ``extra_builtins`` keyword argument. extra_builtins = {} # Expression engine must be provided by subclass engine = None # When ``strict`` is set, expressions must be valid at compile # time. When not set, this is only required at evaluation time. strict = True def __init__(self, body, **config): self.__dict__.update(config) if isinstance(body, byte_string): body, encoding, content_type = read_bytes( body, self.default_encoding ) else: content_type = body.startswith('" % (self.__class__.__name__, self.filename) @property def keep_body(self): # By default, we only save the template body if we're # in debugging mode (to save memory). return self.__dict__.get('keep_body', DEBUG_MODE) @property def keep_source(self): # By default, we only save the generated source code if we're # in debugging mode (to save memory). return self.__dict__.get('keep_source', DEBUG_MODE) def cook(self, body): digest = self._digest(body) builtins_dict = self.builtins.copy() builtins_dict.update(self.extra_builtins) names, builtins = zip(*builtins_dict.items()) program = self._cook(body, digest, names) initialize = program['initialize'] functions = initialize(*builtins) for name, function in functions.items(): setattr(self, "_" + name, function) self._cooked = True if self.keep_body: self.body = body def cook_check(self): assert self._cooked def parse(self, body): raise NotImplementedError("Must be implemented by subclass.") def render(self, **__kw): econtext = Scope(__kw) rcontext = {} self.cook_check() stream = self.output_stream_factory() try: self._render(stream, econtext, rcontext) except: cls, exc, tb = sys.exc_info() errors = rcontext.get('__error__') if errors: try: exc = copy.copy(exc) except TypeError: name = type(exc).__name__ log.warn("Unable to copy exception of type '%s'." % name) else: formatter = ExceptionFormatter(errors, econtext, rcontext) exc = derive_formatted_exception(exc, cls, formatter) raise_with_traceback(exc, tb) raise return join(stream) def _get_module_name(self, digest): return "%s.py" % digest def _cook(self, body, digest, builtins): name = self._get_module_name(digest) cooked = self.loader.get(name) if cooked is None: try: source = self._make(body, builtins) if self.debug: source = "# template: %s\n#\n%s" % (self.filename, source) if self.keep_source: self.source = source cooked = self.loader.build(source, name) except TemplateError: exc = sys.exc_info()[1] exc.filename = self.filename raise elif self.keep_source: module = sys.modules.get(cooked.get('__name__')) if module is not None: self.source = inspect.getsource(module) else: self.source = None return cooked def _digest(self, body): class_name = type(self).__name__.encode('utf-8') sha = pkg_digest.copy() sha.update(body.encode('utf-8', 'ignore')) sha.update(class_name) return sha.hexdigest() def _compile(self, program, builtins): compiler = Compiler(self.engine, program, builtins, strict=self.strict) return compiler.code def _make(self, body, builtins): program = self.parse(body) module = Module("initialize", program) return self._compile(module, builtins) class BaseTemplateFile(BaseTemplate): """File-based template base class. Relative path names are supported only when a template loader is provided as the ``loader`` parameter. """ # Auto reload is not enabled by default because it's a significant # performance hit auto_reload = AUTO_RELOAD def __init__(self, filename, auto_reload=None, **config): # Normalize filename filename = os.path.abspath( os.path.normpath(os.path.expanduser(filename)) ) self.filename = filename # Override reload setting only if value is provided explicitly if auto_reload is not None: self.auto_reload = auto_reload self.__dict__.update(config) # This is only necessary if the ``debug`` flag was passed as a # keyword argument if config.get('debug') is True: self.loader = _make_module_loader() if EAGER_PARSING: self.cook_check() def cook_check(self): if self.auto_reload: mtime = self.mtime() if mtime != self._v_last_read: self._v_last_read = mtime self._cooked = False if self._cooked is False: body = self.read() self.cook(body) def mtime(self): try: return os.path.getmtime(self.filename) except (IOError, OSError): return 0 def read(self): with open(self.filename, "rb") as f: data = f.read() body, encoding, content_type = read_bytes( data, self.default_encoding ) # In non-XML mode, we support various platform-specific line # endings and convert them to the UNIX newline character if content_type != "text/xml" and '\r' in body: body = body.replace('\r\n', '\n').replace('\r', '\n') self.content_type = content_type self.content_encoding = encoding return body def _get_module_name(self, digest): filename = os.path.basename(self.filename) mangled = mangle(filename) return "%s_%s.py" % (mangled, digest) def _get_filename(self): return self.__dict__.get('filename') def _set_filename(self, filename): self.__dict__['filename'] = filename self._v_last_read = None self._cooked = False filename = property(_get_filename, _set_filename) Chameleon-2.6.1/src/chameleon/parser.py0000644000175000001440000001426111634407043020442 0ustar mborchusers00000000000000import re import logging try: from collections import OrderedDict except ImportError: from ordereddict import OrderedDict from .exc import ParseError from .namespaces import XML_NS from .tokenize import Token match_tag_prefix_and_name = re.compile( r'^(?P([^:\n ]+:)?[^ \n\t>/]+)' '(?P(?P\s*)/?>)?', re.UNICODE | re.DOTALL) match_single_attribute = re.compile( r'(?P\s+)(?!\d)' r'(?P[^ =/>\n\t]+)' r'((?P\s*=\s*)' r'((?P[\'"])(?P.*?)(?P=quote)|' r'(?P[^\s\'">/]+))|' r'(?P(?![ \\n\\t\\r]*=)))', re.UNICODE | re.DOTALL) match_comment = re.compile( r'^$', re.DOTALL) match_cdata = re.compile( r'^.*)\]>$', re.DOTALL) match_declaration = re.compile( r'^[^>]+)>$', re.DOTALL) match_processing_instruction = re.compile( r'^<\?(?P.*?)\?>', re.DOTALL) match_xml_declaration = re.compile(r'^<\?xml(?=[ /])', re.DOTALL) log = logging.getLogger('chameleon.parser') def substitute(regex, repl, token): if not isinstance(token, Token): token = Token(token) return Token( regex.sub(repl, token), token.pos, token.source, token.filename ) def groups(m, token): result = [] for i, group in enumerate(m.groups()): if group is not None: j, k = m.span(i + 1) group = token[j:k] result.append(group) return tuple(result) def groupdict(m, token): d = m.groupdict() for name, value in d.items(): if value is not None: i, j = m.span(name) d[name] = token[i:j] return d def match_tag(token, regex=match_tag_prefix_and_name): m = regex.match(token) d = groupdict(m, token) end = m.end() token = token[end:] attrs = d['attrs'] = [] for m in match_single_attribute.finditer(token): attr = groupdict(m, token) alt_value = attr.pop('alt_value', None) if alt_value is not None: attr['value'] = alt_value attr['quote'] = '' simple_value = attr.pop('simple_value', None) if simple_value is not None: attr['quote'] = '' attr['value'] = '' attr['eq'] = '' attrs.append(attr) d['suffix'] = token[m.end():] return d def parse_tag(token, namespace): node = match_tag(token) update_namespace(node['attrs'], namespace) if ':' in node['name']: prefix = node['name'].split(':')[0] else: prefix = None default = node['namespace'] = namespace.get(prefix, XML_NS) node['ns_attrs'] = unpack_attributes( node['attrs'], namespace, default) return node def update_namespace(attributes, namespace): # possibly update namespaces; we do this in a separate step # because this assignment is irrespective of order for attribute in attributes: name = attribute['name'] value = attribute['value'] if name == 'xmlns': namespace[None] = value elif name.startswith('xmlns:'): namespace[name[6:]] = value def unpack_attributes(attributes, namespace, default): namespaced = OrderedDict() for index, attribute in enumerate(attributes): name = attribute['name'] value = attribute['value'] if ':' in name: prefix = name.split(':')[0] name = name[len(prefix) + 1:] try: ns = namespace[prefix] except KeyError: raise KeyError( "Undefined namespace prefix: %s." % prefix) else: ns = default namespaced[ns, name] = value return namespaced def identify(string): if string.startswith("<"): if string.startswith(" Note that the initial question mark character (?) will be omitted from output. - The parser now accepts '<' and '>' in attributes. Note that this is invalid markup. Previously, the '<' would not be accepted as a valid attribute value, but this would result in an 'unexpected end tag' error elsewhere. This fixes issue #38. - The expression compiler now provides methods ``assign_text`` and ``assign_value`` such that a template engine might configure this value conversion to support e.g. encoded strings. Note that currently, the only client for the ``assign_text`` method is the string expression type. - Enable template loader for string-based template classes. Note that the ``filename`` keyword argument may be provided on initialization to identify the template source by filename. This fixes issue #36. - Added ``extra_builtins`` option to the page template class. These builtins are added to the default builtins dictionary at cook time and may be provided at initialization using the ``extra_builtins`` keyword argument. Bugfixes: - If a translation domain is set for a fill slot, use this setting instead of the macro template domain. - The Python expression compiler now correctly decodes HTML entities ``'gt'`` and ``'lt'``. This fixes issue #32. - The string expression compiler now correctly handles encoded text (when support for encoded strings is enabled). This fixes issue #35. - Fixed an issue where setting the ``filename`` attribute on a file-based template would not automatically cause an invalidation. - Exceptions raised by Chameleon can now be copied via ``copy.copy``. This fixes issue #36. [leorochael] - If copying the exception fails in the exception handler, simply re-raise the original exception and log a warning. 2.2 (2011-07-28) ---------------- Features: - Added new expression type ``load:`` that allows loading a template. Both relative and absolute paths are supported. If the path given is relative, then it will be resolved with respect to the directory of the template. - Added support for dynamic evaluation of expressions. Note that this is to support legacy applications. It is not currently wired into the provided template classes. - Template classes now have a ``builtins`` attribute which may be used to define built-in variables always available in the template variable scope. Incompatibilities: - The file-based template class no longer accepts a parameter ``loader``. This parameter would be used to load a template from a relative path, using a ``find(filename)`` method. This was however, undocumented, and probably not very useful since we have the ``TemplateLoader`` mechanism already. - The compiled template module now contains an ``initialize`` function which takes values that map to the template builtins. The return value of this function is a dictionary that contains the render functions. Bugfixes: - The file-based template class no longer verifies the existance of a template file (using ``os.lstat``). This now happens implicitly if eager parsing is enabled, or otherwise when first needed (e.g. at render time). This is classified as a bug fix because the previous behavior was probably not what you'd expect, especially if an application initializes a lot of templates without needing to render them immediately. 2.1.1 (2011-07-28) ------------------ Features: - Improved exception display. The expression string is now shown in the context of the original source (if available) with a marker string indicating the location of the expression in the template source. Bugfixes: - The ``structure`` insertion mode now correctly decodes entities for any expression type (including ``string:``). This fixes issue #30. - Don't show internal variables in the exception formatter variable listing. 2.1 (2011-07-25) ---------------- Features: - Expression interpolation (using the ``${...}`` operator and previously also ``$identifier``) now requires braces everywhere except inside the ``string:`` expression type. This change is motivated by a number of legacy templates in which the interpolation format without braces ``$identifier`` appears as text. 2.0.2 (2011-07-25) ------------------ Bugfixes: - Don't use dynamic variable scope for lambda-scoped variables (#27). - Avoid duplication of exception class and message in traceback. - Fixed issue where a ``metal:fill-slot`` would be ignored if a macro was set to be used on the same element (#16). 2.0.1 (2011-07-23) ------------------ Bugfixes: - Fixed issue where global variable definition from macro slots would fail (they would instead be local). This also affects error reporting from inside slots because this would be recorded internally as a global. - Fixed issue with template cache digest (used for filenames); modules are now invalidated whenever any changes are made to the distribution set available (packages on ``sys.path``). - Fixed exception handler to better let exceptions propagate through the renderer. - The disk-based module compiler now mangles template source filenames such that the output Python module is valid and at root level (dots and hyphens are replaced by an underscore). This fixes issue #17. - Fixed translations (i18n) on Python 2.5. 2.0 (2011-07-14) ---------------- - Point release. 2.0-rc14 (2011-07-13) --------------------- Bugfixes: - The tab character (``\t``) is now parsed correctly when used inside tags. Features: - The ``RepeatDict`` class now works as a proxy behind a seperate dictionary instance. - Added template constructor option ``keep_body`` which is a flag (also available as a class attribute) that controls whether to save the template body input in the ``body`` attribute. This is disabled by default, unless debug-mode is enabled. - The page template loader class now accepts an optional ``formats`` argument which can be used to select an alternative template class. 2.0-rc13 (2011-07-07) --------------------- Bugfixes: - The backslash character (followed by optional whitespace and a line break) was not correctly interpreted as a continuation for Python expressions. Features: - The Python expression implementation is now more flexible for external subclassing via a new ``parse`` method. 2.0-rc12 (2011-07-04) --------------------- Bugfixes: - Initial keyword arguments passed to a template now no longer "leak" into the template variable space after a macro call. - An unexpected end tag is now an unrecoverable error. Features: - Improve exception output. 2.0-rc11 (2011-05-26) --------------------- Bugfixes: - Fixed issue where variable names that begin with an underscore were seemingly allowed, but their use resulted in a compiler error. Features: - Template variable names are now allowed to be prefixed with a single underscore, but not two or more (reserved for internal use). Examples of valid names:: item ITEM _item camelCase underscore_delimited help - Added support for Genshi's comment "drop" syntax:: Note the additional exclamation (!) character. This fixes addresses issue #10. 2.0-rc10 (2011-05-24) --------------------- Bugfixes: - The ``tal:attributes`` statement now correctly operates case-insensitive. The attribute name given in the statement will replace an existing attribute with the same name, without respect to case. Features: - Added ``meta:interpolation`` statement to control expression interpolation setting. Strings that disable the setting: ``"off"`` and ``"false"``. Strings that enable the setting: ``"on"`` and ``"true"``. - Expression interpolation now works inside XML comments. 2.0-rc9 (2011-05-05) -------------------- Features: - Better debugging support for string decode and conversion. If a naive join fails, each element in the output will now be attempted coerced to unicode to try and trigger the failure near to the bad string. 2.0-rc8 (2011-04-11) -------------------- Bugfixes: - If a macro defines two slots with the same name, a caller will now fill both with a single usage. - If a valid of ``None`` is provided as the translation function argument, we now fall back to the class default. 2.0-rc7 (2011-03-29) -------------------- Bugfixes: - Fixed issue with Python 2.5 compatibility AST. This affected at least PyPy 1.4. Features: - The ``auto_reload`` setting now defaults to the class value; the base template class gives a default value of ``chameleon.config.AUTO_RELOAD``. This change allows a subclass to provide a custom default value (such as an application-specific debug mode setting). 2.0-rc6 (2011-03-19) -------------------- Features: - Added support for ``target_language`` keyword argument to render method. If provided, the argument will be curried onto the translation function. Bugfixes: - The HTML entities 'lt', 'gt' and 'quot' appearing inside content subtition expressions are now translated into their native character values. This fixes an issue where you could not dynamically create elements using the ``structure`` (which is possible in ZPT). The need to create such structure stems from the lack of an expression interpolation operator in ZPT. - Fixed duplicate file pointer issue with test suite (affected Windows platforms only). This fixes issue #9. [oliora] - Use already open file using ``os.fdopen`` when trying to write out the module source. This fixes LP #731803. 2.0-rc5 (2011-03-07) -------------------- Bugfixes: - Fixed a number of issues concerning the escaping of attribute values: 1) Static attribute values are now included as they appear in the source. This means that invalid attribute values such as ``"true && false"`` are now left alone. It's not the job of the template engine to correct such markup, at least not in the default mode of operation. 2) The string expression compiler no longer unescapes values. Instead, this is left to each expression compiler. Currently only the Python expression compiler unescapes its input. 3) The dynamic escape code sequence now correctly only replaces ampersands that are part of an HTML escape format. Imports: - The page template classes and the loader class can now be imported directly from the ``chameleon`` module. Features: - If a custom template loader is not provided, relative paths are now resolved using ``os.abspath`` (i.e. to the current working directory). - Absolute paths are normalized using ``os.path.normpath`` and ``os.path.expanduser``. This ensures that all paths are kept in their "canonical" form. 2.0-rc4 (2011-03-03) -------------------- Bugfixes: - Fixed an issue where the output of an end-to-end string expression would raise an exception if the expression evaluated to ``None`` (it should simply output nothing). - The ``convert`` function (which is configurable on the template class level) now defaults to the ``translate`` function (at run-time). This fixes an issue where message objects were not translated (and thus converted to a string) using the a provided ``translate`` function. - Fixed string interpolation issue where an expression immediately succeeded by a right curly bracket would not parse. This fixes issue #5. - Fixed error where ``tal:condition`` would be evaluated after ``tal:repeat``. Features: - Python expression is now a TALES expression. That means that the pipe operator can be used to chain two or more expressions in a try-except sequence. This behavior was ported from the 1.x series. Note that while it's still possible to use the pipe character ("|") in an expression, it must now be escaped. - The template cache can now be shared by multiple processes. 2.0-rc3 (2011-03-02) -------------------- Bugfixes: - Fixed ``atexit`` handler. This fixes issue #3. - If a cache directory is specified, it will now be used even when not in debug mode. - Allow "comment" attribute in the TAL namespace. This fixes an issue in the sense that the reference engine allows any attribute within the TAL namespace. However, only "comment" is in common use. - The template constructor now accepts a flag ``debug`` which puts the template *instance* into debug-mode regardless of the global setting. This fixes issue #1. Features: - Added exception handler for exceptions raised while evaluating an expression. This handler raises (or attempts to) a new exception of the type ``RenderError``, with an additional base class of the original exception class. The string value of the exception is a formatted error message which includes the expression that caused the exception. If we are unable to create the exception class, the original exception is re-raised. 2.0-rc2 (2011-02-28) -------------------- - Fixed upload issue. 2.0-rc1 (2011-02-28) -------------------- - Initial public release. See documentation for what's new in this series.