Files
sloth/items/factory.py
T
2011-05-13 11:07:44 +02:00

162 lines
4.6 KiB
Python

import exceptions
import importlib
class Factory:
"""
A generic factory for both items and inserters.
"""
def __init__(self, items=None):
"""
Constructor.
Parameters
==========
items: dict
Mapping from type to python callable. If not None, all mappings
will be registered with the factory.
"""
self.items_ = {}
if items is not None:
for _type, item in items.iteritems():
self.register(_type, item, replace=True)
def import_callable(self, module_path_name):
"""
Import the callable given by ``module_path_name``.
"""
try:
module_path, name = module_path_name.rsplit('.', 1)
except ValueError:
raise exceptions.ImproperlyConfigured('%s is not a valid module path' % module_path_name)
try:
mod = importlib.import_module(module_path)
except ImportError, e:
raise exceptions.ImproperlyConfigured('Error importing module %s: "%s"' % (module_path, e))
try:
item_callable = getattr(mod, name)
except AttributeError:
raise exceptions.ImproperlyConfigured('Module "%s" does not define a "%s" callable' % (module_path, name))
return item_callable
def register(self, _type, item, replace=False):
"""
Register a new type-item mapping.
Parameters
==========
_type: string
Type of the item.
item: python callable or string
Reference to the callable which creates the new object.
"""
_type = _type.lower()
if _type in self.items_ and not replace:
raise Exception("Type %s already has an item: %s" % \
(_type, str(self.items_[_type])))
else:
if type(item) == str:
item = self.import_callable(item)
self.items_[_type] = item
def clear(self, _type=None):
"""
Remove a type-item mapping.
Parameters
==========
_type: str
Type for which the mapping should be removed. If None, all
mappings will be removed.
"""
if _type is None:
self.items_ = {}
else:
_type = _type.lower()
if _type in self.items_:
del self.items_[_type]
def create(self, _type, *args, **kwargs):
"""
Create a new object.
Parameters
==========
_type: str
Type for which a new object should be created.
All further arguments will be passed to the constructor/creating function
of the mapping.
Returns
=======
Newly created object. If for the given type no mapping exists, this
function returns ``None``.
"""
_type = _type.lower()
if _type not in self.items_:
return None
item = self.items_[_type]
if item is None:
return None
return item(*args, **kwargs)
### testing
import pytest
class MockupRectItem: pass
class MockupPointItem: pass
class MockupPolygonItem: pass
def _create_factory():
itemfactory = Factory({'point': MockupPointItem,
'polygon': MockupPolygonItem})
itemfactory.register('rect', MockupRectItem)
return itemfactory
def test_register():
itemfactory = _create_factory()
item = itemfactory.create('rect')
assert isinstance(item, MockupRectItem)
item = itemfactory.create('point')
assert isinstance(item, MockupPointItem)
item = itemfactory.create('polygon')
assert isinstance(item, MockupPolygonItem)
item = itemfactory.create('polygon2')
assert item is None
def test_register_fail():
itemfactory = _create_factory()
with pytest.raises(Exception):
itemfactory.register('rect', MockupRectItem)
def test_register_replace():
itemfactory = _create_factory()
itemfactory.register('rect', MockupPolygonItem, replace=True)
item = itemfactory.create('rect')
assert isinstance(item, MockupPolygonItem)
def test_clear():
itemfactory = _create_factory()
item = itemfactory.create('rect')
assert isinstance(item, MockupRectItem)
itemfactory.clear('rect')
item = itemfactory.create('rect')
assert item is None
item = itemfactory.create('point')
assert isinstance(item, MockupPointItem)
item = itemfactory.create('polygon')
assert isinstance(item, MockupPolygonItem)
itemfactory.clear()
assert itemfactory.create('point') is None
assert itemfactory.create('polygon') is None