Description
openedon Jun 8, 2017
I figured out what I was worried about in regards to #84.
The changes in 4.4.1 break the tests for zc.catalog (but it should be very simple to reproduce outside of those tests). All versions of Python now fail with a variation on this error:
File "<doctest extentcatalog.rst[23]>", line 1, in <module>
root = makeRoot()
File "<doctest extentcatalog.rst[13]>", line 12, in makeRoot
transaction.commit()
...
File "//py27/lib/python2.7/site-
return self._dump(meta, obj.__getstate__())
File "//py27/lib/python2.7/site-packages/ZODB/serialize.py", line 428, in _dump
self._p.dump(state)
File "//.tox/py27/bin/../lib/python2.7/copy_reg.py", line 70, in _reduce_ex
raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle function objects
Or _pickle.PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
.
Coercing Python 3 to use the Python implementation of ZODB pickle gets us a better error message:
File "//py36/lib/python3.6/site-packages/zodbpickle/pickle_3.py", line 694, in save_global
(obj, module, name))
_pickle.PicklingError: Can't pickle <function _UtilityRegistrations.__init__.<locals>.<lambda> at 0x11073c598>: it's not found as zope.interface.registry.<lambda>
Debugging the error shows that state
is {'data': {'components': <PersistentComponents>}}
, and further debugging shows that we're trying to pickle PersistentComponents.__dict__
, leading to this error:
(Pdb) pp state
{'data': {'components': <PersistentComponents >}}
(Pdb) pp self._p.memo.copy()[id(state['data']['components'].__dict__)]
(7,
{'__bases__': (<BaseGlobalComponents base>,),
'__name__': '',
'_adapter_registrations': {},
'_handler_registrations': [],
'_subscription_registrations': [],
'_utility_registrations': {(<InterfaceClass zope.intid.interfaces.IIntIds>, ''): (<zope.intid.IntIds object at 0x10a9c42e8>,
'',
None)},
'_v_utility_registrations_cache': <zope.interface.registry._UtilityRegistrations object at 0x10a9d6ac8>,
'adapters': <zope.component.persistentregistry.PersistentAdapterRegistry object at 0x10a972eb8>,
'utilities': <zope.component.persistentregistry.PersistentAdapterRegistry object at 0x10a9c40b8>})
The makeRoot
function is very simple:
>>> def makeRoot():
... db = DB()
... conn = db.open()
... root = conn.root()
... site_manager = root['components'] = (
... zope.component.persistentregistry.PersistentComponents())
... site_manager.__bases__ = (zope.component.getGlobalSiteManager(),)
... site_manager.registerUtility(
... zope.intid.IntIds(family=btrees_family),
... provided=zope.intid.interfaces.IIntIds)
... setSiteManager(site_manager)
... transaction.commit()
... return root
Reverting any tested python to zope.interface 4.4.0 fixes the problem.
The problem is that PersistentComponents
isn't actually a Persistent
object; only its .adapters
and .utilities
are, so the magic that's supposed to elide _v
attributes never happens:
class PersistentComponents(Components):
def _init_registries(self):
self.adapters = PersistentAdapterRegistry()
self.utilities = PersistentAdapterRegistry()
Suggestions on how to tackle this?