npx skills add https://github.com/wshobson/agents --skill python-packagingHow Python Packaging fits into a Paperclip company.
Python Packaging drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
SKILL.md510 linesExpandCollapse
---name: python-packagingdescription: Create distributable Python packages with proper project structure, setup.py/pyproject.toml, and publishing to PyPI. Use when packaging Python libraries, creating CLI tools, or distributing Python code.--- # Python Packaging Comprehensive guide to creating, structuring, and distributing Python packages using modern packaging tools, pyproject.toml, and publishing to PyPI. ## When to Use This Skill - Creating Python libraries for distribution- Building command-line tools with entry points- Publishing packages to PyPI or private repositories- Setting up Python project structure- Creating installable packages with dependencies- Building wheels and source distributions- Versioning and releasing Python packages- Creating namespace packages- Implementing package metadata and classifiers ## Core Concepts ### 1. Package Structure - **Source layout**: `src/package_name/` (recommended)- **Flat layout**: `package_name/` (simpler but less flexible)- **Package metadata**: pyproject.toml, setup.py, or setup.cfg- **Distribution formats**: wheel (.whl) and source distribution (.tar.gz) ### 2. Modern Packaging Standards - **PEP 517/518**: Build system requirements- **PEP 621**: Metadata in pyproject.toml- **PEP 660**: Editable installs- **pyproject.toml**: Single source of configuration ### 3. Build Backends - **setuptools**: Traditional, widely used- **hatchling**: Modern, opinionated- **flit**: Lightweight, for pure Python- **poetry**: Dependency management + packaging ### 4. Distribution - **PyPI**: Python Package Index (public)- **TestPyPI**: Testing before production- **Private repositories**: JFrog, AWS CodeArtifact, etc. ## Quick Start ### Minimal Package Structure ```my-package/├── pyproject.toml├── README.md├── LICENSE├── src/│ └── my_package/│ ├── __init__.py│ └── module.py└── tests/ └── test_module.py``` ### Minimal pyproject.toml ```toml[build-system]requires = ["setuptools>=61.0"]build-backend = "setuptools.build_meta" [project]name = "my-package"version = "0.1.0"description = "A short description"authors = [{name = "Your Name", email = "you@example.com"}]readme = "README.md"requires-python = ">=3.8"dependencies = [ "requests>=2.28.0",] [project.optional-dependencies]dev = [ "pytest>=7.0", "black>=22.0",]``` ## Package Structure Patterns ### Pattern 1: Source Layout (Recommended) ```my-package/├── pyproject.toml├── README.md├── LICENSE├── .gitignore├── src/│ └── my_package/│ ├── __init__.py│ ├── core.py│ ├── utils.py│ └── py.typed # For type hints├── tests/│ ├── __init__.py│ ├── test_core.py│ └── test_utils.py└── docs/ └── index.md``` **Advantages:** - Prevents accidentally importing from source- Cleaner test imports- Better isolation **pyproject.toml for source layout:** ```toml[tool.setuptools.packages.find]where = ["src"]``` ### Pattern 2: Flat Layout ```my-package/├── pyproject.toml├── README.md├── my_package/│ ├── __init__.py│ └── module.py└── tests/ └── test_module.py``` **Simpler but:** - Can import package without installing- Less professional for libraries ### Pattern 3: Multi-Package Project ```project/├── pyproject.toml├── packages/│ ├── package-a/│ │ └── src/│ │ └── package_a/│ └── package-b/│ └── src/│ └── package_b/└── tests/``` ## Complete pyproject.toml Examples ### Pattern 4: Full-Featured pyproject.toml ```toml[build-system]requires = ["setuptools>=61.0", "wheel"]build-backend = "setuptools.build_meta" [project]name = "my-awesome-package"version = "1.0.0"description = "An awesome Python package"readme = "README.md"requires-python = ">=3.8"license = {text = "MIT"}authors = [ {name = "Your Name", email = "you@example.com"},]maintainers = [ {name = "Maintainer Name", email = "maintainer@example.com"},]keywords = ["example", "package", "awesome"]classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12",] dependencies = [ "requests>=2.28.0,<3.0.0", "click>=8.0.0", "pydantic>=2.0.0",] [project.optional-dependencies]dev = [ "pytest>=7.0.0", "pytest-cov>=4.0.0", "black>=23.0.0", "ruff>=0.1.0", "mypy>=1.0.0",]docs = [ "sphinx>=5.0.0", "sphinx-rtd-theme>=1.0.0",]all = [ "my-awesome-package[dev,docs]",] [project.urls]Homepage = "https://github.com/username/my-awesome-package"Documentation = "https://my-awesome-package.readthedocs.io"Repository = "https://github.com/username/my-awesome-package""Bug Tracker" = "https://github.com/username/my-awesome-package/issues"Changelog = "https://github.com/username/my-awesome-package/blob/main/CHANGELOG.md" [project.scripts]my-cli = "my_package.cli:main"awesome-tool = "my_package.tools:run" [project.entry-points."my_package.plugins"]plugin1 = "my_package.plugins:plugin1" [tool.setuptools]package-dir = {"" = "src"}zip-safe = false [tool.setuptools.packages.find]where = ["src"]include = ["my_package*"]exclude = ["tests*"] [tool.setuptools.package-data]my_package = ["py.typed", "*.pyi", "data/*.json"] # Black configuration[tool.black]line-length = 100target-version = ["py38", "py39", "py310", "py311"]include = '\.pyi?$' # Ruff configuration[tool.ruff]line-length = 100target-version = "py38" [tool.ruff.lint]select = ["E", "F", "I", "N", "W", "UP"] # MyPy configuration[tool.mypy]python_version = "3.8"warn_return_any = truewarn_unused_configs = truedisallow_untyped_defs = true # Pytest configuration[tool.pytest.ini_options]testpaths = ["tests"]python_files = ["test_*.py"]addopts = "-v --cov=my_package --cov-report=term-missing" # Coverage configuration[tool.coverage.run]source = ["src"]omit = ["*/tests/*"] [tool.coverage.report]exclude_lines = [ "pragma: no cover", "def __repr__", "raise AssertionError", "raise NotImplementedError",]``` ### Pattern 5: Dynamic Versioning ```toml[build-system]requires = ["setuptools>=61.0", "setuptools-scm>=8.0"]build-backend = "setuptools.build_meta" [project]name = "my-package"dynamic = ["version"]description = "Package with dynamic version" [tool.setuptools.dynamic]version = {attr = "my_package.__version__"} # Or use setuptools-scm for git-based versioning[tool.setuptools_scm]write_to = "src/my_package/_version.py"``` **In **init**.py:** ```python# src/my_package/__init__.py__version__ = "1.0.0" # Or with setuptools-scmfrom importlib.metadata import version__version__ = version("my-package")``` ## Command-Line Interface (CLI) Patterns ### Pattern 6: CLI with Click ```python# src/my_package/cli.pyimport click @click.group()@click.version_option()def cli(): """My awesome CLI tool.""" pass @cli.command()@click.argument("name")@click.option("--greeting", default="Hello", help="Greeting to use")def greet(name: str, greeting: str): """Greet someone.""" click.echo(f"{greeting}, {name}!") @cli.command()@click.option("--count", default=1, help="Number of times to repeat")def repeat(count: int): """Repeat a message.""" for i in range(count): click.echo(f"Message {i + 1}") def main(): """Entry point for CLI.""" cli() if __name__ == "__main__": main()``` **Register in pyproject.toml:** ```toml[project.scripts]my-tool = "my_package.cli:main"``` **Usage:** ```bashpip install -e .my-tool greet Worldmy-tool greet Alice --greeting="Hi"my-tool repeat --count=3``` ### Pattern 7: CLI with argparse ```python# src/my_package/cli.pyimport argparseimport sys def main(): """Main CLI entry point.""" parser = argparse.ArgumentParser( description="My awesome tool", prog="my-tool" ) parser.add_argument( "--version", action="version", version="%(prog)s 1.0.0" ) subparsers = parser.add_subparsers(dest="command", help="Commands") # Add subcommand process_parser = subparsers.add_parser("process", help="Process data") process_parser.add_argument("input_file", help="Input file path") process_parser.add_argument( "--output", "-o", default="output.txt", help="Output file path" ) args = parser.parse_args() if args.command == "process": process_data(args.input_file, args.output) else: parser.print_help() sys.exit(1) def process_data(input_file: str, output_file: str): """Process data from input to output.""" print(f"Processing {input_file} -> {output_file}") if __name__ == "__main__": main()``` ## Building and Publishing ### Pattern 8: Build Package Locally ```bash# Install build toolspip install build twine # Build distributionpython -m build # This creates:# dist/# my-package-1.0.0.tar.gz (source distribution)# my_package-1.0.0-py3-none-any.whl (wheel) # Check the distributiontwine check dist/*``` ### Pattern 9: Publishing to PyPI ```bash# Install publishing toolspip install twine # Test on TestPyPI firsttwine upload --repository testpypi dist/* # Install from TestPyPI to testpip install --index-url https://test.pypi.org/simple/ my-package # If all good, publish to PyPItwine upload dist/*``` **Using API tokens (recommended):** ```bash# Create ~/.pypirc[distutils]index-servers = pypi testpypi [pypi]username = __token__password = pypi-...your-token... [testpypi]username = __token__password = pypi-...your-test-token...``` ### Pattern 10: Automated Publishing with GitHub Actions ```yaml# .github/workflows/publish.ymlname: Publish to PyPI on: release: types: [created] jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.11" - name: Install dependencies run: | pip install build twine - name: Build package run: python -m build - name: Check package run: twine check dist/* - name: Publish to PyPI env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: twine upload dist/*``` For advanced patterns including data files, namespace packages, C extensions, version management, testing installation, documentation templates, and distribution workflows, see [references/advanced-patterns.md](references/advanced-patterns.md)Accessibility Compliance
This walks you through implementing proper WCAG 2.2 compliance with real code patterns for screen readers, keyboard navigation, and mobile accessibility. It cov
Airflow Dag Patterns
If you're building data pipelines with Airflow, this skill gives you production-ready DAG patterns that actually work in the real world. It covers TaskFlow API
Angular Migration
Migrating from AngularJS to Angular is notoriously painful, and this skill tackles the practical stuff that makes or breaks these projects. It covers hybrid app