Although I really like cookiecutter since it enables you to really start a lot of different projects in exactly the same way, one of the things that I think could be improved is an easy way to provide some additional help/information when the questions are being asked. I saw that there already exists an issue for this on the cookiecutter repo, but this is already quite old. Not too much progress has been made on this area, so not sure when such a new feature would become available, unless you start using forked projects.

The reason I want to do this is that sometimes it can be useful for new users of a cookiecutter template to get access to inline documentation instead of having to open some other page.

One thing I have always liked with computers/programs since I was little, is to see how I can use what is provided in such a way that I can do something that it originally was not supposed to do. This process requires a lot of puzzling and trying to be very creative with the inputs you can provide.

After a bit of puzzling and playing around, I was able to at least provide some help text in a cookiecutter.json that could be shown to a user, and this post shows how I got this to work.

Base scenario cookiecutter.json

Let's start with a very basic cookiecutter project, where we have a README.md file in a project directory that will be templated using the information from cookiecutter.

In the folder {{ cookiecutter.project_slug }} we will have a README.md with the following contents:

# {{ cookiecutter.project_name }}

This is a small project create with the cookiecutter demo template

Relevant settings:
* Project name: {{ cookiecutter.project_name }}
* Author: {{ cookiecutter.full_name }} ({{ cookiecutter.email }})

Debug (complete cookiecutter context):
{% for key, value in cookiecutter.items() -%}
* "{{ key }}": {{ value }}
{% endfor %}

Our cookiecutter.json will have the following contents:

{
  "full_name": "Your name",
  "email": "your email address",
  "project_name": "Your project name",
  "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}"
}

If you run this json file with cookiecutter, you will get the following list of questions:

(testing_cookiecutter)  guido@cartman:~/Programming/cookiecutter-demo$ cookiecutter -o . cookiecutter-demo/
full_name [Your name]: Guido Diepen
email [your email address]: a@b.com
project_name [Your project name]: My Awesome Project
project_slug [my_awesome_project]: 

and after answering them as in the above example, cookiecutter will automatically generate a new folder my_awesome_project (since that is the default project_slug) with a README.md with the following contents:

(testing_cookiecutter)  guido@cartman:~/Programming/cookiecutter-demo$ cat my_awesome_project/README.md 
# My Awesome Project

This is a small project create with the cookiecutter demo template

Relevant settings:
* Project name: My Awesome Project
* Author: Guido Diepen (a@b.com)

Debug (complete cookiecutter context):
* "full_name": Guido Diepen
* "email": a@b.com
* "project_name": My Awesome Project
* "project_slug": my_awesome_project
* "_template": cookiecutter-demo/

This is the way the usage of cookiecutter was intended and should not be a big surprise.

Adding help text

Now let's see how we can add some help. After some playing around, I found that I could just add new-line characters in the default values for variables and they would be printed. Since I am not really interested in the actual value later on, I am for now using one space as the key.

For example, if you change the cookiecutter.json to the following:

{
  " ": "CookieCutter with inline help demo\n\nThis is some\nmultiline\ntext explaining the options",
  "full_name": "Your name",
  "email": "your email address",
  "project_name": "Your project name",
  "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}"
}

we would end up with the following set of questions:

(testing_cookiecutter)  guido@cartman:~/Programming/cookiecutter-demo$ cookiecutter -o . cookiecutter-demo/
  [CookieCutter with inline help demo

This is some
multiline
text explaining the options]: 
full_name [Your name]: Guido Diepen
email [your email address]: a@b.com
project_name [Your project name]: My Awesome Project
project_slug [my_awesome_project]: 

As you can see, this does give us a nice multi-line place where we can put any text. If we are to show some text and make it more informative to an end-user, we can play around a bit with the exact 'default value' of the key.

From the above example it is clear that the way cookiecutter prints the key-value combination is as follows:

key [value]:

