Python code
A personal, growing collection of agent skills for coding agents (Claude Code, Codex, ...).
npx -y skills add tripcher/skills --skill python-codeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Guidance for writing, reviewing, and refactoring Python code with clear style, naming, design principles, and testing discipline. Use when you write Python modules, scripts, packages, tests, refactors Python code, or reviews Python implementation quality.
SKILL.md
13.6 KB, as published. Nobody here has run it
Python Code
Use this skill when producing or changing Python code. Prefer the existing project conventions first; apply these rules where the project is silent.
Style
- Use PEP 8 style guidelines
- Use PEP 257 – Docstring Conventions
- Always use mypy with strict mode
- Always use mypy stubs for third-party libraries
- Always use ruff for code linting
- Always use ruff for code formatting
- Always use isort for imports sorting
Ruff
extend-select = [
"F", # Pyflakes rules
"W", # PyCodeStyle warnings
"E", # PyCodeStyle errors
"I", # Sort imports properly
"S", # Enforce bandit rules
"G", # Validate (lack of) logging format strings
"B", # Finding likely bugs and design problems in your program
"A", # Check for python builtins being used as variables or parameters
"Q", # Check quotes
"N", # PEP8 naming conventions
"UP", # Warn if certain things can changed due to newer Python versions
"C4", # Catch incorrect use of comprehensions, dict, list, etc
"FA", # Enforce from __future__ import annotations
"ISC", # Good use of string concatenation
"ICN", # Use common import conventions
"RET", # Good return practices
"SIM", # Common simplification rules
"TID", # Some good import practices
"TC", # Enforce importing certain types in a TYPE_CHECKING block
"PTH", # Use pathlib instead of os.path
"TD", # Be diligent with TODO comments
"NPY", # Some numpy-specific things
"FURB", # Suggest more idiomatic Python patterns
"ANN", # Enforce type annotations
"BLE", # Avoid blind exception
"COM", # Avoid common mistakes
"DTZ", # Ban the usage of unsafe naive datetime class
"EM", # Helps format nice error messages
"LOG", # Checks for issues using the standard library logging module
"T20", # Check for Print statements in python files
"PT", # Checking common style issues or inconsistencies with pytest-based tests
"ARG", # Checks for unused function arguments
"C90", # Enable mccabe
"RUF", # Enable ruff rules
]
# Required when using `ruff format`; these rules conflict with the formatter.
ignore = [
"W191", # Conflicts with formatter indentation
"E111", # Conflicts with formatter indentation
"E114", # Conflicts with formatter indentation
"E117", # Conflicts with formatter indentation
"Q000", # Configure quotes via [tool.ruff.format] instead
"Q001", # Configure quotes via [tool.ruff.format] instead
"Q002", # Configure quotes via [tool.ruff.format] instead
"Q003", # Configure quotes via [tool.ruff.format] instead
"Q004", # Configure quotes via [tool.ruff.format] instead
"COM812", # Conflicts with formatter trailing-comma behavior
"COM819", # Conflicts with formatter trailing-comma behavior
]
Philosophy
- Use PEP 20 – The Zen of Python.
- The general idea for separation of concerns.
- The best components are those that provide powerful functionality yet have a simple interface.
- A module should be responsible to one, and only one, user or stakeholder.
- A little copying is better than a little dependency.
- Always try to reduce a cognitive load.
- Code should be structured and predictable. Recurring tasks should be solved using the same pattern, adding the same layers, directories, files, and functions (creating an API, creating a CLI command, etc.).
Naming
- Is the function a test? -> test_<entity>_<behavior>.
- Does the function has a @property decorator? -> don't use a verb in the function name.
- Does the function use a disk or a network: 3.1. … to store data? -> save_to, send, write_to 3.2. … to receive data? -> fetch, load, read
- Does the function output any data? -> print, output
- Returns boolean value? -> is_, has_/have_, can_, check_if_<entity>_<characteristic>
- Aggregates data? -> calculate, extract, analyze
- Put data from one form to another: 7.1. Creates a single meaningful object? -> create 7.2. Fills an existing object with data? -> initialize, configure 7.3. Clean raw data? -> clean 7.4. Receive a string as input? -> parse 7.5. Return a string as output? -> render 7.6. Return an iterator as output? ->iter 7.7. Mutates its arguments or some global state? -> update, mutate, add, remove, insert, set 7.8. Return a list of errors? -> validate 7.9. Checks data items recursively? -> walk 7.10. Finds appropriate item in data? -> find, search, match 7.11. Transform data type? -> <something>to<something_else> 7.12. None of the above, but still works with data? -> Check one of those: morph, compose, prepare, extract, generate, initialize, filter, map, aggregate, export, import, normalize, calculate .
Principles
Prefer LBYL for routine branching when the precondition is cheap and precise.
LBYL means validating a known condition before doing the work. EAFP means executing the operation and handling the failure. Use LBYL for normal in-memory branching when the check is cheap and accurate. Use EAFP when the operation itself is the only reliable check, or when translating low-level failures at an application boundary.
# CORRECT: branch on explicit state
if user_id in users_by_id:
user = users_by_id[user_id]
send_welcome_email(user)
else:
mark_unknown_user(user_id)
# CORRECT: use .get() when a default is real domain behavior
timeout = settings.get("timeout_seconds", 30)
run_job(timeout=timeout)
# WRONG: KeyError is not a business branch
try:
user = users_by_id[user_id]
except KeyError:
mark_unknown_user(user_id)
# CORRECT: translate boundary failures once, near the CLI/API edge
try:
publish_report(report_id)
except StorageError as exc:
raise click.ClickException(f"Could not publish report {report_id}: {exc}") from exc
Use .exists() when filesystem presence is part of your requirement
from pathlib import Path
# CORRECT: skip optional files that are not present
for config_path in candidate_configs:
config_path = config_path.expanduser()
if not config_path.exists():
continue
load_config(config_path)
# CORRECT: require the file to exist when missing input is an error
template_path = Path("templates/invoice.html").resolve(strict=True)
# WRONG: broad exception handling hides unrelated filesystem problems
for config_path in candidate_configs:
try:
load_config(config_path)
except OSError:
continue
Always Use Pathlib (Never os.path)
from pathlib import Path
# CORRECT: build and use a path as a Path
cache_dir = Path.home() / ".cache" / "invoice-tool"
cache_file = cache_dir / "latest.json"
payload = cache_file.read_text(encoding="utf-8")
# WRONG: string path composition is harder to read and refactor
import os.path
cache_file = os.path.join(os.path.expanduser("~"), ".cache", "invoice-tool", "latest.json")
with open(cache_file, encoding="utf-8") as file:
payload = file.read()
ALWAYS place imports at module level and use absolute imports only
from pathlib import Path
from billing.config import load_settings
from billing.rendering import render_invoice
def build_invoice(invoice_path: Path) -> str:
settings = load_settings()
return render_invoice(invoice_path, settings=settings)
# WRONG: dependency is hidden inside the function
def build_invoice_with_hidden_import(invoice_path: Path) -> str:
from .config import load_settings
settings = load_settings()
return render_invoice(invoice_path, settings=settings)
Properties Must Be O(1)
class Report:
def __init__(self, rows: list[Row]) -> None:
self._rows = rows
@property
def row_count(self) -> int:
return len(self._rows)
class RemoteReport:
# CORRECT: method name makes latency visible to the caller
def fetch_row_count(self) -> int:
return self._client.count_rows(self.report_id)
# WRONG: property hides a network request
@property
def row_count(self) -> int:
return self._client.count_rows(self.report_id)
ALWAYS use keyword-only arguments
Exceptions:
self- always positional.ctx/ context objects - may remain positional as the first parameter.- ABC/Protocol methods - may keep framework-compatible signatures.
def export_orders(
*,
output_path: Path,
include_archived: bool,
batch_size: int,
) -> None:
write_orders(output_path, include_archived=include_archived, batch_size=batch_size)
# CORRECT: the call documents intent
export_orders(output_path=Path("orders.csv"), include_archived=False, batch_size=500)
# WRONG: boolean and number meaning is invisible at the call site
export_orders(Path("orders.csv"), False, 500)
Prefer modules and functions instead of classes and objects
Create classes only when the code needs identity, lifecycle, shared state, polymorphism, or an interface boundary. For stateless transformations, use functions in a clear module.
Exceptions:
- Use ABCs or Protocols for repository, strategy, adapter, singleton, or similar boundaries.
# CORRECT: stateless behavior as functions
def calculate_invoice_total(lines: list[InvoiceLine]) -> Decimal:
return sum((line.quantity * line.unit_price for line in lines), Decimal("0"))
# CORRECT: class owns a dependency and represents a boundary
class InvoiceRepository(Protocol):
def save(self, invoice: Invoice) -> None: ...
# WRONG: empty class used as a namespace
class InvoiceCalculator:
def calculate_total(self, lines: list[InvoiceLine]) -> Decimal:
return sum((line.quantity * line.unit_price for line in lines), Decimal("0"))
ALWAYS use typing annotations, avoid Any if possible
# CORRECT: type annotations clarify intent and prevent bugs
def parse_invoice(raw_invoice: Mapping[str, object]) -> Invoice:
invoice_id = require_str(raw_invoice, "id")
total = require_decimal(raw_invoice, "total")
return Invoice(id=invoice_id, total=total)
# WRONG: Any spreads uncertainty through the codebase
def parse_invoice_untyped(raw_invoice: Any) -> Any:
return {"id": raw_invoice["id"], "total": raw_invoice["total"]}
ALWAYS use pydantic for data classes
# CORRECT: use pydantic for data classes and validation
from pydantic import BaseModel, Field
class CreateInvoiceRequest(BaseModel):
customer_id: str
amount_cents: int = Field(gt=0)
currency: str = Field(min_length=3, max_length=3)
request = CreateInvoiceRequest.model_validate(payload)
# WRONG: unvalidated external data copied into a dataclass
@dataclass
class RawCreateInvoiceRequest:
customer_id: str
amount_cents: int
currency: str
ALWAYS write Pure Functions
# CORRECT: pure functions are easy to test and reason about
def apply_discount(total: Decimal, percent: Decimal) -> Decimal:
discount = total * percent / Decimal("100")
return total - discount
# WRONG: side effects are hard to test and reason about
def charge_invoice(invoice_id: str, gateway: PaymentGateway) -> None:
invoice = load_invoice(invoice_id)
amount = apply_discount(invoice.total, invoice.discount_percent)
gateway.charge(invoice.customer_id, amount)
# WRONG: calculation reads global state and performs I/O
def apply_discount_from_storage(invoice_id: str) -> Decimal:
invoice = load_invoice(invoice_id)
return invoice.total - invoice.total * settings.discount_percent / Decimal("100")
# CORRECT: pure code
all_invoices = []
invoice = load_invoice(invoice_id)
all_invoices.append(invoice)
# WRONG: side effects are hard to reason about
all_invoices = []
def add_available_invoices(invoices: list[Invoice], invoice_id: str) -> None:
invoice = load_invoice(invoice_id)
invoices.append(invoice)
Use f-strings for string formatting or .format in challenging cases
message = f"Invoice {invoice.id} was sent to {invoice.email}"
template = "Invoice {invoice_id} was sent to {email}"
message = template.format(invoice_id=invoice.id, email=invoice.email)
# WRONG: percent formatting is harder to read and easy to mismatch
message = "Invoice %s was sent to %s" % (invoice.id, invoice.email)
Testing
- Always use pytest, pytest-cov, pytest-mock, pytest-factoryboy, pytest-freezegun, pytest-dotenv, pytest-randomly pytest-socket
- Use factories, fixtures, faker to remove duplication, but keep each test readable on its own.
- Use pytest_plugins to include factories and fixtures in the conftest.py file.
- Avoid real network calls, clocks, randomness, and filesystem dependence unless the test is explicitly integration-level.
Structure
Split tests depending on the type of code they represent.
The file structure usually looks like this:
project_name ├── app_name │ ├── init.py │ └── tests │ ├── init.py │ ├── factories.py │ ├── fixturies.py │ ├── conftest.py │ ├── models │ │ └── init.py │ │ └── test_some_model_file_name.py │ ├── selectors │ │ └── init.py │ │ └── test_some_selector_file_name.py │ └── services │ ├── init.py │ └── test_some_service_file_name.py └── init.py