Chameleon-3.6.2/0000755000076500000240000000000013503370500014006 5ustar mborchstaff00000000000000Chameleon-3.6.2/LICENSE.txt0000644000076500000240000002046313131416610015636 0ustar mborchstaff00000000000000The 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-3.6.2/MANIFEST.in0000644000076500000240000000016313450341714015552 0ustar mborchstaff00000000000000recursive-include src/chameleon/tests/inputs * recursive-include src/chameleon/tests/outputs * include LICENSE.txt Chameleon-3.6.2/PKG-INFO0000644000076500000240000015323013503370500015107 0ustar mborchstaff00000000000000Metadata-Version: 1.1 Name: Chameleon Version: 3.6.2 Summary: Fast HTML/XML Template Compiler. Home-page: https://chameleon.readthedocs.io 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.7 and up, including 3.4+ and `pypy `_). Visit the `documentation `_ for more information. 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 ======= 3.6.2 (2019-06-22) ------------------ - Fix SyntaxWarnings in Python 3.8 resulting from comparing literals with 'is'. See https://github.com/plone/Products.CMFPlone/issues/2890. 3.6.1 (2019-04-01) ------------------ - Fix limited search expression for illegal double hyphens in HTML comments to fix issue #289. 3.6 (2019-02-19) ---------------- - Exclude `RuntimeError` (or `RecursionError` when available) from exception wrapping. - Fix double dollar '$$' escaping such that a double dollar is always resolved, either as an interpolation expression, or as an escape where it is substituted by a single dollar symbol. This is now consistent with Zope's handling of this character. Backslash-escaping of dollar-based string interpolation is no longer supported. The documentation has been updated to reflect this change. This fixes issue #283. Note that this reverses some of the changes introduced to fix issue #265. - Drop support for Python 3.3. 3.5 (2018-10-17) ---------------- - Add support for Python 3.8. - Add support for TAL attributes in an XML declaration tag. This fixes issue #269. - Add support for custom exception handling for the `tal:on-error` statement. There is now an option `on_error_handler` available as a template configuration (issue #266). - Fix issue where double '$$' escaping would affect non-interpolation expressions such as the bare '$$' (issue #265). - Fix an issue where backslash dollar escaping would leave the backslash character still in place. 3.4 (2018-07-14) ---------------- Bugfixes: - Fix regression with translations in case of multiple nodes. 3.3 (2018-05-23) ---------------- Bugfixes: - Reset error token when rendering internal macro calls. - Fix edge case in exception handler causing recursion. [MatthewWilkes] 3.2 (2017-10-06) ---------------- Features: - Add the automatic variable ``macroname`` that's bound to the name of the executing macro. Fixes https://github.com/malthe/chameleon/issues/238 - A tokenizer can now be configured on the template class. This is useful in the case where the template file input is modified before parsing (for example, where some tags are stripped away) such that token positions need to be offset accordingly for error locations to be rendered correctly. - Expression errors now display source marker (previously only filename, line and column was shown). - No longer require Python source files to import modules. [mrh1997] Optimizations: - Exception tracking now defers metadata allocation to time of error. 3.1 (2017-02-21) ---------------- Features: - Add option ``restricted_namespace`` which controls whether to restrict namespaces to those defined and used by the page template language. [hansroh] Bugs: - Fixed attribute HTML entity escaping issue where an entity such as ``&`` would be encoded twice. Optimizations: - Simplify exception tracking, reducing bytecode size significantly. - Avoid checking if a static string is ``None`` during expression interpolation. 3.0 (2016-12-07) ---------------- Bugs: - Fix issue on Python 2 where an exception was not cleared when using the pipe operator and was thus accessible through `sys.exc_info()`. - The "exists" expression no longer leaks error information. - Escape '$$' into '$' in both content and string expressions. - Fix use of macro definition inside translation block. Improvements: - Allow unquoted attribute values. - Wrap attribute error thrown when trying to use a non-macro as a macro as a `RenderError` to get proper error output. - Throw a parse error if '--' (double hyphen) appears in an XML comment. - The `i18n:target` attribute now overrides a default `target_language` variable and is passed to the translation function. - Include filename in the on-disk cache module name. Previously, only the SHA digest in hex representation would be used, making it difficult to see where the module came from. This fixes issue #132. - Add support for non-ascii attribute names. [sank] Compatibility: - Drop support for Python 2.6, 3.1, and 3.2. 2.25 (2016-09-24) ----------------- - Add explicit support / testing for Python 3.5. - Add ``\r`` to negative regex matches to the chameleon parser, where ``\n`` is used but ``\r`` was missing. Fixes a case, where the tag name was parsed into ``html\r`` instead of ``html``. Fixes: https://github.com/malthe/chameleon/issues/219 2.24 (2015-10-28) ----------------- - Fixed Python 3.5 compatibility. - Fixed brown bag release. 2.23 (2015-10-26) ----------------- - Added ``enable_data_attributes`` option that allows using HTML5 data attributes as control attributes instead or in addition to XML namespace attributes. 2.22 (2015-02-06) ----------------- - Fix brown bag release. 2.21 (2015-02-06) ----------------- - Added ``RenderError`` exception which indicates that an error occurred during the evaluation of an expression. - Clean up ``TemplateError`` exception implementation. 2.20 (2015-01-12) ----------------- - Pass ``search_path`` to template class when loaded using ``TemplateLoader`` (or one of the derived classes). [faassen] 2.19 (2015-01-06) ----------------- - Fix logging deprecation. - Fix environment-based configuration logging error. 2.18 (2014-11-03) ----------------- - Fix minor compilation error. 2.17 (2014-11-03) ----------------- - Add support for ``i18n:context``. [wiggy] - Add missing 'parity' repeat property. [voxspox] - Don't modify environment when getting variables from it. [fschulze] 2.16 (2014-05-06) ----------------- - If a repeat expression evaluates to ``None`` then it is now equivalent to an empty set. This changes a behavior introduced in 2.14. This fixes issue #172. - Remove fossil test dependency on deprecated ``distribute``. - Add explicit support / testing for Python 3.3 / 3.4. - Drop explicit support for Python 2.5 (out of maintenance, and no longer supported by ``tox`` or ``Travis-CI``). 2.15 (2014-03-11) ----------------- - Add Support for Python 3.4's ``NameConstant``. [brakhane] 2.14 (2013-11-28) ----------------- - Element repetition using the ``TAL`` namespace no longer includes whitespace. This fixes issue #110. - Use absolute import for ``chameleon.interfaces`` module. This fixes issue #161. 2.13-1 (2013-10-24) ------------------- - Fixing brown bag release. 2.13 (2013-10-21) ----------------- Bugfixes: - The template cache mechanism now includes additional configuration settings as part of the cache key such as ``strict`` and ``trim_attribute_space``. [ossmkitty] - Fix cache issue where sometimes cached templates would not load correctly. [ossmkitty] - In debug-mode, correctly remove temporary files when the module loader is garbage-collected (on ``__del__``). [graffic] - Fix error message when duplicate i18n:name directives are used in a translation. - Using the three-argument form of ``getattr`` on a ``chameleon.tal.RepeatDict`` no longer raises ``KeyError``, letting the default provided to ``getattr`` be used. This fixes attempting to adapt a ``RepeatDict`` to a Zope interface under PyPy. 2.12 (2013-03-26) ----------------- Changes: - When a ``tal:case`` condition succeeds, no other case now will. Bugfixes: - Implicit translation now correctly extracts and normalizes complete sentences, instead of words. [witsch] - The ``default`` symbol in a ``tal:case`` condition now allows the element only if no other case succeeds. 2.11 (2012-11-15) ----------------- Bugfixes: - An issue was resolved where a METAL statement was combined with a ``tal:on-error`` handler. - Fix minor parser issue with incorrectly formatted processing instructions. - Provide proper error handling for Python inline code blocks. Features: - The simple translation function now supports the ``translationstring`` interface. Optimizations: - Minor optimization which correctly detects when an element has no attributes. 2.10 (2012-10-12) ----------------- Deprecations: - The ``fast_translate`` function has been deprecated. Instead, the default translation function is now always a function that simply interpolates the mapping onto the message default or id. The motivation is that since version 2.9, the ``context`` argument is non-trivial: the ``econtext`` mapping is passed. This breaks an expectation on the Zope platform that the ``context`` parameter is the HTTP request. Previously, with Chameleon this parameter was simply not provided and so that did not cause issues as such. - The ``ast24`` module has been renamed to ``ast25``. This should help clear up any confusion that Chameleon 2.x might be support a Python interpreter less than version 2.5 (it does not). Features: - The ``ProxyExpr`` expression class (and hence the ``load:`` expression type) is now a TALES-expression. In practical terms, this means that the expression type (which computes a string result using the standard ``"${...}"`` interpolation syntax and proxies the result through a function) now supports fallback using the pipe operator (``"|"``). This fixes issue #128. - An attempt to interpolate using the empty string as the expression (i.e. ``${}``) now does nothing: the string ``${}`` is simply output as is. - Added support for adding, modifying, and removing attributes using a dictionary expression in ``tal:attributes`` (analogous to Genshi's ``py:attrs`` directive)::
In the example above, ``name`` is an identifier, while ``value`` and ``attrs`` are Python expressions. However, ``attrs`` must evaluate to a Python dictionary object (more concisely, the value must implement the dictionary API-methods ``update()`` and ``items()``). Optimizations: - In order to cut down on the size of the compiled function objects, some conversion and quoting statements have been put into functions. In one measurement, the reduction was 35%. The benchmark suite does *not* report of an increased render time (actually slightly decreased). Bugfixes: - An exception is now raised if a trivial string is passed for ``metal:fill-slot``. This fixes issue #89. - An empty string is now never translated. Not really a bug, but it's been reported in as an issue (#92) because some translation frameworks handle this case incorrectly. - The template module loader (file cache) now correctly encodes generated template source code as UTF-8. This fixes issue #125. - Fixed issue where a closure might be reused unsafely in nested template rendering. - Fixed markup class ``__repr__`` method. This fixes issue #124. - Added missing return statement to fix printing the non-abbreviated filename in case of an exception. [tomo] 2.9.2 (2012-06-06) ------------------ Bugfixes: - Fixed a PyPy incompatibility. - Fixed issue #109 which caused testing failures on some platforms. 2.9.1 (2012-06-01) ------------------ Bugfixes: - Fixed issue #103. The ``tal:on-error`` statement now always adds an explicit end-tag to the element, even with a substitution content of nothing. - Fixed issue #113. The ``tal:on-error`` statement now works correctly also for dynamic attributes. That is, the fallback tag now includes only static attributes. - Fixed name error which prevented the benchmark from running correctly. Compatibility: - Fixed deprecation warning on Python 3 for zope interface implements declaration. This fixes issue #116. 2.9.0 (2012-05-31) ------------------ Features: - The translation function now gets the ``econtext`` argument as the value for ``context``. Note that historically, this was usually an HTTP request which might provide language negotiation data through a dictionary interface. [alvinyue] Bugfixes: - Fixed import alias issue which would lead to a syntax error in generated Python code. Fixes issue #114. 2.8.5 (2012-05-02) ------------------ Bugfixes: - Fixed minor installation issues on Python 2.5 and 3. [ppaez] - Ensure output is unicode even when trivial (an empty string). 2.8.4 (2012-04-18) ------------------ Features: - In exception output, long filenames are now truncated to 60 characters of output, preventing line wrap which makes it difficult to scan the exception output. Bugfixes: - Include filename and location in exception output for exceptions raised during compilation. - If a trivial translation substitution variable is given (i.e. an empty string), simply ignore it. This fixes issue #106. 2.8.3 (2012-04-16) ------------------ Features: - Log template source on debug-level before cooking. - The `target_language` argument, if given, is now available as a variable in templates. 2.8.2 (2012-03-30) ------------------ Features: - Temporary caches used in debug mode are cleaned up eagerly, rather than waiting for process termination. [mitchellrj] Bugfixes: - The `index`, `start` and `end` methods on the TAL repeat object are now callable. This fixes an incompatibility with ZPT. - The loader now correctly handles absolute paths on Windows. [rdale] 2.8.1 (2012-03-29) ------------------ Features: - The exception formatter now lists errors in 'wrapping order'. This means that the innermost, and presumably most relevant exception is shown last. Bugfixes: - The exception formatter now correctly recognizes nested errors and does not rewrap the dynamically generated exception class. - The exception formatter now correctly sets the ``__module__`` attribute to that of the original exception class. 2.8.0 (2012-02-29) ------------------ Features: - Added support for code blocks using the `` processing instruction syntax. The scope is name assignments is up until the nearest macro definition, or the template itself if macros are not used. Bugfixes: - Fall back to the exception class' ``__new__`` method to safely create an exception object that is not implemented in Python. - The exception formatter now keeps track of already formatted exceptions, and ignores them from further output. 2.7.4 (2012-02-27) ------------------ - The error handler now invokes the ``__init__`` method of ``BaseException`` instead of the possibly overriden method (which may take required arguments). This fixes issue #97. [j23d, malthe] 2.7.3 (2012-01-16) ------------------ Bugfixes: - The trim whitespace option now correctly trims actual whitespace to a single character, appearing either to the left or to the right of an element prefix or suffix string. 2.7.2 (2012-01-08) ------------------ Features: - Added option ``trim_attribute_space`` that decides whether attribute whitespace is stripped (at most down to a single space). This option exists to provide compatibility with the reference implementation. Fixes issue #85. Bugfixes: - Ignore unhashable builtins when generating a reverse builtin map to quickly look up a builtin value. [malthe] - Apply translation mapping even when a translation function is not available. This fixes issue #83. [malthe] - Fixed issue #80. The translation domain for a slot is defined by the source document, i.e. the template providing the content for a slot whether it be the default or provided through ``metal:fill-slot``. [jcbrand] - In certain circumstances, a Unicode non-breaking space character would cause a define clause to fail to parse. 2.7.1 (2011-12-29) ------------------ Features: - Enable expression interpolation in CDATA. - The page template class now implements dictionary access to macros:: template[name] This is a short-hand for:: template.macros[name] Bugfixes: - An invalid define clause would be silently ignored; we now raise a language error exception. This fixes issue #79. - Fixed regression where ``${...}`` interpolation expressions could not span multiple lines. This fixes issue #77. 2.7.0 (2011-12-13) ------------------ Features: - The ``load:`` expression now derives from the string expression such that the ``${...}`` operator can be used for expression interpolation. - The ``load:`` expression now accepts asset specs; these are resolved by the ``pkg_resources.resource_filename`` function:: : An example from the test suite:: chameleon:tests/inputs/hello_world.pt Bugfixes: - If an attribute name for translation was not a valid Python identifier, the compiler would generate invalid code. This has been fixed, and the compiler now also throws an exception if an attribute specification contains a comma. (Note that the only valid separator character is the semicolon, when specifying attributes for translation via the ``i18n:translate`` statement). This addresses issue #76. 2.6.2 (2011-12-08) ------------------ Bugfixes: - Fixed issue where ``tal:on-error`` would not respect ``tal:omit-tag`` or namespace elements which are omitted by default (such as ````). - Fixed issue where ``macros`` attribute would not be available on file-based templates due to incorrect initialization. - The ``TryExcept`` and ``TryFinally`` AST nodes are not available on Python 3.3. These have been aliased to ``Try``. This fixes issue #75. Features: - The TAL repeat item now makes a security declaration that grants access to unprotected subobjects on the Zope 2 platform:: __allow_access_to_unprotected_subobjects__ = True This is required for legacy compatibility and does not affect other environments. - The template object now has a method ``write(body)`` which explicitly decodes and cooks a string input. - Added configuration option ``loader_class`` which sets the class used to create the template loader object. The class (essentially a callable) is created at template construction time. 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 :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Chameleon-3.6.2/README.rst0000644000076500000240000000120213426041304015471 0ustar mborchstaff00000000000000Overview ======== 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.7 and up, including 3.4+ and `pypy `_). Visit the `documentation `_ for more information. 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. Chameleon-3.6.2/setup.cfg0000644000076500000240000000004613503370500015627 0ustar mborchstaff00000000000000[egg_info] tag_build = tag_date = 0 Chameleon-3.6.2/setup.py0000644000076500000240000000402613503370273015531 0ustar mborchstaff00000000000000__version__ = '3.6.2' import os from setuptools import setup, find_packages from setuptools.command.test import test here = os.path.abspath(os.path.dirname(__file__)) try: README = open(os.path.join(here, 'README.rst')).read() CHANGES = open(os.path.join(here, 'CHANGES.rst')).read() except: # doesn't work under tox/pip README = '' CHANGES = '' install_requires = [] class Benchmark(test): description = "Run benchmarks" def finalize_options(self): self.distribution.tests_require = [ 'zope.pagetemplate', 'zope.component', 'zope.i18n', 'zope.testing'] test.finalize_options(self) def run_tests(self): from chameleon import benchmark print("running benchmark...") benchmark.start() setup( name="Chameleon", version=__version__, description="Fast HTML/XML Template Compiler.", long_description="\n\n".join((README, CHANGES)), classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ], author="Malthe Borch", author_email="mborch@gmail.com", url="https://chameleon.readthedocs.io", license='BSD-like (http://repoze.org/license.html)', packages=find_packages('src'), package_dir = {'': 'src'}, include_package_data=True, install_requires=install_requires, zip_safe=False, test_suite="chameleon.tests", cmdclass={ 'benchmark': Benchmark, } ) Chameleon-3.6.2/src/0000755000076500000240000000000013503370500014575 5ustar mborchstaff00000000000000Chameleon-3.6.2/src/Chameleon.egg-info/0000755000076500000240000000000013503370500020162 5ustar mborchstaff00000000000000Chameleon-3.6.2/src/Chameleon.egg-info/PKG-INFO0000644000076500000240000015323013503370477021300 0ustar mborchstaff00000000000000Metadata-Version: 1.1 Name: Chameleon Version: 3.6.2 Summary: Fast HTML/XML Template Compiler. Home-page: https://chameleon.readthedocs.io 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.7 and up, including 3.4+ and `pypy `_). Visit the `documentation `_ for more information. 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 ======= 3.6.2 (2019-06-22) ------------------ - Fix SyntaxWarnings in Python 3.8 resulting from comparing literals with 'is'. See https://github.com/plone/Products.CMFPlone/issues/2890. 3.6.1 (2019-04-01) ------------------ - Fix limited search expression for illegal double hyphens in HTML comments to fix issue #289. 3.6 (2019-02-19) ---------------- - Exclude `RuntimeError` (or `RecursionError` when available) from exception wrapping. - Fix double dollar '$$' escaping such that a double dollar is always resolved, either as an interpolation expression, or as an escape where it is substituted by a single dollar symbol. This is now consistent with Zope's handling of this character. Backslash-escaping of dollar-based string interpolation is no longer supported. The documentation has been updated to reflect this change. This fixes issue #283. Note that this reverses some of the changes introduced to fix issue #265. - Drop support for Python 3.3. 3.5 (2018-10-17) ---------------- - Add support for Python 3.8. - Add support for TAL attributes in an XML declaration tag. This fixes issue #269. - Add support for custom exception handling for the `tal:on-error` statement. There is now an option `on_error_handler` available as a template configuration (issue #266). - Fix issue where double '$$' escaping would affect non-interpolation expressions such as the bare '$$' (issue #265). - Fix an issue where backslash dollar escaping would leave the backslash character still in place. 3.4 (2018-07-14) ---------------- Bugfixes: - Fix regression with translations in case of multiple nodes. 3.3 (2018-05-23) ---------------- Bugfixes: - Reset error token when rendering internal macro calls. - Fix edge case in exception handler causing recursion. [MatthewWilkes] 3.2 (2017-10-06) ---------------- Features: - Add the automatic variable ``macroname`` that's bound to the name of the executing macro. Fixes https://github.com/malthe/chameleon/issues/238 - A tokenizer can now be configured on the template class. This is useful in the case where the template file input is modified before parsing (for example, where some tags are stripped away) such that token positions need to be offset accordingly for error locations to be rendered correctly. - Expression errors now display source marker (previously only filename, line and column was shown). - No longer require Python source files to import modules. [mrh1997] Optimizations: - Exception tracking now defers metadata allocation to time of error. 3.1 (2017-02-21) ---------------- Features: - Add option ``restricted_namespace`` which controls whether to restrict namespaces to those defined and used by the page template language. [hansroh] Bugs: - Fixed attribute HTML entity escaping issue where an entity such as ``&`` would be encoded twice. Optimizations: - Simplify exception tracking, reducing bytecode size significantly. - Avoid checking if a static string is ``None`` during expression interpolation. 3.0 (2016-12-07) ---------------- Bugs: - Fix issue on Python 2 where an exception was not cleared when using the pipe operator and was thus accessible through `sys.exc_info()`. - The "exists" expression no longer leaks error information. - Escape '$$' into '$' in both content and string expressions. - Fix use of macro definition inside translation block. Improvements: - Allow unquoted attribute values. - Wrap attribute error thrown when trying to use a non-macro as a macro as a `RenderError` to get proper error output. - Throw a parse error if '--' (double hyphen) appears in an XML comment. - The `i18n:target` attribute now overrides a default `target_language` variable and is passed to the translation function. - Include filename in the on-disk cache module name. Previously, only the SHA digest in hex representation would be used, making it difficult to see where the module came from. This fixes issue #132. - Add support for non-ascii attribute names. [sank] Compatibility: - Drop support for Python 2.6, 3.1, and 3.2. 2.25 (2016-09-24) ----------------- - Add explicit support / testing for Python 3.5. - Add ``\r`` to negative regex matches to the chameleon parser, where ``\n`` is used but ``\r`` was missing. Fixes a case, where the tag name was parsed into ``html\r`` instead of ``html``. Fixes: https://github.com/malthe/chameleon/issues/219 2.24 (2015-10-28) ----------------- - Fixed Python 3.5 compatibility. - Fixed brown bag release. 2.23 (2015-10-26) ----------------- - Added ``enable_data_attributes`` option that allows using HTML5 data attributes as control attributes instead or in addition to XML namespace attributes. 2.22 (2015-02-06) ----------------- - Fix brown bag release. 2.21 (2015-02-06) ----------------- - Added ``RenderError`` exception which indicates that an error occurred during the evaluation of an expression. - Clean up ``TemplateError`` exception implementation. 2.20 (2015-01-12) ----------------- - Pass ``search_path`` to template class when loaded using ``TemplateLoader`` (or one of the derived classes). [faassen] 2.19 (2015-01-06) ----------------- - Fix logging deprecation. - Fix environment-based configuration logging error. 2.18 (2014-11-03) ----------------- - Fix minor compilation error. 2.17 (2014-11-03) ----------------- - Add support for ``i18n:context``. [wiggy] - Add missing 'parity' repeat property. [voxspox] - Don't modify environment when getting variables from it. [fschulze] 2.16 (2014-05-06) ----------------- - If a repeat expression evaluates to ``None`` then it is now equivalent to an empty set. This changes a behavior introduced in 2.14. This fixes issue #172. - Remove fossil test dependency on deprecated ``distribute``. - Add explicit support / testing for Python 3.3 / 3.4. - Drop explicit support for Python 2.5 (out of maintenance, and no longer supported by ``tox`` or ``Travis-CI``). 2.15 (2014-03-11) ----------------- - Add Support for Python 3.4's ``NameConstant``. [brakhane] 2.14 (2013-11-28) ----------------- - Element repetition using the ``TAL`` namespace no longer includes whitespace. This fixes issue #110. - Use absolute import for ``chameleon.interfaces`` module. This fixes issue #161. 2.13-1 (2013-10-24) ------------------- - Fixing brown bag release. 2.13 (2013-10-21) ----------------- Bugfixes: - The template cache mechanism now includes additional configuration settings as part of the cache key such as ``strict`` and ``trim_attribute_space``. [ossmkitty] - Fix cache issue where sometimes cached templates would not load correctly. [ossmkitty] - In debug-mode, correctly remove temporary files when the module loader is garbage-collected (on ``__del__``). [graffic] - Fix error message when duplicate i18n:name directives are used in a translation. - Using the three-argument form of ``getattr`` on a ``chameleon.tal.RepeatDict`` no longer raises ``KeyError``, letting the default provided to ``getattr`` be used. This fixes attempting to adapt a ``RepeatDict`` to a Zope interface under PyPy. 2.12 (2013-03-26) ----------------- Changes: - When a ``tal:case`` condition succeeds, no other case now will. Bugfixes: - Implicit translation now correctly extracts and normalizes complete sentences, instead of words. [witsch] - The ``default`` symbol in a ``tal:case`` condition now allows the element only if no other case succeeds. 2.11 (2012-11-15) ----------------- Bugfixes: - An issue was resolved where a METAL statement was combined with a ``tal:on-error`` handler. - Fix minor parser issue with incorrectly formatted processing instructions. - Provide proper error handling for Python inline code blocks. Features: - The simple translation function now supports the ``translationstring`` interface. Optimizations: - Minor optimization which correctly detects when an element has no attributes. 2.10 (2012-10-12) ----------------- Deprecations: - The ``fast_translate`` function has been deprecated. Instead, the default translation function is now always a function that simply interpolates the mapping onto the message default or id. The motivation is that since version 2.9, the ``context`` argument is non-trivial: the ``econtext`` mapping is passed. This breaks an expectation on the Zope platform that the ``context`` parameter is the HTTP request. Previously, with Chameleon this parameter was simply not provided and so that did not cause issues as such. - The ``ast24`` module has been renamed to ``ast25``. This should help clear up any confusion that Chameleon 2.x might be support a Python interpreter less than version 2.5 (it does not). Features: - The ``ProxyExpr`` expression class (and hence the ``load:`` expression type) is now a TALES-expression. In practical terms, this means that the expression type (which computes a string result using the standard ``"${...}"`` interpolation syntax and proxies the result through a function) now supports fallback using the pipe operator (``"|"``). This fixes issue #128. - An attempt to interpolate using the empty string as the expression (i.e. ``${}``) now does nothing: the string ``${}`` is simply output as is. - Added support for adding, modifying, and removing attributes using a dictionary expression in ``tal:attributes`` (analogous to Genshi's ``py:attrs`` directive)::
In the example above, ``name`` is an identifier, while ``value`` and ``attrs`` are Python expressions. However, ``attrs`` must evaluate to a Python dictionary object (more concisely, the value must implement the dictionary API-methods ``update()`` and ``items()``). Optimizations: - In order to cut down on the size of the compiled function objects, some conversion and quoting statements have been put into functions. In one measurement, the reduction was 35%. The benchmark suite does *not* report of an increased render time (actually slightly decreased). Bugfixes: - An exception is now raised if a trivial string is passed for ``metal:fill-slot``. This fixes issue #89. - An empty string is now never translated. Not really a bug, but it's been reported in as an issue (#92) because some translation frameworks handle this case incorrectly. - The template module loader (file cache) now correctly encodes generated template source code as UTF-8. This fixes issue #125. - Fixed issue where a closure might be reused unsafely in nested template rendering. - Fixed markup class ``__repr__`` method. This fixes issue #124. - Added missing return statement to fix printing the non-abbreviated filename in case of an exception. [tomo] 2.9.2 (2012-06-06) ------------------ Bugfixes: - Fixed a PyPy incompatibility. - Fixed issue #109 which caused testing failures on some platforms. 2.9.1 (2012-06-01) ------------------ Bugfixes: - Fixed issue #103. The ``tal:on-error`` statement now always adds an explicit end-tag to the element, even with a substitution content of nothing. - Fixed issue #113. The ``tal:on-error`` statement now works correctly also for dynamic attributes. That is, the fallback tag now includes only static attributes. - Fixed name error which prevented the benchmark from running correctly. Compatibility: - Fixed deprecation warning on Python 3 for zope interface implements declaration. This fixes issue #116. 2.9.0 (2012-05-31) ------------------ Features: - The translation function now gets the ``econtext`` argument as the value for ``context``. Note that historically, this was usually an HTTP request which might provide language negotiation data through a dictionary interface. [alvinyue] Bugfixes: - Fixed import alias issue which would lead to a syntax error in generated Python code. Fixes issue #114. 2.8.5 (2012-05-02) ------------------ Bugfixes: - Fixed minor installation issues on Python 2.5 and 3. [ppaez] - Ensure output is unicode even when trivial (an empty string). 2.8.4 (2012-04-18) ------------------ Features: - In exception output, long filenames are now truncated to 60 characters of output, preventing line wrap which makes it difficult to scan the exception output. Bugfixes: - Include filename and location in exception output for exceptions raised during compilation. - If a trivial translation substitution variable is given (i.e. an empty string), simply ignore it. This fixes issue #106. 2.8.3 (2012-04-16) ------------------ Features: - Log template source on debug-level before cooking. - The `target_language` argument, if given, is now available as a variable in templates. 2.8.2 (2012-03-30) ------------------ Features: - Temporary caches used in debug mode are cleaned up eagerly, rather than waiting for process termination. [mitchellrj] Bugfixes: - The `index`, `start` and `end` methods on the TAL repeat object are now callable. This fixes an incompatibility with ZPT. - The loader now correctly handles absolute paths on Windows. [rdale] 2.8.1 (2012-03-29) ------------------ Features: - The exception formatter now lists errors in 'wrapping order'. This means that the innermost, and presumably most relevant exception is shown last. Bugfixes: - The exception formatter now correctly recognizes nested errors and does not rewrap the dynamically generated exception class. - The exception formatter now correctly sets the ``__module__`` attribute to that of the original exception class. 2.8.0 (2012-02-29) ------------------ Features: - Added support for code blocks using the `` processing instruction syntax. The scope is name assignments is up until the nearest macro definition, or the template itself if macros are not used. Bugfixes: - Fall back to the exception class' ``__new__`` method to safely create an exception object that is not implemented in Python. - The exception formatter now keeps track of already formatted exceptions, and ignores them from further output. 2.7.4 (2012-02-27) ------------------ - The error handler now invokes the ``__init__`` method of ``BaseException`` instead of the possibly overriden method (which may take required arguments). This fixes issue #97. [j23d, malthe] 2.7.3 (2012-01-16) ------------------ Bugfixes: - The trim whitespace option now correctly trims actual whitespace to a single character, appearing either to the left or to the right of an element prefix or suffix string. 2.7.2 (2012-01-08) ------------------ Features: - Added option ``trim_attribute_space`` that decides whether attribute whitespace is stripped (at most down to a single space). This option exists to provide compatibility with the reference implementation. Fixes issue #85. Bugfixes: - Ignore unhashable builtins when generating a reverse builtin map to quickly look up a builtin value. [malthe] - Apply translation mapping even when a translation function is not available. This fixes issue #83. [malthe] - Fixed issue #80. The translation domain for a slot is defined by the source document, i.e. the template providing the content for a slot whether it be the default or provided through ``metal:fill-slot``. [jcbrand] - In certain circumstances, a Unicode non-breaking space character would cause a define clause to fail to parse. 2.7.1 (2011-12-29) ------------------ Features: - Enable expression interpolation in CDATA. - The page template class now implements dictionary access to macros:: template[name] This is a short-hand for:: template.macros[name] Bugfixes: - An invalid define clause would be silently ignored; we now raise a language error exception. This fixes issue #79. - Fixed regression where ``${...}`` interpolation expressions could not span multiple lines. This fixes issue #77. 2.7.0 (2011-12-13) ------------------ Features: - The ``load:`` expression now derives from the string expression such that the ``${...}`` operator can be used for expression interpolation. - The ``load:`` expression now accepts asset specs; these are resolved by the ``pkg_resources.resource_filename`` function:: : An example from the test suite:: chameleon:tests/inputs/hello_world.pt Bugfixes: - If an attribute name for translation was not a valid Python identifier, the compiler would generate invalid code. This has been fixed, and the compiler now also throws an exception if an attribute specification contains a comma. (Note that the only valid separator character is the semicolon, when specifying attributes for translation via the ``i18n:translate`` statement). This addresses issue #76. 2.6.2 (2011-12-08) ------------------ Bugfixes: - Fixed issue where ``tal:on-error`` would not respect ``tal:omit-tag`` or namespace elements which are omitted by default (such as ````). - Fixed issue where ``macros`` attribute would not be available on file-based templates due to incorrect initialization. - The ``TryExcept`` and ``TryFinally`` AST nodes are not available on Python 3.3. These have been aliased to ``Try``. This fixes issue #75. Features: - The TAL repeat item now makes a security declaration that grants access to unprotected subobjects on the Zope 2 platform:: __allow_access_to_unprotected_subobjects__ = True This is required for legacy compatibility and does not affect other environments. - The template object now has a method ``write(body)`` which explicitly decodes and cooks a string input. - Added configuration option ``loader_class`` which sets the class used to create the template loader object. The class (essentially a callable) is created at template construction time. 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 :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Chameleon-3.6.2/src/Chameleon.egg-info/SOURCES.txt0000644000076500000240000003600113503370477022063 0ustar mborchstaff00000000000000LICENSE.txt MANIFEST.in README.rst setup.py 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/ast25.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_exc.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-load-spec.pt src/chameleon/tests/inputs/081.xml src/chameleon/tests/inputs/082-load-spec-computed.pt src/chameleon/tests/inputs/082.xml src/chameleon/tests/inputs/083-template-dict-to-macro.pt src/chameleon/tests/inputs/083.xml src/chameleon/tests/inputs/084-interpolation-in-cdata.pt src/chameleon/tests/inputs/084.xml src/chameleon/tests/inputs/085-nested-translation.pt src/chameleon/tests/inputs/085.xml src/chameleon/tests/inputs/086-self-closing.pt src/chameleon/tests/inputs/086.xml src/chameleon/tests/inputs/087-code-blocks.pt src/chameleon/tests/inputs/087.xml src/chameleon/tests/inputs/088-python-newlines.pt src/chameleon/tests/inputs/088.xml src/chameleon/tests/inputs/089-load-fallback.pt src/chameleon/tests/inputs/089.xml src/chameleon/tests/inputs/090-tuple-expression.pt src/chameleon/tests/inputs/090.xml src/chameleon/tests/inputs/091-repeat-none.pt 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/120-translation-context.pt src/chameleon/tests/inputs/121-translation-comment.pt src/chameleon/tests/inputs/122-translation-ignore.pt src/chameleon/tests/inputs/123-html5-data-attributes.pt src/chameleon/tests/inputs/124-translation-target.pt src/chameleon/tests/inputs/125-macro-translation-ordering.pt src/chameleon/tests/inputs/126-define-escaping.pt src/chameleon/tests/inputs/238-macroname.pt src/chameleon/tests/inputs/greeting.pt src/chameleon/tests/inputs/hello_world.pt src/chameleon/tests/inputs/hello_world.txt src/chameleon/tests/inputs/multinode-implicit-i18n.pt 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/081.pt src/chameleon/tests/outputs/082.pt src/chameleon/tests/outputs/083.pt src/chameleon/tests/outputs/084.pt src/chameleon/tests/outputs/085-en.pt src/chameleon/tests/outputs/085.pt src/chameleon/tests/outputs/086.pt src/chameleon/tests/outputs/087.pt src/chameleon/tests/outputs/088.pt src/chameleon/tests/outputs/089.pt src/chameleon/tests/outputs/090.pt src/chameleon/tests/outputs/091.pt src/chameleon/tests/outputs/101.html src/chameleon/tests/outputs/102.html src/chameleon/tests/outputs/103.html src/chameleon/tests/outputs/120-en.pt src/chameleon/tests/outputs/120.pt src/chameleon/tests/outputs/121.pt src/chameleon/tests/outputs/122.pt src/chameleon/tests/outputs/123.pt src/chameleon/tests/outputs/124-en.pt src/chameleon/tests/outputs/124.pt src/chameleon/tests/outputs/125.pt src/chameleon/tests/outputs/126.pt src/chameleon/tests/outputs/238.pt src/chameleon/tests/outputs/greeting.pt src/chameleon/tests/outputs/hello_world.pt src/chameleon/tests/outputs/hello_world.txt src/chameleon/tests/outputs/multinode-en.pt src/chameleon/tests/outputs/multinode.pt src/chameleon/zpt/__init__.py src/chameleon/zpt/loader.py src/chameleon/zpt/program.py src/chameleon/zpt/template.pyChameleon-3.6.2/src/Chameleon.egg-info/dependency_links.txt0000644000076500000240000000000113503370477024245 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/Chameleon.egg-info/not-zip-safe0000644000076500000240000000000113131416735022420 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/Chameleon.egg-info/top_level.txt0000644000076500000240000000001213503370477022722 0ustar mborchstaff00000000000000chameleon Chameleon-3.6.2/src/chameleon/0000755000076500000240000000000013503370500016530 5ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/__init__.py0000644000076500000240000000041013131416610020634 0ustar mborchstaff00000000000000from .zpt.template import PageTemplate from .zpt.template import PageTemplateFile from .zpt.template import PageTextTemplate from .zpt.template import PageTextTemplateFile from .zpt.loader import TemplateLoader as PageTemplateLoader from .exc import TemplateError Chameleon-3.6.2/src/chameleon/ast25.py0000644000076500000240000001023713131416610020043 0ustar mborchstaff00000000000000# -*- coding: utf-8 -*- # # Copyright 2008 by Armin Ronacher. # License: Python License. # import _ast from _ast import * def fix_missing_locations(node): """ When you compile a node tree with compile(), the compiler expects lineno and col_offset attributes for every node that supports them. This is rather tedious to fill in for generated nodes, so this helper adds these attributes recursively where not already set, by setting them to the values of the parent node. It works recursively starting at *node*. """ def _fix(node, lineno, col_offset): if 'lineno' in node._attributes: if not hasattr(node, 'lineno'): node.lineno = lineno else: lineno = node.lineno if 'col_offset' in node._attributes: if not hasattr(node, 'col_offset'): node.col_offset = col_offset else: col_offset = node.col_offset for child in iter_child_nodes(node): _fix(child, lineno, col_offset) _fix(node, 1, 0) return node def iter_child_nodes(node): """ Yield all direct child nodes of *node*, that is, all fields that are nodes and all items of fields that are lists of nodes. """ for name, field in iter_fields(node): if isinstance(field, (AST, _ast.AST)): yield field elif isinstance(field, list): for item in field: if isinstance(item, (AST, _ast.AST)): yield item def iter_fields(node): """ Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields`` that is present on *node*. """ for field in node._fields or (): try: yield field, getattr(node, field) except AttributeError: pass def walk(node): """ Recursively yield all child nodes of *node*, in no specified order. This is useful if you only want to modify nodes in place and don't care about the context. """ from collections import deque todo = deque([node]) while todo: node = todo.popleft() todo.extend(iter_child_nodes(node)) yield node class NodeVisitor(object): """ A node visitor base class that walks the abstract syntax tree and calls a visitor function for every node found. This function may return a value which is forwarded by the `visit` method. This class is meant to be subclassed, with the subclass adding visitor methods. Per default the visitor functions for the nodes are ``'visit_'`` + class name of the node. So a `TryFinally` node visit function would be `visit_TryFinally`. This behavior can be changed by overriding the `visit` method. If no visitor function exists for a node (return value `None`) the `generic_visit` visitor is used instead. Don't use the `NodeVisitor` if you want to apply changes to nodes during traversing. For this a special visitor exists (`NodeTransformer`) that allows modifications. """ def visit(self, node): """Visit a node.""" method = 'visit_' + node.__class__.__name__ visitor = getattr(self, method, self.generic_visit) return visitor(node) def generic_visit(self, node): """Called if no explicit visitor function exists for a node.""" for field, value in iter_fields(node): if isinstance(value, list): for item in value: if isinstance(item, (AST, _ast.AST)): self.visit(item) elif isinstance(value, (AST, _ast.AST)): self.visit(value) class AST(object): _fields = () _attributes = 'lineno', 'col_offset' def __init__(self, *args, **kwargs): self.__dict__.update(kwargs) self._fields = self._fields or () for name, value in zip(self._fields, args): setattr(self, name, value) for name, cls in _ast.__dict__.items(): if isinstance(cls, type) and issubclass(cls, _ast.AST): try: cls.__bases__ = (AST, ) + cls.__bases__ except TypeError: pass class ExceptHandler(AST): _fields = "type", "name", "body" Chameleon-3.6.2/src/chameleon/astutil.py0000644000076500000240000007113013361677000020600 0ustar mborchstaff00000000000000# -*- 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 ast25 as ast import sys import logging import weakref import collections 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 iter_fields(node): """ Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields`` that is present on *node*. """ for field in node._fields: try: yield field, getattr(node, field) except AttributeError: pass def iter_child_nodes(node): """ Yield all direct child nodes of *node*, that is, all fields that are nodes and all items of fields that are lists of nodes. """ for name, field in iter_fields(node): if isinstance(field, Node): yield field elif isinstance(field, list): for item in field: if isinstance(item, Node): yield item def walk(node): """ Recursively yield all descendant nodes in the tree starting at *node* (including *node* itself), in no specified order. This is useful if you only want to modify nodes in place and don't care about the context. """ todo = collections.deque([node]) while todo: node = todo.popleft() todo.extend(iter_child_nodes(node)) yield node 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) ) def extract(self, condition): result = [] for node in walk(self): if condition(node): result.append(node) return result 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 TokenRef(Node): """Represents a source-code token reference.""" _fields = "pos", "length" 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) def visit_arg(self, node): self._write(node.arg) # 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) # Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody) def visit_Try(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) 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) # 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) if keyword.arg is not None: self._write(keyword.arg) self._write('=') else: self._write('**') self.visit(keyword.value) # Attribute removed in Python 3.5 if getattr(node, 'starargs', None): if not first: self._write(', ') first = False self._write('*') self.visit(node.starargs) # Attribute removed in Python 3.5 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('`') # Constant(object value) def visit_Constant(self, node): if node.value is Ellipsis: self._write('...') else: self._write(repr(node.value)) # 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)) def visit_Ellipsis(self, node): self._write('...') # 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('[') if isinstance(node.slice, ast.Tuple) and node.slice.elts: self.visit(node.slice.elts[0]) if len(node.slice.elts) == 1: self._write(', ') else: for dim in node.slice.elts[1:]: self._write(', ') self.visit(dim) else: self.visit(node.slice) self._write(']') # Slice(expr? lower, expr? upper, expr? step) def visit_Slice(self, node): if getattr(node, 'lower', None) is not None: self.visit(node.lower) self._write(':') if getattr(node, 'upper', None) is not None: self.visit(node.upper) if getattr(node, 'step', None) is not None: self._write(':') self.visit(node.step) # Index(expr value) def visit_Index(self, node): self.visit(node.value) # ExtSlice(slice* dims) def visit_ExtSlice(self, node): self.visit(node.dims[0]) if len(node.dims) == 1: self._write(', ') else: for dim in node.dims[1:]: self._write(', ') self.visit(dim) # Starred(expr value, expr_context ctx) def visit_Starred(self, node): self._write('*') self.visit(node.value) # 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(')') # NameConstant(singleton value) def visit_NameConstant(self, node): self._write(str(node.value)) 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_arg(self, node): scope = self.scopes[-1] scope.add(node.arg) 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_FunctionDef(self, node): self.scopes[-1].add(node.name) def visit_alias(self, node): name = node.asname if node.asname is not None else node.name self.scopes[-1].add(name) 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-3.6.2/src/chameleon/benchmark.py0000644000076500000240000003544213131416610021044 0ustar mborchstaff00000000000000import unittest import time import os import re from .utils import text_ re_amp = re.compile(r'&(?!([A-Za-z]+|#[0-9]+);)') BIGTABLE_ZPT = """\
""" MANY_STRINGS_ZPT = """\
""" HELLO_WORLD_ZPT = """\

