From Zero to Production: A Complete Python Setup Guide
You've got a brilliant Python project idea. You open your editor, create a new file called main.py
, and start coding. Fast forward six months: your "simple script" has grown into a complex application with multiple files, dependencies, and contributors. Now you're drowning in import errors, inconsistent code styles, and deployment headaches.
Sound familiar? Most Python projects start this way, but they don't have to stay that way.
The difference between a weekend script and production-ready software isn't just the code—it's the foundation. Professional Python projects follow established patterns that prevent problems before they happen. Today, we're building that foundation from scratch.
By the end of this guide, you'll have a bulletproof project template that handles everything from code quality to deployment. We'll cover project structure, automated testing, continuous integration, and all the tooling that makes the difference between "it works on my machine" and "it works everywhere."
By setting a strong foundation for your Python project, you save headaches and plan for success.
Foundation: Project Structure That Scales
The first decision that will save you countless hours later is getting your project structure right from day one. A well-organized project isn't just neat—it's functional.
The Anatomy of a Professional Python Project
Here's what a production-ready Python project looks like:
my-awesome-project/
├── src/
│ └── my_awesome_project/
│ ├── __init__.py
│ ├── main.py
│ └── utils/
│ ├── __init__.py
│ └── helpers.py
├── tests/
│ ├── __init__.py
│ ├── test_main.py
│ └── test_utils/
│ └── test_helpers.py
├── docs/
│ └── README.md
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── pyproject.toml
├── README.md
└── requirements.txt
This structure follows Python packaging best practices and scales beautifully. The src/
directory keeps your source code organized and prevents import issues. Tests mirror your source structure, making it easy to find and maintain them. Configuration files live at the root where tools expect them.
Essential Files You Can't Skip
pyproject.toml is your project's control center. It defines dependencies, build settings, and tool configurations in one place:
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-awesome-project"
version = "0.1.0"
description = "A project that does awesome things"
authors = [{name = "Your Name", email = "you@example.com"}]
dependencies = [
"requests>=2.25.0",
"click>=8.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=6.0",
"black>=22.0",
"isort>=5.0",
"ruff>=0.1.0",
]
README.md is your project's front door. Include installation instructions, usage examples, and contribution guidelines. This isn't just documentation—it's marketing for your code.
Virtual Environment Setup
Never, ever work on Python projects without a virtual environment. Virtual environments isolate your project's dependencies, preventing the "it works on my machine but breaks everywhere else" problem.
For modern Python development, we recommend using either Poetry or uv—both are significant upgrades over the traditional pip + venv approach.
Poetry is the established choice for dependency management:
poetry init
poetry add requests click
poetry add --group dev pytest black isort ruff
poetry install
Poetry handles virtual environments automatically and creates lock files for reproducible builds. It's mature, well-documented, and integrates smoothly with most development workflows.
uv is the new speed demon of Python tooling:
uv init
uv add requests click
uv add --dev pytest black isort ruff
uv sync
uv is incredibly fast (written in Rust) and can replace pip, pip-tools, and virtualenv with a single tool. It's rapidly gaining adoption for its performance and simplicity.
Both tools eliminate the manual virtual environment management that trips up many developers. Choose Poetry if you want stability and extensive ecosystem support, or uv if you prioritize speed and want to try the latest tooling.
Code Quality Arsenal: Your First Line of Defense
Code quality tools are like spell-check for programmers. They catch errors, enforce consistency, and make your code more readable. Setting them up early prevents technical debt from accumulating.
The Linting and Formatting Trio
Black formats your code automatically, eliminating style debates:
# In pyproject.toml
[tool.black]
line-length = 88
target-version = ['py38']
isort organizes your imports consistently:
[tool.isort]
profile = "black"
multi_line_output = 3
line_length = 88
Ruff is the speed demon of Python linting, replacing multiple tools:
[tool.ruff]
target-version = "py38"
line-length = 88
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
]
Pre-commit Hooks: Quality Gates That Actually Work
Pre-commit hooks run these tools automatically before every commit, catching issues before they enter your repository. Install pre-commit and create a .pre-commit-config.yaml
:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.1.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Install the hooks with:
pre-commit install
Now every commit automatically runs these checks. If something fails, the commit is blocked until you fix it. This prevents bad code from ever entering your repository.
Type Checking with mypy
Type hints make Python code more readable and catch bugs before runtime. Start with basic mypy configuration:
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
Add type hints gradually:
def process_data(input_file: str, output_file: str) -> int:
"""Process data and return number of records processed."""
# Your code here
return record_count
Testing Strategy: Confidence Through Automation
Tests aren't just about catching bugs—they're about confidence. Good tests let you refactor fearlessly, knowing you'll catch any regressions.
Test Structure and Organization
Mirror your source code structure in your tests:
src/my_project/utils/helpers.py
tests/test_utils/test_helpers.py
Use pytest for its simple syntax and powerful features:
# tests/test_main.py
import pytest
from my_awesome_project.main import process_data
def test_process_data_with_valid_input():
result = process_data("input.txt", "output.txt")
assert result > 0
def test_process_data_with_invalid_file():
with pytest.raises(FileNotFoundError):
process_data("nonexistent.txt", "output.txt")
Coverage Tracking
Coverage tells you which parts of your code are tested. Add coverage.py to your dev dependencies and configure it:
[tool.coverage.run]
source = ["src"]
omit = ["*/tests/*"]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
]
Run tests with coverage:
pytest --cov=src --cov-report=html
Aim for 80-90% coverage, but remember: 100% coverage doesn't mean 100% bug-free.
Continuous Integration: Automating Your Quality Checks
CI/CD automates everything we've set up locally, running tests and quality checks on every push. GitHub Actions makes this straightforward:
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8, 3.9, '3.10', 3.11]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: Run pre-commit
run: pre-commit run --all-files
- name: Run tests
run: pytest --cov=src --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
This workflow runs on multiple Python versions, ensuring your code works everywhere. The matrix build catches version-specific issues early.
Quality Gates and Branch Protection
Set up branch protection rules in GitHub:
Require status checks to pass
Require up-to-date branches
Require review from code owners
This prevents broken code from reaching your main branch.
Documentation: Making Your Code Accessible
Good documentation is like a welcome mat for your code. It invites people in and helps them get started quickly.
README.md Essentials
Your README should answer three questions immediately:
What does this project do?
How do I use it?
How do I contribute?
Here's a template:
# My Awesome Project
Brief description of what your project does and why it's useful.
## Installation
```bash
pip install my-awesome-project
Quick Start
from my_awesome_project import main
result = main.process_data("input.txt")
print(f"Processed {result} records")
Contributing
Fork the repository
Create a feature branch
Make your changes
Run tests:
pytest
Submit a pull request
### API Documentation
For libraries, generate API documentation from docstrings using Sphinx or mkdocs:
```python
def process_data(input_file: str, chunk_size: int = 1000) -> int:
"""Process data from input file in chunks.
Args:
input_file: Path to the input file
chunk_size: Number of records to process at once
Returns:
Total number of records processed
Raises:
FileNotFoundError: If input file doesn't exist
ValueError: If chunk_size is less than 1
"""
Dependency Management: Keeping Things Secure and Updated
Dependencies are both a blessing and a curse. They save development time but introduce security risks and compatibility issues.
Modern Dependency Management
Pin your dependencies to specific versions in production:
# requirements.txt
requests==2.28.2
click==8.1.3
But allow flexibility in development:
# pyproject.toml
dependencies = [
"requests>=2.25.0,<3.0.0",
"click>=8.0.0,<9.0.0",
]
Security and Updates
Use tools like safety
to scan for known vulnerabilities:
pip install safety
safety check
Set up Dependabot in GitHub to automatically create PRs for dependency updates:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
Deployment Preparation: Production-Ready Code
Getting your code ready for production involves more than just making it work—it needs to be observable, configurable, and secure.
Configuration Management
Never hardcode configuration. Use environment variables with sensible defaults:
import os
from dataclasses import dataclass
@dataclass
class Config:
database_url: str = os.getenv("DATABASE_URL", "sqlite:///default.db")
debug: bool = os.getenv("DEBUG", "false").lower() == "true"
log_level: str = os.getenv("LOG_LEVEL", "INFO")
config = Config()
Logging Setup for Production
Structured logging helps you debug issues in production:
import logging
import sys
def setup_logging(level: str = "INFO"):
logging.basicConfig(
level=getattr(logging, level.upper()),
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler("app.log")
]
)
logger = logging.getLogger(__name__)
Docker Containerization
Container your application for consistent deployments:
FROM python:3.11-slim
WORKDIR /app
COPY pyproject.toml .
RUN pip install -e .
COPY src/ src/
EXPOSE 8000
CMD ["python", "-m", "my_awesome_project"]
Use multi-stage builds for smaller production images:
# Build stage
FROM python:3.11 as builder
WORKDIR /app
COPY pyproject.toml .
RUN pip install build && python -m build
# Production stage
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /app/dist/*.whl .
RUN pip install *.whl
CMD ["my-awesome-project"]
Putting It All Together: The Complete Workflow
Here's your step-by-step checklist for setting up any new Python project:
Initial Setup
Create project directory and navigate to it
Initialize git repository:
git init
Create virtual environment:
python -m venv venv
Activate virtual environment:
source venv/bin/activate
Create basic project structure (src/, tests/, docs/)
Configuration Files
Create
pyproject.toml
with project metadata and dependenciesCreate
.gitignore
for Python projectsSet up
.pre-commit-config.yaml
Configure tools (Black, isort, Ruff, mypy) in
pyproject.toml
Quality Setup
Install development dependencies:
pip install -e ".[dev]"
Install pre-commit hooks:
pre-commit install
Run initial formatting:
black src/ tests/
Run initial linting:
ruff src/ tests/
CI/CD Setup
Create
.github/workflows/ci.yml
Set up branch protection rules
Configure Dependabot for automatic updates
Documentation
Write comprehensive README.md
Add docstrings to all public functions
Set up documentation generation (optional)
Testing
Write initial tests
Configure coverage tracking
Set coverage thresholds
This checklist transforms into a reusable template. Create a GitHub template repository with all these files configured, then use it for every new project.
Advanced Automation with Zumbro
While this manual setup gives you complete control, it's also a lot to remember and maintain. This is exactly why we built Zumbro—our GitHub app that automates many of these quality checks and setup steps right in your workflow.
Zumbro can automatically apply the linting rules, formatting standards, and quality checks we've discussed, without you having to configure each tool individually. It's like having a DevOps expert on your team who never forgets to run the checks.
Conclusion: Your Foundation for Success
Setting up a Python project properly takes effort upfront, but it pays dividends throughout the project's lifetime. Every hour you spend on this foundation saves you days of debugging, deployment issues, and team coordination problems later.
The tools and practices we've covered—from project structure to CI/CD—aren't just nice-to-haves. They're the difference between code that works in isolation and software that works reliably for everyone, everywhere.
Start your next Python project with this foundation. Your future self (and your teammates) will thank you.
Ready to level up your Python projects? Download our complete project template from GitHub and start building software that's production-ready from day one.