Skip to content

Instantly share code, notes, and snippets.

@santa4nt
Created February 15, 2011 23:38
Show Gist options
  • Select an option

  • Save santa4nt/828528 to your computer and use it in GitHub Desktop.

Select an option

Save santa4nt/828528 to your computer and use it in GitHub Desktop.
A module that defines metaclasses for slotted "info" data types.
# -*- coding: utf-8 -*-
"""A module that defines metaclasses for slotted "info" data types.
"""
class InfoMeta(type):
"""A metaclass for classes that declaratively defines fields in
advance.
When a class with this metaclass is created, it should define a
`_fields_` class attribute as a list of field names. During class
creation these class attributes will be created with None values.
"""
def __new__(cls, name, bases, attrs):
if '_fields_' in attrs:
for field in attrs['_fields_']:
attrs[field] = None
else:
raise TypeError('{0} is defined by metaclass InfoMeta '
'but it does not define a class attribute '
'_field_.'.format(name))
super_new = super(InfoMeta, cls).__new__
extend_bases = bases
return super_new(cls, name, extend_bases, attrs)
class _StrictInfo(object):
"""Define inherited "magic methods" that enforce the strict rules
described below.
"""
def __init__(self, **kwargs):
for key, value in kwargs.items():
if not key in self.fields:
raise AttributeError('_StrictInfo: {0} is not in the '
'pre-defined list of fields.'.format(key))
setattr(self, key, value)
@property
def fields(self):
return type(self)._fields_
def __setattr__(self, name, value):
if name not in self.fields:
raise AttributeError('_StrictInfo: {0} is not in the '
'pre-defined list of fields.'.format(name))
super(_StrictInfo, self).__setattr__(name, value)
def __repr__(self):
import cStringIO as StringIO
buildstr = StringIO.StringIO()
buildstr.write(type(self).__name__ + '(')
for attr in self.fields:
value = getattr(self, attr)
buildstr.write('{0}={1}, '.format(attr, repr(value)))
buildstr.write(')')
content = buildstr.getvalue()
buildstr.close()
return content
def __str__(self):
return repr(self)
class StrictInfoMeta(type):
"""Similar to InfoMeta, but enforces attribute access to those
that are defined in the class' _fields_ content only.
This is done by forcing the configured class to inherit _StrictInfo.
The latter defines the "magic methods" necessary to enforce this
restriction.
"""
def __new__(cls, name, bases, attrs):
if '_fields_' in attrs:
for field in attrs['_fields_']:
attrs[field] = None
else:
raise TypeError('{0} is defined by metaclass StrictInfoMeta '
'but it does not define a class attribute '
'_field_.'.format(name))
super_new = super(StrictInfoMeta, cls).__new__
extend_bases = (_StrictInfo,) + bases # Enforce inheritance
return super_new(cls, name, extend_bases, attrs)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import meta
class TestMeta(unittest.TestCase):
def test_InfoMeta(self):
fields = ['foo', 'bar']
class Info(object):
__metaclass__ = meta.InfoMeta
_fields_ = fields
f = Info()
self.assertEqual(f.foo, None)
self.assertEqual(f.bar, None)
self.assertRaises(AttributeError, getattr, f, 'baz')
self.assertEqual(f._fields_, fields)
f.foo = 'Hello'
self.assertEqual(f.foo, 'Hello')
f.baz = 'World'
self.assertEqual(f.baz, 'World')
## with self.assertRaises(TypeError):
## f = Info(foo='Hello', bar='World')
self.assertRaises(TypeError, Info, foo='Hello', bar='World')
def test_StrictInfoMeta(self):
fields = ['foo', 'bar']
class StrictInfo(object):
__metaclass__ = meta.StrictInfoMeta
_fields_ = fields
f = StrictInfo()
self.assertEqual(f.foo, None)
self.assertEqual(f.bar, None)
self.assertRaises(AttributeError, getattr, f, 'baz')
self.assertEqual(f.fields, fields)
f.foo = 'Hello'
self.assertEqual(f.foo, 'Hello')
self.assertRaises(AttributeError, setattr, f, 'baz', 'World')
self.assertRaises(AttributeError, StrictInfo, foo='Hello', bar='World', baz='!')
if __name__ == '__main__':
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment