Feature-Request: Templates in context variables should support boolean values
- Cookiecutter version: 2.6.0
- Python version: 3.9.16
- Operating System: RHEL 8
This issue is a reopening of #1738.
Description:
As described here, we expect template authors to be able to reuse values of previous keys from cookiecutter.json, but currently that is not possible if you are working with boolean values. I believe it would be very important to support this use case or describe an alternative way to do it in docs. Example:
cookiecutter.json:
{
"likes_potatoes": false,
"likes_mashed_potatoes": "{{ cookiecutter.likes_potatoes }}"
}
Template example using above variables:
#!/bin/bash
{% if cookiecutter.likes_mashed_potatoes %}
echo "I like mashed potatoes!"
{% endif %}
The echo "I like mashed potatoes" line always renders, even when likes_potatoes is false.
There's no simple functionality in Jinja2 to base this on.
>>> import jinja2
>>> t = jinja2.Template(' context.val(: {{context.val}}) is {% if context.val %} truthy {% else %} falsey {% endif %}')
>>> t.render(context={'other': False, 'val': '{{ context.other }}'})
' context.val(: {{ context.other }}) is truthy '
Cookiecutter would need to make two rendering passes to support this.
What would you think of adding a metadata variable to cookiecutter.json (similar to _copy_without_render) to indicate a variable should have a certain type? e.g.
{
"_variable_types": {
"likes_mashed_potatoes": "Boolean"
},
"likes_potatoes": false,
"likes_mashed_potatoes": "{{ cookiecutter.likes_potatoes }}"
}
I didn't quite notice at first, that currently Cookiecutter already makes multiple rendering passes over the values in cookiecutter.json that are templates, to pick up self references. But by definition, a text templating language like Jinja2 can only render strings in those passes, not booleans. So cookiecuter would have to read the metadata and remove it from the rendering context itself
If it supported cookiecutter.yml, wouldn't you be able to achieve the same thing with Yaml Anchors, Aliases, or even Tags, with much less new code required in Cookiecutter, without altering the way thw code treats existing templates that use cookiecutter.json?
You're suggesting something like the following if YAML was supported?
likes_potatoes: false
likes_mashed_potatoes: !!bool "{{ cookiecutter.likes_potatoes }}"
If so, I think that would work for me. I just need cookiecutter to know that likes_mashed_potatoes's value is a Boolean when using a template for the value.
I'm suggesting Yaml anchors, and that cookiecutter just lets a good Yaml library handle everything:
anchored_content: &anchor_name This string will appear as the value of two keys.
other_anchor: *anchor_name
https://learnxinyminutes.com/docs/yaml/
The problem with YAML anchors is that I need the second value to be a template, so I can accomplish something like:
potato_preference: ['mashed', 'boiled', 'roasted', 'raw']
should_boil_water: "{{ cookiecutter.potato_preference == 'boiled' }}"
I'd like should_boil_water to be a Boolean.
I don't see a way to use YAML anchors to accomplish that, unless I'm missing something.
Fair enough. It's still a huge change to start processing different data types, within what's essentially a sophisticated text/string global search/replace program, that delegates much of the text/string handling to Jinja2.
Does it just need to be a Boolean, only for that test within Jinja? Or does something else also use your cookiecutter.json?
Other templates avoid the need for Booleans by simply writing a more explicit test, e.g. using 'y', or anything else.
{%- if cookiecutter.use_celery == 'y' %}
celeryworker:
<<: *django
image: {{ cookiecutter.project_slug }}_production_celeryworker
command: /start-celeryworker
celerybeat:
<<: *django
image: {{ cookiecutter.project_slug }}_production_celerybeat
command: /start-celerybeat
flower:
<<: *django
image: {{ cookiecutter.project_slug }}_production_flower
command: /start-flower
{%- endif %}
https://github.com/cookiecutter/cookiecutter-django/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/production.yml
Fair enough. It's still a huge change to start processing different data types, within what's essentially a sophisticated text/string global search/replace program, that delegates much of the text/string handling to Jinja2.
Does it just need to be a Boolean, only for that test within Jinja? Or does something else also use your cookiecutter.json?
Other templates avoid the need for Booleans by simply writing a more explicit test, e.g. using 'y', or anything else.
{%- if cookiecutter.use_celery == 'y' %} celeryworker: <<: *django image: {{ cookiecutter.project_slug }}_production_celeryworker command: /start-celeryworker celerybeat: <<: *django image: {{ cookiecutter.project_slug }}_production_celerybeat command: /start-celerybeat flower: <<: *django image: {{ cookiecutter.project_slug }}_production_flower command: /start-flower {%- endif %}https://github.com/cookiecutter/cookiecutter-django/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/production.yml
This was the solution we ended up going with for now, comparing the variable's value with "yes"/"no" strings. Just nice to use Booleans where we can (also nice to get the [y/n] input prompt). 😄