benchflow-ai

python-packaging

@benchflow-ai/python-packaging
benchflow-ai
230
165 forks
Updated 1/18/2026
View on GitHub

Automatically applies when configuring Python project packaging. Ensures proper pyproject.toml setup, project layout, build configuration, metadata, and distribution best practices.

Installation

$skills install @benchflow-ai/python-packaging
Claude Code
Cursor
Copilot
Codex
Antigravity

Details

Pathregistry/terminal_bench_2.0/terminal_bench_2_0_pypi-server/environment/skills/python-packaging/SKILL.md
Branchmain
Scoped Name@benchflow-ai/python-packaging

Usage

After installing, this skill will be available to your AI coding assistant.

Verify installation:

skills list

Skill Instructions


name: python-packaging description: Automatically applies when configuring Python project packaging. Ensures proper pyproject.toml setup, project layout, build configuration, metadata, and distribution best practices. category: python

Python Packaging Patterns

When packaging Python projects, follow these patterns for modern, maintainable package configuration.

Trigger Keywords: pyproject.toml, packaging, setup.py, build, distribution, package, publish, PyPI, wheel, sdist, project structure

Agent Integration: Used by backend-architect, devops-engineer, python-engineer

✅ Correct Pattern: pyproject.toml Setup

# pyproject.toml - Modern Python packaging
[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mypackage"
version = "0.1.0"
description = "A short description of your package"
readme = "README.md"
license = {text = "MIT"}
authors = [
    {name = "Your Name", email = "your.email@example.com"}
]
maintainers = [
    {name = "Maintainer Name", email = "maintainer@example.com"}
]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Topic :: Software Development :: Libraries",
]
keywords = ["example", "package", "keywords"]
requires-python = ">=3.11"

# Core dependencies
dependencies = [
    "fastapi>=0.109.0",
    "pydantic>=2.5.0",
    "sqlalchemy>=2.0.0",
    "httpx>=0.26.0",
]

# Optional dependencies (extras)
[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "pytest-cov>=4.1.0",
    "pytest-asyncio>=0.23.0",
    "black>=24.0.0",
    "ruff>=0.1.0",
    "mypy>=1.8.0",
]
docs = [
    "sphinx>=7.2.0",
    "sphinx-rtd-theme>=2.0.0",
]
all = [
    "mypackage[dev,docs]",
]

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

[project.scripts]
# Entry points for CLI commands
mypackage-cli = "mypackage.cli:main"

# Tool configurations
[tool.setuptools]
# Package discovery
packages = ["mypackage", "mypackage.submodule"]

[tool.setuptools.package-data]
mypackage = ["py.typed", "*.json", "templates/*.html"]

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
    "--strict-markers",
    "--cov=mypackage",
    "--cov-report=term-missing",
    "--cov-report=html",
]
markers = [
    "slow: marks tests as slow",
    "integration: marks tests as integration tests",
]

[tool.black]
line-length = 100
target-version = ["py311", "py312"]
include = '\.pyi?$'
exclude = '''
/(
    \.git
  | \.venv
  | build
  | dist
)/
'''

[tool.ruff]
line-length = 100
target-version = "py311"
select = [
    "E",  # pycodestyle errors
    "W",  # pycodestyle warnings
    "F",  # pyflakes
    "I",  # isort
    "B",  # flake8-bugbear
    "C4", # flake8-comprehensions
    "UP", # pyupgrade
]
ignore = []
exclude = [
    ".git",
    ".venv",
    "build",
    "dist",
]

