Skip to content

Commit

Permalink
Merged trunk 25413:
Browse files Browse the repository at this point in the history
Added a get method to security declarations for looking up attribute
specifications using a cache.  Modified container constraint machinery
to use this.  This sped up rendering of a sample content page about 15%.
  • Loading branch information
Jim Fulton committed Jul 2, 2004
1 parent 9e78aa5 commit 4f60c1b
Showing 1 changed file with 333 additions and 0 deletions.
333 changes: 333 additions & 0 deletions constraints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
##############################################################################
#
# 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.app.container.interfaces import IContainer
>>> class C1:
... 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:
... 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:
... 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:
... 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. Ti illistrate this, we'll make preNoZ it's
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$
"""

import zope.interface
from zope.interface import providedBy
from zope.app.container.interfaces import InvalidItemType, InvalidContainerType
from zope.app.i18n import ZopeMessageIDFactory as _
from zope.app.container.interfaces import IContainer

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 objet is invalid.
"""

def factory(container, name, factory):
"""Test whether objects provided by the factory are acceptable
Return a boolean value.
"""

class ItemTypePrecondition:
"""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:
... pass
>>> ob = Ob()
>>> class Factory:
... 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 __init__(self, *types):
self.types = types

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)


class ContainerTypesConstraint:
"""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:
... 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
"""

def __init__(self, *types):
self.types = types

def __call__(self, object):
for iface in self.types:
if iface.providedBy(object):
return True
else:
raise InvalidContainerType(object, self.types)

0 comments on commit 4f60c1b

Please sign in to comment.