Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add default public implementations of IMime/ClassObjectFactory.
- Loading branch information
Showing
5 changed files
with
230 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Implementations of object factories. | ||
.. versionadded:: 1.0 | ||
""" | ||
from __future__ import absolute_import | ||
from __future__ import division | ||
from __future__ import print_function | ||
|
||
from zope import interface | ||
from zope.component.factory import Factory | ||
|
||
from nti.externalization.interfaces import IClassObjectFactory | ||
from nti.externalization.interfaces import IMimeObjectFactory | ||
|
||
_builtin_callable = callable | ||
|
||
# pylint: disable=redefined-builtin, protected-access | ||
|
||
class ObjectFactory(Factory): | ||
""" | ||
A convenient :class:`zope.component.interfaces.IFactory` meant to be | ||
registered as a named utility. | ||
The callable object SHOULD be a type/class object, because that's | ||
the only thing we base equality off of (class identity). | ||
This class can be subclassed and trivially instantiated by setting | ||
class properties :attr:`default_factory` and (optionally) | ||
:attr:`default_title` and :attr:`default_description`. Constructor | ||
arguments will override these, but if not given, the class values | ||
will be used. | ||
For example:: | ||
>>> class MyObjectFactory(ObjectFactory): | ||
... default_factory = object | ||
... default_title = 'A Title' | ||
... default_description = 'A Description' | ||
>>> factory = MyObjectFactory() | ||
>>> factory # doctest: +ELLIPSIS | ||
<MyObjectFactory for <... 'object'>> | ||
>>> print(factory.title) | ||
A Title | ||
>>> print(factory.description) | ||
A Description | ||
""" | ||
|
||
#: The default callable argument, if none is given to the | ||
#: constructor. | ||
default_factory = None | ||
#: The default title, if none is given to the constructor. | ||
default_title = u'' | ||
#: The default description, if none is given to the constructor. | ||
default_description = u'' | ||
|
||
def __init__(self, callable=None, title='', description='', interfaces=None): | ||
callable = callable if callable is not None else self.default_factory | ||
if callable is None or not _builtin_callable(callable): | ||
raise ValueError("Must provide callable object, not %r" % (callable,)) | ||
Factory.__init__(self, | ||
callable, | ||
title or self.default_title, | ||
description or self.default_description, | ||
interfaces) | ||
|
||
def __eq__(self, other): | ||
# Implementing equality is needed to prevent multiple inclusions | ||
# of the same module from different places from conflicting. | ||
try: | ||
return self._callable is other._callable | ||
except AttributeError: # pragma: no cover | ||
return NotImplemented | ||
|
||
def __hash__(self): | ||
return hash(self._callable) | ||
|
||
|
||
@interface.implementer(IMimeObjectFactory) | ||
class MimeObjectFactory(ObjectFactory): | ||
""" | ||
Default implementation of | ||
:class:`nti.externalization.interfaces.IMimeObjectFactory`. | ||
""" | ||
|
||
@interface.implementer(IClassObjectFactory) | ||
class ClassObjectFactory(ObjectFactory): | ||
""" | ||
Default implementation of | ||
:class:`nti.externalization.interfaces.IClassObjectFactory`. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Tests for factory.py | ||
""" | ||
from __future__ import absolute_import | ||
from __future__ import division | ||
from __future__ import print_function | ||
|
||
# stdlib imports | ||
import doctest | ||
import unittest | ||
|
||
from nti.testing import base | ||
from nti.testing import matchers | ||
|
||
from hamcrest import assert_that | ||
from hamcrest import has_entry | ||
from hamcrest import has_key | ||
from hamcrest import is_ | ||
from hamcrest import is_not | ||
from hamcrest import has_property | ||
from nti.testing.matchers import validly_provides | ||
|
||
logger = __import__('logging').getLogger(__name__) | ||
|
||
# disable: accessing protected members, too many methods | ||
# pylint: disable=W0212,R0904 | ||
|
||
class TestObjectFactory(unittest.TestCase): | ||
|
||
def _getInterface(self): | ||
from zope.component.interfaces import IFactory | ||
return IFactory | ||
|
||
def _getTargetClass(self): | ||
from nti.externalization.factory import ObjectFactory | ||
return ObjectFactory | ||
|
||
def _makeOne(self, *args, **kwargs): | ||
return self._getTargetClass()(*args, **kwargs) | ||
|
||
def test_implements_interface(self): | ||
|
||
class Callable(object): | ||
pass | ||
|
||
factory = self._makeOne(Callable) | ||
|
||
assert_that(factory(), is_(Callable)) | ||
assert_that(factory.title, is_('')) | ||
assert_that(factory.description, is_('')) | ||
|
||
assert_that(factory, validly_provides(self._getInterface())) | ||
|
||
def test_equality(self): | ||
|
||
class Callable1(object): | ||
pass | ||
|
||
factory1 = self._makeOne(Callable1) | ||
factory1_a = self._makeOne(Callable1) | ||
|
||
assert_that(factory1, is_(factory1_a)) | ||
|
||
class Callable2(object): | ||
pass | ||
|
||
factory2 = self._makeOne(Callable2) | ||
assert_that(factory1, is_not(factory2)) | ||
assert_that(factory2, is_not(factory1)) | ||
|
||
def test_subclass(self): | ||
class Callable(object): | ||
pass | ||
|
||
class Factory(self._getTargetClass()): | ||
default_factory = Callable | ||
default_title = "title" | ||
default_description = "description" | ||
|
||
factory = Factory() | ||
|
||
assert_that(factory, has_property('_callable', Callable)) | ||
assert_that(factory, has_property('title', Factory.default_title)) | ||
assert_that(factory, has_property('description', Factory.default_description)) | ||
|
||
factory = Factory(object, 'foo', 'baz') | ||
assert_that(factory, has_property('_callable', object)) | ||
assert_that(factory, has_property('title', 'foo')) | ||
assert_that(factory, has_property('description', 'baz')) | ||
|
||
def test_create_non_callable(self): | ||
with self.assertRaises(ValueError): | ||
self._makeOne() | ||
|
||
|
||
class TestMimeObjectFactory(TestObjectFactory): | ||
|
||
def _getInterface(self): | ||
from nti.externalization.interfaces import IMimeObjectFactory | ||
return IMimeObjectFactory | ||
|
||
def _getTargetClass(self): | ||
from nti.externalization.factory import MimeObjectFactory | ||
return MimeObjectFactory | ||
|
||
class TestClassObjectFactory(TestObjectFactory): | ||
|
||
def _getInterface(self): | ||
from nti.externalization.interfaces import IClassObjectFactory | ||
return IClassObjectFactory | ||
|
||
def _getTargetClass(self): | ||
from nti.externalization.factory import ClassObjectFactory | ||
return ClassObjectFactory | ||
|
||
def test_suite(): | ||
return unittest.TestSuite(( | ||
unittest.defaultTestLoader.loadTestsFromName(__name__), | ||
doctest.DocTestSuite("nti.externalization.factory"), | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters