Skip to content

Commit

Permalink
Merge pull request #22 from NextThought/issue17
Browse files Browse the repository at this point in the history
Fix tests on Python 3.
  • Loading branch information
jamadden committed Sep 19, 2017
2 parents 829f2f7 + 7e4930b commit d60691f
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 87 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def _read(fname):
'PyYAML',
'pytz',
'simplejson',
'six',
'six >= 1.11.0', # for the reference cycle fix in reraise()
'ZODB',
'zope.annotation',
'zope.cachedescriptors',
Expand Down
25 changes: 12 additions & 13 deletions src/nti/externalization/datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ def _syntheticKeys():


def _isMagicKey(key):
"""
"""
For our mixin objects that have special keys, defines
those keys that are special and not settable by the user.
those keys that are special and not settable by the user.
"""
return key in _syntheticKeys()
isSyntheticKey = _isMagicKey


class ExternalizableDictionaryMixin(object):
"""
"""
Implements a toExternalDictionary method as a base for subclasses.
"""

Expand Down Expand Up @@ -78,7 +78,7 @@ def toExternalDictionary(self, mergeFrom=None, *unused_args, **kwargs):
**kwargs)

def stripSyntheticKeysFromExternalDictionary(self, external):
"""
"""
Given a mutable dictionary, removes all the external keys
that might have been added by toExternalDictionary and echoed back.
"""
Expand Down Expand Up @@ -235,18 +235,18 @@ def updateFromExternalObject(self, parsed, *unused_args, **unused_kwargs):

if StandardExternalFields.CONTAINER_ID in parsed \
and getattr(ext_self, StandardInternalFields.CONTAINER_ID, parsed) is None:
setattr(ext_self,
setattr(ext_self,
StandardInternalFields.CONTAINER_ID,
parsed[StandardExternalFields.CONTAINER_ID])
if StandardExternalFields.CREATOR in parsed \
and getattr(ext_self, StandardExternalFields.CREATOR, parsed) is None:
setattr(ext_self,
setattr(ext_self,
StandardExternalFields.CREATOR,
parsed[StandardExternalFields.CREATOR])
if ( StandardExternalFields.ID in parsed
and getattr(ext_self, StandardInternalFields.ID, parsed) is None
and self._ext_accept_external_id(ext_self, parsed)):
setattr(ext_self,
setattr(ext_self,
StandardInternalFields.ID,
parsed[StandardExternalFields.ID])

Expand Down Expand Up @@ -368,7 +368,7 @@ def __init__(self, ext_self, iface_upper_bound=None, validate_after_update=True)
# does show up in the profiling data
cache = _InterfaceCache.cache_for(self, ext_self)
if not cache.iface:
cache.iface = self._ext_find_schema(ext_self,
cache.iface = self._ext_find_schema(ext_self,
iface_upper_bound or self._ext_iface_upper_bound)
self._iface = cache.iface

Expand All @@ -382,7 +382,7 @@ def __init__(self, ext_self, iface_upper_bound=None, validate_after_update=True)

@property
def schema(self):
"""
"""
The schema we will use to guide the process
"""
return self._iface
Expand Down Expand Up @@ -460,10 +460,9 @@ def _validate_after_update(self, iface, ext_self):
try:
raise errors[0][1]
except SchemaNotProvided as e:
exc_info = sys.exc_info()
if not e.args: # zope.schema doesn't fill in the details, which sucks
e.args = (errors[0][0],)
raise exc_info[0], exc_info[1], exc_info[2]
raise

def toExternalObject(self, mergeFrom=None, **kwargs):
ext_class_name = None
Expand All @@ -472,7 +471,7 @@ def toExternalObject(self, mergeFrom=None, **kwargs):
if callable(ext_class_name):
# Even though the tagged value may have come from a superclass,
# give the actual class (interface) we're using
ext_class_name = ext_class_name(self._iface,
ext_class_name = ext_class_name(self._iface,
self._ext_replacement())
if ext_class_name:
break
Expand Down Expand Up @@ -526,7 +525,7 @@ def _ext_find_schema(self, ext_self, iface_upper_bound):
"Searching module %s and considered %s on object %s of class %s and type %s"
% (most_derived, iface, self._ext_search_module,
list(self._ext_schemas_to_consider(ext_self)),
ext_self, ext_self.__class__,
ext_self, ext_self.__class__,
type(ext_self)))

return most_derived
Expand Down
23 changes: 11 additions & 12 deletions src/nti/externalization/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@
from __future__ import print_function, absolute_import, division
__docformat__ = "restructuredtext en"

