Skip to main content

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.