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 installyour 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_requiresto 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
.gitignoreand exclude test files from distribution - ๐ Use setup.cfg: Move static metadata to
setup.cfgfor 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
trackto manage tasks - ๐พ JSON data file support
๐ Bonus Points:
- Add colorful output with
colorama - Include configuration file support
- Create both
setup.pyandsetup.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.pyfor 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! ๐๐ฆโจ