[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_any_generics = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
check_untyped_defs = true

[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false

Project Structure

mypackage/
├── pyproject.toml          # Project configuration
├── README.md               # Project documentation
├── LICENSE                 # License file
├── CHANGELOG.md            # Version history
├── .gitignore              # Git ignore patterns
│
├── src/                    # Source code (src layout)
│   └── mypackage/
│       ├── __init__.py     # Package init
│       ├── __main__.py     # Entry point for -m
│       ├── py.typed        # PEP 561 marker
│       ├── core.py
│       ├── models.py
│       └── utils/
│           ├── __init__.py
│           └── helpers.py
│
├── tests/                  # Test suite
│   ├── __init__.py
│   ├── conftest.py        # Pytest fixtures
│   ├── test_core.py
│   └── test_models.py
│
├── docs/                   # Documentation
│   ├── conf.py
│   ├── index.rst
│   └── api.rst
│
└── scripts/                # Utility scripts
    └── setup_dev.sh

Package Init

# src/mypackage/__init__.py
"""
MyPackage - A Python package for doing things.

This package provides tools for X, Y, and Z.
"""

from mypackage.core import MainClass, main_function
from mypackage.models import Model1, Model2

# Version
__version__ = "0.1.0"

# Public API
__all__ = [
    "MainClass",
    "main_function",
    "Model1",
    "Model2",
]


# Convenience imports for common use cases
def quick_start():
    """Quick start helper for new users."""
    return MainClass()

CLI Entry Point

# src/mypackage/__main__.py
"""
Entry point for python -m mypackage.
"""
from mypackage.cli import main

if __name__ == "__main__":
    main()


# src/mypackage/cli.py
"""Command-line interface."""
import argparse
import sys
from typing import List, Optional


def main(argv: Optional[List[str]] = None) -> int:
    """
    Main CLI entry point.

    Args:
        argv: Command-line arguments (defaults to sys.argv)

    Returns:
        Exit code (0 for success, non-zero for error)
    """
    parser = argparse.ArgumentParser(
        prog="mypackage",
        description="MyPackage CLI tool",
    )

    parser.add_argument(
        "--version",
        action="version",
        version=f"%(prog)s {__version__}"
    )

    parser.add_argument(
        "--verbose",
        "-v",
        action="store_true",
        help="Enable verbose output"
    )

    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # Add subcommands
    init_parser = subparsers.add_parser("init", help="Initialize project")
    init_parser.add_argument("path", help="Project path")

    run_parser = subparsers.add_parser("run", help="Run the application")
    run_parser.add_argument("--config", help="Config file path")

    # Parse arguments
    args = parser.parse_args(argv)

    # Execute command
    if args.command == "init":
        return init_command(args)
    elif args.command == "run":
        return run_command(args)
    else:
        parser.print_help()
        return 1


def init_command(args) -> int:
    """Handle init command."""
    print(f"Initializing project at {args.path}")
    return 0


def run_command(args) -> int:
    """Handle run command."""
    print("Running application")
    return 0


if __name__ == "__main__":
    sys.exit(main())

Type Hints for Distribution

# src/mypackage/py.typed
# This file signals that the package supports type hints (PEP 561)
# Leave empty or add configuration if needed

Version Management

# src/mypackage/_version.py
"""Version information."""

__version__ = "0.1.0"
__version_info__ = tuple(int(i) for i in __version__.split("."))


# src/mypackage/__init__.py
from mypackage._version import __version__, __version_info__

__all__ = ["__version__", "__version_info__"]

Build and Distribution

# Build package
python -m build

# This creates:
# dist/mypackage-0.1.0-py3-none-any.whl  (wheel)
# dist/mypackage-0.1.0.tar.gz            (source distribution)

# Test installation locally
pip install dist/mypackage-0.1.0-py3-none-any.whl

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

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

Manifest for Non-Python Files

# MANIFEST.in - Include additional files in source distribution
include README.md
include LICENSE
include CHANGELOG.md
include pyproject.toml

recursive-include src/mypackage *.json
recursive-include src/mypackage *.yaml
recursive-include src/mypackage/templates *.html
recursive-include tests *.py

global-exclude __pycache__
global-exclude *.py[co]

Development Installation

# Install in editable mode with dev dependencies
pip install -e ".[dev]"

# Or with all optional dependencies
pip install -e ".[all]"

# This allows you to edit code without reinstalling

GitHub Actions for Publishing

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

on:
  release:
    types: [published]

jobs:
  build-and-publish:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - 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 distribution
        run: twine check dist/*

      - name: Publish to PyPI
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
        run: twine upload dist/*

❌ Anti-Patterns

# ❌ Using setup.py instead of pyproject.toml
# setup.py is legacy, use pyproject.toml

# ❌ No version pinning in dependencies
dependencies = ["fastapi"]  # Which version?

# ✅ Better: Pin compatible versions
dependencies = ["fastapi>=0.109.0,<1.0.0"]


# ❌ Flat package structure
mypackage.py  # Single file, hard to scale

# ✅ Better: Proper package structure
src/mypackage/__init__.py


# ❌ No py.typed marker
# Users can't benefit from type hints

# ✅ Better: Include py.typed
src/mypackage/py.typed


# ❌ Not specifying python_requires
# Package might be installed on incompatible Python

# ✅ Better: Specify Python version
requires-python = ">=3.11"


# ❌ No optional dependencies
dependencies = ["pytest", "sphinx"]  # Forces install!

# ✅ Better: Use optional-dependencies
[project.optional-dependencies]
dev = ["pytest"]
docs = ["sphinx"]

Best Practices Checklist

  • ✅ Use pyproject.toml for configuration
  • ✅ Use src/ layout for packages
  • ✅ Include py.typed for type hints
  • ✅ Specify Python version requirement
  • ✅ Pin dependency versions
  • ✅ Use optional-dependencies for extras
  • ✅ Include README, LICENSE, CHANGELOG
  • ✅ Define entry points for CLI tools
  • ✅ Configure tools in pyproject.toml
  • ✅ Test package installation locally
  • ✅ Use build and twine for publishing
  • ✅ Automate publishing with CI/CD

Auto-Apply

When creating Python packages:

  1. Use pyproject.toml for all configuration
  2. Use src/package_name/ layout
  3. Include py.typed marker
  4. Define optional-dependencies for dev/docs
  5. Pin dependency versions
  6. Include README and LICENSE
  7. Define CLI entry points if needed
  8. Configure testing and linting tools

Related Skills

  • dependency-management - For managing dependencies
  • type-safety - For type hints
  • pytest-patterns - For testing
  • git-workflow-standards - For releases
  • docs-style - For documentation