Generated banner for Modern Python
Generated logo for Modern Python

Modern Python

Trail of BitsOfficialPublished by Trail of Bits

  • Skill
  • Tool integration
  • Security
  • CC-BY-SA-4.0
  • Medium risk

From the author

Configures Python projects with modern tooling (uv, ruff, ty). Use when creating projects, writing standalone scripts, or migrating from pip/Poetry/mypy/black.
— the description by Trail of Bits

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach to setting up and maintaining Python projects using contemporary tooling. It focuses on replacing legacy workflows with tools such as uv for dependency management, ruff for linting and formatting, and ty for type checking. The guide includes instructions for new project bootstrapping, migration strategies for existing codebases, and best practices for dependency management and security.

What it does

  • Provides configuration patterns for pyproject.toml
  • Outlines migration steps from legacy tools like pip, Poetry, and mypy
  • Defines best practices for dependency management using uv
  • Offers guidance on setting up linting, formatting, and type checking
  • Includes security-focused setup instructions for dependency auditing
  • Provides templates for project structure and CI integration

When to use it

  • When starting a new Python project or package
  • When migrating an existing project from legacy tools
  • When configuring development environments for linting and testing
  • When writing standalone Python scripts with external dependencies

Advantages

  • Centralizes project configuration in pyproject.toml
  • Standardizes development workflows across projects
  • Provides clear paths for migrating away from legacy tooling
  • Integrates security auditing into the development lifecycle

Usage examples

“How do I start a new Python project with modern tools?”
The agent will guide you through using uv init to create a project structure and configure your pyproject.toml with recommended dependency groups.
“Migrate my existing project from pip and flake8.”
The agent will provide a step-by-step plan to remove legacy files, initialize uv, and configure ruff for linting and formatting.
“How should I manage dev dependencies?”
The agent will explain how to use dependency groups in pyproject.toml and the uv sync command to manage your development environment.
“How do I run a script with temporary dependencies?”
The agent will demonstrate the use of uv run --with to execute scripts without adding dependencies to your project configuration.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/trailofbits/modern-python/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip modern-python.zip -d .agents/skills

Documentation by Trail of Bits

Modern Python

Guide for modern Python tooling and best practices, based on trailofbits/cookiecutter-python.

When to Use This Skill

  • Creating a new Python project or package

  • Setting up pyproject.toml configuration

  • Configuring development tools (linting, formatting, testing)

  • Writing Python scripts with external dependencies

  • Migrating from legacy tools (when user requests it)

When NOT to Use This Skill

  • User wants to keep legacy tooling: Respect existing workflows if explicitly requested

  • Python < 3.11 required: These tools target modern Python

  • Non-Python projects: Mixed codebases where Python isn't primary

Anti-Patterns to Avoid

AvoidUse Instead
[tool.ty] python-version[tool.ty.environment] python-version
uv pip installuv add and uv sync
Editing pyproject.toml manually to add depsuv add <pkg> / uv remove <pkg>
hatchling build backenduv_build (simpler, sufficient for most cases)
Poetryuv (faster, simpler, better ecosystem integration)
requirements.txtPEP 723 for scripts, pyproject.toml for projects
mypy / pyrightty (faster, from Astral team)
[project.optional-dependencies] for dev tools[dependency-groups] (PEP 735)
Manual virtualenv activation (source .venv/bin/activate)uv run <cmd>
pre-commitprek (faster, no Python runtime needed)

Key principles:

  • Always use uv add and uv remove to manage dependencies

  • Never manually activate or manage virtual environments—use uv run for all commands

  • Use [dependency-groups] for dev/test/docs dependencies, not [project.optional-dependencies]

Decision Tree

What are you doing?
│
├─ Single-file script with dependencies?
│   └─ Use PEP 723 inline metadata (./references/pep723-scripts.md)
│
├─ New multi-file project (not distributed)?
│   └─ Minimal uv setup (see Quick Start below)
│
├─ New reusable package/library?
│   └─ Full project setup (see Full Setup below)
│
└─ Migrating existing project?
    └─ See Migration Guide below

Tool Overview

