1. Pengenalan Python Packaging
Python Packaging adalah proses mengemas kode Python agar bisa didistribusikan dan digunakan oleh orang lain. Ekosistem Python memiliki lebih dari 500.000 package di PyPI (Python Package Index), menjadikannya salah satu repositori package terbesar di dunia.
Memahami packaging sangat penting untuk: berbagi library yang Anda buat, mengelola dependensi proyek, deployment ke server, dan menjaga reproducibility (konsistensi) environment.
Ekosistem Packaging Python
| Tool | Fungsi | Status |
|---|---|---|
| pip | Instal dan kelola package dari PyPI | ✅ Standar |
| venv | Membuat virtual environment terisolasi | ✅ Built-in |
| setuptools | Build backend untuk packaging | ✅ Mature |
| pyproject.toml | Konfigurasi packaging modern (PEP 621) | ✅ Standar baru |
| build | Frontend untuk mem-build package | ✅ PEP 517 |
| twine | Upload package ke PyPI | ✅ Standar |
| Poetry | All-in-one: dependency + packaging | 🟢 Populer |
| uv | Package manager ultra-cepat (Rust) | 🟢 Baru |
| PDM | Modern package manager (PEP 621) | 🟢 Alternatif |
2. pip: Package Installer
pip adalah package manager standar Python untuk menginstal dan mengelola package dari PyPI. pip sudah terinstal otomatis bersama Python 3.4+.
Perintah pip Dasar
# Instal package
pip install requests
pip install flask==3.0.3 # Versi spesifik
pip install "django>=4.2,<5.0" # Rentang versi
pip install requests[security] # Dengan extras
# Instal beberapa package sekaligus
pip install flask sqlalchemy pytest
# Instal dari file requirements
pip install -r requirements.txt
# Upgrade package
pip install --upgrade flask
pip install -U requests
# Uninstall package
pip uninstall requests
# List semua package yang terinstal
pip list
# Cek info package
pip show flask
# Output:
# Name: Flask
# Version: 3.0.3
# Summary: A simple framework for building web applications.
# Location: /path/to/lib/python3.12/site-packages
# Freeze requirements (export dependencies)
pip freeze > requirements.txt
# Output format:
# Flask==3.0.3
# Werkzeug==3.0.3
# Jinja2==3.1.4
# click==8.1.7
# ...
# Cek package yang outdated
pip list --outdated
requirements.txt
# requirements.txt — Pin versi untuk reproducibility
Flask==3.0.3
Flask-SQLAlchemy==3.1.1
Flask-Migrate==4.0.7
Flask-WTF==1.2.1
gunicorn==22.0.0
psycopg2-binary==2.9.9
python-dotenv==1.0.1
# Development dependencies (pisahkan)
# requirements-dev.txt
pytest==8.2.2
pytest-cov==5.0.0
black==24.4.2
ruff==0.4.8
mypy==1.10.0
Jangan instal package secara global. Selalu gunakan virtual environment untuk menghindari konflik antar proyek. Tanpa venv, proyek A yang butuh Flask 2.x bisa konflik dengan proyek B yang butuh Flask 3.x.
3. Virtual Environments
Virtual environment adalah Python environment terisolasi yang memiliki package dan versi sendiri, terpisah dari environment global. Setiap proyek sebaiknya memiliki venv sendiri.
# Membuat virtual environment
python -m venv venv
# Aktifkan virtual environment
# macOS/Linux:
source venv/bin/activate
# Windows (Command Prompt):
venv\Scripts\activate.bat
# Windows (PowerShell):
venv\Scripts\Activate.ps1
# Cek bahwa venv aktif (prompt berubah)
# (venv) $ which python
# /path/to/proyek/venv/bin/python
# Instal package di dalam venv
pip install flask
pip install -r requirements.txt
# Deactivate venv
deactivate
# Menghapus venv (cukup hapus folder)
rm -rf venv # macOS/Linux
rmdir /s /q venv # Windows
# .gitignore — JANGAN commit venv!
echo "venv/" >> .gitignore
echo "__pycache__/" >> .gitignore
echo "*.pyc" >> .gitignore
echo ".env" >> .gitignore
Alternatif: uv (Ultra Fast)
# uv — Package manager ultra-cepat ditulis dalam Rust
# 10-100x lebih cepat dari pip!
# Instal uv
pip install uv
# atau: curl -LsSf https://astral.sh/uv/install.sh | sh
# Buat venv dengan uv
uv venv
# Instal package (sangat cepat!)
uv pip install flask sqlalchemy pytest
# Sync dari requirements
uv pip sync requirements.txt
# Compile requirements (seperti pip-compile)
uv pip compile requirements.in -o requirements.txt
# Jalankan script dengan uv
uv run python app.py
4. pyproject.toml: Konfigurasi Modern
pyproject.toml adalah file konfigurasi standar untuk proyek Python (PEP 621 + PEP 518). File ini menggantikan setup.py, setup.cfg, requirements.txt, dan berbagai file konfigurasi lainnya dalam satu file terpadu.
# pyproject.toml — Konfigurasi proyek Python modern
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.backends._legacy:_Backend"
[project]
name = "my-awesome-library"
version = "1.0.0"
description = "Library Python yang luar biasa untuk melakukan X"
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.10"
authors = [
{name = "Budi Santoso", email = "budi@example.com"},
]
keywords = ["python", "library", "awesome"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Libraries",
]
dependencies = [
"requests>=2.31.0",
"pydantic>=2.0",
"rich>=13.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"pytest-cov>=5.0",
"black>=24.0",
"ruff>=0.4",
"mypy>=1.10",
]
docs = [
"sphinx>=7.0",
"sphinx-rtd-theme>=2.0",
]
[project.urls]
Homepage = "https://github.com/budi/my-awesome-library"
Documentation = "https://my-awesome-library.readthedocs.io"
Repository = "https://github.com/budi/my-awesome-library"
Issues = "https://github.com/budi/my-awesome-library/issues"
[project.scripts]
# CLI entry point — bisa dijalankan dari terminal
my-cli = "my_library.cli:main"
# Konfigurasi tools
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --tb=short"
[tool.black]
line-length = 88
target-version = ["py312"]
[tool.ruff]
line-length = 88
select = ["E", "F", "I", "N", "W", "UP"]
[tool.mypy]
python_version = "3.12"
strict = true
[tool.setuptools.packages.find]
where = ["src"]
Struktur Proyek Modern
/"my-awesome-library/"/
src/
/"my_library/"/
/"__init__.py"/
core.py
/"utils.py"/
cli.py
tests/
/"__init__.py"/
/"test_core.py"/
/"test_utils.py"/
docs/
/"index.md"/
/"pyproject.toml"/
/"README.md"/
/".gitignore"/
/"CHANGELOG.md"/
5. Building Packages
Setelah proyek siap, kita perlu mem-build package menjadi distributable artifacts (wheel dan sdist) sebelum bisa dipublish ke PyPI atau diinstal oleh orang lain.
# Instal build tool
pip install build
# Build package (menghasilkan wheel + sdist)
python -m build
# Output:
# Successfully built my-awesome-library-1.0.0.tar.gz
# and my_awesome_library-1.0.0-py3-none-any.whl
# Struktur output:
# dist/
# ├── my_awesome_library-1.0.0-py3-none-any.whl ← Wheel (binary)
# └── my-awesome-library-1.0.0.tar.gz ← Sdist (source)
# Instal package lokal (untuk testing)
pip install dist/my_awesome_library-1.0.0-py3-none-any.whl
# Instal dalam mode editable (development)
pip install -e .
# Editable mode: perubahan kode langsung ter-refleksi tanpa reinstall
# Instal dengan dev dependencies
pip install -e ".[dev]"
# Build dengan hatchling (alternatif modern)
pip install hatchling
# Ganti build-backend di pyproject.toml:
# build-backend = "hatchling.build"
python -m build
Entry Points (CLI)
# src/my_library/cli.py — CLI Entry Point
import argparse
from my_library import __version__
from my_library.core import proses_data
def main():
parser = argparse.ArgumentParser(
description='My Awesome Library CLI'
)
parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
parser.add_argument('input', help='Input file atau data')
parser.add_argument('-o', '--output', help='Output file')
parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
args = parser.parse_args()
if args.verbose:
print(f"Memproses: {args.input}")
hasil = proses_data(args.input)
if args.output:
with open(args.output, 'w') as f:
f.write(hasil)
print(f"Output disimpan ke {args.output}")
else:
print(hasil)
if __name__ == '__main__':
main()
# Setelah install, bisa dijalankan dari terminal:
# my-cli data.txt -o output.txt -v
6. Publishing ke PyPI
PyPI (Python Package Index) adalah repositori resmi untuk package Python. Setelah di-publish, siapa saja bisa menginstal package Anda dengan pip install nama-package.
Persiapan
# 1. Buat akun di https://pypi.org/register/
# 2. Buat API token di https://pypi.org/manage/account/token/
# Instal twine
pip install twine
# Verifikasi package sebelum upload
twine check dist/*
# Output: PASSED dist/my_awesome_library-1.0.0-py3-none-any.whl
# Upload ke TestPyPI dulu (testing)
twine upload --repository testpypi dist/*
# Upload ke PyPI (production)
twine upload dist/*
# Atau gunakan Trusted Publisher (tanpa API token):
# 1. Setup GitHub Actions + PyPI Trusted Publisher
# 2. Upload otomatis setiap push tag
Setelah Publish
# Install dari PyPI
pip install my-awesome-library
# Install dari TestPyPI
pip install --index-url https://test.pypi.org/simple/ my-awesome-library
# Install dari GitHub langsung
pip install git+https://github.com/budi/my-awesome-library.git
# Install dari local folder
pip install /path/to/my-awesome-library/
# Install dengan extras
pip install "my-awesome-library[dev]"
pip install "my-awesome-library[docs]"
Best Practices Publishing
| Best Practice | Alasan |
|---|---|
| Semantic Versioning | Gunakan format MAJOR.MINOR.PATCH (contoh: 1.2.3) |
| README.md yang bagus | Dokumentasi, instalasi, contoh penggunaan |
| LICENSE | Jelaskan lisensi (MIT, Apache 2.0, GPL, dll) |
| CHANGELOG.md | Catat perubahan di setiap versi |
| CI/CD | Otomatisasi test dan publish dengan GitHub Actions |
| py.typed marker | Untuk library yang mendukung type hints |
7. Docker untuk Python
Docker mengemas aplikasi Python beserta semua dependensinya ke dalam container yang bisa dijalankan di mana saja. Docker memastikan aplikasi berjalan konsisten di development, testing, dan production.
Dockerfile Multi-Stage (Best Practice)
# Dockerfile — Multi-stage build untuk Python
# Stage 1: Build
FROM python:3.12-slim as builder
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# Stage 2: Production
FROM python:3.12-slim
WORKDIR /app
# Copy installed packages from builder
COPY --from=builder /install /usr/local
# Copy application code
COPY . .
# Create non-root user (security best practice)
RUN adduser --disabled-password --gecos '' appuser
USER appuser
# Environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
FLASK_ENV=production
EXPOSE 5000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
# Run with gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "--timeout", "120", "app:create_app()"]
docker-compose.yml
# docker-compose.yml — Full stack Python app
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile
ports:
- "5000:5000"
environment:
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
- SECRET_KEY=my-secret-key-change-this
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
db:
image: postgres:15
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secret
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
worker:
build: .
command: celery -A app.celery worker --loglevel=info
environment:
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
volumes:
pgdata:
Best Practices Docker Python
# .dockerignore — Jangan masukkan ini ke Docker image
__pycache__
*.pyc
*.pyo
.git
.gitignore
.venv
venv
.env
*.md
tests/
docs/
dist/
build/
.pytest_cache
.mypy_cache
.coverage
htmlcov/
# Build image
docker build -t my-python-app:latest .
# Jalankan container
docker run -p 5000:5000 my-python-app:latest
# Jalankan dengan docker-compose
docker-compose up -d # Background
docker-compose up --build # Rebuild and start
docker-compose down # Stop and remove
docker-compose logs -f web # Lihat logs
# Masuk ke container (debugging)
docker exec -it container_id /bin/bash
# Cek ukuran image
docker images my-python-app
docker system df # Cek disk usage Docker
# Cleanup
docker system prune -a # Hapus semua yang tidak terpakai
Gunakan python:3.12-slim sebagai base image (bukan python:3.12 yang full) untuk mengurangi ukuran image. Selalu gunakan --no-cache-dir saat pip install di Docker. Gunakan multi-stage build untuk production image yang lebih kecil.
8. Dependency Managers
Selain pip + venv, ada beberapa dependency manager modern yang menyediakan fitur lebih lengkap seperti lock files, dependency resolution, dan build system terintegrasi.
Poetry
# Instal Poetry
curl -sSL https://install.python-poetry.org | python3 -
# Buat proyek baru
poetry new my-project
cd my-project
# Tambah dependency
poetry add requests flask sqlalchemy
poetry add --group dev pytest black ruff
# Instal semua dependencies
poetry install
# Jalankan perintah dalam environment
poetry run python app.py
poetry run pytest
# Update dependencies
poetry update
# Export ke requirements.txt
poetry export -f requirements.txt -o requirements.txt
# Build dan publish
poetry build
poetry publish
# Lock file (poetry.lock) — commit ke Git!
# Memastikan semua developer menggunakan versi yang sama
Perbandingan Tools
| Tool | Lock File | Kecepatan | Built-in Build | Learning Curve |
|---|---|---|---|---|
| pip | ❌ (pip freeze manual) | 🟡 Sedang | ❌ | 🟢 Mudah |
| Poetry | ✅ poetry.lock | 🟡 Sedang | ✅ | 🟡 Sedang |
| PDM | ✅ pdm.lock | 🟡 Sedang | ✅ | 🟡 Sedang |
| uv | ✅ uv.lock | 🟢 Sangat Cepat | ✅ | 🟢 Mudah |
Pemula: Gunakan pip + venv — sederhana dan banyak tutorial. Proyek menengah: Gunakan Poetry atau PDM — lock file dan dependency resolution lebih baik. Performa tinggi: Gunakan uv — sangat cepat karena ditulis dalam Rust. Tim besar: Poetry atau PDM dengan CI/CD yang terkonfigurasi.
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Python packaging: