Runtime Power Ladder¶
Reference Position¶
flowchart TD
family["Python Programming"] --> program["Python Metaprogramming"]
program --> reference["Runtime Power Ladder"]
reference --> review["Design or review decision"]
review --> capstone["Capstone proof surface"]
flowchart TD
trigger["Hit a naming, boundary, or trade-off question"] --> lookup["Use this page as a glossary, map, rubric, or atlas"]
lookup --> compare["Compare the current code or workflow against the boundary"]
compare --> decision["Turn the comparison into a keep, change, or reject call"]
Read the first diagram as a lookup map: this page is part of the review shelf, not a first-read narrative. Read the second diagram as the reference rhythm: arrive with a concrete ambiguity, compare the current work against the boundary on the page, then turn that comparison into a decision.
This page is the central decision rule for the course. Higher-power runtime hooks are not "more advanced" in a way that makes them better. They are more invasive, harder to debug, and easier to misuse.
The ladder¶
Plain code¶
Use functions, explicit classes, and direct composition when the behavior can stay local and visible.
Introspection¶
Use type, isinstance, vars, and inspect when you need to observe runtime shape
without changing it.
Decorators¶
Use decorators when you need controlled callable transformation and preserved metadata.
Descriptors¶
Use descriptors when an invariant belongs to attribute access itself and should be shared across a class boundary.
Metaclasses¶
Use metaclasses only when the invariant belongs to class creation and lower-power tools cannot own it cleanly.
Global hooks and dynamic execution¶
Treat import hooks, exec, eval, and monkey-patching as exceptional tools with strict
security, observability, and governance boundaries.
Review prompts¶
- What lower rung almost solved this problem?
- What new failure mode did the higher rung introduce?
- Can the resulting behavior still be explained by reading one file at a time?
- Would a reviewer approve this without a live walkthrough?
Decision matrix¶
| Need | Lowest honest tool | Do not escalate when | Prefer instead |
|---|---|---|---|
| Better names, extracted helpers, or explicit branching | Plain code | the behavior is still local and readable | plain functions, small classes, explicit composition |
| Runtime observation or evidence gathering | Introspection | you are only trying to inspect shape, signature, or provenance | inspect, type, isinstance, vars, getattr_static |
| Per-call instrumentation or callable policy | Decorators | the invariant belongs to attribute storage or class creation | functools.wraps, thin wrappers, explicit wrapper factories |
| Shared attribute validation or computed access | Descriptors | a property or class decorator already solves the problem locally | property, descriptors with __set_name__, class decorators for post-creation wiring |
| Hierarchy-wide class-creation invariants | Metaclasses | the problem can be solved after class creation or on one attribute at a time | class decorators, descriptors, explicit registration |
| Process-wide import behavior or runtime-generated code | Global hooks and dynamic execution | the same result can be achieved with explicit imports, entry points, or data-driven configuration | explicit imports, plugin registries, AST-free configuration |
Escalation rule¶
When you think you need a higher rung, write down four sentences before you implement it:
- What lower-power tool almost works?
- What exact invariant does it fail to own?
- What new blast radius does the higher tool introduce?
- How will tests and review bundles prove the higher tool stayed worth it?