[Checkins] SVN: Sandbox/malthe/ Spun off from zope.app.container.
Malthe Borch
mborch at gmail.com
Tue Apr 22 08:01:21 EDT 2008
Log message for revision 85588:
Spun off from zope.app.container.
Changed:
A Sandbox/malthe/
A Sandbox/malthe/zope.container/
A Sandbox/malthe/zope.container/.installed.cfg
A Sandbox/malthe/zope.container/CHANGES.txt
A Sandbox/malthe/zope.container/README.txt
A Sandbox/malthe/zope.container/bootstrap.py
A Sandbox/malthe/zope.container/buildout.cfg
A Sandbox/malthe/zope.container/setup.py
A Sandbox/malthe/zope.container/src/
A Sandbox/malthe/zope.container/src/zope/
A Sandbox/malthe/zope.container/src/zope/__init__.py
A Sandbox/malthe/zope.container/src/zope/container/
A Sandbox/malthe/zope.container/src/zope/container/__init__.py
A Sandbox/malthe/zope.container/src/zope/container/constraints.py
A Sandbox/malthe/zope.container/src/zope/container/constraints.txt
A Sandbox/malthe/zope.container/src/zope/container/find.py
A Sandbox/malthe/zope.container/src/zope/container/i18n.py
A Sandbox/malthe/zope.container/src/zope/container/interfaces.py
A Sandbox/malthe/zope.container/src/zope/container/property.py
A Sandbox/malthe/zope.container/src/zope/container/property.txt
A Sandbox/malthe/zope.container/src/zope/container/size.py
A Sandbox/malthe/zope.container/src/zope/container/tests/
A Sandbox/malthe/zope.container/src/zope/container/tests/__init__.py
A Sandbox/malthe/zope.container/src/zope/container/tests/test_constraints.py
A Sandbox/malthe/zope.container/src/zope/container/tests/test_find.py
A Sandbox/malthe/zope.container/src/zope/container/tests/test_property.py
A Sandbox/malthe/zope.container/src/zope/container/tests/test_size.py
-=-
Added: Sandbox/malthe/zope.container/.installed.cfg
===================================================================
--- Sandbox/malthe/zope.container/.installed.cfg (rev 0)
+++ Sandbox/malthe/zope.container/.installed.cfg 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,50 @@
+[buildout]
+installed_develop_eggs =
+parts = test
+
+[test]
+__buildout_installed__ = /Users/mborch/co/zope.container/parts/test
+ /Users/mborch/co/zope.container/bin/test
+__buildout_signature__ = zc.recipe.testrunner-1.0.0-py2.5.egg zc.recipe.egg-1.0.0-py2.5.egg setuptools-0.6c8-py2.5.egg zope.testing-3.5.1-py2.5.egg zc.buildout-1.0.1-py2.5.egg zc.buildout-1.0.1-py2.5.egg
+_b = /Users/mborch/co/zope.container/bin
+_d = /Users/mborch/co/zope.container/develop-eggs
+_e = /Users/mborch/Development/sources/buildout
+bin-directory = /Users/mborch/co/zope.container/bin
+develop-eggs-directory = /Users/mborch/co/zope.container/develop-eggs
+eggs = zope.container
+eggs-directory = /Users/mborch/Development/sources/buildout
+executable = /opt/local/bin/python2.5
+index = http://download.zope.org/ppix
+location = /Users/mborch/co/zope.container/parts/test
+recipe = zc.recipe.testrunner
+script = /Users/mborch/co/zope.container/bin/test
+
+[buildout]
+installed_develop_eggs =
+
+[buildout]
+parts = test
+
+[buildout]
+installed_develop_eggs =
+
+[buildout]
+parts = test
+
+[buildout]
+installed_develop_eggs =
+
+[buildout]
+parts = test
+
+[buildout]
+installed_develop_eggs =
+
+[buildout]
+parts = test
+
+[buildout]
+installed_develop_eggs =
+
+[buildout]
+parts = test
Added: Sandbox/malthe/zope.container/CHANGES.txt
===================================================================
--- Sandbox/malthe/zope.container/CHANGES.txt (rev 0)
+++ Sandbox/malthe/zope.container/CHANGES.txt 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,9 @@
+=======
+CHANGES
+=======
+
+3.6.0 (unreleased)
+------------------
+
+- Initial creation of package based on parts in ``zope.app.container``
+ that do not depend on the ZODB.
Added: Sandbox/malthe/zope.container/README.txt
===================================================================
--- Sandbox/malthe/zope.container/README.txt (rev 0)
+++ Sandbox/malthe/zope.container/README.txt 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,2 @@
+This package define interfaces of container components and implements
+container constraints and utilities.
Added: Sandbox/malthe/zope.container/bootstrap.py
===================================================================
--- Sandbox/malthe/zope.container/bootstrap.py (rev 0)
+++ Sandbox/malthe/zope.container/bootstrap.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,56 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 77225 2007-06-29 09:20:13Z dobe $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+try:
+ import pkg_resources
+except ImportError:
+ ez = {}
+ exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+ import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
+
Added: Sandbox/malthe/zope.container/buildout.cfg
===================================================================
--- Sandbox/malthe/zope.container/buildout.cfg (rev 0)
+++ Sandbox/malthe/zope.container/buildout.cfg 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,8 @@
+[buildout]
+develop = .
+parts = test
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = zope.container
+
Added: Sandbox/malthe/zope.container/setup.py
===================================================================
--- Sandbox/malthe/zope.container/setup.py (rev 0)
+++ Sandbox/malthe/zope.container/setup.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Setup for zope.app.container package
+
+$Id: setup.py 81389 2007-11-01 21:09:32Z srichter $
+"""
+import os
+from setuptools import setup, find_packages, Extension
+
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup(name='zope.container',
+ version = '3.6.0',
+ author='Zope Corporation and Contributors',
+ author_email='zope3-dev at zope.org',
+ description='Zope Container',
+ long_description=(
+ read('README.txt')
+ + '\n\n' +
+ 'Detailed Documentation\n'
+ '**********************\n'
+ + '\n\n' +
+ read('src', 'zope', 'container', 'constraints.txt')
+ + '\n\n' +
+ read('CHANGES.txt')
+ ),
+ keywords = "zope3 container",
+ classifiers = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ url='http://cheeseshop.python.org/pypi/zope.container',
+ license='ZPL 2.1',
+ packages=find_packages('src'),
+ package_dir = {'': 'src'},
+ namespace_packages=['zope', ],
+ install_requires=['setuptools',
+ 'zope.interface',
+ 'zope.component',
+ 'zope.size',
+ 'zope.dottedname',
+ 'zope.location',
+ 'zope.lifecycleevent',
+ 'zope.deprecation',
+ ],
+ include_package_data = True,
+ zip_safe = False,
+ )
Added: Sandbox/malthe/zope.container/src/zope/__init__.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/__init__.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/__init__.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+ import pkg_resources
+ pkg_resources.declare_namespace(__name__)
+except ImportError:
+ import pkgutil
+ __path__ = pkgutil.extend_path(__path__, __name__)
Added: Sandbox/malthe/zope.container/src/zope/container/__init__.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/__init__.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/__init__.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1 @@
+#
Added: Sandbox/malthe/zope.container/src/zope/container/constraints.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/constraints.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/constraints.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,462 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Support for containment constraints
+
+ Either a container or an object can provide constraints on the
+ containment relationship.
+
+ A container expresses constraints through a precondition on it's
+ `__setitem__` method in it's interface.
+
+ Preconditions can be simple callable objects, like functions. They
+ should raise a ``zope.interface.Invalid`` exception to indicate that a
+ constraint isn't satisfied:
+
+ >>> def preNoZ(container, name, ob):
+ ... "Silly precondition example"
+ ... if name.startswith("Z"):
+ ... raise zope.interface.Invalid("Names can not start with Z")
+
+ >>> class I1(zope.interface.Interface):
+ ... def __setitem__(name, on):
+ ... "Add an item"
+ ... __setitem__.precondition = preNoZ
+
+ >>> from zope.container.interfaces import IContainer
+ >>> class C1(object):
+ ... zope.interface.implements(I1, IContainer)
+ ... def __repr__(self):
+ ... return 'C1'
+
+ Given such a precondition, we can then check whether an object can be
+ added:
+
+ >>> c1 = C1()
+ >>> checkObject(c1, "bob", None)
+ >>> checkObject(c1, "Zbob", None)
+ Traceback (most recent call last):
+ ...
+ Invalid: Names can not start with Z
+
+ We can also express constaints on the containers an object can be
+ added to. We do this by setting a field constraint on an object's
+ `__parent__` attribute:
+
+ >>> import zope.schema
+
+ A field constraint is a callable object that returns a boolean value:
+
+ >>> def con1(container):
+ ... "silly container constraint"
+ ... if not hasattr(container, 'x'):
+ ... return False
+ ... return True
+
+ >>> class I2(zope.interface.Interface):
+ ... __parent__ = zope.schema.Field(constraint = con1)
+
+ >>> class O(object):
+ ... zope.interface.implements(I2)
+
+ If the constraint isn't satisfied, we'll get a validation error when we
+ check whether the object can be added:
+
+ >>> checkObject(c1, "bob", O())
+ Traceback (most recent call last):
+ ...
+ ConstraintNotSatisfied: C1
+
+ Note that the validation error isn't very informative. For that
+ reason, it's better for constraints to raise Invalid errors when they
+ aren't satisfied:
+
+ >>> def con1(container):
+ ... "silly container constraint"
+ ... if not hasattr(container, 'x'):
+ ... raise zope.interface.Invalid("What, no x?")
+ ... return True
+
+ >>> class I2(zope.interface.Interface):
+ ... __parent__ = zope.schema.Field(constraint = con1)
+
+ >>> class O(object):
+ ... zope.interface.implements(I2)
+
+ >>> checkObject(c1, "bob", O())
+ Traceback (most recent call last):
+ ...
+ Invalid: What, no x?
+
+ >>> c1.x = 1
+ >>> checkObject(c1, "bob", O())
+
+ The `checkObject` function is handy when checking whether we can add an
+ existing object to a container, but, sometimes, we want to check
+ whether an object produced by a factory can be added. To do this, we
+ use `checkFactory`:
+
+ >>> class Factory(object):
+ ... def __call__(self):
+ ... return O()
+ ... def getInterfaces(self):
+ ... return zope.interface.implementedBy(O)
+
+ >>> factory = Factory()
+
+ >>> checkFactory(c1, "bob", factory)
+ True
+
+ >>> del c1.x
+ >>> checkFactory(c1, "bob", factory)
+ False
+
+ Unlike `checkObject`, `checkFactory`:
+
+ - Returns a boolean value
+
+ - Takes a factory (e.g. a class) rather than an argument.
+
+ The container constraint we defined for C1 isn't actually used to
+ check the factory:
+
+ >>> c1.x = 1
+ >>> checkFactory(c1, "Zbob", factory)
+ True
+
+ To work with `checkFactory`, a container precondition has to
+ implement a factory method. This is because a factory, rather than
+ an object is passed. To illustrate this, we'll make preNoZ its own
+ factory method:
+
+ >>> preNoZ.factory = preNoZ
+
+ We can do this (silly thing) because preNoZ doesn't use the object
+ argument.
+
+ >>> checkFactory(c1, "Zbob", factory)
+ False
+
+ $Id: constraints.py 73547 2007-03-25 09:03:04Z dobe $
+ """
+__docformat__ = 'restructuredtext'
+
+import sys
+
+
+from zope.dottedname.resolve import resolve
+import zope.schema
+from zope.interface import providedBy
+from zope.container.interfaces import InvalidItemType, InvalidContainerType
+from zope.container.interfaces import IContainer
+from zope.container.i18n import ZopeMessageFactory as _
+from zope.container.property import readproperty
+
+def checkObject(container, name, object):
+ """Check containement constraints for an object and container
+ """
+
+ # check __setitem__ precondition
+ containerProvided = providedBy(container)
+ __setitem__ = containerProvided.get('__setitem__')
+ if __setitem__ is not None:
+ precondition = __setitem__.queryTaggedValue('precondition')
+ if precondition is not None:
+ precondition(container, name, object)
+
+ # check the constraint on __parent__
+ __parent__ = providedBy(object).get('__parent__')
+ if __parent__ is not None:
+ try:
+ validate = __parent__.validate
+ except AttributeError:
+ pass
+ else:
+ validate(container)
+
+
+ if not containerProvided.extends(IContainer):
+ # If it doesn't implement IContainer, it can't contain stuff.
+ raise TypeError(
+ _('Container is not a valid Zope container.')
+ )
+
+def checkFactory(container, name, factory):
+ __setitem__ = providedBy(container).get('__setitem__')
+ if __setitem__ is not None:
+ precondition = __setitem__.queryTaggedValue('precondition')
+ if precondition is not None:
+ try:
+ precondition = precondition.factory
+ except AttributeError:
+ pass
+ else:
+ try:
+ precondition(container, name, factory)
+ except zope.interface.Invalid:
+ return False
+
+ # check the constraint on __parent__
+ __parent__ = factory.getInterfaces().get('__parent__')
+ if __parent__ is not None:
+ try:
+ validate = __parent__.validate
+ except AttributeError:
+ pass
+ else:
+ try:
+ validate(container)
+ except zope.interface.Invalid:
+ return False
+
+ return True
+
+class IItemTypePrecondition(zope.interface.Interface):
+
+ def __call__(container, name, object):
+ """Test whether container setitem arguments are valid.
+
+ Raise zope.interface.Invalid if the object is invalid.
+ """
+
+ def factory(container, name, factory):
+ """Test whether objects provided by the factory are acceptable
+
+ Return a boolean value.
+ """
+
+
+class _TypesBased(object):
+
+ @readproperty
+ def types(self):
+ raw_types, module = self.raw_types
+ types = []
+ for t in raw_types:
+ if isinstance(t, str):
+ t = resolve(t, module)
+ types.append(t)
+
+ self.types = types
+ return types
+
+ def __init__(self, *types, **kw):
+ if [t for t in types if isinstance(t, str)]:
+ # have dotted names
+ module = kw.get('module', sys._getframe(1).f_globals['__name__'])
+ self.raw_types = types, module
+ else:
+ self.types = types
+
+
+class ItemTypePrecondition(_TypesBased):
+ """Specify a `__setitem__` precondition that restricts item types
+
+ Items must be one of the given types.
+
+ >>> class I1(zope.interface.Interface):
+ ... pass
+ >>> class I2(zope.interface.Interface):
+ ... pass
+
+
+ >>> precondition = ItemTypePrecondition(I1, I2)
+
+ >>> class Ob(object):
+ ... pass
+ >>> ob = Ob()
+
+ >>> class Factory(object):
+ ... def __call__(self):
+ ... return Ob()
+ ... def getInterfaces(self):
+ ... return zope.interface.implementedBy(Ob)
+
+ >>> factory = Factory()
+
+ >>> try:
+ ... precondition(None, 'foo', ob)
+ ... except InvalidItemType, v:
+ ... print v[0], (v[1] is ob), (v[2] == (I1, I2))
+ ... else:
+ ... print 'Should have failed'
+ None True True
+
+ >>> try:
+ ... precondition.factory(None, 'foo', factory)
+ ... except InvalidItemType, v:
+ ... print v[0], (v[1] is factory), (v[2] == (I1, I2))
+ ... else:
+ ... print 'Should have failed'
+ None True True
+
+ >>> zope.interface.classImplements(Ob, I2)
+ >>> precondition(None, 'foo', ob)
+ >>> precondition.factory(None, 'foo', factory)
+
+ """
+
+ zope.interface.implements(IItemTypePrecondition)
+
+ def __call__(self, container, name, object):
+ for iface in self.types:
+ if iface.providedBy(object):
+ return
+ raise InvalidItemType(container, object, self.types)
+
+ def factory(self, container, name, factory):
+ implemented = factory.getInterfaces()
+
+ for iface in self.types:
+ if implemented.isOrExtends(iface):
+ return
+ raise InvalidItemType(container, factory, self.types)
+
+
+def contains(*types):
+ """Declare that a container type contains only the given types
+
+ This is used within a class suite defining an interface to create
+ a __setitem__ specification with a precondition allowing only the
+ given types:
+
+ >>> class IFoo(zope.interface.Interface):
+ ... pass
+ >>> class IBar(zope.interface.Interface):
+ ... pass
+ >>> class IFooBarContainer(IContainer):
+ ... contains(IFoo, IBar)
+
+ >>> __setitem__ = IFooBarContainer['__setitem__']
+ >>> __setitem__.getTaggedValue('precondition').types == (IFoo, IBar)
+ True
+
+ It is invalid to call contains outside a class suite:
+
+ >>> contains(IFoo, IBar)
+ Traceback (most recent call last):
+ ...
+ TypeError: contains not called from suite
+ """
+
+ frame = sys._getframe(1)
+ f_locals = frame.f_locals
+ f_globals = frame.f_globals
+
+ if not (f_locals is not f_globals
+ and f_locals.get('__module__')
+ and f_locals.get('__module__') == f_globals.get('__name__')
+ ):
+ raise TypeError("contains not called from suite")
+
+ def __setitem__(key, value):
+ pass
+ __setitem__.__doc__ = IContainer['__setitem__'].__doc__
+ __setitem__.precondition = ItemTypePrecondition(
+ *types,
+ **dict(module=f_globals['__name__'])
+ )
+ f_locals['__setitem__'] = __setitem__
+
+
+class IContainerTypesConstraint(zope.interface.Interface):
+
+ def __call__(object):
+ """Test whether object is valid.
+
+ Return True if valid.
+ Raise zope.interface.Invalid if the objet is invalid.
+ """
+
+
+class ContainerTypesConstraint(_TypesBased):
+ """Constrain a container to be one of a number of types
+
+ >>> class I1(zope.interface.Interface):
+ ... pass
+ >>> class I2(zope.interface.Interface):
+ ... pass
+ >>> class Ob(object):
+ ... pass
+ >>> ob = Ob()
+ >>> constraint = ContainerTypesConstraint(I1, I2)
+ >>> try:
+ ... constraint(ob)
+ ... except InvalidContainerType, v:
+ ... print (v[0] is ob), (v[1] == (I1, I2))
+ ... else:
+ ... print 'Should have failed'
+ True True
+
+ >>> zope.interface.classImplements(Ob, I2)
+ >>> constraint(Ob())
+ True
+
+ """
+
+ zope.interface.implements(IContainerTypesConstraint)
+
+ def __call__(self, object):
+ for iface in self.types:
+ if iface.providedBy(object):
+ return True
+ else:
+ raise InvalidContainerType(object, self.types)
+
+
+def containers(*types):
+ """Declare the container types a type can be contained in
+
+ This is used within a class suite defining an interface to create
+ a __parent__ specification with a constraint allowing only the
+ given types:
+
+ >>> class IFoo(IContainer):
+ ... pass
+ >>> class IBar(IContainer):
+ ... pass
+
+ >>> from zope.container.interfaces import IContained
+ >>> class IFooBarContained(IContained):
+ ... containers(IFoo, IBar)
+
+ >>> __parent__ = IFooBarContained['__parent__']
+ >>> __parent__.constraint.types == (IFoo, IBar)
+ True
+
+ It is invalid to call containers outside a class suite:
+
+ >>> containers(IFoo, IBar)
+ Traceback (most recent call last):
+ ...
+ TypeError: containers not called from suite
+ """
+
+ frame = sys._getframe(1)
+ f_locals = frame.f_locals
+ f_globals = frame.f_globals
+
+ if not (f_locals is not f_globals
+ and f_locals.get('__module__')
+ and f_locals.get('__module__') == f_globals.get('__name__')
+ ):
+ raise TypeError("containers not called from suite")
+
+ __parent__ = zope.schema.Field(
+ constraint = ContainerTypesConstraint(
+ *types,
+ **dict(module=f_globals['__name__'])
+ )
+ )
+ f_locals['__parent__'] = __parent__
+
Added: Sandbox/malthe/zope.container/src/zope/container/constraints.txt
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/constraints.txt (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/constraints.txt 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,97 @@
+Containment constraints
+=======================
+
+Containment constraints allow us to express restrictions on the types
+of items that can be placed in containers or on the types of
+containers an item can be placed in. We express these constraints in
+interfaces. Let's define some container and item interfaces:
+
+ >>> from zope.container.interfaces import IContainer, IContained
+ >>> from zope.container.constraints import containers, contains
+
+ >>> class IBuddyFolder(IContainer):
+ ... contains('.IBuddy')
+
+
+In this example, we used the contains function to declare that objects
+that provide IBuddyFolder can only contain items that provide IBuddy.
+Note that we used a string containing a dotted name for the IBuddy
+interface. This is because IBuddy hasn't been defined yet. When we
+define IBuddy, we can use IBuddyFolder directly:
+
+ >>> class IBuddy(IContained):
+ ... containers(IBuddyFolder)
+
+
+Now, with these interfaces in place, we can define Buddy and
+BuddyFolder classes and verify that we can put buddies in buddy
+folders:
+
+ >>> from zope import interface
+
+ >>> class Buddy:
+ ... interface.implements(IBuddy)
+
+ >>> class BuddyFolder:
+ ... interface.implements(IBuddyFolder)
+
+ >>> from zope.container.constraints import checkObject, checkFactory
+ >>> from zope.component.factory import Factory
+
+ >>> checkObject(BuddyFolder(), 'x', Buddy())
+ >>> checkFactory(BuddyFolder(), 'x', Factory(Buddy))
+ True
+
+If we try to use other containers or folders, we'll get errors:
+
+ >>> class Container:
+ ... interface.implements(IContainer)
+
+ >>> class Contained:
+ ... interface.implements(IContained)
+
+ >>> checkObject(Container(), 'x', Buddy())
+ ... # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ InvalidContainerType: ...
+
+ >>> checkFactory(Container(), 'x', Factory(Buddy))
+ False
+
+ >>> checkObject(BuddyFolder(), 'x', Contained())
+ ... # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ InvalidItemType: ...
+
+ >>> checkFactory(BuddyFolder(), 'x', Factory(Contained))
+ False
+
+In the example, we defined the container first and then the items. We
+could have defined these in the opposite order:
+
+ >>> class IContact(IContained):
+ ... containers('.IContacts')
+
+ >>> class IContacts(IContainer):
+ ... contains(IContact)
+
+ >>> class Contact:
+ ... interface.implements(IContact)
+
+ >>> class Contacts:
+ ... interface.implements(IContacts)
+
+ >>> checkObject(Contacts(), 'x', Contact())
+
+ >>> checkFactory(Contacts(), 'x', Factory(Contact))
+ True
+
+ >>> checkObject(Contacts(), 'x', Buddy())
+ ... # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ InvalidItemType: ...
+
+ >>> checkFactory(Contacts(), 'x', Factory(Buddy))
+ False
+
+
Added: Sandbox/malthe/zope.container/src/zope/container/find.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/find.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/find.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,89 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Find Support
+
+$Id: find.py 68442 2006-06-01 12:54:41Z mj $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.interface import implements
+from interfaces import IFind, IIdFindFilter, IObjectFindFilter
+from interfaces import IReadContainer
+
+class FindAdapter(object):
+
+ implements(IFind)
+
+ __used_for__ = IReadContainer
+
+ def __init__(self, context):
+ self._context = context
+
+ def find(self, id_filters=None, object_filters=None):
+ 'See IFind'
+ id_filters = id_filters or []
+ object_filters = object_filters or []
+ result = []
+ container = self._context
+ for id, object in container.items():
+ _find_helper(id, object, container,
+ id_filters, object_filters,
+ result)
+ return result
+
+
+def _find_helper(id, object, container, id_filters, object_filters, result):
+ for id_filter in id_filters:
+ if not id_filter.matches(id):
+ break
+ else:
+ # if we didn't break out of the loop, all name filters matched
+ # now check all object filters
+ for object_filter in object_filters:
+ if not object_filter.matches(object):
+ break
+ else:
+ # if we didn't break out of the loop, all filters matched
+ result.append(object)
+
+ if not IReadContainer.providedBy(object):
+ return
+
+ container = object
+ for id, object in container.items():
+ _find_helper(id, object, container, id_filters, object_filters, result)
+
+class SimpleIdFindFilter(object):
+
+ implements(IIdFindFilter)
+
+ def __init__(self, ids):
+ self._ids = ids
+
+ def matches(self, id):
+ 'See INameFindFilter'
+ return id in self._ids
+
+class SimpleInterfacesFindFilter(object):
+ """Filter objects on the provided interfaces"""
+ implements(IObjectFindFilter)
+
+ def __init__(self, *interfaces):
+ self.interfaces = interfaces
+
+ def matches(self, object):
+ for iface in self.interfaces:
+ if iface.providedBy(object):
+ return True
+ return False
Added: Sandbox/malthe/zope.container/src/zope/container/i18n.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/i18n.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/i18n.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Customization of zope.i18n for the Zope application server
+
+$Id: i18n.py 73547 2007-03-25 09:03:04Z dobe $
+"""
+__docformat__ = 'restructuredtext'
+
+# import this as _ to create i18n messages in the zope domain
+from zope.i18nmessageid import MessageFactory
+ZopeMessageFactory = MessageFactory('zope')
Added: Sandbox/malthe/zope.container/src/zope/container/interfaces.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/interfaces.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/interfaces.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,335 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Container-related interfaces
+
+$Id: interfaces.py 85414 2008-04-16 02:10:55Z fdrake $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.deprecation import deprecated
+
+from zope.interface import Interface, Attribute, Invalid
+from zope.component.interfaces import IView, IObjectEvent
+from zope.interface.common.mapping import IItemMapping
+from zope.interface.common.mapping import IReadMapping, IEnumerableMapping
+from zope.location.interfaces import ILocation
+from zope.lifecycleevent.interfaces import IObjectModifiedEvent
+
+class DuplicateIDError(KeyError):
+ pass
+
+
+class ContainerError(Exception):
+ """An error of a container with one of its components."""
+
+
+class InvalidContainerType(Invalid, TypeError):
+ """The type of a container is not valid."""
+
+
+class InvalidItemType(Invalid, TypeError):
+ """The type of an item is not valid."""
+
+
+class InvalidType(Invalid, TypeError):
+ """The type of an object is not valid."""
+
+
+class IContained(ILocation):
+ """Objects contained in containers."""
+
+
+class IItemContainer(IItemMapping):
+ """Minimal readable container."""
+
+
+class ISimpleReadContainer(IItemContainer, IReadMapping):
+ """Readable content containers."""
+
+
+class IReadContainer(ISimpleReadContainer, IEnumerableMapping):
+ """Readable containers that can be enumerated."""
+
+
+class IWriteContainer(Interface):
+ """An interface for the write aspects of a container."""
+
+ def __setitem__(name, object):
+ """Add the given `object` to the container under the given name.
+
+ Raises a ``TypeError`` if the key is not a unicode or ascii string.
+ Raises a ``ValueError`` if key is empty.
+
+ The container might choose to add a different object than the
+ one passed to this method.
+
+ If the object doesn't implement `IContained`, then one of two
+ things must be done:
+
+ 1. If the object implements `ILocation`, then the `IContained`
+ interface must be declared for the object.
+
+ 2. Otherwise, a `ContainedProxy` is created for the object and
+ stored.
+
+ The object's `__parent__` and `__name__` attributes are set to the
+ container and the given name.
+
+ If the old parent was ``None``, then an `IObjectAddedEvent` is
+ generated, otherwise, an `IObjectMovedEvent` is generated. An
+ `IContainerModifiedEvent` is generated for the container.
+
+ If the object replaces another object, then the old object is
+ deleted before the new object is added, unless the container
+ vetos the replacement by raising an exception.
+
+ If the object's `__parent__` and `__name__` were already set to
+ the container and the name, then no events are generated and
+ no hooks. This allows advanced clients to take over event
+ generation.
+
+ """
+
+ def __delitem__(name):
+ """Delete the named object from the container.
+
+ Raises a ``KeyError`` if the object is not found.
+
+ If the deleted object's `__parent__` and `__name__` match the
+ container and given name, then an `IObjectRemovedEvent` is
+ generated and the attributes are set to ``None``. If the object
+ can be adapted to `IObjectMovedEvent`, then the adapter's
+ `moveNotify` method is called with the event.
+
+ Unless the object's `__parent__` and `__name__` attributes were
+ initially ``None``, generate an `IContainerModifiedEvent` for the
+ container.
+
+ If the object's `__parent__` and `__name__` were already set to
+ ``None``, then no events are generated. This allows advanced
+ clients to take over event generation.
+
+ """
+
+
+class IItemWriteContainer(IWriteContainer, IItemContainer):
+ """A write container that also supports minimal reads."""
+
+
+class IContainer(IReadContainer, IWriteContainer):
+ """Readable and writable content container."""
+
+
+class IBTreeContainer(IContainer):
+ """Container that supports BTree semantics for some methods."""
+
+ def items(key=None):
+ """Return an iterator over the key-value pairs in the container.
+
+ If ``None`` is passed as `key`, this method behaves as if no argument
+ were passed; exactly as required for ``IContainer.items()``.
+
+ If `key` is in the container, the first item provided by the iterator
+ will correspond to that key. Otherwise, the first item will be for
+ the key that would come next if `key` were in the container.
+
+ """
+
+ def keys(key=None):
+ """Return an iterator over the keys in the container.
+
+ If ``None`` is passed as `key`, this method behaves as if no argument
+ were passed; exactly as required for ``IContainer.keys()``.
+
+ If `key` is in the container, the first key provided by the iterator
+ will be that key. Otherwise, the first key will be the one that would
+ come next if `key` were in the container.
+
+ """
+
+ def values(key=None):
+ """Return an iterator over the values in the container.
+
+ If ``None`` is passed as `key`, this method behaves as if no argument
+ were passed; exactly as required for ``IContainer.values()``.
+
+ If `key` is in the container, the first value provided by the iterator
+ will correspond to that key. Otherwise, the first value will be for
+ the key that would come next if `key` were in the container.
+
+ """
+
+
+class IOrderedContainer(IContainer):
+ """Containers whose contents are maintained in order."""
+
+ def updateOrder(order):
+ """Revise the order of keys, replacing the current ordering.
+
+ order is a list or a tuple containing the set of existing keys in
+ the new order. `order` must contain ``len(keys())`` items and cannot
+ contain duplicate keys.
+
+ Raises ``TypeError`` if order is not a tuple or a list.
+
+ Raises ``ValueError`` if order contains an invalid set of keys.
+ """
+
+
+class IContainerNamesContainer(IContainer):
+ """Containers that always choose names for their items."""
+
+
+##############################################################################
+# Moving Objects
+
+class IObjectMovedEvent(IObjectEvent):
+ """An object has been moved."""
+
+ oldParent = Attribute("The old location parent for the object.")
+ oldName = Attribute("The old location name for the object.")
+ newParent = Attribute("The new location parent for the object.")
+ newName = Attribute("The new location name for the object.")
+
+
+##############################################################################
+# Adding objects
+
+class UnaddableError(ContainerError):
+ """An object cannot be added to a container."""
+
+ def __init__(self, container, obj, message=""):
+ self.container = container
+ self.obj = obj
+ self.message = message and ": %s" % message
+
+ def __str__(self):
+ return ("%(obj)s cannot be added "
+ "to %(container)s%(message)s" % self.__dict__)
+
+
+class IObjectAddedEvent(IObjectMovedEvent):
+ """An object has been added to a container."""
+
+
+class IAdding(IView):
+
+ def add(content):
+ """Add content object to container.
+
+ Add using the name in `contentName`. Returns the added object
+ in the context of its container.
+
+ If `contentName` is already used in container, raises
+ ``DuplicateIDError``.
+ """
+
+ contentName = Attribute(
+ """The content name, as usually set by the Adder traverser.
+
+ If the content name hasn't been defined yet, returns ``None``.
+
+ Some creation views might use this to optionally display the
+ name on forms.
+ """
+ )
+
+ def nextURL():
+ """Return the URL that the creation view should redirect to.
+
+ This is called by the creation view after calling add.
+
+ It is the adder's responsibility, not the creation view's to
+ decide what page to display after content is added.
+ """
+
+ def nameAllowed():
+ """Return whether names can be input by the user."""
+
+ def addingInfo():
+ """Return add menu data as a sequence of mappings.
+
+ Each mapping contains 'action', 'title', and possibly other keys.
+
+ The result is sorted by title.
+ """
+
+ def isSingleMenuItem():
+ """Return whether there is single menu item or not."""
+
+ def hasCustomAddView():
+ "This should be called only if there is `singleMenuItem` else return 0"
+
+
+class INameChooser(Interface):
+
+ def checkName(name, object):
+ """Check whether an object name is valid.
+
+ Raises a user error if the name is not valid.
+ """
+
+ def chooseName(name, object):
+ """Choose a unique valid name for the object
+
+ The given name and object may be taken into account when
+ choosing the name.
+ """
+
+
+##############################################################################
+# Removing objects
+
+
+class IObjectRemovedEvent(IObjectMovedEvent):
+ """An object has been removed from a container."""
+
+
+##############################################################################
+# Modifying containers
+
+
+class IContainerModifiedEvent(IObjectModifiedEvent):
+ """The container has been modified.
+
+ This event is specific to "containerness" modifications, which means
+ addition, removal or reordering of sub-objects.
+ """
+
+
+##############################################################################
+# Finding objects
+
+class IFind(Interface):
+ """
+ Find support for containers.
+ """
+
+ def find(id_filters=None, object_filters=None):
+ """Find object that matches all filters in all sub-objects.
+
+ This container itself is not included.
+ """
+
+
+class IObjectFindFilter(Interface):
+
+ def matches(object):
+ """Return True if the object matches the filter criteria."""
+
+
+class IIdFindFilter(Interface):
+
+ def matches(id):
+ """Return True if the id matches the filter criteria."""
Added: Sandbox/malthe/zope.container/src/zope/container/property.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/property.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/property.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,106 @@
+##############################################################################
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+##############################################################################
+"""Cached properties
+
+See the CachedProperty class.
+
+$Id: property.py 75652 2007-05-09 13:11:30Z zagy $
+"""
+
+ncaches = 0l
+
+
+class CachedProperty(object):
+ """Cached Properties.
+ """
+
+ def __init__(self, func, *names):
+ global ncaches
+ ncaches += 1
+ self.data = (func, names,
+ "_v_cached_property_key_%s" % ncaches,
+ "_v_cached_property_value_%s" % ncaches)
+
+ def __get__(self, inst, class_):
+ if inst is None:
+ return self
+
+ func, names, key_name, value_name = self.data
+
+ key = names and [getattr(inst, name) for name in names]
+ value = getattr(inst, value_name, self)
+
+ if value is not self:
+ # We have a cached value
+ if key == getattr(inst, key_name, self):
+ # Cache is still good!
+ return value
+
+ # We need to compute and cache the value
+
+ value = func(inst)
+ setattr(inst, key_name, key)
+ setattr(inst, value_name, value)
+
+ return value
+
+
+class Lazy(object):
+ """Lazy Attributes.
+ """
+
+ def __init__(self, func, name=None):
+ if name is None:
+ name = func.__name__
+ self.data = (func, name)
+
+ def __get__(self, inst, class_):
+ if inst is None:
+ return self
+
+ func, name = self.data
+ value = func(inst)
+ inst.__dict__[name] = value
+
+ return value
+
+
+class readproperty(object):
+
+ def __init__(self, func):
+ self.func = func
+
+ def __get__(self, inst, class_):
+ if inst is None:
+ return self
+
+ func = self.func
+ return func(inst)
+
+
+class cachedIn(object):
+ """Cached property with given cache attribute."""
+
+ def __init__(self, attribute_name):
+ self.attribute_name = attribute_name
+
+ def __call__(self, func):
+
+ def get(instance):
+ try:
+ value = getattr(instance, self.attribute_name)
+ except AttributeError:
+ value = func(instance)
+ setattr(instance, self.attribute_name, value)
+ return value
+
+ return property(get)
Added: Sandbox/malthe/zope.container/src/zope/container/property.txt
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/property.txt (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/property.txt 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,208 @@
+Cached Properties
+=================
+
+Cached properties are computed properties that cache their computed
+values. They take into account instance attributes that they depend
+on, so when the instance attributes change, the properties will change
+the values they return.
+
+Cached properties cache their data in _v_ attributes, so they are
+also useful for managing the computation of volatile attributes for
+persistent objects. Let's look at an example::
+
+ >>> from zope.container import property
+ >>> import math
+
+ >>> class Point:
+ ...
+ ... def __init__(self, x, y):
+ ... self.x, self.y = x, y
+ ...
+ ... def radius(self):
+ ... print 'computing radius'
+ ... return math.sqrt(self.x**2 + self.y**2)
+ ... radius = property.CachedProperty(radius, 'x', 'y')
+
+ >>> point = Point(1.0, 2.0)
+
+If we ask for the radius the first time::
+
+ >>> '%.2f' % point.radius
+ computing radius
+ '2.24'
+
+We see that the radius function is called, but if we ask for it again::
+
+ >>> '%.2f' % point.radius
+ '2.24'
+
+The function isn't called. If we change one of the attribute the
+radius depends on, it will be recomputed::
+
+ >>> point.x = 2.0
+ >>> '%.2f' % point.radius
+ computing radius
+ '2.83'
+
+But changing other attributes doesn't cause recomputation::
+
+ >>> point.q = 1
+ >>> '%.2f' % point.radius
+ '2.83'
+
+Note that we don't have any non-volitile attributes added::
+
+ >>> names = [name for name in point.__dict__ if not name.startswith('_v_')]
+ >>> names.sort()
+ >>> names
+ ['q', 'x', 'y']
+
+
+Lazy Computed Attributes
+------------------------
+
+The `property` module provides another descriptor that supports a
+slightly different caching model: lazy attributes. Like cached
+proprties, they are computed the first time they are used. however,
+they aren't stored in volatile attributes and they aren't
+automatically updated when other attributes change. Furthermore, the
+store their data using their attribute name, thus overriding
+themselves. This provides much faster attribute access after the
+attribute has been computed. Let's look at the previous example using
+lazy attributes::
+
+ >>> class Point:
+ ...
+ ... def __init__(self, x, y):
+ ... self.x, self.y = x, y
+ ...
+ ... def radius(self):
+ ... print 'computing radius'
+ ... return math.sqrt(self.x**2 + self.y**2)
+ ... radius = property.Lazy(radius)
+
+ >>> point = Point(1.0, 2.0)
+
+If we ask for the radius the first time::
+
+ >>> '%.2f' % point.radius
+ computing radius
+ '2.24'
+
+We see that the radius function is called, but if we ask for it again::
+
+ >>> '%.2f' % point.radius
+ '2.24'
+
+The function isn't called. If we change one of the attribute the
+radius depends on, it still isn't called::
+
+ >>> point.x = 2.0
+ >>> '%.2f' % point.radius
+ '2.24'
+
+If we want the radius to be recomputed, we have to manually delete it::
+
+ >>> del point.radius
+
+ >>> point.x = 2.0
+ >>> '%.2f' % point.radius
+ computing radius
+ '2.83'
+
+Note that the radius is stored in the instance dictionary::
+
+ >>> '%.2f' % point.__dict__['radius']
+ '2.83'
+
+The lazy attribute needs to know the attribute name. It normally
+deduces the attribute name from the name of the function passed. If we
+want to use a different name, we need to pass it::
+
+ >>> def d(point):
+ ... print 'computing diameter'
+ ... return 2*point.radius
+
+ >>> Point.diameter = property.Lazy(d, 'diameter')
+ >>> '%.2f' % point.diameter
+ computing diameter
+ '5.66'
+
+
+readproperties
+==============
+
+readproperties are like lazy computed attributes except that the
+attribute isn't set by the property::
+
+
+ >>> class Point:
+ ...
+ ... def __init__(self, x, y):
+ ... self.x, self.y = x, y
+ ...
+ ... def radius(self):
+ ... print 'computing radius'
+ ... return math.sqrt(self.x**2 + self.y**2)
+ ... radius = property.readproperty(radius)
+
+ >>> point = Point(1.0, 2.0)
+
+ >>> '%.2f' % point.radius
+ computing radius
+ '2.24'
+
+ >>> '%.2f' % point.radius
+ computing radius
+ '2.24'
+
+But you *can* replace the property by setting a value. This is the major
+difference to the builtin `property`::
+
+ >>> point.radius = 5
+ >>> point.radius
+ 5
+
+
+cachedIn
+========
+
+The `cachedIn` property allows to specify the attribute where to store the
+computed value::
+
+ >>> class Point:
+ ...
+ ... def __init__(self, x, y):
+ ... self.x, self.y = x, y
+ ...
+ ... @property.cachedIn('_radius_attribute')
+ ... def radius(self):
+ ... print 'computing radius'
+ ... return math.sqrt(self.x**2 + self.y**2)
+
+ >>> point = Point(1.0, 2.0)
+
+ >>> '%.2f' % point.radius
+ computing radius
+ '2.24'
+
+ >>> '%.2f' % point.radius
+ '2.24'
+
+The radius is cached in the attribute with the given name, `_radius_attribute`
+in this case::
+
+ >>> '%.2f' % point._radius_attribute
+ '2.24'
+
+When the attribute is removed the radius is re-calculated once. This allows
+invalidation::
+
+ >>> del point._radius_attribute
+
+ >>> '%.2f' % point.radius
+ computing radius
+ '2.24'
+
+ >>> '%.2f' % point.radius
+ '2.24'
Added: Sandbox/malthe/zope.container/src/zope/container/size.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/size.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/size.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,41 @@
+
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Adapters that give the size of an object.
+
+$Id: size.py 73547 2007-03-25 09:03:04Z dobe $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.container.i18n import ZopeMessageFactory as _
+from zope.size.interfaces import ISized
+from zope.interface import implements
+
+class ContainerSized(object):
+
+ implements(ISized)
+
+ def __init__(self, container):
+ self._container = container
+
+ def sizeForSorting(self):
+ """See `ISized`"""
+ return ('item', len(self._container))
+
+ def sizeForDisplay(self):
+ """See `ISized`"""
+ num_items = len(self._container)
+ if num_items == 1:
+ return _('1 item')
+ return _('${items} items', mapping={'items': str(num_items)})
Added: Sandbox/malthe/zope.container/src/zope/container/tests/__init__.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/tests/__init__.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/tests/__init__.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1 @@
+#
Added: Sandbox/malthe/zope.container/src/zope/container/tests/test_constraints.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/tests/test_constraints.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/tests/test_constraints.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,34 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Container constraint tests
+
+$Id: test_constraints.py 40495 2005-12-02 17:51:22Z efge $
+"""
+import unittest
+from zope.testing import doctest, module
+
+def setUp(test):
+ module.setUp(test, 'zope.container.constraints_txt')
+
+def tearDown(test):
+ module.tearDown(test, 'zope.container.constraints_txt')
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocTestSuite('zope.container.constraints'),
+ doctest.DocFileSuite('../constraints.txt',
+ setUp=setUp, tearDown=tearDown),
+ ))
+
+if __name__ == '__main__': unittest.main()
Added: Sandbox/malthe/zope.container/src/zope/container/tests/test_find.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/tests/test_find.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/tests/test_find.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,174 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Find functionality tests
+
+$Id: test_find.py 68442 2006-06-01 12:54:41Z mj $
+"""
+from unittest import TestCase, main, makeSuite
+from zope.container.interfaces import IReadContainer
+from zope.container.interfaces import IObjectFindFilter
+from zope.container.find import FindAdapter, SimpleIdFindFilter
+from zope.container.find import SimpleInterfacesFindFilter
+from zope.interface import implements, Interface, directlyProvides
+
+class FakeContainer(object):
+ implements(IReadContainer)
+
+ def __init__(self, id, objects):
+ self._id = id
+ self._objects = objects
+
+ def keys(self):
+ return [object._id for object in self._objects]
+
+ def values(self):
+ return self._objects
+
+ def items(self):
+ return [(object._id, object) for object in self._objects]
+
+ def __getitem__(self, id):
+ for object in self._objects:
+ if object._id == id:
+ return object
+ raise KeyError("Could not find %s" % id)
+
+ def get(self, id, default=None):
+ for object in self._objects:
+ if object._id == id:
+ return object
+
+ return default
+
+ def __contains__(self, id):
+ for object in self._objects:
+ if object.id == id:
+ return True
+ return False
+
+ def __len__(self):
+ return len(self._objects)
+
+class FakeInterfaceFoo(Interface):
+ """Test interface Foo"""
+
+class FakeInterfaceBar(Interface):
+ """Test interface Bar"""
+
+class FakeInterfaceSpam(Interface):
+ """Test interface Spam"""
+
+class TestObjectFindFilter(object):
+ implements(IObjectFindFilter)
+
+ def __init__(self, count):
+ self._count = count
+
+ def matches(self, object):
+ if IReadContainer.providedBy(object):
+ return len(object) == self._count
+ else:
+ return False
+
+class Test(TestCase):
+ def test_idFind(self):
+ alpha = FakeContainer('alpha', [])
+ delta = FakeContainer('delta', [])
+ beta = FakeContainer('beta', [delta])
+ gamma = FakeContainer('gamma', [])
+ tree = FakeContainer(
+ 'tree',
+ [alpha, beta, gamma])
+ find = FindAdapter(tree)
+ # some simple searches
+ result = find.find([SimpleIdFindFilter(['beta'])])
+ self.assertEquals([beta], result)
+ result = find.find([SimpleIdFindFilter(['gamma'])])
+ self.assertEquals([gamma], result)
+ result = find.find([SimpleIdFindFilter(['delta'])])
+ self.assertEquals([delta], result)
+ # we should not find the container we search on
+ result = find.find([SimpleIdFindFilter(['tree'])])
+ self.assertEquals([], result)
+ # search for multiple ids
+ result = find.find([SimpleIdFindFilter(['alpha', 'beta'])])
+ self.assertEquals([alpha, beta], result)
+ result = find.find([SimpleIdFindFilter(['beta', 'delta'])])
+ self.assertEquals([beta, delta], result)
+ # search without any filters, find everything
+ result = find.find([])
+ self.assertEquals([alpha, beta, delta, gamma], result)
+ # search for something that doesn't exist
+ result = find.find([SimpleIdFindFilter(['foo'])])
+ self.assertEquals([], result)
+ # find for something that has two ids at the same time,
+ # can't ever be the case
+ result = find.find([SimpleIdFindFilter(['alpha']),
+ SimpleIdFindFilter(['beta'])])
+ self.assertEquals([], result)
+
+ def test_objectFind(self):
+ alpha = FakeContainer('alpha', [])
+ delta = FakeContainer('delta', [])
+ beta = FakeContainer('beta', [delta])
+ gamma = FakeContainer('gamma', [])
+ tree = FakeContainer(
+ 'tree',
+ [alpha, beta, gamma])
+ find = FindAdapter(tree)
+ result = find.find(object_filters=[TestObjectFindFilter(0)])
+ self.assertEquals([alpha, delta, gamma], result)
+ result = find.find(object_filters=[TestObjectFindFilter(1)])
+ self.assertEquals([beta], result)
+ result = find.find(object_filters=[TestObjectFindFilter(2)])
+ self.assertEquals([], result)
+
+ def test_combinedFind(self):
+ alpha = FakeContainer('alpha', [])
+ delta = FakeContainer('delta', [])
+ beta = FakeContainer('beta', [delta])
+ gamma = FakeContainer('gamma', [])
+ tree = FakeContainer(
+ 'tree',
+ [alpha, beta, gamma])
+ find = FindAdapter(tree)
+ result = find.find(id_filters=[SimpleIdFindFilter(['alpha'])],
+ object_filters=[TestObjectFindFilter(0)])
+ self.assertEquals([alpha], result)
+
+ result = find.find(id_filters=[SimpleIdFindFilter(['alpha'])],
+ object_filters=[TestObjectFindFilter(1)])
+ self.assertEquals([], result)
+
+ def test_interfaceFind(self):
+ alpha = FakeContainer('alpha', [])
+ directlyProvides(alpha, FakeInterfaceBar)
+ delta = FakeContainer('delta', [])
+ directlyProvides(delta, FakeInterfaceFoo)
+ beta = FakeContainer('beta', [delta])
+ directlyProvides(beta, FakeInterfaceSpam)
+ gamma = FakeContainer('gamma', [])
+ tree = FakeContainer(
+ 'tree',
+ [alpha, beta, gamma])
+ find = FindAdapter(tree)
+ result = find.find(object_filters=[
+ SimpleInterfacesFindFilter(FakeInterfaceFoo, FakeInterfaceSpam)])
+ self.assertEqual([beta, delta], result)
+
+def test_suite():
+ return makeSuite(Test)
+
+if __name__=='__main__':
+ main(defaultTest='test_suite')
Added: Sandbox/malthe/zope.container/src/zope/container/tests/test_property.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/tests/test_property.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/tests/test_property.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,33 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Container constraint tests
+
+$Id: test_constraints.py 40495 2005-12-02 17:51:22Z efge $
+"""
+import unittest
+from zope.testing import doctest, module
+
+def setUp(test):
+ module.setUp(test, 'zope.container.property_txt')
+
+def tearDown(test):
+ module.tearDown(test, 'zope.container.property_txt')
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite('../property.txt',
+ setUp=setUp, tearDown=tearDown),
+ ))
+
+if __name__ == '__main__': unittest.main()
Added: Sandbox/malthe/zope.container/src/zope/container/tests/test_size.py
===================================================================
--- Sandbox/malthe/zope.container/src/zope/container/tests/test_size.py (rev 0)
+++ Sandbox/malthe/zope.container/src/zope/container/tests/test_size.py 2008-04-22 12:01:20 UTC (rev 85588)
@@ -0,0 +1,70 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test container ISized adapter.
+
+$Id: test_size.py 67630 2006-04-27 00:54:03Z jim $
+"""
+import unittest
+
+from zope.interface import implements
+from zope.size.interfaces import ISized
+from zope.container.interfaces import IContainer
+
+class DummyContainer(object):
+
+ implements(IContainer)
+
+ def __init__(self, numitems):
+ self._numitems = numitems
+
+ def __len__(self):
+ return self._numitems
+
+
+class Test(unittest.TestCase):
+
+ def testImplementsISized(self):
+ from zope.container.size import ContainerSized
+ sized = ContainerSized(DummyContainer(23))
+ self.assert_(ISized.providedBy(sized))
+
+ def testEmptyContainer(self):
+ from zope.container.size import ContainerSized
+ obj = DummyContainer(0)
+ sized = ContainerSized(obj)
+ self.assertEqual(sized.sizeForSorting(), ('item', 0))
+ self.assertEqual(sized.sizeForDisplay(), u'${items} items')
+ self.assertEqual(sized.sizeForDisplay().mapping['items'], '0')
+
+ def testOneItem(self):
+ from zope.container.size import ContainerSized
+ obj = DummyContainer(1)
+ sized = ContainerSized(obj)
+ self.assertEqual(sized.sizeForSorting(), ('item', 1))
+ self.assertEqual(sized.sizeForDisplay(), u'1 item')
+
+ def testSeveralItems(self):
+ from zope.container.size import ContainerSized
+ obj = DummyContainer(2)
+ sized = ContainerSized(obj)
+ self.assertEqual(sized.sizeForSorting(), ('item', 2))
+ self.assertEqual(sized.sizeForDisplay(), u'${items} items')
+ self.assertEqual(sized.sizeForDisplay().mapping['items'], '2')
+
+def test_suite():
+ loader = unittest.TestLoader()
+ return loader.loadTestsFromTestCase(Test)
+
+if __name__=='__main__':
+ unittest.TextTestRunner().run(test_suite())
More information about the Checkins
mailing list