Hello, world!

""" I18N_ZPT = """\
Hello world!
Hello world!
Hello world!
""" def benchmark(title): def decorator(f): def wrapper(*args): print( "==========================\n " \ "%s\n==========================" % \ title) return f(*args) return wrapper return decorator def timing(func, *args, **kwargs): t1 = t2 = time.time() i = 0 while t2 - t1 < 3: func(**kwargs) func(**kwargs) func(**kwargs) func(**kwargs) i += 4 t2 = time.time() return float(10 * (t2 - t1)) / i START = 0 END = 1 TAG = 2 def yield_tokens(table=None): index = [] tag = index.append _re_amp = re_amp tag(START) yield "<", "html", "", ">\n" for r in table: tag(START) yield "<", "tr", "", ">\n" for c in r.values(): d = c + 1 tag(START) yield "<", "td", "", ">\n" _tmp5 = d if not isinstance(_tmp5, unicode): _tmp5 = str(_tmp5) if ('&' in _tmp5): if (';' in _tmp5): _tmp5 = _re_amp.sub('&', _tmp5) else: _tmp5 = _tmp5.replace('&', '&') if ('<' in _tmp5): _tmp5 = _tmp5.replace('<', '<') if ('>' in _tmp5): _tmp5 = _tmp5.replace('>', '>') if ('"' in _tmp5): _tmp5 = _tmp5.replace('"', '"') _tmp5 = "column-%s" % _tmp5 _tmp = d if (_tmp.__class__ not in (str, unicode, int, float, )): raise if (_tmp is not None): if not isinstance(_tmp, unicode): _tmp = str(_tmp) if ('&' in _tmp): if (';' in _tmp): _tmp = _re_amp.sub('&', _tmp) else: _tmp = _tmp.replace('&', '&') if ('<' in _tmp): _tmp = _tmp.replace('<', '<') if ('>' in _tmp): _tmp = _tmp.replace('>', '>') tag(START) t = ["classicism"] yield "<", "span", " ", t[0], '="', _tmp5, '"', ">\n" tag(END) yield "\n" tag(END) yield "\n" tag(END) yield "\n" tag(END) yield "\n" def yield_tokens_dict_version(**kwargs): index = [] tag = index.append _re_amp = re_amp tag(START) yield "<", "html", "", ">\n" for r in kwargs['table']: kwargs['r'] = r tag(START) yield "<", "tr", "", ">\n" for c in kwargs['r'].values(): kwargs['d'] = c + 1 tag(START) yield "<", "td", "", ">\n" _tmp5 = kwargs['d'] if not isinstance(_tmp5, unicode): _tmp5 = str(_tmp5) if ('&' in _tmp5): if (';' in _tmp5): _tmp5 = _re_amp.sub('&', _tmp5) else: _tmp5 = _tmp5.replace('&', '&') if ('<' in _tmp5): _tmp5 = _tmp5.replace('<', '<') if ('>' in _tmp5): _tmp5 = _tmp5.replace('>', '>') if ('"' in _tmp5): _tmp5 = _tmp5.replace('"', '"') _tmp5 = "column-%s" % _tmp5 _tmp = kwargs['d'] if (_tmp.__class__ not in (str, unicode, int, float, )): raise if (_tmp is not None): if not isinstance(_tmp, unicode): _tmp = str(_tmp) if ('&' in _tmp): if (';' in _tmp): _tmp = _re_amp.sub('&', _tmp) else: _tmp = _tmp.replace('&', '&') if ('<' in _tmp): _tmp = _tmp.replace('<', '<') if ('>' in _tmp): _tmp = _tmp.replace('>', '>') tag(START) t = ["classicism"] yield "<", "span", " ", t[0], '="', _tmp5, '"', ">\n" tag(END) yield "\n" tag(END) yield "\n" tag(END) yield "\n" tag(END) yield "\n" def yield_stream(table=None): _re_amp = re_amp yield START, ("html", "", "\n"), None for r in table: yield START, ("tr", "", "\n"), None for c in r.values(): d = c + 1 yield START, ("td", "", "\n"), None _tmp5 = d if not isinstance(_tmp5, unicode): _tmp5 = str(_tmp5) if ('&' in _tmp5): if (';' in _tmp5): _tmp5 = _re_amp.sub('&', _tmp5) else: _tmp5 = _tmp5.replace('&', '&') if ('<' in _tmp5): _tmp5 = _tmp5.replace('<', '<') if ('>' in _tmp5): _tmp5 = _tmp5.replace('>', '>') if ('"' in _tmp5): _tmp5 = _tmp5.replace('"', '"') _tmp5 = "column-%s" % _tmp5 _tmp = d if (_tmp.__class__ not in (str, unicode, int, float, )): raise if (_tmp is not None): if not isinstance(_tmp, unicode): _tmp = str(_tmp) if ('&' in _tmp): if (';' in _tmp): _tmp = _re_amp.sub('&', _tmp) else: _tmp = _tmp.replace('&', '&') if ('<' in _tmp): _tmp = _tmp.replace('<', '<') if ('>' in _tmp): _tmp = _tmp.replace('>', '>') yield START, ("span", "", _tmp, " ", "class", _tmp5), None yield END, ("span", "", "\n"), None yield END, ("td", "", "\n"), None yield END, ("tr", "", "\n"), None yield END, ("html", "", "\n"), None from itertools import chain def bigtable_python_tokens(table=None, renderer=None): iterable = renderer(table=table) stream = chain(*iterable) return "".join(stream) def bigtable_python_stream(table=None, renderer=None): stream = renderer(table=table) return "".join(stream_output(stream)) def bigtable_python_stream_with_filter(table=None, renderer=None): stream = renderer(table=table) return "".join(stream_output(uppercase_filter(stream))) def uppercase_filter(stream): for kind, data, pos in stream: if kind is START: data = (data[0], data[1], data[2].upper(),) + data[3:] elif kind is END: data = (data[0], data[1], data[2].upper()) elif kind is TAG: raise NotImplemented yield kind, data, pos def stream_output(stream): for kind, data, pos in stream: if kind is START: tag = data[0] yield "<%s" % tag l = len(data) # optimize for common cases if l == 3: pass elif l == 6: yield '%s%s="%s"' % (data[3], data[4], data[5]) else: i = 3 while i < l: yield '%s%s="%s"' % (data[i], data[i + 1], data[i + 2]) i += 3 yield "%s>%s" % (data[1], data[2]) elif kind is END: yield "%s" % data elif kind is TAG: raise NotImplemented class Benchmarks(unittest.TestCase): table = [dict(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10) \ for x in range(1000)] def setUp(self): # set up i18n component from zope.i18n import translate from zope.i18n.interfaces import INegotiator from zope.i18n.interfaces import ITranslationDomain from zope.i18n.negotiator import Negotiator from zope.i18n.simpletranslationdomain import SimpleTranslationDomain from zope.i18n.tests.test_negotiator import Env from zope.tales.tales import Context self.env = Env(('klingon', 'da', 'en', 'fr', 'no')) class ZopeI18NContext(Context): def translate(self, msgid, domain=None, context=None, mapping=None, default=None): context = self.vars['options']['env'] return translate(msgid, domain, mapping, context=context, default=default) def _getContext(self, contexts=None, **kwcontexts): if contexts is not None: if kwcontexts: kwcontexts.update(contexts) else: kwcontexts = contexts return ZopeI18NContext(self, kwcontexts) def _pt_getEngineContext(namespace): self = namespace['template'] engine = self.pt_getEngine() return _getContext(engine, namespace) import zope.component zope.component.provideUtility(Negotiator(), INegotiator) catalog = SimpleTranslationDomain('domain') zope.component.provideUtility(catalog, ITranslationDomain, 'domain') self.files = os.path.abspath(os.path.join(__file__, '..', 'input')) @staticmethod def _chameleon(body, **kwargs): from .zpt.template import PageTemplate return PageTemplate(body, **kwargs) @staticmethod def _zope(body): from zope.pagetemplate.pagetemplatefile import PageTemplate template = PageTemplate() template.pt_edit(body, 'text/xhtml') return template @benchmark(text_("BIGTABLE [python]")) def test_bigtable(self): options = {'table': self.table} t_chameleon = timing(self._chameleon(BIGTABLE_ZPT), options=options) print("chameleon: %7.2f" % t_chameleon) t_chameleon_utf8 = timing( self._chameleon(BIGTABLE_ZPT, encoding='utf-8'), options=options) print("chameleon (utf-8): %7.2f" % t_chameleon_utf8) t_tokens = timing( bigtable_python_tokens, table=self.table, renderer=yield_tokens) print("token: %7.2f" % t_tokens) t_tokens_dict_version = timing( bigtable_python_tokens, table=self.table, renderer=yield_tokens_dict_version) print("token (dict): %7.2f" % t_tokens_dict_version) t_stream = timing( bigtable_python_stream, table=self.table, renderer=yield_stream) print("stream: %7.2f" % t_stream) t_zope = timing(self._zope(BIGTABLE_ZPT), table=self.table) print("zope.pagetemplate: %7.2f" % t_zope) print(" %7.1fX" % (t_zope / t_chameleon)) print("--------------------------") print("check: %d vs %d" % ( len(self._chameleon(BIGTABLE_ZPT)(options=options)), len(self._zope(BIGTABLE_ZPT)(table=self.table)))) print("--------------------------") @benchmark(text_("MANY STRINGS [python]")) def test_many_strings(self): t_chameleon = timing(self._chameleon(MANY_STRINGS_ZPT)) print("chameleon: %7.2f" % t_chameleon) t_zope = timing(self._zope(MANY_STRINGS_ZPT)) print("zope.pagetemplate: %7.2f" % t_zope) print(" %7.1fX" % (t_zope / t_chameleon)) print("--------------------------") print("check: %d vs %d" % ( len(self._chameleon(MANY_STRINGS_ZPT)()), len(self._zope(MANY_STRINGS_ZPT)()))) print("--------------------------") @benchmark(text_("HELLO WORLD")) def test_hello_world(self): t_chameleon = timing(self._chameleon(HELLO_WORLD_ZPT)) * 1000 print("chameleon: %7.2f" % t_chameleon) t_zope = timing(self._zope(HELLO_WORLD_ZPT)) * 1000 print("zope.pagetemplate: %7.2f" % t_zope) print(" %7.1fX" % (t_zope / t_chameleon)) print("--------------------------") print("check: %d vs %d" % ( len(self._chameleon(HELLO_WORLD_ZPT)()), len(self._zope(HELLO_WORLD_ZPT)()))) print("--------------------------") @benchmark(text_("I18N")) def test_i18n(self): from zope.i18n import translate t_chameleon = timing( self._chameleon(I18N_ZPT), translate=translate, language="klingon") * 1000 print("chameleon: %7.2f" % t_chameleon) t_zope = timing(self._zope(I18N_ZPT), env=self.env) * 1000 print("zope.pagetemplate: %7.2f" % t_zope) print(" %7.1fX" % (t_zope / t_chameleon)) @benchmark(text_("COMPILATION")) def test_compilation(self): template = self._chameleon(HELLO_WORLD_ZPT) def chameleon_cook_and_render(template=template): template.cook(HELLO_WORLD_ZPT) template() t_chameleon = timing(chameleon_cook_and_render) * 1000 print("chameleon: %7.2f" % t_chameleon) template = self._zope(HELLO_WORLD_ZPT) def zope_cook_and_render(templte=template): template._cook() template() t_zope = timing(zope_cook_and_render) * 1000 print("zope.pagetemplate: %7.2f" % t_zope) print(" %0.3fX" % (t_zope / t_chameleon)) def start(): result = unittest.TestResult() test = unittest.makeSuite(Benchmarks) test.run(result) for error in result.errors: print("Error in %s...\n" % error[0]) print(error[1]) for failure in result.failures: print("Failure in %s...\n" % failure[0]) print(failure[1]) Chameleon-3.6.2/src/chameleon/codegen.py0000644000076500000240000001464013165626215020526 0ustar mborchstaff00000000000000try: import ast except ImportError: from chameleon import ast25 as ast import inspect import textwrap import types import copy try: import __builtin__ as builtins except ImportError: import builtins reverse_builtin_map = {} for name, value in builtins.__dict__.items(): try: hash(value) except TypeError: continue reverse_builtin_map[value] = name 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(source, mode='exec', is_func=False, func_args=(), func_defaults=(), **kw): def wrapper(*vargs, **kwargs): symbols = dict(zip(args, vargs + defaults)) symbols.update(kwargs) class Visitor(ast.NodeVisitor): def visit_FunctionDef(self, node): self.generic_visit(node) name = symbols.get(node.name, self) if name is not self: node_annotations[node] = ast.FunctionDef( name=name, args=node.args, body=node.body, decorator_list=getattr(node, "decorator_list", []), ) 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(textwrap.dedent(source), mode=mode) Visitor().visit(expr) return expr.body assert isinstance(source, basestring) defaults = func_defaults args = func_args if is_func: return wrapper else: return wrapper(**kw) 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, source=None): self.imports = {} self.defines = {} self.markers = {} self.source = source self.tokens = [] # 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)).\ rsplit('.', 1)[-1] 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_%s" % str(id(node.value)).replace('-', '_') else: name = node.name node = self.define(name, node.value) self.visit(node) def visit_TokenRef(self, node): self.tokens.append((node.pos, node.length)) super(TemplateCodeGenerator, self).visit(ast.Num(n=node.pos)) Chameleon-3.6.2/src/chameleon/compiler.py0000644000076500000240000015125213503370217020727 0ustar mborchstaff00000000000000import re import sys import itertools import logging import threading import functools import collections import pickle import textwrap 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 .astutil import Static from .astutil import TokenRef from .codegen import TemplateCodeGenerator from .codegen import template from .tal import ErrorInfo from .tal import NAME from .i18n import simple_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(r'[^\w_]') 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( '_', unicode_string(string) ).replace('\n', '').replace('-', '_') 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_token(stmts, token): pos = getattr(token, "pos", 0) body = template("__token = pos", pos=TokenRef(pos, len(token))) return body + stmts def eval_token(token): try: line, column = token.location filename = token.filename except AttributeError: line, column = 0, 0 filename = "" string = safe_native(token) return template( "(string, line, col)", string=ast.Str(s=string), line=ast.Num(n=line), col=ast.Num(n=column), mode="eval" ) emit_node = template(is_func=True, func_args=('node',), source=r""" __append(node)""") emit_node_if_non_trivial = template(is_func=True, func_args=('node',), source=r""" if node is not None: __append(node) """) emit_bool = template(is_func=True, func_args=('target', 's', 'default_marker', 'default'), func_defaults=(None, None), source=r""" if target is default_marker: target = default elif target: target = s else: target = None""") emit_convert = template(is_func=True, func_args=('target', 'encoded', 'str', 'long', 'type', 'default_marker', 'default'), func_defaults=(byte_string, unicode_string, long, type, None), source=r""" 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()""") emit_func_convert = template(is_func=True, func_args=('func', 'encoded', 'str','long','type'), func_defaults=(byte_string, unicode_string, long, type), source=r""" def func(target): if target is None: return __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() return target""") emit_translate = template(is_func=True, func_args=('target', 'msgid', 'target_language', 'default'), func_defaults=(None,), source=r""" target = translate(msgid, default=default, domain=__i18n_domain, context=__i18n_context, target_language=target_language)""") emit_func_convert_and_escape = template( is_func=True, func_args=('func', 'str', 'long', 'type', 'encoded'), func_defaults=(unicode_string, long, type, byte_string,), source=r""" def func(target, quote, quote_entity, default, default_marker): if target is None: return if target is default_marker: return default __tt = type(target) if __tt is int or __tt is float or __tt is long: target = str(target) else: 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: return target() 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) return target""") class Interpolator(object): braces_required_regex = re.compile( r'(\$)?\$({(?P.*)})', re.DOTALL) braces_optional_regex = re.compile( r'(\$)?\$({(?P.*)}|(?P[A-Za-z][A-Za-z0-9_]*))', re.DOTALL) def __init__(self, expression, braces_required, translate=False, decode_htmlentities=False): self.expression = expression self.regex = self.braces_required_regex if braces_required else \ self.braces_optional_regex self.translate = translate self.decode_htmlentities = decode_htmlentities 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: text = text.replace('$$', '$') nodes.append(ast.Str(s=text)) break part = text[:m.start()] text = text[m.start():] skip = text.startswith('$$') if skip: part = part + '$' if part: part = part.replace('$$', '$') node = ast.Str(s=part) nodes.append(node) if skip: text = text[2:] continue 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.get("variable") or "" if self.decode_htmlentities: string = decode_htmlentities(string) if 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 continue else: s = m.group() assign = ast.Assign(targets=[target], value=ast.Str(s=s)) body += [assign] 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, context=__i18n_context, target_language=target_language)", msgid=target, mode="eval", target_language=load("target_language"), ) 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, context=__i18n_context, target_language=target_language)", msgid=ast.Str(s=formatting_string), target_language=load("target_language"), mapping=ast.Dict(keys=keys, values=values), mode="eval" ) else: nodes = [ node if isinstance(node, ast.Str) else 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 """ 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, handle_errors=True, char_escape=None): expression = self._parser(string) compiler = self.get_compiler(expression, string, handle_errors, char_escape) return ExpressionCompiler(compiler, self) def get_compiler(self, expression, string, handle_errors, char_escape): if char_escape is None: char_escape = self._char_escape 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, char_escape, *args) stmts.extend(steps) if handle_errors: return set_token(stmts, string.strip()) return stmts return compiler def _convert_bool(self, target, char_escape, 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_structure(self, target, char_escape): """Converts value given by ``target`` to structure output.""" return emit_convert( target, default=self._default, default_marker=self._default_marker, ) def _convert_text(self, target, char_escape): """Converts value given by ``target`` to text.""" if not char_escape: return self._convert_structure(target, char_escape) # This is a cop-out - we really only support a very select # set of escape characters other = set(char_escape) - self.supported_char_escape_set if other: for supported in '"', '\'', '': if supported in char_escape: quote = supported break else: raise RuntimeError( "Unsupported escape set: %s." % repr(char_escape) ) else: quote = '\0' entity = char2entity(quote or '\0') return template( "TARGET = __quote(TARGET, QUOTE, Q_ENTITY, DEFAULT, MARKER)", TARGET=target, QUOTE=ast.Str(s=quote), Q_ENTITY=ast.Str(s=entity), DEFAULT=self._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, "", string, ('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, visitor, strict=True): self.engine_factory = engine_factory self.cache = cache self.strict = strict self.visitor = visitor 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, -1) stmts = template( "__exc = loads(p)", loads=self.loads_symbol, p=ast.Str(s=p) ) stmts += set_token([ast.Raise(exc=load("__exc"))], exc.token) # Apply visitor to each statement for stmt in stmts: self.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_Copy(self, node, target): return self.translate(node.expression, 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(default=node.default) compiler = engine.parse(node.value, char_escape=node.char_escape) 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, translate=node.translation, decode_htmlentities=True ) compiler = engine.get_compiler( interpolator, expr.value, True, () ) return compiler(target, engine, "text") 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, "target_language", 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(simple_translate), 'decode': Builtin("str"), 'convert': Builtin("str"), 'on_error_handler': Builtin("str") } lock = threading.Lock() global_builtins = set(builtins.__dict__) def __init__(self, engine_factory, node, filename, source, 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._visitor = visitor = NameLookupRewriteVisitor(transform) self._engine = ExpressionTransform( engine_factory, self._expression_cache, visitor, 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) prelude = "__filename = %r\n__default = object()" % filename generator = TemplateCodeGenerator(module, source) tokens = [ Token(source[pos:pos + length], pos, source) for pos, length in generator.tokens ] token_map_def = "__tokens = {" + ", ".join("%d: %r" % ( token.pos, (token, ) + token.location ) for token in tokens) + "}" finally: if backup is not None: node_annotations.clear() node_annotations.update(backup) self.lock.release() self.code = "\n".join(( prelude, token_map_def, 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): 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 def visit_Module(self, node): body = [] body += template("import re") body += template("import functools") body += template("from itertools import chain as __chain") if version < (3, 0, 0): body += template("from sys import exc_clear as __exc_clear") 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("__token = None") body += template("__re_needs_escape = g_re_needs_escape") body += emit_func_convert("__convert") body += emit_func_convert_and_escape("__quote") # 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)) exc = template( "exc_info()[1]", exc_info=Symbol(sys.exc_info), mode="eval" ) exc_handler = template( "if pos is not None: rcontext.setdefault('__error__', [])." "append(token + (__filename, exc, ))", exc=exc, token=template("__tokens[pos]", pos="__token", mode="eval"), pos="__token" ) + template("raise") # Wrap visited nodes in try-except error handler. body += [ ast.TryExcept( body=nodes, handlers=[ast.ExceptHandler(body=exc_handler)] ) ] 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"), param("__i18n_context"), ], defaults=[load("None"), 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_%s" % mangle(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_TxContext(self, node): backup = "__previous_i18n_context_%s" % mangle(id(node)) return template("BACKUP = __i18n_context", BACKUP=backup) + \ template("__i18n_context = NAME", NAME=ast.Str(s=node.name)) + \ self.visit(node.node) + \ template("__i18n_context = 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, __tokens[__token][1:3])\n" "if handler is not None: handler(__exc)", cls=ErrorInfo, handler=load("on_error_handler"), 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, load_econtext("target_language") ) if node.char_escape: body += template( "NAME=__quote(NAME, None, '\255', None, None)", NAME=name, ) else: body += template("NAME = __convert(NAME)", NAME=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) self._aliases.append(self._aliases[-1].copy()) 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() self._aliases.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") swap(ast.Suite(body=code), load(stream), "__stream") 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( "if msgid: __append(translate(" "msgid, mapping=mapping, default=default, domain=__i18n_domain, context=__i18n_context, target_language=target_language))", msgid=msgid, default=default, mapping=mapping, target_language=load_econtext("target_language") ) # 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 stmt in self.visit(node.attributes): 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): attr_format = (node.space + node.name + node.eq + node.quote + "%s" + node.quote) filter_args = list(map(self._engine.cache.get, node.filters)) filter_condition = template( "NAME not in CHAIN", NAME=ast.Str(s=node.name), CHAIN=ast.Call( func=load("__chain"), args=filter_args, keywords=[], starargs=None, kwargs=None, ), mode="eval" ) # Static attributes are just outputted directly if isinstance(node.expression, ast.Str): s = attr_format % node.expression.s if node.filters: return template( "if C: __append(S)", C=filter_condition, S=ast.Str(s=s) ) else: return template("__append(S)", S=ast.Str(s=s)) target = identifier("attr", node.name) body = self._engine(node.expression, store(target)) condition = template("TARGET is not None", TARGET=target, mode="eval") if node.filters: condition = ast.BoolOp( values=[condition, filter_condition], op=ast.And(), ) return body + template( "if CONDITION: __append(FORMAT % TARGET)", FORMAT=ast.Str(s=attr_format), TARGET=target, CONDITION=condition, ) def visit_DictAttributes(self, node): target = identifier("attr", id(node)) body = self._engine(node.expression, store(target)) exclude = Static(template( "set(LIST)", LIST=ast.List( elts=[ast.Str(s=name) for name in node.exclude], ctx=ast.Load(), ), mode="eval" )) body += template( "for name, value in TARGET.items():\n " "if name not in EXCLUDE and value is not None: __append(" "' ' + name + '=' + QUOTE + " "QUOTE_FUNC(value, QUOTE, QUOTE_ENTITY, None, None) + QUOTE" ")", TARGET=target, EXCLUDE=exclude, QUOTE_FUNC="__quote", QUOTE=ast.Str(s=node.quote), QUOTE_ENTITY=ast.Str(s=char2entity(node.quote or '\0')), ) return body 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_Cancel(self, node): body = [] for expression in node.expressions: name = identifier("cache", id(expression)) target = store(name) if not self._expression_cache.get(expression): continue body.append(ast.Assign([target], load("None"))) 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) token_reset = template("__token = None") return token_reset + 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)", 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_CodeBlock(self, node): stmts = template(textwrap.dedent(node.source.strip('\n'))) for stmt in stmts: self._visitor(stmt) return set_token(stmts, node.source) 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"), param("__i18n_context"), ], defaults=[load("__i18n_domain"), load("__i18n_context")], ), 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 + set_token( template("__m = __macro.include"), node.expression.value ) + template( "__m(__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 = str(id(self._translations[-1])).replace('-', '_') stream = identifier("stream_%s" % prefix, name) append = identifier("append_%s" % 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-3.6.2/src/chameleon/config.py0000644000076500000240000000353413131416610020354 0ustar mborchstaff00000000000000import os import logging log = logging.getLogger('chameleon.config') environment = dict( (k[10:], v) for (k, v) in ( ((j.lower(), x) for (j, x) in os.environ.items())) if k.startswith('chameleon_') ) # Define which values are read as true TRUE = ('y', 'yes', 't', 'true', 'on', '1') # If eager parsing is enabled, templates are parsed upon # instantiation, rather than when first called upon; this mode is # useful for verifying validity of templates across a project EAGER_PARSING = environment.pop('eager', 'false') EAGER_PARSING = EAGER_PARSING.lower() in TRUE # Debug mode is mostly useful for debugging the template engine # itself. When enabled, generated source code is written to disk to # ease step-debugging and some log levels are lowered to increase # output. Also, the generated source code is available in the # ``source`` attribute of the template instance if compilation # succeeded. DEBUG_MODE = environment.pop('debug', 'false') DEBUG_MODE = DEBUG_MODE.lower() in TRUE # If a cache directory is specified, template source code will be # persisted on disk and reloaded between sessions path = environment.pop('cache', None) if path is not None: CACHE_DIRECTORY = os.path.abspath(path) if not os.path.exists(CACHE_DIRECTORY): raise ValueError( "Cache directory does not exist: %s." % CACHE_DIRECTORY ) log.info("directory cache: %s." % CACHE_DIRECTORY) else: CACHE_DIRECTORY = None # When auto-reload is enabled, templates are reloaded on file change. AUTO_RELOAD = environment.pop('reload', 'false') AUTO_RELOAD = AUTO_RELOAD.lower() in TRUE for key in environment: log.warning( "unknown environment variable set: \"CHAMELEON_%s\"." % key.upper() ) # This is the slice length of the expression displayed in the # formatted exception string SOURCE_EXPRESSION_MARKER_LENGTH = 60 Chameleon-3.6.2/src/chameleon/exc.py0000644000076500000240000002135413274234474017704 0ustar mborchstaff00000000000000# -*- coding: utf-8 -*- import traceback from .utils import create_formatted_exception from .utils import format_kwargs from .utils import safe_native from .tokenize import Token from .config import SOURCE_EXPRESSION_MARKER_LENGTH as LENGTH def compute_source_marker(line, column, expression, size): """Computes source marker location string. >>> def test(l, c, e, s): ... s, marker = compute_source_marker(l, c, e, s) ... out = s + '\\n' + marker ... ... # Replace dot with middle-dot to work around doctest ellipsis ... print(out.replace('...', '···')) >>> test('foo bar', 4, 'bar', 7) foo bar ^^^ >>> test('foo ${bar}', 4, 'bar', 10) foo ${bar} ^^^ >>> test(' foo bar', 6, 'bar', 6) ··· oo bar ^^^ >>> test(' foo bar baz ', 6, 'bar', 6) ··· o bar ··· ^^^ The entire expression is always shown, even if ``size`` does not accomodate for it. >>> test(' foo bar baz ', 6, 'bar baz', 10) ··· oo bar baz ^^^^^^^ >>> test(' foo bar', 10, 'bar', 5) ··· o bar ^^^ >>> test(' foo bar', 10, 'boo', 5) ··· o bar ^ """ s = line.lstrip() column -= len(line) - len(s) s = s.rstrip() try: i = s[column:].index(expression) except ValueError: # If we can't find the expression # (this shouldn't happen), simply # use a standard size marker marker = "^" else: column += i marker = "^" * len(expression) if len(expression) > size: offset = column size = len(expression) else: window = (size - len(expression)) / 2.0 offset = column - window offset -= min(3, max(0, column + window + len(expression) - len(s))) offset = int(offset) if offset > 0: s = s[offset:] r = s.lstrip() d = len(s) - len(r) s = "... " + r column += 4 - d column -= offset # This also adds to the displayed length size += 4 if len(s) > size: s = s[:size].rstrip() + " ..." return s, column * " " + marker def iter_source_marker_lines(source, expression, line, column): for i, l in enumerate(source): if i + 1 != line: continue s, marker = compute_source_marker( l, column, expression, LENGTH ) yield " - Source: %s" % s yield " %s" % marker break def ellipsify(string, limit): if len(string) > limit: return "... " + string[-(limit - 4):] return string class RenderError(Exception): """An error raised during rendering. This class is used as a mixin which is added to the original exception. """ class TemplateError(Exception): """An error raised by Chameleon. >>> from chameleon.tokenize import Token >>> token = Token('token') >>> message = 'message' Make sure the exceptions can be copied: >>> from copy import copy >>> copy(TemplateError(message, token)) TemplateError('message', 'token') And pickle/unpickled: >>> from pickle import dumps, loads >>> loads(dumps(TemplateError(message, token), -1)) TemplateError('message', 'token') """ def __init__(self, msg, token): if not isinstance(token, Token): token = Token(token, 0) Exception.__init__(self, msg, token) def __copy__(self): inst = Exception.__new__(type(self)) inst.args = self.args return inst def __str__(self): text = "%s\n\n" % self.args[0] text += " - String: \"%s\"" % safe_native(self.token) if self.filename: text += "\n" text += " - Filename: %s" % self.filename line, column = self.location text += "\n" text += " - Location: (line %d: col %d)" % (line, column) if line and column: if self.token.source: lines = iter_source_marker_lines( self.token.source.splitlines(), self.token, line, column ) elif self.filename and not self.filename.startswith('<'): try: f = open(self.filename, 'r') except IOError: pass else: it = iter_source_marker_lines( iter(f), self.token, line, column ) try: lines = list(lines) finally: f.close() else: lines = () # Prepend newlines. for line in lines: text += "\n" + line return text def __repr__(self): try: return "%s('%s', '%s')" % ( self.__class__.__name__, self.args[0], safe_native(self.token) ) except AttributeError: return object.__repr__(self) @property def token(self): return self.args[1] @property def filename(self): return self.token.filename @property def location(self): return self.token.location @property def offset(self): return getattr(self.token, "pos", 0) class ParseError(TemplateError): """An error occurred during parsing. Indicates an error on the structural level. """ class CompilationError(TemplateError): """An error occurred during compilation. Indicates a general compilation error. """ class TranslationError(TemplateError): """An error occurred during translation. Indicates a general translation error. """ class LanguageError(CompilationError): """Language syntax error. Indicates a syntactical error due to incorrect usage of the template language. """ class ExpressionError(LanguageError): """An error occurred compiling an expression. Indicates a syntactical error in an expression. """ class ExceptionFormatter(object): def __init__(self, errors, econtext, rcontext): kwargs = rcontext.copy() kwargs.update(econtext) for name in tuple(kwargs): if name.startswith('__'): del kwargs[name] self._errors = errors self._kwargs = kwargs def __call__(self): # Format keyword arguments; consecutive arguments are indented # for readability try: formatted = format_kwargs(self._kwargs) except: # the ``pprint.pformat`` method calls the representation # method of the arguments; this may fail and since we're # already in an exception handler, there's no point in # pursuing this further formatted = () for index, string in enumerate(formatted[1:]): formatted[index + 1] = " " * 15 + string out = [] for error in self._errors: expression, line, column, filename, exc = error if isinstance(exc, UnicodeDecodeError): string = safe_native(exc.object) s, marker = compute_source_marker( string, exc.start, string[exc.start:exc.end], LENGTH ) out.append(" - Stream: %s" % s) out.append(" %s" % marker) _filename = ellipsify(filename, 60) if filename else "" out.append(" - Expression: \"%s\"" % expression) out.append(" - Filename: %s" % _filename) out.append(" - Location: (line %d: col %d)" % (line, column)) if filename and not filename.startswith('<') and line and column: try: f = open(filename, 'r') except IOError: pass else: lines = iter_source_marker_lines( iter(f), expression, line, column ) try: out.extend(lines) finally: f.close() out.append(" - Arguments: %s" % "\n".join(formatted)) if isinstance(exc.__str__, ExceptionFormatter): # This is a nested error that has already been wrapped # We must unwrap it before trying to format it to prevent # recursion exc = create_formatted_exception(exc, type(exc), exc._original__str__) formatted = traceback.format_exception_only(type(exc), exc)[-1] formatted_class = "%s:" % type(exc).__name__ if formatted.startswith(formatted_class): formatted = formatted[len(formatted_class):].lstrip() return "\n".join(map(safe_native, [formatted] + out)) Chameleon-3.6.2/src/chameleon/i18n.py0000644000076500000240000000730413131416610017665 0ustar mborchstaff00000000000000############################################################################## # # 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 from .exc import CompilationError from .utils import unicode_string NAME_RE = r"[a-zA-Z][-a-zA-Z0-9_]*" WHITELIST = frozenset([ "translate", "domain", "context", "target", "source", "attributes", "data", "name", "mode", "xmlns", "xml", "comment", "ignore", "ignore-attributes", ]) _interp_regex = re.compile(r'(?" % ( 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 DictAttributes(Node): """Element attributes from one or more Python dicts.""" _fields = "expression", "char_escape", "quote", "exclude" class Attribute(Node): """Element attribute.""" _fields = "name", "expression", "quote", "eq", "space", "filters" 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 Cancel(Cache): pass class Copy(Node): _fields = "expression", 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(Node): """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 TxContext(Node): """Update translation context.""" _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-3.6.2/src/chameleon/parser.py0000644000076500000240000001642713450341714020416 0ustar mborchstaff00000000000000import 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_double_hyphen = re.compile(r'--(?!(-)*>)') match_tag_prefix_and_name = re.compile( r'^(?P([^:\n\r ]+:)?[^ \n\t\r>/]+)' r'(?P(?P\s*)/?>)?', re.UNICODE | re.DOTALL) match_single_attribute = re.compile( r'(?P\s+)(?!\d)' r'(?P[^ =/>\n\t\r]+)' 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\w+)(?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, restricted_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, restricted_namespace ) node['ns_map'] = namespace 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, restricted_namespace): 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: if restricted_namespace: raise KeyError( "Undefined namespace prefix: %s." % prefix) else: ns = default else: ns = default namespaced[ns, name] = value return namespaced def identify(string): if string.startswith("<"): if string.startswith(" Chameleon-3.6.2/src/chameleon/tests/inputs/022-switch.pt0000644000076500000240000000106613131416610023366 0ustar mborchstaff00000000000000
bad ok ok bad bad ${default|string:ok}
bad ok
bad bad ok
Chameleon-3.6.2/src/chameleon/tests/inputs/022.xml0000644000076500000240000000012013131416610022232 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/023-condition.pt0000644000076500000240000000025213131416610024050 0ustar mborchstaff00000000000000 bad ok Chameleon-3.6.2/src/chameleon/tests/inputs/023.xml0000644000076500000240000000011713131416610022241 0ustar mborchstaff00000000000000 ]> &e; Chameleon-3.6.2/src/chameleon/tests/inputs/024-namespace-elements.pt0000644000076500000240000000040213131416610025626 0ustar mborchstaff00000000000000 ${'first'} second ok bad Chameleon-3.6.2/src/chameleon/tests/inputs/024.xml0000644000076500000240000000016413131416610022244 0ustar mborchstaff00000000000000 "> ]> &e; Chameleon-3.6.2/src/chameleon/tests/inputs/025-repeat-whitespace.pt0000644000076500000240000000075113131416610025502 0ustar mborchstaff00000000000000
  • , .
