All skills
github avatar

/python-pypi-package-builder

@49fd3f3 official
by githubgithub/awesome-copilot40k stars
5,040

End-to-end skill for building, testing, linting, versioning, and publishing a production-grade Python library to PyPI. Covers all four build backends (setuptools+setuptools_scm, hatchling, flit, poetry), PEP 440 versioning, semantic versioning, dynamic git-tag versioning, OOP/SOLID design, type hints (PEP 484/526/544/561), Trusted Publishing (OIDC), and the full PyPA packaging flow. Use for: creating Python packages, pip-installable SDKs, CLI tools, framework plugins, pyproject.toml setup, py.typed, setuptools_scm, semver, mypy, pre-commit, GitHub Actions CI/CD, or PyPI publishing.

Use this Skill: https://skilld.dev/gh/github/awesome-copilot/python-pypi-package-builder

This session only. Nothing lands on disk.

referencestesting-quality.md

≈1.6k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Testing and Code Quality

Table of Contents

  1. conftest.py
  2. Unit tests
  3. Backend unit tests
  4. Running tests
  5. Code quality tools
  6. Pre-commit hooks

1. conftest.py

Use conftest.py to define shared fixtures. Keep fixtures focused — one fixture per concern. For async tests, use pytest-asyncio with asyncio_mode = "auto" in pyproject.toml.

# tests/conftest.py
import pytest
from your_package.core import YourClient
from your_package.backends.memory import MemoryBackend


@pytest.fixture
def memory_backend() -> MemoryBackend:
    return MemoryBackend()


@pytest.fixture
def client(memory_backend: MemoryBackend) -> YourClient:
    return YourClient(
        api_key="test-key",
        backend=memory_backend,
    )

2. Unit Tests

Test both the happy path and the edge cases (e.g. invalid inputs, error conditions).

# tests/test_core.py
import pytest
from your_package import YourClient
from your_package.exceptions import YourPackageError


def test_client_creates_with_valid_key():
    client = YourClient(api_key="sk-test")
    assert client is not None


def test_client_raises_on_empty_key():
    with pytest.raises(ValueError, match="api_key"):
        YourClient(api_key="")


def test_client_raises_on_invalid_timeout():
    with pytest.raises(ValueError, match="timeout"):
        YourClient(api_key="sk-test", timeout=-1)


@pytest.mark.asyncio
async def test_process_returns_expected_result(client: YourClient):
    result = await client.process({"input": "value"})
    assert "output" in result


@pytest.mark.asyncio
async def test_process_raises_on_invalid_input(client: YourClient):
    with pytest.raises(YourPackageError):
        await client.process({})  # empty input should fail

3. Backend Unit Tests

Test each backend independently, in isolation from the rest of the library. This makes failures easier to diagnose and ensures your abstract interface is actually implemented correctly.

# tests/test_backends.py
import pytest
from your_package.backends.memory import MemoryBackend


@pytest.mark.asyncio
async def test_set_and_get():
    backend = MemoryBackend()
    await backend.set("key1", "value1")
    result = await backend.get("key1")
    assert result == "value1"


@pytest.mark.asyncio
async def test_get_missing_key_returns_none():
    backend = MemoryBackend()
    result = await backend.get("nonexistent")
    assert result is None


@pytest.mark.asyncio
async def test_delete_removes_key():
    backend = MemoryBackend()
    await backend.set("key1", "value1")
    await backend.delete("key1")
    result = await backend.get("key1")
    assert result is None


@pytest.mark.asyncio
async def test_ttl_expires_entry():
    import asyncio
    backend = MemoryBackend()
    await backend.set("key1", "value1", ttl=1)
    await asyncio.sleep(1.1)
    result = await backend.get("key1")
    assert result is None


@pytest.mark.asyncio
async def test_different_keys_are_independent():
    backend = MemoryBackend()
    await backend.set("key1", "a")
    await backend.set("key2", "b")
    assert await backend.get("key1") == "a"
    assert await backend.get("key2") == "b"
    await backend.delete("key1")
    assert await backend.get("key2") == "b"

4. Running Tests

pip install -e ".[dev]"
pytest                           # All tests
pytest --cov --cov-report=html   # With HTML coverage report (opens in browser)
pytest -k "test_middleware"      # Filter by name
pytest -x                        # Stop on first failure
pytest -v                        # Verbose output

