Daily Tech Brief

Top startup stories in your inbox

Subscribe Free

© 2026 rakrisi Daily

Python Package Distribution - Sharing Your Code

Python Package Distribution: Sharing Your Code

Welcome to Package Distribution! Think of this as publishing your book - making your code available for others to install and use. Python has excellent tools for creating distributable packages.

Why Distribute Packages?

Package distribution allows you:

  • Share code with other developers
  • Install easily with pip install
  • Manage dependencies automatically
  • Version control releases
  • Build communities around your code

Distribution Methods

1. Source Distribution (sdist)

Contains source code that can be installed on any platform:

my_package-1.0.0.tar.gz
├── my_package-1.0.0/
│   ├── setup.py
│   ├── my_package/
│   │   ├── __init__.py
│   │   └── core.py
│   └── README.md

2. Built Distribution (wheel)

Pre-compiled binary distribution (faster installation):

my_package-1.0.0-py3-none-any.whl
├── my_package/
│   ├── __init__.py
│   └── core.py
└── metadata/

3. Platform Wheels

Platform-specific binaries for compiled extensions:

my_package-1.0.0-cp39-cp39-win_amd64.whl  # Windows
my_package-1.0.0-cp39-cp39-macosx_10_9_x86_64.whl  # macOS
my_package-1.0.0-cp39-cp39-linux_x86_64.whl  # Linux

Creating Your First Package

Step 1: Project Structure

my_package/
├── src/
│   └── my_package/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   └── test_core.py
├── docs/
├── pyproject.toml
├── README.md
├── LICENSE
└── .gitignore

Step 2: pyproject.toml Configuration

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

[project]
name = "my-package"
version = "1.0.0"
description = "A useful Python package"
readme = "README.md"
license = {file = "LICENSE"}
requires-python = ">=3.8"
authors = [
    {name = "Your Name", email = "your.email@example.com"},
]
maintainers = [
    {name = "Your Name", email = "your.email@example.com"},
]
keywords = ["useful", "package", "python"]
classifiers = [
    "Development Status :: 4 - Beta",
    "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",
    "Programming Language :: Python :: 3.11",
]
dependencies = [
    "requests>=2.25.0",
    "click>=8.0.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.0",
    "pytest-cov>=4.0",
    "black>=22.0",
    "flake8>=5.0",
    "mypy>=1.0",
]
docs = [
    "sphinx>=5.0",
    "sphinx-rtd-theme>=1.0",
]
test = [
    "pytest>=7.0",
    "pytest-cov>=4.0",
]

[project.scripts]
my-command = "my_package.cli:main"

[project.urls]
Homepage = "https://github.com/yourusername/my-package"
Documentation = "https://my-package.readthedocs.io/"
Repository = "https://github.com/yourusername/my-package.git"
Issues = "https://github.com/yourusername/my-package/issues"
Changelog = "https://github.com/yourusername/my-package/blob/main/CHANGELOG.md"

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
my_package = ["*.txt", "*.md"]

[tool.black]
line-length = 88
target-version = ['py38']

[tool.pytest.ini_options]
minversion = "7.0"
addopts = "-ra -q --cov=my_package"
testpaths = ["tests"]

[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true

Step 3: Package Code

# src/my_package/__init__.py
"""My Package - A useful Python package."""

__version__ = "1.0.0"
__author__ = "Your Name"
__email__ = "your.email@example.com"

from .core import main_function
from .utils import helper_function

__all__ = ["main_function", "helper_function"]
# src/my_package/core.py
"""Core functionality."""

def main_function(name: str) -> str:
    """Return a greeting message."""
    return f"Hello, {name}!"

def calculate_sum(*args: float) -> float:
    """Calculate the sum of numbers."""
    return sum(args)
# src/my_package/utils.py
"""Utility functions."""

def helper_function(text: str) -> str:
    """Convert text to uppercase."""
    return text.upper()

def validate_email(email: str) -> bool:
    """Validate email address format."""
    import re
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))

Step 4: README.md

# My Package

[![PyPI version](https://badge.fury.io/py/my-package.svg)](https://pypi.org/project/my-package/)
[![Python versions](https://img.shields.io/pypi/pyversions/my-package.svg)](https://pypi.org/project/my-package/)
[![License](https://img.shields.io/pypi/l/my-package.svg)](https://pypi.org/project/my-package/)

A useful Python package that provides core functionality and utilities.

## Installation

```bash
pip install my-package

Usage

import my_package

# Use main function
result = my_package.main_function("World")
print(result)  # "Hello, World!"

# Use utilities
upper = my_package.helper_function("hello")
print(upper)  # "HELLO"

Development

# Clone the repository
git clone https://github.com/yourusername/my-package.git
cd my-package

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Format code
black src/ tests/

# Type checking
mypy src/

Contributing

Contributions are welcome! Please see our Contributing Guide for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.


### Step 5: Build the Package

```bash
# Install build tools
pip install build

# Build distributions
python -m build

# This creates:
# dist/
# ├── my_package-1.0.0.tar.gz      # Source distribution
# └── my_package-1.0.0-py3-none-any.whl  # Built distribution

Step 6: Test Installation

# Test in a virtual environment
python -m venv test_env
test_env\Scripts\activate  # Windows
# source test_env/bin/activate  # macOS/Linux

# Install from local build
pip install dist/my_package-1.0.0-py3-none-any.whl

# Test the package
python -c "import my_package; print(my_package.main_function('test'))"

Publishing to PyPI

TestPyPI (Testing)

# Install twine for uploading
pip install twine

# Upload to TestPyPI
twine upload --repository testpypi dist/*

# Install from TestPyPI
pip install --index-url https://test.pypi.org/simple/ my-package

Production PyPI

# Upload to real PyPI
twine upload dist/*

# Install from PyPI
pip install my-package

Advanced Distribution Features

Entry Points (Console Scripts)

[project.scripts]
my-command = "my_package.cli:main"
hello-world = "my_package.cli:hello_command"
# src/my_package/cli.py
import click

@click.group()
def main():
    """My Package CLI"""
    pass

@main.command()
@click.argument('name')
def hello(name):
    """Say hello to someone."""
    from .core import main_function
    click.echo(main_function(name))

@main.command()
def version():
    """Show package version."""
    from . import __version__
    click.echo(f"My Package v{__version__}")

if __name__ == "__main__":
    main()

Plugin System with Entry Points

[project.entry-points."my_package.plugins"]
csv_plugin = "my_package.plugins.csv_plugin:CSVPlugin"
json_plugin = "my_package.plugins.json_plugin:JSONPlugin"
# src/my_package/plugins.py
import importlib.metadata

def load_plugins():
    """Load all registered plugins."""
    plugins = {}
    for entry_point in importlib.metadata.entry_points(group="my_package.plugins"):
        plugin_class = entry_point.load()
        plugins[entry_point.name] = plugin_class()
    return plugins

Including Data Files

[tool.setuptools.package-data]
my_package = ["data/*.json", "templates/*.html", "static/*"]

Namespace Packages

For packages split across distributions:

# No __init__.py in namespace root
my_namespace/
├── package_a/
│   ├── __init__.py
│   └── ...
└── package_b/
    ├── __init__.py
    └── ...

Version Management

Version Schemes

  • Semantic Versioning: MAJOR.MINOR.PATCH (1.2.3)
  • Calendar Versioning: YEAR.MONTH.PATCH (2023.5.1)
  • Development Versions: 1.0.0.dev0, 1.0.0a1

Automatic Versioning

# src/my_package/__init__.py
try:
    from importlib.metadata import version
    __version__ = version("my-package")
except ImportError:
    # Fallback for older Python
    __version__ = "unknown"

Version Files

# _version.py
__version__ = "1.0.0"

# __init__.py
from ._version import __version__

Dependency Management

Core vs Optional Dependencies

[project]
dependencies = [
    "requests>=2.25.0",  # Always required
]

[project.optional-dependencies]
pandas = ["pandas>=1.3.0"]  # Optional
plotting = ["matplotlib>=3.5.0", "seaborn>=0.11.0"]
all = ["my-package[pandas,plotting]"]  # All optional

Conditional Dependencies

# setup.py (legacy)
import sys

install_requires = ["requests>=2.25.0"]
if sys.version_info < (3, 9):
    install_requires.append("importlib-metadata")

setup(
    name="my-package",
    install_requires=install_requires,
    # ...
)

Testing Distributions

tox for Multi-Environment Testing

# tox.ini
[tox]
envlist = py38, py39, py310, py311

[testenv]
deps = pytest
commands = pytest tests/
# Run tests on all Python versions
tox

CI/CD with GitHub Actions

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

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 tests
      run: pytest --cov=my_package
    - name: Upload coverage
      uses: codecov/codecov-action@v3

Common Issues and Solutions

Import Errors After Installation

Problem: Package installs but imports fail

Solutions:

  • Check pyproject.toml package discovery
  • Ensure proper __init__.py files
  • Verify import structure matches package structure

Missing Dependencies

Problem: Installation fails due to missing build dependencies

Solution:

[build-system]
requires = ["setuptools>=61.0", "wheel", "cython>=0.29.0"]  # Add missing deps
build-backend = "setuptools.build_meta"

Platform-Specific Issues

Problem: Package works on one platform but not others

Solutions:

  • Use platform-specific dependencies
  • Provide platform-specific wheels
  • Test on multiple platforms in CI

Version Conflicts

Problem: Package conflicts with installed versions

Solutions:

  • Use version ranges carefully
  • Test with different dependency versions
  • Use virtual environments

Distribution Best Practices

1. Use Semantic Versioning

  • MAJOR: Breaking changes
  • MINOR: New features (backward compatible)
  • PATCH: Bug fixes (backward compatible)

2. Test Before Publishing

# Test build
python -m build

# Test installation
pip install dist/*.whl --force-reinstall

# Test imports
python -c "import my_package; print('Import successful')"

# Test functionality
python -c "import my_package; my_package.main_function('test')"

3. Use Classifiers

classifiers = [
    "Development Status :: 4 - Beta",          # Status
    "Intended Audience :: Developers",         # Audience
    "License :: OSI Approved :: MIT License",  # License
    "Programming Language :: Python :: 3",     # Python versions
    "Topic :: Software Development :: Libraries :: Python Modules",  # Topic
]

4. Provide Comprehensive Metadata

[project]
# Basic info
name = "my-package"
version = "1.0.0"
description = "One-line description"
readme = "README.md"

# URLs
[project.urls]
Homepage = "https://my-package.com"
Documentation = "https://docs.my-package.com"
Repository = "https://github.com/user/my-package"
Issues = "https://github.com/user/my-package/issues"

5. Include License and Changelog

LICENSE          # Full license text
CHANGELOG.md     # Version history
CONTRIBUTING.md  # How to contribute

Practice Exercises

Exercise 1: Create a Distributable Package

Create a complete package with:

  • Core functionality
  • CLI interface
  • Tests
  • Documentation
  • Proper configuration
  • Publish to TestPyPI

Exercise 2: Plugin Architecture

Build a package with:

  • Plugin system using entry points
  • Multiple plugin implementations
  • Plugin discovery and loading
  • Example plugins included

Exercise 3: Multi-Platform Package

Create a package that:

  • Works on Windows, macOS, and Linux
  • Includes platform-specific features
  • Provides fallbacks for missing features
  • Tests on multiple platforms

Exercise 4: Version Management

Implement automatic versioning with:

  • Git tags for releases
  • Development versions
  • Version checking
  • Changelog generation

Summary

Package distribution makes your code shareable and installable:

Key Components:

  • pyproject.toml - Modern package configuration
  • Source and wheel distributions
  • Entry points for CLI commands
  • Optional dependencies
  • Comprehensive metadata

Publishing Process:

  1. Build: python -m build
  2. Test: Install locally and test
  3. Upload: twine upload dist/*
  4. Verify: Install from PyPI and test

Best Practices:

  • Use semantic versioning
  • Test on multiple platforms
  • Provide clear documentation
  • Include comprehensive metadata
  • Use proper classifiers and licenses

Tools:

  • build: Create distributions
  • twine: Upload to PyPI
  • setuptools: Package building
  • pip: Installation testing

Congratulations! You’ve completed the Modules and Packages module. Your Python code is now properly organized, modular, and distributable! 🎉