Chameleon-3.6.2/src/chameleon/tests/inputs/025.xml0000644000076500000240000000014413131416610022243 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/026-repeat-variable.pt0000644000076500000240000000113513131416610025131 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/inputs/026.xml0000644000076500000240000000014013131416610022240 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/027-attribute-replacement.pt0000644000076500000240000000067413131416610026376 0ustar mborchstaff00000000000000
Hello Universe! Hello Universe!
Chameleon-3.6.2/src/chameleon/tests/inputs/027.xml0000644000076500000240000000013613131416610022246 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/028-attribute-toggle.pt0000644000076500000240000000040013131416610025344 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/inputs/028.xml0000644000076500000240000000012313131416610022243 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/029-attribute-ordering.pt0000644000076500000240000000031613131416610025703 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/inputs/029.xml0000644000076500000240000000012313131416610022244 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/030-repeat-tuples.pt0000644000076500000240000000021113131416610024645 0ustar mborchstaff00000000000000
${repeat['i', 'j'].number}, ${i}, ${j}
Chameleon-3.6.2/src/chameleon/tests/inputs/030.xml0000644000076500000240000000012513131416610022236 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/031-namespace-with-tal.pt0000644000076500000240000000035413131416610025547 0ustar mborchstaff00000000000000
True
Chameleon-3.6.2/src/chameleon/tests/inputs/031.xml0000644000076500000240000000014413131416610022240 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/032-master-template.pt0000644000076500000240000000115113131416610025165 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/inputs/032.xml0000644000076500000240000000014413131416610022241 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/033-use-macro-trivial.pt0000644000076500000240000000011113131416610025420 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/inputs/033.xml0000644000076500000240000000016513131416610022245 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/034-use-template-as-macro.pt0000644000076500000240000000007113131416610026170 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/inputs/034.xml0000644000076500000240000000006713131416610022247 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/035-use-macro-with-fill-slot.pt0000644000076500000240000000024513131416610026636 0ustar mborchstaff00000000000000 ${kind} title Chameleon-3.6.2/src/chameleon/tests/inputs/035.xml0000644000076500000240000000007013131416610022242 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/036-use-macro-inherits-dynamic-scope.pt0000644000076500000240000000016313131416610030336 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/inputs/036.xml0000644000076500000240000000011113131416610022237 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/037-use-macro-local-variable-scope.pt0000644000076500000240000000030313131416610027741 0ustar mborchstaff00000000000000 ok Chameleon-3.6.2/src/chameleon/tests/inputs/037.xml0000644000076500000240000000012013131416610022240 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/038-use-macro-globals.pt0000644000076500000240000000021413131416610025402 0ustar mborchstaff00000000000000 ok Chameleon-3.6.2/src/chameleon/tests/inputs/038.xml0000644000076500000240000000012013131416610022241 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/039-globals.pt0000644000076500000240000000005413131416610023514 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/inputs/039.xml0000644000076500000240000000011113131416610022242 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/040-macro-using-template-symbol.pt0000644000076500000240000000153713131416610027430 0ustar mborchstaff00000000000000 ${foo}
Chameleon-3.6.2/src/chameleon/tests/inputs/040.xml0000644000076500000240000000017513131416610022244 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/041-translate-nested-names.pt0000644000076500000240000000076513131416610026451 0ustar mborchstaff00000000000000
Hello world!
Hello world!
Goodbye world!
Chameleon-3.6.2/src/chameleon/tests/inputs/041.xml0000644000076500000240000000015113131416610022237 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/042-use-macro-fill-footer.pt0000644000076500000240000000022013131416610026171 0ustar mborchstaff00000000000000 New footer Chameleon-3.6.2/src/chameleon/tests/inputs/042.xml0000644000076500000240000000014213131416610022240 0ustar mborchstaff00000000000000 ]> A Chameleon-3.6.2/src/chameleon/tests/inputs/043-macro-nested-dynamic-vars.pt0000644000076500000240000000061413131416610027042 0ustar mborchstaff00000000000000 ${title}
Chameleon-3.6.2/src/chameleon/tests/inputs/043.xml0000644000076500000240000000015413131416610022244 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/044-tuple-define.pt0000644000076500000240000000011613131416610024445 0ustar mborchstaff00000000000000 ${a}, ${b} Chameleon-3.6.2/src/chameleon/tests/inputs/044.xml0000644000076500000240000000027313131416610022247 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/045-namespaces.pt0000644000076500000240000000104713131416610024210 0ustar mborchstaff00000000000000 ]> ZZZ YYY XXX Chameleon-3.6.2/src/chameleon/tests/inputs/045.xml0000644000076500000240000000017013131416610022244 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/046-extend-macro.pt0000644000076500000240000000034513131416610024460 0ustar mborchstaff00000000000000 New footer Chameleon-3.6.2/src/chameleon/tests/inputs/046.xml0000644000076500000240000000017013131416610022245 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/047-use-extended-macro.pt0000644000076500000240000000017213131416610025562 0ustar mborchstaff00000000000000 Extended Chameleon-3.6.2/src/chameleon/tests/inputs/047.xml0000644000076500000240000000010013131416610022237 0ustar mborchstaff00000000000000 ]> X Y Chameleon-3.6.2/src/chameleon/tests/inputs/048-use-extended-macro-fill-original.pt0000644000076500000240000000023613131416610030312 0ustar mborchstaff00000000000000 Extended footer Chameleon-3.6.2/src/chameleon/tests/inputs/048.xml0000644000076500000240000000007513131416610022253 0ustar mborchstaff00000000000000 ]> ] Chameleon-3.6.2/src/chameleon/tests/inputs/049-entities-in-attributes.pt0000644000076500000240000000070713131416610026513 0ustar mborchstaff00000000000000
    
    
  
  
    
  
Chameleon-3.6.2/src/chameleon/tests/inputs/058.xml0000644000076500000240000000015713131416610022255 0ustar  mborchstaff00000000000000

]>

