Py Launch Blueprint is a comprehensive Python project template that eliminates setup friction by providing a pre-configured development environment with carefully selected tools for linting, formatting, and type checking. It includes an annotated CLI example and detailed documentation explaining each tool choice and configuration decision, making it an ideal starting point for professional Python projects.
Py Launch Blueprint eliminates the setup friction in Python projects by providing a production-ready template with carefully curated tools and best practices. Here's what makes it special:
- Zero Configuration Setup: Get started immediately with pre-configured development tools
- Type Safety First: Built-in MyPy configuration and VS Code integration for robust type checking
- Modern Development Tools:
- ⚡ Ruff for lightning-fast linting and formatting
- 🔍 Pre-commit hooks for code quality enforcement
- 📝 Type checking with MyPy and Pylance
- Python 3.10+ Support: Leverages modern Python features
- Dependency Management: Uses
uv
for fast, reliable package management - CI/CD Ready: Includes GitHub Actions workflows
- Comprehensive Testing: Pre-configured test setup with best practices
- VS Code Integration: Curated set of recommended extensions
- Intelligent Defaults: Optimized settings for common development tasks
- Clear Documentation: Detailed explanations for all tool choices and configurations
- Git Hooks: Automated code quality checks before commits
- Professionals looking for a production-ready Python project
- Teams wanting a standardized Python development environment
- Projects requiring maintainable, type-safe code
- Developers who value clean, consistent code style
- Anyone looking to adopt Python best practices from day one
Start your next Python project with confidence, knowing you're building on a foundation of best practices and modern development tools.
- Heavily Documented: Every detail is explained, making it easy to understand, even for folks new to the project, tools, or new to Python.
- Pragmatic: Focuses on practical best practices instead of strict rules
- Modular: Designed to be easy to remove features, replace with alternatives or add new ones
- Transparent: Every detail is documented, making it easy to understand and contribute
- Collaborative: A community of developers is formed, fostering a shared learning environment
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Before we can accept your contributions, you will need to sign a Contributor License Agreement (CLA). This is a legal document in which you state that you are entitled to contribute the code you are submitting and that you grant us the rights to use that contribution.
- Individual Contributors: If you are contributing as an individual, please sign the Individual CLA.
- Corporate Contributors: If you are contributing on behalf of a company, please sign the Corporate CLA.
We have integrated the CLA Assistant bot to streamline the CLA signing process. When you open a pull request, the bot will check if you have signed the CLA. If you have not, it will provide a link to sign it.
This project is licensed under the MIT License - see the LICENSE file for details.
The project uses a simple, maintainable structure:
py-utils/
├── projects.py # Main script with all functionality
├── tests/ # Test files
│ ├── __init__.py
│ ├── test_api.py
│ ├── test_config.py
│ └── test_cli.py
├── pyproject.toml # Project and tool configuration
└── README.md # Documentation
This project uses automatic version management powered by Git tags and setuptools_scm
. The version is dynamically generated during installation and build processes.
- Automatic Versioning: Version derived from Git tags (e.g.,
v1.2.3
→1.2.3
) - Development Versions: Unreleased commits show as
1.2.4.dev1+gabcdef12
(includes commit ID by default) - CI/CD Integration: GitHub Actions validate version-tag alignment
- Semantic Versioning: Enforces proper version format in tags
- Customizable Local Scheme: Control how development versions are formatted (e.g., include/exclude commit ID)
make check
Project requires Python 3.10+ (which is also specified inside .python-version file) and uv installed.
There are two options for setting up the development environment:
It depends on the tool you choose, but both offer a convenient way to install the package in editable mode with development dependencies. UV is recommended as it offers much greater speed and a lot of features and tools out of the box.
# This command creates a live development installation that allows you to modify the code without reinstalling while also installing additional development tools (like pytest, mypy, etc.) specified in your project's dev dependencies.
uv pip install --editable ".[dev]"
# Format the code
uvx ruff format py_launch_blueprint/
# Run linter
uvx ruff check py_launch_blueprint/
# Run type checker
uvx --with-editable . mypy py_launch_blueprint/
# Run tests
uvx --with-editable . pytest
# Run tests with coverage
uvx --with pytest-cov --with-editable . pytest --cov=py_launch_blueprint.projects --cov-report=term-missing
# Run command
uvx --with-editable . --from py_launch_blueprint py-projects
# Setup Pre-Commit Hook
uvx --with-editable . pre-commit install
#Run all pre-Commit Hooks
uvx pre-commit run --all-files
# Create and activate a virtual environment if needed
python3 -m venv .venv
source .venv/bin/activate # On Unix/macOS
.venv\Scripts\activate # On Windows
# Install the package in editable mode with development dependencies
pip install --editable ".[dev]"
# Run development tools directly (no need for 'uv pip run')
ruff format py_launch_blueprint/
ruff check py_launch_blueprint/
mypy py_launch_blueprint/
pytest --cov=py_launch_blueprint.projects --cov-report=term-missing
# Check the installed package cli tool version
py-projects --version
# Setup Pre-Commit Hook
pre-commit install
#Run all pre-Commit Hooks
pre-commit run --all-files
This project includes a Justfile
that provides convenient commands for common development tasks. Just is a handy command runner that helps standardize commands across your project.
To use these commands, first install Just. You can see all available commands by running:
just --list
Here are some commonly used commands (this is just a subset of all available commands):
# Setup your development environment
just setup
# Format code (includes ruff format and import sorting)
just format
# Run linter (code style and quality checks)
just lint
# Run type checker
just typecheck
# Run tests
just test
# Run all checks (tests, linting, and type checking)
just check
# Check installed package version
just version
# Clean up temporary files and caches
just clean
# Set up pre-commit hooks
just pre-commit-setup
# Build the package
just build
# Install in development mode
just install-dev
When your virtual environment is activated, you can also use direct commands without uvx:
just format-pip # Run formatter directly
just lint-pip # Run linter directly
just typecheck-pip # Run type checker directly
just test-pip # Run tests directly with pytest
The Justfile standardizes common development tasks and provides a consistent interface for both uvx and direct command execution.
Ruff is a high-performance linter and code formatter for Python. It combines multiple tools into one, offering faster performance and comprehensive functionality compared to traditional Python tools.
Pros:
- Very Fast: Written in Rust, Ruff is significantly faster than traditional linters, allowing it to process large codebases quickly.
- All-in-One Solution: Ruff incorporates checks and fixes from a variety of popular linters like Flake8, Black, isort, pydocstyle, pyupgrade, autoflake. This means less maintenance of multiple separate tools.
- Customizable: Allows users to select and ignore specific checks or enforce particular rules according to the project needs.
- Easy Integration: Works well with CI/CD pipelines, IDEs, and modern developer workflows. Automated Fixes: Ruff can automatically correct a wide range of issues in your code.
Cons:
- Relatively New: As a newer tool, it might not yet be as widely adopted or supported in some edge cases.
Python line length standards:
- 79/80: Traditional PEP 8 standard. Good for side-by-side editing but can feel restrictive.
- 88: Black's default. Modern sweet spot between readability and expressiveness. Becoming the community standard.
- 100: Google's choice. Popular in enterprise. Good for complex expressions.
- 120: Maximum reasonable length. Works on wide screens but can hurt readability.
- Recommendation: Use 88 characters (Black's default) unless your team/project has an existing standard. It offers the best balance of readability and practicality while following modern community practices.
[tool.ruff]
line-length = 88
[tool.ruff.pycodestyle]
max-line-length = 88
Most teams today actually run both mypy and pyright/Pylance:
- mypy in CI/pre-commit hooks for strict checking
- Pylance in VS Code for real-time development feedback
This combination provides comprehensive type checking coverage while maintaining a smooth development experience.
Let me explain the difference between disallow_untyped_defs = false
vs true
:
disallow_untyped_defs = true
# This will raise an error
def process_data(data): # Error: Function is missing type annotations
return data + 1
# This is required instead
def process_data(data: int) -> int:
return data + 1
disallow_untyped_defs = false
# This is allowed
def process_data(data):
return data + 1
# This is also allowed
def process_data(data: int) -> int:
return data + 1
When to use each:
Use true
when:
- Starting a new project
- Working on a codebase that's fully committed to type hints
- Want to ensure complete type coverage
- Have a team that's comfortable with Python type hints
Use false
when:
- Gradually adding types to a legacy codebase
- Working with test files (common to disable for tests)
- Training team members who are new to type hints
- Need to temporarily bypass type checking for specific modules
Best Practice Recommendation:
Start new projects with true
for maximum type safety. For existing projects, use false
initially and gradually enable it as you add type hints to the codebase. Many teams set it to false
for test files but true
for production code.
VS Code Settings for pyright/Pylance
{
"python.analysis.typeCheckingMode": "strict",
"python.analysis.diagnosticMode": "workspace",
"python.analysis.autoImportCompletions": true,
"python.analysis.importFormat": "relative",
"python.analysis.inlayHints.functionReturnTypes": true,
"python.analysis.inlayHints.variableTypes": true
}
Common Type Annotation Examples
from typing import Dict, List, Optional, Tuple, Union, TypeVar, Generic
# Basic type annotations
def greet(name: str) -> str:
return f"Hello {name}"
# Optional parameters
def fetch_user(user_id: Optional[int] = None) -> Dict[str, Union[str, int]]:
...
# Generic types
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
self.items: List[T] = []
def push(self, item: T) -> None:
self.items.append(item)
def pop(self) -> T:
return self.items.pop()
# Type aliases
UserId = int
UserDict = Dict[UserId, Dict[str, Union[str, int]]]
# Callable types
from typing import Callable
Handler = Callable[[str, int], bool]
def process(handler: Handler) -> None:
...
Hooks are designed to maintain clean, consistent, and error-free code and configuration files. They save time by catching issues before they make it into your repository.
Following pre-commit hooks are used in this repo
check-yaml
checks if all YAML files in your repository are valid,end-of-file-fixer
ensures every file in your repository ends with a single newline character,trailing-whitespace
removes trailing spaces at the end of lines in your files,check-toml
checks if all TOML files in your repository are valid,check-added-large-files
warns when you try to add large files to the repository,mypy
checks your Python code for type errors based on type annotations,ruff
acts as a fast linter and formatter for Python, ensuring clean code,
# Create annotated tag
git tag -a v0.0.2 -m "Release version 0.0.2"
git push --tags
# Verify version
uv run python -c "import py_launch_blueprint; print(py_launch_blueprint.__version__)"
# Install with development dependencies
uv pip install --editable ".[dev]"
# Check current version
uv run python -c "import py_launch_blueprint; print(py_launch_blueprint.__version__)"
# Build package
hatch build
The version is automatically derived from Git tags using setuptools_scm
. Key behaviors:
- Tagged Releases:
v1.2.3
→1.2.3
- Development Versions:
1.2.4.dev1+gabcdef12
(after commits since last tag) - Fallback Version:
0.0.0
(if no tags exist)
The format of development versions can be customized using the local_scheme
option in pyproject.toml
. By default, the commit ID is included (e.g., 1.2.4.dev1+gabcdef12
). To change this behavior:
[tool.hatch.version.raw-options]
local_scheme = "no-local-version" # Excludes commit ID
Output:
1.2.4.dev1
[tool.hatch.version.raw-options]
local_scheme = "node-and-date" # Includes commit ID and date
Output:
1.2.4.dev1+gabcdef12.d20231025
Scheme | Example Output | Description |
---|---|---|
"no-local-version" |
1.2.4.dev1 |
No commit ID or dirty flag |
"node-and-date" |
1.2.4.dev1+gabcdef12.d20231025 |
Commit ID and date |
"node-and-timestamp" |
1.2.4.dev1+gabcdef12.1698249600 |
Commit ID and timestamp |
"dirty-tag" |
1.2.4.dev1+gabcdef12.dirty |
Commit ID and dirty flag |
The pre-configured GitHub Actions workflow (release.yml
) ensures a robust release process. Here's how it works:
- Trigger: Pushing a tag (e.g.,
v1.0.0
) triggers the workflow. - Checkout: The repository is checked out with full Git history (
fetch-depth: 0
) to enablesetuptools_scm
version detection. - Setup:
- Installs
uv
for fast dependency management. - Sets up Python using the specified version.
- Installs
- Build:
- Installs build tools (
hatch
andbuild
). - Builds the package using
hatch build
.
- Installs build tools (
- Version Validation:
- Extracts the version from the Git tag (e.g.,
v1.0.0
→1.0.0
). - Compares it with the package version generated by
setuptools_scm
. - Fails if there’s a mismatch.
- Extracts the version from the Git tag (e.g.,
- Output:
- If successful, the built distributions are available in the
dist/
directory.
- If successful, the built distributions are available in the
- Version shows 0.0.0: Create initial Git tag (
v0.1.0
). - Version mismatch in CI: Ensure GitHub Actions uses
fetch-depth: 0
. - Dirty version suffix: Commit all changes before tagging.
- Missing commit ID: Check the
local_scheme
setting inpyproject.toml
.
- Workflow not triggered: Ensure the tag follows the
v*
format (e.g.,v1.0.0
). - Build failures: Check the
pyproject.toml
configuration and dependencies.
When using this workflow as a template for a new project, update the following:
-
Project Name: Replace
py_launch_blueprint
with your package name in the version verification step:PY_VERSION=$(uv run python -c "import your_package_name; print(your_package_name.__version__)")
-
Python Version: Update the Python version to match your project's requirements:
with: python-version: "3.11" # Change to your required version
- Third-party library types:
# Install type stubs for common libraries
pip install types-requests types-PyYAML types-python-dateutil
- Ignoring specific lines:
# mypy
reveal_type(x) # type: ignore
# pyright
x = something() # pyright: ignore
- Type checking only specific files:
# mypy
mypy src/main.py src/utils.py
# pyright
pyright src/main.py src/utils.py
disallow_untyped_defs = true vs false
- Use
true
when starting new projects or working with teams experienced in type hints who want complete type coverage. - Use
false
when adding types to legacy code, working with test files, or training developers new to type hints.
disallow_untyped_defs = true
# This will raise an error
def process_data(data): # Error: Function is missing type annotations
return data + 1
# This is required instead
def process_data(data: int) -> int:
return data + 1
disallow_untyped_defs = false
# This is allowed
def process_data(data):
return data + 1
# This is also allowed
def process_data(data: int) -> int:
return data + 1
This project comes with recommended VS Code extensions to enhance your development experience. When you open this project in VS Code, you'll be prompted to install these extensions:
- Python (
ms-python.python
): Essential Python language support - Ruff (
charliermarsh.ruff
): Fast Python linter and formatter - MyPy (
matangover.mypy
): Static type checking for Python - Pylance (
ms-python.vscode-pylance
): Intelligent Python language support - Even Better TOML (
tamasfe.even-better-toml
): Improved TOML file support - YAML (
redhat.vscode-yaml
): YAML language support - GitLens (
eamodio.gitlens
): Enhanced Git integration - Code Spell Checker (
streetsidesoftware.code-spell-checker
): Catch common spelling mistakes
These extensions are configured to work seamlessly with the project's setup and will help maintain code quality standards. VS Code will automatically suggest installing these extensions when you open the project.