Skip to content
Back to skills

Python Tooling

ASecurity

Python project tooling: pyproject.toml as the single source of truth, package managers (pip, Poetry, uv, pipenv), virtual environments, lockfiles, ruff (lint + format), mypy (type checking), and console scripts / entry points. Stack-agnostic — referenced by every Python plugin in the marketplace. Use this skill to: - Read pyproject.toml (or requirements.txt / Pipfile) to learn which package manager and Python version the project uses. - Install, add, and remove dependencies with the project'...

  • 35 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 22, 2026
toolspythongoshellbashdjangoflasktestinggitbackend

Works with

  • cli

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 22, 2026

npx -y skills add AratKruglik/claude-sdlc --skill python-tooling --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Python Tooling?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Python Tooling
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aratkruglik-python-tooling/badge)](https://www.skillsdirectory.com/skills/aratkruglik-python-tooling)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: python-tooling
description: |
  Python project tooling: pyproject.toml as the single source of truth, package managers (pip, Poetry, uv, pipenv), virtual environments, lockfiles, ruff (lint + format), mypy (type checking), and console scripts / entry points. Stack-agnostic — referenced by every Python plugin in the marketplace.

  Use this skill to:
  - Read pyproject.toml (or requirements.txt / Pipfile) to learn which package manager and Python version the project uses.
  - Install, add, and remove dependencies with the project's package manager.
  - Run ruff for formatting and linting, mypy for type checking.
  - Define console scripts and entry points for CLI applications.

  Do NOT use this skill for:
  - Language idioms — see python-foundation:python-conventions.
  - Testing patterns — see python-foundation:pytest-testing.
  - Framework-specific tooling (django management commands, flask CLI) — see framework plugin skills.
user-invocable: false
paths: ["pyproject.toml", "ruff.toml", "setup.cfg", "requirements*.txt", "tox.ini"]
---

# Python Tooling (stack-agnostic)

`pyproject.toml` is the single source of truth for a modern Python project. Read it before doing anything else — it tells you the Python version, the package manager, the linting config, and the test settings.

## Detection — which package manager is active

Check for lockfiles in this order:

```bash
test -f poetry.lock   && echo "Poetry"
test -f uv.lock       && echo "uv"
test -f Pipfile.lock  && echo "Pipenv"
test -f requirements.txt && echo "pip"
```

| Signal | Package manager | Install command |
|---|---|---|
| `poetry.lock` | Poetry | `poetry install` |
| `uv.lock` | uv | `uv sync` |
| `Pipfile.lock` | Pipenv | `pipenv install` |
| `requirements.txt` only | pip | `pip install -r requirements.txt` |

When only a `pyproject.toml` with a `[build-system]` section exists but no lockfile, check `[build-system].requires` to identify the build backend (setuptools, hatch, flit) and fall back to `pip install -e .`.

Always use the project's existing package manager. Do not mix managers (e.g., do not run `pip install` in a Poetry project).

## pyproject.toml — canonical structure

A fully configured `pyproject.toml` for a PEP 621 project:

```toml
[project]
name = "myapp"
version = "0.1.0"
description = "Short project description"
requires-python = ">=3.10"
dependencies = [
    "httpx>=0.27",
    "pydantic>=2.6",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0",
    "pytest-cov>=5.0",
    "ruff>=0.4",
    "mypy>=1.10",
]

[project.scripts]
myapp = "myapp.cli:main"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

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

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]
ignore = ["E501"]

[tool.mypy]
python_version = "3.10"
strict = true
ignore_missing_imports = true

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--strict-markers -q"

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

[tool.coverage.report]
fail_under = 80
show_missing = true
```

For Poetry projects the structure differs slightly — dependencies live under `[tool.poetry.dependencies]` and scripts under `[tool.poetry.scripts]`. The `[tool.ruff]`, `[tool.mypy]`, and `[tool.pytest.ini_options]` sections are identical regardless of build backend.

## ruff — lint and format

ruff replaces flake8, isort, pyupgrade, and black in a single fast tool. Configure it in `pyproject.toml`:

```toml
[tool.ruff]
line-length = 88
target-version = "py310"

[tool.ruff.lint]
# E/F = pycodestyle + pyflakes, I = isort, UP = pyupgrade, B = bugbear, SIM = simplify
select = ["E", "F", "I", "UP", "B", "SIM"]
ignore = ["E501"]  # line-too-long — formatter handles this

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

Key commands:

```bash
ruff check .            # lint — report issues
ruff check --fix .      # lint — auto-fix safe issues
ruff format .           # format — applies opinionated style (like black)
ruff format --check .   # format — exit non-zero if files would change (CI mode)
```

Run both in CI:

```bash
ruff format --check . && ruff check .
```

ruff's formatter is intentionally compatible with black's output — switching a black project to ruff format requires no manual changes.

## mypy — type checking

Configure mypy in `pyproject.toml`:

```toml
[tool.mypy]
python_version = "3.10"
strict = true
ignore_missing_imports = true
```

`strict = true` enables a comprehensive set of checks including `disallow_untyped_defs`, `warn_return_any`, `warn_unused_ignores`, and others. Start with `strict` on new projects; on legacy projects enable checks incrementally.

Per-module overrides for third-party packages that ship without stubs:

```toml
[[tool.mypy.overrides]]
module = ["boto3.*", "botocore.*"]
ignore_missing_imports = true
```

Add a `py.typed` marker file to signal that your package ships inline type information:

```bash
touch src/myapp/py.typed
```

Then declare it in `pyproject.toml`:

```toml
[tool.hatch.build.targets.wheel]
include = ["src/myapp/py.typed"]
```

Run mypy:

```bash
mypy src/          # check all source files
mypy src/myapp/service.py   # check a single file
```

## Virtual environments

Each package manager manages its own virtual environment:

```bash
# pip — manual venv
python -m venv .venv
source .venv/bin/activate      # Linux/macOS
.venv\Scripts\activate         # Windows
pip install -e ".[dev]"

# Poetry — automatic venv
poetry install                 # creates .venv in project root (or in cache)
poetry shell                   # activate
poetry run pytest              # run without activating

# uv — automatic venv
uv sync                        # creates .venv and installs from uv.lock
uv run pytest                  # run without activating

# Pipenv — automatic venv
pipenv install --dev
pipenv shell
pipenv run pytest
```

Never commit the virtual environment. Add to `.gitignore`:

```
.venv/
__pycache__/
*.pyc
*.pyo
.mypy_cache/
.ruff_cache/
.pytest_cache/
dist/
*.egg-info/
```

## Common commands — pip / Poetry / uv

| Task | pip | Poetry | uv |
|---|---|---|---|
| Install all deps | `pip install -r requirements.txt` | `poetry install` | `uv sync` |
| Install dev deps | `pip install -e ".[dev]"` | `poetry install --with dev` | `uv sync --extra dev` |
| Add runtime dep | `pip install X` then freeze | `poetry add X` | `uv add X` |
| Add dev dep | `pip install X` then freeze | `poetry add --group dev X` | `uv add --dev X` |
| Remove dep | `pip uninstall X` then freeze | `poetry remove X` | `uv remove X` |
| Update dep | `pip install -U X` then freeze | `poetry update X` | `uv lock --upgrade-package X && uv sync` |
| Run script | `python -m mymodule` | `poetry run python -m mymodule` | `uv run python -m mymodule` |
| Run tool | `ruff check .` | `poetry run ruff check .` | `uv run ruff check .` |
| Show dep tree | `pip list` | `poetry show --tree` | `uv tree` |

For pip projects, always regenerate the lockfile after adding or changing a dependency:

```bash
pip freeze > requirements.txt
```

Consider splitting into `requirements.txt` (runtime) and `requirements-dev.txt` (dev/test) to keep production images lean.

## Console scripts

Define CLI entry points in `pyproject.toml` so users can run your tool by name after installation:

```toml
# PEP 621 / hatch / setuptools
[project.scripts]
myapp = "myapp.cli:main"
myapp-worker = "myapp.worker:run"
```

```toml
# Poetry
[tool.poetry.scripts]
myapp = "myapp.cli:main"
```

The value is `"package.module:callable"`. The callable must accept no arguments (or parse `sys.argv` internally):

```python
# src/myapp/cli.py
import argparse
import sys


def main() -> None:
    parser = argparse.ArgumentParser(description="myapp CLI")
    parser.add_argument("command", choices=["serve", "migrate"])
    args = parser.parse_args()
    match args.command:
        case "serve":
            from myapp.server import serve
            serve()
        case "migrate":
            from myapp.db import migrate
            migrate()
        case _:
            parser.print_help()
            sys.exit(1)
```

After installation (`pip install -e .` / `poetry install` / `uv sync`), the script is available on `$PATH`:

```bash
myapp serve
myapp migrate
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…