Chameleon-3.6.2/src/chameleon/tests/inputs/059-embedded-javascript.pt0000644000076500000240000000025613131416610025774 0ustar  mborchstaff00000000000000
  
    test
    test
  

Chameleon-3.6.2/src/chameleon/tests/inputs/059.xml0000644000076500000240000000034313131416610022253 0ustar  mborchstaff00000000000000


]>





Chameleon-3.6.2/src/chameleon/tests/inputs/060-macro-with-multiple-same-slots.pt0000644000076500000240000000033713131416610030057 0ustar  mborchstaff00000000000000
  
    <metal:title define-slot="title">Untitled</metal:title>
  
  
    

Untitled

Chameleon-3.6.2/src/chameleon/tests/inputs/060.xml0000644000076500000240000000010313131416610022235 0ustar mborchstaff00000000000000 ]> X Y Chameleon-3.6.2/src/chameleon/tests/inputs/061-fill-one-slot-but-two-defined.pt0000644000076500000240000000023013131416610027537 0ustar mborchstaff00000000000000 My document Chameleon-3.6.2/src/chameleon/tests/inputs/061.xml0000644000076500000240000000010213131416610022235 0ustar mborchstaff00000000000000 ]> £ Chameleon-3.6.2/src/chameleon/tests/inputs/062-comments-and-expressions.pt0000644000076500000240000000056413131416610027040 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/inputs/062.xml0000644000076500000240000000012713131416610022245 0ustar mborchstaff00000000000000 ]> เจมส์ Chameleon-3.6.2/src/chameleon/tests/inputs/063-continuation.pt0000644000076500000240000000010313131416610024573 0ustar mborchstaff00000000000000
${foo}
Chameleon-3.6.2/src/chameleon/tests/inputs/063.xml0000644000076500000240000000015413131416610022246 0ustar mborchstaff00000000000000 ]> <เจมส์> Chameleon-3.6.2/src/chameleon/tests/inputs/064-tags-and-special-characters.pt0000644000076500000240000000010513131416610027315 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/inputs/064.xml0000644000076500000240000000011713131416610022246 0ustar mborchstaff00000000000000 ]> 𐀀􏿽 Chameleon-3.6.2/src/chameleon/tests/inputs/065-use-macro-in-fill.pt0000644000076500000240000000042413131416610025314 0ustar mborchstaff00000000000000 <div metal:fill-slot="content">Content</div> </html>��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon-3.6.2/src/chameleon/tests/inputs/065.xml��������������������������������������������������0000644�0000765�0000024�00000000121�13131416610�022242� 0����������������������������������������������������������������������������������������������������ustar �mborch��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ENTITY e "<"> <!ELEMENT doc (#PCDATA)> ]> <doc></doc> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon-3.6.2/src/chameleon/tests/inputs/066-load-expression.pt�����������������������������������0000644�0000765�0000024�00000000125�13131416610�025204� 0����������������������������������������������������������������������������������������������������ustar �mborch��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html tal:define="hello_world load: hello_world.pt" metal:use-macro="hello_world" /> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon-3.6.2/src/chameleon/tests/inputs/066.xml��������������������������������������������������0000644�0000765�0000024�00000000233�13131416610�022247� 0����������������������������������������������������������������������������������������������������ustar �mborch��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc a1 CDATA #IMPLIED> <!-- 34 is double quote --> <!ENTITY e1 """> ]> <doc a1="&e1;"></doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon-3.6.2/src/chameleon/tests/inputs/067-attribute-decode.pt����������������������������������0000644�0000765�0000024�00000000311�13131416610�025312� 0����������������������������������������������������������������������������������������������������ustar �mborch��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <img src="#" tal:attributes="class 1 > 0 and 'up' or 0 < 1 and 'down';" /> <img src="#" tal:attributes="class 0 > 1 and 'up' or 0 < 1 and 'down';" /> </body> </html> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon-3.6.2/src/chameleon/tests/inputs/067.xml��������������������������������������������������0000644�0000765�0000024�00000000101�13131416610�022242� 0����������������������������������������������������������������������������������������������������ustar �mborch��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc> </doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon-3.6.2/src/chameleon/tests/inputs/068-less-than-greater-than-in-attributes.pt��������������0000644�0000765�0000024�00000000322�13131416610�031136� 0����������������������������������������������������������������������������������������������������ustar �mborch��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <span tal:content="string:0 < 1 or 0 > 1" /> <span tal:content="structure string:0 < 1 or 0 > 1" /> <span class="0 < 1 or 0 > 1" /> <span>0 < 1 or 0 > 1</span> </body> </html> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon-3.6.2/src/chameleon/tests/inputs/068.xml��������������������������������������������������0000644�0000765�0000024�00000000124�13131416610�022250� 0����������������������������������������������������������������������������������������������������ustar �mborch��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ENTITY e " "> ]> <doc>&e;</doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon-3.6.2/src/chameleon/tests/inputs/069-translation-domain-and-macro.pt����������������������0000644�0000765�0000024�00000000247�13131416610�027542� 0����������������������������������������������������������������������������������������������������ustar �mborch��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html metal:use-macro="load('032-master-template.pt').macros['main']"> <title metal:fill-slot="title" i18n:domain="test" i18n:translate="title">Title Chameleon-3.6.2/src/chameleon/tests/inputs/069.xml0000644000076500000240000000013513131416610022253 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/070-translation-domain-and-use-macro.pt0000644000076500000240000000024713131416610030324 0ustar mborchstaff00000000000000 Title Chameleon-3.6.2/src/chameleon/tests/inputs/070.xml0000644000076500000240000000012113131416610022236 0ustar mborchstaff00000000000000"> %e; ]> Chameleon-3.6.2/src/chameleon/tests/inputs/071-html-attribute-defaults.pt0000644000076500000240000000105313131416610026637 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/inputs/071.xml0000644000076500000240000000013213131416610022241 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/072-repeat-interpolation.pt0000644000076500000240000000054713131416610026242 0ustar mborchstaff00000000000000
  • ${i}
  • ${i}
  • ${i}
Chameleon-3.6.2/src/chameleon/tests/inputs/072.xml0000644000076500000240000000013513131416610022245 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/073-utf8-encoded.pt0000644000076500000240000000022313131416610024352 0ustar mborchstaff00000000000000 ${'my title'} — ${'my site'} Chameleon-3.6.2/src/chameleon/tests/inputs/073.xml0000644000076500000240000000013613131416610022247 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/074-encoded-template.pt0000644000076500000240000000025113131416610025301 0ustar mborchstaff00000000000000 ${'my title'} — ${'my site'} Chameleon-3.6.2/src/chameleon/tests/inputs/074.xml0000644000076500000240000000013613131416610022250 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/075-nested-macros.pt0000644000076500000240000000043213131416610024635 0ustar mborchstaff00000000000000 foo Chameleon-3.6.2/src/chameleon/tests/inputs/075.xml0000644000076500000240000000014013131416610022244 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/076-nested-macro-override.pt0000644000076500000240000000021213131416610026264 0ustar mborchstaff00000000000000 bar Chameleon-3.6.2/src/chameleon/tests/inputs/076.xml0000644000076500000240000000030013131416610022243 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/077-i18n-attributes.pt0000644000076500000240000000015013131416610025033 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/inputs/077.xml0000644000076500000240000000013513131416610022252 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/078-tags-and-newlines.pt0000644000076500000240000000143613131416610025421 0ustar mborchstaff00000000000000 , Chameleon-3.6.2/src/chameleon/tests/inputs/078.xml0000644000076500000240000000014413131416610022253 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/079-implicit-i18n.pt0000644000076500000240000000064413131416610024471 0ustar mborchstaff00000000000000 Welcome

Welcome

An edge case: ${. Site logo Site logo
boo foo.
bar.
Chameleon-3.6.2/src/chameleon/tests/inputs/079.xml0000644000076500000240000000014513131416610022255 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/080-xmlns-namespace-on-tal.pt0000644000076500000240000000037213131416610026353 0ustar mborchstaff00000000000000 Hello world Chameleon-3.6.2/src/chameleon/tests/inputs/080.xml0000644000076500000240000000013713131416610022246 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/081-load-spec.pt0000644000076500000240000000010613131416610023733 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/inputs/081.xml0000644000076500000240000000021413131416610022243 0ustar mborchstaff00000000000000 ]>
Chameleon-3.6.2/src/chameleon/tests/inputs/082-load-spec-computed.pt0000644000076500000240000000014213131416610025552 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/inputs/082.xml0000644000076500000240000000013213131416610022243 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/083-template-dict-to-macro.pt0000644000076500000240000000014413131416610026343 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/inputs/083.xml0000644000076500000240000000014513131416610022250 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/084-interpolation-in-cdata.pt0000644000076500000240000000017113131416610026436 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/inputs/084.xml0000644000076500000240000000006613131416610022253 0ustar mborchstaff00000000000000]> Chameleon-3.6.2/src/chameleon/tests/inputs/085-nested-translation.pt0000644000076500000240000000046513131416610025716 0ustar mborchstaff00000000000000 Welcome

