[Zope-dev] Adding broken/missing support to zope.interface? (was: experimental.broken - Graceful handling of broken interfaces and components in the ZODB)
Leonardo Rochael Almeida
leorochael at gmail.com
Sun Apr 8 20:52:30 UTC 2012
Not ZCA/ZODB maintainer, but a big +1 from me.
I've also experienced the negative effects of missing code for
(marker) interfaces in persistent data.
Cheers,
Leo
On Sun, Apr 8, 2012 at 22:04, Ross Patterson <me at rpatterson.net> wrote:
> experimental.broken is working well for me. It greatly aided me in
> getting through a particularly nasty upgrade allowing me to cleanup the
> ZCA cruft left by poorly behaved add-ons. I'd like to proceed with
> adding the core of this to zope.interface and I need the
> developers/maintainers to weigh in.
>
> The first and most fundamental matter is changing interface pickles such
> that they can be unpickled into something that can fulfill the minimum
> interface contract and don't break the ZCA. To that end, I'd like to
> add the following to zope.interface.interface:
>
> ...
> try:
> from ZODB.broken import find_global
> from ZODB.broken import IBroken
> def find_interface(modulename, globalname,
> Broken=IBroken, type=InterfaceClass):
> """
> Find a global interface, returning a broken interface if it can't be found.
> """
> return find_global(modulename, globalname,
> Broken=IBroken, type=InterfaceClass)
> except ImportError:
> IBroken = Interface
> def find_interface(modulename, globalname,
> Broken=IBroken, type=InterfaceClass):
> """
> Find a global interface, raising ImportError if it can't be found.
> """
> # From pickle.Unpickler.find_class
> __import__(module)
> mod = sys.modules[module]
> klass = getattr(mod, name)
> return klass
> ...
> class InterfaceClass(Element, InterfaceBase, Specification):
> ...
> def __reduce__(self):
> if self is IBroken:
> return self.__name__
> return (find_interface, (modulename, globalname))
> ...
>
> With this change, previously existing interface pickles will be
> different from newly committed ones but both pickles would unpickle to
> the same object. Also, running zodbupdate would convert all pickles to
> the new format.
>
> Is this the correct approach? If not, how should it be changed or what
> might be the correct way? Is there a better way to put the broken
> object support in ZODB and still have the same interface pickles when
> using ZODB?
>
> This still leaves the problem of instance provides declarations and
> component registrations which contain previously existing interface
> pickles for missing interfaces. Without addressing these, previously
> existing instance pickles cannot be unpickled and all component registry
> operations fail. experimental.broken addresses these by adding handling
> for broken interfaces when provides declaration are unpickled (in the
> ProvidesClass.__init__ method) and when component registries are
> unpickled (in the __setstate__ method of
> persistentregistry.PersistentAdapterRegistry and
> persistentregistry.PersistentComponents). Again, with these patches,
> missing interfaces don't break instances or registries and allow running
> zodbupdate to fix all existing pickles. Where should this support live?
> Should I keep it in a separate package, maybe just rename
> experimental.broken to z3c.broken or somesuch? Should it be merged into
> zope.interface and zope.component?
>
> Will the developers/maintainers of zope.interface, zope.component and/or
> ZODB please weigh in on this and give me feedback towards getting this
> finished?
>
> Thanks!
> Ross
>
> Ross Patterson <me at rpatterson.net> writes:
>
>> I've done some more work on this and I've gotten the component
>> registrations fully working now with one exception that I'm having real
>> trouble with. I'd like some help with that, more below. I'm also a bit
>> more clear on what might be appropriate to bring back into
>> zope.interface and I'd like feedback on that.
>>
>> Currently interfaces are pickled as globals. Given their centrality in
>> any ZCA application, I think they should be pickled using a function:
>>
>> def __reduce__(self):
>> return (find_interface, (modulename, globalname))
>>
>> where find_interface, if ZODB.broken.find_global can be imported, in
>> turn calls:
>>
>> ZODB.broken.find_global(modulename, globalname,
>> Broken=IBroken, type=InterfaceClass)
>>
>> since find_global already has nicely abstracted graceful missing global
>> handling.
>>
>> If this were added to zope.interface, and changed ZODB objects with
>> marker interfaces or persistent registries where all the code for the
>> interfaces is still available will then be updated with pickles that
>> will gracefully handle missing interfaces in the future. Thus you could
>> use zodbupdate to make sure that the interfaces in your ZODB will fail
>> gracefully in the future. Do you agree/disagree that this belongs in
>> zope.interface?
>>
>> What this will not address are existing interfaces-as-globals pickles
>> where the original interface is now missing. That's where the other
>> patches in experimental.broken come in, they intercept the two contexts
>> where we can infer that a missing global should be an interface:
>> instance provides declarations and persistent component registries. By
>> hooking into the unpickling of those objects, we can replace broken
>> classes with broken interfaces as appropriate for those structures.
>> With these patches installed it should be possible to use applications
>> with missing interfaces and to use zodbupdate to make sure that even
>> *already missing* interfaces will fail gracefully both now and in the
>> future. I think these patches don't belong in zope.interface/component
>> and if they work I would likely move them to five.localsitemanager along
>> with a ZCML file that is not loaded by default. Does that sound right?
>>
>> Then one thing I haven't been able to get working is making it possible
>> to commit a changed persistent registry when it includes a component
>> registration for a non-persistent component whose class/type is
>> missing. This works just fine for a *persistent* component whose
>> class/type is missing but fails for a non-persistent component. The
>> error is:
>>
>> AttributeError: 'Bar' object has no attribute '__Broken_newargs__'
>>
>> More specifically, '__Broken_newargs__' is set by the Broken.__new__
>> method and I've confirmed that this isn't being called by instrumenting
>> __new__, __init__, and __setstate__. Only __setstate__ is called when
>> unpickling the non-persistent broken component not __new__ as should
>> be. Below is the full traceback. I've also left the package repo in
>> the broken state if you want to examine it, the egg checkout is also a
>> buildout:
>>
>> https://github.com/rpatterson/experimental.broken/commit/a4b648dc78e53c7303ea2d417d2d7c5e7fea24ac
>>
>> Any help with this last issue would be appreciated.
>>
>> Ross
>>
>> Ross Patterson <me at rpatterson.net> writes:
>>
>>> Please take a look at experimental.broken:
>>>
>>> https://github.com/rpatterson/experimental.broken
>>> http://pypi.python.org/pypi/experimental.broken
>>>
>>> The handling of broken objects by the ZODB can make an application with
>>> add-ons that use zope.interface far too fragile. If marker interfaces
>>> from an add-on are used on objects in the ZODB, removing that add-on can
>>> make any zope.interface operation on that object fail. Even worse, if
>>> an add-on registers any components in a registry in the ZODB, that
>>> entire registry will become unusable for any ZCA operations which pretty
>>> much breaks everything, including admin interfaces.
>>>
>>> Since the interfaces and the ZCA are often core parts of an application
>>> using the ZODB, it may be appropriate to add special handling for broken
>>> objects to those services. The experimental.broken patches are my
>>> attempt to prototype such special handling.
>>>
>>> For objects in the ZODB which directly provide a marker interface,
>>> these patches allow that object to behave as without the application
>>> of the marker interface if the interface is no longer available. If
>>> the interface is made available again, the full behavior of that
>>> interface is restored. Similarly, if a component whose class,
>>> provided interface, or required interfaces are missing, these patches
>>> allow the registry to perform lookups it would have been able to do
>>> without the broken component registration. If the component
>>> class, provided interface, and required interfaces are restored,
>>> then the component registration is fully restored.
>>>
>>> If an object or registry in the ZODB is committed to the ZODB with
>>> broken interfaces or components, the commit will succeed and it is still
>>> possible to fully restore previous behavior if the missing classes and
>>> interfaces are restored. Unfortunately, because interfaces are pickled
>>> as globals, there's no good way to have the same pickle written on
>>> commit for the interface as was in the original pickle, but it should
>>> behave exactly the same.
>>>
>>> The intention of this package is to see if the implementation of broken
>>> object handling is correct and robust enough to merge into
>>> zope.interface and zope.component themselves. Is this the right
>>> approach? If not why and what would be better? How might this approach
>>> be improved?
>>>
>>> Ross
>>>
>>> ------------------------------------------------------------------------------
>>> RSA(R) Conference 2012
>>> Save $700 by Nov 18
>>> Register now
>>> http://p.sf.net/sfu/rsa-sfdev2dev1
>>
>> _______________________________________________
>> Zope-Dev maillist - Zope-Dev at zope.org
>> https://mail.zope.org/mailman/listinfo/zope-dev
>> ** No cross posts or HTML encoding! **
>> (Related lists -
>> https://mail.zope.org/mailman/listinfo/zope-announce
>> https://mail.zope.org/mailman/listinfo/zope )
>
> _______________________________________________
> Zope-Dev maillist - Zope-Dev at zope.org
> https://mail.zope.org/mailman/listinfo/zope-dev
> ** No cross posts or HTML encoding! **
> (Related lists -
> https://mail.zope.org/mailman/listinfo/zope-announce
> https://mail.zope.org/mailman/listinfo/zope )
More information about the Zope-Dev
mailing list