docs/scripts.md
This guide covers how to define and use custom script shortcuts in Pipenv, including basic and advanced usage patterns, best practices, and troubleshooting tips.
Pipenv allows you to define custom script shortcuts in your Pipfile, providing a convenient way to run common commands within your project's virtual environment. These shortcuts can simplify development workflows, standardize commands across team members, and document common operations.
You can define scripts in the [scripts] section of your Pipfile:
[scripts]
start = "python app.py"
test = "pytest"
lint = "flake8 ."
format = "black ."
Each script consists of a name (the key) and a command (the value) that will be executed in the context of your virtual environment.
To run a script, use the pipenv run command followed by the script name:
$ pipenv run start
This executes the command python app.py within your project's virtual environment, even if you haven't activated the shell first.
You can pass additional arguments to your scripts:
$ pipenv run test tests/test_api.py -v
This appends the arguments to the command defined in your script, resulting in pytest tests/test_api.py -v in this example.
For more complex scripts, you can use the extended syntax with a dictionary:
[scripts]
start = {cmd = "python app.py"}
This is functionally equivalent to the simple syntax but allows for additional options.
You can call Python functions directly from your scripts using the call option:
[scripts]
my_function = {call = "package.module:function()"}
my_function_with_args = {call = "package.module:function('arg1', 'arg2')"}
When you run pipenv run my_function, Pipenv will import the specified module and call the function.
You can combine multiple commands using shell syntax:
[scripts]
setup = "mkdir -p data logs && touch .env && echo 'Setup complete'"
For more complex combinations, consider using platform-specific syntax:
[scripts]
# Unix-like systems
build_and_run = "npm run build && python app.py"
# Windows
build_and_run_win = "npm run build & python app.py"
You can define scripts that set environment variables using standard shell syntax:
[scripts]
dev = "FLASK_ENV=development FLASK_DEBUG=1 flask run"
prod = "FLASK_ENV=production gunicorn app:app"
**Important Limitation**: Inline environment variable assignment (like `VAR=value command`) works because the entire command string is passed to the shell. However, more complex shell features like command substitution (`$(command)`) or environment variable expansion within the script definition may not work as expected.
This is because Pipenv parses the script command and executes it directly, rather than always passing it through a full shell interpreter.
For more reliable environment variable handling, use one of these approaches:
Create a .env file in your project directory:
# .env
PROJECT_DIR=/path/to/project
FLASK_ENV=development
Pipenv automatically loads these variables before running scripts:
[scripts]
dev = "flask run"
You can also define environment variables directly in the script definition using the extended table syntax. However, note that these values are static and cannot use shell expansion:
[scripts.dev]
cmd = "flask run"
env = {FLASK_ENV = "development", DEBUG = "1"}
For complex scripts that require shell features, call an external shell script:
[scripts]
dev = "bash scripts/dev.sh"
Then in scripts/dev.sh:
#!/bin/bash
export PROJECT_DIR="$(pipenv --where)"
python "$PROJECT_DIR/src/main.py"
To see all available scripts defined in your Pipfile, use the pipenv scripts command:
$ pipenv scripts
command script
start python app.py
test pytest
lint flake8 .
format black .
This provides a convenient way to discover available commands, especially in projects you're not familiar with.
[scripts]
server = "python manage.py runserver"
migrations = "python manage.py makemigrations"
migrate = "python manage.py migrate"
shell = "python manage.py shell"
[scripts]
notebook = "jupyter notebook"
lab = "jupyter lab"
preprocess = "python scripts/preprocess_data.py"
train = "python scripts/train_model.py"
[scripts]
test = "pytest"
test_cov = "pytest --cov=app tests/"
lint = "flake8 ."
type_check = "mypy ."
format = "black ."
check_format = "black --check ."
security = "pipenv scan"
[scripts]
build = "python setup.py build"
dist = "python setup.py sdist bdist_wheel"
publish = "twine upload dist/*"
docs = "mkdocs build"
serve_docs = "mkdocs serve"
If you're familiar with npm scripts, you can create similar workflows:
[scripts]
start = "python app.py"
dev = "python app.py --debug"
build = "python build.py"
postbuild = "python scripts/post_build.py"
You can emulate Makefile targets:
[scripts]
all = "pipenv run clean && pipenv run build"
build = "python setup.py build"
clean = "rm -rf build/ dist/ *.egg-info"
install = "pip install -e ."
Define scripts that can be used in CI/CD pipelines:
[scripts]
ci_test = "pytest --junitxml=test-results.xml"
ci_lint = "flake8 . --output-file=flake8.txt"
ci_type_check = "mypy . --txt-report reports"
Group related scripts with consistent naming conventions:
[scripts]
test = "pytest"
test_cov = "pytest --cov=app"
test_watch = "ptw"
Use prefixes for different categories of scripts:
[scripts]
db_migrate = "alembic upgrade head"
db_rollback = "alembic downgrade -1"
db_reset = "alembic downgrade base"
Document complex scripts with comments in your README or documentation.
[scripts]
dangerous_reset = "python -c \"input('Are you sure? [y/N] ') == 'y' or exit(1)\" && python reset_database.py"
If you get "Command 'script-name' not found":
PipfileIf a script fails to execute:
pipenv run --verbose script-name for more informationIf your script can't find files or executables:
import os; print(os.getcwd())) for debuggingOn Windows:
/) with Windows-style (\\) or use raw strings&& for command chaining in CMD or ;& in PowerShellOn Unix-like systems:
You can generate scripts dynamically using Python:
# build_scripts.py
import toml
# Load existing Pipfile
with open("Pipfile", "r") as f:
pipfile = toml.load(f)
# Ensure scripts section exists
if "scripts" not in pipfile:
pipfile["scripts"] = {}
# Add scripts for each module
modules = ["users", "products", "orders"]
for module in modules:
pipfile["scripts"][f"test_{module}"] = f"pytest tests/{module}"
pipfile["scripts"][f"lint_{module}"] = f"flake8 app/{module}"
# Write updated Pipfile
with open("Pipfile", "w") as f:
toml.dump(pipfile, f)
[scripts]
db_setup = {cmd = "python -c \"from app import db; db.create_all()\""}
db_seed = "python scripts/seed_database.py"
db_migrate = "flask db migrate -m 'Auto-migration'"
db_upgrade = "flask db upgrade"
db_downgrade = "flask db downgrade"
db_reset = "pipenv run db_downgrade && pipenv run db_upgrade && pipenv run db_seed"
[scripts]
dev = {cmd = "concurrently \"pipenv run backend\" \"pipenv run frontend\""}
backend = "flask run --port=5000"
frontend = "cd frontend && npm start"
setup = "pipenv install && cd frontend && npm install"
[scripts]
deploy = {cmd = "pipenv run test && pipenv run build && pipenv run publish"}
build = "python setup.py sdist bdist_wheel"
publish = "twine upload dist/*"
Custom script shortcuts in Pipenv provide a powerful way to standardize and simplify common development tasks. By defining scripts in your Pipfile, you can create a consistent interface for project-specific commands, improve developer productivity, and document important workflows.
Whether you're using simple command shortcuts or complex function calls, Pipenv scripts help create a more streamlined and maintainable development environment for Python projects.