Modern Python Development Practices

From the archive: a general guide from March 2024, tidied and kept for reference.

Modern Python Development Practices

Introduction

Python tooling has changed a lot in recent years. This guide covers the practices and tools worth setting up on a Python project: the development environment, code quality and testing, dependency management, newer language features and a few general habits.

Development Environment Setup

Tools

  • VS Code or PyCharm: editors with good Python support
  • Python 3.11 or newer: a supported release; 3.11 was noticeably faster than the versions before it
  • Git: version control

# Setting up a new Python project
python -m venv venv
source venv/bin/activate  # or venv\Scripts\activate on Windows
pip install --upgrade pip
                            

Code Quality and Testing

Testing and Quality Tools

  • Pytest: testing framework
  • Black: code formatter
  • Flake8: style guide enforcement
  • Ruff: a newer, much faster linter and formatter that can replace both Black and Flake8
  • MyPy: static type checking

# A function and its pytest test
def calculate_total(numbers: list[int]) -> int:
    return sum(numbers)

def test_calculate_total():
    assert calculate_total([1, 2, 3]) == 6
    assert calculate_total([]) == 0
                            

Dependency Management

Pin your dependencies so every install gets the same versions, and keep each project's packages separate. The usual tools:

Tools and Practices

  • Poetry: dependency management and packaging, configured in pyproject.toml
  • pip-tools: compiles a fully pinned requirements.txt from your top-level dependencies
  • uv: a newer, fast tool that covers installing, locking and virtual environments in one command
  • Virtual environments: one per project, so packages do not leak between projects

Modern Python Features

Type Hints


# Python 3.10+ syntax: "dict | None" replaces Optional[dict]
def get_user(user_id: int) -> dict | None:
    ...
                                    

Async/Await


import aiohttp

async def fetch_data(url: str) -> dict:
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.json()
                                    

Best Practices and Tips

Code Organization

  • Use clear directory structure
  • Follow package naming conventions
  • Implement proper logging

Performance

  • Use built-in data structures appropriately
  • Implement caching when needed
  • Profile code for optimizations