Prerequisites
- Basic understanding of programming concepts ๐
- Python installation (3.8+) ๐
- VS Code or preferred IDE ๐ป
What you'll learn
- Understand the concept fundamentals ๐ฏ
- Apply the concept in real projects ๐๏ธ
- Debug common issues ๐
- Write clean, Pythonic code โจ
๐ฏ Introduction
Welcome to the exciting world of Python package distribution! ๐ Have you ever created an amazing Python project and wanted to share it with the world? Or maybe youโve wondered how to install your own code with pip install
? Today, weโll unlock the magic behind Python package distribution using setup.py
!
Think of setup.py
as your packageโs passport ๐ - it contains all the essential information needed to travel from your computer to Python developers around the globe! Whether youโre building the next great library ๐, a handy CLI tool ๐ ๏ธ, or sharing utilities with your team, understanding setup.py
is your gateway to Python packaging success.
By the end of this tutorial, youโll be packaging Python projects like a pro! Letโs dive in! ๐โโ๏ธ
๐ Understanding Package Distribution
๐ค What is setup.py?
setup.py
is like a recipe card ๐ for your Python package. Just as a recipe tells someone how to make your favorite dish, setup.py
tells Python how to install and configure your package. Think of it as the instruction manual that comes with furniture - it tells pip exactly how to put your package together!
In Python terms, setup.py
is a build script that uses setuptools
to define your packageโs metadata, dependencies, and installation instructions. This means you can:
- โจ Share your code professionally with pip
- ๐ Manage dependencies automatically
- ๐ก๏ธ Version your package properly
- ๐ฆ Include data files and resources
- ๐ Distribute to PyPI (Python Package Index)
๐ก Why Use setup.py?
Hereโs why Python developers love proper package distribution:
- Professional Distribution ๐: Share code like the pros do
- Dependency Management ๐: Automatically install required packages
- Version Control ๐ท๏ธ: Track and manage package versions
- Easy Installation โก: Users can simply
pip install
your package - PyPI Publishing ๐: Share with millions of Python developers
Real-world example: Imagine youโve built an awesome weather API wrapper ๐ค๏ธ. With setup.py
, instead of telling users to โdownload these files and put them here,โ they can simply run pip install your-weather-lib
and start using it immediately!
๐ง Basic Syntax and Usage
๐ Simple setup.py Example
Letโs start with a friendly example:
# ๐ Hello, setup.py!
from setuptools import setup, find_packages
# ๐จ Basic package configuration
setup(
name="awesome-greeting", # ๐ฆ Package name
version="1.0.0", # ๐ท๏ธ Version number
author="Your Name", # ๐ค That's you!
author_email="[email protected]", # ๐ง Contact email
description="A friendly greeting package", # ๐ Short description
packages=find_packages(), # ๐ Auto-find Python packages
python_requires=">=3.6", # ๐ Python version requirement
)
๐ก Explanation: This minimal setup.py
defines the essential metadata for your package. The find_packages()
function automatically discovers all Python packages in your project!
๐ฏ Common Patterns
Here are patterns youโll use in every project:
# ๐๏ธ Pattern 1: Complete package setup
setup(
name="my-awesome-package",
version="2.1.0",
author="Python Developer",
author_email="[email protected]",
description="Makes Python even more awesome! ๐",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
url="https://github.com/yourusername/my-awesome-package",
packages=find_packages(exclude=["tests*"]),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
)
# ๐จ Pattern 2: With dependencies
setup(
name="web-scraper-pro",
version="1.0.0",
packages=find_packages(),
install_requires=[
"requests>=2.25.0", # ๐ HTTP library
"beautifulsoup4>=4.9.0", # ๐ฒ HTML parsing
"pandas>=1.2.0", # ๐ Data analysis
],
)
# ๐ Pattern 3: With entry points (CLI commands)
setup(
name="task-manager",
version="1.0.0",
packages=find_packages(),
entry_points={
"console_scripts": [
"tasks=task_manager.cli:main", # ๐ป CLI command
],
},
)
๐ก Practical Examples
๐ Example 1: E-commerce Tools Package
Letโs build a real package for e-commerce utilities:
# ๐๏ธ setup.py for e-commerce tools
from setuptools import setup, find_packages
# ๐ Read the long description from README
with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()
setup(
name="ecommerce-helpers",
version="0.1.0",
author="Shop Developer",
author_email="[email protected]",
description="Handy e-commerce utilities ๐",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/shopdev/ecommerce-helpers",
packages=find_packages(where="src"),
package_dir={"": "src"},
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Topic :: Software Development :: Libraries",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
],
python_requires=">=3.7",
install_requires=[
"requests>=2.25.0", # ๐ API calls
"python-dateutil", # ๐
Date handling
"currency-converter", # ๐ฑ Currency conversion
],
extras_require={
"dev": [
"pytest>=6.0", # ๐งช Testing
"black", # ๐จ Code formatting
"flake8", # ๐ Linting
],
},
package_data={
"ecommerce_helpers": ["data/*.json", "templates/*.html"],
},
)
๐ฏ Try it yourself: Create a project structure and install your package locally with pip install -e .
!
๐ฎ Example 2: Game Development Toolkit
Letโs create a package for game developers:
# ๐ setup.py for game development tools
from setuptools import setup, find_packages
import pathlib
here = pathlib.Path(__file__).parent.resolve()
# ๐ Get the long description from README
long_description = (here / "README.md").read_text(encoding="utf-8")
# ๐ Get requirements from requirements.txt
requirements = (here / "requirements.txt").read_text(encoding="utf-8").splitlines()
setup(
name="pygamekit",
version="2.0.0",
author="Game Dev Pro",
author_email="[email protected]",
description="Ultimate Python game development toolkit ๐ฎ",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/gamedevpro/pygamekit",
project_urls={
"Bug Reports": "https://github.com/gamedevpro/pygamekit/issues",
"Documentation": "https://pygamekit.readthedocs.io/",
"Source": "https://github.com/gamedevpro/pygamekit",
},
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Topic :: Games/Entertainment",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
],
packages=find_packages(where="src"),
package_dir={"": "src"},
python_requires=">=3.8",
install_requires=requirements,
extras_require={
"audio": ["pygame>=2.0.0"], # ๐ Sound support
"physics": ["pymunk>=6.0.0"], # ๐ Physics engine
"ai": ["numpy>=1.19.0"], # ๐ค AI features
"all": ["pygame>=2.0.0", "pymunk>=6.0.0", "numpy>=1.19.0"],
},
entry_points={
"console_scripts": [
"gamekit=pygamekit.cli:main", # ๐ฎ Main CLI
"gamekit-init=pygamekit.init:create", # ๐ Project creator
],
},
include_package_data=True,
package_data={
"pygamekit": [
"assets/sprites/*.png", # ๐จ Game sprites
"assets/sounds/*.wav", # ๐ Sound effects
"templates/*.py", # ๐ Project templates
],
},
)
๐ Advanced Concepts
๐งโโ๏ธ Advanced Feature 1: Dynamic Versioning
When youโre ready to level up, try dynamic version management:
# ๐ฏ Advanced versioning technique
import re
from pathlib import Path
def get_version():
"""Extract version from __init__.py ๐ท๏ธ"""
init_file = Path("src/mypackage/__init__.py").read_text()
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", init_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string! ๐ฑ")
setup(
name="smart-package",
version=get_version(), # โจ Dynamic version!
# ... rest of setup
)
๐๏ธ Advanced Feature 2: Platform-Specific Dependencies
For the brave developers handling multiple platforms:
# ๐ Platform-specific setup
import platform
# ๐ฅ๏ธ Detect the operating system
system = platform.system()
platform_deps = []
if system == "Windows":
platform_deps = ["pywin32"] # ๐ช Windows specific
elif system == "Darwin":
platform_deps = ["pyobjc"] # ๐ macOS specific
elif system == "Linux":
platform_deps = ["python-xlib"] # ๐ง Linux specific
setup(
name="cross-platform-app",
version="3.0.0",
install_requires=[
"click>=7.0", # ๐ป CLI framework
"colorama>=0.4.0", # ๐จ Cross-platform colors
] + platform_deps, # โ Platform-specific deps
extras_require={
"windows": ["pywin32"],
"macos": ["pyobjc"],
"linux": ["python-xlib"],
},
)
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Missing MANIFEST.in
# โ Wrong - Non-Python files not included!
setup(
name="my-package",
packages=find_packages(),
# Where are my data files? ๐ฐ
)
# โ
Correct - Create MANIFEST.in file!
# In MANIFEST.in:
# include README.md
# include LICENSE
# recursive-include my_package/data *
# recursive-include my_package/templates *.html
# Then in setup.py:
setup(
name="my-package",
packages=find_packages(),
include_package_data=True, # ๐ Include all files from MANIFEST.in!
)
๐คฏ Pitfall 2: Incorrect Package Discovery
# โ Dangerous - Wrong package structure!
# Project structure:
# myproject/
# setup.py
# mypackage.py # Single file, not a package!
setup(
name="myproject",
packages=find_packages(), # ๐ฅ Won't find anything!
)
# โ
Safe - Proper package structure!
# Project structure:
# myproject/
# setup.py
# mypackage/
# __init__.py # Makes it a package! โจ
# core.py
setup(
name="myproject",
packages=find_packages(), # โ
Finds mypackage!
)
๐ ๏ธ Best Practices
- ๐ฏ Use Semantic Versioning: Follow MAJOR.MINOR.PATCH format (e.g., 2.1.3)
- ๐ Include Documentation: Always add README.md and use it as long_description
- ๐ก๏ธ Specify Python Version: Use
python_requires
to prevent compatibility issues - ๐จ Use Classifiers: Help users find your package with proper classifiers
- โจ Test Locally First: Always
pip install -e .
before publishing - ๐ฆ Keep It Clean: Use
.gitignore
and exclude test files from distribution - ๐ Use setup.cfg: Move static metadata to
setup.cfg
for cleaner code
๐งช Hands-On Exercise
๐ฏ Challenge: Create Your Own Package
Build a complete Python package with the following features:
๐ Requirements:
- โ A utility package called โtask-trackerโ
- ๐ท๏ธ Version 1.0.0 with proper metadata
- ๐ค Your name as the author
- ๐ A module for managing tasks with due dates
- ๐จ CLI command
track
to manage tasks - ๐พ JSON data file support
๐ Bonus Points:
- Add colorful output with
colorama
- Include configuration file support
- Create both
setup.py
andsetup.cfg
- Add development dependencies
๐ก Solution
๐ Click to see solution
# ๐ฏ Complete setup.py for task-tracker!
from setuptools import setup, find_packages
from pathlib import Path
# ๐ Read README for long description
this_directory = Path(__file__).parent
long_description = (this_directory / "README.md").read_text()
setup(
name="task-tracker",
version="1.0.0",
author="Your Name",
author_email="[email protected]",
description="A colorful task tracking utility ๐",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/yourusername/task-tracker",
packages=find_packages(where="src"),
package_dir={"": "src"},
classifiers=[
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: End Users/Desktop",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Topic :: Utilities",
],
python_requires=">=3.8",
install_requires=[
"click>=8.0.0", # ๐ป CLI framework
"colorama>=0.4.4", # ๐จ Colorful output
"python-dateutil", # ๐
Date handling
],
extras_require={
"dev": [
"pytest>=6.0", # ๐งช Testing
"black", # ๐จ Code formatting
"mypy", # ๐ Type checking
"build", # ๐ฆ Build backend
],
},
entry_points={
"console_scripts": [
"track=task_tracker.cli:main", # ๐ Main CLI command
],
},
package_data={
"task_tracker": ["data/*.json", "config/*.yaml"],
},
include_package_data=True,
)
# ๐ Project structure:
# task-tracker/
# setup.py
# setup.cfg
# README.md
# LICENSE
# src/
# task_tracker/
# __init__.py
# cli.py
# tasks.py
# config.py
# data/
# default_tasks.json
# config/
# default_config.yaml
๐ Key Takeaways
Youโve learned so much about Python packaging! Hereโs what you can now do:
- โ Create setup.py files with confidence ๐ช
- โ Define package metadata properly ๐
- โ Manage dependencies like a pro ๐
- โ Include data files in your packages ๐
- โ Create CLI entry points for your tools ๐ป
- โ Prepare packages for PyPI distribution ๐
Remember: Every popular Python package started with a simple setup.py
. Youโre now ready to share your code with the world! ๐
๐ค Next Steps
Congratulations! ๐ Youโve mastered the basics of Python package distribution!
Hereโs what to do next:
- ๐ป Create a
setup.py
for one of your existing projects - ๐๏ธ Try installing your package locally with
pip install -e .
- ๐ Learn about
pyproject.toml
- the modern way to configure packages - ๐ Explore publishing to PyPI when youโre ready to share!
Keep packaging, keep sharing, and remember - the Python community thrives because developers like you share their amazing code! ๐
Happy packaging! ๐๐ฆโจ