ToolPurposeReplaces
uvPackage/dependency managementpip, virtualenv, pip-tools, pipx, pyenv
ruffLinting AND formattingflake8, black, isort, pyupgrade, pydocstyle
tyType checkingmypy, pyright (faster alternative)
pytestTesting with coverageunittest
prekPre-commit hooks (setup)pre-commit (faster, Rust-native)
Security Tools
ToolPurposeWhen It Runs
shellcheckShell script lintingpre-commit
detect-secretsSecret detectionpre-commit
actionlintWorkflow syntax validationpre-commit, CI
zizmorWorkflow security auditpre-commit, CI
pip-auditDependency vulnerability scanningCI, manual
DependabotAutomated dependency updatesscheduled

See security-setup.md for configuration and usage.

Quick Start: Minimal Project

For simple multi-file projects not intended for distribution:

# Create project with uv
uv init myproject
cd myproject

# Add dependencies
uv add requests rich

# Add dev dependencies
uv add --group dev pytest ruff ty

# Run code
uv run python src/myproject/main.py

# Run tools
uv run pytest
uv run ruff check .

Full Project Setup

If starting from scratch, ask the user if they prefer to use the Trail of Bits cookiecutter template to bootstrap a complete project with already preconfigured tooling.

uvx cookiecutter gh:trailofbits/cookiecutter-python
1. Create Project Structure
uv init --package myproject
cd myproject

This creates:

myproject/
├── pyproject.toml
├── README.md
├── src/
│   └── myproject/
│       └── __init__.py
└── .python-version
2. Configure pyproject.toml

See pyproject.md for complete configuration reference.

Key sections:

[project]
name = "myproject"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = []

[dependency-groups]
dev = [{include-group = "lint"}, {include-group = "test"}, {include-group = "audit"}]
lint = ["ruff", "ty"]
test = ["pytest", "pytest-cov"]
audit = ["pip-audit"]

[tool.ruff]
line-length = 100
target-version = "py311"

[tool.ruff.lint]
select = ["ALL"]
ignore = ["D", "COM812", "ISC001"]

[tool.pytest]
addopts = ["--cov=myproject", "--cov-fail-under=80"]

[tool.ty.terminal]
error-on-warning = true

[tool.ty.environment]
python-version = "3.11"

[tool.ty.rules]
# Strict from day 1 for new projects
possibly-unresolved-reference = "error"
unused-ignore-comment = "warn"
3. Install Dependencies
# Install all dependency groups
uv sync --all-groups

# Or install specific groups
uv sync --group dev
4. Add Makefile
.PHONY: dev lint format test build

dev:
	uv sync --all-groups

lint:
	uv run ruff format --check && uv run ruff check && uv run ty check src/

format:
	uv run ruff format .

test:
	uv run pytest

build:
	uv build

Migration Guide

When a user requests migration from legacy tooling:

From requirements.txt + pip

First, determine the nature of the code:

For standalone scripts: Convert to PEP 723 inline metadata (see pep723-scripts.md)

For projects:

# Initialize uv in existing project
uv init --bare

# Add dependencies using uv (not by editing pyproject.toml)
uv add requests rich  # add each package

# Or import from requirements.txt (review each package before adding)
# Note: Complex version specifiers may need manual handling
grep -v '^#' requirements.txt | grep -v '^-' | grep -v '^\s*$' | while read -r pkg; do
    uv add "$pkg" || echo "Failed to add: $pkg"
done

uv sync

Then:

  1. Delete requirements.txt, requirements-dev.txt

  2. Delete virtual environment (venv/, .venv/)

  3. Add uv.lock to version control

From setup.py / setup.cfg
  1. Run uv init --bare to create pyproject.toml

  2. Use uv add to add each dependency from install_requires

  3. Use uv add --group dev for dev dependencies

  4. Copy non-dependency metadata (name, version, description, etc.) to [project]

  5. Delete setup.py, setup.cfg, MANIFEST.in

From flake8 + black + isort
  1. Remove flake8, black, isort via uv remove

  2. Delete .flake8, pyproject.toml [tool.black], [tool.isort] configs

  3. Add ruff: uv add --group dev ruff

  4. Add ruff configuration (see ruff-config.md)

  5. Run uv run ruff check --fix . to apply fixes

  6. Run uv run ruff format . to format

