2.5 KiB
2.5 KiB
Exercise 7.6 - Solution
# structure.py
from validate import Validator, validated
from collections import ChainMap
class StructureMeta(type):
@classmethod
def __prepare__(meta, clsname, bases):
return ChainMap({}, Validator.validators)
@staticmethod
def __new__(meta, name, bases, methods):
= methods.maps[0]
methods return super().__new__(meta, name, bases, methods)
class Structure(metaclass=StructureMeta):
= ()
_fields = ()
_types
def __setattr__(self, name, value):
if name.startswith('_') or name in self._fields:
super().__setattr__(name, value)
else:
raise AttributeError('No attribute %s' % name)
def __repr__(self):
return '%s(%s)' % (type(self).__name__,
', '.join(repr(getattr(self, name)) for name in self._fields))
@classmethod
def from_row(cls, row):
= [ func(val) for func, val in zip(cls._types, row) ]
rowdata return cls(*rowdata)
@classmethod
def create_init(cls):
'''
Create an __init__ method from _fields
'''
= ','.join(cls._fields)
args = f'def __init__(self, {args}):\n'
code for name in cls._fields:
+= f' self.{name} = {name}\n'
code = { }
locs exec(code, locs)
__init__ = locs['__init__']
cls.
@classmethod
def __init_subclass__(cls):
# Apply the validated decorator to subclasses
validate_attributes(cls)
def validate_attributes(cls):
'''
Class decorator that scans a class definition for Validators
and builds a _fields variable that captures their definition order.
'''
= []
validators for name, val in vars(cls).items():
if isinstance(val, Validator):
validators.append(val)
# Apply validated decorator to any callable with annotations
elif callable(val) and val.__annotations__:
setattr(cls, name, validated(val))
# Collect all of the field names
= tuple([v.name for v in validators])
cls._fields
# Collect type conversions. The lambda x:x is an identity
# function that's used in case no expected_type is found.
= tuple([ getattr(v, 'expected_type', lambda x: x)
cls._types for v in validators ])
# Create the __init__ method
if cls._fields:
cls.create_init()
return cls
def typed_structure(clsname, **validators):
= type(clsname, (Structure,), validators)
cls return cls