Welcome

Click here to continue.

Chameleon-3.6.2/src/chameleon/tests/inputs/085.xml0000644000076500000240000000014613131416610022253 0ustar mborchstaff00000000000000 "> ]> &e; Chameleon-3.6.2/src/chameleon/tests/inputs/086-self-closing.pt0000644000076500000240000000037313131416610024464 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/inputs/086.xml0000644000076500000240000000014413131416610022252 0ustar mborchstaff00000000000000 "> ]> &e; Chameleon-3.6.2/src/chameleon/tests/inputs/087-code-blocks.pt0000644000076500000240000000067313131416610024270 0ustar mborchstaff00000000000000
Please input a number from the range ${", ".join(numbers)}.
41 + 1 = ${function(41)}.
Chameleon-3.6.2/src/chameleon/tests/inputs/087.xml0000644000076500000240000000015313131416610022253 0ustar mborchstaff00000000000000 ]> &e; Chameleon-3.6.2/src/chameleon/tests/inputs/088-python-newlines.pt0000644000076500000240000000007713131416610025245 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/inputs/088.xml0000644000076500000240000000012713131416610022255 0ustar mborchstaff00000000000000 "> ]> &e; Chameleon-3.6.2/src/chameleon/tests/inputs/089-load-fallback.pt0000644000076500000240000000017413131416610024555 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/inputs/089.xml0000644000076500000240000000015413131416610022256 0ustar mborchstaff00000000000000 ]> &e; Chameleon-3.6.2/src/chameleon/tests/inputs/090-tuple-expression.pt0000644000076500000240000000044113131416610025414 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/inputs/090.xml0000644000076500000240000000022613131416610022246 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/091-repeat-none.pt0000644000076500000240000000011313131416610024300 0ustar mborchstaff00000000000000
error
Chameleon-3.6.2/src/chameleon/tests/inputs/091.xml0000644000076500000240000000026513131416610022252 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/092.xml0000644000076500000240000000014613131416610022251 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/093.xml0000644000076500000240000000007413131416610022252 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/094.xml0000644000076500000240000000016013131416610022247 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/095.xml0000644000076500000240000000021513131416610022251 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/096.xml0000644000076500000240000000014313131416610022252 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/097.xml0000644000076500000240000000023513131416610022255 0ustar mborchstaff00000000000000 %e; ]> Chameleon-3.6.2/src/chameleon/tests/inputs/098.xml0000644000076500000240000000010713131416610022254 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/099.xml0000644000076500000240000000014413131416610022256 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/100.xml0000644000076500000240000000014513131416610022236 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/101-unclosed-tags.html0000644000076500000240000000010313131416610025143 0ustar mborchstaff00000000000000



Hello world

Chameleon-3.6.2/src/chameleon/tests/inputs/101.xml0000644000076500000240000000012113131416610022231 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/102-unquoted-attributes.html0000644000076500000240000000012413131416610026427 0ustar mborchstaff00000000000000

Hello world

Chameleon-3.6.2/src/chameleon/tests/inputs/102.xml0000644000076500000240000000014713131416610022242 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/103-simple-attribute.html0000644000076500000240000000032013131416610025670 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/inputs/103.xml0000644000076500000240000000010513131416610022235 0ustar mborchstaff00000000000000 ]> <doc> Chameleon-3.6.2/src/chameleon/tests/inputs/104.xml0000644000076500000240000000014513131416610022242 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/105.xml0000644000076500000240000000015013131416610022237 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/106.xml0000644000076500000240000000015113131416610022241 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/107.xml0000644000076500000240000000015113131416610022242 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/108.xml0000644000076500000240000000017113131416610022245 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/109.xml0000644000076500000240000000014213131416610022244 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/110.xml0000644000076500000240000000020113131416610022230 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/111.xml0000644000076500000240000000017313131416610022241 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/112.xml0000644000076500000240000000013113131416610022234 0ustar mborchstaff00000000000000 ]>
Chameleon-3.6.2/src/chameleon/tests/inputs/113.xml0000644000076500000240000000013313131416610022237 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/114.xml0000644000076500000240000000014013131416610022236 0ustar mborchstaff00000000000000 "> ]> &e; Chameleon-3.6.2/src/chameleon/tests/inputs/115.xml0000644000076500000240000000014713131416610022246 0ustar mborchstaff00000000000000 ]> &e1; Chameleon-3.6.2/src/chameleon/tests/inputs/116.xml0000644000076500000240000000011213131416610022237 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/117.xml0000644000076500000240000000012613131416610022245 0ustar mborchstaff00000000000000 ]> ] Chameleon-3.6.2/src/chameleon/tests/inputs/118.xml0000644000076500000240000000012713131416610022247 0ustar mborchstaff00000000000000 ]> ] Chameleon-3.6.2/src/chameleon/tests/inputs/119.xml0000644000076500000240000000010213131416610022241 0ustar mborchstaff00000000000000 ]> Chameleon-3.6.2/src/chameleon/tests/inputs/120-translation-context.pt0000644000076500000240000000042713131416610026104 0ustar mborchstaff00000000000000
Hello world!
Tab
Chameleon-3.6.2/src/chameleon/tests/inputs/121-translation-comment.pt0000644000076500000240000000016413131416610026061 0ustar mborchstaff00000000000000

Hello world!

Chameleon-3.6.2/src/chameleon/tests/inputs/122-translation-ignore.pt0000644000076500000240000000034113131416610025700 0ustar mborchstaff00000000000000

Hello world!

Python is a programming language. Chameleon-3.6.2/src/chameleon/tests/inputs/123-html5-data-attributes.pt0000644000076500000240000000013213131416610026204 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/inputs/124-translation-target.pt0000644000076500000240000000025313131416610025707 0ustar mborchstaff00000000000000

Hello world!

It's a big world.

Chameleon-3.6.2/src/chameleon/tests/inputs/125-macro-translation-ordering.pt0000644000076500000240000000056213131416610027335 0ustar mborchstaff00000000000000

h1

h2

Chameleon-3.6.2/src/chameleon/tests/inputs/126-define-escaping.pt0000644000076500000240000000035013131416610025106 0ustar mborchstaff00000000000000 ${s1} ${s2} ${structure: s1}amp; ${structure: s2}amp; Chameleon-3.6.2/src/chameleon/tests/inputs/238-macroname.pt0000644000076500000240000000033313131416610024034 0ustar mborchstaff00000000000000

name

