[TransWarp] PyProtocols: adapting classes to instance

Ian Bicking ianb at colorstudy.com
Mon Sep 29 23:17:42 EDT 2003


On Monday, September 29, 2003, at 10:11 AM, Phillip J. Eby wrote:
> At 02:54 PM 9/28/03 -0500, Ian Bicking wrote:
>> I wasn't sure if this is the right place to post questions about 
>> PyProtocols...
>
> Go for it; I haven't seen a need yet to set up a separate mailing 
> list.  In fact, you're the first person to *ask* anything about 
> PyProtocols that wasn't already using PEAK.  :)

I think PyProtocols deserves a bit more advertisement, though what that 
would be I don't know for sure.  I wanted to use interfaces and 
adapters for a while, but everytime I looked at Zope3 or Twisted I'd 
look at some documentation, realize it was inaccurate and/or 
incomplete, look through some twisty code, then decide to work on 
something else.  Only when I stumbled on PyProtocols did I actually get 
anywhere, mostly because it has good documentation (thanks!) -- it's 
also nice that it's separately packaged.

>> Anyway, I want to adapt classes to instances of those classes.  So 
>> you could do something like:
>>
>> class MyValidator(Validator):
>>     protocols.advise(instancesProvide=[IValidator])
>>     ....
>>
>> adapt(MyValidator, IValidator)
>> # --> MyValidator()
>>
>>
>> This way classes can be used in place of instances in most cases (and 
>> obviously the classes are written with this in mind).  But I haven't 
>> been sure how to do this.
>
> I don't understand why you want to do this.  It seems to me it would 
> be simpler to use a singleton instance, instead of creating new ones 
> all over the place.  Perhaps you could explain your use case.

It's using objects like:

class Declarative(object):
     def __init__(self, **kw):
         for name, value in kw.items():
             setattr(self, name, value)
     def __call__(self, **kw):
         if not kw: return self
         newdict = self.__dict__.copy()
         newdict.update(kw)
         return self.__class__(**newdict)

If you can adapt classes, then these two expressions would be 
equivalent (assuming Regex subclasses Declarative):

validator = Regex(strip=True, regex=r'^[a-zA-Z]*$', 
ifInvalid='[Invalid]')

class validator(Regex):
     strip = True
     regex = r'^[a-zA-Z]*$'
     ifInvalid = '[Invalid]'


These would potentially be nested, class statements inside class 
statements, so I want to keep clutter down as much as possible.  I'd 
like to be able to use instances and classes interchangeably.  And in 
some cases, if there's no modification to any of the variables for the 
validator, you could use the validator without instantiating it (to 
save typing "()"), though that's just an incidental benefit.

>> So, my basic question (if you understand what I'm trying to do) -- 
>> how should I do it?  I might be all backwards, so I'm open to any 
>> ideas.
>> Add an __adapt__ method to IValidator?  Add a __conform__ method to 
>> ValidatorMeta?  I've tried different combinations, but to no avail.
>
> Another relevant excerpt from the manual:
>
> """class ProviderMixin
>
>     If you have a class with a __conform__ method for its instances, 
> but you also want the instances to support IOpenprovider (so that 
> adviseObject can be used on them), you may want to include this class 
> as one of your class' bases. The default adapters for IOpenprovider 
> can only adapt objects that do not already have a __conform__ method 
> of their own.
>
>     So, to support IOpenprovider with a custom __conform__ method, 
> subclass ProviderMixin, and have your __conform__ method invoke the 
> base __conform__ method as a default, using supermeta(). (E.g. return 
> supermeta(MyClass,self).__conform__(protocol).) See below for more on 
> the supermeta() function. """

I haven't really been able to understand what the Open* 
classes/interfaces really are, or what they mean in the overall system. 
  The names are very vague to me... but then, many of the names in 
PyProtocols feel vague to me.  Maybe a little glossary would help.  
It's hard, because everything in an interface system is very abstract, 
which I suppose is inevitable for an interface system.

> In practice, your situation isn't quite what's described here.  In 
> your case, you want to mix ProviderMixin into the metaclass, and you 
> shouldn't need to do anything special to the class-level __conform__.  
> The metaclass __conform__ should then be called whenever you adapt the 
> class itself, and the class __conform__ should be called whenever you 
> adapt an instance.
>
> So, probably your finished metaclasss will look like this:
>
> class ValidatorMeta(ProviderMixin, type):
>
>     protocols.advise(instancesProvide=protocols.IOpenImplementor)
>
>     def declareClassImplements(cls,protocol,adapter,depth):
>         if adapter is protocols.NO_ADAPTER_NEEDED:
>             protocols.declareAdapter(lambda o,p: o(), protocol, 
> forObjects=[cls])

Yes, that seems to work (with your other change).  I don't know why 
though... ;)  I'll reread the manual, though, and probably get it this 
time around.

Thanks,
   Ian




More information about the PEAK mailing list