Development Tools
Virtual Environments
venv (Built-in)
# Create virtual environment
python -m venv myenv
python3 -m venv myenv
# Activate environment
source myenv/bin/activate # Linux/Mac
myenv\Scripts\activate # Windows
# Deactivate environment
deactivate
# Remove environment
rm -rf myenv # Linux/Mac
rmdir /s myenv # Windows
conda
# Create environment
conda create -n myenv python=3.9
conda create -n myenv python=3.9 numpy pandas
# Activate/deactivate
conda activate myenv
conda deactivate
# List environments
conda env list
conda info --envs
# Remove environment
conda remove -n myenv --all
# Export/import environment
conda env export > environment.yml
conda env create -f environment.yml
pipenv
# Install pipenv
pip install pipenv
# Create environment and Pipfile
pipenv install
pipenv install requests numpy
# Activate shell
pipenv shell
# Install from Pipfile
pipenv install
# Install dev dependencies
pipenv install pytest --dev
# Generate requirements.txt
pipenv requirements > requirements.txt
# Run commands in environment
pipenv run python script.py
poetry
# Install poetry
curl -sSL https://install.python-poetry.org | python3 -
# Create new project
poetry new myproject
cd myproject
# Initialize in existing project
poetry init
# Add dependencies
poetry add requests
poetry add pytest --group dev
# Install dependencies
poetry install
# Activate shell
poetry shell
# Run commands
poetry run python script.py
# Build package
poetry build
# Update dependencies
poetry update
Package Management
pip Basics
# Install packages
pip install requests
pip install requests==2.28.0
pip install 'requests>=2.25.0,<3.0.0'
# Install from requirements
pip install -r requirements.txt
# Install in development mode
pip install -e .
# Upgrade packages
pip install --upgrade requests
pip install --upgrade pip
# List installed packages
pip list
pip list --outdated
# Show package info
pip show requests
# Uninstall packages
pip uninstall requests
pip uninstall -r requirements.txt
requirements.txt
# requirements.txt example
requests==2.28.1
numpy>=1.21.0
pandas~=1.4.0
flask
django>=3.2,<4.0
# Development dependencies
pytest>=6.0.0
black
flake8
# Optional dependencies
psycopg2-binary; sys_platform != "win32"
pywin32; sys_platform == "win32"
# From git repository
git+https://github.com/user/repo.git
git+https://github.com/user/repo.git@v1.0.0
setup.py
from setuptools import setup, find_packages
setup(
name="mypackage",
version="0.1.0",
description="A sample Python package",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
author="Your Name",
author_email="your.email@example.com",
url="https://github.com/yourusername/mypackage",
packages=find_packages(),
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
],
python_requires=">=3.8",
install_requires=[
"requests>=2.25.0",
"numpy>=1.21.0",
],
extras_require={
"dev": ["pytest>=6.0.0", "black", "flake8"],
"docs": ["sphinx", "sphinx-rtd-theme"],
},
entry_points={
"console_scripts": [
"mycommand=mypackage.cli:main",
],
},
)
pyproject.toml
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mypackage"
version = "0.1.0"
description = "A sample Python package"
readme = "README.md"
authors = [{name = "Your Name", email = "your.email@example.com"}]
license = {text = "MIT"}
classifiers = [
"Development Status :: 3 - Alpha",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
]
requires-python = ">=3.8"
dependencies = [
"requests>=2.25.0",
"numpy>=1.21.0",
]
[project.optional-dependencies]
dev = ["pytest>=6.0.0", "black", "flake8"]
docs = ["sphinx", "sphinx-rtd-theme"]
[project.scripts]
mycommand = "mypackage.cli:main"
[tool.setuptools.packages.find]
where = ["src"]
Code Formatting
black
# Install black
pip install black
# Format files
black script.py
black src/
black .
# Check what would be changed
black --check script.py
black --diff script.py
# Configuration in pyproject.toml
[tool.black]
line-length = 88
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
(
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
)
'''
autopep8
# Install autopep8
pip install autopep8
# Format files
autopep8 --in-place script.py
autopep8 --in-place --recursive src/
# Show differences
autopep8 --diff script.py
# Aggressive formatting
autopep8 --aggressive --aggressive --in-place script.py
# Configuration in setup.cfg
[tool:autopep8]
max_line_length = 88
ignore = E501,W6
in-place = true
recursive = true
aggressive = 3
yapf
# Install yapf
pip install yapf
# Format files
yapf -i script.py
yapf -ir src/
# Show differences
yapf -d script.py
# Configuration in .style.yapf
[style]
based_on_style = pep8
spaces_before_comment = 2
split_before_logical_operator = true
column_limit = 88
Linting
flake8
# Install flake8
pip install flake8
# Run linting
flake8 script.py
flake8 src/
flake8 .
# Configuration in setup.cfg
[flake8]
max-line-length = 88
ignore = E203, E266, E501, W503
max-complexity = 10
exclude = .git,__pycache__,docs/source/conf.py,old,build,dist
pylint
# Install pylint
pip install pylint
# Run linting
pylint script.py
pylint src/
# Generate config file
pylint --generate-rcfile > pylintrc
# Configuration in pylintrc
[MASTER]
extension-pkg-whitelist=
[MESSAGES CONTROL]
disable=C0114,C0115,C0116
[FORMAT]
max-line-length=88
[DESIGN]
max-args=7
max-locals=15
max-returns=6
max-branches=12
max-statements=50
mypy (Type Checking)
# Install mypy
pip install mypy
# Run type checking
mypy script.py
mypy src/
# Configuration in mypy.ini
[mypy]
python_version = 3.8
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
[mypy-requests.*]
ignore_missing_imports = True
Type Hints Examples
from typing import List, Dict, Optional, Union, Callable
# Basic type hints
def greet(name: str) -> str:
return f"Hello, {name}!"
# Collections
def process_items(items: List[str]) -> Dict[str, int]:
return {item: len(item) for item in items}
# Optional and Union types
def find_user(user_id: int) -> Optional[Dict[str, str]]:
# Returns user dict or None
return {"name": "John", "email": "john@example.com"}
def parse_value(value: Union[str, int]) -> int:
if isinstance(value, str):
return int(value)
return value
# Function types
def apply_function(func: Callable[[int], int], value: int) -> int:
return func(value)
# Classes
class User:
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
def get_info(self) -> Dict[str, Union[str, int]]:
return {"name": self.name, "age": self.age}
Testing Frameworks
pytest
# Install pytest
pip install pytest
# Run tests
pytest # Run all tests
pytest test_module.py # Run specific file
pytest -v # Verbose output
pytest -x # Stop after first failure
pytest --tb=short # Short traceback format
# Run with coverage
pip install pytest-cov
pytest --cov=src --cov-report=html
pytest Examples
# test_calculator.py
import pytest
def add(a, b):
return a + b
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
# Basic test
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
# Test with exception
def test_divide_by_zero():
with pytest.raises(ValueError):
divide(10, 0)
# Parametrized test
@pytest.mark.parametrize("a,b,expected", [
(2, 3, 5),
(-1, 1, 0),
(0, 0, 0),
])
def test_add_parametrized(a, b, expected):
assert add(a, b) == expected
# Fixtures
@pytest.fixture
def sample_data():
return [1, 2, 3, 4, 5]
def test_with_fixture(sample_data):
assert len(sample_data) == 5
assert sum(sample_data) == 15
# Setup and teardown
@pytest.fixture
def temp_file():
# Setup
with open("temp.txt", "w") as f:
f.write("test data")
yield "temp.txt"
# Teardown
import os
os.remove("temp.txt")
unittest
import unittest
class TestCalculator(unittest.TestCase):
def setUp(self):
"""Run before each test method"""
self.calc = Calculator()
def tearDown(self):
"""Run after each test method"""
pass
def test_add(self):
result = self.calc.add(2, 3)
self.assertEqual(result, 5)
def test_divide_by_zero(self):
with self.assertRaises(ValueError):
self.calc.divide(10, 0)
def test_is_positive(self):
self.assertTrue(self.calc.is_positive(5))
self.assertFalse(self.calc.is_positive(-5))
@unittest.skip("Not implemented yet")
def test_future_feature(self):
pass
if __name__ == '__main__':
unittest.main()
doctest
def factorial(n):
"""
Calculate factorial of n.
>>> factorial(0)
1
>>> factorial(1)
1
>>> factorial(5)
120
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: Factorial is not defined for negative numbers
"""
if n < 0:
raise ValueError("Factorial is not defined for negative numbers")
if n == 0 or n == 1:
return 1
return n * factorial(n - 1)
if __name__ == "__main__":
import doctest
doctest.testmod()
Documentation
Docstrings
def calculate_area(length: float, width: float) -> float:
"""
Calculate the area of a rectangle.
Args:
length (float): The length of the rectangle
width (float): The width of the rectangle
Returns:
float: The area of the rectangle
Raises:
ValueError: If length or width is negative
Examples:
>>> calculate_area(5, 3)
15.0
>>> calculate_area(2.5, 4.0)
10.0
"""
if length < 0 or width < 0:
raise ValueError("Length and width must be non-negative")
return length * width
class Rectangle:
"""
A class to represent a rectangle.
Attributes:
length (float): The length of the rectangle
width (float): The width of the rectangle
Methods:
area(): Calculate the area of the rectangle
perimeter(): Calculate the perimeter of the rectangle
"""
def __init__(self, length: float, width: float):
"""
Initialize a Rectangle object.
Args:
length (float): The length of the rectangle
width (float): The width of the rectangle
"""
self.length = length
self.width = width
Sphinx
# Install Sphinx
pip install sphinx sphinx-rtd-theme
# Create documentation
sphinx-quickstart docs
# Build documentation
cd docs
make html # Generate HTML
make latexpdf # Generate PDF
make epub # Generate EPUB
# Configuration in conf.py
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.napoleon',
]
html_theme = 'sphinx_rtd_theme'
Sphinx Configuration
# docs/conf.py
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
project = 'My Project'
author = 'Your Name'
release = '0.1.0'
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.napoleon',
'sphinx.ext.intersphinx',
]
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
# Napoleon settings
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = False
napoleon_include_private_with_doc = False
mkdocs
# Install mkdocs
pip install mkdocs mkdocs-material
# Create new project
mkdocs new my-project
cd my-project
# Serve locally
mkdocs serve
# Build site
mkdocs build
# Deploy to GitHub Pages
mkdocs gh-deploy
mkdocs.yml
site_name: My Project
site_description: A sample Python project
site_author: Your Name
site_url: https://yourusername.github.io/my-project/
theme:
name: material
palette:
primary: blue
accent: light blue
nav:
- Home: index.md
- User Guide: user-guide.md
- API Reference: api.md
plugins:
- search
- mkdocstrings
markdown_extensions:
- admonition
- codehilite
- pymdownx.superfences
Version Control Integration
Git Hooks
# Create pre-commit hook
touch .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
# .git/hooks/pre-commit
#!/bin/bash
echo "Running pre-commit checks..."
# Run linting
flake8 src/
if [ $? -ne 0 ]; then
echo "Linting failed. Commit aborted."
exit 1
fi
# Run tests
pytest
if [ $? -ne 0 ]; then
echo "Tests failed. Commit aborted."
exit 1
fi
echo "Pre-commit checks passed!"
pre-commit Framework
# Install pre-commit
pip install pre-commit
# Create .pre-commit-config.yaml
touch .pre-commit-config.yaml
# Install hooks
pre-commit install
# Run on all files
pre-commit run --all-files
.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: 22.3.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 4.0.1
hooks:
- id: flake8
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.950
hooks:
- id: mypy
GitHub Actions
# .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]
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 -r requirements.txt
pip install -r requirements-dev.txt
- name: Run linting
run: |
flake8 src/
black --check src/
- name: Run tests
run: |
pytest --cov=src --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
IDE Configuration
VSCode Settings
// .vscode/settings.json
{
"python.defaultInterpreterPath": "./venv/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true,
"python.formatting.provider": "black",
"python.formatting.blackArgs": ["--line-length", "88"],
"python.testing.pytestEnabled": true,
"python.testing.pytestArgs": ["tests/"],
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
}
PyCharm Configuration
# File -> Settings -> Tools -> External Tools
# Add Black formatter:
# Name: Black
# Description: Black formatter
# Program: $PyInterpreterDirectory$/black
# Arguments: $FilePath$
# Working directory: $ProjectFileDir$
# Add Flake8 linter:
# Name: Flake8
# Description: Flake8 linter
# Program: $PyInterpreterDirectory$/flake8
# Arguments: $FilePath$
# Working directory: $ProjectFileDir$
Debugging
# Using pdb
import pdb
def problematic_function(x):
pdb.set_trace() # Breakpoint
result = x * 2
return result
# Using logging for debugging
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def debug_function(x):
logger.debug(f"Input value: {x}")
result = x * 2
logger.debug(f"Result: {result}")
return result
# Using ipdb (enhanced pdb)
import ipdb
ipdb.set_trace()
Project Structure
Standard Project Layout
myproject/
├── src/
│ └── myproject/
│ ├── __init__.py
│ ├── main.py
│ ├── utils.py
│ └── models/
│ ├── __init__.py
│ └── user.py
├── tests/
│ ├── __init__.py
│ ├── test_main.py
│ └── test_utils.py
├── docs/
│ ├── index.md
│ └── api.md
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .pre-commit-config.yaml
├── README.md
├── requirements.txt
├── requirements-dev.txt
├── setup.py
└── pyproject.toml
Package Structure
mypackage/
├── mypackage/
│ ├── __init__.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── engine.py
│ │ └── utils.py
│ ├── api/
│ │ ├── __init__.py
│ │ └── endpoints.py
│ └── cli/
│ ├── __init__.py
│ └── commands.py
├── tests/
│ ├── __init__.py
│ ├── test_core/
│ │ ├── __init__.py
│ │ └── test_engine.py
│ └── test_api/
│ ├── __init__.py
│ └── test_endpoints.py
├── docs/
├── scripts/
├── requirements/
│ ├── base.txt
│ ├── dev.txt
│ └── prod.txt
└── tox.ini
Continuous Integration
tox Configuration
# tox.ini
[tox]
envlist = py38,py39,py310,flake8,black
[testenv]
deps =
pytest
pytest-cov
commands = pytest {posargs}
[testenv:flake8]
deps = flake8
commands = flake8 src/ tests/
[testenv:black]
deps = black
commands = black --check src/ tests/
[testenv:docs]
deps =
sphinx
sphinx-rtd-theme
commands = sphinx-build -b html docs/ docs/_build/html
Makefile
# Makefile
.PHONY: install test lint format docs clean
install:
pip install -r requirements.txt
pip install -r requirements-dev.txt
test:
pytest --cov=src --cov-report=html
lint:
flake8 src/ tests/
mypy src/
format:
black src/ tests/
isort src/ tests/
docs:
cd docs && make html
clean:
rm -rf build/
rm -rf dist/
rm -rf *.egg-info/
rm -rf htmlcov/
find . -type d -name __pycache__ -exec rm -rf {} +
find . -type f -name "*.pyc" -delete
Requirements Files
# requirements/base.txt
requests>=2.25.0
click>=8.0.0
pydantic>=1.8.0
# requirements/dev.txt
-r base.txt
pytest>=6.0.0
pytest-cov>=2.10.0
black>=22.0.0
flake8>=4.0.0
mypy>=0.950
pre-commit>=2.15.0
# requirements/prod.txt
-r base.txt
gunicorn>=20.1.0
Performance & Profiling
cProfile
import cProfile
import pstats
def slow_function():
# Simulate slow operation
import time
time.sleep(1)
return sum(range(1000000))
# Profile function
cProfile.run('slow_function()', 'profile_output.prof')
# Analyze results
stats = pstats.Stats('profile_output.prof')
stats.sort_stats('cumulative')
stats.print_stats(10)
line_profiler
# Install line_profiler
pip install line_profiler
# Add @profile decorator to functions
@profile
def my_function():
# Your code here
pass
# Run profiler
kernprof -l -v script.py
memory_profiler
# Install memory_profiler
pip install memory_profiler
# Add @profile decorator
@profile
def my_function():
# Your code here
pass
# Run profiler
python -m memory_profiler script.py
Quick Commands Reference
Development Workflow
# Setup new project
mkdir myproject && cd myproject
python -m venv venv
source venv/bin/activate # Linux/Mac
pip install --upgrade pip
# Install development tools
pip install black flake8 pytest mypy pre-commit
# Initialize git
git init
git add .
git commit -m "Initial commit"
# Setup pre-commit
pre-commit install
# Run quality checks
black .
flake8 .
mypy .
pytest
# Build and distribute
python setup.py sdist bdist_wheel
twine upload dist/*
Essential Commands
# Virtual environment
python -m venv venv && source venv/bin/activate
# Install package in development mode
pip install -e .
# Run tests with coverage
pytest --cov=src --cov-report=html
# Format and lint
black . && flake8 . && mypy .
# Build documentation
sphinx-build -b html docs/ docs/_build/html
# Clean build artifacts
rm -rf build/ dist/ *.egg-info/
This comprehensive cheatsheet covers the essential Python development tools and workflows. Use it as a reference for setting up robust Python projects with proper tooling, testing, and documentation.