logger = __import__('logging').getLogger(__name__)

import sys
import time
from datetime import datetime

import isodate

import pytz
import six

from zope import component
from zope import interface
Expand All @@ -37,13 +35,14 @@
def _parse_with(func, string):
try:
return func(string)
except isodate.ISO8601Error:
_, v, tb = sys.exc_info()
e = InvalidValue(*v.args, value=string)
raise e, None, tb


@component.adapter(basestring)
except isodate.ISO8601Error as e:
e = InvalidValue(*e.args, value=string)
six.reraise(InvalidValue, e, sys.exc_info()[2])

_input_type = (str if sys.version_info[0] >= 3 else basestring)
# XXX: This should really be either unicode or str on Python 2. We need to *know*
# what our input type is. All the tests pass on Python 3 with this registered to 'str'.
@component.adapter(_input_type)
@interface.implementer(IDate)
def _date_from_string(string):
"""
Expand Down Expand Up @@ -89,7 +88,7 @@ def _local_tzinfo(local_tzname=None):
and all((bool(x) for x in local_tzname))):
offset_hours = time.timezone // 3600
local_tzname = '%s%d%s' % (local_tzname[0],
offset_hours,
offset_hours,
local_tzname[1])
tzinfo = _pytz_timezone(local_tzname)

Expand Down Expand Up @@ -120,7 +119,7 @@ def _as_utc_naive(dt, assume_local=True, local_tzname=None):
return dt


@component.adapter(basestring)
@component.adapter(_input_type)
@interface.implementer(IDateTime)
def datetime_from_string(string, assume_local=False, local_tzname=None):
"""
Expand Down
20 changes: 11 additions & 9 deletions src/nti/externalization/externalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
from __future__ import print_function, absolute_import, division
__docformat__ = "restructuredtext en"

logger = __import__('logging').getLogger(__name__)

import six
import numbers
import collections
from collections import defaultdict

import six
from six import iteritems

from zope import component
from zope import interface
from zope import deprecation
Expand Down Expand Up @@ -48,6 +48,8 @@

from nti.externalization.oids import to_external_oid

logger = __import__('logging').getLogger(__name__)

# Local for speed
StandardExternalFields_ID = StandardExternalFields.ID
StandardExternalFields_OID = StandardExternalFields.OID
Expand Down Expand Up @@ -151,7 +153,7 @@ class _ExternalizationState(object):
registry = None

def __init__(self, **kwargs):
for k, v in kwargs.iteritems():
for k, v in iteritems(kwargs):
setattr(self, k, v)

@CachedProperty
Expand Down Expand Up @@ -316,7 +318,7 @@ def toExternalObject(obj,
decorate_callback=None,
default_non_externalizable_replacer=DefaultNonExternalizableReplacer,
**kwargs):
"""
"""
Translates the object into a form suitable for
external distribution, through some data formatting process. See :const:`SEQUENCE_TYPES`
and :const:`MAPPING_TYPES` for details on what we can handle by default.
Expand Down Expand Up @@ -393,7 +395,7 @@ def get_externals():

def get_external_param(name, default=None):
"""
Return the currently value for an externalization param or default
Return the currently value for an externalization param or default
"""
try:
return get_externals()[name]
Expand All @@ -403,9 +405,9 @@ def get_external_param(name, default=None):


def stripSyntheticKeysFromExternalDictionary(external):
"""
"""
Given a mutable dictionary, removes all the external keys
that might have been added by :func:`to_standard_external_dictionary` and echoed back.
that might have been added by :func:`to_standard_external_dictionary` and echoed back.
"""
for key in _syntheticKeys():
external.pop(key, None)
Expand All @@ -417,7 +419,7 @@ def _syntheticKeys():


def _isMagicKey(key):
"""
"""
For our mixin objects that have special keys, defines
those keys that are special and not settable by the user.
"""
Expand Down
40 changes: 26 additions & 14 deletions src/nti/externalization/internalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
from __future__ import print_function, absolute_import, division
__docformat__ = "restructuredtext en"

logger = __import__('logging').getLogger(__name__)

import six
import sys
import inspect
import numbers
import collections

import six
from six import reraise

from zope import component
from zope import interface

Expand Down Expand Up @@ -45,6 +45,8 @@
from nti.externalization.interfaces import ObjectModifiedFromExternalEvent
from nti.externalization.interfaces import IExternalizedObjectFactoryFinder

logger = __import__('logging').getLogger(__name__)

LEGACY_FACTORY_SEARCH_MODULES = set()

