Skip to content

How To Create a Template Based on an Existing Project

  1. Take a copy of your project, because obviously you don't want to lose your work :D
  2. To create a template, cookiecutter needs two things:

    • The folder/file structure
    • a cookiecutter.json file which stores the configuration of the project. To achieve this, rename the project root folder to “{{cookiecutter.project}}” and create “cookiecutter.json” in the folder.
  3. Configure: cookiecutter.json example:

        { 
            "project": "project", 
            "author": "author", 
            "version": "0.1",
            "description": "description"
        }
    
    Feel free to add any additional variables if you like. You will be prompted to enter the above fields when you try to create a repository from the template that you're building now

  4. Folder structure: {{cookiecutter.project}}

    • inside the {{cookiecutter.project}} folder, create the following files:
    • README.md, pyproject.toml
  5. README.md example
    ## readme of the /{{cookiecutter.project}}/
    
    Author : {{cookiecutter.author}}
    Version: {{cookiecutter.version}}
    Descritption: {{cookiecutter.description}}
    
  6. pyproject.toml example
    [tool.poetry]
    name = {{cookiecutter.project}}
    version = {{cookiecutter.version}}
    description = {{cookiecutter.description}}
    authors = [{{cookiecutter.author}}]
    
    [tool.poetry.dependencies]
    python = "*"
    
    [tool.poetry.dev-dependencies]
    pytest = "^3.4"
    
  7. Push the template to your Bitbucket/github repository for future use
  8. Example command to download and use your template cookiecutter git@bitbucket.org:jacksonlaboratory/software-templates.git --directory="python-poetry-cli"
  9. Additionally if you want to change a folder name in the project use jinja2 templating i.e., name the folder {{cookiecutter.variable_name}} and add the default variable_name to the cookiecutter.json file.
  10. You can also use the cookiecutter variables in the python files if you want to configure any content
    example __init__.py:
# -*- coding: utf-8 -*-

__author__ = '{{cookiecutter.full_name}}'
__email__ = '{{cookiecutter.email}}'
__version__ = '{{cookiecutter.version}}'

Using pre & post hooks

You can have Python or Shell scripts that run before and/or after your project is generated.

Put them in hooks folder like this:

├── {{cookiecutter.project}}/
├── hooks
│   ├── pre_gen_project.py
│   └── post_gen_project.py
└── cookiecutter.json

Pre generate hook example:

import re
import sys


MODULE_REGEX = r'^[_a-zA-Z][_a-zA-Z0-9]+$'

module_name = '{{ cookiecutter.module_name }}'

if not re.match(MODULE_REGEX, module_name):
    print('ERROR: %s is not a valid Python module name!' % module_name)

    # exits with status 1 to indicate failure
    sys.exit(1)

The above example validates the module name using the MODULE_REGEX before generating the project

Post generate hook example (with jinja2 if conditions)

import os
import sys

REMOVE_PATHS = [
    '{% if cookiecutter.packaging != "pip" %} requirements.txt {% endif %}',
    '{% if cookiecutter.packaging != "poetry" %} poetry.lock {% endif %}',
]

for path in REMOVE_PATHS:
    path = path.strip()
    if path and os.path.exists(path):
        if os.path.isdir(path):
            os.rmdir(path)
        else:
            os.unlink(path)

The above example deletes any unwanted files if they are in the project folder after the cookiecutter generates the project

Note: When the hook scripts are run, their current working directory is the root of the generated project