A metaclass constructs class objects and can enforce a class-level contract before publishing a class into a registry.
Python metaclasses: validate a class before registering its wire format
Operation contract
The codec registry requires a unique nonempty wire name and a decode function on each concrete class. Validation and type construction finish before the registry is updated. A duplicate declaration raises without replacing the previously accepted codec. The base class deliberately has no wire name and is not registered.
Failure and ownership boundary
This is a local class registry, not automatic loading of received plugin paths. Metaclass conflicts can arise with multiple bases, and another hook can have its own side effects. Preserve the class namespace when passing it to type.__new__, including compiler-provided cells. For many simple subclass checks, __init_subclass__ is sufficient; Python ABC: prevent construction of an incomplete implementation and Python super and method resolution: cooperate across mixins cover other mechanisms.
Working program
CODECS = {}
class CodecMeta(type):
def __new__(metaclass, name, bases, namespace, **options):
wire_name = namespace.get("wire_name")
if wire_name is not None:
if not isinstance(wire_name, str) or not wire_name or wire_name in CODECS:
raise ValueError("wire name rejected")
if not callable(namespace.get("decode")):
raise ValueError("decode function required")
created = super().__new__(metaclass, name, bases, namespace, **options)
if wire_name is not None:
CODECS[wire_name] = created
return created
class ReceiptCodec(metaclass=CodecMeta):
wire_name = None
class ReceiptJson(ReceiptCodec):
wire_name = "receipt-json"
def decode(payload):
return payload["receipt_id"]
print(CODECS["receipt-json"].decode({"receipt_id": "R-0041"}))
try:
class DuplicateReceipt(ReceiptCodec):
wire_name = "receipt-json"
def decode(payload):
return payload
except ValueError:
print("duplicate class rejected")
print(CODECS["receipt-json"] is ReceiptJson)Output
R-0041
duplicate class rejected
TrueCosts and limits
Class construction performs validation once per declaration and retains the accepted class in the registry. The registry therefore owns its classes until removed. This fixture has no weak retention or plugin unload policy.
Common Mistakes
- Do not publish a registry entry before class construction succeeds.
- A metaclass does not make received plugin code safe to execute.
Connected lessons
Python ABC: prevent construction of an incomplete implementation, Python super and method resolution: cooperate across mixins, Python weak references: a cache does not own its values.
Check the next state boundary
Python __init_subclass__: validate declarations without a custom metaclass, Python plugin project: load owned code and publish only an accepted interface.