StandardExternalFields_CLASS = StandardExternalFields.CLASS
Expand Down Expand Up @@ -97,7 +99,7 @@ def _search_for_external_factory(typeName, search_set=None):
module = resolve(module_name)
except (AttributeError, ImportError):
# This is a programming error, so that's why we log it
logger.exception("Failed to resolve legacy factory search module %s",
logger.exception("Failed to resolve legacy factory search module %s",
module_name)

value = getattr(module, '__dict__', _EMPTY_DICT) if mod_dict is None else mod_dict
Expand All @@ -122,12 +124,12 @@ def default_externalized_object_factory_finder(externalized_object):
name=mime_type)
if not factory:
# What about a named utility?
factory = component.queryUtility(IMimeObjectFactory,
factory = component.queryUtility(IMimeObjectFactory,
name=mime_type)

if not factory:
# Is there a default?
factory = component.queryAdapter(externalized_object,
factory = component.queryAdapter(externalized_object,
IMimeObjectFactory)

if not factory and StandardExternalFields_CLASS in externalized_object:
Expand Down Expand Up @@ -164,7 +166,7 @@ def find_factory_for(externalized_object, registry=component):
Given a :class:`IExternalizedObject`, locate and return a factory
to produce a Python object to hold its contents.
"""
factory_finder = registry.getAdapter(externalized_object,
factory_finder = registry.getAdapter(externalized_object,
IExternalizedObjectFactoryFinder)
return factory_finder.find_factory(externalized_object)

Expand Down Expand Up @@ -202,7 +204,7 @@ def _resolve_externals(object_io, updating_object, externalObject,
elif len(inspect.getargspec(resolver_func)[0]) == 4: # instance method
_resolver_func = resolver_func

def resolver_func(x, y, z):
def resolver_func(x, y, z):
return _resolver_func(object_io, x, y, z)

externalObject[ext_key] = resolver_func(context, externalObject,
Expand Down Expand Up @@ -381,10 +383,10 @@ def update_from_external_object(containedObject, externalObject,
# The signature may vary.
argspec = inspect.getargspec(updater.updateFromExternalObject)
if 'context' in argspec.args or (argspec.keywords and 'dataserver' not in argspec.args):
updated = updater.updateFromExternalObject(externalObject,
updated = updater.updateFromExternalObject(externalObject,
context=context)
elif argspec.keywords or 'dataserver' in argspec.args:
updated = updater.updateFromExternalObject(externalObject,
updated = updater.updateFromExternalObject(externalObject,
dataserver=context)
else:
updated = updater.updateFromExternalObject(externalObject)
Expand Down Expand Up @@ -436,7 +438,9 @@ def validate_field_value(self, field_name, field, value):
# Nope. TypeError (or AttrError - Variant) means we couldn't adapt,
# and a validation error means we could adapt, but it still wasn't
# right. Raise the original SchemaValidationError.
raise exc_info[0], exc_info[1], exc_info[2]
reraise(*exc_info)
finally:
del exc_info
except WrongType as e:
# Like SchemaNotProvided, but for a primitive type,
# most commonly a date
Expand All @@ -454,14 +458,16 @@ def validate_field_value(self, field_name, field, value):
value = component.getAdapter(value, schema)
except (LookupError, TypeError):
# No registered adapter, darn
raise exc_info[0], exc_info[1], exc_info[2]
raise reraise(*exc_info)
except ValidationError as e:
# Found an adapter, but it does its own validation,
# and that validation failed (eg, IDate below)
# This is still a more useful error than WrongType,
# so go with it after ensuring it has a field
e.field = field
raise
finally:
del exc_info

# Lets try again with the adapted value
return validate_field_value(self, field_name, field, value)
Expand Down Expand Up @@ -499,7 +505,11 @@ def converter(x): return x
# to raise the original error. If we could adapt,
# but the converter does its own validation (e.g., fromObject)
# then we want to let that validation error rise
raise exc_info[0], exc_info[1], exc_info[2]
try:
raise reraise(*exc_info)
finally:
del exc_info


# Now try to set the converted value
try:
Expand All @@ -508,7 +518,9 @@ def converter(x): return x
# Nope. TypeError means we couldn't adapt, and a
# validation error means we could adapt, but it still wasn't
# right. Raise the original SchemaValidationError.
raise exc_info[0], exc_info[1], exc_info[2]
raise reraise(*exc_info)
finally:
del exc_info

if ( field.readonly
and field.get(self) is None
Expand Down
Loading

0 comments on commit d60691f

Please sign in to comment.