Pythurion helps coding agents write Python you’ll want to maintain: correct, minimal, and idiomatic.
An always-on Python engineering discipline for coding agents:
- inspect the real context;
- establish the behavioral contract;
- reproduce failures before fixing them;
- solve the shared cause with the smallest coherent mechanism;
- avoid speculative abstractions and unrelated churn;
- verify every claim with focused checks and observed evidence.
npx skills add -g arlegotin/pythurionPythurion is designed to activate automatically whenever your agent works with Python code, tests, or project configuration. It favors modern Python 3.12+ practices, the smallest design that fits, explicit risk boundaries, and executable evidence.
Use a function until state or lifecycle earns a class.
# Before
class Slugifier:
def __call__(self, text: str) -> str:
return text.lower().replace(" ", "-")
slugify = Slugifier()
# After
def slugify(text: str) -> str:
return text.lower().replace(" ", "-")Accept the weakest useful interface and enforce equal lengths.
from collections.abc import Iterable
# Before
def prices(names: list[str], values: list[int]) -> dict[str, int]:
return dict(zip(names, values))
# After
def prices(names: Iterable[str], values: Iterable[int]) -> dict[str, int]:
return dict(zip(names, values, strict=True))Catch only the failure you can translate, and preserve its cause.
from pathlib import Path
# Before
def load_config(path: Path) -> str | None:
try:
return path.read_text()
except Exception:
return None
# After
class ConfigError(RuntimeError):
pass
def load_config(path: Path) -> str:
try:
return path.read_text()
except OSError as error:
raise ConfigError(f"cannot read {path}") from errorParameterize input and make both transaction and connection lifetimes explicit.
from contextlib import closing
import sqlite3
# Before
def add_label(path: str, label: str) -> None:
connection = sqlite3.connect(path)
connection.execute(f"INSERT INTO labels(value) VALUES ('{label}')")
connection.commit()
# After
def add_label(path: str, label: str) -> None:
with closing(sqlite3.connect(path)) as connection:
with connection:
connection.execute(
"INSERT INTO labels(value) VALUES (?)",
(label,),
)Own a small fail-together task set so errors and cancellation clean up the group.
import asyncio
# Before
async def refresh() -> None:
asyncio.create_task(refresh_users())
asyncio.create_task(refresh_orders())
# After
async def refresh() -> None:
async with asyncio.TaskGroup() as group:
group.create_task(refresh_users())
group.create_task(refresh_orders())Use aware UTC instants for persisted or cross-host time.
from datetime import UTC, datetime, timedelta
# Before
expires_at = datetime.now() + timedelta(minutes=5)
# After
expires_at = datetime.now(UTC) + timedelta(minutes=5)