A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Helper functions, logging configuration, replay functionality, and comprehensive exception hierarchy. This module provides supporting utilities and error handling for all cookiecutter operations.
File and directory management functions.
def rmtree(path):
"""
Remove directory and contents like rm -rf.
Parameters:
- path: str - Directory path to remove
"""
def force_delete(func, path, exc_info):
"""
Error handler for shutil.rmtree equivalent to rm -rf.
Parameters:
- func: callable - Function that failed
- path: str - Path that caused the error
- exc_info: tuple - Exception information
"""
def make_sure_path_exists(path):
"""
Ensure directory exists.
Parameters:
- path: str - Directory path to create if needed
"""
def make_executable(script_path):
"""
Make script executable.
Parameters:
- script_path: str - Path to script file
"""Utilities for working directory and environment management.
def work_in(dirname=None):
"""
Context manager for changing working directory.
Parameters:
- dirname: str, optional - Directory to change to
Returns:
ContextManager - Context manager for directory change
"""
def create_tmp_repo_dir(repo_dir):
"""
Create temporary directory with repo copy.
Parameters:
- repo_dir: str - Source repository directory
Returns:
str - Path to temporary directory copy
"""
def create_env_with_context(context):
"""
Create Jinja2 environment with context.
Parameters:
- context: dict - Template context
Returns:
Environment - Configured Jinja2 environment
"""Utilities for template processing and Jinja2 integration.
def simple_filter(filter_function):
"""
Decorator to wrap function in Jinja2 extension.
Parameters:
- filter_function: callable - Function to wrap as Jinja2 filter
Returns:
callable - Decorated function
"""Logging setup and configuration for cookiecutter operations.
def configure_logger(stream_level='DEBUG', debug_file=None):
"""
Configure logging for cookiecutter.
Parameters:
- stream_level: str - Logging level for console output
- debug_file: str, optional - File path for debug logging
"""Session replay functionality for reusing previous configurations.
def get_file_name(replay_dir, template_name):
"""
Get replay file name.
Parameters:
- replay_dir: str - Directory containing replay files
- template_name: str - Name of template
Returns:
str - Full path to replay file
"""
def dump(replay_dir, template_name, context):
"""
Write context data to replay file.
Parameters:
- replay_dir: str - Directory to store replay file
- template_name: str - Name of template
- context: dict - Context data to save
"""
def load(replay_dir, template_name):
"""
Read context data from replay file.
Parameters:
- replay_dir: str - Directory containing replay files
- template_name: str - Name of template
Returns:
dict - Loaded context data
"""LOG_LEVELS: dict # Dictionary mapping level names to logging constants
# Contains: {'DEBUG': logging.DEBUG, 'INFO': logging.INFO, 'WARNING': logging.WARNING, 'ERROR': logging.ERROR}
LOG_FORMATS: dict # Dictionary of log format strings
# Contains format strings for different logging levelsComprehensive exception classes for error handling throughout cookiecutter.
class CookiecutterException(Exception):
"""Base exception class for all cookiecutter errors."""class ConfigDoesNotExistException(CookiecutterException):
"""Missing config file."""
class InvalidConfiguration(CookiecutterException):
"""Invalid configuration file."""class NonTemplatedInputDirException(CookiecutterException):
"""Input directory is not templated."""
class UnknownTemplateDirException(CookiecutterException):
"""Ambiguous project template directory."""
class MissingProjectDir(CookiecutterException):
"""Missing generated project directory."""
class ContextDecodingException(CookiecutterException):
"""Failed JSON context decoding."""
class UndefinedVariableInTemplate(CookiecutterException):
"""Template uses undefined variables."""class UnknownRepoType(CookiecutterException):
"""Unknown repository type."""
class RepositoryNotFound(CookiecutterException):
"""Repository doesn't exist."""
class RepositoryCloneFailed(CookiecutterException):
"""Repository can't be cloned."""
class InvalidZipRepository(CookiecutterException):
"""Invalid zip repository."""class VCSNotInstalled(CookiecutterException):
"""Version control system not available."""
class OutputDirExistsException(CookiecutterException):
"""Output directory already exists."""
class InvalidModeException(CookiecutterException):
"""Incompatible modes (no_input + replay)."""
class FailedHookException(CookiecutterException):
"""Hook script failures."""
class UnknownExtension(CookiecutterException):
"""Un-importable Jinja2 extension."""from cookiecutter.utils import rmtree, make_sure_path_exists, work_in
import os
# Ensure directory exists
make_sure_path_exists('./my-project/src')
# Work in different directory
with work_in('./my-project'):
# Operations here happen in ./my-project
print(os.getcwd()) # Shows full path to ./my-project
# Back to original directory
# Clean up directory
if os.path.exists('./temp-dir'):
rmtree('./temp-dir')from cookiecutter.log import configure_logger, LOG_LEVELS
# Basic logging setup
configure_logger(stream_level='INFO')
# Debug logging with file output
configure_logger(
stream_level='DEBUG',
debug_file='./cookiecutter-debug.log'
)
# Available log levels
print("Available log levels:", list(LOG_LEVELS.keys()))from cookiecutter.replay import dump, load, get_file_name
# Save context for replay
context = {
'cookiecutter': {
'project_name': 'my-awesome-project',
'author': 'Jane Developer',
'version': '1.0.0'
}
}
dump(
replay_dir='~/.cookiecutter_replay',
template_name='python-package',
context=context
)
# Load previous context
loaded_context = load(
replay_dir='~/.cookiecutter_replay',
template_name='python-package'
)
print("Loaded context:", loaded_context)from cookiecutter.main import cookiecutter
from cookiecutter.exceptions import (
CookiecutterException,
OutputDirExistsException,
RepositoryNotFound,
RepositoryCloneFailed,
ContextDecodingException,
FailedHookException
)
def safe_cookiecutter(template, **kwargs):
"""Wrapper for cookiecutter with comprehensive error handling."""
try:
return cookiecutter(template, **kwargs)
except OutputDirExistsException as e:
print(f"Output directory exists: {e}")
# Optionally retry with overwrite
return cookiecutter(template, overwrite_if_exists=True, **kwargs)
except RepositoryNotFound as e:
print(f"Template repository not found: {e}")
return None
except RepositoryCloneFailed as e:
print(f"Failed to clone repository: {e}")
return None
except ContextDecodingException as e:
print(f"Invalid cookiecutter.json format: {e}")
return None
except FailedHookException as e:
print(f"Hook execution failed: {e}")
return None
except CookiecutterException as e:
print(f"Cookiecutter error: {e}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
return None
# Usage
result = safe_cookiecutter('gh:audreyfeldroy/cookiecutter-pypackage')from cookiecutter.utils import (
create_tmp_repo_dir,
create_env_with_context,
simple_filter,
make_executable
)
import tempfile
import os
# Create temporary copy of repository
temp_dir = create_tmp_repo_dir('./my-template')
print(f"Temporary copy created at: {temp_dir}")
# Create Jinja2 environment with context
context = {'project_name': 'test-project', 'version': '1.0'}
env = create_env_with_context(context)
# Create custom Jinja2 filter
@simple_filter
def uppercase_filter(text):
return text.upper()
# Apply filter in template
template = env.from_string('{{ project_name | uppercase }}')
result = template.render(project_name='hello world')
# Returns: 'HELLO WORLD'
# Make script executable
script_path = './setup-script.sh'
if os.path.exists(script_path):
make_executable(script_path)from cookiecutter.utils import work_in
import os
import subprocess
# Complex operation in different directory
original_dir = os.getcwd()
print(f"Starting in: {original_dir}")
with work_in('./my-project'):
print(f"Working in: {os.getcwd()}")
# Run commands in the project directory
subprocess.run(['pip', 'install', '-e', '.'], check=True)
subprocess.run(['python', '-m', 'pytest'], check=True)
# Create subdirectory
os.makedirs('build', exist_ok=True)
print(f"Back to: {os.getcwd()}") # Back to original directoryfrom cookiecutter.exceptions import UndefinedVariableInTemplate
# Exception with additional context
try:
# Some template rendering operation
pass
except UndefinedVariableInTemplate as e:
print(f"Undefined variable error: {e.message}")
print(f"Error details: {e.error}")
print(f"Context: {e.context}")from cookiecutter.replay import get_file_name, load
import os
import json
# Check if replay file exists
replay_dir = '~/.cookiecutter_replay'
template_name = 'my-template'
replay_file = get_file_name(replay_dir, template_name)
if os.path.exists(replay_file):
# Load and examine replay data
replay_data = load(replay_dir, template_name)
print(f"Previous configuration:")
print(json.dumps(replay_data, indent=2))
else:
print("No replay file found for this template")Install with Tessl CLI
npx tessl i tessl/pypi-cookiecutter