Chameleon-3.6.2/src/chameleon/tests/inputs/greeting.pt0000644000076500000240000000005113131416610023361 0ustar mborchstaff00000000000000
Hello, ${name | 'undefined'}.
Chameleon-3.6.2/src/chameleon/tests/inputs/hello_world.pt0000644000076500000240000000006713131416610024076 0ustar mborchstaff00000000000000 ${'Hello world!'} Chameleon-3.6.2/src/chameleon/tests/inputs/hello_world.txt0000644000076500000240000000002213131416610024261 0ustar mborchstaff00000000000000${'Hello world!'} Chameleon-3.6.2/src/chameleon/tests/inputs/multinode-implicit-i18n.pt0000644000076500000240000000005713322447520026156 0ustar mborchstaff00000000000000 Foo ${message} Chameleon-3.6.2/src/chameleon/tests/outputs/0000755000076500000240000000000013503370500021415 5ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/outputs/001.html0000644000076500000240000000012513131416610022601 0ustar mborchstaff00000000000000 Hello world! Hello world! Goodbye world! Chameleon-3.6.2/src/chameleon/tests/outputs/001.pt0000644000076500000240000000007513131416610022264 0ustar mborchstaff00000000000000 Hello world! ok Chameleon-3.6.2/src/chameleon/tests/outputs/001.txt0000644000076500000240000000002113131416610022447 0ustar mborchstaff00000000000000<&> Chameleon-3.6.2/src/chameleon/tests/outputs/002.pt0000644000076500000240000000027713131416610022271 0ustar mborchstaff00000000000000
Hello! Hello.
Goodbye! Goodbye.
ok Chameleon-3.6.2/src/chameleon/tests/outputs/003.pt0000644000076500000240000000054313131416610022266 0ustar mborchstaff00000000000000
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-3.6.2/src/chameleon/tests/outputs/004.pt0000644000076500000240000000124013131416610022262 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/outputs/005.pt0000644000076500000240000000030713131416610022266 0ustar mborchstaff00000000000000 Default True False Computed default Chameleon-3.6.2/src/chameleon/tests/outputs/006.pt0000644000076500000240000000037413131416610022273 0ustar mborchstaff00000000000000 copyright (c) 2010 copyright (c) 2010 copyright (c) 2010 $ignored <type 'str'> Chameleon-3.6.2/src/chameleon/tests/outputs/007.pt0000644000076500000240000000054113131416610022270 0ustar mborchstaff00000000000000 Hello world!
Hello world!
Hello world!
<type 'str'> && Hello world $leftalone
Hello world
${} is ignored.
Chameleon-3.6.2/src/chameleon/tests/outputs/008.pt0000644000076500000240000000017713131416610022276 0ustar mborchstaff00000000000000 {}
static
nothing
Chameleon-3.6.2/src/chameleon/tests/outputs/009.pt0000644000076500000240000000007613131416610022275 0ustar mborchstaff00000000000000
Hello world!
Chameleon-3.6.2/src/chameleon/tests/outputs/010.pt0000644000076500000240000000024413131416610022262 0ustar mborchstaff00000000000000
1 < 2
2 < 3, 2&3, 2<3, 2>3
3 < 4
4 < 5
Hello world!
Chameleon-3.6.2/src/chameleon/tests/outputs/011-en.pt0000644000076500000240000000046413131416610022667 0ustar mborchstaff00000000000000
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-3.6.2/src/chameleon/tests/outputs/011.pt0000644000076500000240000000021213131416610022256 0ustar mborchstaff00000000000000
Message
Message
Message
Message
Message Chameleon-3.6.2/src/chameleon/tests/outputs/012-en.pt0000644000076500000240000000076113131416610022670 0ustar mborchstaff00000000000000
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-3.6.2/src/chameleon/tests/outputs/012.pt0000644000076500000240000000041513131416610022264 0ustar mborchstaff00000000000000
Hello world!
Hello world!
Hello world!
Hello world! Goodbye planet!
Hello world! Goodbye planet!
Chameleon-3.6.2/src/chameleon/tests/outputs/013.pt0000644000076500000240000000042513131416610022266 0ustar mborchstaff00000000000000
[1,1] [1,2]
[2,1] [2,2]
Chameleon-3.6.2/src/chameleon/tests/outputs/014.pt0000644000076500000240000000026413131416610022270 0ustar mborchstaff00000000000000 [3,3] [3,4] [4,3] [4,4] Chameleon-3.6.2/src/chameleon/tests/outputs/015-en.pt0000644000076500000240000000026313131416610022670 0ustar mborchstaff00000000000000
Price: Per kilo 12.5 ('Per kilo ${amount}' translation into 'en') ('Price: ${price}' translation into 'en')
Chameleon-3.6.2/src/chameleon/tests/outputs/015.pt0000644000076500000240000000013413131416610022265 0ustar mborchstaff00000000000000
Price: Per kilo 12.5
Chameleon-3.6.2/src/chameleon/tests/outputs/016-en.pt0000644000076500000240000000057313131416610022675 0ustar mborchstaff00000000000000
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-3.6.2/src/chameleon/tests/outputs/016.pt0000644000076500000240000000027213131416610022271 0ustar mborchstaff00000000000000
Hello world!
Hello world! Hello world! Hello world! Hello world! Chameleon-3.6.2/src/chameleon/tests/outputs/017.pt0000644000076500000240000000024013131416610022265 0ustar mborchstaff00000000000000 Hello world! 1 Hello world! 23 4Hello world!
Hello world!
Hello world! Hello world! Chameleon-3.6.2/src/chameleon/tests/outputs/018-en.pt0000644000076500000240000000026113131416610022671 0ustar mborchstaff00000000000000
october ('october' translation into 'en') 1982 ('1982' translation into 'en') ('${monthname} ${year}' translation into 'en')
Chameleon-3.6.2/src/chameleon/tests/outputs/018.pt0000644000076500000240000000010113131416610022262 0ustar mborchstaff00000000000000
october 1982
Chameleon-3.6.2/src/chameleon/tests/outputs/019.pt0000644000076500000240000000024213131416610022271 0ustar mborchstaff00000000000000 Hello world! Hello world!1 2Hello world! Hello world!3 Hello world!5 6Hello world! 1 1.0 True Chameleon-3.6.2/src/chameleon/tests/outputs/020.pt0000644000076500000240000000020513131416610022260 0ustar mborchstaff00000000000000
NameError thrown at 5:24.
Chameleon-3.6.2/src/chameleon/tests/outputs/021-en.pt0000644000076500000240000000063613131416610022671 0ustar mborchstaff00000000000000
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-3.6.2/src/chameleon/tests/outputs/021.pt0000644000076500000240000000030413131416610022261 0ustar mborchstaff00000000000000
Hello world!
Hello world!
Hello world!
Hello world!
Chameleon-3.6.2/src/chameleon/tests/outputs/022.pt0000644000076500000240000000026313131416610022266 0ustar mborchstaff00000000000000
ok ok
ok
ok
Chameleon-3.6.2/src/chameleon/tests/outputs/023.pt0000644000076500000240000000006713131416610022271 0ustar mborchstaff00000000000000 ok Chameleon-3.6.2/src/chameleon/tests/outputs/024.pt0000644000076500000240000000011513131416610022264 0ustar mborchstaff00000000000000 first second ok Chameleon-3.6.2/src/chameleon/tests/outputs/025.pt0000644000076500000240000000036313131416610022272 0ustar mborchstaff00000000000000
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 1, 2, 3 .
Chameleon-3.6.2/src/chameleon/tests/outputs/026.pt0000644000076500000240000000053313131416610022272 0ustar mborchstaff00000000000000
  • 0
  • 1
  • 2
  • 0
  • 1
  • 2
  • even
  • odd
  • even
Chameleon-3.6.2/src/chameleon/tests/outputs/027.pt0000644000076500000240000000026313131416610022273 0ustar mborchstaff00000000000000
abcghi Hello World! Hello World!
Chameleon-3.6.2/src/chameleon/tests/outputs/028.pt0000644000076500000240000000017613131416610022277 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/outputs/029.pt0000644000076500000240000000015313131416610022273 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/outputs/030.pt0000644000076500000240000000015013131416610022260 0ustar mborchstaff00000000000000
1, 1, 2
2, 3, 4
Chameleon-3.6.2/src/chameleon/tests/outputs/031.pt0000644000076500000240000000010713131416610022263 0ustar mborchstaff00000000000000
Hello World! Hello World! Hello World! 012 True
Chameleon-3.6.2/src/chameleon/tests/outputs/032.pt0000644000076500000240000000027413131416610022271 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/outputs/033.pt0000644000076500000240000000027413131416610022272 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/outputs/034.pt0000644000076500000240000000027413131416610022273 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/outputs/035.pt0000644000076500000240000000027513131416610022275 0ustar mborchstaff00000000000000 New title
Chameleon-3.6.2/src/chameleon/tests/outputs/036.pt0000644000076500000240000000026613131416610022276 0ustar mborchstaff00000000000000 New title
Chameleon-3.6.2/src/chameleon/tests/outputs/037.pt0000644000076500000240000000026213131416610022273 0ustar mborchstaff00000000000000 Master template
ok
Chameleon-3.6.2/src/chameleon/tests/outputs/038.pt0000644000076500000240000000006713131416610022277 0ustar mborchstaff00000000000000 ok Chameleon-3.6.2/src/chameleon/tests/outputs/039.pt0000644000076500000240000000000013131416610022263 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/outputs/040.pt0000644000076500000240000000071213131416610022265 0ustar mborchstaff00000000000000 foo Chameleon-3.6.2/src/chameleon/tests/outputs/041.pt0000644000076500000240000000021313131416610022262 0ustar mborchstaff00000000000000
Hello world!
Hello world!
Goodbye
Chameleon-3.6.2/src/chameleon/tests/outputs/042.pt0000644000076500000240000000031313131416610022264 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/outputs/043.pt0000644000076500000240000000007013131416610022265 0ustar mborchstaff00000000000000 My title Chameleon-3.6.2/src/chameleon/tests/outputs/044.pt0000644000076500000240000000005313131416610022267 0ustar mborchstaff00000000000000 a, b Chameleon-3.6.2/src/chameleon/tests/outputs/045.pt0000644000076500000240000000071713131416610022277 0ustar mborchstaff00000000000000 ]> ZZZ YYY XXX Chameleon-3.6.2/src/chameleon/tests/outputs/046.pt0000644000076500000240000000033013131416610022267 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/outputs/047.pt0000644000076500000240000000033113131416610022271 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/outputs/048.pt0000644000076500000240000000032013131416610022270 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/outputs/049.pt0000644000076500000240000000032413131416610022275 0ustar mborchstaff00000000000000
amp=&amp; lt=&lt;
amp=& lt=<
Chameleon-3.6.2/src/chameleon/tests/outputs/059.pt0000644000076500000240000000021613131416610022276 0ustar mborchstaff00000000000000 test test Chameleon-3.6.2/src/chameleon/tests/outputs/060.pt0000644000076500000240000000014713131416610022271 0ustar mborchstaff00000000000000 Untitled

Untitled

Chameleon-3.6.2/src/chameleon/tests/outputs/061.pt0000644000076500000240000000015413131416610022270 0ustar mborchstaff00000000000000 My document

My document

Chameleon-3.6.2/src/chameleon/tests/outputs/062.pt0000644000076500000240000000043213131416610022270 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/outputs/063.pt0000644000076500000240000000002013131416610022262 0ustar mborchstaff00000000000000
2
Chameleon-3.6.2/src/chameleon/tests/outputs/064.pt0000644000076500000240000000005113131416610022267 0ustar mborchstaff00000000000000
Chameleon-3.6.2/src/chameleon/tests/outputs/065.pt0000644000076500000240000000025213131416610022273 0ustar mborchstaff00000000000000 Title
Content
Chameleon-3.6.2/src/chameleon/tests/outputs/066.pt0000644000076500000240000000006313131416610022274 0ustar mborchstaff00000000000000 Hello world! Chameleon-3.6.2/src/chameleon/tests/outputs/067.pt0000644000076500000240000000014213131416610022273 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/outputs/068.pt0000644000076500000240000000025413131416610022300 0ustar mborchstaff00000000000000 0 < 1 or 0 > 1 0 < 1 or 0 > 1 0 < 1 or 0 > 1 Chameleon-3.6.2/src/chameleon/tests/outputs/069-en.pt0000644000076500000240000000034413131416610022701 0ustar mborchstaff00000000000000 Title ('title' translation into 'en' with domain 'test')
Chameleon-3.6.2/src/chameleon/tests/outputs/069.pt0000644000076500000240000000026113131416610022277 0ustar mborchstaff00000000000000 Title
Chameleon-3.6.2/src/chameleon/tests/outputs/070-en.pt0000644000076500000240000000034413131416610022671 0ustar mborchstaff00000000000000 Title ('title' translation into 'en' with domain 'test')
Chameleon-3.6.2/src/chameleon/tests/outputs/070.pt0000644000076500000240000000026113131416610022267 0ustar mborchstaff00000000000000 Title
Chameleon-3.6.2/src/chameleon/tests/outputs/071.pt0000644000076500000240000000047013131416610022272 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/outputs/072.pt0000644000076500000240000000047413131416610022277 0ustar mborchstaff00000000000000
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
Chameleon-3.6.2/src/chameleon/tests/outputs/073.pt0000644000076500000240000000021113131416610022265 0ustar mborchstaff00000000000000 my title — my site Chameleon-3.6.2/src/chameleon/tests/outputs/074.pt0000644000076500000240000000023713131416610022276 0ustar mborchstaff00000000000000 my title — my site Chameleon-3.6.2/src/chameleon/tests/outputs/075.pt0000644000076500000240000000026013131416610022273 0ustar mborchstaff00000000000000 Master template
foo
Chameleon-3.6.2/src/chameleon/tests/outputs/076.pt0000644000076500000240000000031513131416610022275 0ustar mborchstaff00000000000000 Master template
bar
Chameleon-3.6.2/src/chameleon/tests/outputs/077-en.pt0000644000076500000240000000017013131416610022675 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/outputs/077.pt0000644000076500000240000000010513131416610022273 0ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/tests/outputs/078.pt0000644000076500000240000000043313131416610022300 0ustar mborchstaff00000000000000 1, 2, 3 Chameleon-3.6.2/src/chameleon/tests/outputs/079-en.pt0000644000076500000240000000107313131416610022702 0ustar mborchstaff00000000000000 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')
boo foo. ('boo foo.' translation into 'en')
bar. ('bar.' translation into 'en')
Chameleon-3.6.2/src/chameleon/tests/outputs/079.pt0000644000076500000240000000050113131416610022275 0ustar mborchstaff00000000000000 Welcome

Welcome

An edge case: ${. Site logo Site logo
boo foo.
bar.
Chameleon-3.6.2/src/chameleon/tests/outputs/080.pt0000644000076500000240000000002113131416610022262 0ustar mborchstaff00000000000000 Hello world Chameleon-3.6.2/src/chameleon/tests/outputs/081.pt0000644000076500000240000000006213131416610022270 0ustar mborchstaff00000000000000 Hello world! Chameleon-3.6.2/src/chameleon/tests/outputs/082.pt0000644000076500000240000000006213131416610022271 0ustar mborchstaff00000000000000 Hello world! Chameleon-3.6.2/src/chameleon/tests/outputs/083.pt0000644000076500000240000000027313131416610022276 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/outputs/084.pt0000644000076500000240000000016413131416610022276 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/outputs/085-en.pt0000644000076500000240000000044013131416610022674 0ustar mborchstaff00000000000000 Welcome

Welcome

Click here ('Click here' translation into 'en' with domain 'new') to continue. ('${click_here} to continue.' translation into 'en' with domain 'new')

Chameleon-3.6.2/src/chameleon/tests/outputs/085.pt0000644000076500000240000000024213131416610022274 0ustar mborchstaff00000000000000 Welcome

Welcome

Click here to continue.

Chameleon-3.6.2/src/chameleon/tests/outputs/086.pt0000644000076500000240000000041013131416610022272 0ustar mborchstaff00000000000000 Master template
Chameleon-3.6.2/src/chameleon/tests/outputs/087.pt0000644000076500000240000000032713131416610022302 0ustar mborchstaff00000000000000
  • 1
  • 2
  • 3
  • 5
  • 7
  • 9
Please input a number from the range 1, 2, 3, 4, 5, 6, 7, 8, 9.
41 + 1 = 42.
Chameleon-3.6.2/src/chameleon/tests/outputs/088.pt0000644000076500000240000000001013131416610022270 0ustar mborchstaff00000000000000a, b, c Chameleon-3.6.2/src/chameleon/tests/outputs/089.pt0000644000076500000240000000006213131416610022300 0ustar mborchstaff00000000000000 Hello world! Chameleon-3.6.2/src/chameleon/tests/outputs/090.pt0000644000076500000240000000041213131416610022267 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/outputs/091.pt0000644000076500000240000000004313131416610022270 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/outputs/101.html0000644000076500000240000000010313131416610022576 0ustar mborchstaff00000000000000



Hello world

Chameleon-3.6.2/src/chameleon/tests/outputs/102.html0000644000076500000240000000012413131416610022602 0ustar mborchstaff00000000000000

Hello world

Chameleon-3.6.2/src/chameleon/tests/outputs/103.html0000644000076500000240000000032013131416610022601 0ustar mborchstaff00000000000000 Chameleon-3.6.2/src/chameleon/tests/outputs/120-en.pt0000644000076500000240000000045513131416610022670 0ustar mborchstaff00000000000000
Hello world! ('Hello world!' translation into 'en')
Chameleon-3.6.2/src/chameleon/tests/outputs/120.pt0000644000076500000240000000020713131416610022263 0ustar mborchstaff00000000000000
Hello world!
Tab
Chameleon-3.6.2/src/chameleon/tests/outputs/121.pt0000644000076500000240000000007413131416610022266 0ustar mborchstaff00000000000000

Hello world!

Chameleon-3.6.2/src/chameleon/tests/outputs/122.pt0000644000076500000240000000025013131416610022263 0ustar mborchstaff00000000000000

Hello world!

Python is a programming language. Chameleon-3.6.2/src/chameleon/tests/outputs/123.pt0000644000076500000240000000011013131416610022257 0ustar mborchstaff00000000000000
Hello world!
Chameleon-3.6.2/src/chameleon/tests/outputs/124-en.pt0000644000076500000240000000025313131416610022670 0ustar mborchstaff00000000000000

Hello world! ('Hello world!' translation into 'fr')

It's a big world. ('It's a big world.' translation into 'en')

Chameleon-3.6.2/src/chameleon/tests/outputs/124.pt0000644000076500000240000000017713131416610022275 0ustar mborchstaff00000000000000

Hello world! ('Hello world!' translation into 'fr')

It's a big world.

Chameleon-3.6.2/src/chameleon/tests/outputs/125.pt0000644000076500000240000000021513131416610022267 0ustar mborchstaff00000000000000

h1

h2

Chameleon-3.6.2/src/chameleon/tests/outputs/126.pt0000644000076500000240000000016013131416610022267 0ustar mborchstaff00000000000000 & & & & Chameleon-3.6.2/src/chameleon/tests/outputs/238.pt0000644000076500000240000000004213131416610022272 0ustar mborchstaff00000000000000

macros['page']

