pax_global_header00006660000000000000000000000064137006750720014521gustar00rootroot0000000000000052 comment=ea1636c85204ec92b32ddfe2d80661ab19072314 chameleon-3.8.1/000077500000000000000000000000001370067507200134655ustar00rootroot00000000000000chameleon-3.8.1/.gitignore000066400000000000000000000001641370067507200154560ustar00rootroot00000000000000*.pyc *.egg *.egg-info .coverage .tox/ coverage.xml nosetests.xml *.tar.gz env25/ env26/ env27/ env32/ docs/_build/ chameleon-3.8.1/.travis.yml000066400000000000000000000004741370067507200156030ustar00rootroot00000000000000language: python python: - 2.7 - pypy - 3.4 - 3.5 - 3.6 - pypy3 matrix: include: - python: "3.7" dist: xenial sudo: true - python: "3.8-dev" dist: xenial sudo: true install: - python setup.py install script: - python setup.py -q test -q chameleon-3.8.1/CHANGES.rst000066400000000000000000001240071370067507200152730ustar00rootroot00000000000000Changes ======= 3.8.1 (2020-07-06) ------------------ - Added code optimization to reduce sequential appends of static text. - The `default` symbol in dynamic attributes is now symbolic. Previously, it was assigned the string value of the default attribute text. A similar change has been made for switch/case expressions. - The built-in `attrs` dictionary of static element attributes now correctly works with `tal:define`, etc. - Fix slice code generation compatibility issue on Python 3.9. 3.8.0 (2020-06-25) ------------------ - Expose default marker as importable symbol `chameleon.tales.DEFAULT_MARKER`. - Removed legacy flag `literal_false`. To get a similar behavior, use `boolean_attributes`. 3.7.4 (2020-06-17) ------------------ - Fix brown-bag release. 3.7.3 (2020-06-17) ------------------ - Fix regression introduced in 3.6.2 where the default marker would incorrectly change its value between templates, causing issues in software which depends on the value being treated as a global object. 3.7.2 (2020-05-31) ------------------ - Allow setting a custom value representation function, allowing custom formatting of variables during exception formatting. 3.7.1 (2020-05-10) ------------------ - Fix compatiblity issue with Python 3.9. 3.7.0 (2020-03-26) ------------------ - Fixed garbage collection issue with variable scope objects (issue #301). - Fixed issue where setting a global variable would not be available locally. - A `RepeatDict` no longer inherits from `dict` since it does not actually provide that interface in a meaningful way. - Added feature gate `enable_comment_interpolation` which controls whether expression interpolation is enabled inside HTML comments (default is enabled). - Added support for Python 3.6+ f-strings (issue #294). 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. chameleon-3.8.1/COPYRIGHT.txt000066400000000000000000000003221370067507200155730ustar00rootroot00000000000000Copyright (c) 2011 Malthe Borch and Contributors. All Rights Reserved. Portions (c) Zope Foundation and contributors (http://www.zope.org/). Portions (c) Edgewall Software. Portions (c) 2008 Armin Ronacher. chameleon-3.8.1/LICENSE.txt000066400000000000000000000204631370067507200153150ustar00rootroot00000000000000The 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.8.1/MANIFEST.in000066400000000000000000000001201370067507200152140ustar00rootroot00000000000000global-exclude tests/*.py exclude MANIFEST.in exclude *.rst include LICENSE.txt chameleon-3.8.1/Makefile000066400000000000000000000060771370067507200151370ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = docs SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Chameleon.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Chameleon.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." chameleon-3.8.1/README.rst000066400000000000000000000012021370067507200151470ustar00rootroot00000000000000Overview ======== 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.8.1/benchmarks/000077500000000000000000000000001370067507200156025ustar00rootroot00000000000000chameleon-3.8.1/benchmarks/bm_chameleon.py000066400000000000000000000077071370067507200206000ustar00rootroot00000000000000#!/usr/bin/python2 """ Benchmark for test the performance of Chameleon page template engine. """ __author__ = "mborch@gmail.com (Malthe Borch)" # Python imports import os import sys import optparse import time # Local imports import util def relative(*args): return os.path.join(os.path.dirname(os.path.abspath(__file__)), *args) sys.path.insert(0, relative('..', 'src')) # Chameleon imports from chameleon import PageTemplate LOREM_IPSUM = """Quisque lobortis hendrerit posuere. Curabitur aliquet consequat sapien molestie pretium. Nunc adipiscing luc tus mi, viverra porttitor lorem vulputate et. Ut at purus sem, sed tincidunt ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent pulvinar sodales justo at congue. Praesent aliquet facilisis nisl a molestie. Sed tempus nisl ut augue eleifend tincidunt. Sed a lacinia nulla. Cras tortor est, mollis et consequat at, vulputate et orci. Nulla sollicitudin""" BASE_TEMPLATE = '''
${col}
${alt}
${title.strip()} ''' PAGE_TEMPLATE = ''' images:

${lorem}

''' CONTENT_TEMPLATE = ''' fun1fun2fun3fun4fun5fun6

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

''' def test_mako(count): template = PageTemplate(CONTENT_TEMPLATE) base = PageTemplate(BASE_TEMPLATE) page = PageTemplate(PAGE_TEMPLATE) table = [range(150) for i in range(150)] paragraphs = range(50) title = 'Hello world!' times = [] for i in range(count): t0 = time.time() data = template.render( table=table, paragraphs=paragraphs, lorem=LOREM_IPSUM, title=title, img_count=50, base=base, page=page, ) t1 = time.time() times.append(t1-t0) return times if __name__ == "__main__": parser = optparse.OptionParser( usage="%prog [options]", description=("Test the performance of Chameleon templates.")) util.add_standard_options_to(parser) (options, args) = parser.parse_args() util.run_benchmark(options, options.num_runs, test_mako) chameleon-3.8.1/benchmarks/bm_mako.py000066400000000000000000000074401370067507200175660ustar00rootroot00000000000000#!/usr/bin/python """ Benchmark for test the performance of Mako templates engine. Includes: -two template inherences -HTML escaping, XML escaping, URL escaping, whitespace trimming -function defitions and calls -forloops """ __author__ = "virhilo@gmail.com (Lukasz Fidosz)" # Python imports import os import sys import optparse import time # Local imports import util def relative(*args): return os.path.join(os.path.dirname(os.path.abspath(__file__)), *args) sys.path.insert(0, relative('..', 'lib')) # Mako imports from mako.template import Template from mako.lookup import TemplateLookup LOREM_IPSUM = """Quisque lobortis hendrerit posuere. Curabitur aliquet consequat sapien molestie pretium. Nunc adipiscing luc tus mi, viverra porttitor lorem vulputate et. Ut at purus sem, sed tincidunt ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent pulvinar sodales justo at congue. Praesent aliquet facilisis nisl a molestie. Sed tempus nisl ut augue eleifend tincidunt. Sed a lacinia nulla. Cras tortor est, mollis et consequat at, vulputate et orci. Nulla sollicitudin""" BASE_TEMPLATE = """ <%def name="render_table(table)">
% for row in table: % for col in row: % endfor % endfor
${col|h}
<%def name="img(src, alt)"> ${alt} ${title|h,trim} ${next.body()} """ PAGE_TEMPLATE = """ <%inherit file="base.mako"/> % for row in table: % for col in row: % endfor % endfor
${col}
% for nr in range(img_count): ${parent.img('/foo/bar/baz.png', 'no image :o')} % endfor ${next.body()} % for nr in paragraphs:

${lorem|x}

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

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

${fun1()} ${fun2()} ${fun3()} ${fun4()} ${fun5()} ${fun6()} """ def test_mako(count): lookup = TemplateLookup() lookup.put_string('base.mako', BASE_TEMPLATE) lookup.put_string('page.mako', PAGE_TEMPLATE) template = Template(CONTENT_TEMPLATE, lookup=lookup) table = [range(150) for i in range(150)] paragraphs = range(50) title = 'Hello world!' times = [] for i in range(count): t0 = time.time() data = template.render(table=table, paragraphs=paragraphs, lorem=LOREM_IPSUM, title=title, img_count=50) t1 = time.time() times.append(t1-t0) return times if __name__ == "__main__": parser = optparse.OptionParser( usage="%prog [options]", description=("Test the performance of Mako templates.")) util.add_standard_options_to(parser) (options, args) = parser.parse_args() util.run_benchmark(options, options.num_runs, test_mako) chameleon-3.8.1/benchmarks/util.py000066400000000000000000000035751370067507200171430ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function """Utility code for benchmark scripts.""" __author__ = "collinwinter@google.com (Collin Winter)" import math import operator try: reduce except NameError: import functools reduce = functools.reduce def run_benchmark(options, num_runs, bench_func, *args): """Run the given benchmark, print results to stdout. Args: options: optparse.Values instance. num_runs: number of times to run the benchmark bench_func: benchmark function. `num_runs, *args` will be passed to this function. This should return a list of floats (benchmark execution times). """ if options.profile: import cProfile prof = cProfile.Profile() prof.runcall(bench_func, num_runs, *args) prof.print_stats(sort=options.profile_sort) else: data = bench_func(num_runs, *args) if options.take_geo_mean: product = reduce(operator.mul, data, 1) print (math.pow(product, 1.0 / len(data))) else: for x in data: print (x) def add_standard_options_to(parser): """Add a bunch of common command-line flags to an existing OptionParser. This function operates on `parser` in-place. Args: parser: optparse.OptionParser instance. """ parser.add_option("-n", action="store", type="int", default=100, dest="num_runs", help="Number of times to run the test.") parser.add_option("--profile", action="store_true", help="Run the benchmark through cProfile.") parser.add_option("--profile_sort", action="store", type="str", default="time", help="Column to sort cProfile output by.") parser.add_option("--take_geo_mean", action="store_true", help="Return the geo mean, rather than individual data.") chameleon-3.8.1/docs/000077500000000000000000000000001370067507200144155ustar00rootroot00000000000000chameleon-3.8.1/docs/conf.py000066400000000000000000000151151370067507200157170ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Chameleon documentation build configuration file, created by # sphinx-quickstart on Sun Nov 1 16:08:00 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import pkg_resources sys.path.append(os.path.abspath('../src')) rqmt = pkg_resources.require('Chameleon')[0] # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Chameleon' copyright = u'2008-2017 by Malthe Borch and the Repoze Community' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '%s.%s' % tuple(map(int, rqmt.version.split('.')[:2])) # The full version, including alpha/beta/rc tags. release = rqmt.version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = "Chameleon %s documentation" % version # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bchameleonm, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'chameleondoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'chameleon.tex', u'Chameleon Documentation', u'Malthe Borch et. al', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # Configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'https://docs.python.org/': None, } chameleon-3.8.1/docs/configuration.rst000066400000000000000000000021151370067507200200150ustar00rootroot00000000000000Configuration ============= Most settings can be provided as keyword-arguments to the template constructor classes. There are certain settings which are required at environment level. Acceptable values are ``"0"``, ``"1"``, or the literals ``"true"`` or ``"false"`` (case-insensitive). General usage ------------- The following settings are useful in general. ``CHAMELEON_EAGER`` Parse and compile templates on instantiation. ``CHAMELEON_CACHE`` When set to a file system path, the template compiler will write its output to files in this directory and use it as a cache. This not only enables you to see the compiler output, but also speeds up startup. ``CHAMELEON_RELOAD`` This setting controls the default value of the ``auto_reload`` parameter. Development ----------- The following settings are mostly useful during development or debugging of the library itself. ``CHAMELEON_DEBUG`` Enables a set of debugging settings which make it easier to discover and research issues with the engine itself. This implicitly enables auto-reload for any template. chameleon-3.8.1/docs/index.rst000066400000000000000000000160321370067507200162600ustar00rootroot00000000000000Chameleon ========= Chameleon is an HTML/XML template engine for `Python `_. It's designed to generate the document output of a web application, typically HTML markup or XML. The language used is *page templates*, originally a `Zope `_ invention [1]_, but available here as a :ref:`standalone library ` that you can use in any script or application running Python 2.7 and up, including 3.4+ and `pypy `_). It comes with a set of :ref:`new features `, too. The template engine compiles templates into Python byte-code and is optimized for speed. For a complex template language, the performance is :ref:`very good `. *Found a bug?* Please report issues to the `issue tracker `_. *Need help?* Post to the Pylons `discussion list `_ or join the ``#pyramid`` channel on `Freenode IRC `_. Getting the code ---------------- You can `download `_ the package from the Python package index or install the latest release using setuptools or the newer `distribute `_ (required for Python 3.x):: $ easy_install Chameleon .. _no-dependencies: There are no required library dependencies on Python 2.7 and up [2]_. The project is hosted in a `GitHub repository `_. Code contributions are welcome. The easiest way is to use the `pull request `_ interface. Introduction ------------ The *page templates* language is used within your document structure as special element attributes and text markup. Using a set of simple language constructs, you control the document flow, element repetition, text replacement and translation. .. note:: If you've used page templates in a Zope environment previously, note that Chameleon uses Python as the default expression language (instead of *path* expressions). The basic language (known as the *template attribute language* or TAL) is simple enough to grasp from an example: .. code-block:: genshi

Hello, ${'world'}!

${row.capitalize()} ${col}
The ``${...}`` notation is short-hand for text insertion [3]_. The Python-expression inside the braces is evaluated and the result included in the output. By default, the string is escaped before insertion. To avoid this, use the ``structure:`` prefix: .. code-block:: genshi
${structure: ...}
Note that if the expression result is an object that implements an ``__html__()`` method [4]_, this method will be called and the result treated as "structure". An example of such an object is the ``Markup`` class that's included as a utility:: from chameleon.utils import Markup username = Markup("%s" % username) The macro language (known as the *macro expansion language* or METAL) provides a means of filling in portions of a generic template. On the left, the macro template; on the right, a template that loads and uses the macro, filling in the "content" slot: .. code-block:: genshi

${structure: document.body}

Example — ${document.title}

${document.title}

In the example, the expression type :ref:`load ` is used to retrieve a template from the file system using a path relative to the calling template. The METAL system works with TAL such that you can for instance fill in a slot that appears in a ``tal:repeat`` loop, or refer to variables defined using ``tal:define``. The third language subset is the translation system (known as the *internationalization language* or I18N): .. code-block:: genshi ...
You have ${round(amount, 2)} dollars in your account.
... Each translation message is marked up using ``i18n:translate`` and values can be mapped using ``i18n:name``. Attributes are marked for translation using ``i18n:attributes``. The template engine generates `gettext `_ translation strings from the markup:: "You have ${amount} dollars in your account." If you use a web framework such as `Pyramid `_, the translation system is set up automatically and will negotiate on a *target language* based on the HTTP request or other parameter. If not, then you need to configure this manually. Next steps ---------- This was just an introduction. There are a number of other basic statements that you need to know in order to use the language. This is all covered in the :ref:`language reference `. If you're already familiar with the page template language, you can skip ahead to the :ref:`getting started ` section to learn how to use the template engine in your code. To learn about integration with your favorite web framework see the section on :ref:`framework integration `. License ------- This software is made available under a BSD-like license. Contents ======== .. toctree:: :maxdepth: 2 library.rst reference.rst integration.rst configuration.rst Indices and Tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` Notes ===== .. [1] The template language specifications and API for the Page Templates engine are based on Zope Page Templates (see in particular `zope.pagetemplate `_). However, the Chameleon compiler and Page Templates engine is an entirely new codebase, packaged as a standalone distribution. It does not require a Zope software environment. .. [2] The translation system in Chameleon is pluggable and based on `gettext `_. There is built-in support for the `zope.i18n `_ package. If this package is installed, it will be used by default. The `translationstring `_ package offers some of the same helper and utility classes, without the Zope application interface. .. [3] This syntax was taken from `Genshi `_. .. [4] See the `WebHelpers `_ library which provide a simple wrapper around this method. chameleon-3.8.1/docs/integration.rst000066400000000000000000000025611370067507200174760ustar00rootroot00000000000000.. _framework-integration: Integration =========== Integration with Chameleon is available for a number of popular web frameworks. The framework will usually provide loading mechanisms and translation (internationalization) configuration. Pyramid ------- `pyramid_chameleon `_ is a set of bindings that make templates written for the Chameleon templating system work under the Pyramid web framework. Zope 2 / Plone -------------- Install the `five.pt `_ package to replace the reference template engine (globally). Zope Toolkit (ZTK) ------------------ Install the `z3c.pt `_ package for applications based on the `Zope Toolkit `_ (ZTK). Note that you need to explicit use the template classes from this package. Grok ---- Support for the `Grok `_ framework is available in the `grokcore.chameleon `_ package. This package will setup Grok's policy for templating integration and associate the Chameleon template components for the ``.cpt`` template filename extension. Django ------ Install the `django-chameleon-templates `_ app to enable Chameleon as a template engine. chameleon-3.8.1/docs/library.rst000066400000000000000000000126731370067507200166240ustar00rootroot00000000000000Library Documentation ===================== This section documents the package as a Python library. To learn about the page template language, consult the :ref:`language reference `. .. _getting-started-with-cpt: Getting started --------------- There are several template constructor classes available, one for each of the combinations *text* or *xml*, and *string* or *file*. The file-based constructor requires an absolute path. To set up a templates directory *once*, use the template loader class:: import os path = os.path.dirname(__file__) from chameleon import PageTemplateLoader templates = PageTemplateLoader(os.path.join(path, "templates")) Then, to load a template relative to the provided path, use dictionary syntax:: template = templates['hello.pt'] Alternatively, use the appropriate template class directly. Let's try with a string input:: from chameleon import PageTemplate template = PageTemplate("
Hello, ${name}.
") All template instances are callable. Provide variables by keyword argument:: >>> template(name='John') '
Hello, John.
' .. _fast: Performance ----------- The template engine compiles (or *translates*) template source code into Python byte-code. In simple templates this yields an increase in performance of about 7 times in comparison to the reference implementation. In benchmarks for the content management system `Plone `_, switching to Chameleon yields a request to response improvement of 20-50%. Extension --------- You can extend the language through the expression engine by writing your own expression compiler. Let's try and write an expression compiler for an expression type that will simply uppercase the supplied value. We'll call it ``upper``. You can write such a compiler as a closure: .. code-block:: python import ast def uppercase_expression(string): def compiler(target, engine): uppercased = self.string.uppercase() value = ast.Str(uppercased) return [ast.Assign(targets=[target], value=value)] return compiler To make it available under a certain prefix, we'll add it to the expression types dictionary. .. code-block:: python from chameleon import PageTemplate PageTemplate.expression_types['upper'] = uppercase_expression Alternatively, you could subclass the template class and set the attribute ``expression_types`` to a dictionary that includes your expression: .. code-block:: python from chameleon import PageTemplateFile from chameleon.tales import PythonExpr class MyPageTemplateFile(PageTemplateFile): expression_types = { 'python': PythonExpr, 'upper': uppercase_expression } You can now uppercase strings *natively* in your templates::
It's probably best to stick with a Python expression::
API reference ------------- This section describes the documented API of the library. Templates ~~~~~~~~~ Use the ``PageTemplate*`` template classes to define a template from a string or file input: .. automodule:: chameleon .. autoclass:: chameleon.PageTemplate Note: The remaining classes take the same general configuration arguments. .. automethod:: render .. autoclass:: chameleon.PageTemplateFile(filename, **config) .. autoclass:: chameleon.PageTextTemplate .. autoclass:: chameleon.PageTextTemplateFile Loader ~~~~~~ Some systems have framework support for loading templates from files. The following loader class is directly compatible with the Pylons framework and may be adapted to other frameworks: .. class:: chameleon.PageTemplateLoader(search_path=None, default_extension=None, **config) Load templates from ``search_path`` (must be a string or a list of strings):: templates = PageTemplateLoader(path) example = templates['example.pt'] If ``default_extension`` is provided, this will be added to inputs that do not already have an extension:: templates = PageTemplateLoader(path, ".pt") example = templates['example'] Any additional keyword arguments will be passed to the template constructor:: templates = PageTemplateLoader(path, debug=True, encoding="utf-8") .. automethod:: load Exceptions ~~~~~~~~~~ Chameleon may raise exceptions during both the cooking and the rendering phase, but those raised during the cooking phase (parse and compile) all inherit from a single base class: .. class:: chameleon.TemplateError(msg, token) This exception is the base class of all exceptions raised by the template engine in the case where a template has an error. It may be raised during rendering since templates are processed lazily (unless eager loading is enabled). An error that occurs during the rendering of a template is wrapped in an exception class to disambiguate the two cases: .. class:: chameleon.RenderError(*args) Indicates an exception that resulted from the evaluation of an expression in a template. A complete traceback is attached to the exception beginning with the expression that resulted in the error. The traceback includes a string representation of the template variable scope for further reference. Expressions ~~~~~~~~~~~ For advanced integration, the compiler module provides support for dynamic expression evaluation: .. automodule:: chameleon.compiler .. autoclass:: chameleon.compiler.ExpressionEvaluator chameleon-3.8.1/docs/reference.rst000066400000000000000000001501571370067507200171160ustar00rootroot00000000000000:tocdepth: 4 .. _language-reference: .. highlight:: xml Language Reference ================== The language reference is structured such that it can be read as a general introduction to the *page templates* language. It's split into parts that correspond to each of the main language features. Syntax ###### You can safely :ref:`skip this section ` if you're familiar with how template languages work or just want to learn by example. An *attribute language* is a programming language designed to render documents written in XML or HTML markup. The input must be a well-formed document. The output from the template is usually XML-like but isn't required to be well-formed. The statements of the language are document tags with special attributes, and look like this::

...

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

...

Note how ``tal`` is used without an explicit namespace declaration. Chameleon sets up defaults for ``metal`` and ``i18n`` as well. .. note:: Default prefixes are a special feature of Chameleon. If the ``enable_data_attributes`` option is set then you can use ``data-prefix-command="argument"`` in addition to the namespace prefix attributes. .. _tal: Basics (TAL) ############ The *template attribute language* is used to create dynamic XML-like content. It allows elements of a document to be replaced, repeated, or omitted. Statements ---------- These are the available statements: ================== ============== Statement Description ================== ============== ``tal:define`` Define variables. ``tal:switch`` Defines a switch condition ``tal:condition`` Include element only if expression is true. ``tal:repeat`` Repeat an element. ``tal:case`` Includes element only if expression is equal to parent switch. ``tal:content`` Substitute the content of an element. ``tal:replace`` Replace the element with dynamic content. ``tal:omit-tag`` Omit the element tags, leaving only the inner content. ``tal:attributes`` Dynamically change or insert element attributes. ``tal:on-error`` Substitute the content of an element if processing fails. ================== ============== When there is only one TAL statement per element, the order in which they are executed is simple. Starting with the root element, each element's statements are executed, then each of its child elements is visited, in order, to do the same:: </meta> <body> <div tal:condition="items"> <p>These are your items:</p> <ul> <li tal:repeat="item items" tal:content="item" /> </ul> </div> </body> </html> Any combination of statements may appear on the same element, except that the ``tal:content`` and ``tal:replace`` statements may not be used on the same element. .. note:: The ``tal:case`` and ``tal:switch`` statements are available in Chameleon only. TAL does not use the order in which statements are written in the tag to determine the order in which they are executed. When an element has multiple statements, they are executed in the order printed in the table above. There is a reasoning behind this ordering. Because users often want to set up variables for use in other statements contained within this element or subelements, ``tal:define`` is executed first. Then any switch statement. ``tal:condition`` follows, then ``tal:repeat``, then ``tal:case``. We are now rendering an element; first ``tal:content`` or ``tal:replace``. Finally, before ``tal:attributes``, we have ``tal:omit-tag`` (which is implied with ``tal:replace``). .. note:: *TALES* is used as the expression language for the "stuff in the quotes". The default syntax is simply Python, but other inputs are possible --- see the section on :ref:`expressions <tales>`. ``tal:attributes`` ^^^^^^^^^^^^^^^^^^ Removes, updates or inserts element attributes. :: tal:attributes="href request.url" Syntax ~~~~~~ ``tal:attributes`` syntax:: argument ::= attribute_statement [';' attribute_statement]* attribute_statement ::= (attribute_name expression | expression) attribute_name ::= [namespace-prefix ':'] Name namespace-prefix ::= Name Description ~~~~~~~~~~~ The ``tal:attributes`` statement replaces the value of an attribute (or drops, or creates an attribute) with a dynamic value. The value of each expression is converted to a string, if necessary. .. note:: You can qualify an attribute name with a namespace prefix, for example ``html:table``, if you are generating an XML document with multiple namespaces. If an attribute expression evaluates to ``None``, the attribute is deleted from the statement element (or simply not inserted). If an attribute statement is just an expression, it must evaluate to a Python dict (or implement the methods ``update()`` and ``items()`` from the dictionary specification). If the expression evaluates to the symbol ``default`` (a symbol which is always available when evaluating attributes), its value is defined as the default static attribute value. If there is no such default value, a return value of ``default`` will drop the attribute. If you use ``tal:attributes`` on an element with an active ``tal:replace`` command, the ``tal:attributes`` statement is ignored. If you use ``tal:attributes`` on an element with a ``tal:repeat`` statement, the replacement is made on each repetition of the element, and the replacement expression is evaluated fresh for each repetition. .. note:: If you want to include a semicolon (";") in an expression, it must be escaped by doubling it (";;"). Similarly, you can escape expression interpolation using the "$" symbol by doubling it ("$$"). Examples ~~~~~~~~ Replacing a link:: <a href="/sample/link.html" tal:attributes="href context.url()" > ... </a> Replacing two attributes:: <textarea rows="80" cols="20" tal:attributes="rows request.rows();cols request.cols()" /> A checkbox input:: <input type="checkbox" tal:attributes="checked True" /> ``tal:condition`` ^^^^^^^^^^^^^^^^^ Conditionally includes or omits an element:: <div tal:condition="comments"> ... </div> Syntax ~~~~~~ ``tal:condition`` syntax:: argument ::= expression Description ~~~~~~~~~~~ The ``tal:condition`` statement includes the statement element in the template only if the condition is met, and omits it otherwise. If its expression evaluates to a *true* value, then normal processing of the element continues, otherwise the statement element is immediately removed from the template. For these purposes, the value ``nothing`` is false, and ``default`` has the same effect as returning a true value. .. note:: Like Python itself, ZPT considers None, zero, empty strings, empty sequences, empty dictionaries, and instances which return a nonzero value from ``__len__`` or which return false from ``__nonzero__``; all other values are true, including ``default``. Examples ~~~~~~~~ Test a variable before inserting it:: <p tal:condition="request.message" tal:content="request.message" /> Testing for odd/even in a repeat-loop:: <div tal:repeat="item range(10)"> <p tal:condition="repeat.item.even">Even</p> <p tal:condition="repeat.item.odd">Odd</p> </div> ``tal:content`` ^^^^^^^^^^^^^^^ Replaces the content of an element. Syntax ~~~~~~ ``tal:content`` syntax:: argument ::= (['text'] | 'structure') expression Description ~~~~~~~~~~~ Rather than replacing an entire element, you can insert text or structure in place of its children with the ``tal:content`` statement. The statement argument is exactly like that of ``tal:replace``, and is interpreted in the same fashion. If the expression evaluates to ``nothing``, the statement element is left childless. If the expression evaluates to ``default``, then the element's contents are evaluated. The default replacement behavior is ``text``, which replaces angle-brackets and ampersands with their HTML entity equivalents. The ``structure`` keyword passes the replacement text through unchanged, allowing HTML/XML markup to be inserted. This can break your page if the text contains unanticipated markup (eg. text submitted via a web form), which is the reason that it is not the default. .. note:: The ``structure`` keyword exists to provide backwards compatibility. In Chameleon, the ``structure:`` expression type provides the same functionality (also for inline expressions). Examples ~~~~~~~~ Inserting the user name:: <p tal:content="user.getUserName()">Fred Farkas</p> Inserting HTML/XML:: <p tal:content="structure context.getStory()"> Marked <b>up</b> content goes here. </p> ``tal:define`` ^^^^^^^^^^^^^^ Defines local variables. Syntax ~~~~~~ ``tal:define`` syntax:: variable_name ::= Name | '(' Name [',' Name]* ')' define_var ::= variable_name expression define_scope ::= (['local'] | 'global') define_var argument ::= define_scope [';' define_scope]* Description ~~~~~~~~~~~ The ``tal:define`` statement defines variables. When you define a local variable in a statement element, you can use that variable in that element and the elements it contains. If you redefine a variable in a contained element, the new definition hides the outer element's definition within the inner element. Note that valid variable names are any Python identifier string including underscore, although two or more leading underscores are disallowed (used internally by the compiler). Further, names are case-sensitive. Variable names support basic iterable unpacking when surrounded by parenthesis. This also applies to the variable established by ``tal:repeat``. .. note:: This is a Chameleon-specific :ref:`language extension <new-features>`. Python builtins are always "in scope", but most of them may be redefined (such as ``help``). Exceptions are:: ``float``, ``int``, ``len``, ``long``, ``str``, ``None``, ``True`` and ``False``. In addition, the following names are reserved: ``econtext``, ``rcontext``, ``translate``, ``decode`` and ``convert``. If the expression associated with a variable evaluates to ``nothing``, then that variable has the value ``nothing``, and may be used as such in further expressions. Likewise, if the expression evaluates to ``default``, then the variable has the value ``default``, and may be used as such in further expressions. You can define two different kinds of variables: *local* and *global*. When you define a local variable in a statement element, you can only use that variable in that element and the elements it contains. If you redefine a local variable in a contained element, the new definition hides the outer element's definition within the inner element. When you define a global variables, you can use it in any element processed after the defining element. If you redefine a global variable, you replace its definition for the rest of the template. .. tip:: Global variables may be changed by the execution of a macro if that macro also declares the variable to be global. To set the definition scope of a variable, use the keywords ``local`` or ``global`` in front of the assignment. The default setting is ``local``; thus, in practice, only the ``global`` keyword is used. .. note:: If you want to include a semicolon (";") in an expression, it must be escaped by doubling it (";;"). Examples ~~~~~~~~ Defining a variable:: tal:define="company_name 'Zope Corp, Inc.'" Defining two variables, where the second depends on the first:: tal:define="mytitle context.title; tlen len(mytitle)" Defining a local and global variable:: tal:define="global mytitle context.title; tlen len(mytitle)" Unpacking a sequence:: tal:define="(key,value) ('a', 42)" ``tal:switch`` and ``tal:case`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Defines a switch clause. :: <ul tal:switch="len(items) % 2"> <li tal:case="True">odd</li> <li tal:case="False">even</li> </ul> Syntax ~~~~~~ ``tal:case`` and ``tal:switch`` syntax:: argument ::= expression Description ~~~~~~~~~~~ The *switch* and *case* construct is a short-hand syntax for matching a set of expressions against a single parent. The ``tal:switch`` statement is used to set a new parent expression and the contained ``tal:case`` statements are then matched in sequence such that only the first match succeeds. Note that the symbol ``default`` affirms the case precisely when no previous case has been successful. It should therefore be placed last. .. note:: These statements are only available in Chameleon 2.x and not part of the ZPT specification. Examples ~~~~~~~~ :: <ul tal:switch="item.type"> <li tal:case="'document'"> Document </li> <li tal:case="'folder'"> Folder </li> <li tal:case="default"> Other </li> </ul> ``tal:omit-tag`` ^^^^^^^^^^^^^^^^ Removes an element, leaving its contents. Syntax ~~~~~~ ``tal:omit-tag`` syntax:: argument ::= [ expression ] Description ~~~~~~~~~~~ The ``tal:omit-tag`` statement leaves the contents of an element in place while omitting the surrounding start and end tags. If the expression evaluates to a *false* value, then normal processing of the element continues and the tags are not omitted. If the expression evaluates to a *true* value, or no expression is provided, the statement element is replaced with its contents. .. note:: Like Python itself, ZPT considers None, zero, empty strings, empty sequences, empty dictionaries, and instances which return a nonzero value from ``__len__`` or which return false from ``__nonzero__``; all other values are true, including ``default``. Examples ~~~~~~~~ Unconditionally omitting a tag:: <div tal:omit-tag="" comment="This tag will be removed"> <i>...but this text will remain.</i> </div> Conditionally omitting a tag:: <b tal:omit-tag="not:bold">I may be bold.</b> The above example will omit the ``b`` tag if the variable ``bold`` is false. Creating ten paragraph tags, with no enclosing tag:: <span tal:repeat="n range(10)" tal:omit-tag=""> <p tal:content="n">1</p> </span> .. _tal_repeat: ``tal:repeat`` ^^^^^^^^^^^^^^ Repeats an element. Syntax ~~~~~~ ``tal:repeat`` syntax:: argument ::= variable_name expression variable_name ::= Name Description ~~~~~~~~~~~ The ``tal:repeat`` statement replicates a sub-tree of your document once for each item in a sequence. The expression should evaluate to a sequence. If the sequence is empty, then the statement element is deleted, otherwise it is repeated for each value in the sequence. If the expression is ``default``, then the element is left unchanged, and no new variables are defined. The ``variable_name`` is used to define a local variable and a repeat variable. For each repetition, the local variable is set to the current sequence element, and the repeat variable is set to an iteration object. Repeat variables ~~~~~~~~~~~~~~~~~ You use repeat variables to access information about the current repetition (such as the repeat index). The repeat variable has the same name as the local variable, but is only accessible through the built-in variable named ``repeat``. The following information is available from the repeat variable: ================== ============== Attribute Description ================== ============== ``index`` Repetition number, starting from zero. ``number`` Repetition number, starting from one. ``even`` True for even-indexed repetitions (0, 2, 4, ...). ``odd`` True for odd-indexed repetitions (1, 3, 5, ...). ``parity`` For odd-indexed repetitions, this is 'odd', else 'even'. ``start`` True for the starting repetition (index 0). ``end`` True for the ending, or final, repetition. ``length`` Length of the sequence, which will be the total number of repetitions. ``letter`` Repetition number as a lower-case letter: "a" - "z", "aa" - "az", "ba" - "bz", ..., "za" - "zz", "aaa" - "aaz", and so forth. ``Letter`` Upper-case version of *letter*. ``roman`` Repetition number as a lower-case roman numeral: "i", "ii", "iii", "iv", "v", etc. ``Roman`` Upper-case version of *roman*. ================== ============== You can access the contents of the repeat variable using either dictionary- or attribute-style access, e.g. ``repeat['item'].start`` or ``repeat.item.start``. .. note:: For legacy compatibility, the attributes ``odd``, ``even``, ``number``, ``letter``, ``Letter``, ``roman``, and ``Roman`` are callable (returning ``self``). .. note:: Earlier versions of this document, and the `Zope Page Templates Reference <https://zope.readthedocs.io/en/latest/zopebook/AppendixC.html#repeat-variables>`_, referred to ``first`` and ``last`` attributes for use with sorted sequences. These are not implemented in Chameleon or the Zope reference implementation zope.tales. Instead, you can use :func:`itertools.groupby`, as in the example below. Examples ~~~~~~~~ Iterating over a sequence of strings:: <p tal:repeat="txt ('one', 'two', 'three')"> <span tal:replace="txt" /> </p> Inserting a sequence of table rows, and using the repeat variable to number the rows:: <table> <tr tal:repeat="item here.cart"> <td tal:content="repeat.item.number">1</td> <td tal:content="item.description">Widget</td> <td tal:content="item.price">$1.50</td> </tr> </table> Nested repeats:: <table border="1"> <tr tal:repeat="row range(10)"> <td tal:repeat="column range(10)"> <span tal:define="x repeat.row.number; y repeat.column.number; z x * y" tal:replace="string:$x * $y = $z">1 * 1 = 1</span> </td> </tr> </table> Grouping objects by type, drawing a rule between elements of different types:: <div tal:repeat="(type,objects) list(map(lambda g: (g[0], list(g[1])), itertools.groupby(objects, key=lambda o: o.meta_type)))" tal:define="itertools import:itertools"> <h2 tal:content="type">Meta Type</h2> <p tal:repeat="object objects" tal:content="object.id">Object ID</p> <hr /> </div> .. caution:: It is important to fully realize the iterator produced by :func:`itertools.groupby`, as well as the iterator produced for each group, in the expression passed to ``tal:repeat``. This is because the implementation of certain repeat variables, such as ``length`` and ``end`` requires Chameleon to look ahead in the iterator, consuming it faster than is visible. The iterator returned by :func:`itertools.groupby` is shared among all of its subgroups, so without the full reification of all the iterators, incorrect results will be produced. ``tal:replace`` ^^^^^^^^^^^^^^^ Replaces an element. Syntax ~~~~~~ ``tal:replace`` syntax:: argument ::= ['structure'] expression Description ~~~~~~~~~~~ The ``tal:replace`` statement replaces an element with dynamic content. It replaces the statement element with either text or a structure (unescaped markup). The body of the statement is an expression with an optional type prefix. The value of the expression is converted into an escaped string unless you provide the 'structure' prefix. Escaping consists of converting ``&`` to ``&amp;``, ``<`` to ``&lt;``, and ``>`` to ``&gt;``. .. note:: If the inserted object provides an ``__html__`` method, that method is called with the result inserted as structure. This feature is not implemented by ZPT. If the expression evaluates to ``None``, the element is simply removed. If the value is ``default``, then the element is left unchanged. Examples ~~~~~~~~ Inserting a title:: <span tal:replace="context.title">Title</span> Inserting HTML/XML:: <div tal:replace="structure table" /> .. _tales: Expressions (TALES) ################### The *Template Attribute Language Expression Syntax* (TALES) standard describes expressions that supply :ref:`tal` and :ref:`metal` with data. TALES is *one* possible expression syntax for these languages, but they are not bound to this definition. Similarly, TALES could be used in a context having nothing to do with TAL or METAL. TALES expressions are described below with any delimiter or quote markup from higher language layers removed. Here is the basic definition of TALES syntax:: Expression ::= [type_prefix ':'] String type_prefix ::= Name Here are some simple examples:: 1 + 2 None string:Hello, ${view.user_name} The optional *type prefix* determines the semantics and syntax of the *expression string* that follows it. A given implementation of TALES can define any number of expression types, with whatever syntax you like. It also determines which expression type is indicated by omitting the prefix. Types ----- These are the available TALES expression types: ============= ============== Prefix Description ============= ============== ``exists`` Evaluate the result inside an exception handler; if one of the exceptions ``AttributeError``, ``LookupError``, ``TypeError``, ``NameError``, or ``KeyError`` is raised during evaluation, the result is ``False``, otherwise ``True``. Note that the original result is discarded in any case. ``import`` Import a global symbol using dotted notation. ``load`` Load a template relative to the current template or absolute. ``not`` Negate the expression result ``python`` Evaluate a Python expression ``string`` Format a string ``structure`` Wraps the expression result as *structure*. ============= ============== .. note:: The default expression type is ``python``. .. warning:: The Zope reference engine defaults to a ``path`` expression type, which is closely tied to the Zope framework. This expression is not implemented in Chameleon (but it's available in a Zope framework compatibility package, `z3c.pt <http://pypi.python.org/pypi/z3c.pt>`_). There's a mechanism to allow fallback to alternative expressions, if one should fail (raise an exception). The pipe character ('|') is used to separate two expressions:: <div tal:define="page request.GET['page'] | 0"> This mechanism applies only to the ``python`` expression type, and by derivation ``string``. .. _tales_built_in_names: ``python`` ^^^^^^^^^^ Evaluates a Python expression. Syntax ~~~~~~ Python expression syntax:: Any valid Python language expression Description ~~~~~~~~~~~ Python expressions are executed natively within the translated template source code. There is no built-in security apparatus. ``string`` ^^^^^^^^^^ Syntax ~~~~~~ String expression syntax:: string_expression ::= ( plain_string | [ varsub ] )* varsub ::= ( '$' Variable ) | ( '${ Expression }' ) plain_string ::= ( '$$' | non_dollar )* non_dollar ::= any character except '$' Description ~~~~~~~~~~~ String expressions interpret the expression string as text. If no expression string is supplied the resulting string is *empty*. The string can contain variable substitutions of the form ``$name`` or ``${expression}``, where ``name`` is a variable name, and ``expression`` is a TALES-expression. The escaped string value of the expression is inserted into the string. .. note:: To prevent a ``$`` from being interpreted this way, it must be escaped as ``$$``. Using a backslash-escape is not supported. Examples ~~~~~~~~ Basic string formatting:: <span tal:replace="string:$this and $that"> Spam and Eggs </span> <p tal:content="string:${request.form['total']}"> total: 12 </p> Including a dollar sign:: <p tal:content="string:$$$cost"> cost: $42.00 </p> .. _import-expression: ``import`` ^^^^^^^^^^ Imports a module global. .. _structure-expression: ``structure`` ^^^^^^^^^^^^^ Wraps the expression result as *structure*: The replacement text is inserted into the document without escaping, allowing HTML/XML markup to be inserted. This can break your page if the text contains unanticipated markup (eg. text submitted via a web form), which is the reason that it is not the default. .. _load-expression: ``load`` ^^^^^^^^ Loads a template instance. Syntax ~~~~~~ Load expression syntax:: Relative or absolute file path Description ~~~~~~~~~~~ The template will be loaded using the same template class as the calling template. Examples ~~~~~~~~ Loading a template and using it as a macro:: <div tal:define="master load: ../master.pt" metal:use-macro="master" /> Built-in names -------------- These are the names always available in the TALES expression namespace: - ``default`` - special value used to specify that existing text or attributes should not be replaced. See the documentation for individual TAL statements for details on how they interpret *default*. - ``repeat`` - the *repeat* variables; see :ref:`tal_repeat` for more information. - ``template`` - reference to the template which was first called; this symbol is carried over when using macros. - ``macros`` - reference to the macros dictionary that corresponds to the current template. .. _metal: Macros (METAL) ############## The *Macro Expansion Template Attribute Language* (METAL) standard is a facility for HTML/XML macro preprocessing. It can be used in conjunction with or independently of TAL and TALES. Macros provide a way to define a chunk of presentation in one template, and share it in others, so that changes to the macro are immediately reflected in all of the places that share it. Additionally, macros are always fully expanded, even in a template's source text, so that the template appears very similar to its final rendering. A single Page Template can accomodate multiple macros. Namespace --------- The METAL namespace URI and recommended alias are currently defined as:: xmlns:metal="http://xml.zope.org/namespaces/metal" Just like the TAL namespace URI, this URI is not attached to a web page; it's just a unique identifier. This identifier must be used in all templates which use METAL. Note that elements that appear in a template with the METAL namespace are omitted from the output where they appear. This is useful when defining a macro:: <metal:block define-macro="hello"> ... </metal:block> In the example above the element is named `block` but any name can be used to the same effect as long as it is qualified with the METAL namespace. Statements ---------- METAL defines a number of statements: * ``metal:define-macro`` Define a macro. * ``metal:use-macro`` Use a macro. * ``metal:extend-macro`` Extend a macro. * ``metal:define-slot`` Define a macro customization point. * ``metal:fill-slot`` Customize a macro. Although METAL does not define the syntax of expression non-terminals, leaving that up to the implementation, a canonical expression syntax for use in METAL arguments is described in TALES Specification. ``define-macro`` ^^^^^^^^^^^^^^^^ Defines a macro. Syntax ~~~~~~ ``metal:define-macro`` syntax:: argument ::= Name Description ~~~~~~~~~~~ The ``metal:define-macro`` statement defines a macro. The macro is named by the statement expression, and is defined as the element and its sub-tree. Examples ~~~~~~~~ Simple macro definition:: <p metal:define-macro="copyright"> Copyright 2011, <em>Foobar</em> Inc. </p> ``define-slot`` ^^^^^^^^^^^^^^^ Defines a macro customization point. Syntax ~~~~~~ ``metal:define-slot`` syntax:: argument ::= Name Description ~~~~~~~~~~~ The ``metal:define-slot`` statement defines a macro customization point or *slot*. When a macro is used, its slots can be replaced, in order to customize the macro. Slot definitions provide default content for the slot. You will get the default slot contents if you decide not to customize the macro when using it. The ``metal:define-slot`` statement must be used inside a ``metal:define-macro`` statement. Slot names must be unique within a macro. Examples ~~~~~~~~ Simple macro with slot:: <p metal:define-macro="hello"> Hello <b metal:define-slot="name">World</b> </p> This example defines a macro with one slot named ``name``. When you use this macro you can customize the ``b`` element by filling the ``name`` slot. ``fill-slot`` ^^^^^^^^^^^^^ Customize a macro. Syntax ~~~~~~ ``metal:fill-slot`` syntax:: argument ::= Name Description ~~~~~~~~~~~ The ``metal:fill-slot`` statement customizes a macro by replacing a *slot* in the macro with the statement element (and its content). The ``metal:fill-slot`` statement must be used inside a ``metal:use-macro`` statement. Slot names must be unique within a macro. If the named slot does not exist within the macro, the slot contents will be silently dropped. Examples ~~~~~~~~ Given this macro:: <p metal:define-macro="hello"> Hello <b metal:define-slot="name">World</b> </p> You can fill the ``name`` slot like so:: <p metal:use-macro="container['master.html'].macros.hello"> Hello <b metal:fill-slot="name">Kevin Bacon</b> </p> ``use-macro`` ^^^^^^^^^^^^^ Use a macro. Syntax ~~~~~~ ``metal:use-macro`` syntax:: argument ::= expression Description ~~~~~~~~~~~ The ``metal:use-macro`` statement replaces the statement element with a macro. The statement expression describes a macro definition. The ``macroname`` variable will be bound to the defined name of the macro being used. .. note:: In Chameleon the expression may point to a template instance; in this case it will be rendered in its entirety. ``extend-macro`` ^^^^^^^^^^^^^^^^ Extends a macro. Syntax ~~~~~~ ``metal:extend-macro`` syntax:: argument ::= expression Description ~~~~~~~~~~~ To extend an existing macro, choose a name for the macro and add a define-macro attribute to a document element with the name as the argument. Add an extend-macro attribute to the document element with an expression referencing the base macro as the argument. The extend-macro must be used in conjunction with define-macro, and must not be used with use-macro. The element's subtree is the macro body. Examples ~~~~~~~~ :: <div metal:define-macro="page-header" metal:extend-macro="standard_macros['page-header']"> <div metal:fill-slot="breadcrumbs"> You are here: <div metal:define-slot="breadcrumbs"/> </div> </div> .. _i18n: Translation (I18N) ################## Translation of template contents and attributes is supported via the ``i18n`` namespace and message objects. Messages -------- The translation machinery defines a message as *any object* which is not a string or a number and which does not provide an ``__html__`` method. When any such object is inserted into the template, the translate function is invoked first to see if it needs translation. The result is always coerced to a native string before it's inserted into the template. Translation function -------------------- The simplest way to hook into the translation machinery is to provide a translation function to the template constructor or at render-time. In either case it should be passed as the keyword argument ``translate``. The function has the following signature: .. code-block:: python def translate(msgid, domain=None, mapping=None, context=None, target_language=None, default=None): ... The result should be a string or ``None``. If another type of object is returned, it's automatically coerced into a string. If `zope.i18n <http://pypi.python.org/pypi/zope.i18n>`_ is available, the translation machinery defaults to using its translation function. Note that this function requires messages to conform to the message class from `zope.i18nmessageid <http://pypi.python.org/pypi/zope.i18nmessageid>`_; specifically, messages must have attributes ``domain``, ``mapping`` and ``default``. Example use: .. code-block:: python from zope.i18nmessageid import MessageFactory _ = MessageFactory("food") apple = _(u"Apple") There's currently no further support for other translation frameworks. Using Zope's translation framework ----------------------------------- The translation function from ``zope.i18n`` relies on *translation domains* to provide translations. These are components that are registered for some translation domain identifier and which implement a ``translate`` method that translates messages for that domain. .. note:: To register translation domain components, the Zope Component Architecture must be used (see `zope.component <http://pypi.python.org/pypi/zope.component>`_). The easiest way to configure translation domains is to use the the ``registerTranslations`` ZCML-directive; this requires the use of the `zope.configuration <http://pypi.python.org/pypi/zope.configuration>`_ package. This will set up translation domains and gettext catalogs automatically: .. code-block:: xml <configure xmlns="http://namespaces.zope.org/zope" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> <i18n:registerTranslations directory="locales" /> </configure> The ``./locales`` directory must follow a particular directory structure: .. code-block:: bash ./locales/en/LC_MESSAGES ./locales/de/LC_MESSAGES ... In each of the ``LC_MESSAGES`` directories, one `GNU gettext <http://en.wikipedia.org/wiki/GNU_gettext>`_ file in the ``.po`` format must be present per translation domain: .. code-block:: po # ./locales/de/LC_MESSAGES/food.po msgid "" msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "Apple" msgstr "Apfel" It may be necessary to compile the message catalog using the ``msgfmt`` utility. This will produce a ``.mo`` file. Translation domains without gettext ----------------------------------- The following example demonstrates how to manually set up and configure a translation domain for which messages are provided directly:: from zope import component from zope.i18n.simpletranslationdomain import SimpleTranslationDomain food = SimpleTranslationDomain("food", { ('de', u'Apple'): u'Apfel', }) component.provideUtility(food, food.domain) An example of a custom translation domain class:: from zope import interface class TranslationDomain(object): interface.implements(ITranslationDomain) def translate(self, msgid, mapping=None, context=None, target_language=None, default=None): ... component.provideUtility(TranslationDomain(), name="custom") This approach can be used to integrate other translation catalog implementations. .. highlight:: xml Namespace --------- The ``i18n`` namespace URI and recommended prefix are currently defined as:: xmlns:i18n="http://xml.zope.org/namespaces/i18n" This is not a URL, but merely a unique identifier. Do not expect a browser to resolve it successfully. Statements ---------- The allowable ``i18n`` statements are: - ``i18n:translate`` - ``i18n:domain`` - ``i18n:context`` - ``i18n:source`` - ``i18n:target`` - ``i18n:name`` - ``i18n:attributes`` - ``i18n:data`` - ``i18n:comment`` - ``i18n:ignore`` - ``i18n:ignore-attributes`` ``i18n:translate`` ^^^^^^^^^^^^^^^^^^ This attribute is used to mark units of text for translation. If this attribute is specified with an empty string as the value, the message ID is computed from the content of the element bearing this attribute. Otherwise, the value of the element gives the message ID. ``i18n:domain`` ^^^^^^^^^^^^^^^ The ``i18n:domain`` attribute is used to specify the domain to be used to get the translation. If not specified, the translation services will use a default domain. The value of the attribute is used directly; it is not a TALES expression. ``i18n:context`` ^^^^^^^^^^^^^^^^ The ``i18n:context`` attribute is used to specify the context to be used to get the translation. If not specified, the translation services will use a default context. The context is generally use to distinguish identical texts in different context (because in a translation this may not be the case.) The value of the attribute is used literally; it is not an expression. ``i18n:source`` ^^^^^^^^^^^^^^^ The ``i18n:source`` attribute specifies the language of the text to be translated. The default is ``nothing``, which means we don't provide this information to the translation services. ``i18n:target`` ^^^^^^^^^^^^^^^ The ``i18n:target`` attribute specifies the language of the translation we want to get. If the value is ``default``, the language negotiation services will be used to choose the destination language. If the value is ``nothing``, no translation will be performed; this can be used to suppress translation within a larger translated unit. Any other value must be a language code. The attribute value is a TALES expression; the result of evaluating the expression is the language code or one of the reserved values. .. note:: ``i18n:target`` is primarily used for hints to text extraction tools and translation teams. If you had some text that should only be translated to e.g. German, then it probably shouldn't be wrapped in an ``i18n:translate`` span. ``i18n:name`` ^^^^^^^^^^^^^ Name the content of the current element for use in interpolation within translated content. This allows a replaceable component in content to be re-ordered by translation. For example:: <span i18n:translate=''> <span tal:replace='context.name' i18n:name='name' /> was born in <span tal:replace='context.country_of_birth' i18n:name='country' />. </span> would cause this text to be passed to the translation service:: "${name} was born in ${country}." ``i18n:attributes`` ^^^^^^^^^^^^^^^^^^^ This attribute will allow us to translate attributes of HTML tags, such as the ``alt`` attribute in the ``img`` tag. The ``i18n:attributes`` attribute specifies a list of attributes to be translated with optional message IDs for each; if multiple attribute names are given, they must be separated by semicolons. Message IDs used in this context must not include whitespace. Note that the value of the particular attributes come either from the HTML attribute value itself or from the data inserted by ``tal:attributes``. If an attibute is to be both computed using ``tal:attributes`` and translated, the translation service is passed the result of the TALES expression for that attribute. An example:: <img src="http://foo.com/logo" alt="Visit us" tal:attributes="alt context.greeting" i18n:attributes="alt" > In this example, we let ``tal:attributes`` set the value of the ``alt`` attribute to the text "Stop by for a visit!". This text will be passed to the translation service, which uses the result of language negotiation to translate "Stop by for a visit!" into the requested language. The example text in the template, "Visit us", will simply be discarded. Another example, with explicit message IDs:: <img src="../icons/uparrow.png" alt="Up" i18n:attributes="src up-arrow-icon; alt up-arrow-alttext" > Here, the message ID ``up-arrow-icon`` will be used to generate the link to an icon image file, and the message ID 'up-arrow-alttext' will be used for the "alt" text. ``i18n:data`` ^^^^^^^^^^^^^ Since TAL always returns strings, we need a way in ZPT to translate objects, one of the most obvious cases being ``datetime`` objects. The ``data`` attribute will allow us to specify such an object, and ``i18n:translate`` will provide us with a legal format string for that object. If ``data`` is used, ``i18n:translate`` must be used to give an explicit message ID, rather than relying on a message ID computed from the content. ``i18n:comment`` ^^^^^^^^^^^^^^^^ The ``i18n:comment`` attribute can be used to add extra comments for translators. It is not used by Chameleon for processing, but will be picked up by tools like `lingua <http://pypi.python.org/pypi/lingua>`_. An example: <h3 i18n:comment="Header for the news section" i18n:translate="">News</h3> ``i18n:ignore`` ^^^^^^^^^^^^^^^ The ``i18n:ignore`` attribute can be used to inform translation extraction tools like `i18ndude <http://pypi.python.org/pypi/i18ndude>`_ to not give a warning/error on the given tag if there is no ``i18n:translate`` attribute. An example: <h1 i18n:ignore="">News</h3> ``i18n:ignore-attributes`` ^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``i18n:ignore-attributes``, just like ``i18n:ignore`` is expected to be used by translation extraction tools like `i18ndude <http://pypi.python.org/pypi/i18ndude>`_. If ``i18n:ignore`` makes text within a tag to be ignored, ``i18n:ignore-attributes`` marks the given attributes as ignored. An example: <a href="http://python.org" title="Python!" i18n:ignore-attributes="title">Python website</a> Relation with TAL processing ---------------------------- The attributes defined in the ``i18n`` namespace modify the behavior of the TAL interpreter for the ``tal:attributes``, ``tal:content``, ``tal:repeat``, and ``tal:replace`` attributes, but otherwise do not affect TAL processing. Since these attributes only affect TAL processing by causing translations to occur at specific times, using these with a TAL processor which does not support the ``i18n`` namespace degrades well; the structural expectations for a template which uses the ``i18n`` support is no different from those for a page which does not. The only difference is that translations will not be performed in a legacy processor. Relation with METAL processing ------------------------------- When using translation with METAL macros, the internationalization context is considered part of the specific documents that page components are retrieved from rather than part of the combined page. This makes the internationalization context lexical rather than dynamic, making it easier for a site builder to understand the behavior of each element with respect to internationalization. Let's look at an example to see what this means:: <html i18n:translate='' i18n:domain='EventsCalendar' metal:use-macro="container['master.html'].macros.thismonth"> <div metal:fill-slot='additional-notes'> <ol tal:condition="context.notes"> <li tal:repeat="note context.notes"> <tal:block tal:omit-tag="" tal:condition="note.heading"> <strong tal:content="note.heading"> Note heading goes here </strong> <br /> </tal:block> <span tal:replace="note/description"> Some longer explanation for the note goes here. </span> </li> </ol> </div> </html> And the macro source:: <html i18n:domain='CalendarService'> <div tal:replace='python:DateTime().Month()' i18n:translate=''>January</div> <!-- really hairy TAL code here ;-) --> <div define-slot="additional-notes"> Place for the application to add additional notes if desired. </div> </html> Note that the macro is using a different domain than the application (which it should be). With lexical scoping, no special markup needs to be applied to cause the slot-filler in the application to be part of the same domain as the rest of the application's page components. If dynamic scoping were used, the internationalization context would need to be re-established in the slot-filler. Extracting translatable message ------------------------------- Translators use `PO files <http://www.gnu.org/software/hello/manual/gettext/PO-Files.html>`_ when translating messages. To create and update PO files you need to do two things: *extract* all messages from python and templates files and store them in a ``.pot`` file, and for each language *update* its ``.po`` file. Chameleon facilitates this by providing extractors for `Babel <http://babel.edgewall.org/>`_. To use this you need modify ``setup.py``. For example: .. code-block:: python from setuptools import setup setup(name="mypackage", install_requires = [ "Babel", ], message_extractors = { "src": [ ("**.py", "chameleon_python", None ), ("**.pt", "chameleon_xml", None ), ]}, ) This tells Babel to scan the ``src`` directory while using the ``chameleon_python`` extractor for all ``.py`` files and the ``chameleon_xml`` extractor for all ``.pt`` files. You can now use Babel to manage your PO files: .. code-block:: bash python setup.py extract_messages --output-file=i18n/mydomain.pot python setup.py update_catalog \ -l nl \ -i i18n/mydomain.pot \ -o i18n/nl/LC_MESSAGES/mydomain.po python setup.py compile_catalog \ --directory i18n --locale nl You can also configure default options in a ``setup.cfg`` file. For example:: [compile_catalog] domain = mydomain directory = i18n [extract_messages] copyright_holder = Acme Inc. output_file = i18n/mydomain.pot charset = UTF-8 [init_catalog] domain = mydomain input_file = i18n/mydomain.pot output_dir = i18n [update_catalog] domain = mydomain input_file = i18n/mydomain.pot output_dir = i18n previous = true You can now use the Babel commands directly:: python setup.py extract_messages python setup.py update_catalog python setup.py compile_catalog ${...} operator ############### The ``${...}`` notation is short-hand for text insertion. The Python-expression inside the braces is evaluated and the result included in the output (all inserted text is escaped by default): .. code-block:: html <div id="section-${index + 1}"> ${content} </div> To escape this behavior, prefix the notation with a backslash character: ``\${...}``. Note that if an object implements the ``__html__`` method, the result of this method will be inserted as-is (without XML escaping). Code blocks ########### The ``<?python ... ?>`` notation allows you to embed Python code in templates: .. code-block:: html <div> <?python numbers = map(str, range(1, 10)) ?> Please input a number from the range ${", ".join(numbers)}. </div> The scope of name assignments is up to the nearest macro definition, or the template, if macros are not used. Note that code blocks can span multiple line and start on the next line of where the processing instruction begins: .. code-block:: html <?python foo = [1, 2, 3] ?> You can use this to debug templates: .. code-block:: html <div> <?python import pdb; pdb.set_trace() ?> </div> Markup comments ############### You can apply the "!" and "?" modifiers to change how comments are processed: Drop ``<!--! This comment will be dropped from output -->`` Verbatim ``<!--? This comment will be included verbatim -->`` That is, evaluation of ``${...}`` expressions is disabled if the comment opens with the "?" character. .. _new-features: Language extensions ################### Chameleon extends the *page template* language with a new expression types and language features. Some take inspiration from `Genshi <http://genshi.edgewall.org/>`_. *New expression types* The :ref:`structure <structure-expression>` expression wraps an expression result as *structure*:: <div>${structure: body.text}</div> The :ref:`import <import-expression>` expression imports module globals:: <div tal:define="compile import: re.compile"> ... </div> The :ref:`load <load-expression>` expression loads templates relative to the current template:: <div tal:define="compile load: main.pt"> ... </div> *Tuple unpacking* The ``tal:define`` and ``tal:repeat`` statements support tuple unpacking:: tal:define="(a, b, c) [1, 2, 3]" Extended `iterable unpacking <http://www.python.org/dev/peps/pep-3132/>`_ using the asterisk character is not currently supported (even for versions of Python that support it natively). *Dictionary lookup as fallback after attribute error* If attribute lookup (using the ``obj.<name>`` syntax) raises an ``AttributeError`` exception, a secondary lookup is attempted using dictionary lookup --- ``obj['<name>']``. Behind the scenes, this is done by rewriting all attribute-lookups to a custom lookup call: .. code-block:: python def lookup_attr(obj, key): try: return getattr(obj, key) except AttributeError as exc: try: get = obj.__getitem__ except AttributeError: raise exc try: return get(key) except KeyError: raise exc *Inline string substitution* In element attributes and in the text or tail of an element, string expression interpolation is available using the ``${...}`` syntax:: <span class="content-${item_type}"> ${title or item_id} </span> *Code blocks* Using ``<?python ... ?>`` notation, you can embed Python statements in your templates: .. code-block:: html <div> <?python numbers = map(str, range(1, 10)) ?> Please input a number from the range ${", ".join(numbers)}. </div> *Literal content* While the ``tal:content`` and ``tal:repeat`` attributes both support the ``structure`` keyword which inserts the content as a literal (without XML-escape), an object may also provide an ``__html__`` method to the same effect. The result of the method will be inserted as *structure*. This is particularly useful for content which is substituted using the expression operator: ``"${...}"`` since the ``structure`` keyword is not allowed here. *Switch statement* Two new attributes have been added: ``tal:switch`` and ``tal:case``. A case attribute works like a condition and only allows content if the value matches that of the nearest parent switch value. Incompatibilities and differences ################################# There are a number of incompatibilities and differences between the Chameleon language implementation and the Zope reference implementation (ZPT): *Default expression* The default expression type is Python. *Template arguments* Arguments passed by keyword to the render- or call method are inserted directly into the template execution namespace. This is different from ZPT where these are only available through the ``options`` dictionary. Zope:: <div tal:content="options/title" /> Chameleon:: <div tal:content="title" /> *Special symbols* The ``CONTEXTS`` symbol is not available. The `z3c.pt <http://pypi.python.org/pypi/z3c.pt>`_ package works as a compatibility layer. The template classes in this package provide a implementation which is fully compatible with ZPT. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/docs/requirements.txt���������������������������������������������������������������0000664�0000000�0000000�00000000017�13700675072�0017677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Chameleon>=3.3 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/setup.py����������������������������������������������������������������������������0000664�0000000�0000000�00000004106�13700675072�0015200�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������__version__ = '3.8.1' 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 :: 3.9", "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.8.1/src/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13700675072�0014254�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�13700675072�0016207�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/__init__.py�����������������������������������������������������������0000664�0000000�0000000�00000000410�13700675072�0020313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from .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.8.1/src/chameleon/ast25.py��������������������������������������������������������������0000664�0000000�0000000�00000010237�13700675072�0017522�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- 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.8.1/src/chameleon/astutil.py������������������������������������������������������������0000664�0000000�0000000�00000073661�13700675072�0020263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- 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 AST_NONE = ast.Name(id='None', ctx=ast.Load()) 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(body, replacement, name): root = ast.Expression(body=body) 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) # JoinedStr(expr* values) def visit_JoinedStr(self, node): if node.values: self._write('"".join((') for value in node.values: self.visit(value) self._write(',') self._write('))') else: self._write('""') # FormattedValue(expr value) def visit_FormattedValue(self, node): if node.conversion == ord('r'): self._write('repr') elif node.conversion == ord('a'): self._write('ascii') else: self._write('str') self._write('(') self.visit(node.value) if node.format_spec is not None: self._write(').__format__(') self.visit(node.format_spec) self._write(')') # 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) elif isinstance(node.slice, ast.Slice): self.visit_Slice(node.slice, True) else: self.visit(node.slice) self._write(']') # Slice(expr? lower, expr? upper, expr? step) def visit_Slice(self, node, subscription=False): if subscription: 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) else: self._write('slice(') self.visit(getattr(node, "lower", None) or AST_NONE) self._write(', ') self.visit(getattr(node, "upper", None) or AST_NONE) self._write(', ') self.visit(getattr(node, "step", None) or AST_NONE) self._write(')') # 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.8.1/src/chameleon/benchmark.py����������������������������������������������������������0000664�0000000�0000000�00000035442�13700675072�0020523�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import unittest import time import os import re from .utils import text_ re_amp = re.compile(r'&(?!([A-Za-z]+|#[0-9]+);)') BIGTABLE_ZPT = """\ <table xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal"> <tr tal:repeat="row python: options['table']"> <td tal:repeat="c python: row.values()"> <span tal:define="d python: c + 1" tal:attributes="class python: 'column-' + str(d)" tal:content="python: d" /> </td> </tr> </table>""" MANY_STRINGS_ZPT = """\ <table xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal"> <tr tal:repeat="i python: xrange(1000)"> <td tal:content="string: number ${i}" /> </tr> </table> """ HELLO_WORLD_ZPT = """\ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal"> <body> <h1>Hello, world!</h1> </body> </html> """ I18N_ZPT = """\ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> <body> <div tal:repeat="i python: xrange(10)"> <div i18n:translate=""> Hello world! </div> <div i18n:translate="hello_world"> Hello world! </div> <div i18n:translate=""> <sup>Hello world!</sup> </div> </div> </body> </html> """ 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 "</", "span", ">\n" tag(END) yield "</", "td", ">\n" tag(END) yield "</", "tr", ">\n" tag(END) yield "</", "html", ">\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 "</", "span", ">\n" tag(END) yield "</", "td", ">\n" tag(END) yield "</", "tr", ">\n" tag(END) yield "</", "html", ">\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%s>%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.8.1/src/chameleon/codegen.py������������������������������������������������������������0000664�0000000�0000000�00000014640�13700675072�0020172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������try: 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.8.1/src/chameleon/compiler.py�����������������������������������������������������������0000664�0000000�0000000�00000155317�13700675072�0020407�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import 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 .astutil import Node 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 .nodes import Is from .nodes import IsNot from .nodes import Equals from .nodes import Logical from .nodes import And 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 from .utils import join 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>" 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_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 EmitText(Node): """Append text to output.""" _fields = "s", class Scope(Node): """"Set a local output scope.""" _fields = "body", "append", "stream" body = None append = None stream = None class Interpolator(object): braces_required_regex = re.compile( r'(\$)?\$({(?P<expression>.*)})', re.DOTALL) braces_optional_regex = re.compile( r'(\$)?\$({(?P<expression>.*)}|(?P<variable>[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 ${<expression>} 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>", 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( default=node.default, default_marker=node.default_marker ) compiler = engine.parse(node.value) return compiler.assign_value(target) def visit_Copy(self, node, target): return self.translate(node.expression, target) def visit_Substitution(self, node, target): engine = self.engine_factory( default=node.default, default_marker=node.default_marker ) 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_BinOp(self, node, target): expression = self.translate(node.left, "__expression") value = self.translate(node.right, "__value") op = { Is: "is", IsNot: "is not", Equals: "==", }[node.op] return expression + value + \ template("TARGET = __expression %s __value" % op, TARGET=target) def visit_Boolean(self, node, target): engine = self.engine_factory( default=node.default, default_marker=node.default_marker, ) 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, default_marker=expr.default_marker ) elif isinstance(expr, Value): engine = self.engine_factory( default=expr.default, default_marker=expr.default_marker ) 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)] def visit_Symbol(self, node, target): value = annotated(node) return template("TARGET = SYMBOL", TARGET=target, SYMBOL=node) 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) class Generator(TemplateCodeGenerator): scopes = [Scope()] def visit_EmitText(self, node): append = load(self.scopes[-1].append or "__append") for node in template("append(s)", append=append, s=ast.Str(s=node.s)): self.visit(node) def visit_Scope(self, node): self.scopes.append(node) body = list(node.body) swap(body, load(node.append), "__append") if node.stream: swap(body, load(node.stream), "__stream") for node in body: self.visit(node) self.scopes.pop() generator = Generator(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(( "__filename = %r\n" % filename, 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) result = [] for key, group in itertools.groupby(iterator, lambda node: node.__class__): nodes = list(group) if key is EmitText: text = join(node.s for node in nodes) nodes = [EmitText(text)] result.extend(nodes) return result 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") else: body += template("from sys import intern") body += template("__default = intern('__default__')") 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): yield EmitText(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" def step(expressions, body, condition): for i, expression in enumerate(reversed(expressions)): stmts = evaluate(expression, body) if i > 0: stmts.append( ast.If( ast.Compare( left=load(target), ops=[ast.Is()], comparators=[load(str(condition))] ), body, None ) ) body = stmts return body def evaluate(node, body=None): if isinstance(node, Logical): condition = isinstance(node, And) return step(node.expressions, body, condition) return self._engine(node, target) body = evaluate(node.expression) orelse = getattr(node, "orelse", None) body.append( ast.If( test=load(target), body=self.visit(node.node) or [ast.Pass()], orelse=self.visit(orelse) if orelse else None, ) ) return body 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) body.append(Scope(code, append, stream)) # 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: yield EmitText(node.prefix + node.name) for stmt in self.visit(node.attributes): yield stmt yield EmitText(node.suffix) else: yield EmitText(node.prefix + node.name + node.suffix) def visit_End(self, node): yield EmitText(node.prefix + node.name + node.space + node.suffix) 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 [EmitText(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: # Skip re-evaluation if self._expression_cache.get(expression): continue name = identifier("cache", id(expression)) target = store(name) 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: assert self._expression_cache.get(expression) is not None name = identifier("cache", id(expression)) target = store(name) body += self._engine(node.value, target) body += self.visit(node.node) return body def visit_UseInternalMacro(self, node): if node.name is None: render = "render" else: render = "render_%s" % mangle(node.name) 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) body.append(Scope(code, append)) # 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.8.1/src/chameleon/config.py�������������������������������������������������������������0000664�0000000�0000000�00000003534�13700675072�0020033�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import 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.8.1/src/chameleon/exc.py����������������������������������������������������������������0000664�0000000�0000000�00000021053�13700675072�0017341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*- import traceback from .utils import create_formatted_exception 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, value_repr): kwargs = rcontext.copy() kwargs.update(econtext) for name in tuple(kwargs): if name.startswith('__'): del kwargs[name] self._errors = errors self._kwargs = kwargs self._value_repr = value_repr def __call__(self): # Format keyword arguments; consecutive arguments are indented # for readability formatted = [ "%s: %s" % (name, self._value_repr(value)) for name, value in self._kwargs.items() ] 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 "<string>" 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.8.1/src/chameleon/i18n.py���������������������������������������������������������������0000664�0000000�0000000�00000007304�13700675072�0017344�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################## # # 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'(?<!\$)(\$(?:(%(n)s)|{(%(n)s)}))' % ({'n': NAME_RE})) try: # pragma: no cover str = unicode except NameError: pass # BBB: The ``fast_translate`` function here is kept for backwards # compatibility reasons. Do not use! try: # pragma: no cover from zope.i18n import interpolate from zope.i18n import translate from zope.i18nmessageid import Message except ImportError: # pragma: no cover pass else: # pragma: no cover def fast_translate(msgid, domain=None, mapping=None, context=None, target_language=None, default=None): if msgid is None: return if target_language is not None or context is not None: result = translate( msgid, domain=domain, mapping=mapping, context=context, target_language=target_language, default=default) if result != msgid: return result if isinstance(msgid, Message): default = msgid.default mapping = msgid.mapping if default is None: default = str(msgid) if not isinstance(default, basestring): return default return interpolate(default, mapping) def simple_translate(msgid, domain=None, mapping=None, context=None, target_language=None, default=None): if default is None: default = getattr(msgid, "default", msgid) if mapping is None: mapping = getattr(msgid, "mapping", None) if mapping: def replace(match): whole, param1, param2 = match.groups() return unicode_string(mapping.get(param1 or param2, whole)) return _interp_regex.sub(replace, default) return default def parse_attributes(attrs, xml=True): d = {} # filter out empty items, eg: # i18n:attributes="value msgid; name msgid2;" # would result in 3 items where the last one is empty attrs = [spec for spec in attrs.split(";") if spec] for spec in attrs: if ',' in spec: raise CompilationError( "Attribute must not contain comma. Use semicolon to " "list multiple attributes", spec ) parts = spec.split() if len(parts) == 2: attr, msgid = parts elif len(parts) == 1: attr = parts[0] msgid = None else: raise CompilationError( "Illegal i18n:attributes specification.", spec) if not xml: attr = attr.lower() attr = attr.strip() if attr in d: raise CompilationError( "Attribute may only be specified once in i18n:attributes", attr) d[attr] = msgid return d ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/interfaces.py���������������������������������������������������������0000664�0000000�0000000�00000005223�13700675072�0020706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from zope.interface import Interface from zope.interface import Attribute class ITALExpressionErrorInfo(Interface): type = Attribute("type", "The exception class.") value = Attribute("value", "The exception instance.") lineno = Attribute("lineno", "The line number the error occurred on in the source.") offset = Attribute("offset", "The character offset at which the error occurred.") class ITALIterator(Interface): # pragma: no cover """A TAL iterator Not to be confused with a Python iterator. """ def next(): """Advance to the next value in the iteration, if possible Return a true value if it was possible to advance and return a false value otherwise. """ class ITALESIterator(ITALIterator): # pragma: no cover """TAL Iterator provided by TALES Values of this iterator are assigned to items in the repeat namespace. For example, with a TAL statement like: tal:repeat="item items", an iterator will be assigned to "repeat/item". The iterator provides a number of handy methods useful in writing TAL loops. The results are undefined of calling any of the methods except 'length' before the first iteration. """ def index(): """Return the position (starting with "0") within the iteration """ def number(): """Return the position (starting with "1") within the iteration """ def even(): """Return whether the current position is even """ def odd(): """Return whether the current position is odd """ def parity(): """Return 'odd' or 'even' depending on the position's parity Useful for assigning CSS class names to table rows. """ def start(): """Return whether the current position is the first position """ def end(): """Return whether the current position is the last position """ def letter(): """Return the position (starting with "a") within the iteration """ def Letter(): """Return the position (starting with "A") within the iteration """ def roman(): """Return the position (starting with "i") within the iteration """ def Roman(): """Return the position (starting with "I") within the iteration """ def item(): """Return the item at the current position """ def length(): """Return the length of the sequence Note that this may fail if the TAL iterator was created on a Python iterator. """ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/loader.py�������������������������������������������������������������0000664�0000000�0000000�00000012567�13700675072�0020042�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import functools import logging import os import py_compile import shutil import sys import tempfile import warnings import pkg_resources try: from importlib.machinery import SourceFileLoader from threading import RLock lock = RLock() acquire_lock = lock.acquire release_lock = lock.release del lock except ImportError: from imp import acquire_lock, release_lock, load_source class SourceFileLoader: def __init__(self, base, filename): self.base = base self.filename = filename def load_module(self): try: acquire_lock() assert self.base not in sys.modules with open(self.filename, 'rb') as f: return load_source(self.base, self.filename, f) finally: release_lock() log = logging.getLogger('chameleon.loader') from .utils import string_type from .utils import encode_string def cache(func): def load(self, *args, **kwargs): template = self.registry.get(args) if template is None: self.registry[args] = template = func(self, *args, **kwargs) return template return load def abspath_from_asset_spec(spec): pname, filename = spec.split(':', 1) return pkg_resources.resource_filename(pname, filename) if os.name == "nt": def abspath_from_asset_spec(spec, f=abspath_from_asset_spec): if spec[1] == ":": return spec return f(spec) class TemplateLoader(object): """Template loader class. To load templates using relative filenames, pass a sequence of paths (or a single path) as ``search_path``. To apply a default filename extension to inputs which do not have an extension already (i.e. no dot), provide this as ``default_extension`` (e.g. ``'.pt'``). Additional keyword-arguments will be passed on to the template constructor. """ default_extension = None def __init__(self, search_path=None, default_extension=None, **kwargs): if search_path is None: search_path = [] if isinstance(search_path, string_type): search_path = [search_path] if default_extension is not None: self.default_extension = ".%s" % default_extension.lstrip('.') self.search_path = search_path self.registry = {} self.kwargs = kwargs @cache def load(self, spec, cls=None): if cls is None: raise ValueError("Unbound template loader.") spec = spec.strip() if self.default_extension is not None and '.' not in spec: spec += self.default_extension if ':' in spec: spec = abspath_from_asset_spec(spec) if not os.path.isabs(spec): for path in self.search_path: path = os.path.join(path, spec) if os.path.exists(path): spec = path break else: raise ValueError("Template not found: %s." % spec) return cls(spec, search_path=self.search_path, **self.kwargs) def bind(self, cls): return functools.partial(self.load, cls=cls) class MemoryLoader(object): def build(self, source, filename): code = compile(source, filename, 'exec') env = {} exec(code, env) return env def get(self, name): return None class ModuleLoader(object): def __init__(self, path, remove=False): self.path = path self.remove = remove def __del__(self, shutil=shutil): if not self.remove: return try: shutil.rmtree(self.path) except: warnings.warn("Could not clean up temporary file path: %s" % (self.path,)) def get(self, filename): path = os.path.join(self.path, filename) if os.path.exists(path): log.debug("loading module from cache: %s." % filename) base, ext = os.path.splitext(filename) return self._load(base, path) else: log.debug('cache miss: %s' % filename) def build(self, source, filename): acquire_lock() try: d = self.get(filename) if d is not None: return d base, ext = os.path.splitext(filename) name = os.path.join(self.path, base + ".py") log.debug("writing source to disk (%d bytes)." % len(source)) fd, fn = tempfile.mkstemp(prefix=base, suffix='.tmp', dir=self.path) temp = os.fdopen(fd, 'wb') encoded = source.encode('utf-8') header = encode_string("# -*- coding: utf-8 -*-" + "\n") try: try: temp.write(header) temp.write(encoded) finally: temp.close() except: os.remove(fn) raise os.rename(fn, name) log.debug("compiling %s into byte-code..." % filename) py_compile.compile(name) return self._load(base, name) finally: release_lock() def _load(self, base, filename): acquire_lock() try: module = sys.modules.get(base) if module is None: module = SourceFileLoader(base, filename).load_module() finally: release_lock() return module.__dict__ �����������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/metal.py��������������������������������������������������������������0000664�0000000�0000000�00000001426�13700675072�0017666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################## # # 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. # ############################################################################## WHITELIST = frozenset([ "define-macro", "extend-macro", "use-macro", "define-slot", "fill-slot", "xmlns", "xml" ]) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/namespaces.py���������������������������������������������������������0000664�0000000�0000000�00000000625�13700675072�0020703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_NS = "http://www.w3.org/XML/1998/namespace" XMLNS_NS = "http://www.w3.org/2000/xmlns/" XHTML_NS = "http://www.w3.org/1999/xhtml" TAL_NS = "http://xml.zope.org/namespaces/tal" META_NS = "http://xml.zope.org/namespaces/meta" METAL_NS = "http://xml.zope.org/namespaces/metal" XI_NS = "http://www.w3.org/2001/XInclude" I18N_NS = "http://xml.zope.org/namespaces/i18n" PY_NS = "http://genshi.edgewall.org/" �����������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/nodes.py��������������������������������������������������������������0000664�0000000�0000000�00000010252�13700675072�0017671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from .astutil import Node class UseExternalMacro(Node): """Extend external macro.""" _fields = "expression", "slots", "extend" class Sequence(Node): """Element sequence.""" _fields = "items", def __nonzero__(self): return bool(self.items) class Content(Node): """Content substitution.""" _fields = "expression", "char_escape", "translate" class CodeBlock(Node): _fields = "source", class Value(Node): """Expression object value.""" _fields = "value", "default", "default_marker" default = None default_marker = None def __repr__(self): try: line, column = self.value.location except AttributeError: line, column = 0, 0 return "<%s %r (%d:%d)>" % ( type(self).__name__, self.value, line, column ) class Substitution(Value): """Expression value for text substitution.""" _fields = "value", "char_escape", "default", "default_marker" default = None class Boolean(Value): _fields = "value", "s", "default", "default_marker" 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", "default", "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 one of the condition holds.""" _fields = "expression", "node", "orelse" class Op(Node): """An operator node.""" class Is(Op): """Object identity.""" class IsNot(Op): """Object identity.""" class Equals(Op): """Object equality.""" class Logical(Node): """Logical operator.""" _fields = "expressions", class And(Logical): """All terms must be met.""" class Or(Logical): """At least one term must be met.""" class BinOp(Node): """Binary comparison.""" _fields = "left", "op", "right" class Cache(Node): """Cache (evaluate only once) the value of ``expression`` inside ``node``. """ _fields = "expressions", "node" class Cancel(Cache): _fields = "expressions", "node", "value" 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", "default", "default_marker" 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.8.1/src/chameleon/parser.py�������������������������������������������������������������0000664�0000000�0000000�00000016427�13700675072�0020067�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import 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<prefix></?)(?P<name>([^:\n\r ]+:)?[^ \n\t\r>/]+)' r'(?P<suffix>(?P<space>\s*)/?>)?', re.UNICODE | re.DOTALL) match_single_attribute = re.compile( r'(?P<space>\s+)(?!\d)' r'(?P<name>[^ =/>\n\t\r]+)' r'((?P<eq>\s*=\s*)' r'((?P<quote>[\'"])(?P<value>.*?)(?P=quote)|' r'(?P<alt_value>[^\s\'">/]+))|' r'(?P<simple_value>(?![ \\n\\t\\r]*=)))', re.UNICODE | re.DOTALL) match_comment = re.compile( r'^<!--(?P<text>.*)-->$', re.DOTALL) match_cdata = re.compile( r'^<!\[CDATA\[(?P<text>.*)\]>$', re.DOTALL) match_declaration = re.compile( r'^<!(?P<text>[^>]+)>$', re.DOTALL) match_processing_instruction = re.compile( r'^<\?(?P<name>\w+)(?P<text>.*?)\?>', 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("<!--"): m = match_double_hyphen.search(string[4:]) if m is not None: raise ParseError( "The string '--' is not allowed in a comment.", string[4 + m.start():4 + m.end()] ) return "comment" if string.startswith("<![CDATA["): return "cdata" if string.startswith("<!"): return "declaration" if string.startswith("<?xml"): return "xml_declaration" if string.startswith("<?"): return "processing_instruction" if string.startswith("</"): return "end_tag" if string.endswith("/>"): return "empty_tag" if string.endswith(">"): return "start_tag" return "error" return "text" class ElementParser(object): """Parses tokens into elements.""" def __init__(self, stream, default_namespaces, restricted_namespace=True): self.stream = stream self.queue = [] self.index = [] self.namespaces = [default_namespaces.copy()] self.restricted_namespace = restricted_namespace def __iter__(self): for token in self.stream: item = self.parse(token) self.queue.append(item) return iter(self.queue) def parse(self, token): kind = identify(token) visitor = getattr(self, "visit_%s" % kind, self.visit_default) return visitor(kind, token) def visit_comment(self, kind, token): return "comment", (token, ) def visit_cdata(self, kind, token): return "cdata", (token, ) def visit_default(self, kind, token): return "default", (token, ) def visit_processing_instruction(self, kind, token): m = match_processing_instruction.match(token) if m is None: return self.visit_default(kind, token) return "processing_instruction", (groupdict(m, token), ) def visit_text(self, kind, token): return kind, (token, ) def visit_start_tag(self, kind, token): namespace = self.namespaces[-1].copy() self.namespaces.append(namespace) node = parse_tag(token, namespace, self.restricted_namespace) self.index.append((node['name'], len(self.queue))) return kind, (node, ) def visit_end_tag(self, kind, token): try: namespace = self.namespaces.pop() except IndexError: raise ParseError("Unexpected end tag.", token) node = parse_tag(token, namespace, self.restricted_namespace) while self.index: name, pos = self.index.pop() if name == node['name']: start, = self.queue.pop(pos)[1] children = self.queue[pos:] del self.queue[pos:] break else: raise ParseError("Unexpected end tag.", token) return "element", (start, node, children) def visit_empty_tag(self, kind, token): namespace = self.namespaces[-1].copy() node = parse_tag(token, namespace, self.restricted_namespace) return "element", (node, None, []) def visit_xml_declaration(self, kind, token): return self.visit_empty_tag(kind, token) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/program.py������������������������������������������������������������0000664�0000000�0000000�00000002004�13700675072�0020224�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������try: str = unicode except NameError: long = int from .tokenize import iter_xml from .tokenize import iter_text from .parser import ElementParser from .namespaces import XML_NS from .namespaces import XMLNS_NS class ElementProgram(object): DEFAULT_NAMESPACES = { 'xmlns': XMLNS_NS, 'xml': XML_NS, } tokenizers = { 'xml': iter_xml, 'text': iter_text, } restricted_namespace = True def __init__(self, source, mode="xml", filename=None, tokenizer=None): if tokenizer is None: tokenizer = self.tokenizers[mode] tokens = tokenizer(source, filename) parser = ElementParser(tokens, self.DEFAULT_NAMESPACES, self.restricted_namespace) self.body = [] for kind, args in parser: node = self.visit(kind, args) if node is not None: self.body.append(node) def visit(self, kind, args): visitor = getattr(self, "visit_%s" % kind) return visitor(*args) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/py25.py���������������������������������������������������������������0000664�0000000�0000000�00000001412�13700675072�0017356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import sys def lookup_attr(obj, key): try: return getattr(obj, key) except AttributeError: exc = sys.exc_info()[1] try: get = obj.__getitem__ except AttributeError: raise exc try: return get(key) except KeyError: raise exc def exec_(code, globs=None, locs=None): """Execute code in a namespace.""" if globs is None: frame = sys._getframe(1) globs = frame.f_globals if locs is None: locs = frame.f_locals del frame elif locs is None: locs = globs exec("""exec code in globs, locs""") exec_("""def raise_with_traceback(exc, tb): raise type(exc), exc, tb """) def next(iter): return iter.next() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/py26.py���������������������������������������������������������������0000664�0000000�0000000�00000000510�13700675072�0017355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import sys def lookup_attr(obj, key): try: return getattr(obj, key) except AttributeError: exc = sys.exc_info()[1] try: get = obj.__getitem__ except AttributeError: raise exc try: return get(key) except KeyError: raise exc ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tal.py����������������������������������������������������������������0000775�0000000�0000000�00000030406�13700675072�0017347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import re import copy from .exc import LanguageError from .utils import descriptorint from .utils import descriptorstr from .namespaces import XMLNS_NS from .parser import groups try: next except NameError: from chameleon.py25 import next try: # optional library: `zope.interface` from chameleon import interfaces import zope.interface except ImportError: interfaces = None NAME = r"[a-zA-Z_][-a-zA-Z0-9_]*" DEFINE_RE = re.compile(r"(?s)\s*(?:(global|local)\s+)?" + r"(%s|\(%s(?:,\s*%s)*\))\s+(.*)\Z" % (NAME, NAME, NAME), re.UNICODE) SUBST_RE = re.compile(r"\s*(?:(text|structure)\s+)?(.*)\Z", re.S | re.UNICODE) ATTR_RE = re.compile(r"\s*([^\s{}'\"]+)\s+([^\s].*)\Z", re.S | re.UNICODE) ENTITY_RE = re.compile(r'(&(#?)(x?)(\d{1,5}|\w{1,8});)') WHITELIST = frozenset([ "define", "comment", "condition", "content", "replace", "repeat", "attributes", "on-error", "omit-tag", "script", "switch", "case", "xmlns", "xml" ]) def split_parts(arg): # Break in pieces at undoubled semicolons and # change double semicolons to singles: i = 0 while i < len(arg): m = ENTITY_RE.search(arg[i:]) if m is None: break arg = arg[:i + m.end()] + ';' + arg[i + m.end():] i += m.end() arg = arg.replace(";;", "\0") parts = arg.split(';') parts = [p.replace("\0", ";") for p in parts] if len(parts) > 1 and not parts[-1].strip(): del parts[-1] # It ended in a semicolon return parts def parse_attributes(clause): attrs = [] seen = set() for part in split_parts(clause): m = ATTR_RE.match(part) if not m: name, expr = None, part.strip() else: name, expr = groups(m, part) if name in seen: raise LanguageError( "Duplicate attribute name in attributes.", part) seen.add(name) attrs.append((name, expr)) return attrs def parse_substitution(clause): m = SUBST_RE.match(clause) if m is None: raise LanguageError( "Invalid content substitution syntax.", clause) key, expression = groups(m, clause) if not key: key = "text" return key, expression def parse_defines(clause): """ Parses a tal:define value. # Basic syntax, implicit local >>> parse_defines('hello lovely') [('local', ('hello',), 'lovely')] # Explicit local >>> parse_defines('local hello lovely') [('local', ('hello',), 'lovely')] # With global >>> parse_defines('global hello lovely') [('global', ('hello',), 'lovely')] # Multiple expressions >>> parse_defines('hello lovely; tea time') [('local', ('hello',), 'lovely'), ('local', ('tea',), 'time')] # With multiple names >>> parse_defines('(hello, howdy) lovely') [('local', ['hello', 'howdy'], 'lovely')] # With unicode whitespace >>> try: ... s = '\xc2\xa0hello lovely'.decode('utf-8') ... except AttributeError: ... s = '\xa0hello lovely' >>> from chameleon.utils import unicode_string >>> parse_defines(s) == [ ... ('local', ('hello',), 'lovely') ... ] True """ defines = [] for part in split_parts(clause): m = DEFINE_RE.match(part) if m is None: raise LanguageError("Invalid define syntax", part) context, name, expr = groups(m, part) context = context or "local" if name.startswith('('): names = [n.strip() for n in name.strip('()').split(',')] else: names = (name,) defines.append((context, names, expr)) return defines def prepare_attributes(attrs, dyn_attributes, i18n_attributes, ns_attributes, drop_ns): drop = set([attribute['name'] for attribute, (ns, value) in zip(attrs, ns_attributes) if ns in drop_ns or ( ns == XMLNS_NS and attribute['value'] in drop_ns ) ]) attributes = [] normalized = {} computed = [] for attribute in attrs: name = attribute['name'] if name in drop: continue attributes.append(( name, attribute['value'], attribute['quote'], attribute['space'], attribute['eq'], None, )) normalized[name.lower()] = len(attributes) - 1 for name, expr in dyn_attributes: index = normalized.get(name.lower()) if name else None if index is not None: _, text, quote, space, eq, _ = attributes[index] add = attributes.__setitem__ else: text = None quote = '"' space = " " eq = "=" index = len(attributes) add = attributes.insert if name is not None: normalized[name.lower()] = len(attributes) - 1 attribute = name, text, quote, space, eq, expr add(index, attribute) for name in i18n_attributes: attr = name.lower() if attr not in normalized: attributes.append((name, name, '"', " ", "=", None)) normalized[attr] = len(attributes) - 1 return attributes class RepeatItem(object): __slots__ = "length", "_iterator" __allow_access_to_unprotected_subobjects__ = True def __init__(self, iterator, length): self.length = length self._iterator = iterator def __iter__(self): return self._iterator try: iter(()).__len__ except AttributeError: @descriptorint def index(self): try: remaining = self._iterator.__length_hint__() except AttributeError: remaining = len(tuple(copy.copy(self._iterator))) return self.length - remaining - 1 else: @descriptorint def index(self): remaining = self._iterator.__len__() return self.length - remaining - 1 @descriptorint def start(self): return self.index == 0 @descriptorint def end(self): return self.index == self.length - 1 @descriptorint def number(self): return self.index + 1 @descriptorstr def odd(self): """Returns a true value if the item index is odd. >>> it = RepeatItem(iter(("apple", "pear")), 2) >>> next(it._iterator) 'apple' >>> it.odd() '' >>> next(it._iterator) 'pear' >>> it.odd() 'odd' """ return self.index % 2 == 1 and 'odd' or '' @descriptorstr def even(self): """Returns a true value if the item index is even. >>> it = RepeatItem(iter(("apple", "pear")), 2) >>> next(it._iterator) 'apple' >>> it.even() 'even' >>> next(it._iterator) 'pear' >>> it.even() '' """ return self.index % 2 == 0 and 'even' or '' @descriptorstr def parity(self): """Return 'odd' or 'even' depending on the position's parity Useful for assigning CSS class names to table rows. """ return self.index % 2 == 0 and 'even' or 'odd' def next(self): raise NotImplementedError( "Method not implemented (can't update local variable).") def _letter(self, base=ord('a'), radix=26): """Get the iterator position as a lower-case letter >>> it = RepeatItem(iter(("apple", "pear", "orange")), 3) >>> next(it._iterator) 'apple' >>> it.letter() 'a' >>> next(it._iterator) 'pear' >>> it.letter() 'b' >>> next(it._iterator) 'orange' >>> it.letter() 'c' """ index = self.index if index < 0: raise TypeError("No iteration position") s = "" while 1: index, off = divmod(index, radix) s = chr(base + off) + s if not index: return s letter = descriptorstr(_letter) @descriptorstr def Letter(self): """Get the iterator position as an upper-case letter >>> it = RepeatItem(iter(("apple", "pear", "orange")), 3) >>> next(it._iterator) 'apple' >>> it.Letter() 'A' >>> next(it._iterator) 'pear' >>> it.Letter() 'B' >>> next(it._iterator) 'orange' >>> it.Letter() 'C' """ return self._letter(base=ord('A')) @descriptorstr def Roman(self, rnvalues=( (1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'), (100, 'C'), (90, 'XC'), (50, 'L'), (40, 'XL'), (10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I'))): """Get the iterator position as an upper-case roman numeral >>> it = RepeatItem(iter(("apple", "pear", "orange")), 3) >>> next(it._iterator) 'apple' >>> it.Roman() 'I' >>> next(it._iterator) 'pear' >>> it.Roman() 'II' >>> next(it._iterator) 'orange' >>> it.Roman() 'III' """ n = self.index + 1 s = "" for v, r in rnvalues: rct, n = divmod(n, v) s = s + r * rct return s @descriptorstr def roman(self): """Get the iterator position as a lower-case roman numeral >>> it = RepeatItem(iter(("apple", "pear", "orange")), 3) >>> next(it._iterator) 'apple' >>> it.roman() 'i' >>> next(it._iterator) 'pear' >>> it.roman() 'ii' >>> next(it._iterator) 'orange' >>> it.roman() 'iii' """ return self.Roman().lower() if interfaces is not None: zope.interface.classImplements(RepeatItem, interfaces.ITALESIterator) class RepeatDict(object): """Repeat dictionary implementation. >>> repeat = RepeatDict({}) >>> iterator, length = repeat('numbers', range(5)) >>> length 5 >>> repeat['numbers'] <chameleon.tal.RepeatItem object at ...> >>> repeat.numbers <chameleon.tal.RepeatItem object at ...> >>> getattr(repeat, 'missing_key', None) is None True >>> try: ... from chameleon import interfaces ... interfaces.ITALESIterator(repeat,None) is None ... except ImportError: ... True ... True """ __slots__ = "__setitem__", "__getitem__" def __init__(self, d): self.__setitem__ = d.__setitem__ self.__getitem__ = d.__getitem__ def __getattr__(self,key): try: return self[key] except KeyError: raise AttributeError(key) def __call__(self, key, iterable): """We coerce the iterable to a tuple and return an iterator after registering it in the repeat dictionary.""" iterable = list(iterable) if iterable is not None else () length = len(iterable) iterator = iter(iterable) # Insert as repeat item self[key] = RepeatItem(iterator, length) return iterator, length class ErrorInfo(object): """Information about an exception passed to an on-error handler.""" def __init__(self, err, position=(None, None)): if isinstance(err, Exception): self.type = err.__class__ self.value = err else: self.type = err self.value = None self.lineno = position[0] self.offset = position[1] if interfaces is not None: zope.interface.classImplements(ErrorInfo, interfaces.ITALExpressionErrorInfo) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tales.py��������������������������������������������������������������0000664�0000000�0000000�00000041126�13700675072�0017675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������import re import sys import types import importlib from .astutil import parse from .astutil import store from .astutil import load from .astutil import ItemLookupOnAttributeErrorVisitor from .codegen import TemplateCodeGenerator from .codegen import template from .codegen import reverse_builtin_map from .astutil import Builtin from .astutil import Symbol from .exc import ExpressionError from .utils import ast from .utils import resolve_dotted from .utils import ImportableMarker from .utils import Markup from .tokenize import Token from .parser import substitute from .compiler import Interpolator DEFAULT_MARKER = ImportableMarker(__name__, "DEFAULT") try: from .py26 import lookup_attr except SyntaxError: from .py25 import lookup_attr split_parts = re.compile(r'(?<!\\)\|') match_prefix = re.compile(r'^\s*([a-z][a-z0-9\-_]*):').match re_continuation = re.compile(r'\\\s*$', re.MULTILINE) try: from __builtin__ import basestring except ImportError: basestring = str exc_clear = getattr(sys, "exc_clear", None) def resolve_global(value): name = reverse_builtin_map.get(value) if name is not None: return Builtin(name) return Symbol(value) def test(expression, engine=None, **env): if engine is None: engine = SimpleEngine() body = expression(store("result"), engine) module = ast.Module(body) module = ast.fix_missing_locations(module) env['rcontext'] = {} if exc_clear is not None: env['__exc_clear'] = exc_clear source = TemplateCodeGenerator(module).code code = compile(source, '<string>', 'exec') exec(code, env) result = env["result"] if isinstance(result, basestring): result = str(result) return result def transform_attribute(node): return template( "lookup(object, name)", lookup=Symbol(lookup_attr), object=node.value, name=ast.Str(s=node.attr), mode="eval" ) class TalesExpr(object): """Base class. This class helps implementations for the Template Attribute Language Expression Syntax (TALES). The syntax evaluates one or more expressions, separated by '|' (pipe). The first expression that succeeds, is returned. Expression: expression := (type ':')? line ('|' expression)? line := .* Expression lines may not contain the pipe character unless escaped. It has a special meaning: If the expression to the left of the pipe fails (raises one of the exceptions listed in ``catch_exceptions``), evaluation proceeds to the expression(s) on the right. Subclasses must implement ``translate`` which assigns a value for a given expression. >>> class PythonPipeExpr(TalesExpr): ... def translate(self, expression, target): ... compiler = PythonExpr(expression) ... return compiler(target, None) >>> test(PythonPipeExpr('foo | bar | 42')) 42 >>> test(PythonPipeExpr('foo|42')) 42 """ exceptions = NameError, \ ValueError, \ AttributeError, \ LookupError, \ TypeError ignore_prefix = True def __init__(self, expression): self.expression = expression def __call__(self, target, engine): remaining = self.expression assignments = [] while remaining: if self.ignore_prefix and match_prefix(remaining) is not None: compiler = engine.parse(remaining) assignment = compiler.assign_value(target) remaining = "" else: for m in split_parts.finditer(remaining): expression = remaining[:m.start()] remaining = remaining[m.end():] break else: expression = remaining remaining = "" expression = expression.replace('\\|', '|') assignment = self.translate_proxy(engine, expression, target) assignments.append(assignment) if not assignments: if not remaining: raise ExpressionError("No input:", remaining) assignments.append( self.translate_proxy(engine, remaining, target) ) for i, assignment in enumerate(reversed(assignments)): if i == 0: body = assignment else: body = [ast.TryExcept( body=assignment, handlers=[ast.ExceptHandler( type=ast.Tuple( elts=map(resolve_global, self.exceptions), ctx=ast.Load()), name=None, body=body if exc_clear is None else body + [ ast.Expr( ast.Call( func=load("__exc_clear"), args=[], keywords=[], starargs=None, kwargs=None, ) ) ], )], )] return body def translate_proxy(self, engine, *args): """Default implementation delegates to ``translate`` method.""" return self.translate(*args) def translate(self, expression, target): """Return statements that assign a value to ``target``.""" raise NotImplementedError( "Must be implemented by a subclass.") class PathExpr(TalesExpr): """Path expression compiler. Syntax:: PathExpr ::= Path [ '|' Path ]* Path ::= variable [ '/' URL_Segment ]* variable ::= Name For example:: request/cookies/oatmeal nothing here/some-file 2001_02.html.tar.gz/foo root/to/branch | default When a path expression is evaluated, it attempts to traverse each path, from left to right, until it succeeds or runs out of paths. To traverse a path, it first fetches the object stored in the variable. For each path segment, it traverses from the current object to the subobject named by the path segment. Once a path has been successfully traversed, the resulting object is the value of the expression. If it is a callable object, such as a method or class, it is called. The semantics of traversal (and what it means to be callable) are implementation-dependent (see the ``translate`` method). """ def translate(self, expression, target): raise NotImplementedError( "Path expressions are not yet implemented. " "It's unclear whether a general implementation " "can be devised.") class PythonExpr(TalesExpr): r"""Python expression compiler. >>> test(PythonExpr('2 + 2')) 4 The Python expression is a TALES expression. That means we can use the pipe operator: >>> test(PythonExpr('foo | 2 + 2 | 5')) 4 To include a pipe character, use a backslash escape sequence: >>> test(PythonExpr(r'"\|"')) '|' """ transform = ItemLookupOnAttributeErrorVisitor(transform_attribute) def parse(self, string): return parse(string, 'eval').body def translate(self, expression, target): # Strip spaces string = expression.strip() # Conver line continuations to newlines string = substitute(re_continuation, '\n', string) # Convert newlines to spaces string = string.replace('\n', ' ') try: value = self.parse(string) except SyntaxError: exc = sys.exc_info()[1] raise ExpressionError(exc.msg, string) # Transform attribute lookups to allow fallback to item lookup self.transform.visit(value) return [ast.Assign(targets=[target], value=value)] class ImportExpr(object): re_dotted = re.compile(r'^[A-Za-z.]+$') def __init__(self, expression): self.expression = expression def __call__(self, target, engine): string = self.expression.strip().replace('\n', ' ') value = template( "RESOLVE(NAME)", RESOLVE=Symbol(resolve_dotted), NAME=ast.Str(s=string), mode="eval", ) return [ast.Assign(targets=[target], value=value)] class NotExpr(object): """Negates the expression. >>> engine = SimpleEngine(PythonExpr) >>> test(NotExpr('False'), engine) True >>> test(NotExpr('True'), engine) False """ def __init__(self, expression): self.expression = expression def __call__(self, target, engine): compiler = engine.parse(self.expression) body = compiler.assign_value(target) return body + template("target = not target", target=target) class StructureExpr(object): """Wraps the expression result as 'structure'. >>> engine = SimpleEngine(PythonExpr) >>> test(StructureExpr('\"<tt>foo</tt>\"'), engine) '<tt>foo</tt>' """ wrapper_class = Symbol(Markup) def __init__(self, expression): self.expression = expression def __call__(self, target, engine): compiler = engine.parse(self.expression) body = compiler.assign_value(target) return body + template( "target = wrapper(target)", target=target, wrapper=self.wrapper_class ) class IdentityExpr(object): """Identity expression. Exists to demonstrate the interface. >>> test(IdentityExpr('42')) 42 """ def __init__(self, expression): self.expression = expression def __call__(self, target, engine): compiler = engine.parse(self.expression) return compiler.assign_value(target) class StringExpr(object): """Similar to the built-in ``string.Template``, but uses an expression engine to support pluggable string substitution expressions. Expr string: string := (text | substitution) (string)? substitution := ('$' variable | '${' expression '}') text := .* In other words, an expression string can contain multiple substitutions. The text- and substitution parts will be concatenated back into a string. >>> test(StringExpr('Hello ${name}!'), name='world') 'Hello world!' In the default configuration, braces may be omitted if the expression is an identifier. >>> test(StringExpr('Hello $name!'), name='world') 'Hello world!' The ``braces_required`` flag changes this setting: >>> test(StringExpr('Hello $name!', True)) 'Hello $name!' To avoid interpolation, use two dollar symbols. Note that only a single symbol will appear in the output. >>> test(StringExpr('$${name}')) '${name}' In previous versions, it was possible to escape using a regular backslash coding, but this is no longer supported. >>> test(StringExpr(r'\\${name}'), name='Hello world!') '\\\\Hello world!' Multiple interpolations in one: >>> test(StringExpr("Hello ${'a'}${'b'}${'c'}!")) 'Hello abc!' Here's a more involved example taken from a javascript source: >>> result = test(StringExpr(\"\"\" ... function($$, oid) { ... $('#' + oid).autocomplete({source: ${'source'}}); ... } ... \"\"\")) >>> 'source: source' in result True As noted previously, the double-dollar escape also affects non-interpolation expressions. >>> 'function($, oid)' in result True >>> test(StringExpr('test ${1}${2}')) 'test 12' >>> test(StringExpr('test $${1}${2}')) 'test ${1}2' >>> test(StringExpr('test $$')) 'test $' >>> test(StringExpr('$$.ajax(...)')) '$.ajax(...)' >>> test(StringExpr('test $$ ${1}')) 'test $ 1' In the above examples, the expression is evaluated using the dummy engine which just returns the input as a string. As an example, we'll implement an expression engine which instead counts the number of characters in the expresion and returns an integer result. >>> class engine: ... @staticmethod ... def parse(expression, char_escape=None): ... class compiler: ... @staticmethod ... def assign_text(target): ... return [ ... ast.Assign( ... targets=[target], ... value=ast.Num(n=len(expression)) ... )] ... ... return compiler This will demonstrate how the string expression coerces the input to a string. >>> expr = StringExpr( ... 'There are ${hello world} characters in \"hello world\"') We evaluate the expression using the new engine: >>> test(expr, engine) 'There are 11 characters in \"hello world\"' """ def __init__(self, expression, braces_required=False): # The code relies on the expression being a token string if not isinstance(expression, Token): expression = Token(expression, 0) self.translator = Interpolator(expression, braces_required) def __call__(self, name, engine): return self.translator(name, engine) class ProxyExpr(TalesExpr): braces_required = False def __init__(self, name, expression, ignore_prefix=True): super(ProxyExpr, self).__init__(expression) self.ignore_prefix = ignore_prefix self.name = name def translate_proxy(self, engine, expression, target): translator = Interpolator(expression, self.braces_required) assignment = translator(target, engine) return assignment + [ ast.Assign(targets=[target], value=ast.Call( func=load(self.name), args=[target], keywords=[], starargs=None, kwargs=None )) ] class ExistsExpr(object): """Boolean wrapper. Return 0 if the expression results in an exception, otherwise 1. As a means to generate exceptions, we set up an expression engine which evaluates the provided expression using Python: >>> engine = SimpleEngine(PythonExpr) >>> test(ExistsExpr('int(0)'), engine) 1 >>> test(ExistsExpr('int(None)'), engine) 0 """ exceptions = AttributeError, LookupError, TypeError, NameError, KeyError def __init__(self, expression): self.expression = expression def __call__(self, target, engine): ignore = store("_ignore") compiler = engine.parse(self.expression, False) body = compiler.assign_value(ignore) classes = map(resolve_global, self.exceptions) return [ ast.TryExcept( body=body, handlers=[ast.ExceptHandler( type=ast.Tuple(elts=classes, ctx=ast.Load()), name=None, body=template("target = 0", target=target), )], orelse=template("target = 1", target=target) ) ] class ExpressionParser(object): def __init__(self, factories, default): self.factories = factories self.default = default def __call__(self, expression): m = match_prefix(expression) if m is not None: prefix = m.group(1) expression = expression[m.end():] else: prefix = self.default try: factory = self.factories[prefix] except KeyError: exc = sys.exc_info()[1] raise LookupError( "Unknown expression type: %s." % str(exc) ) return factory(expression) class SimpleEngine(object): expression = PythonExpr def __init__(self, expression=None): if expression is not None: self.expression = expression def parse(self, string, handle_errors=False, char_escape=None): compiler = self.expression(string) return SimpleCompiler(compiler, self) class SimpleCompiler(object): def __init__(self, compiler, engine): self.compiler = compiler self.engine = engine def assign_text(self, target): """Assign expression string as a text value.""" return self._assign_value_and_coerce(target, "str") def assign_value(self, target): """Assign expression string as object value.""" return self.compiler(target, self.engine) def _assign_value_and_coerce(self, target, builtin): return self.assign_value(target) + template( "target = builtin(target)", target=target, builtin=builtin ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/template.py�����������������������������������������������������������0000664�0000000�0000000�00000024671�13700675072�0020406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from __future__ import with_statement import os import sys import hashlib import logging import tempfile import inspect try: RecursionError except NameError: RecursionError = RuntimeError def get_package_versions(): try: import pkg_resources except ImportError: logging.info("Setuptools not installed. Unable to determine version.") return [] versions = dict() for path in sys.path: for distribution in pkg_resources.find_distributions(path): if distribution.has_version(): versions.setdefault( distribution.project_name, distribution.version, ) return sorted(versions.items()) pkg_digest = hashlib.sha1(__name__.encode('utf-8')) for name, version in get_package_versions(): pkg_digest.update(name.encode('utf-8')) pkg_digest.update(version.encode('utf-8')) from .exc import RenderError from .exc import TemplateError from .exc import ExceptionFormatter from .compiler import Compiler from .config import DEBUG_MODE from .config import AUTO_RELOAD from .config import EAGER_PARSING from .config import CACHE_DIRECTORY from .loader import ModuleLoader from .loader import MemoryLoader from .nodes import Module from .utils import DebuggingOutputStream from .utils import Scope from .utils import join from .utils import mangle from .utils import create_formatted_exception from .utils import read_bytes from .utils import raise_with_traceback from .utils import byte_string from .utils import value_repr log = logging.getLogger('chameleon.template') def _make_module_loader(): remove = False if CACHE_DIRECTORY: path = CACHE_DIRECTORY else: path = tempfile.mkdtemp() remove = True return ModuleLoader(path, remove) class BaseTemplate(object): """Template base class. Takes a string input which must be one of the following: - a unicode string (or string on Python 3); - a utf-8 encoded byte string; - a byte string for an XML document that defines an encoding in the document premamble; - an HTML document that specifies the encoding via the META tag. Note that the template input is decoded, parsed and compiled on initialization. """ default_encoding = "utf-8" # This attribute is strictly informational in this template class # and is used in exception formatting. It may be set on # initialization using the optional ``filename`` keyword argument. filename = '<string>' _cooked = False if DEBUG_MODE or CACHE_DIRECTORY: loader = _make_module_loader() else: loader = MemoryLoader() if DEBUG_MODE: output_stream_factory = DebuggingOutputStream else: output_stream_factory = list debug = DEBUG_MODE # The ``builtins`` dictionary can be used by a template class to # add symbols which may not be redefined and which are (cheaply) # available in the template variable scope builtins = {} # The ``builtins`` dictionary is updated with this dictionary at # cook time. Note that it can be provided at class initialization # using the ``extra_builtins`` keyword argument. extra_builtins = {} # Expression engine must be provided by subclass engine = None # When ``strict`` is set, expressions must be valid at compile # time. When not set, this is only required at evaluation time. strict = True # This should return a value string representation for exception # formatting. value_repr = staticmethod(value_repr) def __init__(self, body=None, **config): self.__dict__.update(config) if body is not None: self.write(body) # This is only necessary if the ``debug`` flag was passed as a # keyword argument if self.__dict__.get('debug') is True: self.loader = _make_module_loader() def __call__(self, **kwargs): return self.render(**kwargs) def __repr__(self): return "<%s %s>" % (self.__class__.__name__, self.filename) @property def keep_body(self): # By default, we only save the template body if we're # in debugging mode (to save memory). return self.__dict__.get('keep_body', DEBUG_MODE) @property def keep_source(self): # By default, we only save the generated source code if we're # in debugging mode (to save memory). return self.__dict__.get('keep_source', DEBUG_MODE) def cook(self, body): builtins_dict = self.builtins.copy() builtins_dict.update(self.extra_builtins) names, builtins = zip(*sorted(builtins_dict.items())) digest = self.digest(body, names) program = self._cook(body, digest, names) initialize = program['initialize'] functions = initialize(*builtins) for name, function in functions.items(): setattr(self, "_" + name, function) self._cooked = True if self.keep_body: self.body = body def cook_check(self): assert self._cooked def parse(self, body): raise NotImplementedError("Must be implemented by subclass.") def render(self, **__kw): econtext = Scope(__kw) rcontext = {} self.cook_check() stream = self.output_stream_factory() try: self._render(stream, econtext, rcontext) except RecursionError: raise except: cls, exc, tb = sys.exc_info() errors = rcontext.get('__error__') if errors: formatter = exc.__str__ if isinstance(formatter, ExceptionFormatter): if errors is not formatter._errors: formatter._errors.extend(errors) raise formatter = ExceptionFormatter(errors, econtext, rcontext, self.value_repr) try: exc = create_formatted_exception( exc, cls, formatter, RenderError ) except TypeError: pass raise_with_traceback(exc, tb) raise return join(stream) def write(self, body): if isinstance(body, byte_string): body, encoding, content_type = read_bytes( body, self.default_encoding ) else: content_type = body.startswith('<?xml') encoding = None self.content_type = content_type self.content_encoding = encoding self.cook(body) def _get_module_name(self, name): return "%s.py" % name def _cook(self, body, name, builtins): filename = self._get_module_name(name) cooked = self.loader.get(filename) if cooked is None: try: source = self._compile(body, builtins) if self.debug: source = "# template: %s\n#\n%s" % ( self.filename, source) if self.keep_source: self.source = source cooked = self.loader.build(source, filename) except TemplateError: exc = sys.exc_info()[1] exc.token.filename = self.filename raise elif self.keep_source: module = sys.modules.get(cooked.get('__name__')) if module is not None: self.source = inspect.getsource(module) else: self.source = None return cooked def digest(self, body, names): class_name = type(self).__name__.encode('utf-8') sha = pkg_digest.copy() sha.update(body.encode('utf-8', 'ignore')) sha.update(class_name) digest = sha.hexdigest() if self.filename is not BaseTemplate.filename: digest = os.path.splitext(self.filename)[0] + '-' + digest return digest def _compile(self, body, builtins): program = self.parse(body) module = Module("initialize", program) compiler = Compiler( self.engine, module, self.filename, body, builtins, strict=self.strict ) return compiler.code class BaseTemplateFile(BaseTemplate): """File-based template base class. Relative path names are supported only when a template loader is provided as the ``loader`` parameter. """ # Auto reload is not enabled by default because it's a significant # performance hit auto_reload = AUTO_RELOAD def __init__(self, filename, auto_reload=None, **config): # Normalize filename filename = os.path.abspath( os.path.normpath(os.path.expanduser(filename)) ) self.filename = filename # Override reload setting only if value is provided explicitly if auto_reload is not None: self.auto_reload = auto_reload super(BaseTemplateFile, self).__init__(**config) if EAGER_PARSING: self.cook_check() def cook_check(self): if self.auto_reload: mtime = self.mtime() if mtime != self._v_last_read: self._v_last_read = mtime self._cooked = False if self._cooked is False: body = self.read() log.debug("cooking %r (%d bytes)..." % (self.filename, len(body))) self.cook(body) def mtime(self): try: return os.path.getmtime(self.filename) except (IOError, OSError): return 0 def read(self): with open(self.filename, "rb") as f: data = f.read() body, encoding, content_type = read_bytes( data, self.default_encoding ) # In non-XML mode, we support various platform-specific line # endings and convert them to the UNIX newline character if content_type != "text/xml" and '\r' in body: body = body.replace('\r\n', '\n').replace('\r', '\n') self.content_type = content_type self.content_encoding = encoding return body def _get_module_name(self, name): filename = os.path.basename(self.filename) mangled = mangle(filename) return "%s_%s.py" % (mangled, name) def _get_filename(self): return self.__dict__.get('filename') def _set_filename(self, filename): self.__dict__['filename'] = filename self._v_last_read = None self._cooked = False filename = property(_get_filename, _set_filename) �����������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/����������������������������������������������������������������0000775�0000000�0000000�00000000000�13700675072�0017351�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/__init__.py�����������������������������������������������������0000664�0000000�0000000�00000000002�13700675072�0021452�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/���������������������������������������������������������0000775�0000000�0000000�00000000000�13700675072�0020673�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/001-interpolation.txt������������������������������������0000664�0000000�0000000�00000000026�13700675072�0024617�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������${'<Hello world>'}<&> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/001-variable-scope.html����������������������������������0000664�0000000�0000000�00000000163�13700675072�0024753�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body py:with="text 'Hello world!'"> ${text} $text </body> ${text | 'Goodbye world!'} </html> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/001-variable-scope.pt������������������������������������0000664�0000000�0000000�00000000317�13700675072�0024433�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body tal:define="text 'Hello world!'"> ${text} </body> <tal:check condition="exists: text"> bad </tal:check> <tal:check condition="not: exists: text"> ok </tal:check> </html> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/001.xml��������������������������������������������������0000664�0000000�0000000�00000000074�13700675072�0021716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc></doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/002-repeat-scope.pt��������������������������������������0000664�0000000�0000000�00000000331�13700675072�0024123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div tal:repeat="text ('Hello', 'Goodbye')"> <span tal:repeat="char ('!', '.')">${text}${char}</span> </div> <tal:check condition="not: exists: text">ok</tal:check> </body> </html> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/002.xml��������������������������������������������������0000664�0000000�0000000�00000000075�13700675072�0021720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc ></doc> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/003-content.pt�������������������������������������������0000664�0000000�0000000�00000000772�13700675072�0023220�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div tal:content="'Hello world!'" /> <div tal:content="'Hello world!'" />1 2<div tal:content="'Hello world!'" /> <div tal:content="'Hello world!'" />3 <div tal:content="'Hello world!'">4</div>5 6<div tal:content="'Hello world!'"></div> <div tal:content="1" /> <div tal:content="1.0" /> <div tal:content="True" /> <div tal:content="False" /> <div tal:content="0" /> <div tal:content="None" /> <div tal:replace="content" /> </body> </html> ������chameleon-3.8.1/src/chameleon/tests/inputs/003.xml��������������������������������������������������0000664�0000000�0000000�00000000075�13700675072�0021721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc></doc > �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/004-attributes.pt����������������������������������������0000664�0000000�0000000�00000002471�13700675072�0023733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <span tal:attributes="class 'hello'" /> <span class="goodbye" tal:attributes="class 'hello'" /> <span CLASS="goodbye" tal:attributes="class 'hello'" /> <span data-æøå="goodbye" tal:attributes="data-æøå 'hello'" /> <span tal:attributes="class None" /> <span a="1" b="2" c="3" d=4 tal:attributes="a None"></span> <span a="1" b="2" c="3" tal:attributes="b None" /> <span a="1" b="2" c="3" tal:attributes="c None" /> <span a="1" b="2" c="3" tal:attributes="b None; c None" /> <span a="1" b="2" c="3" tal:attributes="b string:;;" /> <span a="1" b="2" c="3" tal:attributes="b string:&" /> <span class="hello" tal:attributes="class 'goodbye'" /> <span class="hello" tal:attributes="class '"goodbye"'" /> <span class="hello" tal:attributes="class '\'goodbye\''" /> <span class='hello' tal:attributes="class '\'goodbye\''" /> <span tal:attributes="{'class': 'goodbye'}" /> <span class="hello" tal:attributes="{'class': 'goodbye'}" /> <span a="1" class="hello" tal:attributes="{'class': 'goodbye'}" /> <span tal:attributes="{'class': '"goodbye"'}" /> <span tal:attributes="class 'hello'; {'class': '"goodbye"'}" /> <span tal:attributes="{'class': '"goodbye"'}; class 'hello'" /> </body> </html> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/004.xml��������������������������������������������������0000664�0000000�0000000�00000000146�13700675072�0021721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc a1 CDATA #IMPLIED> ]> <doc a1="v1"></doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/005-default.pt�������������������������������������������0000664�0000000�0000000�00000000602�13700675072�0023164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <img class="default" tal:attributes="class default" /> <img tal:attributes="class default" /> <span tal:define="foo string:" tal:content="foo or default">Default</span> <span tal:content="True">Default</span> <span tal:content="False">Default</span> <span tal:content="default"> <em>${'Computed default'}</em> </span> </body> </html> ������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/005.xml��������������������������������������������������0000664�0000000�0000000�00000000150�13700675072�0021715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc a1 CDATA #IMPLIED> ]> <doc a1 = "v1"></doc> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/006-attribute-interpolation.pt���������������������������0000664�0000000�0000000�00000000617�13700675072�0026437�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body class="ltr" tal:define="hash string:#"> <img src="${'#'}" alt="copyright (c) ${2010}" /> <img src="" alt="copyright (c) ${2010}" tal:attributes="src string:$hash" /> <img src="" alt="copyright (c) ${2010}" tal:attributes="src string:${hash}" /> <img src="${None}" alt="$ignored" /> <img src="" alt="${'%stype \'str\'%s' % (chr(60), chr(62))}" /> </body> </html> �����������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/006.xml��������������������������������������������������0000664�0000000�0000000�00000000146�13700675072�0021723�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc a1 CDATA #IMPLIED> ]> <doc a1='v1'></doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/007-content-interpolation.pt�����������������������������0000664�0000000�0000000�00000000657�13700675072�0026113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> ${'Hello world!'} ${literal} ${structure: literal.s} ${"%stype 'str'%s" % (chr(60), chr(62))} && <script> $$(document).ready(function(){ imagecropping.init_editor(); }); </script> ${None} ${None or 'Hello world'} $leftalone <div>${None}</div> <div>${1 < 2 and 'Hello world' or None}</div> <div>${} is ignored.</div> </body> </html> ���������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/007.xml��������������������������������������������������0000664�0000000�0000000�00000000101�13700675072�0021713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc> </doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/008-builtins.pt������������������������������������������0000664�0000000�0000000�00000000633�13700675072�0023400�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> ${attrs} ${nothing} <div tal:attributes="class string:dynamic" class="static"> ${attrs['class']} </div> <div class="static" tal:content="attrs['class']" /> <div class="static" tal:replace="attrs['class']" /> <div class="static" tal:define="x attrs">${x['class']}</div> <div tal:define="nothing string:nothing"> ${nothing} </div> </body> </html> �����������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/008.xml��������������������������������������������������0000664�0000000�0000000�00000000125�13700675072�0021722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc>&<>"'</doc> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/009-literals.pt������������������������������������������0000664�0000000�0000000�00000000061�13700675072�0023362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> ${literal} </body> </html> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/009.xml��������������������������������������������������0000664�0000000�0000000�00000000102�13700675072�0021716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc> </doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/010-structure.pt�����������������������������������������0000664�0000000�0000000�00000000475�13700675072�0023604�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div tal:content="text string:1 < 2" /> <div tal:content="structure string:2 < 3, 2&3, 2<3, 2>3" /> <div tal:content="structure string:3 ${'<'} 4" /> <div tal:content="structure '%d < %d' % (4, 5)" /> <div tal:replace="structure content" /> </body> </html> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/010.xml��������������������������������������������������0000664�0000000�0000000�00000000147�13700675072�0021717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc a1 CDATA #IMPLIED> ]> <doc a1="v1" ></doc> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/011-messages.pt������������������������������������������0000664�0000000�0000000�00000000353�13700675072�0023347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div tal:content="text message" /> <div tal:content="structure message" /> <div tal:content="text string:${message}" /> <div tal:content="structure string:${message}" /> ${message} </body> </html> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/011.xml��������������������������������������������������0000664�0000000�0000000�00000000200�13700675072�0021706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc a1 CDATA #IMPLIED a2 CDATA #IMPLIED> ]> <doc a1="v1" a2="v2"></doc> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/012-translation.pt���������������������������������������0000664�0000000�0000000�00000001067�13700675072�0024102�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div i18n:translate=""></div> <div i18n:translate=""> Hello world! </div> <div i18n:translate="hello_world"> Hello world! </div> <div i18n:translate=""> <sup>Hello world!</sup> </div> <div i18n:translate=""> Hello <em i18n:name="first">${'world'}</em>! Goodbye <em i18n:name="second">${'planet'}</em>! </div> <div i18n:translate="hello_goodbye"> Hello <em i18n:name="first">${'world'}</em>! Goodbye <em i18n:name="second">${'planet'}</em>! </div> </body> </html> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/012.xml��������������������������������������������������0000664�0000000�0000000�00000000144�13700675072�0021716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc : CDATA #IMPLIED> ]> <doc :="v1"></doc> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/013-repeat-nested.pt�������������������������������������0000664�0000000�0000000�00000000255�13700675072�0024303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <table> <tr tal:repeat="i (1,2)"> <td tal:repeat="j (1,2)"> [${i},${j}] </td> </tr> </table> </body> </html> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/013.xml��������������������������������������������������0000664�0000000�0000000�00000000174�13700675072�0021722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc _.-0123456789 CDATA #IMPLIED> ]> <doc _.-0123456789="v1"></doc> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/014-repeat-nested-similar.pt�����������������������������0000664�0000000�0000000�00000000202�13700675072�0025732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <span tal:repeat="i (3,4)"> <span tal:repeat="j (3,4)">[${i},${j}]</span> </span> </body> </html> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/014.xml��������������������������������������������������0000664�0000000�0000000�00000000226�13700675072�0021721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc abcdefghijklmnopqrstuvwxyz CDATA #IMPLIED> ]> <doc abcdefghijklmnopqrstuvwxyz="v1"></doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/015-translation-nested.pt��������������������������������0000664�0000000�0000000�00000000312�13700675072�0025355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div i18n:translate=""> Price: <span i18n:name="price" i18n:translate=""> Per kilo <em i18n:name="amount">${12.5}</em> </span> </div> </body> </html> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/015.xml��������������������������������������������������0000664�0000000�0000000�00000000226�13700675072�0021722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc ABCDEFGHIJKLMNOPQRSTUVWXYZ CDATA #IMPLIED> ]> <doc ABCDEFGHIJKLMNOPQRSTUVWXYZ="v1"></doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/016-explicit-translation.pt������������������������������0000664�0000000�0000000�00000000626�13700675072�0025725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div i18n:translate="" tal:content="string:Hello world!"> Hello world! </div> <img alt="${'Hello world!'}" i18n:attributes="alt" /> <img alt="${'Hello world!'}" i18n:attributes="alt hello_world" /> <img tal:attributes="alt 'Hello world!'" i18n:attributes="alt" /> <img tal:attributes="alt 'Hello world!'" i18n:attributes="alt hello_world" /> </body> </html> ����������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/016.xml��������������������������������������������������0000664�0000000�0000000�00000000102�13700675072�0021714�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc><?pi?></doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/017-omit-tag.pt������������������������������������������0000664�0000000�0000000�00000000556�13700675072�0023274�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div tal:omit-tag="">Hello world!</div> <div tal:omit-tag="">1 Hello world! 2</div>3 4<div tal:omit-tag="True">Hello world!</div> <div tal:omit-tag="False">Hello world!</div> <div class="omitted" tal:omit-tag="True">Hello world!</div> <div class="${'omitted'}" tal:omit-tag="True">Hello world!</div> </body> </html> ��������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/017.xml��������������������������������������������������0000664�0000000�0000000�00000000123�13700675072�0021720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc><?pi some data ? > <??></doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/018-translation-nested-dynamic.pt������������������������0000664�0000000�0000000�00000000656�13700675072�0027015�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<div xmlns="http://www.w3.org/1999/xhtml" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> <div i18n:translate="" tal:omit-tag=""> <span i18n:name="monthname" i18n:translate="" tal:content="'october'" tal:omit-tag="">monthname</span> <span i18n:name="year" i18n:translate="" tal:content="1982" tal:omit-tag="">year</span> </div> </div> ����������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/018.xml��������������������������������������������������0000664�0000000�0000000�00000000115�13700675072�0021722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc><![CDATA[<foo>]]></doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/019-replace.pt�������������������������������������������0000664�0000000�0000000�00000000575�13700675072�0023171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div tal:replace="'Hello world!'" /> <div tal:replace="'Hello world!'" />1 2<div tal:replace="'Hello world!'" /> <div tal:replace="'Hello world!'" />3 <div tal:replace="'Hello world!'">4</div>5 6<div tal:replace="'Hello world!'"></div> <div tal:replace="1" /> <div tal:replace="1.0" /> <div tal:replace="True" /> </body> </html> �����������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/019.xml��������������������������������������������������0000664�0000000�0000000�00000000112�13700675072�0021720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc><![CDATA[<&]]></doc> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/020-on-error.pt������������������������������������������0000664�0000000�0000000�00000000610�13700675072�0023277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div id="test" tal:attributes="class python: 'abc' + 2" tal:on-error="nothing" /> <div tal:on-error="string:${type(error.value).__name__} thrown at ${error.lineno}:${error.offset}."> <div tal:content="undefined" /> </div> <div tal:replace="undefined" tal:on-error="nothing" /> <div tal:content="undefined" tal:on-error="nothing" /> </body> </html> ������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/020.xml��������������������������������������������������0000664�0000000�0000000�00000000115�13700675072�0021713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc><![CDATA[<&]>]]]></doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/021-translation-domain.pt��������������������������������0000664�0000000�0000000�00000000603�13700675072�0025342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body i18n:domain="old"> <div i18n:domain="new" i18n:translate=""> Hello world! </div> <div i18n:translate=""> Hello world! </div> <div class="test" i18n:domain="new" i18n:attributes="class"> Hello world! </div> <div class="test" i18n:domain="new" i18n:attributes="class test_msgid"> Hello world! </div> </body> </html> �����������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/021.xml��������������������������������������������������0000664�0000000�0000000�00000000116�13700675072�0021715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc><!-- a comment --></doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/022-switch.pt��������������������������������������������0000664�0000000�0000000�00000001066�13700675072�0023045�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div tal:switch="True"> <span tal:case="False">bad</span> <span tal:case="True">ok</span> <span tal:case="True">ok</span> <span tal:case="default">bad</span> <span tal:case="True">bad</span> ${default|string:ok} </div> <div tal:switch="True"> <span tal:case="False">bad</span> <span tal:case="default">ok</span> </div> <div tal:switch="3"> <span tal:case="1">bad</span> <span tal:case="2">bad</span> <span tal:case="default">ok</span> </div> </body> </html> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/022.xml��������������������������������������������������0000664�0000000�0000000�00000000120�13700675072�0021711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc><!-- a comment ->--></doc> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/023-condition.pt�����������������������������������������0000664�0000000�0000000�00000000252�13700675072�0023527�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body tal:condition="True"> <span tal:define="selector False" tal:condition="selector">bad</span> <span tal:condition="True">ok</span> </body> </html> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/023.xml��������������������������������������������������0000664�0000000�0000000�00000000117�13700675072�0021720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ENTITY e ""> ]> <doc>&e;</doc> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/024-namespace-elements.pt��������������������������������0000664�0000000�0000000�00000000402�13700675072�0025305�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <tal:first> <tal:second> ${'first'} </tal:second> second </tal:first> <tal:block condition="True"> ok </tal:block> <tal:block condition="False"> bad </tal:block> </body> </html> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/024.xml��������������������������������������������������0000664�0000000�0000000�00000000164�13700675072�0021723�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (foo)> <!ELEMENT foo (#PCDATA)> <!ENTITY e "<foo></foo>"> ]> <doc>&e;</doc> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/025-repeat-whitespace.pt���������������������������������0000664�0000000�0000000�00000000751�13700675072�0025161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <ul> <li tal:repeat="i (1, 2, 3)" tal:content="i" /> <tal:item repeat="i (1, 2, 3)"><li tal:content="i" /></tal:item> <span tal:omit-tag="" tal:repeat="j (1, 2, 3)"><li tal:content="j" /></span> <tal:count> <tal:count-loop repeat="count (1, 2, 3)"> <span tal:replace="count" /><tal:comma condition="not repeat['count'].end">,</tal:comma> </tal:count-loop> </tal:count>. </ul> </body> </html> �����������������������chameleon-3.8.1/src/chameleon/tests/inputs/025.xml��������������������������������������������������0000664�0000000�0000000�00000000144�13700675072�0021722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (foo*)> <!ELEMENT foo (#PCDATA)> ]> <doc><foo/><foo></foo></doc> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/026-repeat-variable.pt�����������������������������������0000664�0000000�0000000�00000001135�13700675072�0024610�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<div xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal"> <ul> <li tal:attributes="class repeat['i'].even()+repeat['i'].odd()" name="${i}-${repeat.i.index}" tal:repeat="i range(3)"><span tal:replace="i" /></li> </ul> <ul> <li tal:attributes="class repeat['i'].even+repeat['i'].odd" tal:repeat="i range(3)"><span tal:replace="i" /></li> </ul> <ul> <li tal:repeat="i range(3)"><span tal:condition="repeat['i'].even" tal:replace="repeat['i'].even" /><span tal:condition="repeat['i'].odd" tal:replace="repeat['i'].odd" /></li> </ul> </div> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/026.xml��������������������������������������������������0000664�0000000�0000000�00000000140�13700675072�0021717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (foo*)> <!ELEMENT foo EMPTY> ]> <doc><foo/><foo></foo></doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/027-attribute-replacement.pt�����������������������������0000664�0000000�0000000�00000000660�13700675072�0026050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<div xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal"> <span id="test" class="dummy" onClick="" tal:define="a 'abc'" tal:attributes="class default; style 'hij'; onClick 'alert();;'" tal:content="a + 'ghi'" /> <span tal:replace="'Hello World!'">Hello <b>Universe</b>!</span> <span tal:replace="'Hello World!'"><b>Hello Universe!</b></span> </div> ��������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/027.xml��������������������������������������������������0000664�0000000�0000000�00000000136�13700675072�0021725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (foo*)> <!ELEMENT foo ANY> ]> <doc><foo/><foo></foo></doc> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/028-attribute-toggle.pt����������������������������������0000664�0000000�0000000�00000000400�13700675072�0025023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<div xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal"> <option tal:attributes="selected True"></option> <option tal:attributes="selected False"></option> <option tal:attributes="selected None"></option> </div> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/028.xml��������������������������������������������������0000664�0000000�0000000�00000000123�13700675072�0021722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0"?> <!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc></doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/029-attribute-ordering.pt��������������������������������0000664�0000000�0000000�00000000316�13700675072�0025362�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<div xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal"> <a rel="self" href="http://repoze.org" id="link-id" tal:attributes="href 'http://python.org'" /> </div> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/029.xml��������������������������������������������������0000664�0000000�0000000�00000000123�13700675072�0021723�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version='1.0'?> <!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc></doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/030-repeat-tuples.pt�������������������������������������0000664�0000000�0000000�00000000211�13700675072�0024324�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html> <body> <div tal:repeat="(i, j) ((1, 2), (3, 4))"> ${repeat['i', 'j'].number}, ${i}, ${j} </div> </body> </html> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/030.xml��������������������������������������������������0000664�0000000�0000000�00000000125�13700675072�0021715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version = "1.0"?> <!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc></doc> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/031-namespace-with-tal.pt��������������������������������0000664�0000000�0000000�00000000354�13700675072�0025226�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<div> <tal:example replace="'Hello World!'" /> <tal:example tal:replace="'Hello World!'" /> <tal:div content="'Hello World!'" /> <tal:multiple repeat="i range(3)" replace="i" /> <tal:div condition="True">True</tal:div> </div> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/031.xml��������������������������������������������������0000664�0000000�0000000�00000000144�13700675072�0021717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version='1.0' encoding="UTF-8"?> <!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc></doc> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/032-master-template.pt�����������������������������������0000664�0000000�0000000�00000001151�13700675072�0024644�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html i18n:domain="master" metal:define-macro="main" tal:define="content nothing"> <head> <title metal:define-slot="title" metal:define-macro="title" tal:define="has_title exists: title" tal:content="title if has_title else default">Master template
chameleon-3.8.1/src/chameleon/tests/inputs/032.xml000066400000000000000000000001441370067507200217200ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/033-use-macro-trivial.pt000066400000000000000000000001111370067507200250770ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/inputs/033.xml000066400000000000000000000001651370067507200217240ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/034-use-template-as-macro.pt000066400000000000000000000000711370067507200256470ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/inputs/034.xml000066400000000000000000000000671370067507200217260ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/035-use-macro-with-fill-slot.pt000066400000000000000000000002451370067507200263150ustar00rootroot00000000000000 ${kind} title chameleon-3.8.1/src/chameleon/tests/inputs/035.xml000066400000000000000000000000701370067507200217210ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/036-use-macro-inherits-dynamic-scope.pt000066400000000000000000000001631370067507200300150ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/inputs/036.xml000066400000000000000000000001111370067507200217160ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/037-use-macro-local-variable-scope.pt000066400000000000000000000003031370067507200274200ustar00rootroot00000000000000 ok chameleon-3.8.1/src/chameleon/tests/inputs/037.xml000066400000000000000000000001201370067507200217170ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/038-use-macro-globals.pt000066400000000000000000000002141370067507200250610ustar00rootroot00000000000000 ok chameleon-3.8.1/src/chameleon/tests/inputs/038.xml000066400000000000000000000001201370067507200217200ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/039-globals.pt000066400000000000000000000000541370067507200231730ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/inputs/039.xml000066400000000000000000000001111370067507200217210ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/040-macro-using-template-symbol.pt000066400000000000000000000015371370067507200271070ustar00rootroot00000000000000 ${foo}
chameleon-3.8.1/src/chameleon/tests/inputs/040.xml000066400000000000000000000001751370067507200217230ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/041-translate-nested-names.pt000066400000000000000000000007651370067507200261300ustar00rootroot00000000000000
Hello world!
Hello world!
Goodbye world!
chameleon-3.8.1/src/chameleon/tests/inputs/041.xml000066400000000000000000000001511370067507200217160ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/042-use-macro-fill-footer.pt000066400000000000000000000002201370067507200256500ustar00rootroot00000000000000 New footer chameleon-3.8.1/src/chameleon/tests/inputs/042.xml000066400000000000000000000001421370067507200217170ustar00rootroot00000000000000 ]> A chameleon-3.8.1/src/chameleon/tests/inputs/043-macro-nested-dynamic-vars.pt000066400000000000000000000006141370067507200265210ustar00rootroot00000000000000 ${title}
chameleon-3.8.1/src/chameleon/tests/inputs/043.xml000066400000000000000000000001541370067507200217230ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/044-tuple-define.pt000066400000000000000000000001161370067507200241240ustar00rootroot00000000000000 ${a}, ${b} chameleon-3.8.1/src/chameleon/tests/inputs/044.xml000066400000000000000000000002731370067507200217260ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/045-namespaces.pt000066400000000000000000000010471370067507200236670ustar00rootroot00000000000000 ]> ZZZ YYY XXX chameleon-3.8.1/src/chameleon/tests/inputs/045.xml000066400000000000000000000001701370067507200217230ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/046-extend-macro.pt000066400000000000000000000003451370067507200241370ustar00rootroot00000000000000 New footer chameleon-3.8.1/src/chameleon/tests/inputs/046.xml000066400000000000000000000001701370067507200217240ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/047-use-extended-macro.pt000066400000000000000000000001721370067507200252410ustar00rootroot00000000000000 Extended chameleon-3.8.1/src/chameleon/tests/inputs/047.xml000066400000000000000000000001001370067507200217160ustar00rootroot00000000000000 ]> X Y chameleon-3.8.1/src/chameleon/tests/inputs/048-use-extended-macro-fill-original.pt000066400000000000000000000002361370067507200277710ustar00rootroot00000000000000 Extended footer chameleon-3.8.1/src/chameleon/tests/inputs/048.xml000066400000000000000000000000751370067507200217320ustar00rootroot00000000000000 ]> ] chameleon-3.8.1/src/chameleon/tests/inputs/049-entities-in-attributes.pt000066400000000000000000000007071370067507200261720ustar00rootroot00000000000000
    
    
  
  
    
  
chameleon-3.8.1/src/chameleon/tests/inputs/058.xml000066400000000000000000000001571370067507200217340ustar00rootroot00000000000000

]>

chameleon-3.8.1/src/chameleon/tests/inputs/059-embedded-javascript.pt000066400000000000000000000002561370067507200254530ustar00rootroot00000000000000
  
    test
    test
  

chameleon-3.8.1/src/chameleon/tests/inputs/059.xml000066400000000000000000000003431370067507200217320ustar00rootroot00000000000000


]>





chameleon-3.8.1/src/chameleon/tests/inputs/060-macro-with-multiple-same-slots.pt000066400000000000000000000003371370067507200275360ustar00rootroot00000000000000
  
    <metal:title define-slot="title">Untitled</metal:title>
  
  
    

Untitled

chameleon-3.8.1/src/chameleon/tests/inputs/060.xml000066400000000000000000000001031370067507200217140ustar00rootroot00000000000000 ]> X Y chameleon-3.8.1/src/chameleon/tests/inputs/061-fill-one-slot-but-two-defined.pt000066400000000000000000000002301370067507200272160ustar00rootroot00000000000000 My document chameleon-3.8.1/src/chameleon/tests/inputs/061.xml000066400000000000000000000001021370067507200217140ustar00rootroot00000000000000 ]> £ chameleon-3.8.1/src/chameleon/tests/inputs/062-comments-and-expressions.pt000066400000000000000000000005641370067507200265170ustar00rootroot00000000000000
chameleon-3.8.1/src/chameleon/tests/inputs/062.xml000066400000000000000000000001271370067507200217240ustar00rootroot00000000000000 ]> เจมส์ chameleon-3.8.1/src/chameleon/tests/inputs/063-continuation.pt000066400000000000000000000001031370067507200242520ustar00rootroot00000000000000
${foo}
chameleon-3.8.1/src/chameleon/tests/inputs/063.xml000066400000000000000000000001541370067507200217250ustar00rootroot00000000000000 ]> <เจมส์> chameleon-3.8.1/src/chameleon/tests/inputs/064-tags-and-special-characters.pt000066400000000000000000000001051370067507200267740ustar00rootroot00000000000000
chameleon-3.8.1/src/chameleon/tests/inputs/064.xml000066400000000000000000000001171370067507200217250ustar00rootroot00000000000000 ]> 𐀀􏿽 chameleon-3.8.1/src/chameleon/tests/inputs/065-use-macro-in-fill.pt000066400000000000000000000004241370067507200247730ustar00rootroot00000000000000 <div metal:fill-slot="content">Content</div> </html>��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/065.xml��������������������������������������������������0000664�0000000�0000000�00000000121�13700675072�0021721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ENTITY e "<"> <!ELEMENT doc (#PCDATA)> ]> <doc></doc> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/066-load-expression.pt�����������������������������������0000664�0000000�0000000�00000000125�13700675072�0024663�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<html tal:define="hello_world load: hello_world.pt" metal:use-macro="hello_world" /> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/066.xml��������������������������������������������������0000664�0000000�0000000�00000000233�13700675072�0021726�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ATTLIST doc a1 CDATA #IMPLIED> <!-- 34 is double quote --> <!ENTITY e1 """> ]> <doc a1="&e1;"></doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/067-attribute-decode.pt����������������������������������0000664�0000000�0000000�00000000311�13700675072�0024771�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������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.8.1/src/chameleon/tests/inputs/067.xml��������������������������������������������������0000664�0000000�0000000�00000000101�13700675072�0021721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> ]> <doc> </doc> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/068-less-than-greater-than-in-attributes.pt��������������0000664�0000000�0000000�00000000322�13700675072�0030615�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������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.8.1/src/chameleon/tests/inputs/068.xml��������������������������������������������������0000664�0000000�0000000�00000000124�13700675072�0021727�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE doc [ <!ELEMENT doc (#PCDATA)> <!ENTITY e " "> ]> <doc>&e;</doc> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������chameleon-3.8.1/src/chameleon/tests/inputs/069-translation-domain-and-macro.pt����������������������0000664�0000000�0000000�00000000247�13700675072�0027221�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������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.8.1/src/chameleon/tests/inputs/069.xml000066400000000000000000000001351370067507200217320ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/070-translation-domain-and-use-macro.pt000066400000000000000000000002471370067507200300030ustar00rootroot00000000000000 Title chameleon-3.8.1/src/chameleon/tests/inputs/070.xml000066400000000000000000000001211370067507200217150ustar00rootroot00000000000000"> %e; ]> chameleon-3.8.1/src/chameleon/tests/inputs/071-html-attribute-defaults.pt000066400000000000000000000010531370067507200263160ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/inputs/071.xml000066400000000000000000000001321370067507200217200ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/072-repeat-interpolation.pt000066400000000000000000000003731370067507200257160ustar00rootroot00000000000000
  • ${i}
  • ${i}
chameleon-3.8.1/src/chameleon/tests/inputs/072.xml000066400000000000000000000001351370067507200217240ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/073-utf8-encoded.pt000066400000000000000000000002231370067507200240310ustar00rootroot00000000000000 ${'my title'} — ${'my site'} chameleon-3.8.1/src/chameleon/tests/inputs/073.xml000066400000000000000000000001361370067507200217260ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/074-encoded-template.pt000066400000000000000000000002511370067507200247600ustar00rootroot00000000000000 ${'my title'} — ${'my site'} chameleon-3.8.1/src/chameleon/tests/inputs/074.xml000066400000000000000000000001361370067507200217270ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/075-nested-macros.pt000066400000000000000000000004321370067507200243140ustar00rootroot00000000000000 foo chameleon-3.8.1/src/chameleon/tests/inputs/075.xml000066400000000000000000000001401370067507200217230ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/076-nested-macro-override.pt000066400000000000000000000002121370067507200257430ustar00rootroot00000000000000 bar chameleon-3.8.1/src/chameleon/tests/inputs/076.xml000066400000000000000000000003001370067507200217220ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/077-i18n-attributes.pt000066400000000000000000000001501370067507200245120ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/inputs/077.xml000066400000000000000000000001351370067507200217310ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/078-tags-and-newlines.pt000066400000000000000000000014361370067507200251000ustar00rootroot00000000000000 , chameleon-3.8.1/src/chameleon/tests/inputs/078.xml000066400000000000000000000001441370067507200217320ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/079-implicit-i18n.pt000066400000000000000000000006441370067507200241500ustar00rootroot00000000000000 Welcome

Welcome

An edge case: ${. Site logo Site logo
boo foo.
bar.
chameleon-3.8.1/src/chameleon/tests/inputs/079.xml000066400000000000000000000001451370067507200217340ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/080-xmlns-namespace-on-tal.pt000066400000000000000000000003721370067507200260320ustar00rootroot00000000000000 Hello world chameleon-3.8.1/src/chameleon/tests/inputs/080.xml000066400000000000000000000001371370067507200217250ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/081-load-spec.pt000066400000000000000000000001061370067507200234120ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/inputs/081.xml000066400000000000000000000002141370067507200217220ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/082-load-spec-computed.pt000066400000000000000000000001421370067507200252310ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/inputs/082.xml000066400000000000000000000001321370067507200217220ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/083-template-dict-to-macro.pt000066400000000000000000000001441370067507200260220ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/inputs/083.xml000066400000000000000000000001451370067507200217270ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/084-interpolation-in-cdata.pt000066400000000000000000000001711370067507200261150ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/inputs/084.xml000066400000000000000000000000661370067507200217320ustar00rootroot00000000000000]> chameleon-3.8.1/src/chameleon/tests/inputs/085-nested-translation.pt000066400000000000000000000004651370067507200253750ustar00rootroot00000000000000 Welcome

Welcome

Click here to continue.

chameleon-3.8.1/src/chameleon/tests/inputs/085.xml000066400000000000000000000001461370067507200217320ustar00rootroot00000000000000 "> ]> &e; chameleon-3.8.1/src/chameleon/tests/inputs/086-self-closing.pt000066400000000000000000000003731370067507200241430ustar00rootroot00000000000000
chameleon-3.8.1/src/chameleon/tests/inputs/086.xml000066400000000000000000000001441370067507200217310ustar00rootroot00000000000000 "> ]> &e; chameleon-3.8.1/src/chameleon/tests/inputs/087-code-blocks.pt000066400000000000000000000006731370067507200237470ustar00rootroot00000000000000
Please input a number from the range ${", ".join(numbers)}.
41 + 1 = ${function(41)}.
chameleon-3.8.1/src/chameleon/tests/inputs/087.xml000066400000000000000000000001531370067507200217320ustar00rootroot00000000000000 ]> &e; chameleon-3.8.1/src/chameleon/tests/inputs/088-python-newlines.pt000066400000000000000000000000771370067507200247240ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/inputs/088.xml000066400000000000000000000001271370067507200217340ustar00rootroot00000000000000 "> ]> &e; chameleon-3.8.1/src/chameleon/tests/inputs/089-load-fallback.pt000066400000000000000000000001741370067507200242340ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/inputs/089.xml000066400000000000000000000001541370067507200217350ustar00rootroot00000000000000 ]> &e; chameleon-3.8.1/src/chameleon/tests/inputs/090-tuple-expression.pt000066400000000000000000000004411370067507200250730ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/inputs/090.xml000066400000000000000000000002261370067507200217250ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/091-repeat-none.pt000066400000000000000000000001131370067507200237570ustar00rootroot00000000000000
error
chameleon-3.8.1/src/chameleon/tests/inputs/091.xml000066400000000000000000000002651370067507200217310ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/092.xml000066400000000000000000000001461370067507200217300ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/093.xml000066400000000000000000000000741370067507200217310ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/094.xml000066400000000000000000000001601370067507200217260ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/095.xml000066400000000000000000000002151370067507200217300ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/096.xml000066400000000000000000000001431370067507200217310ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/097.xml000066400000000000000000000002351370067507200217340ustar00rootroot00000000000000 %e; ]> chameleon-3.8.1/src/chameleon/tests/inputs/098.xml000066400000000000000000000001071370067507200217330ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/099.xml000066400000000000000000000001441370067507200217350ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/100.xml000066400000000000000000000001451370067507200217150ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/101-unclosed-tags.html000066400000000000000000000001031370067507200246220ustar00rootroot00000000000000



Hello world

chameleon-3.8.1/src/chameleon/tests/inputs/101.xml000066400000000000000000000001211370067507200217100ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/102-unquoted-attributes.html000066400000000000000000000001241370067507200261060ustar00rootroot00000000000000

Hello world

chameleon-3.8.1/src/chameleon/tests/inputs/102.xml000066400000000000000000000001471370067507200217210ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/103-simple-attribute.html000066400000000000000000000003201370067507200253470ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/inputs/103.xml000066400000000000000000000001051370067507200217140ustar00rootroot00000000000000 ]> <doc> chameleon-3.8.1/src/chameleon/tests/inputs/104.xml000066400000000000000000000001451370067507200217210ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/105.xml000066400000000000000000000001501370067507200217160ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/106.xml000066400000000000000000000001511370067507200217200ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/107.xml000066400000000000000000000001511370067507200217210ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/108.xml000066400000000000000000000001711370067507200217240ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/109.xml000066400000000000000000000001421370067507200217230ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/110.xml000066400000000000000000000002011370067507200217070ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/111.xml000066400000000000000000000001731370067507200217200ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/112.xml000066400000000000000000000001311370067507200217130ustar00rootroot00000000000000 ]>
chameleon-3.8.1/src/chameleon/tests/inputs/113.xml000066400000000000000000000001331370067507200217160ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/114.xml000066400000000000000000000001401370067507200217150ustar00rootroot00000000000000 "> ]> &e; chameleon-3.8.1/src/chameleon/tests/inputs/115.xml000066400000000000000000000001471370067507200217250ustar00rootroot00000000000000 ]> &e1; chameleon-3.8.1/src/chameleon/tests/inputs/116.xml000066400000000000000000000001121370067507200217160ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/117.xml000066400000000000000000000001261370067507200217240ustar00rootroot00000000000000 ]> ] chameleon-3.8.1/src/chameleon/tests/inputs/118.xml000066400000000000000000000001271370067507200217260ustar00rootroot00000000000000 ]> ] chameleon-3.8.1/src/chameleon/tests/inputs/119.xml000066400000000000000000000001021370067507200217200ustar00rootroot00000000000000 ]> chameleon-3.8.1/src/chameleon/tests/inputs/120-translation-context.pt000066400000000000000000000004271370067507200255630ustar00rootroot00000000000000
Hello world!
Tab
chameleon-3.8.1/src/chameleon/tests/inputs/121-translation-comment.pt000066400000000000000000000001641370067507200255400ustar00rootroot00000000000000

Hello world!

chameleon-3.8.1/src/chameleon/tests/inputs/122-translation-ignore.pt000066400000000000000000000003411370067507200253570ustar00rootroot00000000000000

Hello world!

Python is a programming language. chameleon-3.8.1/src/chameleon/tests/inputs/123-html5-data-attributes.pt000066400000000000000000000001321370067507200256630ustar00rootroot00000000000000
chameleon-3.8.1/src/chameleon/tests/inputs/124-translation-target.pt000066400000000000000000000002531370067507200253660ustar00rootroot00000000000000

Hello world!

It's a big world.

chameleon-3.8.1/src/chameleon/tests/inputs/125-macro-translation-ordering.pt000066400000000000000000000005621370067507200270140ustar00rootroot00000000000000

h1

h2

chameleon-3.8.1/src/chameleon/tests/inputs/126-define-escaping.pt000066400000000000000000000003501370067507200245650ustar00rootroot00000000000000 ${s1} ${s2} ${structure: s1}amp; ${structure: s2}amp; chameleon-3.8.1/src/chameleon/tests/inputs/238-macroname.pt000066400000000000000000000003331370067507200235130ustar00rootroot00000000000000

name

chameleon-3.8.1/src/chameleon/tests/inputs/greeting.pt000066400000000000000000000000511370067507200230400ustar00rootroot00000000000000
Hello, ${name | 'undefined'}.
chameleon-3.8.1/src/chameleon/tests/inputs/hello_world.pt000066400000000000000000000000671370067507200235550ustar00rootroot00000000000000 ${'Hello world!'} chameleon-3.8.1/src/chameleon/tests/inputs/hello_world.txt000066400000000000000000000000221370067507200237400ustar00rootroot00000000000000${'Hello world!'} chameleon-3.8.1/src/chameleon/tests/inputs/multinode-implicit-i18n.pt000066400000000000000000000000571370067507200256270ustar00rootroot00000000000000 Foo ${message} chameleon-3.8.1/src/chameleon/tests/outputs/000077500000000000000000000000001370067507200210745ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/outputs/001.html000066400000000000000000000001251370067507200222600ustar00rootroot00000000000000 Hello world! Hello world! Goodbye world! chameleon-3.8.1/src/chameleon/tests/outputs/001.pt000066400000000000000000000000751370067507200217430ustar00rootroot00000000000000 Hello world! ok chameleon-3.8.1/src/chameleon/tests/outputs/001.txt000066400000000000000000000000211370067507200221260ustar00rootroot00000000000000<&> chameleon-3.8.1/src/chameleon/tests/outputs/002.pt000066400000000000000000000002771370067507200217500ustar00rootroot00000000000000
Hello! Hello.
Goodbye! Goodbye.
ok chameleon-3.8.1/src/chameleon/tests/outputs/003.pt000066400000000000000000000005431370067507200217450ustar00rootroot00000000000000
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.8.1/src/chameleon/tests/outputs/004.pt000066400000000000000000000012401370067507200217410ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/outputs/005.pt000066400000000000000000000003071370067507200217450ustar00rootroot00000000000000 Default True False Computed default chameleon-3.8.1/src/chameleon/tests/outputs/006.pt000066400000000000000000000003741370067507200217520ustar00rootroot00000000000000 copyright (c) 2010 copyright (c) 2010 copyright (c) 2010 $ignored <type 'str'> chameleon-3.8.1/src/chameleon/tests/outputs/007.pt000066400000000000000000000005411370067507200217470ustar00rootroot00000000000000 Hello world!
Hello world!
Hello world!
<type 'str'> && Hello world $leftalone
Hello world
${} is ignored.
chameleon-3.8.1/src/chameleon/tests/outputs/008.pt000066400000000000000000000003241370067507200217470ustar00rootroot00000000000000 {}
static
static
static
static
nothing
chameleon-3.8.1/src/chameleon/tests/outputs/009.pt000066400000000000000000000000761370067507200217540ustar00rootroot00000000000000
Hello world!
chameleon-3.8.1/src/chameleon/tests/outputs/010.pt000066400000000000000000000002441370067507200217410ustar00rootroot00000000000000
1 < 2
2 < 3, 2&3, 2<3, 2>3
3 < 4
4 < 5
Hello world!
chameleon-3.8.1/src/chameleon/tests/outputs/011-en.pt000066400000000000000000000004641370067507200223460ustar00rootroot00000000000000
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.8.1/src/chameleon/tests/outputs/011.pt000066400000000000000000000002121370067507200217350ustar00rootroot00000000000000
Message
Message
Message
Message
Message chameleon-3.8.1/src/chameleon/tests/outputs/012-en.pt000066400000000000000000000007611370067507200223470ustar00rootroot00000000000000
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.8.1/src/chameleon/tests/outputs/012.pt000066400000000000000000000004151370067507200217430ustar00rootroot00000000000000
Hello world!
Hello world!
Hello world!
Hello world! Goodbye planet!
Hello world! Goodbye planet!
chameleon-3.8.1/src/chameleon/tests/outputs/013.pt000066400000000000000000000004251370067507200217450ustar00rootroot00000000000000
[1,1] [1,2]
[2,1] [2,2]
chameleon-3.8.1/src/chameleon/tests/outputs/014.pt000066400000000000000000000002641370067507200217470ustar00rootroot00000000000000 [3,3] [3,4] [4,3] [4,4] chameleon-3.8.1/src/chameleon/tests/outputs/015-en.pt000066400000000000000000000002631370067507200223470ustar00rootroot00000000000000
Price: Per kilo 12.5 ('Per kilo ${amount}' translation into 'en') ('Price: ${price}' translation into 'en')
chameleon-3.8.1/src/chameleon/tests/outputs/015.pt000066400000000000000000000001341370067507200217440ustar00rootroot00000000000000
Price: Per kilo 12.5
chameleon-3.8.1/src/chameleon/tests/outputs/016-en.pt000066400000000000000000000005731370067507200223540ustar00rootroot00000000000000
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.8.1/src/chameleon/tests/outputs/016.pt000066400000000000000000000002721370067507200217500ustar00rootroot00000000000000
Hello world!
Hello world! Hello world! Hello world! Hello world! chameleon-3.8.1/src/chameleon/tests/outputs/017.pt000066400000000000000000000002401370067507200217440ustar00rootroot00000000000000 Hello world! 1 Hello world! 23 4Hello world!
Hello world!
Hello world! Hello world! chameleon-3.8.1/src/chameleon/tests/outputs/018-en.pt000066400000000000000000000002611370067507200223500ustar00rootroot00000000000000
october ('october' translation into 'en') 1982 ('1982' translation into 'en') ('${monthname} ${year}' translation into 'en')
chameleon-3.8.1/src/chameleon/tests/outputs/018.pt000066400000000000000000000001011370067507200217410ustar00rootroot00000000000000
october 1982
chameleon-3.8.1/src/chameleon/tests/outputs/019.pt000066400000000000000000000002421370067507200217500ustar00rootroot00000000000000 Hello world! Hello world!1 2Hello world! Hello world!3 Hello world!5 6Hello world! 1 1.0 True chameleon-3.8.1/src/chameleon/tests/outputs/020.pt000066400000000000000000000002051370067507200217370ustar00rootroot00000000000000
NameError thrown at 5:24.
chameleon-3.8.1/src/chameleon/tests/outputs/021-en.pt000066400000000000000000000006361370067507200223500ustar00rootroot00000000000000
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.8.1/src/chameleon/tests/outputs/021.pt000066400000000000000000000003041370067507200217400ustar00rootroot00000000000000
Hello world!
Hello world!
Hello world!
Hello world!
chameleon-3.8.1/src/chameleon/tests/outputs/022.pt000066400000000000000000000002631370067507200217450ustar00rootroot00000000000000
ok ok
ok
ok
chameleon-3.8.1/src/chameleon/tests/outputs/023.pt000066400000000000000000000000671370067507200217500ustar00rootroot00000000000000 ok chameleon-3.8.1/src/chameleon/tests/outputs/024.pt000066400000000000000000000001151370067507200217430ustar00rootroot00000000000000 first second ok chameleon-3.8.1/src/chameleon/tests/outputs/025.pt000066400000000000000000000003631370067507200217510ustar00rootroot00000000000000
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 1, 2, 3 .
chameleon-3.8.1/src/chameleon/tests/outputs/026.pt000066400000000000000000000005331370067507200217510ustar00rootroot00000000000000
  • 0
  • 1
  • 2
  • 0
  • 1
  • 2
  • even
  • odd
  • even
chameleon-3.8.1/src/chameleon/tests/outputs/027.pt000066400000000000000000000002551370067507200217530ustar00rootroot00000000000000
abcghi Hello World! Hello World!
chameleon-3.8.1/src/chameleon/tests/outputs/028.pt000066400000000000000000000002171370067507200217520ustar00rootroot00000000000000
chameleon-3.8.1/src/chameleon/tests/outputs/029.pt000066400000000000000000000001531370067507200217520ustar00rootroot00000000000000
chameleon-3.8.1/src/chameleon/tests/outputs/030.pt000066400000000000000000000001501370067507200217370ustar00rootroot00000000000000
1, 1, 2
2, 3, 4
chameleon-3.8.1/src/chameleon/tests/outputs/031.pt000066400000000000000000000001071370067507200217420ustar00rootroot00000000000000
Hello World! Hello World! Hello World! 012 True
chameleon-3.8.1/src/chameleon/tests/outputs/032.pt000066400000000000000000000002741370067507200217500ustar00rootroot00000000000000 Master template
chameleon-3.8.1/src/chameleon/tests/outputs/033.pt000066400000000000000000000002741370067507200217510ustar00rootroot00000000000000 Master template
chameleon-3.8.1/src/chameleon/tests/outputs/034.pt000066400000000000000000000002741370067507200217520ustar00rootroot00000000000000 Master template
chameleon-3.8.1/src/chameleon/tests/outputs/035.pt000066400000000000000000000002751370067507200217540ustar00rootroot00000000000000 New title
chameleon-3.8.1/src/chameleon/tests/outputs/036.pt000066400000000000000000000002661370067507200217550ustar00rootroot00000000000000 New title
chameleon-3.8.1/src/chameleon/tests/outputs/037.pt000066400000000000000000000002621370067507200217520ustar00rootroot00000000000000 Master template
ok
chameleon-3.8.1/src/chameleon/tests/outputs/038.pt000066400000000000000000000000671370067507200217560ustar00rootroot00000000000000 ok chameleon-3.8.1/src/chameleon/tests/outputs/039.pt000066400000000000000000000000001370067507200217420ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/outputs/040.pt000066400000000000000000000007121370067507200217440ustar00rootroot00000000000000 foo chameleon-3.8.1/src/chameleon/tests/outputs/041.pt000066400000000000000000000002131370067507200217410ustar00rootroot00000000000000
Hello world!
Hello world!
Goodbye
chameleon-3.8.1/src/chameleon/tests/outputs/042.pt000066400000000000000000000003131370067507200217430ustar00rootroot00000000000000 Master template
chameleon-3.8.1/src/chameleon/tests/outputs/043.pt000066400000000000000000000000701370067507200217440ustar00rootroot00000000000000 My title chameleon-3.8.1/src/chameleon/tests/outputs/044.pt000066400000000000000000000000531370067507200217460ustar00rootroot00000000000000 a, b chameleon-3.8.1/src/chameleon/tests/outputs/045.pt000066400000000000000000000007171370067507200217560ustar00rootroot00000000000000 ]> ZZZ YYY XXX chameleon-3.8.1/src/chameleon/tests/outputs/046.pt000066400000000000000000000003301370067507200217460ustar00rootroot00000000000000 Master template
chameleon-3.8.1/src/chameleon/tests/outputs/047.pt000066400000000000000000000003311370067507200217500ustar00rootroot00000000000000 Master template
chameleon-3.8.1/src/chameleon/tests/outputs/048.pt000066400000000000000000000003201370067507200217470ustar00rootroot00000000000000 Master template
chameleon-3.8.1/src/chameleon/tests/outputs/049.pt000066400000000000000000000003241370067507200217540ustar00rootroot00000000000000
amp=&amp; lt=&lt;
amp=& lt=<
chameleon-3.8.1/src/chameleon/tests/outputs/059.pt000066400000000000000000000002161370067507200217550ustar00rootroot00000000000000 test test chameleon-3.8.1/src/chameleon/tests/outputs/060.pt000066400000000000000000000001471370067507200217500ustar00rootroot00000000000000 Untitled

Untitled

chameleon-3.8.1/src/chameleon/tests/outputs/061.pt000066400000000000000000000001541370067507200217470ustar00rootroot00000000000000 My document

My document

chameleon-3.8.1/src/chameleon/tests/outputs/062.pt000066400000000000000000000004321370067507200217470ustar00rootroot00000000000000
chameleon-3.8.1/src/chameleon/tests/outputs/063.pt000066400000000000000000000000201370067507200217410ustar00rootroot00000000000000
2
chameleon-3.8.1/src/chameleon/tests/outputs/064.pt000066400000000000000000000000511370067507200217460ustar00rootroot00000000000000
chameleon-3.8.1/src/chameleon/tests/outputs/065.pt000066400000000000000000000002521370067507200217520ustar00rootroot00000000000000 Title
Content
chameleon-3.8.1/src/chameleon/tests/outputs/066.pt000066400000000000000000000000631370067507200217530ustar00rootroot00000000000000 Hello world! chameleon-3.8.1/src/chameleon/tests/outputs/067.pt000066400000000000000000000001421370067507200217520ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/outputs/068.pt000066400000000000000000000002541370067507200217570ustar00rootroot00000000000000 0 < 1 or 0 > 1 0 < 1 or 0 > 1 0 < 1 or 0 > 1 chameleon-3.8.1/src/chameleon/tests/outputs/069-en.pt000066400000000000000000000003441370067507200223600ustar00rootroot00000000000000 Title ('title' translation into 'en' with domain 'test')
chameleon-3.8.1/src/chameleon/tests/outputs/069.pt000066400000000000000000000002611370067507200217560ustar00rootroot00000000000000 Title
chameleon-3.8.1/src/chameleon/tests/outputs/070-en.pt000066400000000000000000000003441370067507200223500ustar00rootroot00000000000000 Title ('title' translation into 'en' with domain 'test')
chameleon-3.8.1/src/chameleon/tests/outputs/070.pt000066400000000000000000000002611370067507200217460ustar00rootroot00000000000000 Title
chameleon-3.8.1/src/chameleon/tests/outputs/071.pt000066400000000000000000000005101370067507200217440ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/outputs/072.pt000066400000000000000000000003361370067507200217530ustar00rootroot00000000000000
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
chameleon-3.8.1/src/chameleon/tests/outputs/073.pt000066400000000000000000000002111370067507200217440ustar00rootroot00000000000000 my title — my site chameleon-3.8.1/src/chameleon/tests/outputs/074.pt000066400000000000000000000002371370067507200217550ustar00rootroot00000000000000 my title — my site chameleon-3.8.1/src/chameleon/tests/outputs/075.pt000066400000000000000000000002601370067507200217520ustar00rootroot00000000000000 Master template
foo
chameleon-3.8.1/src/chameleon/tests/outputs/076.pt000066400000000000000000000003151370067507200217540ustar00rootroot00000000000000 Master template
bar
chameleon-3.8.1/src/chameleon/tests/outputs/077-en.pt000066400000000000000000000001701370067507200223540ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/outputs/077.pt000066400000000000000000000001051370067507200217520ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/tests/outputs/078.pt000066400000000000000000000004331370067507200217570ustar00rootroot00000000000000 1, 2, 3 chameleon-3.8.1/src/chameleon/tests/outputs/079-en.pt000066400000000000000000000010731370067507200223610ustar00rootroot00000000000000 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.8.1/src/chameleon/tests/outputs/079.pt000066400000000000000000000005011370067507200217540ustar00rootroot00000000000000 Welcome

Welcome

An edge case: ${. Site logo Site logo
boo foo.
bar.
chameleon-3.8.1/src/chameleon/tests/outputs/080.pt000066400000000000000000000000211370067507200217410ustar00rootroot00000000000000 Hello world chameleon-3.8.1/src/chameleon/tests/outputs/081.pt000066400000000000000000000000621370067507200217470ustar00rootroot00000000000000 Hello world! chameleon-3.8.1/src/chameleon/tests/outputs/082.pt000066400000000000000000000000621370067507200217500ustar00rootroot00000000000000 Hello world! chameleon-3.8.1/src/chameleon/tests/outputs/083.pt000066400000000000000000000002731370067507200217550ustar00rootroot00000000000000 Master template
chameleon-3.8.1/src/chameleon/tests/outputs/084.pt000066400000000000000000000001641370067507200217550ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/outputs/085-en.pt000066400000000000000000000004401370067507200223530ustar00rootroot00000000000000 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.8.1/src/chameleon/tests/outputs/085.pt000066400000000000000000000002421370067507200217530ustar00rootroot00000000000000 Welcome

Welcome

Click here to continue.

chameleon-3.8.1/src/chameleon/tests/outputs/086.pt000066400000000000000000000004101370067507200217510ustar00rootroot00000000000000 Master template
chameleon-3.8.1/src/chameleon/tests/outputs/087.pt000066400000000000000000000003271370067507200217610ustar00rootroot00000000000000
  • 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.8.1/src/chameleon/tests/outputs/088.pt000066400000000000000000000000101370067507200217470ustar00rootroot00000000000000a, b, c chameleon-3.8.1/src/chameleon/tests/outputs/089.pt000066400000000000000000000000621370067507200217570ustar00rootroot00000000000000 Hello world! chameleon-3.8.1/src/chameleon/tests/outputs/090.pt000066400000000000000000000004121370067507200217460ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/outputs/091.pt000066400000000000000000000000431370067507200217470ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/outputs/101.html000066400000000000000000000001031370067507200222550ustar00rootroot00000000000000



Hello world

chameleon-3.8.1/src/chameleon/tests/outputs/102.html000066400000000000000000000001241370067507200222610ustar00rootroot00000000000000

Hello world

chameleon-3.8.1/src/chameleon/tests/outputs/103.html000066400000000000000000000003201370067507200222600ustar00rootroot00000000000000 chameleon-3.8.1/src/chameleon/tests/outputs/120-en.pt000066400000000000000000000004551370067507200223470ustar00rootroot00000000000000
Hello world! ('Hello world!' translation into 'en')
chameleon-3.8.1/src/chameleon/tests/outputs/120.pt000066400000000000000000000002071370067507200217420ustar00rootroot00000000000000
Hello world!
Tab
chameleon-3.8.1/src/chameleon/tests/outputs/121.pt000066400000000000000000000000741370067507200217450ustar00rootroot00000000000000

Hello world!

chameleon-3.8.1/src/chameleon/tests/outputs/122.pt000066400000000000000000000002501370067507200217420ustar00rootroot00000000000000

Hello world!

Python is a programming language. chameleon-3.8.1/src/chameleon/tests/outputs/123.pt000066400000000000000000000001101370067507200217360ustar00rootroot00000000000000
Hello world!
chameleon-3.8.1/src/chameleon/tests/outputs/124-en.pt000066400000000000000000000002531370067507200223470ustar00rootroot00000000000000

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

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

chameleon-3.8.1/src/chameleon/tests/outputs/124.pt000066400000000000000000000001771370067507200217540ustar00rootroot00000000000000

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

It's a big world.

chameleon-3.8.1/src/chameleon/tests/outputs/125.pt000066400000000000000000000002151370067507200217460ustar00rootroot00000000000000

h1

h2

chameleon-3.8.1/src/chameleon/tests/outputs/126.pt000066400000000000000000000001601370067507200217460ustar00rootroot00000000000000 & & & & chameleon-3.8.1/src/chameleon/tests/outputs/238.pt000066400000000000000000000000421370067507200217510ustar00rootroot00000000000000

macros['page']

chameleon-3.8.1/src/chameleon/tests/outputs/greeting.pt000066400000000000000000000000351370067507200232430ustar00rootroot00000000000000
Hello, undefined.
chameleon-3.8.1/src/chameleon/tests/outputs/hello_world.pt000066400000000000000000000000621370067507200237510ustar00rootroot00000000000000 Hello world! chameleon-3.8.1/src/chameleon/tests/outputs/hello_world.txt000066400000000000000000000000151370067507200241430ustar00rootroot00000000000000Hello world! chameleon-3.8.1/src/chameleon/tests/outputs/multinode-en.pt000066400000000000000000000001671370067507200240450ustar00rootroot00000000000000 Foo Message ('message' translation into 'en') ('Foo ${message}' translation into 'en') chameleon-3.8.1/src/chameleon/tests/outputs/multinode.pt000066400000000000000000000000541370067507200234400ustar00rootroot00000000000000 Foo Message chameleon-3.8.1/src/chameleon/tests/test_astutil.py000066400000000000000000000020041370067507200224430ustar00rootroot00000000000000import ast import sys import unittest class ASTCodeGeneratorTestCase(unittest.TestCase): def _eval(self, tree, env): from chameleon.astutil import ASTCodeGenerator source = ASTCodeGenerator(tree).code code = compile(source, '', 'exec') exec(code, env) if sys.version_info >= (3, 7): def test_slice(self): tree = ast.Module( body=[ ast.Assign( targets=[ ast.Name(id='x', ctx=ast.Store())], value=ast.Call( func=ast.Name(id='f', ctx=ast.Load()), args=[ ast.Slice( upper=ast.Constant(value=0))], keywords=[]))], type_ignores=[] ) def f(x): return x d = {"f": f} self._eval(tree, d) assert d['x'] == slice(None, 0, None) chameleon-3.8.1/src/chameleon/tests/test_doctests.py000066400000000000000000000017731370067507200226220ustar00rootroot00000000000000import 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.8.1/src/chameleon/tests/test_exc.py000066400000000000000000000010041370067507200215340ustar00rootroot00000000000000from 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.8.1/src/chameleon/tests/test_loader.py000066400000000000000000000072371370067507200222410ustar00rootroot00000000000000import 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.8.1/src/chameleon/tests/test_parser.py000066400000000000000000000065231370067507200222640ustar00rootroot00000000000000from __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.8.1/src/chameleon/tests/test_sniffing.py000066400000000000000000000076361370067507200226010ustar00rootroot00000000000000from __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.8.1/src/chameleon/tests/test_templates.py000066400000000000000000000634351370067507200227730ustar00rootroot00000000000000# -*- 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 from chameleon.tales import DEFAULT_MARKER 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__) maxDiff = 4096 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_disable_comment_interpolation(self): template = self.from_string( '', enable_comment_interpolation=False ) self.assertEqual(template(), '') 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_default_marker(self): template = self.from_string('') self.assertEqual( template(), str(id(DEFAULT_MARKER)), 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.") if tuple(sys.version_info) >= (3, 6, 0): def test_f_strings(self): from math import pi from math import sin template = self.from_string('${f"sin({a}) is {sin(a):.3}"}') rendered = template(sin=sin, a=pi) self.assertEqual('sin(3.141592653589793) is 1.2', rendered) 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) source = template.source if sys.version_info < (3, ): source = source.encode("utf-8") self.fail("(%s) - \n%s\n\nCode:\n%s" % ( input_path, diff.rstrip('\n'), source)) chameleon-3.8.1/src/chameleon/tests/test_tokenizer.py000066400000000000000000000027021370067507200227750ustar00rootroot00000000000000import 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.8.1/src/chameleon/tokenize.py000066400000000000000000000103071370067507200204120ustar00rootroot00000000000000# 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.8.1/src/chameleon/utils.py000066400000000000000000000260311370067507200177230ustar00rootroot00000000000000import 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') marker = object() # 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 value_repr(value): if isinstance(value, string_type): short = limit_string(value) return short.replace('\n', '\\n') if isinstance(value, (int, float)): return value if isinstance(value, dict): return '{...} (%d)' % len(value) try: name = str(getattr(value, '__name__', None)), except: name = '-' return '<%s %s at %s>' % (type(value).__name__, name, hex(abs(id(value)))) 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): """ >>> scope = Scope() >>> scope['a'] = 1 >>> copy = scope.copy() Setting a local value and then a global value, we expect the local value to take precedence. >>> copy['a'] = 2 >>> copy.set_global('a', 3) >>> assert copy['a'] == 2 However, setting a new global value should be immediately visible. >>> copy.set_global('b', 1) >>> assert copy['b'] == 1 Make sure the objects are reference-counted, not requiring a full collection to be disposed of. >>> import gc >>> _ = gc.collect() >>> del copy >>> del scope >>> import platform >>> assert gc.collect() == ( ... 0 if platform.python_implementation() == 'CPython' else None ... ) """ __slots__ = "_root", set_local = dict.__setitem__ def __getitem__(self, key): value = dict.get(self, key, marker) if value is not marker: return value root = getattr(self, "_root", marker) if root is not marker: value = dict.get(root, key, marker) if value is not marker: return value raise NameError(key) @property def vars(self): return self def copy(self): inst = Scope(self) root = getattr(self, "_root", self) inst._root = root return inst def set_global(self, name, value): root = getattr(self, "_root", self) root[name] = value setLocal = set_local setGlobal = set_global 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 class ImportableMarker(object): def __init__(self, module, name): self.__module__ = module self.name = name @property def __name__(self): return "%s_MARKER" % self.name def __repr__(self): return '<%s>' % self.name chameleon-3.8.1/src/chameleon/zpt/000077500000000000000000000000001370067507200170245ustar00rootroot00000000000000chameleon-3.8.1/src/chameleon/zpt/__init__.py000066400000000000000000000000021370067507200211250ustar00rootroot00000000000000# chameleon-3.8.1/src/chameleon/zpt/loader.py000066400000000000000000000015121370067507200206430ustar00rootroot00000000000000from 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.8.1/src/chameleon/zpt/program.py000066400000000000000000000664211370067507200210560ustar00rootroot00000000000000import 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 Symbol from ..astutil import parse from ..astutil import marker from .. import tal from .. import tales 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 from ..utils import ImportableMarker 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=[])) CANCEL_MARKER = ImportableMarker(__name__, "CANCEL") 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 = "" _cancel_marker = Symbol(CANCEL_MARKER) # 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 # If set, expression interpolation is enabled in comments. enable_comment_interpolation = 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', 'enable_comment_interpolation', '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 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, ) STATIC_ATTRIBUTES = None # -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, ) 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) ) assignments = [ nodes.Assignment( names, nodes.Value(expr), context == "local") for (context, names, expr) in defines ] # Assign static attributes dictionary to "attrs" value assignments.insert(0, nodes.Alias(["attrs"], STATIC_ATTRIBUTES or EMPTY_DICT)) DEFINE = partial(nodes.Define, assignments) # 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"], self.default_marker)], nodes.Condition( nodes.And([ nodes.BinOp(switch, nodes.IsNot, self._cancel_marker), nodes.Or([ nodes.BinOp(value, nodes.Equals, switch), nodes.BinOp(value, nodes.Equals, self.default_marker) ]) ]), nodes.Cancel([switch], node, self._cancel_marker), )) # 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, [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('