Automatically applies when configuring Python project packaging. Ensures proper pyproject.toml setup, project layout, build configuration, metadata, and distribution best practices.
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
skills listSkill 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:
- Use pyproject.toml for all configuration
- Use src/package_name/ layout
- Include py.typed marker
- Define optional-dependencies for dev/docs
- Pin dependency versions
- Include README and LICENSE
- Define CLI entry points if needed
- Configure testing and linting tools
Related Skills
dependency-management- For managing dependenciestype-safety- For type hintspytest-patterns- For testinggit-workflow-standards- For releasesdocs-style- For documentation
More by benchflow-ai
View allRepair an (often imperfect) Flexible Job Shop Scheduling baseline into a downtime-feasible, precedence-correct schedule while staying within policy budgets and matching the evaluator’s exact metrics and “local minimal right-shift” checks.
Test Temporal workflows with pytest, time-skipping, and mocking strategies. Covers unit testing, integration testing, replay testing, and local development setup. Use when implementing Temporal workflow tests or debugging test failures.
Extract locational marginal prices (LMPs) from DC-OPF solutions using dual values. Use when computing nodal electricity prices, reserve clearing prices, or performing price impact analysis.
This skill should be used when the user asks to "design package structure", "create managed package", "configure 2GP", "set up namespace", "version management", or mentions managed package topics like "LMA", "subscriber orgs", or "package versioning". Provides comprehensive guidance for second-generation managed package (2GP) architecture, ISV development patterns, and package lifecycle management.
