Skip to content

4.4.1 breaks pickling of PersistentComponents that have registrations #85

Closed

Description

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions