copier icon indicating copy to clipboard operation
copier copied to clipboard

doc: add inheritance and composition section in user docs

Open francesco086 opened this issue 9 months ago β€’ 10 comments

Contributes to https://github.com/copier-org/copier/issues/934

francesco086 avatar May 23 '25 13:05 francesco086

Codecov Report

:white_check_mark: All modified and coverable lines are covered by tests. :white_check_mark: Project coverage is 97.90%. Comparing base (aad1f7d) to head (2a2116d). :warning: Report is 70 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2165      +/-   ##
==========================================
- Coverage   98.03%   97.90%   -0.14%     
==========================================
  Files          55       55              
  Lines        6005     6108     +103     
==========================================
+ Hits         5887     5980      +93     
- Misses        118      128      +10     
Flag Coverage Ξ”
unittests 97.90% <ΓΈ> (-0.14%) :arrow_down:

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

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

codecov[bot] avatar May 23 '25 13:05 codecov[bot]

Regarding solutions: Meta-templates is another one, although I don't know how relevant it actually is in practice.

Right! I think it is not really a solution for inheritance and composition tbh, but I added it to the list, and we will discuss it at due time

francesco086 avatar May 28 '25 17:05 francesco086

@sisp I am back from vacation and worked on the documentation last week and I just finished a demo of the approach I was suggesting using tasks and migrations.

You can find it here: https://github.com/francesco086/copier-inheritance-via-task-and-migration-demo-child

I will reference it in the documentation and keep moving with other approaches πŸ˜„

francesco086 avatar Aug 05 '25 13:08 francesco086

Thanks, @francesco086! I'll take a look next week, out of office myself at the moment. πŸ˜‰

sisp avatar Aug 05 '25 13:08 sisp

Thanks, @francesco086! I'll take a look next week, out of office myself at the moment. πŸ˜‰

Enjoy your time off!!!

francesco086 avatar Aug 05 '25 13:08 francesco086

Hi @sisp , I am coming to this after quite some long time.

I was trying to look at the alternative ideas, but to me they all look way more complicated and with relevant drawbacks compared to the one I already documented. I know I am biased, but to me it looks like the approach I proposed (and now refined) is the way to go.

Could you perhaps have a look at what I have done and let me know if I should still try to document different approaches?

francesco086 avatar Sep 09 '25 15:09 francesco086

Thanks for the reminder, @francesco086! :+1:

I definitely see some advantages of the task/migrations-based approach, especially the encapsulation of the parent template. But I struggle with the implementation of a simple scenario like this: Imagine you have a generic Python project template (parent) and a Python FastAPI template (child). Consider, e.g., the pyproject.toml.jinja file with PEP 621 project metadata where the parent template declares dependencies = [] (because it makes no assumptions about runtime dependencies) and the child template declares dependencies = ["fastapi[standard]"] (because it generates starter code for a FastAPI app). IIUC, the task/migrations-based approach can only exclude the pyproject.toml.jinja file from the parent template and redefine it in the child template although (for the sake of this example) the only difference is the one line about runtime dependencies. Am I right?

sisp avatar Sep 09 '25 19:09 sisp

Hey @sisp !

Yes, you are absolutely right.

One would make a copy-paste (or git sparse clone) from the parent, and modify it. The real problem would come with the parents upgrade (notice that since the parent version is pinned, this is under manual control). You would have to do the same procedure mentioned before. You could use diff tools to help yourself, but eventually it would be a manual process. Frankly I don't think one can do much better. In these scenarios usually git gives you conflicts to resolve manually. But I may be mistaken.

In general, I think one should try to avoid the overwriting of a file, so it should be limited to as few files as possible. For example, in the use case you mentioned, one could use dynamic dependencies and only overwrite a requirements file (of course this requires being able to influence the parent template, which is not always granted)

[project]
dynamic = ["dependencies"]

[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }

This to say that I think that this drawback has not such strong impact.

francesco086 avatar Sep 10 '25 06:09 francesco086

Thanks for confirming. This means, the limitations of task/migrations-based template inheritance impact design decisions of the generated project. :thinking: The dependencies example is only one among many. For example, a generic Python project template may have a basic pytest configuration, and a FastAPI template may extend the starter to include some E2E tests setup which requires additions in pyproject.toml.jinja, e.g.:

 [tool.pytest.ini_options]
 ...
-addopts = "-n auto"
+addopts = "-n auto --strict-markers -m 'not e2e'"
+markers = ["e2e: mark tests as E2E tests"]
 ...

It feels like these kinds of surgical edits are unpredictable.

Of course, I'm biased towards the "fork"-based approach, but it does support these scenarios – at the cost of a fully materialized parent template and a need to sync the "fork" with its upstream template. We're exploring this approach internally, so far it's looking fine, but we'll need to gain more experience over time.

That said, the task/migrations-based approach may work just fine with less complexity for some template hierarchies that don't need surgical edits.

sisp avatar Sep 10 '25 07:09 sisp

Do you have a reference of this fork-like approach for me to document it? It could be my next method to document.

I will also add this limitation you pointed out to the cons.

francesco086 avatar Sep 10 '25 08:09 francesco086