[PEAK] Type and class adaptation
Radek Kanovsky
rk at dat.cz
Mon Nov 22 03:38:08 EST 2004
On Fri, Nov 19, 2004 at 02:15:30PM -0500, Phillip J. Eby wrote:
> Essentially, the problem with your implementation as far as I can tell, is
> that it doesn't at all consider the possibility that the object is not a
> class. I would recommend that you use 'None' as the key for that
> situation. So, the code would look something like this:
>
> def dispatch_by_subclass (klass, table):
> if isinstance(klass,ClassTypes):
> while True:
> if klass in table:
> return table[klass]
> try:
> klass, = klass.__bases__
> except ValueError:
> if klass.__bases__:
> return table.reseed(klass)
> else:
> break
> if None in table:
> return table[None]
Isn't 'None' key in the table everytime if it is returned by seeds() method?
Is the last 'if None in table' necessary?
> And, the 'ISSubclass.seeds()' method should also include 'None' in its
> seeds.
>
> Essentially, a dispatch function must never return 'None' unless there are
> no cases registered for that situation, and an ITest must create seeds for
> all the possibilities regarding that test. One of the possibilities of a
> subclass test is that the object being tested is not a class, so there
> needs to be a key for that condition. Also, there is the possibility that
> a class is not a subclass of any known test (i.e., it's a new classic
> class). In both of these cases, the 'None' key represents the default
> dispatch path, which is basically a path where none of the subclass-testing
> alternatives are applicable.
>
> This is different from dispatch_by_mro, because in Python everything is an
> *instance* of 'object' eventually, so 'InstanceType' and 'object' are the
> default keys. But, in this case, not everything is a *subclass* of
> 'object', so a different default is needed.
It is more clear to me now. Docstring for seeds() says that this method
should "Return iterable of known-good keys". From the existing code
in dispatching package I have got the picture that "good keys" are
extreme values in some sense (MIN/MAX for comparables, 'object' for
class/types hierarchy) plus important value itself. Nothing can be
subclass of 'None' so it hasn't seemed to be a good key :) On the other
hand, there are some classes that hasn't 'object' among their bases. So
I suppose that None has role of some marker or sentinel here - "bad key"
everytime. OK.
Radek
------------------------ implementation ---------------------------
from peak.core import *
from dispatch import ITest
from dispatch.functions import NullTest
from types import ClassType
ClassTypes = (ClassType, type)
__all__ = ['IsSubclass']
def dispatch_by_subclass (klass, table):
if isinstance(klass, ClassTypes) :
while True:
if klass in table:
return table[klass]
try:
klass, = klass.__bases__
except ValueError:
if klass.__bases__:
return table.reseed(klass)
else:
break
return table[None]
class IsSubclass (protocols.Adapter) :
protocols.advise(instancesProvide=[ITest])
dispatch_function = staticmethod(dispatch_by_subclass)
def seeds(self,table):
return [self.subject, None]
def __contains__(self,ob):
if isinstance(ob, ClassTypes):
return issubclass(ob, self.subject)
return False
def implies(self, otherTest):
return self.subject in ITest(otherTest) or otherTest is NullTest
def __repr__(self):
return "IsSubclass(%s)" % self.subject.__name__
def subscribe(self,listener): pass
def unsubscribe(self,listener): pass
def __eq__(self,other):
return type(self) is type(other) and self.subject is other.subject
def __ne__(self,other):
return not self.__eq__(other)
------------------------------ unittest ---------------------------------
from unittest import *
from peak.core import *
from wm4.utils.dispatching import IsSubclass
import types
[dispatch.generic()]
def typeDescriptor (arg) :
pass
[typeDescriptor.when("arg in IsSubclass(int)")]
def typeDescriptorForInt (arg) :
return "int"
[typeDescriptor.when("arg in IsSubclass(basestring)")]
def typeDescriptorForBasestring (arg) :
return "basestring"
class myint (int) : pass
[typeDescriptor.when("arg in IsSubclass(myint)")]
def typeDescriptorForMyInt (arg) :
return "myint"
class MyClass (object) : pass
class SubClass (MyClass) : pass
[typeDescriptor.when("arg in IsSubclass(MyClass)")]
def typeDescriptorForMyInt (arg) :
return "MyClass"
[typeDescriptor.when("arg in int")]
def typeDescriptorForIntInstance (arg) :
return "int instance"
class Classic : pass
class SubClassic (Classic) : pass
[typeDescriptor.when("arg in IsSubclass(Classic)")]
def typeDescriptorForClassic(arg) :
return "Classic"
class TestIsSubclass (TestCase) :
def test10_Result (self) :
self.assertEqual(typeDescriptor(int), 'int')
self.assertEqual(typeDescriptor(myint), 'myint')
self.assertEqual(typeDescriptor(str), 'basestring')
self.assertEqual(typeDescriptor(unicode), 'basestring')
self.assertEqual(typeDescriptor(MyClass), 'MyClass')
self.assertEqual(typeDescriptor(SubClass), 'MyClass')
self.assertEqual(typeDescriptor(Classic), 'Classic')
self.assertEqual(typeDescriptor(SubClassic), 'Classic')
self.assertEqual(typeDescriptor(444), 'int instance')
self.assertEqual(typeDescriptor(myint(444)), 'int instance')
def test12_MustFail (self) :
NAM = dispatch.NoApplicableMethods
self.assertRaises(NAM, typeDescriptor, 44.4)
self.assertRaises(NAM, typeDescriptor, '444')
self.assertRaises(NAM, typeDescriptor, float)
self.assertRaises(NAM, typeDescriptor, object)
self.assertRaises(NAM, typeDescriptor, None)
self.assertRaises(NAM, typeDescriptor, MyClass())
self.assertRaises(NAM, typeDescriptor, SubClass())
self.assertRaises(NAM, typeDescriptor, Classic())
self.assertRaises(NAM, typeDescriptor, SubClassic())
self.assertRaises(NAM, typeDescriptor, object)
self.assertRaises(NAM, typeDescriptor, object())
self.assertRaises(NAM, typeDescriptor, types.ClassType)
self.assertRaises(NAM, typeDescriptor, types.InstanceType)
def suite() :
return TestSuite((
makeSuite(TestIsSubclass),
))
if __name__ == '__main__':
main( defaultTest='suite' )
More information about the PEAK
mailing list