
Python Type Safety
Apply advanced typing patterns—generics, bounded TypeVars, and repository interfaces—so Python backends stay refactor-safe as a solo builder scales features.
Overview
python-type-safety is an agent skill most often used in Build (also Ship review) that supplies advanced Python typing patterns such as generic repositories and bounded TypeVars for safer backends.
Install
npx skills add https://github.com/wshobson/agents --skill python-type-safetyWhat is this skill?
- Generic Repository ABC with TypeVar T and ID for async get, save, and delete
- Concrete UserRepository example mapping rows to typed User entities
- TypeVar bounds with Pydantic BaseModel for validate_and_create helpers
- Patterns aimed at eliminating untyped dict plumbing in services
Adoption & trust: 7.5k installs on skills.sh; 36.5k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Python API layer passes dicts and Any everywhere, so refactors break silently and your agent-generated repos do not compose.
Who is it for?
Indie backends using async IO, Pydantic v2 models, and repository-style data access who want agent-generated code to match senior typing style.
Skip if: Greenfield scripts with no type checker enforced, or frontends where Python typing is irrelevant.
When should I use this skill?
When implementing or refactoring Python backend modules that need generic repositories, bounded TypeVars, or Pydantic-linked validation helpers.
What do I get? / Deliverables
You get copy-ready generic repository and validation patterns that mypy-friendly services can adopt without relearning typing theory from scratch.
- typed repository interfaces
- validation helper functions
- refactor-ready generic patterns
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Canonical shelf is Build → backend because the worked examples target data layers, Pydantic models, and async persistence—the core implementation surface. Content focuses on type-safe repositories and validation helpers rather than UI, DevOps, or release gates.
Where it fits
Scaffold a UserRepository implementation with correct Generic[T, ID] signatures before wiring routes.
Type webhook payload parsers with bounded BaseModel TypeVars shared across providers.
Refactor a service that returned dicts into validated models before a release candidate.
How it compares
Pattern cookbook for generics in application code—not a substitute for running mypy, pyright, or a dedicated security audit skill.
Common Questions / FAQ
Who is python-type-safety for?
Solo builders and small teams writing Python APIs or workers who want their coding agents to emit maintainable typed data-access layers.
When should I use python-type-safety?
While building backend modules and repositories, when scaffolding a new service layer after validation, and during ship review passes where you tighten types before tagging a release.
Is python-type-safety safe to install?
It is documentation-style pattern guidance with no bundled network or shell side effects; still review the Security Audits panel on this Prism page before adding any third-party skill pack.
SKILL.md
READMESKILL.md - Python Type Safety
# python-type-safety — detailed worked examples ## Advanced Patterns ### Pattern 5: Generic Repository Create type-safe data access patterns. ```python from typing import TypeVar, Generic from abc import ABC, abstractmethod T = TypeVar("T") ID = TypeVar("ID") class Repository(ABC, Generic[T, ID]): """Generic repository interface.""" @abstractmethod async def get(self, id: ID) -> T | None: """Get entity by ID.""" ... @abstractmethod async def save(self, entity: T) -> T: """Save and return entity.""" ... @abstractmethod async def delete(self, id: ID) -> bool: """Delete entity, return True if existed.""" ... class UserRepository(Repository[User, str]): """Concrete repository for Users with string IDs.""" async def get(self, id: str) -> User | None: row = await self._db.fetchrow( "SELECT * FROM users WHERE id = $1", id ) return User(**row) if row else None async def save(self, entity: User) -> User: ... async def delete(self, id: str) -> bool: ... ``` ### Pattern 6: TypeVar with Bounds Restrict generic parameters to specific types. ```python from typing import TypeVar from pydantic import BaseModel ModelT = TypeVar("ModelT", bound=BaseModel) def validate_and_create(model_cls: type[ModelT], data: dict) -> ModelT: """Create a validated Pydantic model from dict.""" return model_cls.model_validate(data) # Works with any BaseModel subclass class User(BaseModel): name: str email: str user = validate_and_create(User, {"name": "Alice", "email": "a@b.com"}) # user is typed as User # Type error: str is not a BaseModel subclass result = validate_and_create(str, {"name": "Alice"}) # Error! ``` ### Pattern 7: Protocols for Structural Typing Define interfaces without requiring inheritance. ```python from typing import Protocol, runtime_checkable @runtime_checkable class Serializable(Protocol): """Any class that can be serialized to/from dict.""" def to_dict(self) -> dict: ... @classmethod def from_dict(cls, data: dict) -> "Serializable": ... # User satisfies Serializable without inheriting from it class User: def __init__(self, id: str, name: str) -> None: self.id = id self.name = name def to_dict(self) -> dict: return {"id": self.id, "name": self.name} @classmethod def from_dict(cls, data: dict) -> "User": return cls(id=data["id"], name=data["name"]) def serialize(obj: Serializable) -> str: """Works with any Serializable object.""" return json.dumps(obj.to_dict()) # Works - User matches the protocol serialize(User("1", "Alice")) # Runtime checking with @runtime_checkable isinstance(User("1", "Alice"), Serializable) # True ``` ### Pattern 8: Common Protocol Patterns Define reusable structural interfaces. ```python from typing import Protocol class Closeable(Protocol): """Resource that can be closed.""" def close(self) -> None: ... class AsyncCloseable(Protocol): """Async resource that can be closed.""" async def close(self) -> None: ... class Readable(Protocol): """Object that can be read from.""" def read(self, n: int = -1) -> bytes: ... class HasId(Protocol): """Object with an ID property.""" @property def id(self) -> str: ... class Comparable(Protocol): """Object that supports comparison.""" def __lt__(self, other: "Comparable") -> bool: ... def __le__(self, other: "Comparable") -> bool: ... ``` ### Pattern 9: Type Aliases Create meaningful type names. **Note:** The `type Alias = ...` statement syntax (PEP 695) was introduced in **Python 3.12**, not 3.10. For projects targeting earlier versions (including 3.10/3.11), use the `TypeAlias` annotation (PEP 613, available since Python 3.10). ```python # Python 3.12+ type statement (PEP 695) type UserId = str type UserDict = dict[str, Any] # Python 3.12+ type