[Checkins] SVN: z3c.persistentfactory/trunk/ Initial release
Ross Patterson
me at rpatterson.net
Wed Apr 9 02:39:34 EDT 2008
Log message for revision 85175:
Initial release
Changed:
A z3c.persistentfactory/trunk/README.txt
A z3c.persistentfactory/trunk/docs/
A z3c.persistentfactory/trunk/docs/HISTORY.txt
A z3c.persistentfactory/trunk/setup.py
A z3c.persistentfactory/trunk/z3c/
A z3c.persistentfactory/trunk/z3c/__init__.py
A z3c.persistentfactory/trunk/z3c/persistentfactory/
A z3c.persistentfactory/trunk/z3c/persistentfactory/README.txt
A z3c.persistentfactory/trunk/z3c/persistentfactory/__init__.py
A z3c.persistentfactory/trunk/z3c/persistentfactory/declarations.py
A z3c.persistentfactory/trunk/z3c/persistentfactory/declarations.txt
A z3c.persistentfactory/trunk/z3c/persistentfactory/factory.py
A z3c.persistentfactory/trunk/z3c/persistentfactory/testing.py
A z3c.persistentfactory/trunk/z3c/persistentfactory/tests.py
-=-
Added: z3c.persistentfactory/trunk/README.txt
===================================================================
--- z3c.persistentfactory/trunk/README.txt (rev 0)
+++ z3c.persistentfactory/trunk/README.txt 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,14 @@
+Introduction
+============
+
+z3c.persistentfactory provides a Persistentfactory class that wraps a
+method in a persistent wrapper. It also provides a function decorator
+for use on class method definitions such that a persistent factory
+will be used when the method is accessed on instance of the class.
+See z3c/persistentfactory/README.txt for more details.
+
+Also see z3c/persistentfactory/declarartions.txt for details about a
+mixin Declarer class for classes implementing callable instances whose
+declarations should pickle and persist correctly.
+
+
Added: z3c.persistentfactory/trunk/docs/HISTORY.txt
===================================================================
--- z3c.persistentfactory/trunk/docs/HISTORY.txt (rev 0)
+++ z3c.persistentfactory/trunk/docs/HISTORY.txt 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,8 @@
+Changelog
+=========
+
+0.1 - 2008-04-08
+----------------
+
+* Initial release
+
Added: z3c.persistentfactory/trunk/setup.py
===================================================================
--- z3c.persistentfactory/trunk/setup.py (rev 0)
+++ z3c.persistentfactory/trunk/setup.py 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,38 @@
+from setuptools import setup, find_packages
+import os
+
+version = '0.1'
+
+setup(name='z3c.persistentfactory',
+ version=version,
+ description=(
+ "Wrap instance methods in persistent factory wrappers for "
+ "using instance methods as ZCA factories."),
+ long_description=(open(os.path.join(
+ "z3c", "persistentfactory", "README.txt")).read() + "\n" +
+ open(os.path.join("docs", "HISTORY.txt")).read()),
+ # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+ classifiers=[
+ "Programming Language :: Python",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ ],
+ keywords='',
+ author='Ross Patterson',
+ author_email='me at rpatterson.net',
+ url='http://pypi.python.org/pypi/z3c.persistentfactory',
+ license='ZPL',
+ packages=find_packages(exclude=['ez_setup']),
+ namespace_packages=['z3c'],
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=[
+ 'setuptools',
+ # -*- Extra requirements: -*-
+ 'zope.interface',
+ 'zope.component',
+ 'ZODB3',
+ ],
+ entry_points="""
+ # -*- Entry points: -*-
+ """,
+ )
Added: z3c.persistentfactory/trunk/z3c/__init__.py
===================================================================
--- z3c.persistentfactory/trunk/z3c/__init__.py (rev 0)
+++ z3c.persistentfactory/trunk/z3c/__init__.py 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+ __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+ from pkgutil import extend_path
+ __path__ = extend_path(__path__, __name__)
Added: z3c.persistentfactory/trunk/z3c/persistentfactory/README.txt
===================================================================
--- z3c.persistentfactory/trunk/z3c/persistentfactory/README.txt (rev 0)
+++ z3c.persistentfactory/trunk/z3c/persistentfactory/README.txt 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,195 @@
+;-*-Doctest-*-
+
+====================
+Persistent Factories
+====================
+
+z3c.persistentfactory provides a PersistentFactory class that wraps a
+method in a persistent wrapper. It also provides a function decorator
+for use on class method definitions such that a persistent factory
+will be used when the method is accessed on instance of the class.
+
+Also see declarartions.txt for details about a mixin Declarer class
+for classes implementing callable instances whose declarations should
+pickle and persist correctly.
+
+Factory
+=======
+
+The factory module provides a persistent declarer class for creating
+callable objects wrapping a instance method as factories in the
+persistent registry.
+
+Create an object with a method that has declarations.
+
+ >>> from z3c.persistentfactory import testing
+ >>> bar = testing.Bar()
+
+Verify that the instance method has declarations.
+
+ >>> from zope import interface, component
+ >>> tuple(interface.implementedBy(bar.factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+ >>> component.adaptedBy(bar.factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
+
+Wrap the instance method in a persistent factory.
+
+ >>> from z3c.persistentfactory import factory
+ >>> bar_factory = factory.PersistentFactory(bar.factory)
+
+Pickle and unpickle the factory to verify everything is pickleable.
+
+ >>> import pickle
+ >>> bar_factory = pickle.loads(pickle.dumps(bar_factory))
+
+The factory's __call__ method is the original instance method of the
+original object, not a method of the persistent factory object.
+
+ >>> bar_factory.__call__
+ <bound method Bar.factory of
+ <z3c.persistentfactory.testing.Bar object at ...>>
+ >>> bar_factory.__call__.im_self.__class__ is bar.__class__
+ True
+
+The factory is callable.
+
+ >>> bar_factory()
+ <z3c.persistentfactory.testing.Bar object at ...>
+
+The factory has the same declarations as the original method.
+
+ >>> tuple(interface.implementedBy(bar_factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+ >>> component.adaptedBy(bar_factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
+
+If the wrapped method's declarations haven't been overridden, then
+changes to the wrapped method's adapts declarations are reflected in
+the factory. Unfortunately, the zope.interface implementation for
+checking implementer declarations checks the factory's instance
+dictionary directly, so changes to the wrapped method's implements
+declarations aren't reflected in the factory. This means that any
+changes to the wrapped methods implements declaration that need to be
+reflected in existing persistent factories will require migrating the
+existing factories.
+
+ >>> _ = interface.implementer(testing.IFoo)(bar.factory.im_func)
+ >>> _ = component.adapter(testing.IBar)(bar.factory.im_func)
+
+ >>> tuple(interface.implementedBy(bar.factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
+ >>> component.adaptedBy(bar.factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+
+ >>> tuple(interface.implementedBy(bar_factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+ >>> component.adaptedBy(bar_factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+
+The wrapped method's declarations can be overridden in the factory.
+
+ >>> _ = interface.implementer(testing.IBaz)(bar_factory)
+ >>> _ = component.adapter(testing.IQux)(bar_factory)
+
+ >>> tuple(interface.implementedBy(bar_factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IBaz>,)
+ >>> component.adaptedBy(bar_factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IQux>,)
+
+Overriding the wrapped method's declarations in the factory doesn't
+modify the declarations on the wrapped method.
+
+ >>> tuple(interface.implementedBy(bar.factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
+ >>> component.adaptedBy(bar.factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+
+However, Once the wrapped method's declarations have been overriden in
+the factory, the factory no longer reflects any changes in the wrapped
+method's declarations.
+
+ >>> _ = interface.implementer(testing.IQux)(bar.factory.im_func)
+ >>> _ = component.adapter(testing.IBaz)(bar.factory.im_func)
+
+ >>> tuple(interface.implementedBy(bar.factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IQux>,)
+ >>> component.adaptedBy(bar.factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IBaz>,)
+
+ >>> tuple(interface.implementedBy(bar_factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IBaz>,)
+ >>> component.adaptedBy(bar_factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IQux>,)
+
+Decorator
+=========
+
+A decorator is provided that will return the decorated method wrapped
+in a persistent factory when the method is accessed on an instance.
+
+The Baz class uses the decorator in the python code. Note that the
+factory decorator must come before the declaration decorators so that
+it will be run last and will reflect the declarations.
+
+ >>> baz = testing.Baz()
+
+On an instance, the method is replaced with a persistent factory on
+first access.
+
+ >>> baz.factory
+ <z3c.persistentfactory.factory.PersistentFactory
+ object at ...>
+
+Pickle and unpickle the object to verify everything is pickleable.
+
+ >>> baz = pickle.loads(pickle.dumps(baz))
+
+The factory is the same object on subsequent accesses.
+
+ >>> baz.factory is baz.factory
+ True
+
+The factory's __call__ method is an instance method of the original
+object, not a method of the persistent factory object.
+
+ >>> baz.factory.__call__
+ <bound method Baz.factory of
+ <z3c.persistentfactory.testing.Baz object at ...>>
+
+The factory is callable and calls the wrapped method.
+
+ >>> baz.factory()
+ <z3c.persistentfactory.testing.Baz object at ...>
+
+The declarations of the factory reflect the declarations on the
+wrapped method.
+
+ >>> tuple(interface.implementedBy(baz.factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+ >>> component.adaptedBy(baz.factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
+
+The declarations can be overridden in the factory.
+
+ >>> _ = interface.implementer(testing.IFoo)(baz.factory)
+ >>> _ = component.adapter(testing.IBar)(baz.factory)
+
+Pickle and unpickle again to verify the pickleability of factory
+declarations.
+
+ >>> baz = pickle.loads(pickle.dumps(baz))
+
+The declaration changes are reflected on the factory.
+
+ >>> tuple(interface.implementedBy(baz.factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
+ >>> component.adaptedBy(baz.factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+
+But the class methods declarations are unaffected.
+
+ >>> tuple(interface.implementedBy(testing.Baz.factory))
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+ >>> component.adaptedBy(testing.Baz.factory)
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
Added: z3c.persistentfactory/trunk/z3c/persistentfactory/__init__.py
===================================================================
--- z3c.persistentfactory/trunk/z3c/persistentfactory/__init__.py (rev 0)
+++ z3c.persistentfactory/trunk/z3c/persistentfactory/__init__.py 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1 @@
+"""Persistent component factories"""
Added: z3c.persistentfactory/trunk/z3c/persistentfactory/declarations.py
===================================================================
--- z3c.persistentfactory/trunk/z3c/persistentfactory/declarations.py (rev 0)
+++ z3c.persistentfactory/trunk/z3c/persistentfactory/declarations.py 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,40 @@
+from zope import interface, component
+from zope.interface import declarations
+
+class Implements(declarations.Implements):
+ """A pickleable implements declaration"""
+
+ def __reduce__(self):
+ return Implements, self.__bases__
+
+class ImplementsDescriptor(object):
+
+ def __get__(self, instance, owner):
+ if instance is None:
+ raise AttributeError('__implemented__')
+
+ if '__implemented__' not in instance.__dict__:
+ instance.__implemented__ = interface.implementedBy(
+ instance.__call__)
+ return instance.__dict__['__implemented__']
+
+ def __set__(self, instance, value):
+ instance.__dict__['__implemented__'] = Implements(*value)
+
+ def __delete__(self, instance):
+ if '__implemented__' not in instance.__dict__:
+ raise AttributeError('__implemented__')
+ del instance.__dict__['__implemented__']
+
+class AdaptsDescriptor(object):
+
+ def __get__(self, instance, owner):
+ if instance is None:
+ raise AttributeError('__implemented__')
+
+ return component.adaptedBy(instance.__call__)
+
+class Declarer(object):
+
+ __implemented__ = ImplementsDescriptor()
+ __component_adapts__ = AdaptsDescriptor()
Added: z3c.persistentfactory/trunk/z3c/persistentfactory/declarations.txt
===================================================================
--- z3c.persistentfactory/trunk/z3c/persistentfactory/declarations.txt (rev 0)
+++ z3c.persistentfactory/trunk/z3c/persistentfactory/declarations.txt 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,73 @@
+;-*-Doctest-*-
+
+=======================
+Pickleable Declarations
+=======================
+
+The implements declaration class in zope.interface only pickles a
+reference to a class to inherit from if available. For declarations
+on persistent factories, we need a implements declaration that pickles
+and unpickles with the interfaces it includes.
+
+Create a sample object.
+
+ >>> from z3c.persistentfactory import testing
+ >>> foo = testing.Foo()
+
+Before any declarations have been made, the object doesn't implement
+or adapt anything.
+
+ >>> from zope import interface, component
+ >>> interface.implementedBy(foo)
+ Traceback (most recent call last):
+ TypeError: ('ImplementedBy called for non-factory',
+ <z3c.persistentfactory.testing.Foo object at ...>)
+ >>> component.adaptedBy(foo)
+
+Declare the interfaces the object implements and adapts.
+
+ >>> _ = interface.implementer(testing.IFoo)(foo)
+ >>> _ = component.adapter(testing.IBar)(foo)
+
+Now the objects declarations can be inspected.
+
+ >>> tuple(interface.implementedBy(foo))
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
+ >>> component.adaptedBy(foo)
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+
+The implements declaration doesn't survive pickling and unpickling.
+
+ >>> import pickle
+ >>> foo_unpickled = pickle.loads(pickle.dumps(foo))
+ >>> tuple(interface.implementedBy(foo_unpickled))
+ ()
+ >>> component.adaptedBy(foo_unpickled)
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+
+Replace the implements declaration with a pickleable declaration.
+
+ >>> from z3c.persistentfactory import declarations
+ >>> foo.__implemented__ = declarations.Implements(*foo.__implemented__)
+
+Now the implements declaration survives pickling and unpickling.
+
+ >>> foo_unpickled = pickle.loads(pickle.dumps(foo))
+ >>> tuple(interface.implementedBy(foo_unpickled))
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
+ >>> component.adaptedBy(foo_unpickled)
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
+
+A mixin class is provided that supports pickleable implements
+declarations.
+
+ >>> bar = declarations.Declarer()
+
+ >>> _ = interface.implementer(testing.IFoo)(bar)
+ >>> _ = component.adapter(testing.IBar)(bar)
+
+ >>> bar_unpickled = pickle.loads(pickle.dumps(bar))
+ >>> tuple(interface.implementedBy(bar_unpickled))
+ (<InterfaceClass z3c.persistentfactory.testing.IFoo>,)
+ >>> component.adaptedBy(bar_unpickled)
+ (<InterfaceClass z3c.persistentfactory.testing.IBar>,)
Added: z3c.persistentfactory/trunk/z3c/persistentfactory/factory.py
===================================================================
--- z3c.persistentfactory/trunk/z3c/persistentfactory/factory.py (rev 0)
+++ z3c.persistentfactory/trunk/z3c/persistentfactory/factory.py 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,36 @@
+import new
+
+from zope import interface, component
+import persistent
+
+import declarations
+
+class PersistentFactory(declarations.Declarer, persistent.Persistent):
+
+ def __init__(self, method):
+ self.context = method.im_self
+ self.__name__ = method.__name__
+ self.__implemented__ = interface.implementedBy(method)
+
+ @property
+ def __call__(self):
+ type_ = type(self.context)
+ return new.instancemethod(
+ getattr(type_, self.__name__), self.context, type_)
+
+class Factory(object):
+
+ def __init__(self, callable_):
+ self.callable = callable_
+
+ def __get__(self, instance, owner):
+ method = new.instancemethod(self.callable, instance, owner)
+ if instance is None:
+ # when accessed for the class, just return the method
+ return method
+
+ result = PersistentFactory(method)
+ setattr(instance, method.__name__, result)
+ return result
+
+factory = Factory
Added: z3c.persistentfactory/trunk/z3c/persistentfactory/testing.py
===================================================================
--- z3c.persistentfactory/trunk/z3c/persistentfactory/testing.py (rev 0)
+++ z3c.persistentfactory/trunk/z3c/persistentfactory/testing.py 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,25 @@
+from zope import interface, component
+
+from z3c.persistentfactory import declarations, factory
+
+class IFoo(interface.Interface): pass
+class IBar(interface.Interface): pass
+class IBaz(interface.Interface): pass
+class IQux(interface.Interface): pass
+
+class Foo(object): pass
+
+class Bar(object):
+
+ @interface.implementer(IBar)
+ @component.adapter(IFoo)
+ def factory(self):
+ return self
+
+class Baz(object):
+
+ @factory.factory
+ @interface.implementer(IBar)
+ @component.adapter(IFoo)
+ def factory(self):
+ return self
Added: z3c.persistentfactory/trunk/z3c/persistentfactory/tests.py
===================================================================
--- z3c.persistentfactory/trunk/z3c/persistentfactory/tests.py (rev 0)
+++ z3c.persistentfactory/trunk/z3c/persistentfactory/tests.py 2008-04-09 06:39:33 UTC (rev 85175)
@@ -0,0 +1,14 @@
+import unittest
+from zope.testing import doctest
+
+def test_suite():
+ return doctest.DocFileSuite(
+ 'README.txt',
+ 'declarations.txt',
+ optionflags=(
+ doctest.REPORT_NDIFF|
+ doctest.NORMALIZE_WHITESPACE|
+ doctest.ELLIPSIS))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
More information about the Checkins
mailing list