AgentSkillsCN

python-packaging

为Python软件包开发提供专业指导,涵盖pyproject.toml配置、构建后端(hatchling、setuptools、flit、pdm)、分发打包以及PyPI发布等环节。当您需要创建、配置或发布Python软件包时,可使用此技能。

SKILL.md
--- frontmatter
name: python-packaging
description: Expert guidance for Python package development, including pyproject.toml configuration, build backends (hatchling, setuptools, flit, pdm), distribution creation, and PyPI publishing. Use when creating, configuring, or publishing Python packages.

Python Package Development

This skill provides expert guidance for developing, configuring, and publishing Python packages following modern best practices.

Project Structure

A properly structured Python package follows this layout:

code
project_name/
├── LICENSE
├── README.md
├── pyproject.toml
├── src/
│   └── package_name/
│       ├── __init__.py
│       └── module.py
└── tests/
    └── test_module.py

Key principles:

  • Use the src/ layout to prevent accidental imports from the working directory
  • Package directory name should match the distribution name (with underscores for hyphens)
  • Always include __init__.py for regular packages (not namespace packages)
  • Include py.typed marker file for typed packages

pyproject.toml Configuration

Build System

Choose a build backend based on project needs:

Hatchling (recommended for most projects):

toml
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"

Setuptools (for complex builds or C extensions):

toml
[build-system]
requires = ["setuptools >= 77.0.3"]
build-backend = "setuptools.build_meta"

Flit (minimal, pure Python packages):

toml
[build-system]
requires = ["flit_core >= 3.12.0, <4"]
build-backend = "flit_core.buildapi"

PDM (with PEP 582 local packages):

toml
[build-system]
requires = ["pdm-backend >= 2.4.0"]
build-backend = "pdm.backend"

Project Metadata

toml
[project]
name = "package-name"
version = "0.1.0"
description = "A short description of the package"
readme = "README.md"
requires-python = ">=3.11"
license = "MIT"
license-files = ["LICENSE"]
authors = [
    { name = "Author Name", email = "author@example.com" }
]
maintainers = [
    { name = "Maintainer Name", email = "maintainer@example.com" }
]
keywords = ["keyword1", "keyword2"]
classifiers = [
    "Development Status :: 3 - Alpha",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Typing :: Typed",
]
dependencies = [
    "numpy>=1.24",
    "pandas>=2.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
    "pytest-cov>=4.1",
    "ruff>=0.5",
    "mypy>=1.8",
]
docs = [
    "mkdocs>=1.6",
    "mkdocs-material>=9.5",
]

[project.urls]
Homepage = "https://github.com/user/project"
Documentation = "https://project.readthedocs.io"
Repository = "https://github.com/user/project"
Issues = "https://github.com/user/project/issues"
Changelog = "https://github.com/user/project/blob/main/CHANGELOG.md"

[project.scripts]
cli-command = "package_name.cli:main"

[project.entry-points."group.name"]
plugin-name = "package_name.plugin:PluginClass"

Build Backend Configuration

Hatchling:

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

[tool.hatch.build.targets.sdist]
include = ["src/", "tests/", "README.md", "LICENSE"]

Setuptools:

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

[tool.setuptools.package-data]
"*" = ["py.typed", "*.pyi"]

Development Tools

toml
[tool.pytest.ini_options]
addopts = "-ra --strict-markers"
testpaths = ["tests"]
pythonpath = ["src"]

[tool.ruff]
line-length = 88
target-version = "py311"
src = ["src"]

[tool.ruff.lint]
select = ["E", "F", "W", "I", "UP", "B", "A", "C4", "DTZ", "T10", "ISC"]
ignore = ["E501"]

[tool.ruff.lint.isort]
known-first-party = ["package_name"]

[tool.mypy]
python_version = "3.11"
strict = true
warn_unused_configs = true
mypy_path = "src"

[[tool.mypy.overrides]]
module = ["pandas.*", "numpy.*"]
ignore_missing_imports = true

[tool.coverage.run]
source = ["src"]
branch = true

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "if TYPE_CHECKING:",
    "@overload",
]

Building and Publishing

Build Distribution Archives

bash
# Install build tool
python -m pip install --upgrade build

# Build source distribution and wheel
python -m build

# Output in dist/:
#   package_name-0.1.0.tar.gz (source distribution)
#   package_name-0.1.0-py3-none-any.whl (wheel)

Upload to PyPI

bash
# Install twine
python -m pip install --upgrade twine

# Upload to TestPyPI first
python -m twine upload --repository testpypi dist/*

# Upload to PyPI
python -m twine upload dist/*

Test Installation

bash
# From TestPyPI
python -m pip install --index-url https://test.pypi.org/simple/ --no-deps package-name

# From PyPI
python -m pip install package-name

Version Management

Manual Versioning

Update version in pyproject.toml directly.

Dynamic Versioning with Hatchling

toml
[project]
dynamic = ["version"]

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

In __init__.py:

python
__version__ = "0.1.0"

Git Tag Versioning

toml
[project]
dynamic = ["version"]

[tool.hatch.version]
source = "vcs"

[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

Best Practices

  1. Always use src/ layout - Prevents import confusion during development
  2. Pin minimum versions, not maximum - numpy>=1.24 not numpy>=1.24,<2.0
  3. Use optional dependencies - Group dev, docs, test dependencies separately
  4. Include py.typed marker - For packages with type annotations
  5. Write comprehensive README - Include installation, quick start, and examples
  6. Choose appropriate license - MIT, Apache-2.0, or BSD-3-Clause for open source
  7. Add classifiers - Help users find your package on PyPI
  8. Test on TestPyPI first - Validate package before publishing to PyPI
  9. Use semantic versioning - MAJOR.MINOR.PATCH format
  10. Automate releases with CI/CD - GitHub Actions for testing and publishing

Common Issues

Package not found after install

  • Ensure packages is correctly configured for your build backend
  • Check src/ layout is properly set up

Import errors in tests

  • Add pythonpath = ["src"] to pytest configuration
  • Or install package in editable mode: pip install -e .

Missing package data

  • Configure package-data or include in build backend settings
  • For non-Python files, use MANIFEST.in with setuptools

Type hints not recognized

  • Add py.typed marker file to package root
  • Ensure package-data includes *.pyi files