copier icon indicating copy to clipboard operation
copier copied to clipboard

Allow imports in inline templates

Open yajo opened this issue 3 years ago • 0 comments

Is your feature request related to a problem? Please describe.

Copier users keep requesting variables support. There are good reasons to avoid supporting variables:

However, it's easy to understand that people still need to keep their templates DRY.

The real problem is not within the template files. As said, Jinja is DRY enough out of the box. The real problem lies within 2 other spaces:

  1. Inside copier.yaml. Here, keys are not rendered but values are.
  2. Templated file and directory names.

These are not files, so they get rendered in-line: https://github.com/copier-org/copier/blob/0f5257a0b01fde57ad69a2d51cb20eec1f9e6025/copier/user_data.py#L389

Describe the solution you'd like

I really don't want to create a new system for declaring variables or importing code when Jinja already has them. I just want to be able to use them in in-line templates.

This way, I could declare a copier.yaml like this:

_exclude:
  - name-slug

name:
  type: str
  default: A nice human-readable name

slug:
  type: str
  default: '{% include "name-slug.jinja" %}'

Also, I could have a template structure like this:

📄 copier.yaml
📄 name-slug.jinja
🐍  {% include "name-slug.jinja" %}.py

Apart from freeing Copier devs from reinventing some wheels, this would empower Copier templaters to not only add variables, but rather do a lot more stuff. Also it would make them more used to get reusable knowledge on the Jinja engine, rather than on a nic~~h~~e use case such as Copier.

Describe how to implement it

Quoting from Jinja docs:

Using a template loader rather than passing strings to Template or Environment.from_string() has multiple advantages. Besides being a lot easier to use it also enables template inheritance.

In other words, you can't import or include from an in-line template (or without a loader).

However, in our in-line templates, we do have a filesystem location context. So, the solution is relatively simple:

  1. Write a custom loader that tells Jinja that context.
  2. Let Copier know which loader to use, depending on where the rendered value comes from (is it a file? a path? or a copier.yml value?). This would mean possibly using one of PrefixLoader or ChoiceLoader.

Describe alternatives you've considered

See other user requests. All of them contain workarounds:

  • #229
  • #545
  • #629
  • #678
  • #700
  • #716

Additional context

Cookiecutter has private variables.

yajo avatar Jul 16 '22 06:07 yajo