~/icsd.ir — bash
SYSTEM_ONLINE

Packaging و انتشار در PyPI

انتشار پکیج پایتون در PyPI به دیگران اجازه می‌دهد با یک pip install package_name کد شما را نصب کنند. در این فصل با ساختار مدرن پکیج (pyproject.toml)، setuptools/hatchling/poetry، انتشار در PyPI، Test PyPI، Versioning، Entry Points و CI/CD آشنا می‌شویم.

انتشار پکیج پایتون در PyPI به دیگران اجازه می‌دهد با یک pip install package_name کد شما را نصب کنند. در این فصل با ساختار مدرن پکیج (pyproject.toml)، setuptools/hatchling/poetry، انتشار در PyPI، Test PyPI، Versioning، Entry Points و CI/CD آشنا می‌شویم.

پکیجینگ مدرن – pyproject.toml

از پایتون 3.11 و PEP 621، فایل pyproject.toml استاندارد رسمی پکیجینگ است:

# pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "my-awesome-package"
version = "0.1.0"
description = "توضیح کوتاه پکیج"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
authors = [
    {name = "محمدعلی ناظری", email = "info@icsd.ir"}
]
keywords = ["python", "tutorial", "icsd"]
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",
    "Natural Language :: Persian",
]
dependencies = [
    "requests>=2.28",
    "click>=8.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.0",
    "pytest-cov",
    "black",
    "mypy",
]
docs = [
    "sphinx",
    "sphinx-rtd-theme",
]

[project.urls]
Homepage = "https://github.com/yourname/my-awesome-package"
Documentation = "https://your-docs.com"
Repository = "https://github.com/yourname/my-awesome-package"
Issues = "https://github.com/yourname/my-awesome-package/issues"

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

ساختار پروژه (src layout)

my-awesome-package/
├── pyproject.toml
├── README.md
├── LICENSE
├── .gitignore
├── src/
│   └── my_package/
│       ├── __init__.py
│       ├── core.py
│       ├── utils.py
│       └── cli.py
├── tests/
│   ├── __init__.py
│   ├── test_core.py
│   └── test_utils.py
└── docs/
    └── index.rst
چرا src layout؟ جلوگیری از import شدن نسخه نصب‌نشده هنگام تست. با src layout، باید پکیج را install کنید تا از آن استفاده کنید.

__init__.py

# src/my_package/__init__.py
"""My awesome package."""

__version__ = "0.1.0"

from .core import main_function, MainClass
from .utils import helper_function

__all__ = ["main_function", "MainClass", "helper_function"]

انتخاب Build Backend

Backend محبوبیت ویژگی
setuptools ⭐⭐⭐⭐⭐ قدیمی، رایج
hatchling ⭐⭐⭐⭐ مدرن، سریع
poetry ⭐⭐⭐⭐ مدیریت dependency کامل
flit ⭐⭐⭐ ساده، بدون C extension
pdm ⭐⭐⭐ سازگار با PEP 582

setuptools

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

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

hatchling (پیشنهاد ما)

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/my_package"]

poetry

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "my-package"
version = "0.1.0"
description = "..."
authors = ["نام <email@example.com>"]

[tool.poetry.dependencies]
python = "^3.10"
requests = "^2.28"

[tool.poetry.dev-dependencies]
pytest = "^7.0"

ساخت پکیج

pip install build
python -m build

# خروجی در dist/
ls dist/
# my_awesome_package-0.1.0-py3-none-any.whl    (wheel)
# my-awesome-package-0.1.0.tar.gz              (sdist)

تفاوت wheel و sdist

  • sdist (.tar.gz): source distribution – کد منبع، نیاز به کامپایل دارد
  • wheel (.whl): binary distribution – آماده نصب، سریع‌تر

نصب محلی برای تست

# نصب editable - تغییرات بدون reinstall
pip install -e .

# با dev dependencies
pip install -e ".[dev]"

# نصب از فایل wheel
pip install dist/my_awesome_package-0.1.0-py3-none-any.whl

Versioning – SemVer

Semantic Versioning: MAJOR.MINOR.PATCH

  • MAJOR – تغییرات breaking (1.0.0 → 2.0.0)
  • MINOR – قابلیت جدید بدون breaking (1.0.0 → 1.1.0)
  • PATCH – bug fix (1.0.0 → 1.0.1)
0.1.0   - نسخه اولیه
0.2.0   - افزودن قابلیت جدید
0.2.1   - رفع باگ
1.0.0   - اولین نسخه پایدار
1.0.0a1 - alpha
1.0.0b1 - beta
1.0.0rc1 - release candidate
2.0.0   - تغییر breaking

Dynamic version

# خواندن version از __init__.py
[project]
dynamic = ["version"]

[tool.hatch.version]
path = "src/my_package/__init__.py"

Test PyPI – تمرین قبل از انتشار

قبل از انتشار در PyPI واقعی، در Test PyPI تست کنید:

  1. ثبت‌نام: test.pypi.org
  2. ساخت API token در تنظیمات حساب
  3. آپلود
pip install twine

# آپلود به Test PyPI
twine upload --repository testpypi dist/*

# نصب از Test PyPI برای تست
pip install --index-url https://test.pypi.org/simple/ 
    --extra-index-url https://pypi.org/simple/ 
    my-awesome-package

~/.pypirc

[distutils]
index-servers =
    pypi
    testpypi

[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmcCJ...

[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-AgENdGVzdC5weXBp...

انتشار در PyPI

# ساخت
python -m build

# آپلود
twine upload dist/*

# با مشخص کردن repository
twine upload --repository pypi dist/*

# تایید قبل از آپلود
twine check dist/*

پس از انتشار:

pip install my-awesome-package

Entry Points – دستورات CLI

# src/my_package/cli.py
import click

@click.command()
@click.option("--name", default="World", help="نام برای سلام")
@click.option("--count", default=1, help="تعداد تکرار")
def main(name, count):
    """ابزار CLI من."""
    for _ in range(count):
        click.echo(f"سلام {name}!")

if __name__ == "__main__":
    main()
# pyproject.toml
[project.scripts]
mytool = "my_package.cli:main"

# پس از نصب:
# $ mytool --name علی --count 3
# سلام علی!
# سلام علی!
# سلام علی!

چند entry point

[project.scripts]
mytool = "my_package.cli:main"
mytool-admin = "my_package.admin:cli"
mytool-server = "my_package.server:run"

[project.gui-scripts]
mytool-gui = "my_package.gui:main"

README و LICENSE

README.md

# My Awesome Package

[![PyPI version](https://badge.fury.io/py/my-awesome-package.svg)](https://pypi.org/project/my-awesome-package/)
[![Python](https://img.shields.io/pypi/pyversions/my-awesome-package.svg)](https://pypi.org/project/my-awesome-package/)
[![Tests](https://github.com/user/repo/actions/workflows/test.yml/badge.svg)](https://github.com/user/repo/actions)

پکیج پایتون عالی برای X و Y.

## نصب

```bash
pip install my-awesome-package
```

## استفاده

```python
from my_package import main_function

result = main_function("hello")
print(result)
```

## مستندات

[my-awesome-package.readthedocs.io](https://my-awesome-package.readthedocs.io)

## مجوز

MIT

LICENSE

MIT License

Copyright (c) 2026 محمدعلی ناظری

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction...

GitHub Actions – CI/CD

# .github/workflows/test.yml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ["3.10", "3.11", "3.12"]
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -e ".[dev]"
    
    - name: Run tests
      run: pytest --cov=my_package
    
    - name: Upload coverage
      uses: codecov/codecov-action@v3

انتشار خودکار با Tag

# .github/workflows/publish.yml
name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # برای trusted publishing
    
    steps:
    - uses: actions/checkout@v4
    
    - uses: actions/setup-python@v5
      with:
        python-version: "3.12"
    
    - name: Build
      run: |
        pip install build
        python -m build
    
    - name: Publish to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

گنجاندن فایل‌های غیر پایتون

# pyproject.toml
[tool.hatch.build.targets.wheel.shared-data]
"data/templates" = "share/my_package/templates"

# یا setuptools
[tool.setuptools.package-data]
my_package = ["data/*.json", "templates/*.html"]
# دسترسی به فایل‌های پکیج
from importlib.resources import files

template = files("my_package.data").joinpath("template.html").read_text()

چک‌لیست انتشار

  1. ✅ نام پکیج در PyPI آزاد است (search کنید)
  2. ✅ pyproject.toml کامل (description، keywords، classifiers)
  3. ✅ README.md کامل با مثال‌ها
  4. ✅ LICENSE وجود دارد
  5. ✅ تست‌ها pass می‌شوند
  6. ✅ Documentation مشخص
  7. ✅ تست در Test PyPI
  8. ✅ Tag در git: git tag v0.1.0; git push --tags
  9. ✅ آپلود نهایی
  10. ✅ بررسی صفحه pypi.org/project/…

بهترین شیوه‌ها

  • از pyproject.toml استفاده کنید (نه setup.py قدیمی)
  • src layout برای جلوگیری از import مسائل
  • نسخه را در یک جا نگه دارید (dynamic version)
  • SemVer را رعایت کنید
  • قبل از انتشار، Test PyPI
  • CHANGELOG.md نگه دارید
  • دو ایمیل: یکی برای PyPI، یکی برای contact
  • 2FA روی حساب PyPI فعال کنید

جمع‌بندی

  • pyproject.toml استاندارد مدرن پکیجینگ
  • src layout بهترین ساختار
  • hatchling/setuptools/poetry برای build
  • twine برای آپلود به PyPI
  • Test PyPI برای تست انتشار
  • SemVer برای versioning
  • GitHub Actions برای CI/CD خودکار

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.