Skip to content

Commit

Permalink
Merge pull request #76 from NextThought/better-anon-fallback
Browse files Browse the repository at this point in the history
Look for IInternalObjectIO adapters as well as INamedExternalizedObje…
  • Loading branch information
jamadden committed Jul 30, 2018
2 parents 3df10e3 + c5f708e commit 33c22c2
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 3 deletions.
8 changes: 7 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
1.0.0a5 (unreleased)
====================

- Nothing changed yet.
- Objects inheriting from ``InterfaceObjectIO`` and registered with
the component registry (in ZCML) for ``IInternalObjectIO`` can still
be found and used as ``INamedExternalizedObjectFactoryFinder``, an
interface implemented by ``InterfaceObjectIO`` through
``IInternalObjectIOFinder``. A warning will be issued to update the
registration (which generally means removing the ``provides`` line
in ZCML).

- ``ExternalizableInstanceDict`` no longer inherits from
``AbstractDynamicIO``, it just implements the same interface (with
Expand Down
2 changes: 2 additions & 0 deletions src/nti/externalization/internalization/_updater.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ cdef iteritems
cdef component
cdef interface
cdef IInternalObjectUpdater
cdef IInternalObjectIO
cdef INamedExternalizedObjectFactoryFinder

# optimizations
Expand Down Expand Up @@ -53,6 +54,7 @@ cdef _invoke_updater(containedObject, externalObject, updater,
cdef _update_sequence(externalObject, _RecallArgs args,
destination_name=*,
find_factory_for_named_value=*)
cpdef _find_INamedExternalizedObjectFactoryFinder(containedObject, registry)
cdef _update_from_external_object(containedObject, externalObject, _RecallArgs args)

cpdef update_from_external_object(containedObject,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from hamcrest import is_
from hamcrest import assert_that
from hamcrest import has_property as has_attr
from hamcrest import has_length
from hamcrest import contains_string

from zope import interface
from zope import component
Expand All @@ -27,7 +29,9 @@

from zope.testing.cleanup import CleanUp

from nti.externalization import interfaces
from nti.externalization.internalization import update_from_external_object
from nti.externalization.internalization import updater
from nti.externalization.datastructures import InterfaceObjectIO


Expand Down Expand Up @@ -214,6 +218,27 @@ def test_nested_single_object_field_names_match_non_primitive_zcml(self):
assert_that(root.field, has_attr('nested', has_attr('value', 42)))


class TestLookups(CleanUp,
unittest.TestCase):

def test_InterfaceObjectIO_subclass_registered_as_IInternalObjectIO(self):
# We still find it even if it's registered with the legacy interface.
import warnings

class Derived(InterfaceObjectIO):
def __init__(self, context): # pylint:disable=super-init-not-called
pass

component.provideAdapter(Derived, (object,), provides=interfaces.IInternalObjectIO)

with warnings.catch_warnings(record=True) as w:
found = updater._find_INamedExternalizedObjectFactoryFinder(self, component)

assert_that(found, is_(Derived))
assert_that(w, has_length(1))
assert_that(str(w[0].message), contains_string('was registered as IInternalObjectIO'))


class IOBase(object):

@classmethod
Expand Down
28 changes: 26 additions & 2 deletions src/nti/externalization/internalization/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from nti.externalization._base_interfaces import PRIMITIVES
from nti.externalization.interfaces import IInternalObjectUpdater
from nti.externalization.interfaces import IInternalObjectIO
from nti.externalization.interfaces import INamedExternalizedObjectFactoryFinder

from .factories import find_factory_for
Expand Down Expand Up @@ -287,6 +288,29 @@ def _invoke_updater(containedObject, externalObject,
updater, external_keys, _EMPTY_DICT)


def _find_INamedExternalizedObjectFactoryFinder(containedObject, registry):
updater = registry.queryAdapter(containedObject, INamedExternalizedObjectFactoryFinder)
if updater is None:
# Ok, check to see if an instance of the old root interface
# InternalObjectIO is there and also provides INamedExternalizedObjectFactoryFinder;
# if so, there's a bad ZCML registration.
updater = registry.queryAdapter(containedObject, IInternalObjectIO)
if INamedExternalizedObjectFactoryFinder.providedBy(updater):
warnings.warn(
"The adapter %r was registered as IInternalObjectIO when it should be "
"IInternalObjectIOFinder; a provides= ZCML directive is probably outdated. "
"If the object extends InterfacObjectIO, no provides= is usually necessary."
% (updater,),
UserWarning
)
else:
updater = None

if updater is None:
updater = _default_factory_finder
return updater


def _update_from_external_object(containedObject, externalObject, args):

# Parse any contained objects
Expand All @@ -309,8 +333,8 @@ def _update_from_external_object(containedObject, externalObject, args):

assert isinstance(externalObject, MutableMapping)

updater = args.registry.queryAdapter(containedObject, INamedExternalizedObjectFactoryFinder,
u'', _default_factory_finder)
updater = _find_INamedExternalizedObjectFactoryFinder(containedObject, args.registry)

find_factory_for_named_value = updater.find_factory_for_named_value

# We have to save the list of keys, it's common that they get popped during the update
Expand Down

0 comments on commit 33c22c2

Please sign in to comment.