简体   繁体   中英

How to use zope.interface.directlyProvides with instances of build in types (dict, string, …)

I have a bunch of dictionaries, which I would like to annotate with type information, to be able to later get adapters for them. In the following example, the failing case is what I would like to do and the other case shows a working version. Is it somehow possible to get the first version working without introducing the extra object? The code which creates the dicts would not be easy to change, so I'm looking for the most simple and non intrusive way to add some type infos.

from zope.interface import Interface, implements, directlyProvides               
from zope.interface.registry import Components                                   

registry = Components()                                                          

class IA(Interface):                                                             
    pass                                                                         

#   this one fails                                                               

data = {}                                                                        
directlyProvides(data, IA)                                                       

#   this way it works                                                            

class X(dict):                                                                   
    pass                                                                         

data = X()                                                                       
directlyProvides(data, IA)

zope.interface relies on the ability to set some magic attributes on various objects and classes. This is what happens

>>> class Dict(dict):
...     pass
...
>>> getattr(Dict, '__provides__', None)

Nothing. Now try to do what you did.

>>> from zope.interface import Interface, implements, directlyProvides
>>> class IA(Interface):
...     pass
... 
>>> data = Dict()
>>> directlyProvides(data, IA)

Try to grab that __provides__ again

>>> getattr(Dict, '__provides__', None)
<zope.interface.declarations.ClassProvides object at 0x7f133cab48d0>

Reason this fails on the builtin types is because you can't actually set attributes on any of them (through standard means).

>>> setattr(dict, '__provides__', None)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'dict'

Hence if you want to assign some marker interface on some instances of standard types, it will fail. Just subclass them as is, or create real objects which is more what the Zope Component Architecture was more designed to be used for.

You cannot annotate Python built-in types with interface information; you simply cannot add the required attributes.

You can register adapters for the type (so no interfaces implemented):

>>> from zope.interface.registry import Components                                   
>>> from zope.interface import Interface
>>> registry = Components()                                                          
>>> class IA(Interface):                                                             
...     pass                                                                         
... 
>>> data = {}
>>> registry.registerAdapter(lambda o: 'adapter from dict to IA', [dict], IA)
>>> registry.queryAdapter(data, IA)
'adapter from dict to IA'

You cannot do this for instances, unfortunately:

>>> registry.registerAdapter(lambda o: 'adapter from data to IA', [data], IA)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mj/Development/venvs/stackoverflow-2.7/lib/python2.7/site-packages/zope/interface/registry.py", line 186, in registerAdapter
    required = _getAdapterRequired(factory, required)
  File "/Users/mj/Development/venvs/stackoverflow-2.7/lib/python2.7/site-packages/zope/interface/registry.py", line 432, in _getAdapterRequired
    raise TypeError("Required specification must be a "
TypeError: Required specification must be a specification or class.

This means that if you really must adapt specific dictionaries, then your options are limited. Using a subclass of dict is one work-around.

However, you must really ask if adapting dictionaries is the best approach for your problem here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM