Python

Package Python Code with Setuptools

Learn how to use setuptools to package Python projects for easy sharing, installation, and deployment, with practical tips on structure, dependencies, and entry points.

August 2026 6 min read 13 views 0 hearts

The Right Way to Package Python Code with Setuptools

I still remember the first time I tried to share a Python project with a coworker back in 2018. I sent them a zip file of my scripts, and they spent the next hour trying to figure out why "import my_utils" wouldn't work. That's when I learned about setuptools, and it changed how I think about Python projects forever.

Let me walk you through what I've learned from real projects at PythonSkillset.

Why Bother with Setuptools?

Here's the thing - you could just copy your files around. But when you need to share code between projects, deploy to servers, or let others install your work with a simple pip install, setuptools is your best friend.

At PythonSkillset, we package everything from small utilities to full web applications. The same principles apply.

Getting Started with setup.py

from setuptools import setup, find_packages

setup(
    name="my_project",
    version="0.1.0",
    packages=find_packages(),
    install_requires=[
        "requests>=2.28.0",
        "pydantic>=2.0.0"
    ]
)

That's the absolute minimum you need. Place this file in your project root, and run pip install -e . to install your package in editable mode. You'll never go back.

A Real Project Structure

Here's how PythonSkillset organizes our internal tools:

my_project/
├── src/
│   └── my_project/
│       ├── __init__.py
│       ├── utils.py
│       └── configs/
│           └── default.yaml
├── tests/
├── docs/
├── setup.py
├── setup.cfg
├── LICENSE
└── README.md

The src/ layout prevents import confusion during development. Trust me on this - learn it early and save yourself headaches.

Making Your Package Installable

We handled a project for Marketing where they needed to install our analytics package on three different machines. With setuptools, it was:

# On their machines
pip install git+https://github.com/PythonSkillset/analytics-pkg.git

They had our full package with dependencies in under a minute. No manual setup, no missing imports.

Adding Entry Points

Here's a feature I didn't discover until my third year of Python: command-line tools from your package.

setup(
    name="my_tool",
    packages=find_packages(),
    entry_points={
        'console_scripts': [
            'my-tool=my_package.cli:main',
        ],
    }
)

After install, users can run my-tool from any terminal. We use this for our data processing tools at PythonSkillset, and it cuts out the "shebang script" step entirely.

Managing Dependencies Cleanly

Stop stuffing everything into install_requires. Use extras:

setup(
    install_requires=[
        "requests"
    ],
    extras_require={
        'dev': [
            'pytest',
            'black',
            'mypy',
        ],
        'docs': [
            'sphinx',
        ],
    }
)

Now your team does pip install -e .[dev] for development, and production only gets what it needs.

Version Management Without Pain

Forget bumping versions manually. Use __version__ in your package:

# my_package/__init__.py
__version__ = "0.2.1"

# setup.py
import re
from pathlib import Path

def get_version():
    init = Path(__file__).parent / "my_package" / "__init__.py"
    version_match = re.search(r'__version__\s*=\s*["\']([^"\']+)["\']', init.read_text())
    return version_match.group(1) if version_match else "0.0.0"

setup(version=get_version())

The Surprise I Learned Late

I spent a weekend debugging why my package installed but wouldn't import. The problem? I'd named the package folder the same as a standard library module. Setuptools won't warn you about this.

Always check your package name against importlib.util.find_spec() before publishing. We made this a mandatory part of our deployment checklist at PythonSkillset after that incident.

What Works in Practice

After packaging dozens of projects, here's what matters: - Test your install on a clean environment every time - Keep setup.py simple; use setup.cfg for configuration - Pin only critical dependencies; let pip handle the rest - Use python_requires to limit Python versions if needed

Your first package will take an afternoon. Your tenth will take ten minutes. Start with something small - maybe that utility module you keep copying between projects. You'll wonder why you didn't do it sooner.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.