From mypy / pyright
  1. Remove mypy/pyright via uv remove

  2. Delete mypy.ini, pyrightconfig.json, or [tool.mypy]/[tool.pyright] sections

  3. Add ty: uv add --group dev ty

  4. Run uv run ty check src/

Quick Reference: uv Commands

CommandDescription
uv initCreate new project
uv init --packageCreate distributable package
uv add <pkg>Add dependency
uv add --group dev <pkg>Add to dependency group
uv remove <pkg>Remove dependency
uv syncInstall dependencies
uv sync --all-groupsInstall all dependency groups
uv run <cmd>Run command in venv
uv run --with <pkg> <cmd>Run with temporary dependency
uv buildBuild package
uv publishPublish to PyPI
Ad-hoc Dependencies with --with

Use uv run --with for one-off commands that need packages not in your project:

# Run Python with a temporary package
uv run --with requests python -c "import requests; print(requests.get('https://httpbin.org/ip').json())"

# Run a module with temporary deps
uv run --with rich python -m rich.progress

# Multiple packages
uv run --with requests --with rich python script.py

# Combine with project deps (adds to existing venv)
uv run --with httpx pytest  # project deps + httpx

When to use --with vs uv add:

  • uv add: Package is a project dependency (goes in pyproject.toml/uv.lock)

  • --with: One-off usage, testing, or scripts outside a project context

See uv-commands.md for complete reference.

Quick Reference: Dependency Groups

[dependency-groups]
dev = ["ruff", "ty"]
test = ["pytest", "pytest-cov", "hypothesis"]
docs = ["sphinx", "myst-parser"]

Install with: uv sync --group dev --group test

Best Practices Checklist

  • [ ] Use src/ layout for packages

  • [ ] Set requires-python = ">=3.11"

  • [ ] Configure ruff with select = ["ALL"] and explicit ignores

  • [ ] Use ty for type checking

  • [ ] Enforce test coverage minimum (80%+)

  • [ ] Use dependency groups instead of extras for dev tools

  • [ ] Add uv.lock to version control

  • [ ] Use PEP 723 for standalone scripts

Files

references/security-setup.md

Security Setup

Security tooling for Python projects: pre-commit hooks, CI auditing, and dependency scanning.

Tool Installation

Install these tools before running the quick setup commands below.

prek (pre-commit runner)
# Homebrew (recommended)
brew install prek

# Cargo
cargo install prek

# Standalone installer
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/j178/prek/releases/latest/download/prek-installer.sh | sh
Security tools

Pre-commit hooks auto-install tools when run via prek. For manual CLI usage:

# Homebrew (macOS/Linux)
brew install actionlint shellcheck

# Python tools via uv
uv tool install detect-secrets
uv tool install zizmor

Alternative installation methods:

  • actionlint: go install github.com/rhysd/actionlint/cmd/actionlint@latest

  • zizmor: cargo install zizmor

  • detect-secrets: pipx install detect-secrets

Quick Setup

# 1. Install security hooks
prek install

# 2. Initialize secrets baseline
detect-secrets scan > .secrets.baseline

# 3. Audit existing workflows
actionlint .github/workflows/
zizmor .github/workflows/

See templates/pre-commit-config.yaml for a complete hook configuration.

Tool Matrix

ToolRunsCatches
shellcheckpre-commitShell script bugs, quoting issues
detect-secretspre-commitLeaked API keys, passwords, tokens
actionlintpre-commit, CIWorkflow syntax errors, invalid refs
zizmorpre-commit, CIWorkflow security issues, excessive permissions
pip-auditCI, manualKnown CVEs in dependencies
DependabotscheduledOutdated dependencies with vulnerabilities

Pre-commit Hooks

These run locally before each commit via prek.

shellcheck - Shell Script Linting

Catches common shell scripting errors: unquoted variables, undefined variables, deprecated syntax.

# In .pre-commit-config.yaml
- repo: https://github.com/koalaman/shellcheck-precommit
  rev: <latest>  # https://github.com/koalaman/shellcheck-precommit/tags
  hooks:
    - id: shellcheck
      args: [--severity=error]  # Start strict, adjust if needed

