diff --git a/constraint.py b/constraint.py index 1749194..4ce4e1e 100644 --- a/constraint.py +++ b/constraint.py @@ -43,13 +43,22 @@ class ResourceConstraint(object): def isResourceAllowed(self, resource, task=None): if self.referenceType == 'parent': - for r in self.referenceValues: - m = getattr(r, self.referenceKey) + for ref in self.referenceValues: + m = getattr(ref, self.referenceKey) if resource in m(): return True return False elif self.referenceType == 'explicit': return resource in self.referenceValues + elif self.referenceType == 'method': + m = getattr(resource, self.referenceKey, None) + if m: + result = m() + if result is None: # method needs additional task parameter + result = m(task=task) + return result + else: + return False else: return False @@ -57,12 +66,15 @@ class ResourceConstraint(object): def getAllowedResources(self, candidates=None, task=None): if self.referenceType == 'parent': result = [] - for r in self.referenceValues: - m = getattr(r, self.referenceKey) + for ref in self.referenceValues: + m = getattr(ref, self.referenceKey) result.extend(m()) return tuple(result) elif self.referenceType == 'explicit': return tuple(self.referenceValues) else: - return () + if candidates is None: + return None + else: + return tuple([ r for r in candidates if self.isResourceAllowed(r, task) ]) diff --git a/interfaces.py b/interfaces.py index 0bd5a67..56a89d9 100644 --- a/interfaces.py +++ b/interfaces.py @@ -41,24 +41,27 @@ class IResourceConstraint(Interface): constraintType = TextLine( title=u'Constraint Type', - description=u'Type of the constraint: select, require, disallow', + description=u'Type of the constraint: select, require, allow, disallow', default=u'select', required=True) referenceType = TextLine( title=u'Reference Type', description=u'Type of reference to the resource attribute to check: ' - 'explicit, parent, type, attribute, property, method', + 'explicit, parent, type, method.', default=u'explicit', required=True) referenceKey = TextLine( title=u'Reference Key', - description=u'Key for referencing the resource attribute') + description=u'Key for referencing the resource attribute, ' + 'e.g. method or type name') referenceValues = List( title=u'Reference Values', - description=u'Attribute values to check for; may be any kind of object', + description=u'Values to check for; usually a list of references to ' + 'the objects to be selected (referenceType=explicit) ' + 'or the parent objects (referenceType=parent)', value_type=Object(Interface, title=u'Value'), unique=True) diff --git a/tests/test_constraint.py b/tests/test_constraint.py index f06841e..87e64a3 100755 --- a/tests/test_constraint.py +++ b/tests/test_constraint.py @@ -53,6 +53,28 @@ class Test(unittest.TestCase): rc1.referenceValues = ([t2]) self.assertEqual(True, rc1.isResourceAllowed(r1)) self.assertEqual((r1,), rc1.getAllowedResources()) + rc1.referenceType = 'parent' + rc1.referenceKey = 'getAllocatedResources' + + def testRequireMethod(self): + rc1 = self.rc1 + rc1.constraintType = 'require' + rc1.referenceType = 'method' + rc1.referenceKey = 'isAllowedForTesting' + r1 = self.r1 + t1 = self.t1 + self.failIf(rc1.isResourceAllowed(r1)) + Resource.isAllowedForTesting = lambda self: True + self.failUnless(rc1.isResourceAllowed(r1)) + Resource.isAllowedForTesting = lambda self: False + self.failIf(rc1.isResourceAllowed(r1)) + def method(self, task='dummy'): + if task == 'dummy': return None # need task keyword parameter + return task == t1 + Resource.isAllowedForTesting = method + self.failUnless(rc1.isResourceAllowed(r1, t1)) + self.failIf(rc1.isResourceAllowed(r1, Task())) + def test_suite():