{ "info": { "author": "Alice Bevan-McGregor", "author_email": "alice@gothcandy.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Internet :: WWW/HTTP :: WSGI", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities" ], "description": "=====\ncinje\n=====\n\n \u00a9 2015-2019 Alice Bevan-McGregor and contributors.\n\n..\n\n https://github.com/marrow/cinje\n\n..\n\n |latestversion| |ghtag| |downloads| |masterstatus| |mastercover| |masterreq| |ghwatch| |ghstar|\n\n\nContents\n========\n\n1. `What is cinje?`_\n\n 1. `What kind of name is cinje?!`_\n 2. `Rationale and Goals`_\n\n2. `Installation`_\n\n 1. `Development Version`_\n\n3. `Getting Started`_\n4. `Basic Syntax`_\n\n 1. `Variable Replacement`_\n\n 1. `HTML/XML Escaped Replacement`_\n 2. `Unescaped Replacement`_\n 3. `HTML Attributes Replacement`_\n 4. `Formatted Replacement`_\n 5. `JSON Object Replacement`_\n\n 2. `Block Transformations`_\n\n 1. `Module Scope`_\n 2. `Function Declaration`_\n 3. `Flow Control`_\n\n 3. `Inline Transformations`_\n\n 1. `Code`_\n 2. `Comments`_\n 3. `Flush`_\n 4. `Text`_\n\n 4. `Inheritance`_\n\n5. `Version History`_\n6. `License`_\n\n\n\nWhat is cinje?\n==============\n\nCinje is a modern, elegant template engine constructed as a Python domain specific language (DSL) that integrates into\nyour applications as any other Python code would: by importing them. Your templates are transformed from their source\ninto clean, straightforward, and understandable Python source prior to the Python interpreter compiling it to bytecode.\n\nWhat kind of name is cinje?!\n----------------------------\n\nIt's a word from the constructed language `Lojban `_. A combination of Hindi \"\u015bikana\", English\n\"wrinkle\", and Chinese \"zh\u00e9\". It translates as \"is a wrinkle/crease/fold [shape] in\". It's also a Hungarian noun\nrepresenting the posessive third-person singular form of \"cin\", meaning \"tin\". The \"c\" makes a \"sh\" sound, the \"j\"\nmakes a \"jy\" sound almost like the \"is\" in \"vision\". Correct use does not capitalize the name except at the beginning\nof sentences.\n\nRationale and Goals\n-------------------\n\nThere is no shortage of template engines available in the Python ecosystem. The following items help differentiate\ncinje from the competition:\n\n* There are few to no high-performance template engines which support:\n\n - Mid-stream flushing for delivery of partial content as it generates. The vast majority of engines buffer the\n entire template during rendering, returning the result once at the end. This is disadvantageous for any content\n which involves large amounts of computation, and prevents browsers from eagerly loading external static assets. By\n comparison, cinje supports a ``: flush`` command to yield the buffer generated so far.\n\n - Direct use as a WSGI iterable body. In cinje, template functions are generators which can be used directly as a\n WSGI body. With no explicit ``: flush`` commands behaviour matches other engines: the buffer will be yielded once,\n at the end.\n\n* Virtually all require boilerplate to \"load\" then \"render\" the template, such as instantiating a ``Template`` class\n and calling a ``render`` method, which is silly and a waste of repeated developer effort. Alternatively, complex\n framework-specific adapters can be used for boilerplate-free engine use, but this solution is sub-optimal. Since\n almost all generate Python code in the end, why not treat the templates as Python modules to start with and let the\n language, which already has all of this machinery, do what it was designed to do? This is what cinje does.\n\n* Virtually all perform low-level parsing, lexing, and Abstract Syntax Tree (AST) manipulation. These things are\n difficult for developers new to the language to understand. Additionally, many manually orchestrate Python's own\n parsing and compilation phases, and some even manually manage the bytecode cache. This greatly increases the\n complexity of the engine itself.\n\n* Only a small minority of engines offer extensible syntax which allows for the creation of new directives.\n\n* Performance is less important than streaming functionality, but it should be at least \"par\" with similar engines\n such as ``mako`` or ``tenjin`` for complete rendering times. Utilizing streaming functionality should not impose\n undue overhead. The capability to stream and be reasonably fast should neither obfuscate the template engine code\n nor obfuscate the generated template code.\n\nInstallation\n============\n\nInstalling ``cinje`` is easy, just execute the following in a terminal::\n\n pip install cinje\n\n**Note:** We *strongly* recommend always using a container, virtualization, or sandboxing environment of some kind when\ndeveloping using Python; installing things system-wide is yucky (for a variety of reasons) nine times out of ten. We\nprefer light-weight `virtualenv `_, others prefer solutions as\nrobust as `Vagrant `_.\n\nIf you add ``cinje`` to the ``install_requires`` argument of the call to ``setup()`` in your application's\n``setup.py`` file, cinje will be automatically installed and made available when your own application or\nlibrary is installed. We recommend using \"less than\" version numbers to ensure there are no unintentional\nside-effects when updating. Use ``cinje<1.2`` to get all bugfixes for the current release, and\n``cinje<2.0`` to get bugfixes and feature updates while ensuring that large breaking changes are not installed.\n\nWhile cinje does not have any hard dependencies on any other package, it is **strongly** recommended that applications\nusing cinje also install the ``markupsafe`` package to provide more efficient string escaping and some additional\nfunctionality such as object protocol support for markup generation.\n\n\nDevelopment Version\n-------------------\n\n |developstatus| |developcover| |ghsince| |issuecount| |ghfork|\n\nDevelopment takes place on `GitHub `_ in the\n`cinje `_ project. Issue tracking, documentation, and downloads\nare provided there.\n\nInstalling the current development version requires `Git `_, a distributed source code management\nsystem. If you have Git you can run the following to download and *link* the development version into your Python\nruntime::\n\n git clone https://github.com/marrow/cinje.git\n (cd cinje; python setup.py develop)\n\nYou can then upgrade to the latest version at any time::\n\n (cd cinje; git pull; python setup.py develop)\n\nIf you would like to make changes and contribute them back to the project, fork the GitHub project, make your changes,\nand submit a pull request. This process is beyond the scope of this documentation; for more information see\n`GitHub's documentation `_.\n\n\nGetting Started\n===============\n\nIn order for imports of cinje template functions to correctly transform the source you must first ``import cinje``\nin order to register the file encoding. This may sound like magic, but it's not: it's just the Python unicode decoding\nhook in the ``cinje.encoding`` module. Once this has been done you can directly import functions from cinje modules.\n\nYour cinje template files are Python modules like any other: they should have a ``.py`` filename extension and begin\nwith the the encoding declaration::\n\n # encoding: cinje\n\nThis tells Python to process the file using the ``cinje`` codec prior to interpreting the code. Cinje itself assumes\nthe file is actually UTF-8 encoded.\n\nCalling a cinje function is identical to calling a generator function, as all cinje template functions\u2014those containing\ntext\u2014are generators. Normal template functions generate unicode fragments. Wrapper template functions will at some\npoint generate a ``None`` value; you can iterate up to that point, and subsequently continue iterating after that\npoint using the ``cinje.util.interrupt`` iterator to iterate up to the first ``None``.\n\nPrimarily for testing small chunks of template template code in actual unit tests, two helpful functions are provided:\n\n* ``cinje.fragment(string, name=\"anonymous\", **context)`` Transform a template fragment into a callable function.\n\n Only one function may be declared, either manually, or automatically. If automatic defintition is chosen the\n resulting function takes no arguments. Additional keyword arguments are passed through as global variables.\n\n* ``cinje.flatten(input, file=None, encoding=None, errors='strict')`` Return a flattened representation of a cinje\n chunk stream.\n\n This has several modes of operation. If no ``file`` argument is given, output will be returned as a string.\n The type of string will be determined by the presence of an ``encoding``; if one is given the returned value is a\n binary string, otherwise the native unicode representation. If a ``file`` is present, chunks will be written\n iteratively through repeated calls to ``file.write()``, and the amount of data (characters or bytes) written\n returned. The type of string written will be determined by ``encoding``, just as the return value is when not\n writing to a file-like object. The ``errors`` argument is passed through when encoding.\n\n We can highly recommend using the various streaming IO containers available in the\n `io `_ module, though\n `tempfile `_ classes are also quite useful.\n\n* ``cinje.stream(input, encoding=None, errors='strict')`` Safely iterate a template generator, ignoring ``None``\n values and optionally stream encoding. Used internally by ``cinje.flatten``, this allows for easy use of a template\n generator as a WSGI body.\n\nYou can always also transform arbitrary template source by passing it through ``.decode('cinje')``, which would return\nthe resulting transformed source code.\n\n\nBasic Syntax\n============\n\nIf you have prior experience using template engines, the syntax should feel quite familiar. Lines prefixed with a\ncolon (``:``) are \"code\". Lines prefixed with a hash mark (`#`) are comments. All other lines are treated as\ntemplate text. Template text is not allowed at the module level as it is not valid for a module to ``yield``.\n\nCode lines are processed by each of the different \"block\" and \"inline\" processor classes and runs of template text\nare processed by the ``cinje.inline.text`` processor, with replacements processed by the ``cinje.util.chunk``\nhelper function.\n\nText lines can have a \"continuation\" marker (``\\``) on the end to denote that no newline should be emitted there.\n\nWe use a shell-like argument format for illustrating the syntax.\n\n\nVariable Replacement\n--------------------\n\nThere are several flavours of variable replacement available. Within these use of curly braces is allowed only if\nthe braces are balanced. Any of the helper functions mentioned can be overridden at the module or function level.\n\nAll variable replacement is a simple transformation of the source text into a function call wrapped version of the\nsource text.\n\nHTML/XML Escaped Replacement\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\t``${}`` \u2192 ``_escape()``\n\nThe default replacement operator is a Python expression surrounded by ``${`` and ``}``. In the generated code your\nexpression will be wrapped in a call to ``_escape()`` which defaults to the ``escape`` function imported from the\n``cinje.helpers`` module. If ``markupsafe`` is installed its escaping function will be used, otherwise the Python-\nstandard ``html.escape`` function will be used. Please see the\n`MarkupSafe `_ documentation for a full description of the additional\ncapabilities it offers. The result is appended to the current buffer.\n\n============================= ================================ ================================\ncinje Python Result\n============================= ================================ ================================\n``${2+2}`` ``_escape(2+2)`` ``\"4\"``\n``${\"Hi.\"}`` ``_escape(\"Hi.\")`` ``\"<i>Hi.</i>\"``\n============================= ================================ ================================\n\nUnescaped Replacement\n~~~~~~~~~~~~~~~~~~~~~\n\n\t``#{}`` \u2192 ``_bless()``\n\nThe less-safe replacement does not escape HTML entities; you should be careful where this is used. For trusted\ndata, though, this form is somewhat more efficient. In the generated code your expression will be wrapped in a call\nto ``_bless()`` which defaults to the ``bless`` function imported from the ``cinje.helpers`` module. If\n``markupsafe`` is installed its ``Markup`` class will be used, otherwise the Python ``str`` function will be used.\nThe result is appended to the current buffer.\n\n============================= ================================ ================================\ncinje Python Result\n============================= ================================ ================================\n``#{27*42}`` ``_bless(27*42)`` ``\"1134\"``\n``#{\"Hi.\"}`` ``_bless(\"Hi.\")`` ``\"Hi.\"``\n============================= ================================ ================================\n\nHTML Attributes Replacement\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\t``&{}`` \u2192 ``_args()``\n\nA frequent pattern in reusable templates is to provide some method to emit key/value pairs, with defaults, as HTML or\nXML attributes. To eliminate boilerplate cinje provides a replacement which handles this naturally and can help\nusers, especially users new to template engines, avoid certain common but hideous structures to conditionally add\nattributes.\n\nAttributes which are literally ``True`` have no emitted value. Attributes which are literally ``False`` or ``None``\nare omitted. Non-string iterables are treated as a space-separated set of strings, for example, for use as a set of\nCSS classes. Trailing underscores are removed, to allow for use of Python-reserved words. Single underscores\n(``_``) within the key are replaced with hyphens. Double underscores (``__``) within a key are replaced with colons.\n\nA value can be provided, then defaults provided using the ``key=value`` keyword argument style; if the key does not\nhave a value in the initial argument, the default will be used.\n\n=================================== ======================================= ================================\ncinje Python Result\n=================================== ======================================= ================================\n``&{autocomplete=True}`` ``_args(autocomplete=True)`` ``\" autocomplete\"``\n``&{autocomplete=False}`` ``_args(autocomplete=False)`` ``\"\"`` (empty)\n``&{data_key=\"value\"}`` ``_args(data_key=\"value\")`` ``' data-key=\"value\"'``\n``&{xmlns__foo=\"bob\"}`` ``_args(xmlns__foo=\"bob\")`` ``' xmlns:bob=\"foo\"'``\n``&{name=\"Bob Dole\"}`` ``_args(name=\"Bob Dole\")`` ``' name=\"Bob Dole\"'``\n``&{somevar, default=27}`` ``_args(somevar, default=\"hello\")`` (depends on ``somevar``)\n=================================== ======================================= ================================\n\nA preceeding space will be emitted automatically if any values would be emitted. The following would be correct::\n\n\t\n\nFormatted Replacement\n~~~~~~~~~~~~~~~~~~~~~\n\n\t``%{ }`` \u2192 ``_bless().format()``\n\nModern string formatting in Python utilizes the ``str.format`` string formatting system. To facilitate replacements\nusing the advanced formatting features available in ``markupsafe`` while removing common boilerplate the \"formatted\nreplacement\" is made available. Your source expression undergoes some mild reformatting, similar to that applied to\nfunction declarations, seen later.\n\n=================================== ===============================================\ncinje Python\n=================================== ===============================================\n``%{somevar 42, num=27}`` ``_bless(somevar).format(42, num=27)``\n``%{\"Lif: {} {num}\" 42, num=27}`` ``_bless(\"Lif: {} {num}\").format(42, num=27)``\n=================================== ===============================================\n\nAny expression can be used for the \"format string\" expression, however for sanity's sake it's generally a good idea to\nkeep it as a short string literal or provide it from a variable.\n\n**Note:** The format string is blessed, meaning it should not be sourced from user-supplied data, for security\nreasons. When MarkupSafe is *not* installed the replacements are passed through to Python-standard string formatting.\nIf, however, MarkupSafe *is* installed, then the replacements are escaped prior to formatting and additional\nfunctionality is available to make your objects HTML-formatting aware. (See the MarkupSafe documentation.)\n\nJSON Object Replacement\n~~~~~~~~~~~~~~~~~~~~~~~\n\n\t``@{}`` \u2192 ``_json()``\n\nIt is sometimes useful to pass data through a template to JavaScript. This will emit the JSON-serialized version of\nthe expression result.\n\n\nBlock Transformations\n---------------------\n\nBlock transformations typically denote some form of scope change or flow control, and must be terminated with an\n\"end\" instruction. Blocks not terminated by the end of the file will be automatically terminated, allowing trailing\nterminators to be elided away and omitted from most templates.\n\nModule Scope\n~~~~~~~~~~~~\n\nThis is an automatic transformer triggered by the start of a source file. It automatically adds a few imports to the\ntop of your file to import the required helpers from cinje.\n\nBy default the ``buffer`` flag is enabled in all modules.\n\nFunction Declaration\n~~~~~~~~~~~~~~~~~~~~\n\n\t``: def [ ]`` \u2192 ``def ([][]):``\n\nLines beginning with ``: def`` are used to declare functions within your template source::\n\n\t: def somefunction\n\t\tHello world!\n\t: end\n\nThe above transforms to, roughly, the following Python source::\n\n\tdef somefunction(*, _escape=_escape, _bless=_bless):\n\t\t_buffer = []\n\t\t_buffer.append(_bless(\"\\tHello world!\\n\"))\n\t\tyield ''.join(_buffer)\n\nYou do not need the extraneous trailing colon to denote the end of the declaration, nor do you need to provide\nparenthesis around the argument specification. The optimization keyword-only arguments will be added automatically to\nthe argument specification you give on non-Pypy Python 3 versions. It will gracefully handle integration into your\narglist even if your arglist already includes the keyword-only marker, or combinations of ``*args`` or ``**kw``.\n\nYou can specify flags to enable or disable within the context of a specific function using Python 3 function\nannotations. These annotations will work for setting and unsetting flags across both Python 2 and Python 3 runtimes.\n\nThe most common use of per-function flags is to disable buffering, or enable whitespace stripping::\n\n\t: def anotherfunction -> !buffer strip\n\t\tThis won't have a trailing newline, and will be immediately yielded.\n\nThe result of this would be::\n\n\tdef anotherfunction(...):\n\t\tyield \"This won't have a trailing newline, and will be immediately yielded.\"\n\nFlags declared in this way will have their effect reversed automatically at the close of the function scope.\n\nFlow Control\n~~~~~~~~~~~~\n\n\t``: `` \u2192 ``:``\n\nCinje is fairly agnostic towards most Python flow control statements. The ``cinje.block.generic`` transformer handles\nmost Python block scope syntax. These include:\n\n* **Conditionals** including ``if``, ``elif``, and ``else``.\n* **Iterators** including ``while``, and ``for``, inlcuding the ``else`` block for ``for`` loops.\n* **Context managers** via ``with``.\n* **Exception handling** including ``try``, ``except``, ``finally``, and ``else``.\n\nIn all cases the only real transformation done is moving the colon from the beginning of the declared line to the end.\n\nA helper is provided called ``iterate`` which acts similarly to ``enumerate`` but can provide additional details.\nIt's a generator that yields ``namedtuple`` values in the form ``(first, last, index, total, value)``. If the current\nloop iteration represents the first iteration, ``first`` will be True. Similarly\u2014and even for generators where a\ntotal number of values being iterated could not be calculated beforehand\u2014on the final iteration ``last`` will be True.\nThe ``index`` value is an atomic counter provided by ``enumerate``, and ``total`` will be the total number of elements\nbeing iterated if the object being iterated supports length determination. You can loop over its results directly::\n\n\t: for item in iterate(iterable)\n\t\t: if item.first\n\t\t\t\u2026\n\t\t: end\n\t: end\n\nYou can also unpack them::\n\n\t: for first, last, index, total, value in iterate(iterable)\n\t\t\u2026\n\t: end\n\nIf you wish to unpack the values being iterated, you can wrap the additional unpacking in a tuple::\n\n\t: for first, last, i, total, (foo, bar, baz) in iterate(iterable)\n\t\t\u2026\n\t: end\n\n\nInline Transformations\n----------------------\n\nInline transformations are code lines that do not \"start\" a section that subsequently needs an \"end\".\n\nCode\n~~~~\n\nLines prefixed with a colon (``:``) that aren't matched by another transformation rule are treated as inline Python\ncode in the generated module. Within these bits of code you do have access to the helpers and buffer, and so can\neasily customize template rendering at will.\n\nThe only lines acceptable at the module scope are code and comments.\n\nComments\n~~~~~~~~\n\nBasic comments are preserved in the final Python source. Any line starting with the Python-standard line comment\nprefix, a ``#`` hash mark or \"pound\" symbol, that doesn't match another rule, will be preserved as a comment. If the\nline is instead prefixed with a double hash mark ``##`` the comment will be stripped and *not* included in the final\nPython module.\n\nFlush\n~~~~~\n\nThe ``: flush`` statement triggers cinje to emit the Python code needed to yield the current contents of the template\nbuffer and clear it. The result, in Python, is roughly analogous to::\n\n\tyield ''.join(_buffer)\n\t_buffer.clear()\n\nA flush is automatically triggered when falling off the bottom of a template function if it is known that there will\nbe un-flushed text in the buffer. (Processing context marked with the \"dirty\" flag.)\n\nText\n~~~~\n\nText covers every other line present in your template source. Cinje efficiently gathers consecutive lines of template\ntext, collapses runs of static text into single strings, and splits the template text up to process replacements.\n\nTemplate text is not permitted at the module scope as there can be no way to \"yield\" the buffer from there. To save\non method calls, the following::\n\n\t\n\nIs transformed, roughly, into the following single outer call and three nested calls::\n\n\t_buffer.extend((\n\t\t_bless('')\n\t))\n\nSee the Variable Replacement section for details on the replacement options that are available and how they operate.\n\nPragma\n~~~~~~\n\n*New in Version 1.1*\n\nThe ``: pragma [ ][...]`` directive allows you to enable or disable one or more processing flags. Usage is\nstraightforward; to add a flag to the current set of flags::\n\n\t: pragma flag\n\nTo subsequently remove a flag::\n\n\t: pragma !flag\n\nMultiple flags may be whitespace separated and can mix addition and removal::\n\n\t: pragma flag !other_flag\n\nNo flag may contain whitespace. Built-in flags include:\n\n* ``init``: The module scope has been prepared. Unsetting this is unwise.\n* ``text``: Text fragments have been utilized within the current function, making this a template function.\n* ``dirty``: It is known to the engine that the current buffer contains content which will need to be flushed.\n* ``buffer``: Enabled by default, its presence tells cinje to use a buffer with explicit flushing. When removed,\n buffering is disabled and every fragment is flushed as it is encountered, and ``: use`` and ``: using`` behaviour\n is altered to ``yield from`` instead of adding the child template to the buffer.\n It is potentially very useful to disable this in the context of ``: use`` and ``: using`` to make child template\n ``: flush`` statements effective.\n* ``using``: Indicates the ``_using_stack`` variable is available at this point in the translated code, i.e. to track\n nested ``: using`` statements.\n\n\nInheritance\n-----------\n\nDue to the streaming and \"native Python code\" natures of cinje, template inheritance is generally handled through\nthe standard definition of functions, and passing of those first-class objects around. The most common case, where\none template \"wraps\" another, is handled through the ``: using`` and ``: yield`` directives.\n\nAn example \"wrapper\" template::\n\n\t: def page **properties\n\t\n\t\t\n\t\t\t: yield\n\t\t\n\t\n\t: end\n\nWhen called, functions that include a bare yield (and only one is allowed per function) will flush their buffers\nautomatically prior to the yield, then flush automatically at the end of the function, just like any other. This has\nthe effect of extending the wrapped template's buffer by, at a minimum, two elements (prefix and suffix), though\nadditional ``: flush`` statements within the wrapper are allowed.\n\n**Note:** Because the bare yield will produce a value of ``None``, wrapping functions like these are **not**\nsafe for use as a WSGI body iterable without wrapping in a generator to throw away ``None`` values.\n\nThe syntax for the ``using`` directive is ``: using [ ]``, thus to use this wrapper::\n\n\t: using page\n\t\t

Hello world!

\n\t: end\n\nExecution of this would produce the following HTML::\n\n\t\n\t\t\n\t\t\t

Hello world!

\n\t\t\n\t\n\nBecause wrapping templates are just template functions like any other, you can pass arguments to them. In the above\nexample we're using arbitrary keyword arguments as an \"HTML attribute\" replacement. The following::\n\n\t: using page class_=\"hero\"\n\t: end\n\nWould produce the following::\n\n\t\n\t\t\n\t\t\n\t\n\nLastly, there is a quick shortcut for consuming a template function and injecting its output into the current buffer::\n\n\t: use [ ]\n\nAnd directly transforms to::\n\n\t_buffer.extend(())\n\nJust like with ``using``, the result of the expression must be a callable generator function.\n\n\nVersion History\n===============\n\nVersion 1.1.2\n-------------\n\n* *Fixed* `Python 3.7 exception use within generators. `_\n\n* *Added* Genshi to the `benchmark comparison suite `_.\n\n* *Fixed* minor docstring typo.\n\nVersion 1.1.1\n-------------\n\n* *Fixed* `incorrect double-decoding (#25) `_ of UTF-8 that was preventing\n use of templates containing non-ASCII text.\n\n* *Fixed* incorrect variable reference in the built-in (`cinje.std.html`) list helper.\n\n* *Added* Python 3.6 testing, pre-commit hooks, and Makefile-based automation.\n\n* *Removed* Python 3.3 testing and support, `flake8` enforcement, and `tox` build/test automation.\n\n\nVersion 1.1\n-----------\n\n* *Enhanced Pypy support.* Pypy does not require optimizations which potentially obfuscate the resulting code.\n So we don't do them.\n\n* *Fixed* incorrect `#{}` handling when it was the first non-whitepsace on a line. (#22)\n\n* *Fixed* buffer iteration edge case if the first template text in a function is deeper than the function scope. (#21)\n\n* *Python 3-style function annotations* can now be used to define function-wide \"pragma\" additions and removals, even\n on Python 2. (#8)\n\n* *Pragma processing directives.* Processing flags can be set and unset during the translation process using\n `: pragma`.\n\n* *Unbuffered mode.* Cinje can now operate in unbuffered mode. Each contiguous chunk is individually yielded. (#8)\n\n* *Secret feature.* Have a cinje template? Want to more easily peek behind the curtain? (Sssh, it's a completely\n unsupported feature that even syntax colors if `pygments` is installed.) `python -m cinje source file.py`\n\nVersion 1.0\n-----------\n\n* Initial release.\n\n\nLicense\n=======\n\ncinje has been released under the MIT Open Source license.\n\nThe MIT License\n---------------\n\nCopyright \u00a9 2015-2019 Alice Bevan-McGregor and contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\ndocumentation files (the \u201cSoftware\u201d), to deal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the\nSoftware.\n\nTHE SOFTWARE IS PROVIDED \u201cAS IS\u201d, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n.. |ghwatch| image:: https://img.shields.io/github/watchers/marrow/cinje.svg?style=social&label=Watch\n :target: https://github.com/marrow/cinje/subscription\n :alt: Subscribe to project activity on Github.\n\n.. |ghstar| image:: https://img.shields.io/github/stars/marrow/cinje.svg?style=social&label=Star\n :target: https://github.com/marrow/cinje/subscription\n :alt: Star this project on Github.\n\n.. |ghfork| image:: https://img.shields.io/github/forks/marrow/cinje.svg?style=social&label=Fork\n :target: https://github.com/marrow/cinje/fork\n :alt: Fork this project on Github.\n\n.. |masterstatus| image:: http://img.shields.io/travis/marrow/cinje/master.svg?style=flat\n :target: https://travis-ci.org/marrow/cinje/branches\n :alt: Release build status.\n\n.. |mastercover| image:: http://img.shields.io/codecov/c/github/marrow/cinje/master.svg?style=flat\n :target: https://codecov.io/github/marrow/cinje?branch=master\n :alt: Release test coverage.\n\n.. |masterreq| image:: https://img.shields.io/requires/github/marrow/cinje.svg\n :target: https://requires.io/github/marrow/cinje/requirements/?branch=master\n :alt: Status of release dependencies.\n\n.. |developstatus| image:: http://img.shields.io/travis/marrow/cinje/develop.svg?style=flat\n :target: https://travis-ci.org/marrow/cinje/branches\n :alt: Development build status.\n\n.. |developcover| image:: http://img.shields.io/codecov/c/github/marrow/cinje/develop.svg?style=flat\n :target: https://codecov.io/github/marrow/cinje?branch=develop\n :alt: Development test coverage.\n\n.. |developreq| image:: https://img.shields.io/requires/github/marrow/cinje.svg\n :target: https://requires.io/github/marrow/cinje/requirements/?branch=develop\n :alt: Status of development dependencies.\n\n.. |issuecount| image:: http://img.shields.io/github/issues-raw/marrow/cinje.svg?style=flat\n :target: https://github.com/marrow/cinje/issues\n :alt: Github Issues\n\n.. |ghsince| image:: https://img.shields.io/github/commits-since/marrow/cinje/1.1.2.svg\n :target: https://github.com/marrow/cinje/commits/develop\n :alt: Changes since last release.\n\n.. |ghtag| image:: https://img.shields.io/github/tag/marrow/cinje.svg\n :target: https://github.com/marrow/cinje/tree/1.1.2\n :alt: Latest Github tagged release.\n\n.. |latestversion| image:: http://img.shields.io/pypi/v/cinje.svg?style=flat\n :target: https://pypi.python.org/pypi/cinje\n :alt: Latest released version.\n\n.. |downloads| image:: http://img.shields.io/pypi/dw/cinje.svg?style=flat\n :target: https://pypi.python.org/pypi/cinje\n :alt: Downloads per week.\n\n.. |cake| image:: http://img.shields.io/badge/cake-lie-1b87fb.svg?style=flat\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "https://github.com/marrow/cinje/releases", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/marrow/cinje/", "keywords": "template,source translation,dsl,streaming,chunked", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "cinje", "package_url": "https://pypi.org/project/cinje/", "platform": "", "project_url": "https://pypi.org/project/cinje/", "project_urls": { "Download": "https://github.com/marrow/cinje/releases", "Homepage": "https://github.com/marrow/cinje/" }, "release_url": "https://pypi.org/project/cinje/1.1.2/", "requires_dist": [ "pytest ; extra == 'development'", "pytest-cov ; extra == 'development'", "pytest-flakes ; extra == 'development'", "markupsafe ; extra == 'development'", "pre-commit ; extra == 'development'", "webob ; extra == 'safe'" ], "requires_python": "", "summary": "A Pythonic and ultra fast template engine DSL.", "version": "1.1.2" }, "last_serial": 4916210, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "474ae063df551b95489464de706198a3", "sha256": "c0e3334d093c5898d0ec6a5ba94609db3827b2be4ea7132d4ac46f0183a9f695" }, "downloads": -1, "filename": "cinje-1.0.0-py2.7.egg", "has_sig": false, "md5_digest": "474ae063df551b95489464de706198a3", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 56639, "upload_time": "2015-12-06T00:58:45", "url": "https://files.pythonhosted.org/packages/00/8c/3d1648dcd733a4d53786de548c47b9edefbb46f00108b9889aa509e56d9a/cinje-1.0.0-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "2d6d148bca8af32676ccdb08ad9bd469", "sha256": "983e8a9ae7492b9968b2ccc9344f813f15da7213d606742a241417b20900c385" }, "downloads": -1, "filename": "cinje-1.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "2d6d148bca8af32676ccdb08ad9bd469", "packagetype": "bdist_wheel", "python_version": "3.4", "requires_python": null, "size": 44461, "upload_time": "2015-12-06T00:58:38", "url": "https://files.pythonhosted.org/packages/57/e6/43cc4abf1b97250381f645a5c785ea4428f2e26ece329f3429aa157d1d01/cinje-1.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ba7488722bb363447b88e690cf18fb13", "sha256": "6db263c174636bb28e670c91151924a8b08ffe78a6d0136b979de8c0970280d3" }, "downloads": -1, "filename": "cinje-1.0.0-py3.4.egg", "has_sig": false, "md5_digest": "ba7488722bb363447b88e690cf18fb13", "packagetype": "bdist_egg", "python_version": "3.4", "requires_python": null, "size": 58201, "upload_time": "2015-12-06T00:58:53", "url": "https://files.pythonhosted.org/packages/74/67/d25879f9b73ae247c0f5038c12121652ef586721bc02453d7b0364366dc3/cinje-1.0.0-py3.4.egg" }, { "comment_text": "", "digests": { "md5": "f2244acf24a08cbfb13681f72301e54c", "sha256": "3b809b27a3ab6717d7e9ac3d41eb533333e9a7624a57f4cddfc9691b26152715" }, "downloads": -1, "filename": "cinje-1.0.0.tar.gz", "has_sig": false, "md5_digest": "f2244acf24a08cbfb13681f72301e54c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43805, "upload_time": "2015-12-06T00:58:32", "url": "https://files.pythonhosted.org/packages/8e/87/ee2217b97506aea25019098f6c927218b91f712c33aed4306e9333a5d019/cinje-1.0.0.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "f10a45c3b44b157a4f0a4271cbdffbbb", "sha256": "fe51c6525e25fe928c4cf787f081a02f55a57ce8d48e2b705d9745bf9fabf0be" }, "downloads": -1, "filename": "cinje-1.1.0-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "f10a45c3b44b157a4f0a4271cbdffbbb", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 50395, "upload_time": "2016-05-03T03:49:12", "url": "https://files.pythonhosted.org/packages/46/83/61775e9cf61de482684aacf78c96b5e3dbb031ebe6d7f93b87465f82e5dc/cinje-1.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c01361a428acf7ba61bc0430bdfc82a7", "sha256": "9f52382c908dedcf97e2ee527624cc484018b9494321ddabf8bf5351429a58ce" }, "downloads": -1, "filename": "cinje-1.1.0.tar.gz", "has_sig": true, "md5_digest": "c01361a428acf7ba61bc0430bdfc82a7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 57284, "upload_time": "2016-05-03T03:49:03", "url": "https://files.pythonhosted.org/packages/08/05/99c3956857295af7bdaa692a1e3ebb15749f4a193d20faa469d8397761aa/cinje-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "f1503422496c7b7e9bfa5d5247abded2", "sha256": "3d06c2632cdb6498f5cb25fa194a45176e2e32fe0eed48848af35ca195bb4294" }, "downloads": -1, "filename": "cinje-1.1.1-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "f1503422496c7b7e9bfa5d5247abded2", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 68574, "upload_time": "2018-10-24T15:07:08", "url": "https://files.pythonhosted.org/packages/93/c4/8d5b0052d2e5827b2c63873e46cdd9b6c0150ba520d6eaccc887a678a279/cinje-1.1.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7a134a0d7285acbcc469ca1591f4dbaa", "sha256": "9ff2657ddc77515706f94e68843b1ea4ef5d026df7ed7ff3a4fee86c739d95da" }, "downloads": -1, "filename": "cinje-1.1.1.tar.gz", "has_sig": true, "md5_digest": "7a134a0d7285acbcc469ca1591f4dbaa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 55636, "upload_time": "2018-10-24T15:07:10", "url": "https://files.pythonhosted.org/packages/86/07/8fde944ee2833b6143f2ba9c45b078344d539aa7a14a8267cf92e4ff255a/cinje-1.1.1.tar.gz" } ], "1.1.2": [ { "comment_text": "", "digests": { "md5": "cf5a2750c34eda7de73808ebe3ebf7d9", "sha256": "10e89369ce2991103665b1472dd89d86b35cccb399fbfdc658c67fe4b577a161" }, "downloads": -1, "filename": "cinje-1.1.2-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "cf5a2750c34eda7de73808ebe3ebf7d9", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 71532, "upload_time": "2019-03-08T16:27:40", "url": "https://files.pythonhosted.org/packages/87/da/7de96a954032c069b0f40db8a35f7a273941128a7f09a509a0ea4027e11d/cinje-1.1.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ca9b21cf97ccdadee24525458c074382", "sha256": "953282c52d0e302a2aeadbe753af415210fdb950ca924e0eccc924959d3c735d" }, "downloads": -1, "filename": "cinje-1.1.2.tar.gz", "has_sig": true, "md5_digest": "ca9b21cf97ccdadee24525458c074382", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 55784, "upload_time": "2019-03-08T16:27:42", "url": "https://files.pythonhosted.org/packages/a5/01/8b979ce8dfd672f79d5f0a92a02e1d4d197899376496b3e0aa12ec7b962a/cinje-1.1.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "cf5a2750c34eda7de73808ebe3ebf7d9", "sha256": "10e89369ce2991103665b1472dd89d86b35cccb399fbfdc658c67fe4b577a161" }, "downloads": -1, "filename": "cinje-1.1.2-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "cf5a2750c34eda7de73808ebe3ebf7d9", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 71532, "upload_time": "2019-03-08T16:27:40", "url": "https://files.pythonhosted.org/packages/87/da/7de96a954032c069b0f40db8a35f7a273941128a7f09a509a0ea4027e11d/cinje-1.1.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ca9b21cf97ccdadee24525458c074382", "sha256": "953282c52d0e302a2aeadbe753af415210fdb950ca924e0eccc924959d3c735d" }, "downloads": -1, "filename": "cinje-1.1.2.tar.gz", "has_sig": true, "md5_digest": "ca9b21cf97ccdadee24525458c074382", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 55784, "upload_time": "2019-03-08T16:27:42", "url": "https://files.pythonhosted.org/packages/a5/01/8b979ce8dfd672f79d5f0a92a02e1d4d197899376496b3e0aa12ec7b962a/cinje-1.1.2.tar.gz" } ] }