Common findings:

  • SC2086: Unquoted variable expansion (word splitting risk)

  • SC2046: Unquoted command substitution

  • SC2155: Declare and assign separately to avoid masking return values

detect-secrets - Secret Detection

Prevents accidentally committing API keys, passwords, and tokens.

- repo: https://github.com/Yelp/detect-secrets
  rev: <latest>  # https://github.com/Yelp/detect-secrets/releases
  hooks:
    - id: detect-secrets
      args: [--baseline, .secrets.baseline]

First-time setup:

# Generate baseline of existing "secrets" (false positives to ignore)
detect-secrets scan > .secrets.baseline

# Review the baseline - ensure no real secrets
cat .secrets.baseline

# Commit the baseline
git add .secrets.baseline

When hook fails:

# View the finding (non-interactive)
detect-secrets audit --report .secrets.baseline

If false positive: update baseline with detect-secrets scan --update .secrets.baseline If real secret: remove from code and rotate the credential.

CI Security

These run in GitHub Actions on every push/PR.

actionlint - Workflow Syntax Validation

Catches syntax errors, invalid action references, and type mismatches before they fail in CI.

- repo: https://github.com/rhysd/actionlint
  rev: <latest>  # https://github.com/rhysd/actionlint/releases
  hooks:
    - id: actionlint

Run manually:

actionlint .github/workflows/

Common findings:

  • Invalid event triggers

  • Undefined workflow inputs

  • Shell syntax errors in run: blocks

  • Invalid action version references

zizmor - Workflow Security Audit

Finds security issues in GitHub Actions workflows: excessive permissions, injection risks, untrusted inputs.

- repo: https://github.com/zizmorcore/zizmor-pre-commit
  rev: <latest>  # https://github.com/zizmorcore/zizmor-pre-commit/releases
  hooks:
    - id: zizmor
      args: [--persona=regular, --min-severity=medium, --min-confidence=medium]

Run manually:

zizmor .github/workflows/

Fixing excessive-permissions:

By default, workflows get write access to everything. Lock down with explicit permissions:

# Read-only workflows (lint, test, audit)
permissions:
  contents: read

# Workflows that push or create releases
permissions:
  contents: write

# Workflows that comment on PRs
permissions:
  contents: read
  pull-requests: write

Common findings:

  • excessive-permissions: No permissions: block

  • template-injection: Using ${{ github.event.* }} unsafely

  • unpinned-action: Actions not pinned to SHA

  • dangerous-triggers: pull_request_target with checkout

Dependency Security

pip-audit - Vulnerability Scanning

Checks installed packages against the Python Advisory Database (PyPA) for known CVEs.

Setup:

# pyproject.toml
[dependency-groups]
audit = ["pip-audit"]

Usage:

# Audit current environment
uv run pip-audit

# Audit without installing (faster for CI)
uv run pip-audit .

# Fix automatically (upgrades vulnerable packages)
uv run pip-audit --fix

In CI:

- name: Security audit
  run: uv run pip-audit .

When vulnerabilities found:

  1. Check if the CVE affects your usage (many are in unused code paths)

  2. Update the package: uv add <package>@latest

  3. If no fix available: evaluate risk, consider alternatives, or add to ignore list

Dependabot - Automated Updates

Automatically creates PRs for outdated dependencies.

Copy templates/dependabot.yml to .github/dependabot.yml.

How pip-audit and Dependabot work together:

ToolTriggerScope
pip-auditEvery CI runKnown CVEs in current deps
DependabotWeekly scheduleAll outdated deps, security + non-security
  • pip-audit catches: "You have a vulnerable version right now"

  • Dependabot prevents: "You'll fall behind and accumulate vulnerabilities"

The 7-day cooldown protects against attackers publishing malicious updates and hoping for quick adoption before detection.

See dependabot.md for advanced configuration.

See prek.md for complete pre-commit hook configuration including security hooks.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Python

Security

What our scanner noticed· Medium risk

  • Instructions that use the network
  • Downloads piped into a shell

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Trail of Bits
Publisher
Trail of Bits (Official)
Source
trailofbits/skills · plugins/modern-python/skills/modern-python · commit e4dd0bb
Licence
Creative Commons Attribution-ShareAlike 4.0 International (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Trail of Bits.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download modern-python.zip