Skip to content

Instantly share code, notes, and snippets.

@j2labs
Created May 23, 2012 07:54
Show Gist options
  • Save j2labs/2773773 to your computer and use it in GitHub Desktop.
Save j2labs/2773773 to your computer and use it in GitHub Desktop.
A path towards understanding DictShield.
#!/usr/bin/env python
###
### Field Concepts
###
class IntField(object):
def __init__(self):
print 'new int field'
i = IntField()
print type(i)
i = 5
print type(i)
### Field in a class
class Foo(object):
i = IntField()
x = 'xxxx'
foo = Foo()
foo.i = 'word'
### Use descriptors to implement getter and setters
class IntField(object):
def __get__(self, instance, owner):
print '__get__'
return self.value
def __set__(self, instance, value):
print '__set__'
self.value = value
class Foo(object):
i = IntField()
x = 'xxxx'
foo = Foo()
foo.i = 'word'
### Validation (almost!)
class IntField(object):
def __get__(self, instance, owner):
print '__get__'
return self.value
def __set__(self, instance, value):
print '__set__'
self.value = value
def validate(self):
try:
int(self.value)
except:
raise Exception()
class Foo(object):
i = IntField()
x = 'xxxx'
foo = Foo()
foo.i = 'word'
### Fails because foo.i returns 'word', which doesn't have a validate function
foo.i.validate()
foo.i = 4
### Fails because foo.i returns 4, which doesn't have a validate function
foo.i.validate()
### Validation Via Indirection
class Foo(object):
i = IntField()
x = 'xxxx'
_fields = {'i': i}
foo = Foo()
f.i = 5
f._fields['i'].validate() # Ugly.
class Foo(object):
i = IntField()
x = 'xxxx'
_fields = {'i': i}
def validate(self):
for field_name, field in self._fields.items():
field.validate()
foo = Foo()
foo.i = 5
foo.validate()
###
### Metaprogramming !
###
class MetaBar(type):
def __new__(cls, name, bases, attrs):
klass = type.__new__(cls, name, bases, attrs)
print 'Hello from MetaBar'
klass._some_field = 'some field'
return klass
class Bar(object):
__metaclass__ = MetaBar
def __init__(self):
print 'Hello from __init__'
### Using the Metaclass to populate _fields
class MetaBar(type):
def __new__(cls, name, bases, attrs):
klass = type.__new__(cls, name, bases, attrs)
klass._fields = dict()
for attr_name, attr_value in attrs.items():
if type(attr_value) is IntField:
klass._fields[attr_name] = attr_value
return klass
class Foo(object):
__metaclass__ = MetaBar
i = IntField()
x = 'xxxx'
def validate(self):
for field_name, field in self._fields.items():
field.validate()
###
### Isolate Complex Logic
###
class BaseDocument(object):
__metaclass__ = MetaBar
def validate(self):
for field_name, field in self._fields.items():
field.validate()
### A user simply writes the class below
class Foo(BaseDocument):
i = IntField()
f = Foo()
f.i = 3
f.validate()
### The current design stores value at class level. This is bad.
f1 = Foo()
f1.i = 3
f2 = Foo()
f2.i = 234234
print f1.i # It's 234234 isn't it?
### Instance-level storage via instance._data (aka self._data)
class IntField(object):
def __get__(self, instance, owner):
return instance._data.get('i', None)
def __set__(self, instance, value):
instance._data['i'] = value
def validate(self, instance, value):
try:
### Store coerced value
value = int(value)
instance._data['i'] = value
except:
raise Exception()
class MetaBar(type):
def __new__(cls, name, bases, attrs):
klass = type.__new__(cls, name, bases, attrs)
klass._fields = dict()
for attr_name, attr_value in attrs.items():
if type(attr_value) is IntField:
klass._fields[attr_name] = attr_value
return klass
class BaseDocument(object):
__metaclass__ = MetaBar
def __init__(self, *a, **kw):
super(BaseDocument, self).__init__(*a, **kw)
self._data = dict() # This is how we have instance._data
def validate(self):
for field_name, field in self._fields.items():
value = self._data[field_name]
field.validate(self, value)
class Foo(BaseDocument):
i = IntField()
### The current design stores value at class level. This is bad.
f1 = Foo()
f1.i = 3
f2 = Foo()
f2.i = 234234
print f1.i # It's 3!
print f2.i # It's 234234.
###
### Instance Level Data
###
### First, break field logic into a `BaseField`
class BaseField(object):
def __get__(self, instance, owner):
"""Instance aware getter
"""
return instance._data.get('i', None)
def __set__(self, instance, value):
"""Instance aware setter
"""
instance._data['i'] = value
### Subclass the new `BaseField`
class IntField(BaseField):
"""Implementation of an int field.
"""
def validate(self, instance, value):
"""Calls `int()` on field for validation
"""
try:
### Store coerced value
value = int(value)
instance._data['i'] = value
except:
raise Exception()
### Adjust `MetaBar` to store any fields that are `BaseField`
class MetaBar(type):
def __new__(cls, name, bases, attrs):
"""Aggregates the `BaseField` instances into a dict called `_fields`
"""
klass = type.__new__(cls, name, bases, attrs)
klass._fields = dict()
for attr_name, attr_value in attrs.items():
if isinstance(attr_value, BaseField):
klass._fields[attr_name] = attr_value
return klass
### Implement __init__ such that keyword arguments map to class fields
class BaseDocument(object):
"""A document that implements validation and provides a simple interface
for populating instance fields easily with a `dict`.
"""
__metaclass__ = MetaBar
def __init__(self, *a, **kw):
self._data = dict()
print 'KW:', kw
### Accept kw as values for class attributes
for field_name, field_value in kw.items():
if field_name in self._fields:
field = self._fields[field_name]
if isinstance(field, BaseField):
self._data[field_name] = field_value
def validate(self):
for field_name, field in self._fields.items():
value = self._data[field_name]
field.validate(self, value)
### Use still has a simple interface
class Foo(BaseDocument):
"""Simple Foo Document that has a single IntField.
"""
i = IntField()
### Use new keyword helpers to populate fields
f1 = Foo(**{'i': 234})
print f1.i
f2 = Foo(**{'i': 23423423423})
print f2.i
###
### But wait, 'i' is hardcoded there... that sucks!
###
class BaseField(object):
def __init__(self):
self._field_name = None # We'll use self._field_name
def __get__(self, instance, owner):
return instance._data.get(self._field_name, None)
def __set__(self, instance, value):
instance._data[self._field_name] = value # self._field_name
### Subclass the new `BaseField`
class IntField(BaseField):
def validate(self, instance, value):
try:
### Store coerced value
value = int(value)
instance._data[self._field_name] = value # self._field_name
except:
raise Exception()
class MetaBar(type):
def __new__(cls, name, bases, attrs):
klass = type.__new__(cls, name, bases, attrs)
klass._fields = dict()
for attr_name, attr_value in attrs.items():
if isinstance(attr_value, BaseField):
klass._fields[attr_name] = attr_value
attr_value._field_name = attr_name # self._field_name
return klass
class BaseDocument(object):
__metaclass__ = MetaBar
def __init__(self, *a, **kw):
self._data = dict()
for field_name, field_value in kw.items():
if field_name in self._fields:
field = self._fields[field_name]
if isinstance(field, BaseField):
self._data[field_name] = field_value
field._field_name = field_name # self._field_name
def validate(self):
for field_name, field in self._fields.items():
value = self._data[field_name]
field.validate(self, value)
### Use still has a simple interface
class Foo(BaseDocument):
i = IntField()
### Use new keyword helpers to populate fields
f1 = Foo(**{'i': 234})
print f1.i
f2 = Foo(**{'i': 23423423423})
print f2.i
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment