Skip to main content

Copier Template-Extensions

ci documentation pypi version

Special Jinja2 extension for Copier that allows to load extensions using file paths relative to the template root instead of Python dotted paths.

Installation

With pip:

pip install copier-template-extensions

With uv:

uv tool install copier --with copier-template-extensions

With pipx:

pipx install copier
pipx inject copier copier-template-extensions

Usage

In your template configuration, first add our loader extension, then add your templates extensions using relative file paths, and the class name after a colon:

_jinja_extensions:
- copier_template_extensions.TemplateExtensionLoader
- extensions/context.py:ContextUpdater
- extensions/slugify.py:SlugifyExtension

With this example, you are supposed to have an extensions directory at the root of your template containing two modules: context.py and slugify.py.

📁 template_root
├── 📄 abc.txt.jinja
├── 📄 copier.yml
└── 📁 extensions
    ├── 📄 context.py
    └── 📄 slugify.py

See Context hook extension to see how the ContextUpdater class can be written.

The SlugifyExtension class could be written like this:

import re
import unicodedata

from jinja2.ext import Extension


# taken from Django
# https://github.com/django/django/blob/main/django/utils/text.py
def slugify(value, allow_unicode=False):
    """
    Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated
    dashes to single dashes. Remove characters that aren't alphanumerics,
    underscores, or hyphens. Convert to lowercase. Also strip leading and
    trailing whitespace, dashes, and underscores.
    """
    value = str(value)
    if allow_unicode:
        value = unicodedata.normalize('NFKC', value)
    else:
        value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
    value = re.sub(r'[^\w\s-]', '', value.lower())
    return re.sub(r'[-\s]+', '-', value).strip('-_')


class SlugifyExtension(Extension):
    def __init__(self, environment):
        super().__init__(environment)
        environment.filters["slugify"] = slugify

Context hook extension

This package also provides a convenient extension class allowing template writers to update the context used to render templates, in order to add, modify or remove items of the context.

In one of your relative path extensions modules, create a class that inherits from ContextHook, and override its hook method:

from copier_template_extensions import ContextHook


class ContextUpdater(ContextHook):
    def hook(self, context):
        context["say"] = "hello " + context["name"]

In your Jinja templates, you will now have access to the {{ say }} variable directly.

This can be extremely useful in template projects where you don't want to ask too many questions to the users and instead infer some values from their answers.

Consider the following example: you ask your users if they want to generate a CLI app or a web API. Depending on their answer, the main Python module should be named cli.py or app.py.

Without the context hook, you would need to write a Jinja macro somewhere, or update the context directly in Jinja, and import this file (still using Jinja) in the filename of the module:

{# using macros #}
{%- macro module_name() %}
  {%- if project_type == "webapi" %}app{% else %}cli{% endif %}
{%- endmacro %}
{# or enhancing the context #}
{#- Initiate context with a copy of Copier answers -#}
{%- set ctx = _copier_answers.copy() -%}

{#- Populate our new variables -#}
{%- set _ = ctx.update({"module_name": ("app" if project_type == "webapi" else "cli") -%}
📁 template_root
├── 📄 copier.yml
├── 📄 macros      # the macros file
├── 📄 context     # the context file
├── 📁 extensions
│   └── 📄 slugify.py
└── 📁 {{project_name|slugify}}
    │
    │   # using the macros
    ├── 📄 {% import 'macros' as macros with context %}{{macros.module_name()}}.py.jinja
    │
    │   # or using the enhanced context
    └── 📄 {% from 'context' import ctx with context %}{{ctx.module_name}}.py.jinja

As you can see, both forms are really ugly to write:

  • the macros or context can only be placed in the root, as slashes / are not allowed in filenames
  • you must use spaces and single-quotes (double-quotes are not valid filename characters on Windows) in your templated filenames, which is not clean
  • filenames are very long

Using our context hook instead makes it so easy and clean!

from copier_template_extensions import ContextHook


class ContextUpdater(ContextHook):
    def hook(self, context):
        context["module_name"] = "app" if context["project_type"] == "webapi" else "cli"
📁 template_root
├── 📄 copier.yml
├── 📁 extensions
│   ├── 📄 slugify.py
│   └── 📄 context.py
└── 📁 {{project_name|slugify}}
    └── 📄 {{module_name}}.py.jinja

You can do many more things with a context hook, like downloading data from external resources to include it in the context, etc.

Sponsors

Silver sponsors

FastAPI

Bronze sponsors

Nixtla


ofek samuelcolvin tlambert03 ssbarnea femtomc cmarqu kolenaIO ramnes machow BenHammersley trevorWieland MarcoGorelli analog-cbarber OdinManiac rstudio-sponsorship schlich butterlyn livingbio NemetschekAllplan EricJayHartman 15r10nk activeloopai roboflow cmclaughlin RapidataAI rodolphebarbanneau theSymbolSyndicate blakeNaccarato ChargeStorm Cusp-AI

And 4 more private sponsor(s).

Metadata

Release files for copier-template-extensions 0.3.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for copier-template-extensions 0.3.4
File Size Uploaded
copier_template_extensions-0.3.4.tar.gz 40.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for copier-template-extensions 0.3.4
File Interpreter ABI Platform
copier_template_extensions-0.3.4-py3-none-any.whl Python 3 none any Details

Total release size: 55.1 kB

Release files / copier_template_extensions-0.3.4.tar.gz

Download URL copier_template_extensions-0.3.4.tar.gz
Size 40.5 kB
Tags Source
SHA-256 checksum
How to use checksums
ec9d2c50c6d6cdd54f11d1bedffcc01ca40cf74cc3035e171cb4d3fef38bb3ea
BLAKE2b-256 checksum
How to use checksums
acf55a67301e6da8729c06b583de290c9840d8c89d6cddd3b47a347bef2a767a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / copier_template_extensions-0.3.4-py3-none-any.whl

Download URL copier_template_extensions-0.3.4-py3-none-any.whl
Size 14.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
81c66adf2dbf77bedf8caf86d4494548b294671cdb276f6370eb111f40f38b39
BLAKE2b-256 checksum
How to use checksums
6772058b8db6813d6390daddfa5ae7684e7b6bdb044bc0c1e1f0da2d0c3bacd0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release history Release notifications | RSS feed

This release

0.3.4 This release

2 release files

0.3.3

2 release files

0.3.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page