Post

Python Build and Deployment Tools Guide

A comprehensive guide to Python build tools (setuptools, pip, poetry, pyproject.toml) and deployment tooling (Docker, AWS SAM, Azure Functions CLI) with decision matrices for choosing the right tools.

Python Build and Deployment Tools Guide

Python has become one of the most popular programming languages for web development, data science, automation, and cloud-native applications. However, building and deploying Python applications presents unique challenges compared to other languages. This guide covers the essential Python build tools, deployment options, and provides decision matrices to help you choose the right approach for your project.

Python Build Tools Overview

Python’s build ecosystem has evolved significantly over the years, moving from traditional setup scripts to modern standardized packaging formats.

1. setuptools

setuptools is the most widely used packaging library for Python. It provides the foundation for building and distributing Python packages.

Key Features:

  • Creates source distributions (sdist) and wheel distributions (bdist_wheel)
  • Supports dependency management
  • Allows inclusion of non-Python files
  • Integrates with easy_install (legacy) and pip

Basic Usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# setup.py
from setuptools import setup, find_packages

setup(
    name="my-package",
    version="0.1.0",
    packages=find_packages(),
    install_requires=[
        "requests>=2.28.0",
        "numpy>=1.24.0"
    ],
    extras_require={
        "dev": ["pytest", "black"],
        "docs": ["sphinx"]
    },
    python_requires=">=3.8",
)

Building:

1
2
3
4
5
6
7
8
# Create source distribution
python setup.py sdist

# Create wheel distribution
python setup.py bdist_wheel

# Install in development mode
pip install -e .

2. pip

While not a build tool per se, pip is essential for installing and managing Python packages.

Key Features:

  • Installs packages from PyPI and other indexes
  • Manages virtual environments
  • Handles dependencies automatically

Common Commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Install package
pip install package-name

# Install from requirements file
pip install -r requirements.txt

# Upgrade package
pip install --upgrade package-name

# List installed packages
pip list

# Export dependencies
pip freeze > requirements.txt

3. poetry

poetry is a modern dependency management and packaging tool that simplifies the entire Python development workflow.

Key Features:

  • Unified dependency management (runtime + dev)
  • Lock file for reproducible builds
  • Built-in virtual environment management
  • Simple CLI interface

Installation:

1
2
3
4
5
# Using pipx (recommended)
pipx install poetry

# Or using curl
curl -sSL https://install.python-poetry.org | python3 -

Basic Workflow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Create new project
poetry new my-project

# Add dependency
poetry add requests

# Add dev dependency
poetry add --group dev pytest

# Build package
poetry build

# Install dependencies from lock file
poetry install

# Run command in virtual environment
poetry run python script.py

pyproject.toml example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[tool.poetry]
name = "my-package"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.28.0"

[tool.poetry.group.dev.dependencies]
pytest = "^7.0.0"
black = "^22.0.0"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

4. pyproject.toml

The pyproject.toml file is the standardized build configuration file introduced in PEP 518 and standardized in PEP 621.

Benefits:

  • Single source of truth for project metadata
  • Tool-agnostic format (works with setuptools, poetry, hatchling)
  • Human-readable TOML format

PEP 621 Standard Metadata:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[project]
name = "my-package"
version = "0.1.0"
description = "A sample Python package"
readme = "README.md"
requires-python = ">=3.8"
dependencies = [
    "requests>=2.28.0",
    "numpy>=1.24.0"
]

[project.optional-dependencies]
dev = ["pytest", "black"]
docs = ["sphinx"]

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

# For poetry compatibility
[tool.poetry]
name = "my-package"
version = "0.1.0"

Comparison of Build Tools

Featuresetuptoolspippoetry
Dependency ManagementBasicYes (with requirements.txt)Full (lock files, virtual envs)
Virtual EnvironmentNoManual or venvBuilt-in
Lock FilesNoManual with requirements.lockYes (poetry.lock)
Build SystemYesNoYes
Modern StandardsLegacyStandard installerPEP 621 compatible
Configuration FormatPythonCLI flags / INITOML

Deployment Tooling

Docker

Docker is the most popular containerization platform for deploying Python applications.

Basic Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Create non-root user
RUN useradd -m appuser
USER appuser

# Expose port
EXPOSE 8000

# Run application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Docker Compose for Multi-Service Apps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
    depends_on:
      - db
  
  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

AWS SAM (Serverless Application Model)

AWS SAM is a framework for building serverless applications on AWS.

SAM Template Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  PythonFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: app.lambda_handler
      Runtime: python3.11
      CodeUri: src/
      Timeout: 30
      MemorySize: 512
      Environment:
        Variables:
          ENVIRONMENT: production
          LOG_LEVEL: info
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /python-function
            Method: get

