{"id":20516453,"url":"https://github.com/copier-org/copier-templates-extensions","last_synced_at":"2025-04-14T00:35:53.901Z","repository":{"id":53136321,"uuid":"351178770","full_name":"copier-org/copier-templates-extensions","owner":"copier-org","description":"Special Jinja2 extension for Copier that allows to load extensions using file paths relative to the template root instead of Python dotted paths.","archived":false,"fork":false,"pushed_at":"2025-03-10T13:06:04.000Z","size":968,"stargazers_count":23,"open_issues_count":3,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-27T14:51:52.956Z","etag":null,"topics":["copier","copier-extension","jinja2-extension"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/copier-org.png","metadata":{"funding":{"github":["Yajo"]},"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2021-03-24T18:09:00.000Z","updated_at":"2025-03-10T13:06:07.000Z","dependencies_parsed_at":"2025-03-03T01:18:48.876Z","dependency_job_id":"e68a4d8e-185f-4288-9ea7-ccc0dd139477","html_url":"https://github.com/copier-org/copier-templates-extensions","commit_stats":null,"previous_names":["pawamoy/copier-templates-extensions"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/copier-org%2Fcopier-templates-extensions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/copier-org%2Fcopier-templates-extensions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/copier-org%2Fcopier-templates-extensions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/copier-org%2Fcopier-templates-extensions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/copier-org","download_url":"https://codeload.github.com/copier-org/copier-templates-extensions/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248802607,"owners_count":21163881,"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":["copier","copier-extension","jinja2-extension"],"created_at":"2024-11-15T21:28:59.075Z","updated_at":"2025-04-14T00:35:53.891Z","avatar_url":"https://github.com/copier-org.png","language":"Python","funding_links":["https://github.com/sponsors/Yajo"],"categories":[],"sub_categories":[],"readme":"# Copier Templates Extensions\n\n[![ci](https://github.com/copier-org/copier-templates-extensions/workflows/ci/badge.svg)](https://github.com/copier-org/copier-templates-extensions/actions?query=workflow%3Aci)\n[![documentation](https://img.shields.io/badge/docs-mkdocs-708FCC.svg?style=flat)](https://copier-org.github.io/copier-templates-extensions/)\n[![pypi version](https://img.shields.io/pypi/v/copier-templates-extensions.svg)](https://pypi.org/project/copier-templates-extensions/)\n\nSpecial Jinja2 extension for Copier that allows to load extensions using file paths relative to the template root instead of Python dotted paths.\n\n## Installation\n\nWith pip:\n\n```bash\npip install copier-templates-extensions\n```\n\nWith uv:\n\n```bash\nuv tool install copier --with copier-templates-extensions\n```\n\nWith pipx:\n\n```bash\npipx install copier\npipx inject copier copier-templates-extensions\n```\n\n## Usage\n\nIn your template configuration,\nfirst add our loader extension,\nthen add your templates extensions\nusing relative file paths,\nand the class name after a colon:\n\n```yaml\n_jinja_extensions:\n- copier_templates_extensions.TemplateExtensionLoader\n- extensions/context.py:ContextUpdater\n- extensions/slugify.py:SlugifyExtension\n```\n\nWith this example, you are supposed to have an `extensions`\ndirectory at the root of your template containing two modules:\n`context.py` and `slugify.py`.\n\n```\n📁 template_root\n├── 📄 abc.txt.jinja\n├── 📄 copier.yml\n└── 📁 extensions\n    ├── 📄 context.py\n    └── 📄 slugify.py\n```\n\nSee [Context hook extension](#context-hook-extension)\nto see how the `ContextUpdater` class can be written.\n\nThe `SlugifyExtension` class could be written like this:\n\n```python\nimport re\nimport unicodedata\n\nfrom jinja2.ext import Extension\n\n\n# taken from Django\n# https://github.com/django/django/blob/main/django/utils/text.py\ndef slugify(value, allow_unicode=False):\n    \"\"\"\n    Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated\n    dashes to single dashes. Remove characters that aren't alphanumerics,\n    underscores, or hyphens. Convert to lowercase. Also strip leading and\n    trailing whitespace, dashes, and underscores.\n    \"\"\"\n    value = str(value)\n    if allow_unicode:\n        value = unicodedata.normalize('NFKC', value)\n    else:\n        value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')\n    value = re.sub(r'[^\\w\\s-]', '', value.lower())\n    return re.sub(r'[-\\s]+', '-', value).strip('-_')\n\n\nclass SlugifyExtension(Extension):\n    def __init__(self, environment):\n        super().__init__(environment)\n        environment.filters[\"slugify\"] = slugify\n```\n\n### Context hook extension\n\nThis package also provides a convenient extension class\nallowing template writers to update the context used\nto render templates, in order to add, modify or remove\nitems of the context.\n\nIn one of your relative path extensions modules,\ncreate a class that inherits from `ContextHook`,\nand override its `hook` method:\n\n```python\nfrom copier_templates_extensions import ContextHook\n\n\nclass ContextUpdater(ContextHook):\n    def hook(self, context):\n        context[\"say\"] = \"hello \" + context[\"name\"]\n```\n\nIn your Jinja templates, you will now have access\nto the `{{ say }}` variable directly.\n\nThis can be extremely useful in template projects\nwhere you don't want to ask too many questions to the users\nand instead infer some values from their answers.\n\nConsider the following example:\nyou ask your users if they want to generate\na CLI app or a web API.\nDepending on their answer,\nthe main Python module should be named\n`cli.py` or `app.py`.\n\nWithout the context hook,\nyou would need to write a Jinja macro somewhere,\nor update the context directly in Jinja,\nand import this file (still using Jinja)\n*in the filename of the module*:\n\n```jinja\n{# using macros #}\n{%- macro module_name() %}\n  {%- if project_type == \"webapi\" %}app{% else %}cli{% endif %}\n{%- endmacro %}\n```\n\n```jinja\n{# or enhancing the context #}\n{#- Initiate context with a copy of Copier answers -#}\n{%- set ctx = _copier_answers.copy() -%}\n\n{#- Populate our new variables -#}\n{%- set _ = ctx.update({\"module_name\": (\"app\" if project_type == \"webapi\" else \"cli\") -%}\n```\n\n```\n📁 template_root\n├── 📄 copier.yml\n├── 📄 macros      # the macros file\n├── 📄 context     # the context file\n├── 📁 extensions\n│   └── 📄 slugify.py\n└── 📁 {{project_name|slugify}}\n    │\n    │   # using the macros\n    ├── 📄 {% import 'macros' as macros with context %}{{macros.module_name()}}.py.jinja\n    │\n    │   # or using the enhanced context\n    └── 📄 {% from 'context' import ctx with context %}{{ctx.module_name}}.py.jinja\n```\n\nAs you can see, both forms are really ugly to write:\n\n- the `macros` or `context` can only be placed in the root,\n  as slashes `/` are not allowed in filenames\n- you must use spaces and single-quotes\n  (double-quotes are not valid filename characters on Windows)\n  in your templated filenames, which is not clean\n- filenames are very long\n\n**Using our context hook instead makes it so easy and clean!**\n\n```python\nfrom copier_templates_extensions import ContextHook\n\n\nclass ContextUpdater(ContextHook):\n    def hook(self, context):\n        context[\"module_name\"] = \"app\" if context[\"project_type\"] == \"webapi\" else \"cli\"\n```\n\n```\n📁 template_root\n├── 📄 copier.yml\n├── 📁 extensions\n│   ├── 📄 slugify.py\n│   └── 📄 context.py\n└── 📁 {{project_name|slugify}}\n    └── 📄 {{module_name}}.py.jinja\n```\n\nYou can do many more things with a context hook,\nlike downloading data from external resources\nto include it in the context, etc.\n\n\u003e [!TIP]\n\u003e **Context hooks run during every Copier rendering phase.**\n\u003e During project generation or project updates, Copier passes\n\u003e through several rendering phases: when prompting (questions / answers),\n\u003e when rendering files, when running tasks, and when running migrations.\n\u003e\n\u003e By default, a context hook runs during all these phases,\n\u003e possibly multiple times, for example once per prompted question,\n\u003e or once per rendered file. The task of running the hook only once,\n\u003e or during a specific phase only, is left to you.\n\u003e\n\u003e To run only once, you can use caching within your class\n\u003e (for example by storing computed values in class variables).\n\u003e\n\u003e To run during a specific phase only, you can check the value\n\u003e of `context[\"_copier_phase\"]` (Copier 9.6+), which is one of:\n\u003e `\"prompt\"`, `\"render\"`, `\"tasks\"`, `\"migrate\"`.\n\u003e\n\u003e Other key-value pairs can be found in the context\n\u003e that you might find useful (Copier configuration, etc.).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcopier-org%2Fcopier-templates-extensions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcopier-org%2Fcopier-templates-extensions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcopier-org%2Fcopier-templates-extensions/lists"}