Chameleon-3.6.2/src/chameleon/tests/outputs/greeting.pt0000644000076500000240000000003513131416610023564 0ustar mborchstaff00000000000000
Hello, undefined.
Chameleon-3.6.2/src/chameleon/tests/outputs/hello_world.pt0000644000076500000240000000006213131416610024272 0ustar mborchstaff00000000000000 Hello world! Chameleon-3.6.2/src/chameleon/tests/outputs/hello_world.txt0000644000076500000240000000001513131416610024464 0ustar mborchstaff00000000000000Hello world! Chameleon-3.6.2/src/chameleon/tests/outputs/multinode-en.pt0000644000076500000240000000016713322447520024374 0ustar mborchstaff00000000000000 Foo Message ('message' translation into 'en') ('Foo ${message}' translation into 'en') Chameleon-3.6.2/src/chameleon/tests/outputs/multinode.pt0000644000076500000240000000005413322447520023767 0ustar mborchstaff00000000000000 Foo Message Chameleon-3.6.2/src/chameleon/tests/test_doctests.py0000644000076500000240000000177313131416610023143 0ustar mborchstaff00000000000000import unittest import doctest OPTIONFLAGS = (doctest.ELLIPSIS | doctest.REPORT_ONLY_FIRST_FAILURE) class DoctestCase(unittest.TestCase): def __new__(self, test): return getattr(self, test)() @classmethod def test_tal(cls): from chameleon import tal return doctest.DocTestSuite( tal, optionflags=OPTIONFLAGS) @classmethod def test_tales(cls): from chameleon import tales return doctest.DocTestSuite( tales, optionflags=OPTIONFLAGS) @classmethod def test_utils(cls): from chameleon import utils return doctest.DocTestSuite( utils, optionflags=OPTIONFLAGS) @classmethod def test_exc(cls): from chameleon import exc return doctest.DocTestSuite( exc, optionflags=OPTIONFLAGS) @classmethod def test_compiler(cls): from chameleon import compiler return doctest.DocTestSuite( compiler, optionflags=OPTIONFLAGS) Chameleon-3.6.2/src/chameleon/tests/test_exc.py0000644000076500000240000000100413131416610022055 0ustar mborchstaff00000000000000from unittest import TestCase class TestTemplateError(TestCase): def test_keep_token_location_info(self): # tokens should not lose information when passed to a TemplateError from chameleon import exc, tokenize, utils token = tokenize.Token('stuff', 5, 'more\nstuff', 'mystuff.txt') error = exc.TemplateError('message', token) s = str(error) self.assertTrue( '- Location: (line 2: col 0)' in s, 'No location data found\n%s' % s) Chameleon-3.6.2/src/chameleon/tests/test_loader.py0000644000076500000240000000723713131416610022562 0ustar mborchstaff00000000000000import 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 ModuleLoadTests(unittest.TestCase): def _makeOne(self, *args, **kwargs): from chameleon.loader import ModuleLoader return ModuleLoader(*args, **kwargs) def test_build(self): import tempfile path = tempfile.mkdtemp() loader = self._makeOne(path) source = "def function(): return %r" % "\xc3\xa6\xc3\xb8\xc3\xa5" try: source = source.decode('utf-8') except AttributeError: import sys self.assertTrue(sys.version_info[0] > 2) module = loader.build(source, "test.xml") result1 = module['function']() d = {} code = compile(source, 'test.py', 'exec') exec(code, d) result2 = d['function']() self.assertEqual(result1, result2) import os self.assertTrue("test.py" in os.listdir(path)) import shutil shutil.rmtree(path) 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-3.6.2/src/chameleon/tests/test_parser.py0000644000076500000240000000652313450341714022613 0ustar mborchstaff00000000000000from __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_comment_double_hyphen_parsing(self): from ..parser import match_double_hyphen self.assertFalse(match_double_hyphen.match('->')) self.assertFalse(match_double_hyphen.match('-->')) self.assertFalse(match_double_hyphen.match('--->')) self.assertFalse(match_double_hyphen.match('---->')) self.assertFalse(match_double_hyphen.match('- >')) self.assertTrue(match_double_hyphen.match('-- >')) 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-3.6.2/src/chameleon/tests/test_sniffing.py0000644000076500000240000000763613131416610023122 0ustar mborchstaff00000000000000from __future__ import with_statement import os import unittest import tempfile import shutil from chameleon.utils import unicode_string from chameleon.utils import encode_string class TypeSniffingTestCase(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp(prefix='chameleon-tests') def tearDown(self): shutil.rmtree(self.tempdir) def _get_temporary_file(self): filename = os.path.join(self.tempdir, 'template.py') assert not os.path.exists(filename) f = open(filename, 'w') f.flush() f.close() return filename def get_template(self, text): fn = self._get_temporary_file() with open(fn, 'wb') as tmpfile: tmpfile.write(text) from chameleon.template import BaseTemplateFile class DummyTemplateFile(BaseTemplateFile): def cook(self, body): self.body = body template = DummyTemplateFile(fn) template.cook_check() return template def check_content_type(self, text, expected_type): from chameleon.utils import read_bytes content_type = read_bytes(text, 'ascii')[2] self.assertEqual(content_type, expected_type) def test_xml_encoding(self): from chameleon.utils import xml_prefixes document1 = unicode_string( "" ) document2 = unicode_string( "" ) for bom, encoding in xml_prefixes: try: "".encode(encoding) except LookupError: # System does not support this encoding continue self.check_content_type(document1.encode(encoding), "text/xml") self.check_content_type(document2.encode(encoding), "text/xml") HTML_PUBLIC_ID = "-//W3C//DTD HTML 4.01 Transitional//EN" HTML_SYSTEM_ID = "http://www.w3.org/TR/html4/loose.dtd" # Couldn't find the code that handles this... yet. # def test_sniffer_html_ascii(self): # self.check_content_type( # "" # % self.HTML_SYSTEM_ID, # "text/html") # self.check_content_type( # "sample document", # "text/html") # TODO: This reflects a case that simply isn't handled by the # sniffer; there are many, but it gets it right more often than # before. def donttest_sniffer_xml_simple(self): self.check_content_type("", "text/xml") def test_html_default_encoding(self): body = encode_string( '' \ '\xc3\x90\xc2\xa2\xc3\x90\xc2\xb5' \ '\xc3\x91\xc2\x81\xc3\x91\xc2\x82' \ '') template = self.get_template(body) self.assertEqual(template.body, body.decode('utf-8')) def test_html_encoding_by_meta(self): body = encode_string( '' \ '\xc3\x92\xc3\xa5\xc3\xb1\xc3\xb2' \ '' \ "") template = self.get_template(body) self.assertEqual(template.body, body.decode('windows-1251')) def test_xhtml(self): body = encode_string( '' \ '\xc3\x92\xc3\xa5\xc3\xb1\xc3\xb2' \ '' \ "") template = self.get_template(body) self.assertEqual(template.body, body.decode('windows-1251')) def test_suite(): return unittest.makeSuite(TypeSniffingTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") Chameleon-3.6.2/src/chameleon/tests/test_templates.py0000644000076500000240000006271613432732464023331 0ustar mborchstaff00000000000000# -*- coding: utf-8 -*- from __future__ import with_statement import re import os import sys import shutil import tempfile from functools import wraps from functools import partial try: from unittest2 import TestCase except ImportError: from unittest import TestCase try: RecursionError except NameError: RecursionError = RuntimeError from chameleon.utils import byte_string from chameleon.exc import RenderError class Message(object): def __str__(self): return "message" class ImportTestCase(TestCase): def test_pagetemplates(self): from chameleon import PageTemplate from chameleon import PageTemplateFile from chameleon import PageTemplateLoader def test_pagetexttemplates(self): from chameleon import PageTextTemplate from chameleon import PageTextTemplateFile class TemplateFileTestCase(TestCase): @property def _class(self): from chameleon.template import BaseTemplateFile class TestTemplateFile(BaseTemplateFile): cook_count = 0 def cook(self, body): self.cook_count += 1 self._cooked = True return TestTemplateFile def setUp(self): self.tempdir = tempfile.mkdtemp(prefix='chameleon-tests') def tearDown(self): shutil.rmtree(self.tempdir) def _get_temporary_file(self): filename = os.path.join(self.tempdir, 'template.py') assert not os.path.exists(filename) f = open(filename, 'w') f.flush() f.close() return filename def test_cook_check(self): fn = self._get_temporary_file() template = self._class(fn) template.cook_check() self.assertEqual(template.cook_count, 1) def test_auto_reload(self): fn = self._get_temporary_file() # set time in past os.utime(fn, (0, 0)) template = self._class(fn, auto_reload=True) template.cook_check() # a second cook check makes no difference template.cook_check() self.assertEqual(template.cook_count, 1) # set current time on file os.utime(fn, None) # file is reloaded template.cook_check() self.assertEqual(template.cook_count, 2) def test_relative_is_expanded_to_cwd(self): template = self._class("___does_not_exist___") try: template.cook_check() except IOError: exc = sys.exc_info()[1] self.assertEqual( os.getcwd(), os.path.dirname(exc.filename) ) else: self.fail("Expected OSError.") class RenderTestCase(TestCase): root = os.path.dirname(__file__) def find_files(self, ext): inputs = os.path.join(self.root, "inputs") outputs = os.path.join(self.root, "outputs") for filename in sorted(os.listdir(inputs)): name, extension = os.path.splitext(filename) if extension != ext: continue path = os.path.join(inputs, filename) # if there's no output file, treat document as static and # expect intput equal to output import glob globbed = tuple(glob.iglob(os.path.join( outputs, "%s*%s" % (name.split('-', 1)[0], ext)))) if not globbed: self.fail("Missing output for: %s." % name) for output in globbed: name, ext = os.path.splitext(output) basename = os.path.basename(name) if '-' in basename: language = basename.split('-')[1] else: language = None yield path, output, language class ZopePageTemplatesTest(RenderTestCase): @property def from_string(body): from ..zpt.template import PageTemplate return partial(PageTemplate, keep_source=True) @property def from_file(body): from ..zpt.template import PageTemplateFile return partial(PageTemplateFile, keep_source=True) def template(body): def decorator(func): @wraps(func) def wrapper(self): template = self.from_string(body) return func(self, template) return wrapper return decorator def error(body): def decorator(func): @wraps(func) def wrapper(self): from chameleon.exc import TemplateError try: template = self.from_string(body) except TemplateError: exc = sys.exc_info()[1] return func(self, body, exc) else: self.fail("Expected exception.") return wrapper return decorator def test_syntax_error_in_strict_mode(self): from chameleon.exc import ExpressionError self.assertRaises( ExpressionError, self.from_string, """""", strict=True ) def test_syntax_error_in_non_strict_mode(self): from chameleon.exc import ExpressionError body = """""" template = self.from_string(body, strict=False) try: template() except ExpressionError: exc = sys.exc_info()[1] self.assertTrue(body[exc.offset:].startswith('bad ///')) else: self.fail("Expected exception") def test_exists_error_leak(self): body = '''\ ''' template = self.from_string(body, strict=False) try: template() except RenderError: exc = sys.exc_info()[1] self.assertNotIn('var_does_not_exists', str(exc)) else: self.fail("Expected exception") def test_sys_exc_info_is_clear_after_pipe(self): body = """
""" template = self.from_string(body, strict=False) got = template.render(error=sys.exc_info) self.assertTrue('(None, None, None)' in got) def test_render_macro_include_subtemplate_containing_error(self): macro_inner = self.from_string('''''') macro_wrap = self.from_string('''''') template = self.from_string( ''' foo ''') try: result = template(macro=macro_wrap, macro_inner=macro_inner) except RenderError: exc = sys.exc_info()[1] self.assertTrue(isinstance(exc, KeyError)) self.assertIn(''''key-does-not-exist' - Expression: "dict()['key-does-not-exist']" - Filename: - Location: (line 1: col 29) - Expression: "macro_inner()" - Filename: - Location: (line 1: col 18) - Expression: "macro" - Filename: - Location: (line 4: col 38) ''', str(exc)) else: self.fail("Expected exception") def test_render_error_macro_include(self): body = """""" template = self.from_string(body, strict=False) try: template() except RenderError: exc = sys.exc_info()[1] self.assertTrue(isinstance(exc, AttributeError)) self.assertIn('bad', str(exc)) else: self.fail("Expected exception") @error("""""") def test_attributes_on_tal_tag_fails(self, body, exc): self.assertTrue(body[exc.offset:].startswith('dummy')) @error("""""") def test_i18n_attributes_with_non_identifiers(self, body, exc): self.assertTrue(body[exc.offset:].startswith('foo,')) @error("""""") def test_repeat_syntax_error_message(self, body, exc): self.assertTrue(body[exc.offset:].startswith('key,value')) @error('''

''') def test_repeat_i18n_name_error(self, body, exc): self.assertTrue(body[exc.offset:].startswith('repeat'), body[exc.offset:]) @error(''' ''') def test_i18n_name_not_in_translation_error(self, body, exc): self.assertTrue(body[exc.offset:].startswith('not_in_translation')) def test_encoded(self): filename = '074-encoded-template.pt' with open(os.path.join(self.root, 'inputs', filename), 'rb') as f: body = f.read() self.from_string(body) def test_utf8_encoded(self): filename = '073-utf8-encoded.pt' with open(os.path.join(self.root, 'inputs', filename), 'rb') as f: body = f.read() self.from_string(body) def test_recursion_error(self): template = self.from_string( '
' ) self.assertRaises(RecursionError, template) try: template() except RecursionError as exc: self.assertFalse(isinstance(exc, RenderError)) def test_unicode_decode_error(self): template = self.from_file( os.path.join(self.root, 'inputs', 'greeting.pt') ) string = native = "the artist formerly known as ƤŗíƞĆě" try: string = string.decode('utf-8') except AttributeError: pass class name: @staticmethod def __html__(): # This raises a decoding exception string.encode('utf-8').decode('ascii') self.fail("Expected exception raised.") try: template(name=name) except UnicodeDecodeError: exc = sys.exc_info()[1] formatted = str(exc) # There's a marker under the expression that has the # unicode decode error self.assertTrue('^^^^^' in formatted) self.assertTrue(native in formatted) else: self.fail("expected error") def test_custom_encoding_for_str_or_bytes_in_content(self): string = '
ТеÑÑ‚${text}
' try: string = string.decode('utf-8') except AttributeError: pass template = self.from_string(string, encoding="windows-1251") text = 'ТеÑÑ‚' try: text = text.decode('utf-8') except AttributeError: pass rendered = template(text=text.encode('windows-1251')) self.assertEqual( rendered, string.replace('${text}', text) ) def test_custom_encoding_for_str_or_bytes_in_attributes(self): string = '' try: string = string.decode('utf-8') except AttributeError: pass template = self.from_string(string, encoding="windows-1251") text = 'ТеÑÑ‚' try: text = text.decode('utf-8') except AttributeError: pass rendered = template(text=text.encode('windows-1251')) self.assertEqual( rendered, string.replace('${text}', text) ) def test_null_translate_function(self): template = self.from_string('${test}', translate=None) rendered = template(test=object()) self.assertTrue('object' in rendered) def test_on_error_handler(self): exc = [] handler = exc.append template = self.from_string( '${test}', on_error_handler=handler ) rendered = template() self.assertEqual(len(exc), 1) self.assertEqual(exc[0].__class__.__name__, "NameError") def test_object_substitution_coerce_to_str(self): template = self.from_string('${test}', translate=None) class dummy(object): def __repr__(inst): self.fail("call not expected") def __str__(inst): return '' rendered = template(test=dummy()) self.assertEqual(rendered, '<dummy>') def test_repr(self): template = self.from_file( os.path.join(self.root, 'inputs', 'hello_world.pt') ) self.assertTrue(template.filename in repr(template)) def test_underscore_variable(self): template = self.from_string( "
${_dummy}
" ) self.assertTrue(template(), "
foo
") def test_trim_attribute_space(self): document = '''
''' result1 = self.from_string( document)() result2 = self.from_string( document, trim_attribute_space=True)() self.assertEqual(result1.count(" "), 49) self.assertEqual(result2.count(" "), 4) self.assertTrue(" />" in result1) self.assertTrue(" />" in result2) def test_exception(self): from traceback import format_exception_only template = self.from_string( "
${dummy}
" ) try: template() except Exception as exc: self.assertIn(RenderError, type(exc).__bases__) exc = sys.exc_info()[1] formatted = str(exc) self.assertFalse('NameError:' in formatted) self.assertTrue('foo' in formatted) self.assertTrue('(line 1: col 23)' in formatted) formatted_exc = "\n".join(format_exception_only(type(exc), exc)) self.assertTrue('NameError: foo' in formatted_exc) else: self.fail("expected error") def test_create_formatted_exception(self): from chameleon.utils import create_formatted_exception exc = create_formatted_exception(NameError('foo'), NameError, str) self.assertEqual(exc.args, ('foo', )) class MyNameError(NameError): def __init__(self, boo): NameError.__init__(self, boo) self.bar = boo exc = create_formatted_exception(MyNameError('foo'), MyNameError, str) self.assertEqual(exc.args, ('foo', )) self.assertEqual(exc.bar, 'foo') def test_create_formatted_exception_no_subclass(self): from chameleon.utils import create_formatted_exception class DifficultMetaClass(type): def __init__(self, class_name, bases, namespace): if not bases == (BaseException, ): raise TypeError(bases) Difficult = DifficultMetaClass('Difficult', (BaseException, ), {'args': ()}) exc = create_formatted_exception(Difficult(), Difficult, str) self.assertEqual(exc.args, ()) def test_error_handler_makes_safe_copy(self): calls = [] class TestException(Exception): def __init__(self, *args, **kwargs): calls.append((args, kwargs)) def _render(stream, econtext, rcontext): exc = TestException('foo', bar='baz') rcontext['__error__'] = ('expression', 1, 42, 'test.pt', exc), raise exc template = self.from_string("") template._render = _render try: template() except TestException: self.assertEqual(calls, [(('foo', ), {'bar': 'baz'})]) exc = sys.exc_info()[1] formatted = str(exc) self.assertTrue('TestException' in formatted) self.assertTrue('"expression"' in formatted) self.assertTrue('(line 1: col 42)' in formatted) else: self.fail("unexpected error") def test_double_underscore_variable(self): from chameleon.exc import TranslationError self.assertRaises( TranslationError, self.from_string, "
${__dummy}
", ) def test_compiler_internals_are_disallowed(self): from chameleon.compiler import COMPILER_INTERNALS_OR_DISALLOWED from chameleon.exc import TranslationError for name in COMPILER_INTERNALS_OR_DISALLOWED: body = "${%s}" % (name, name) self.assertRaises(TranslationError, self.from_string, body) def test_simple_translate_mapping(self): template = self.from_string( '
' 'foo' '
') self.assertEqual(template(), '
foo
') def test_translate_is_not_an_internal(self): macro = self.from_string('bar') template = self.from_string( ''' foo ''') result = template(macro=macro) self.assertTrue('foo' in result) self.assertTrue('foo' in result) def test_literal_false(self): template = self.from_string( '' '' '' '', literal_false=True, ) self.assertEqual( template(), '' '' '' '', template.source ) def test_boolean_attributes(self): template = self.from_string( '' '' '' '' '' '', boolean_attributes=set(['checked']) ) self.assertEqual( template(), '' '' '' '' '' '', template.source ) def test_default_debug_flag(self): from chameleon.config import DEBUG_MODE template = self.from_file( os.path.join(self.root, 'inputs', 'hello_world.pt'), ) self.assertEqual(template.debug, DEBUG_MODE) self.assertTrue('debug' not in template.__dict__) def test_debug_flag_on_string(self): from chameleon.loader import ModuleLoader with open(os.path.join(self.root, 'inputs', 'hello_world.pt')) as f: source = f.read() template = self.from_string(source, debug=True) self.assertTrue(template.debug) self.assertTrue(isinstance(template.loader, ModuleLoader)) def test_debug_flag_on_file(self): from chameleon.loader import ModuleLoader template = self.from_file( os.path.join(self.root, 'inputs', 'hello_world.pt'), debug=True, ) self.assertTrue(template.debug) self.assertTrue(isinstance(template.loader, ModuleLoader)) def test_tag_mismatch(self): from chameleon.exc import ParseError try: self.from_string("""
""") except ParseError: exc = sys.exc_info()[1] self.assertTrue("" in str(exc)) else: self.fail("Expected error.") class ZopeTemplatesTestSuite(RenderTestCase): def setUp(self): self.temp_path = temp_path = tempfile.mkdtemp() @self.addCleanup def cleanup(path=temp_path): shutil.rmtree(path) def test_pt_files(self): from ..zpt.template import PageTemplateFile class Literal(object): def __init__(self, s): self.s = s def __html__(self): return self.s def __str__(self): raise RuntimeError( "%r is a literal." % self.s) from chameleon.loader import TemplateLoader loader = TemplateLoader(os.path.join(self.root, "inputs")) self.execute( ".pt", PageTemplateFile, literal=Literal("
Hello world!
"), content="
Hello world!
", message=Message(), load=loader.bind(PageTemplateFile), ) def test_txt_files(self): from ..zpt.template import PageTextTemplateFile self.execute(".txt", PageTextTemplateFile) def execute(self, ext, factory, **kwargs): def translate(msgid, domain=None, mapping=None, context=None, target_language=None, default=None): if default is None: default = str(msgid) if isinstance(msgid, Message): default = "Message" if mapping: default = re.sub(r'\${([a-z_]+)}', r'%(\1)s', default) % \ mapping if target_language is None: return default if domain is None: with_domain = "" else: with_domain = " with domain '%s'" % domain if context is None: with_context = "" else: with_context = ", context '%s'" % context stripped = default.rstrip('\n ') return "%s ('%s' translation into '%s'%s%s)%s" % ( stripped, msgid, target_language, with_domain, with_context, default[len(stripped):] ) for input_path, output_path, language in self.find_files(ext): # Make friendly title so we can locate the generated # source when debugging self.shortDescription = lambda: input_path # When input path contains the string 'implicit-i18n', we # enable "implicit translation". implicit_i18n = 'implicit-i18n' in input_path implicit_i18n_attrs = ("alt", "title") if implicit_i18n else () enable_data_attributes = 'data-attributes' in input_path template = factory( input_path, keep_source=True, strict=False, implicit_i18n_translate=implicit_i18n, implicit_i18n_attributes=implicit_i18n_attrs, enable_data_attributes=enable_data_attributes, ) params = kwargs.copy() params.update({ 'translate': translate, 'target_language': language, }) template.cook_check() try: got = template.render(**params) except: import traceback e = traceback.format_exc() self.fail("%s\n\n Example source:\n\n%s" % (e, "\n".join( ["%#03.d%s" % (lineno + 1, line and " " + line or "") for (lineno, line) in enumerate(template.source.split( '\n'))]))) if isinstance(got, byte_string): got = got.decode('utf-8') from doctest import OutputChecker checker = OutputChecker() if not os.path.exists(output_path): output = template.body else: with open(output_path, 'rb') as f: output = f.read() from chameleon.utils import read_xml_encoding from chameleon.utils import detect_encoding if template.content_type == 'text/xml': encoding = read_xml_encoding(output) or \ template.default_encoding else: content_type, encoding = detect_encoding( output, template.default_encoding) # Newline normalization across platforms want = '\n'.join(output.decode(encoding).splitlines()) got = '\n'.join(got.splitlines()) if checker.check_output(want, got, 0) is False: from doctest import Example example = Example(input_path, want) diff = checker.output_difference( example, got, 0) self.fail("(%s) - \n%s\n\nCode:\n%s" % ( input_path, diff.rstrip('\n'), template.source.encode('utf-8'))) Chameleon-3.6.2/src/chameleon/tests/test_tokenizer.py0000644000076500000240000000270213131416610023316 0ustar mborchstaff00000000000000import sys from unittest import TestCase class TokenizerTest(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('.xml'): continue f = open(os.path.join(path, filename), 'rb') source = f.read() f.close() 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 try: tokens = iter_xml(want) got = "".join(tokens) except: self.fail(traceback.format_exc()) 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)) def test_token(self): from chameleon.tokenize import Token token = Token("abc", 1) self.assertTrue(isinstance(token[1:], Token)) self.assertEqual(token[1:].pos, 2) Chameleon-3.6.2/src/chameleon/tokenize.py0000644000076500000240000001030713426041304020734 0ustar mborchstaff00000000000000# http://code.activestate.com/recipes/65125-xml-lexing-shallow-parsing/ # by Paul Prescod # licensed under the PSF License # # modified to capture all non-overlapping parts of tokens import re try: str = unicode except NameError: pass class recollector: def __init__(self): self.res = {} def add(self, name, reg ): re.compile(reg) # check that it is valid self.res[name] = reg % self.res collector = recollector() a = collector.add a("TextSE", "[^<]+") a("UntilHyphen", "[^-]*-") a("Until2Hyphens", "%(UntilHyphen)s(?:[^-]%(UntilHyphen)s)*-") a("CommentCE", "%(Until2Hyphens)s>?") a("UntilRSBs", "[^\\]]*](?:[^\\]]+])*]+") a("CDATA_CE", "%(UntilRSBs)s(?:[^\\]>]%(UntilRSBs)s)*>" ) a("S", "[ \\n\\t\\r]+") a("Simple", "[^\"'>/]+") a("NameStrt", "[A-Za-z_:@]|[^\\x00-\\x7F]") a("NameChar", "[A-Za-z0-9_:.-]|[^\\x00-\\x7F]") a("Name", "(?:%(NameStrt)s)(?:%(NameChar)s)*") a("QuoteSE", "\"[^\"]*\"|'[^']*'") a("DT_IdentSE" , "%(S)s%(Name)s(?:%(S)s(?:%(Name)s|%(QuoteSE)s))*" ) a("MarkupDeclCE" , "(?:[^\\]\"'><]+|%(QuoteSE)s)*>" ) a("S1", "[\\n\\r\\t ]") a("UntilQMs", "[^?]*\\?+") a("PI_Tail" , "\\?>|%(S1)s%(UntilQMs)s(?:[^>?]%(UntilQMs)s)*>" ) a("DT_ItemSE", "<(?:!(?:--%(Until2Hyphens)s>|[^-]%(MarkupDeclCE)s)|" "\\?%(Name)s(?:%(PI_Tail)s))|%%%(Name)s;|%(S)s" ) a("DocTypeCE" , "%(DT_IdentSE)s(?:%(S)s)?(?:\\[(?:%(DT_ItemSE)s)*](?:%(S)s)?)?>?" ) a("DeclCE", "--(?:%(CommentCE)s)?|\\[CDATA\\[(?:%(CDATA_CE)s)?|" "DOCTYPE(?:%(DocTypeCE)s)?") a("PI_CE", "%(Name)s(?:%(PI_Tail)s)?") a("EndTagCE", "%(Name)s(?:%(S)s)?>?") a("AttValSE", r"\"[^\"]*\"|'[^']*'|[^\s=<>`]+") a("ElemTagCE", "(%(Name)s)(?:(%(S)s)(%(Name)s)(((?:%(S)s)?=(?:%(S)s)?)" "(?:%(AttValSE)s|%(Simple)s)|(?!(?:%(S)s)?=)))*(?:%(S)s)?(/?>)?") a("MarkupSPE", "<(?:!(?:%(DeclCE)s)?|" "\\?(?:%(PI_CE)s)?|/(?:%(EndTagCE)s)?|(?:%(ElemTagCE)s)?)") a("XML_SPE", "%(TextSE)s|%(MarkupSPE)s") a("XML_MARKUP_ONLY_SPE", "%(MarkupSPE)s") a("ElemTagSPE", "<|%(Name)s") re_xml_spe = re.compile(collector.res['XML_SPE']) re_markup_only_spe = re.compile(collector.res['XML_MARKUP_ONLY_SPE']) def iter_xml(body, filename=None): for match in re_xml_spe.finditer(body): string = match.group() pos = match.start() yield Token(string, pos, body, filename) def iter_text(body, filename=None): yield Token(body, 0, body, filename) class Token(str): __slots__ = "pos", "source", "filename" def __new__(cls, string, pos=0, source=None, filename=None): inst = str.__new__(cls, string) inst.pos = pos inst.source = source inst.filename = filename or "" return inst def __getslice__(self, i, j): slice = str.__getslice__(self, i, j) return Token(slice, self.pos + i, self.source, self.filename) def __getitem__(self, index): s = str.__getitem__(self, index) if isinstance(index, slice): return Token( s, self.pos + (index.start or 0), self.source, self.filename) return s def __add__(self, other): if other is None: return self return Token( str.__add__(self, other), self.pos, self.source, self.filename) def __eq__(self, other): return str.__eq__(self, other) def __hash__(self): return str.__hash__(self) def replace(self, *args): s = str.replace(self, *args) return Token(s, self.pos, self.source, self.filename) def split(self, *args): l = str.split(self, *args) pos = self.pos for i, s in enumerate(l): l[i] = Token(s, pos, self.source, self.filename) pos += len(s) return l def strip(self, *args): return self.lstrip(*args).rstrip(*args) def lstrip(self, *args): s = str.lstrip(self, *args) return Token( s, self.pos + len(self) - len(s), self.source, self.filename) def rstrip(self, *args): s = str.rstrip(self, *args) return Token(s, self.pos, self.source, self.filename) @property def location(self): if self.source is None: return 0, self.pos body = self.source[:self.pos] line = body.count('\n') return line + 1, self.pos - body.rfind('\n', 0) - 1 Chameleon-3.6.2/src/chameleon/utils.py0000644000076500000240000002400613274234474020262 0ustar mborchstaff00000000000000import os import re import sys import codecs import logging from copy import copy version = sys.version_info[:3] try: import ast as _ast except ImportError: from chameleon import ast25 as _ast class ASTProxy(object): aliases = { # Python 3.3 'TryExcept': 'Try', 'TryFinally': 'Try', } def __getattr__(self, name): if name.startswith('__'): raise AttributeError(name) return _ast.__dict__.get(name) or getattr(_ast, self.aliases[name]) ast = ASTProxy() log = logging.getLogger('chameleon.utils') # Python 2 if version < (3, 0, 0): import htmlentitydefs import __builtin__ as builtins from .py25 import raise_with_traceback chr = unichr native_string = str decode_string = unicode encode_string = str unicode_string = unicode string_type = basestring byte_string = str def safe_native(s, encoding='utf-8'): if not isinstance(s, unicode): s = decode_string(s, encoding, 'replace') return s.encode(encoding) # Python 3 else: from html import entities as htmlentitydefs import builtins byte_string = bytes string_type = str native_string = str decode_string = bytes.decode encode_string = lambda s: bytes(s, 'utf-8') unicode_string = str def safe_native(s, encoding='utf-8'): if not isinstance(s, str): s = decode_string(s, encoding, 'replace') return s def raise_with_traceback(exc, tb): exc.__traceback__ = tb raise exc def text_(s, encoding='latin-1', errors='strict'): """ If ``s`` is an instance of ``byte_string``, return ``s.decode(encoding, errors)``, otherwise return ``s``""" if isinstance(s, byte_string): return s.decode(encoding, errors) return s entity_re = re.compile(r'&(#?)(x?)(\d{1,5}|\w{1,8});') module_cache = {} xml_prefixes = ( (codecs.BOM_UTF8, 'utf-8-sig'), (codecs.BOM_UTF16_BE, 'utf-16-be'), (codecs.BOM_UTF16_LE, 'utf-16-le'), (codecs.BOM_UTF16, 'utf-16'), (codecs.BOM_UTF32_BE, 'utf-32-be'), (codecs.BOM_UTF32_LE, 'utf-32-le'), (codecs.BOM_UTF32, 'utf-32'), ) def _has_encoding(encoding): try: "".encode(encoding) except LookupError: return False else: return True # Precomputed prefix table _xml_prefixes = tuple( (bom, str('\s*', re.IGNORECASE ) RE_ENCODING = re.compile( r'encoding\s*=\s*(?:"|\')(?P[\w\-]+)(?:"|\')'.encode('ascii'), re.IGNORECASE ) def read_encoded(data): return read_bytes(data, "utf-8")[0] def read_bytes(body, default_encoding): for bom, prefix, encoding in _xml_prefixes: if body.startswith(bom): document = body.decode(encoding) return document, encoding, \ "text/xml" if document.startswith(">> mangle('hello_world.pt') 'hello_world' >>> mangle('foo.bar.baz.pt') 'foo_bar_baz' >>> mangle('foo-bar-baz.pt') 'foo_bar_baz' """ base, ext = os.path.splitext(filename) return base.replace('.', '_').replace('-', '_') def char2entity(c): cp = ord(c) name = htmlentitydefs.codepoint2name.get(cp) return '&%s;' % name if name is not None else '&#%d;' % cp def substitute_entity(match, n2cp=htmlentitydefs.name2codepoint): ent = match.group(3) if match.group(1) == "#": if match.group(2) == '': return chr(int(ent)) elif match.group(2) == 'x': return chr(int('0x' + ent, 16)) else: cp = n2cp.get(ent) if cp: return chr(cp) else: return match.group() def create_formatted_exception(exc, cls, formatter, base=Exception): try: try: new = type(cls.__name__, (cls, base), { '__str__': formatter, '_original__str__': exc.__str__, '__new__': BaseException.__new__, '__module__': cls.__module__, }) except TypeError: new = cls try: inst = BaseException.__new__(new) except TypeError: inst = cls.__new__(new) BaseException.__init__(inst, *exc.args) inst.__dict__ = exc.__dict__ return inst except ValueError: name = type(exc).__name__ log.warn("Unable to copy exception of type '%s'." % name) raise TypeError(exc) def unescape(string): for name in ('lt', 'gt', 'quot'): cp = htmlentitydefs.name2codepoint[name] string = string.replace('&%s;' % name, chr(cp)) return string _concat = unicode_string("").join def join(stream): """Concatenate stream. >>> print(join(('Hello', ' ', 'world'))) Hello world >>> join(('Hello', 0)) Traceback (most recent call last): ... TypeError: ... expected ... """ try: return _concat(stream) except: # Loop through stream and coerce each element into unicode; # this should raise an exception for element in stream: unicode_string(element) # In case it didn't, re-raise the original exception raise def decode_htmlentities(string): """ >>> native_string(decode_htmlentities('&amp;')) '&' """ decoded = entity_re.subn(substitute_entity, string)[0] # preserve input token data return string.replace(string, decoded) # Taken from zope.dottedname def _resolve_dotted(name, module=None): name = name.split('.') if not name[0]: if module is None: raise ValueError("relative name without base module") module = module.split('.') name.pop(0) while not name[0]: module.pop() name.pop(0) name = module + name used = name.pop(0) found = __import__(used) for n in name: used += '.' + n try: found = getattr(found, n) except AttributeError: __import__(used) found = getattr(found, n) return found def resolve_dotted(dotted): if not dotted in module_cache: resolved = _resolve_dotted(dotted) module_cache[dotted] = resolved return module_cache[dotted] def limit_string(s, max_length=53): if len(s) > max_length: return s[:max_length - 3] + '...' return s def format_kwargs(kwargs): items = [] for name, value in kwargs.items(): if isinstance(value, string_type): short = limit_string(value) items.append((name, short.replace('\n', '\\n'))) elif isinstance(value, (int, float)): items.append((name, value)) elif isinstance(value, dict): items.append((name, '{...} (%d)' % len(value))) else: items.append((name, "<%s %s at %s>" % ( type(value).__name__, getattr(value, '__name__', "-"), hex(abs(id(value)))))) return ["%s: %s" % item for item in items] class callablestr(str): __slots__ = () def __call__(self): return self class callableint(int): __slots__ = () def __call__(self): return self class descriptorstr(object): __slots__ = "function", "__name__" def __init__(self, function): self.function = function self.__name__ = function.__name__ def __get__(self, context, cls): return callablestr(self.function(context)) class descriptorint(object): __slots__ = "function", "__name__" def __init__(self, function): self.function = function self.__name__ = function.__name__ def __get__(self, context, cls): return callableint(self.function(context)) class DebuggingOutputStream(list): def append(self, value): if not isinstance(value, string_type): raise TypeError(value) unicode_string(value) list.append(self, value) class Scope(dict): set_local = setLocal = dict.__setitem__ __slots__ = "set_global", def __new__(cls, *args): inst = dict.__new__(cls, *args) inst.set_global = inst.__setitem__ return inst def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: raise NameError(key) @property def vars(self): return self def copy(self): inst = Scope(self) inst.set_global = self.set_global return inst class ListDictProxy(object): def __init__(self, l): self._l = l def get(self, key): return self._l[-1].get(key) class Markup(unicode_string): """Wraps a string to always render as structure. >>> Markup('
') s'
' """ def __html__(self): return unicode_string(self) def __repr__(self): return "s'%s'" % self Chameleon-3.6.2/src/chameleon/zpt/0000755000076500000240000000000013503370500017345 5ustar mborchstaff00000000000000Chameleon-3.6.2/src/chameleon/zpt/__init__.py0000644000076500000240000000000213131416610021446 0ustar mborchstaff00000000000000# Chameleon-3.6.2/src/chameleon/zpt/loader.py0000644000076500000240000000151213131416610021164 0ustar mborchstaff00000000000000from chameleon.loader import TemplateLoader as BaseLoader from chameleon.zpt import template class TemplateLoader(BaseLoader): formats = { "xml": template.PageTemplateFile, "text": template.PageTextTemplateFile, } default_format = "xml" def __init__(self, *args, **kwargs): formats = kwargs.pop('formats', None) if formats is not None: self.formats = formats super(TemplateLoader, self).__init__(*args, **kwargs) def load(self, filename, format=None): """Load and return a template file. The format parameter determines will parse the file. Valid options are `xml` and `text`. """ cls = self.formats[format or self.default_format] return super(TemplateLoader, self).load(filename, cls) __getitem__ = load Chameleon-3.6.2/src/chameleon/zpt/program.py0000644000076500000240000006462013426041304021377 0ustar mborchstaff00000000000000import re try: import ast except ImportError: from chameleon import ast25 as ast try: str = unicode except NameError: long = int from functools import partial from copy import copy 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() re_trim = re.compile(r'($\s+|\s+^)', re.MULTILINE) EMPTY_DICT = Static(ast.Dict(keys=[], values=[])) 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 ) def convert_data_attributes(ns_attrs, attrs, namespaces): d = 0 for i, attr in list(enumerate(attrs)): name = attr['name'] if name.startswith('data-'): name = name[5:] if '-' not in name: continue prefix, name = name.split('-', 1) ns_attrs[namespaces[prefix], name] = attr['value'] attrs.pop(i - d) d += 1 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 # If set, additional attribute whitespace will be stripped. trim_attribute_space = False # If set, data attributes can be used instead of namespace # attributes, e.g. "data-tal-content" instead of "tal:content". enable_data_attributes = False # If set, XML namespaces are restricted to the list of those # defined and used by the page template language. restricted_namespace = True 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', 'trim_attribute_space', 'enable_data_attributes', 'restricted_namespace', ) 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'] attrs = start['attrs'] if self.enable_data_attributes: attrs = list(attrs) convert_data_attributes(ns, attrs, start['ns_map']) for (prefix, attr), encoded in tuple(ns.items()): if prefix == TAL or prefix == METAL: 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: value = nodes.Value(clause) switch = value, nodes.Copy(value) 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: omit = True slots = [] self._use_macro.append(slots) if use_macro: inner = nodes.UseExternalMacro( nodes.Value(use_macro), slots, False ) macro_name = use_macro else: inner = nodes.UseExternalMacro( nodes.Value(extend_macro), slots, True ) macro_name = extend_macro # While the macro executes, it should have access to the name it was # called with as 'macroname'. Splitting on / mirrors zope.tal and is a # concession to the path expression syntax. macro_name = macro_name.rsplit('/', 1)[-1] inner = nodes.Define( [nodes.Assignment(["macroname"], Static(ast.Str(macro_name)), True)], inner, ) # -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) translate = ns.get((I18N, 'translate')) == '' content = self._make_content_node( value, content, key, translate, ) 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( 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, STATIC_ATTRIBUTES, ) # Start- and end nodes start_tag = nodes.Start( start['name'], self._maybe_trim(start['prefix']), self._maybe_trim(start['suffix']), ATTRIBUTES ) end_tag = nodes.End( end['name'], end['space'], self._maybe_trim(end['prefix']), self._maybe_trim(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 or EMPTY_DICT)], 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) translate = ns.get((I18N, 'translate')) == '' inner = self._make_content_node( value, inner, key, translate ) # 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: defines = [] else: defines = tal.parse_defines(clause) if defines is None: raise ParseError("Invalid define syntax.", clause) # i18n:target try: target_language = ns[I18N, 'target'] except KeyError: pass else: # The name "default" is an alias for the target language # variable. We simply replace it. target_language = target_language.replace( 'default', 'target_language' ) defines.append( ('local', ("target_language", ), target_language) ) if defines: DEFINE = partial( nodes.Define, [nodes.Assignment( names, nodes.Value(expr), context == "local") for (context, names, expr) in defines], ) else: DEFINE = skip # 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.Alias(["default"], switch[1], False)], nodes.Condition( nodes.Equality(switch[0], value), nodes.Cancel([switch[0]], 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) if start['namespace'] == TAL: self._last = None self._whitespace = whitespace.lstrip('\n') whitespace = "" 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, list(switch)) # i18n:domain try: clause = ns[I18N, 'domain'] except KeyError: DOMAIN = skip else: DOMAIN = partial(nodes.Domain, clause) # i18n:context try: clause = ns[I18N, 'context'] except KeyError: CONTEXT = skip else: CONTEXT = partial(nodes.TxContext, clause) # i18n:name try: clause = ns[I18N, 'name'] except KeyError: NAME = skip else: if not clause.strip(): 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, CONTEXT, ) # metal:fill-slot try: clause = ns[METAL, 'fill-slot'] except KeyError: pass else: if not clause.strip(): raise LanguageError( "Must provide a non-trivial string for metal:fill-slot.", clause ) 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: if ns.get((METAL, 'fill-slot')) is not None: raise LanguageError( "Can't have 'fill-slot' and 'define-macro' " "on the same element.", clause ) 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 = ns.get((I18N, 'translate')) == '' fallback = self._make_content_node( value, None, key, translate, ) if omit is False and start['namespace'] not in self.DROP_NS: start_tag = copy(start_tag) start_tag.attributes = nodes.Sequence( start_tag.attributes.extract( lambda attribute: isinstance(attribute, nodes.Attribute) and isinstance(attribute.expression, ast.Str) ) ) if end_tag is None: # Make sure start-tag has opening suffix. We don't # allow self-closing element here. start_tag.suffix = ">" # Explicitly set end-tag. end_tag = nodes.End(start_tag.name, '', '',) fallback = nodes.Element( start_tag, end_tag, fallback, ) 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_cdata(self, node): if not self._interpolation[-1] or not '${' in node: return nodes.Text(node) expr = nodes.Substitution(node, ()) return nodes.Interpolation(expr, True, False) def visit_comment(self, node): if node.startswith('