Since our key is one (or more spaces, if later on you decide to have multiple explaining screens), cookiecutter will print the spaces followed by a [ indicating the current value. In order to clean this up a tiny bit, I am setting the value for the one-space key in the cookiecutter.json as follows:

  " ": "]\nCookieCutter with inline help demo\n\nThis is some\nmultiline\ntext explaining the options\n[Please press enter to continue"

This will print a closing ] right after the opening [ displayed by cookiecutter (have not found any way that I can disable this...), followed by a new-line, followed by the multi-line text. Finally, I show an additional prompt to the end-users, telling him/her to press the enter key to continue (and with that storing the multi-line default value in the cookiecutter context under the single-space key).

Note that cookiecutter will add a closing ] to the default value automatically, therefore I prefix the final prompt line with an opening [.

On the screen, this will look as follows:

(testing_cookiecutter)  guido@cartman:~/Programming/cookiecutter-demo$ cookiecutter -o . cookiecutter-demo/
  []
CookieCutter with inline help demo

This is some
multiline
text
[Please press enter to continue]: 
full_name [Your name]: Guido Diepen
email [your email address]: a@b.com
project_name [Your project name]: My Awesome Project
project_slug [my_awesome_project]: 

This way unfortunately, it is not yet possible to print messages before every question without having the user to explicitly press enter to move to the next question after the 'help' question.

Therefore, my current approach when using this is to divide all of my questions into a couple of blocks and have one multi-line explanation key above each block.

Cleaning up help text

The only thing you will have to very careful with is that the cookiecutter context will now have multi-line values. In case you are performing a loop over all of the key-value combinations in the cookiecutter context and using them without triple-quotes in python for example, you could end up with incorrect python code because these strings would be printed over multiple lines.

In order to solve this, I just add one more last item in the cookiecutter.json that updates the values all of my special space keys from the default multiline value to just empty string.

This can be achieved by adding the following key-value combination to your cookiecutter.json as the last line:

  "  ": "{% set update_result = cookiecutter.update({' ': ''}) %}Finished with all questions - Please press enter to generate your project"

Using this additional key-value, we will now get the following flow through our cookiecutter:

(testing_cookiecutter)  guido@cartman:~/Programming/cookiecutter-demo$ cookiecutter -o . cookiecutter-demo/
  []
CookieCutter with inline help demo

This is some
multiline
text explaining the options
[Please press enter to continue]:    
full_name [Your name]: Guido Diepen
email [your email address]: a@b.com
project_name [Your project name]: My Awesome Project
project_slug [my_awesome_project]: 
   [Finished with all questions - Please press enter to generate your project]: 

After the user presses enter, cookiecutter will generate the new project again.

Taking it to the limit

Now that we are able to show some text, why stop just here with only boring text? Now that we know we can print multiple things, we can also do some ASCII art here!

With the Text to ASCII Art Generator TAAG website, you can create all kinds of cool ASCII art versions for text. Unfortunately, we cannot directly copy/paste that into our cookiecutter.json file, because using the special ASCII values are not valid for the UTF-8 encoded JSON.

In order to overcome this problem, I copy/paste the output of the TAAG website into a UTF-8 encoded text file (e.g., header.txt) and use the following small python script to update the value of the single-space key in the cookiecutter.json file:

import json

with open("cookiecutter.json") as f:
    cc_template = json.load(f)

with open("header.txt", encoding="utf-8") as f:
    cc_template[" "] = f.read()

with open("cookiecutter.json", "w") as f:
    f.write(json.dumps(cc_template, sort_keys=False, indent=4))
    f.write("\n")

This will just open the cookiecutter.json file and read in the dictionary. After that, it opens the header.txt file with UTF-8 encoding, reads the contents and updates the value of the single-space key in the dictionary. Finally, we write the dictionary to the cookiecutter.json file again, ensuring that the keys are NOT getting sorted.

If you open your cookiecutter.json file after the above, you will see a lot of escaped unicode characters, which make it quite difficult/impossible to see how the result will look like.

However, if you initiate a new run with cookiecutter, this will now be the way it looks:

I don't think this was the way the creators of cookiecutter intended to have it being used, but at least I am able to provide the end-user of my cookiecutter template with some inline help/information without requiring any other tools than vanilla cookiecutter! Also tried this on multiple platforms and it works under all platforms I have tested: Windows / Mac / Linux.

I have not tried anything else, but I am assuming it will be possible to do all kinds of cool tricks with ANSI escape sequences also, like colors or other things. With those things, just not 100% sure if that will work cross platform also, as it will depend on support for these sequences of your console.

Curious to hear if you have any other examples of cool ways to trick an application into doing things outside its normal intended use!