Outputs:
  ApiUrl:
    Description: URL of the deployed function
    Value: !Sub "https://${ServerlessApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/python-function"

Building and Deploying:

1
2
3
4
5
6
7
8
9
10
11
# Build the application
sam build

# Validate template
sam validate

# Deploy to AWS
sam deploy --guided

# Local testing
sam local start-api

Azure Functions CLI

Azure Functions allows you to run Python code serverlessly on Microsoft’s cloud platform.

Project Structure:

1
2
3
4
5
6
7
function-app/
├── host.json
├── local.settings.json
├── requirements.txt
└── functions/
    ├── __init__.py
    └── function_app.py

host.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  }
}

Python Function Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import azure.functions as func
import logging

app = func.FunctionApp()

@app.route(route="python-function")
def python_function(req: func.HttpRequest) -> func.HttpResponse:
    logging.info("Python HTTP trigger function processed a request.")
    
    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
            name = req_body.get('name')
        except ValueError:
            pass
    
    if name:
        return func.HttpResponse(f"Hello, {name}!")
    else:
        return func.HttpResponse(
            "Please pass a name on the query string or in the request body",
            status_code=400
        )

Building and Deploying:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Install dependencies
pip install -r requirements.txt

# Local development
func start

# Build for deployment
func azure functionapp publish <function-app-name>

# Or deploy using Azure CLI
az functionapp deployment source config-zip \
    --resource-group <rg-name> \
    --name <function-app-name> \
    --src <zip-file>

Decision Matrix: Choosing the Right Tools

By Project Type

Project TypeBuild ToolDeploymentNotes
Simple Scriptpip + venvDirect executionNo packaging needed
Library/Packagesetuptools or poetryPyPIUse poetry for modern projects
Web ApplicationpoetryDockerContainerize for consistency
MicroservicespoetryDocker/KubernetesManage multiple services
Serverless Functionpip (AWS) / poetry (local)AWS SAM/Azure FunctionsKeep packages small

By Scale

ScaleBuild ToolDeploymentRationale
Small (1-3 developers)poetryDockerEasy setup, good defaults
Medium (4-10 developers)poetry + CI/CDDocker/KubernetesTeam collaboration features
Large (10+ developers)poetry/pdm + monorepoKubernetesScalable workflows
EnterpriseCustom build systemCloud-nativeCompliance and governance

By Deployment Target

TargetBest Build ToolDeployment Method
Local Developmentpoetrypoetry install
Docker Containerpoetry/pdmMulti-stage builds
AWS LambdapipSAM or Zappa
Azure FunctionspipAzure CLI / VS Code
Herokupip + requirements.txtGit push
KubernetespoetryContainer images

Best Practices

1. Use Virtual Environments

1
2
3
4
5
6
7
8
9
10
11
# Create virtual environment
python -m venv .venv

# Activate (Linux/macOS)
source .venv/bin/activate

# Activate (Windows)
.\.venv\Scripts\activate

# Deactivate
deactivate

2. Pin Dependencies

Always pin production dependencies to ensure reproducible builds:

1
2
3
4
5
6
7
# Using poetry
poetry add requests==2.28.0

# Or specify exact versions in pyproject.toml
[tool.poetry.dependencies]
python = "^3.8"
requests = "2.28.0"  # Exact version

3. Use Multi-Stage Docker Builds

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Build stage
FROM python:3.11-slim AS builder

WORKDIR /build
COPY pyproject.toml poetry.lock ./
RUN pip install poetry && poetry build

# Production stage
FROM python:3.11-slim AS production

WORKDIR /app
COPY --from=builder /build/dist/*.whl .
RUN pip install *.whl

COPY . .
CMD ["python", "main.py"]

4. Optimize Dependency Installation

1
2
3
4
5
# Install dependencies before copying code for better caching
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

5. Use CI/CD Pipelines

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# GitHub Actions example
name: Python Package

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: |
          pip install poetry
          poetry install
      
      - name: Run tests
        run: |
          poetry run pytest
      
      - name: Build package
        run: |
          poetry build
      
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

Conclusion

Python’s build and deployment ecosystem offers multiple options to suit different project needs. The key is to:

  1. Start simple: Use pip and venv for basic projects
  2. Adopt modern tools: Transition to poetry for better dependency management
  3. Containerize: Use Docker for consistent deployments across environments
  4. Automate: Implement CI/CD pipelines for reliable releases

By following the guidelines in this post, you can build and deploy Python applications efficiently while maintaining scalability and maintainability.


Further Reading:

Related Tools to Explore:

  • hatch: Modern Python project manager
  • pipenv: Another dependency management tool
  • build: PEP 517 build backend
  • uv: Ultra-fast Python package installer
This post is licensed under CC BY 4.0 by the author.