Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(context): expose a _phase context variable (fix #1883) #1948

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

noirbizarre
Copy link
Contributor

This PR exposes a _phase variable which can be one of prompt, tasks, mmigrate and render.
The known phases are grouped into the copier.types.Phase enum.

This implements #1883 and allows fixing copier-org/copier-templates-extensions#7

Copy link

codecov bot commented Jan 26, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 97.91%. Comparing base (71358ed) to head (17efb40).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1948      +/-   ##
==========================================
- Coverage   98.05%   97.91%   -0.15%     
==========================================
  Files          53       53              
  Lines        5511     5551      +40     
==========================================
+ Hits         5404     5435      +31     
- Misses        107      116       +9     
Flag Coverage Δ
unittests 97.91% <100.00%> (-0.15%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

tests/test_migrations.py Outdated Show resolved Hide resolved
@pawamoy
Copy link
Contributor

pawamoy commented Jan 27, 2025

Looking super good to me 😄 Thank you for the awesome PRs @noirbizarre!

@noirbizarre noirbizarre force-pushed the feat/phase-var branch 2 times, most recently from 3d6dbd8 to dc3071d Compare January 27, 2025 15:15
@noirbizarre
Copy link
Contributor Author

PR updated with the typo fixes.

You're welcome 🙏🏼 It benefits everybody 👌🏼

Copy link
Member

@sisp sisp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic work again, @noirbizarre! 🙏

Just a minor stylistic request: It seems Python enums should be upper-case12. I suggest following this guideline.

Footnotes

  1. https://mail.python.org/pipermail/python-ideas/2016-September/042340.html

  2. https://docs.python.org/3/library/enum.html

@noirbizarre noirbizarre force-pushed the feat/phase-var branch 3 times, most recently from 317a8ed to 41bef6c Compare January 27, 2025 21:20
@noirbizarre
Copy link
Contributor Author

noirbizarre commented Jan 27, 2025

Updated to upper case 👍🏼

I, personally, tend to do the opposite with StrEnum and I explicitly chose lower case (and use this convention on all my projects). There is actually no recommendation, the enum PEP itself is all lower case, and StrEnum auto() values are lower cases too. I think having name==value for StrEnum is easier to handle, but I believe it is a personal taste (as highlighted in the thread following Guido's mail)

Copy link
Member

@sisp sisp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems you're right, there isn't a definitive style rule about enums. 🫣 Thanks for elaborating on this topic (especially the detailed message before you edited it 😉)! Well, now it's upper-case. Shall we leave it like this?

Just a final thought: Shall we expose the phase via the _phase variable (as it is now) or would it be better to rename/move it? E.g.:

  • _copier_phase – only _folder_name, now, and make_secret seems to not have the _copier prefix where now and make_secret are both deprecated.
  • _copier_conf.phase – or should we only have static values under _copier_conf?

@noirbizarre @copier-org/maintainers Any opinions?

@pawamoy
Copy link
Contributor

pawamoy commented Jan 28, 2025

I tend to use lowercase too, except when the enum contains names that are reserved keywords like class! No other choice than going uppercase then. As long as we're consistent within Copier's code base, I'm fine with both.

I'm accessing the phase with parent["_copier_conf"]["_phase"] in copier-org/copier-templates-extensions#8, it would feel a bit redundant as _copier_phase, but again, consistency is more important IMO.

@sisp
Copy link
Member

sisp commented Jan 28, 2025

@pawamoy So, would you tend to be in favor of _copier_phase rather than _phase?

@pawamoy

This comment was marked as outdated.

@pawamoy
Copy link
Contributor

pawamoy commented Jan 28, 2025

Yes, _copier_phase seems good. Do note that we don't strictly need it in _render_context though, see next comment. We also don't need it in render_value, unless some use-cases are shown where a _copier_phase variable whose value is always "prompt" would be used in copier.yml values.

@pawamoy
Copy link
Contributor

pawamoy commented Jan 28, 2025

Here's my final comprehension of the issue (which may be worth adding as documentation or code comments at least).

  • During copier copy, and prompt rendering, context extensions do not get _copier_conf. We can use its absence to decide not to run the context hook.
  • During copier update however, and prompt rendering, context extensions do get _copier_conf. We can't use its absence anymore. Instead, we need the phase. It's passed through parent["_copier_conf"]["phase"]: we don't strictly need the additional top-level "_phase" key. We could still keep it to provide more consistency: a _phase / _copier_phase top-level key always present. No strong opinions. I do notice occurrences where parent["_phase"] is unset when testing locally, so if we want this consistency, there are more places this variable must be added.
  • Extensions can now also skip running the context hook during tasks and migrations, thanks to the phase.

@noirbizarre
Copy link
Contributor Author

noirbizarre commented Jan 28, 2025

Oh, I didn't though about all that 😅
Prefixing by _copier makes sense and is consistent with existing ones.
I would go for a root _copier_phase as it is not a configuration, just a runtime state.
I added it in _render_context just to say that it is always available. I don't like to presume future evolution as well as community usage, so I've been on the dumb "it's the current state, and it's always available" approach.

Note that I actually use context extensions during prompt on my projects, and actually this PR can maybe help to have a more general approach on this and assume that extensions can run at any phase. (This might later lead to having copier-template-extensions TemplateExtensionLoader built-in 🤷🏼‍♂️)

Anyway, PR update with _copier_phase naming.

Copy link
Member

@sisp sisp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thought about this PR some more and also about – what @pawamoy noticed – the phase value being automatically exposed as _copier_conf.phase because most Worker dataclass attributes are automatically included in _copier_conf. As

I would go for a root _copier_phase as it is not a configuration, just a runtime state.

makes sense to me, I think the phase value should not be accessible under _copier_conf though. This PR also reminds me of a similar situation we've had in #1733 where initially a dataclass attribute was used as well and I suggested to use a context variable instead. Now, it feels odd to me to use a dataclass attribute here to manage the phase value because it has no meaning while not rendering, prompting or executing tasks/migrations. How about applying an approach similar to #1733 (review) in this PR as well?

Slightly off-topic: I just realized that #1733 exposes a top-level _operation Jinja variable; we might want to rename it to _copier_operation there for consistency.

@pawamoy
Copy link
Contributor

pawamoy commented Jan 29, 2025

I agree with all of @sisp's points 🙂

Regarding _copier_conf, the code says "backwards compatibility, remove it?":

copier/copier/main.py

Lines 334 to 339 in 57439e5

def _render_context(self) -> Mapping[str, Any]:
"""Produce render context for Jinja."""
# Backwards compatibility
# FIXME Remove it?
conf = asdict(self)
conf.pop("_cleanup_hooks")

Not sure what it means exactly, but it seems like we wanted to stray away from this anyway.

@sisp
Copy link
Member

sisp commented Jan 29, 2025

@pawamoy This comment pre-dates my involvement with Copier, but I don't think the (raw) Worker dataclass attributes are very useful in the Jinja context; useful config values are explicitly added. I'd be in favor of dropping those lines as part of the next major release.

@pawamoy
Copy link
Contributor

pawamoy commented Jan 29, 2025

Fine by me. I'd suggest making sure _copier_phase is always passed. It's currently not passed from the answers_relpath property, which calls template.render(**self.answers.combined) (template being the answers file name). Looks like it's the only missing location 🙂

@noirbizarre
Copy link
Contributor Author

noirbizarre commented Jan 29, 2025

PR updated with answers_relpath handled 👍🏼

I saw the deprecation noticed, but it is used by all rendering except those for the prompts.

I'd be in favor of dropping those lines as part of the next major release.

Agreed, but I would go even further (not in this PR to keep it scoped), I would isolate rendering in a single class/module/function/whatever, so future modification of this kind occurs in a single place. It would be easier to maintain and evolve, and easier to test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants