Provider Patterns¶
This document describes recommended patterns for writing Repolish providers that
are maintainable, user-friendly, and work well with the context_overrides
feature.
Namespaced Context Pattern¶
Providers should organize their context contributions under a namespaced key to avoid conflicts and make overrides easier.
Problem¶
Without namespacing, providers might contribute flat or deeply nested keys that are hard to override:
Users wanting to change just the first devkit name must copy the entire structure:
Solution¶
Namespace provider contexts under a provider-specific key:
Now users can override specific values surgically:
Alternatively, for convenience, you can use nested dictionary syntax:
Both syntaxes are equivalent - the nested form is automatically flattened to dot-notation internally.
Template Usage¶
In Jinja templates, use {% set %} to create local variables for cleaner code:
{# Instead of: {{ cookiecutter.my_provider.devkits.0.name }} #}
{%- set prov = cookiecutter.my_provider -%}
{{ prov.devkits.0.name }}
This makes templates more readable and maintainable.
Provider Inputs¶
For providers that need user input to generate context, use an *_args or
*_config key:
This input affects context generation but typically doesn't appear in templates.
The provider's create_context() can read these values:
def create_context(ctx):
args = ctx.get('my_provider_args', {})
api_version = args.get('api_version', 'v1')
# Generate context based on inputs...
return {
'my_provider': {
'api_url': f'https://api.example.com/{api_version}',
# ...
}
}
Users set inputs in the context section (not via overrides, since inputs are read before overrides are applied):
Why Namespacing Matters¶
Without namespacing, complex data structures in the root context can cause issues with Cookiecutter's option handling. For example:
# Problematic - Cookiecutter treats arrays as multiple choice options
context:
names: ['a', 'b', 'c'] # Becomes CLI prompt options, not an array
Cookiecutter's CLI treats arrays as multiple choice options, so
cookiecutter.names would prompt the user to choose one value instead of being
the full array. Providers work around this by double-wrapping:
# Workaround - ugly and confusing
context:
names: [['a', 'b', 'c']] # cookiecutter.names[0] gives the array
With namespacing, this issue disappears entirely:
Note: Repolish now supports native Jinja rendering (opt‑in via
no_cookiecutter: true) which removes many Cookiecutter-specific edge cases
(e.g. array/option handling) and enables per-file extra context via
tuple-valued file_mappings. Prefer using namespaced, top-level context in
templates and migrate away from direct cookiecutter.* references when
convenient — the cookiecutter namespace is still available during migration.
Benefits¶
- Maintainability: Clear separation of provider concerns
- User Experience: Easy to override specific settings
- Readability: Templates can use local variables
- Flexibility: Providers can accept inputs without cluttering templates
- Safety: Namespaced keys prevent accidental overrides between providers
- Cookiecutter Compatibility: Avoids option handling issues with arrays and complex data structures
Example Provider¶
# my_provider/repolish.py
def create_context(ctx):
args = ctx.get('my_provider_args', {})
base_url = args.get('base_url', 'https://api.example.com')
return {
'my_provider': {
'api': {
'base_url': base_url,
'endpoints': {
'users': f'{base_url}/users',
'posts': f'{base_url}/posts',
}
},
'features': args.get('features', ['basic']),
}
}
# Project config
context:
my_provider_args:
base_url: https://staging.api.example.com
features: ['basic', 'advanced']
context_overrides:
'my_provider.api.endpoints.posts': 'https://custom.api.example.com/posts' # Override generated context
This pattern scales well as projects grow and add more providers.