Coverage config in pyproject.toml enforces a minimum threshold (fail_under = 80). CI will fail if you drop below it, which catches coverage regressions automatically.


5. Code Quality Tools

Ruff (linting — replaces flake8, pylint, many others)

pip install ruff
ruff check .           # Check for issues
ruff check . --fix     # Auto-fix safe issues

Ruff is extremely fast and replaces most of the Python linting ecosystem. Configure it in pyproject.toml — see references/pyproject-toml.md for the full config.

Black (formatting)

pip install black
black .                # Format all files
black . --check        # CI mode — reports issues without modifying files

isort (import sorting)

pip install isort
isort .                # Sort imports
isort . --check-only   # CI mode

Always set profile = "black" in [tool.isort] — otherwise black and isort conflict.

mypy (static type checking)

pip install mypy
mypy your_package/   # Type-check your package source only

Common fixes:

  • ignore_missing_imports = true — ignore untyped third-party deps
  • from __future__ import annotations — enables PEP 563 deferred evaluation (Python 3.9 compat)
  • pip install types-redis — type stubs for the redis library

Run all at once

ruff check . && black . --check && isort . --check-only && mypy your_package/

6. Pre-commit Hooks

Pre-commit runs all quality tools automatically before each commit, so issues never reach CI. Install once per clone with pre-commit install.

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.4.4
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/psf/black
    rev: 24.4.2
    hooks:
      - id: black

  - repo: https://github.com/pycqa/isort
    rev: 5.13.2
    hooks:
      - id: isort

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.10.0
    hooks:
      - id: mypy
        additional_dependencies: [types-redis]  # Add stubs for typed dependencies

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-toml
      - id: check-merge-conflict
      - id: debug-statements
      - id: no-commit-to-branch
        args: [--branch, master, --branch, main]
pip install pre-commit
pre-commit install           # Install once per clone
pre-commit run --all-files   # Run all hooks manually (useful before the first install)

The no-commit-to-branch hook prevents accidentally committing directly to main/master, which would bypass CI checks. Always work on a feature branch.

Source: SKILL.md on GitHub

No alerts10d4 checks · Risk SAFE
  • Gen Agent Trust Hub10d

    This skill provides a comprehensive and secure framework for building, testing, and publishing Python packages following industry best practices. It incorporates secure publishing via OpenID Connect (OIDC), uses modern and efficient tooling like Ruff, and provides a local scaffolding script that follows standard templating procedures. All external references and dependencies target well-known and trusted organizations within the Python and GitHub ecosystems.

  • Socket10d

    No alerts

  • Snyk10d

    Risk: LOW · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 49fd3f3. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 6 months ago
  • Python
  • pypi
  • packaging
  • setuptools
  • hatchling
  • flit
  • poetry
  • versioning
  • ci-cd
  • github-actions

README badge

README badge for github/awesome-copilot/python-pypi-package-builder

Covers the full PyPI publishing pipeline for Python packages: build backend selection (setuptools+setuptools_scm, hatchling, flit, poetry), project structure (src/ vs flat), type hints, testing, versioning (PEP 440 / semver / git-tag), and GitHub Actions CI/CD with Trusted Publishing. Targets teams building pip-installable libraries, SDKs, CLI tools, or framework plugins.

Generated from the current SKILL.md.

Which build backend should I use?
Use setuptools + setuptools_scm if you want version derived from git tags. Use hatchling or flit for pure-Python projects without git-tag versioning. Use poetry v2+ if you want an all-in-one tool. Use setuptools only if you have C/Cython extensions.
Does this skill support publishing with Trusted Publishing (OIDC)?
Yes. The skill covers Trusted Publishing setup in the CI/publishing workflow references and includes GitHub Actions templates for OIDC-based PyPI publishing.
What folder structure should I use for a new package?
Use src/ layout as the default for new projects — it prevents accidental imports of uninstalled code and is PyPA-recommended. Use flat layout only for single-purpose packages with 1–4 modules.
Does this cover type hints and mypy configuration?
Yes. The skill includes PEP 484/526/544/561 type hint patterns, py.typed marker file setup, and complete mypy configuration in the testing and quality reference.
Can I use this skill for CLI tools, SDKs, and plugins?
Yes. The skill covers all package types including utility libraries, API clients/SDKs, CLI tools, framework plugins, and data processing libraries, with patterns and entry-point configuration for each.

Generated from the current SKILL.md. These answers refresh after source changes.