πŸ“¦ Python Tutorial β€” Packaging (setup.py & pyproject.toml)

Introduction 🌟

Packaging your Python project allows others to install it using pip. Two major packaging formats exist in modern Python:

  • setup.py β€” Traditional packaging (setuptools)
  • pyproject.toml β€” Modern packaging (PEP 517 / PEP 518)

Note

πŸ’‘ Today, pyproject.toml is the recommended standard.
πŸ’‘ Packaging is essential for libraries, CLI apps, and internal tools.

1. Project Structure for Packaging πŸ“

project_structure.txt

my_package/
│── my_package/
β”‚   β”œβ”€β”€ __init__.py
β”‚   └── core.py
β”‚
│── tests/
│── README.md
│── LICENSE
│── setup.py            # OR pyproject.toml
│── requirements.txt

βœ” Your code must be inside a folder named exactly like the package.

2. Packaging with setup.py (Traditional Method)

Example setup.py

setup.py

from setuptools import setup, find_packages

setup(
    name="my_package",
    version="1.0.0",
    packages=find_packages(),
    description="A demo Python package",
    author="Sathish",
    author_email="you@example.com",
    install_requires=["requests"],
    license="MIT",
)

Build the package

build.sh

pip install setuptools wheel
python setup.py sdist bdist_wheel

βœ” Creates .tar.gz & .whl files in dist/

Install your package locally

local_install.sh

pip install dist/my_package-1.0.0-py3-none-any.whl

3. Packaging with pyproject.toml (Modern Standard) πŸš€

Introduced by PEP 517/518, pyproject.toml is now the official method for packaging.

Minimal pyproject.toml (Setuptools)

pyproject.toml

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

[project]
name = "my_package"
version = "1.0.0"
description = "A demo Python package"
authors = [
  { name="Sathish", email="you@example.com" }
]
requires-python = ">=3.8"
dependencies = ["requests"]

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

Build the project

build_pyporject.sh

pip install build
python -m build

βœ” Generates wheel and source files in dist/

4. Adding Entry Points (CLI Commands) πŸ–₯️

pyproject.toml (CLI)

[project.scripts]
mytool = "my_package.core:main"

βœ” Running mytool in terminal now runs core.main()

core.py

def main():
    print("Hello from CLI!")

5. Writing an __init__.py File 🧠

__init__.py

__version__ = "1.0.0"

from .core import *

βœ” Makes your package importable via import my_package

6. Uploading Package to PyPI πŸš€

Install tools

pypi_tools.sh

pip install twine

Upload

upload.sh

twine upload dist/*

Note

πŸ’‘ Requires PyPI account & token

7. Versioning Best Practices 🧾

  • 1.0.0 β†’ Major release
  • 1.1.0 β†’ New features
  • 1.1.1 β†’ Bug fixes

8. Setup.cfg (Optional Middle Approach)

Not required today but still used. Structure β†’ minimal setup.py + metadata in setup.cfg.

9. pyproject.toml vs setup.py Comparison βš”οΈ

Featuresetup.pypyproject.toml
Modern StandardβŒβœ”
Build BackendsLimitedFlexible (Poetry, Flit, Setuptools)
Better Dependency HandlingβŒβœ”
Recommended for New ProjectsβŒβœ”

10. Creating a Package with Poetry πŸͺ„ (Advanced)

Poetry is a modern package/dependency manager.

poetry_install.sh

pip install poetry

Create project

poetry_new.sh

poetry new my_package

βœ” Automatically creates structure + pyproject.toml

11. Best Practices πŸ’‘

  • βœ” Prefer pyproject.toml for new projects
  • βœ” Keep package code inside a folder matching project name
  • βœ” Maintain a clean README.md
  • βœ” Use semantic versioning (MAJOR.MINOR.PATCH)
  • βœ” Test package locally before uploading to PyPI

Conclusion πŸŽ‰

>>β€œPackaging transforms your code into a reusable, shareable tool β€” making you not just a developer, but a creator.” ✨

You now understand how to package Python projects using setup.py and pyproject.toml! Want the next tutorial? Try Publishing to PyPI, Dependency Management, Poetry, or Creating CLI Tools. Just tell me! 😊