Open
Description
Describe the problem
The changes introduced in #2023 seem to lead to additional jinja rendering - which are also triggering copier_templates_extensions.ContextHook
. In my particular case this lead to v.9.7.x breaking my setup because my hook did not distinguish between calls from different phases.
extensions/context_hooks.py
from copier_templates_extensions import ContextHook
class ContextUpdater(ContextHook):
def hook(self, context: dict) -> dict:
context["hello_world"] = context["hello"] + ", " + context["world"] + '!'
return context
copier.yml
_subdirectory: template
_preserve_symlinks: True
_min_copier_version: "9.0.1"
_jinja_extensions:
- copier_templates_extensions.TemplateExtensionLoader
- extensions/context_hooks.py:ContextUpdater
hello:
type: str
help: "Say hello, please!"
default: "Hello"
world:
when: false
default: "world"
The behavior under <9.7.x can be restored by
from copier import Phase
from copier_templates_extensions import ContextHook
class ContextUpdater(ContextHook):
def hook(self, context: dict) -> dict:
if context['_copier_phase'] == Phase.RENDER:
context["hello_world"] = context["hello"] + ", " + context["world"] + '!'
return context
... but it seems a bit of a breaking change which the docs also do not seem to cover yet: https://copier.readthedocs.io/en/stable/faq/#how-can-i-alter-the-context-before-rendering-the-project
Template
Not relevant for this one :)
To Reproduce
copier copy --trust . /tmp/foo
Traceback (most recent call last):
File "/Users/christianroth/repos/copier-repro/.pixi/envs/default/lib/python3.13/runpy.py", line 198, in _run_module_as_main
return _run_code(code, main_globals, None,
"__main__", mod_spec)
File "/Users/christianroth/repos/copier-repro/.pixi/envs/default/lib/python3.13/runpy.py", line 88, in _run_code
exec(code, run_globals)
~~~~^^^^^^^^^^^^^^^^^^^
File "/Users/christianroth/.vscode/extensions/ms-python.debugpy-2025.6.0-darwin-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 71, in <module>
cli.main()
~~~~~~~~^^
File "/Users/christianroth/.vscode/extensions/ms-python.debugpy-2025.6.0-darwin-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 501, in main
run()
~~~^^
File "/Users/christianroth/.vscode/extensions/ms-python.debugpy-2025.6.0-darwin-arm64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 384, in run_module
run_module_as_main(options.target, alter_argv=True)
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/christianroth/.vscode/extensions/ms-python.debugpy-2025.6.0-darwin-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 228, in _run_module_as_main
return _run_code(code, main_globals, None, "__main__", mod_spec)
File "/Users/christianroth/.vscode/extensions/ms-python.debugpy-2025.6.0-darwin-arm64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 118, in _run_code
exec(code, run_globals)
~~~~^^^^^^^^^^^^^^^^^^^
File "/Users/christianroth/repos/copier/copier/__main__.py", line 6, in <module>
CopierApp.run()
~~~~~~~~~~~~~^^
File "/Users/christianroth/repos/copier-repro/.pixi/envs/default/lib/python3.13/site-packages/plumbum/cli/application.py", line 640, in run
inst, retcode = subapp.run(argv, exit=False)
~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/Users/christianroth/repos/copier-repro/.pixi/envs/default/lib/python3.13/site-packages/plumbum/cli/application.py", line 635, in run
retcode = inst.main(*tailargs)
File "/Users/christianroth/repos/copier/copier/_cli.py", line 282, in main
return _handle_exceptions(inner)
File "/Users/christianroth/repos/copier/copier/_cli.py", line 71, in _handle_exceptions
method()
~~~~~~^^
File "/Users/christianroth/repos/copier/copier/_cli.py", line 273, in inner
with self._worker(
~~~~~~~~~~~~^
template_src,
^^^^^^^^^^^^^
...<3 lines>...
overwrite=self.force or self.overwrite,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
) as worker:
^
File "/Users/christianroth/repos/copier/copier/_main.py", line 267, in __exit__
raise value
File "/Users/christianroth/repos/copier/copier/_cli.py", line 280, in inner
worker.run_copy()
~~~~~~~~~~~~~~~^^
File "/Users/christianroth/repos/copier/copier/_main.py", line 94, in _wrapper
return func(*args, **kwargs)
File "/Users/christianroth/repos/copier/copier/_main.py", line 1015, in run_copy
self._ask()
~~~~~~~~~^^
File "/Users/christianroth/repos/copier/copier/_main.py", line 601, in _ask
[question.get_questionary_structure()],
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/Users/christianroth/repos/copier/copier/_user_data.py", line 390, in get_questionary_structure
"message": self.get_message(),
~~~~~~~~~~~~~~~~^^
File "/Users/christianroth/repos/copier/copier/_user_data.py", line 365, in get_message
if rendered_help := self.render_value(self.help):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/Users/christianroth/repos/copier/copier/_user_data.py", line 472, in render_value
return template.render({**self.context, **(extra_answers or {})})
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/christianroth/repos/copier-repro/.pixi/envs/default/lib/python3.13/site-packages/jinja2/environment.py", line 1290, in render
ctx = self.new_context(dict(*args, **kwargs))
File "/Users/christianroth/repos/copier-repro/.pixi/envs/default/lib/python3.13/site-packages/jinja2/environment.py", line 1388, in new_context
return new_context(
self.environment, self.name, self.blocks, vars, shared, self.globals, locals
)
File "/Users/christianroth/repos/copier-repro/.pixi/envs/default/lib/python3.13/site-packages/jinja2/runtime.py", line 117, in new_context
return environment.context_class(
~~~~~~~~~~~~~~~~~~~~~~~~~^
environment, parent, template_name, blocks, globals=globals
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/Users/christianroth/repos/copier-repro/.pixi/envs/default/lib/python3.13/site-packages/copier_templates_extensions/extensions/context.py", line 23, in __init__
parent.update(extension_self.hook(parent))
~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "/Users/christianroth/repos/copier-repro/extensions/context_hooks.py", line 8, in hook
context["hello_world"] = context["hello"] + ", " + context["world"] + '!'
~~~~~~~^^^^^^^^^
KeyError: 'hello'
Logs
Expected behavior
Screenshots/screencasts/logs
No response
Operating system
macOS
Operating system distribution and version
15.4.1
Copier version
copier 9.7.2.dev21+g711b32b
Python version
3.13
Installation method
pip+git
Additional context
No response