{"id":16843687,"url":"https://github.com/mahmoud/ashes","last_synced_at":"2025-03-17T05:31:59.747Z","repository":{"id":6424282,"uuid":"7662844","full_name":"mahmoud/ashes","owner":"mahmoud","description":"⚱️ Lightweight, self-contained templating for Python 2 and 3, a la Dust templates","archived":false,"fork":false,"pushed_at":"2024-03-31T23:44:26.000Z","size":499,"stargazers_count":54,"open_issues_count":11,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-15T04:54:25.221Z","etag":null,"topics":["ashes","dust","dust-templates","dustjs","python","templating"],"latest_commit_sha":null,"homepage":"http://ashes.readthedocs.org","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mahmoud.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.txt","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-01-17T08:59:26.000Z","updated_at":"2024-11-28T16:31:11.000Z","dependencies_parsed_at":"2024-06-19T13:26:25.938Z","dependency_job_id":"b046c969-d92a-4f11-9b2a-f7aa226832a9","html_url":"https://github.com/mahmoud/ashes","commit_stats":{"total_commits":285,"total_committers":8,"mean_commits":35.625,"dds":0.3192982456140351,"last_synced_commit":"93485d52ab64b1bdafafc22a2644c8c878a89ded"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahmoud%2Fashes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahmoud%2Fashes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahmoud%2Fashes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahmoud%2Fashes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mahmoud","download_url":"https://codeload.github.com/mahmoud/ashes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243685556,"owners_count":20330982,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ashes","dust","dust-templates","dustjs","python","templating"],"created_at":"2024-10-13T12:53:08.100Z","updated_at":"2025-03-17T05:31:59.276Z","avatar_url":"https://github.com/mahmoud.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Ashes\n=====\n\n[![Build Status](https://travis-ci.org/mahmoud/ashes.png?branch=master)](https://travis-ci.org/mahmoud/ashes)\n\u003ca href=\"https://ashes.readthedocs.io/en/latest/\"\u003e\u003cimg src=\"https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat\"\u003e\u003c/a\u003e\n\u003ca href=\"https://pypi.python.org/pypi/ashes\"\u003e\u003cimg src=\"https://img.shields.io/pypi/v/ashes.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"http://calver.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/calver-YY.MINOR.MICRO-22bfda.svg\"\u003e\u003c/a\u003e\n\n[Dust](http://akdubya.github.com/dustjs/) templating for Python 2 and 3.\nAlso the most convenient, portable, and powerful [command-line\ntemplating utility](#command-line-interface).\n\nA quick example:\n\n```python\n\u003e\u003e\u003e from ashes import AshesEnv\n\u003e\u003e\u003e ashes_env = AshesEnv()\n\n# Register/render from source\n\n\u003e\u003e\u003e ashes_env.register_source('hello', 'Hello, {name}!')\n\u003e\u003e\u003e ashes_env.render('hello', {'name': 'World'})\n'Hello, World!'\n\n# Or a file-based template (note: hella templates sold separately)\n\n\u003e\u003e\u003e ashes_env2 = AshesEnv(['./templates'])\n\u003e\u003e\u003e ashes_env2.render('hella.html', {'names': ['Kurt', 'Alex']})\n'Hella Kurt and Alex!'\n```\n\nThere's also built-in [bottle.py](http://bottlepy.org/docs/dev/)\nsupport, which works exactly like bottle's own `template()` function\nand `view()` decorator.\n\n```python\nfrom ashes import ashes_bottle_template as template\nfrom ashes import ashes_bottle_view as view\n\n@route('/')\ndef hello(name='World'):\n    return template('bottle_hello_template', name=name)\n\n@route('/dec')\n@view('bottle_hello_template')\ndef hello_dec(name='World'):\n    return {'name': name}\n\n# Use debug=True to disable template caching for easier dev\nrun(host='localhost', port=8080, reloader=True, debug=True)\n```\n\nIf you've read [bottle's template\ndocs](http://bottlepy.org/docs/dev/tutorial.html#templates), it'll be\neven dead-simpler, believe it or not.\n\nOne last tip, use the `keep_whitespace` flag to determine whether or\nnot to optimize away whitespace in the rendered template. It's a good\nidea to keep this disabled if you use JavaScript in your templated\nfiles, because occasionally a single-line comment (i.e., `// ...` can\nbreak your page.\n\n```python\nashes_env = AshesEnv(keep_whitespace=False)  # optimize away whitespace\n```\n\nFor more general information about the dust templating language, see\nthe [Dust documentation](http://akdubya.github.com/dustjs/).\n\n\n# Command-line interface\n\nThe ashes command-line interface serves two purposes. First, it makes\nit easy to experiment with and test ashes features and behavior,\nespecially thanks to the inline \"literal\" options, demonstrated\nbelow.\n\n```sh\n# using ashes to pretty-print JSON\n$ python ashes.py --no-filter -T '{.|ppjson}' -M '{\"x\": {\"y\": [1,2,3]}}'\n{\n  \"x\": {\n      \"y\": [\n        1,\n        2,\n        3,\n      ]\n   }\n}\n```\n\nSecondly, thanks to the compact, single-file implementation,\nashes can replace rusty sed and awk scripts, wherever Python 2.7-3.x\nis available. Use ashes for generating shell scripts and much more.\n\nTemplates can be files or passed at the command line. Models, the\ninput data to the template, are passed in as JSON, either as a command\nline option, or through stdin, enabling piping from web\nrequests. Several other options exist, see the help output below.\n\n```\n$ python ashes.py --help\nUsage: ashes.py [options]\n\nrender a template using a JSON input\n\nOptions:\n  --version             show program's version number and exit\n  -h, --help            show this help message and exit\n  --env-path=ENV_PATH   paths to search for templates, separate paths with :\n  --filter=FILTER       autoescape values with this filter, defaults to 'h'\n                        for HTML\n  --no-filter           disables default HTML-escaping filter, overrides\n                        --filter\n  --trim-whitespace     removes whitespace on template load\n  -m MODEL_PATH, --model=MODEL_PATH\n                        path to the JSON model file, default - for stdin\n  -M MODEL_LITERAL, --model-literal=MODEL_LITERAL\n                        the literal string of the JSON model, overrides model\n  -o OUTPUT_PATH, --output=OUTPUT_PATH\n                        path to the output file, default - for stdout\n  --output-encoding=OUTPUT_ENCODING\n                        encoding for the output, default utf-8\n  -t TEMPLATE_PATH, --template=TEMPLATE_PATH\n                        path of template to render, absolute or relative to\n                        env-path\n  -T TEMPLATE_LITERAL, --template-literal=TEMPLATE_LITERAL\n                        the literal string of the template, overrides template\n  --verbose=VERBOSE     emit extra output on stderr\n```\n\nOn systems with `ashes` installed, this interface is accessible\nthrough the `ashes` command.\n\n```\n$ ashes --trim-whitespace --no-filter --template script.sh.dust --model data.json --output script.sh\n```\n\n## Installation\n\nAshes is implemented as a single .py file, so installation can be as\neasy as downloading `ashes.py` above and putting it in the same\ndirectory as your project.\n\nAnd as always, `pip install ashes`. Installing the package has the\nadded benefit of installing the `ashes` command.\n\n## Testimonials\n\nAshes is currently used in production settings at PayPal and Rackspace.\n\nAdditionally, it is part of\n[the Clastic web framework](https://github.com/mahmoud/clastic) and\nused [in several Wikimedia-oriented projects](https://github.com/hatnote).\n\nIf your company or project uses Ashes, feel free to submit an issue or\npull request to get a link or shoutout added to this section.\n\n## Advanced Template Caching\n\nThe javascript implementation of Dust supports a form of template caching in\nwhich the templates are pre-generated into code objects and then cached.  This\nis necessary in javascript, because the templates would otherwise be loaded and\ncompiled on each pageview.\n\nMost Ashes users will not need to use Template Caching, but it is supported\nthrough hooks at several distinct stages:\n\n* caching/loading the template's AST\n* caching/loading the template's generated python function (as a string)\n* caching/loading the template's generated python function (as a code object)\n* caching/loading the template's generated python function (as a function)\n\nThere are many different benefits and concerns to caching templates at these\nstages.  To explain this, let's assume that Ashes is being used to render\ncontent that is correlated from 10 inter-dependent templates in a directory.\n\nThe standard way for rendering this would be to create a new Ashes \"Loader\" for\nthe directory and render it.  Ashes would then load and compile all 10 templates\nas needed and re-use them throughout the life of the application.  This works\nfor most situations, because the Ashes rendering Environment and Template\nLoader are usually created once and are persistent objects.\n\nThis is our \"Baseline\" rendering situation, as Ashes must perform all\nof the following steps -- which are the most expensive portions of templating:\n\n* LOAD the Template\n** Load the file\n* COMPILE the Template\n** Parse the contents into an AST\n** Convert the AST into a Python function string\n** Compile the Python string into a code object\n** Exec the code object into a function\n* RENDER the Template\n\nThe fastest part of Ashes is simply executing the template to render the\ncontent -- this is usually less than 5% of the overall work!\n\nIn certain situations, the Ashes Environment or Template Loaders can not be\npersistent.  This will happen if we have a lot of templates in a multi-tenant\napplication and need to constrain the size of our templating environment\n(like a LRU cache), or need to limit the environment/loader to a very short\nlifespan.  In these situations, hooking into Ashes to generate or load\n(partially) compiled templates is necessary t o overcome bottlenecks.\n\n* ``Template.to_ast``/``Template.from_ast``.  These methods of the ``Template``\nclass will allow templates to be cached in their native AST format.  On average,\nthis will save about 35% of Ashes overhead vs the baseline performance. Data in\nthis format is extremely safe to cache, because it is merely pre-processed.\n\n* ``Template.to_python_string``/``Template.from_python_string``.  These methods\nof the ``Template`` class will allow templates to be cached as the Python\nfunctions that Ashes generates.  These strings can be cached as-is.\nThis is an efficient way to cache the data - depending on the templates this will\nsave around 65-80 % of the overhead.\nNote: Data in this format is not necessarily safe to cache externally, because\nit will be compiled and run through `exec`.  If your cache is compromised,\narbitrary code can be executed.\n\n* ``Template.to_python_code``/``Template.from_python_code``.  These methods\nof the ``Template`` class will allow templates to be cached as the Python\ncode objects that Ashes generates.  Python code objects can be (de)serialized\nusing the `marshal` package in the standard library.  This is the most efficient\nway to cache the data - depending on the templates this will save around\n92-94% of the overhead.\nNote: Data in this format is not necessarily safe to cache externally, because\nit will be run through `exec`.  If your cache is compromised,\narbitrary code can be executed.\n\n* ``Template.to_python_func``/``Template.from_python_func``.  This allows you\nto pregenerate and cache (within a process) the python functions that Ashes\ngenerates for each template.  This is an unbelievably efficient way to cache\nthe data - Ashes will only be rendering the templates, saving around 95-98% of\nthe overhead from the baseline version.\n\n* ``ashes.python_string_to_code`` generates a python code object from an ashes\npython code string.\n\n* ``ashes.python_string_to_function`` generates a python function from an ashes\npython code string.\n\nA very easy way to implement this is with a custom TemplateLoader.  Template\nLoaders are a flexible framework that can be used to precompile families of\ntemplates or even lazily preload them as needed.\n\nIf a custom loader is not used, the template must be registered with the active\nashes environment:\n\n    ashesEnv = ashes.AshesEnv(loaders=(ashesLoader, ))\n\ttemplateObj = ashes.Template.from_python_code(source_python_code,\n\t\t\t\t\t\t\t\t\t\t\t\t  name='apples.dust',\n\t\t\t\t\t\t\t\t\t\t\t\t  )\n    ashesEnv.register(templateObj,\n                      name=\"apples.dust\",\n                      )\n\n### Recap\n\n```\n| method              | cacheable in process | cacheable external        | overhead |\n| ------------------- | -------------------- | ------------------------- | -------- |\n| baseline (standard) | -                    | -                         | 100%     |\n| ast                 | Yes                  | Safe                      | 65%      |\n| python string       | Yes                  | Possible Security Risk    | 20-35%   |\n| python code         | Yes                  | Same Risk, must `marshal` | 6-8%     |\n| python func         | Yes                  | No.                       | 3%       |\n```\n\n## Compatibility\n\nAshes has full support for every feature of the original Dust.js. Here's\nwhat the test suite says about all of the examples on Dust's documentation:\n\n```\n  . = passed, _ = skipped, X = failed, E = exception\n\n Dust.js site refs   tokenize   parse    optimize  compile    render\n---------------------------------------------------------------------\n                path    .         .         _         .         .\n               plain    .         .         _         .         .\n                zero    .         .         _         .         .\n           async_key    .         .         _         .         .\n          sync_chunk    .         .         _         .         .\n            sync_key    .         .         _         .         .\n              params    .         .         _         .         .\n     partial_context    .         .         _         .         .\n             escaped    .         .         _         .         .\n            implicit    .         .         _         .         .\n              filter    .         .         _         .         .\n         empty_array    .         .         _         .         .\n               array    .         .         _         .         .\n            partials    .         .         _         .         .\n               intro    .         .         _         .         .\n           recursion    .         .         _         .         .\n              object    .         .         _         .         .\n       force_current    .         .         _         .         .\n             replace    .         .         _         .         .\n          rename_key    .         .         _         .         .\n             context    .         .         _         .         .\n      async_iterator    .         .         _         .         .\n  interpolated_param    .         .         _         .         .\n            comments    .         .         _         .         .\n       escape_pragma    .         .         .         .         .\n       base_template    .         .         _         .         .\n          else_block    .         .         _         .         .\n      child_template    .         .         _         .         .\n         conditional    .         .         .         .         .\n---------------------------------------------------------------------\n          (29 total)    29        29        2         29        29\n\n```\n\n(NOTE: Optimization is fairly straightforward, so only two of the more\ncomplex examples are tested.)\n\n### A word on functions\n\nOf course, being a Python library, functions and callable items in the\ncontexts intended for server-side rendering must be written in\nPython. However, there are three reasons this is desirable:\n\n   1. Many context functions control some element of UI, such as\n   a transition or delay, which would just waste cycles if executed\n   server-side. (e.g., 'Intro' on the dust.js docs above)\n   2. One-off context functions should be extremely basic, or\n   3. Common, complex functions should be refactored into Helpers\n   and registered in the rendering environment for all templates\n   to use.\n\nAt the end of the day, though, remember that Dust is meant to be\ndata-driven, and if one finds oneself with highly complex functions in\nthe rendering contexts, one would do well to explore other templating\nsolutions.\n\n### Other notes on compatibility\n\n* Ashes uses a different parsing mechanism than dust, so on a few\nrare corner cases, like comments placed within string literals, the\ntemplate behavior might differ.\n\nAshes has been tested extensively on Python 2.7, as well as 2.6, 3.2,\n3.3, and PyPy.\n\n\n## Things to watch out for\n\n* Accidentally passing functions in as values to a\n  template. Dust/Ashes calls all functions referenced by the\n  template. If the function isn't of the right signature, you may see\n  a TypeError.\n\n* Leaving optimization enabled, especially when JavaScript is embedded\n  in the page. Dust-style optimization is meant for HTML/XML and is\n  nowhere near as smart as JavaScript/CSS minification suites. For\n  example, a mixed-mode page (HTML/JS/CSS all in one page) may appear\n  to work fine until the addition of a '//' single-line comment. When\n  Dust/Ashes turns this into a single line, the page from that point\n  on is treated as one long comment.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmahmoud%2Fashes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmahmoud%2Fashes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmahmoud%2Fashes/lists"}