diff --git a/CHANGES.rst b/CHANGES.rst index 89c3e2d..a11d2a1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,13 +3,17 @@ ========= -3.0.1 (unreleased) +3.1.0 (unreleased) ================== - Document the ``to_json_representation`` variants and add one that guarantees sorted keys. Make the "fast" variant not dependent on second-chance externalization. - +- Renamed the "datetime" module to "datetime_ext" to avoid conflicts + with the standard library. Backwards compatibility shims are in place. +- Remove some long-deprecated parameters that were typically + undocumented. +- Introduce some basic type annotations. 3.0.0 (2026-05-07) ================== diff --git a/MANIFEST.in b/MANIFEST.in index d516e2b..3e36184 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -11,6 +11,7 @@ include tox.ini include *.txt include .isort.cfg include pyproject.toml +include .readthedocs.yml include .pylintrc include make-manylinux exclude .nti_cover_package diff --git a/docs/api/datetime.rst b/docs/api/datetime.rst index 0147edb..fcd5a9d 100644 --- a/docs/api/datetime.rst +++ b/docs/api/datetime.rst @@ -2,4 +2,4 @@ Datetime ========== -.. automodule:: nti.externalization.datetime +.. automodule:: nti.externalization.datetime_ext diff --git a/pyproject.toml b/pyproject.toml index da0b6db..ef0df0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,8 +17,54 @@ requires = [ # failing in Python 2 (https://travis-ci.org/github/gevent/gevent/jobs/683782800); # This was fixed in 3.0a5 (https://github.com/cython/cython/issues/3578) # 3.0a6 fixes an issue cythonizing source on 32-bit platforms - "Cython >= 3.2.1", + "Cython >= 3.2.4", ] [tool.check-manifest] ignore = ["*.c"] + + +[tool.mypy] +# Must be present for mypy to read this file. +follow_imports = "normal" +check_untyped_defs = true +allow_redefinition = true +disable_error_code = [ + "method-assign" +] +# Our tests are in terrible shape +# and will need some work to be clean +exclude = [ + 'test_.*\.py', +] + +[[tool.mypy.overrides]] +# third-party untyped code +module = [ + "ZODB.*", + "zope.*", + "persistent.*", + "cpuinfo", + "nti.*", + "botocore.*", + "fsspec.*", + "transaction", + "grpc", + "grpc_health.*", + "google.type.*", + "zc.*", + "z3c.*", + "netaddr.*", + "dnslib", + "cytoolz.*", + "toolz.*", + "boto3.*", + "pg8000", + "urllib3.*", + "indexed_gzip", + "pyperf", + "isodate", + "vmprof" + +] +ignore_missing_imports = true diff --git a/setup.py b/setup.py index e9a548e..2005a8b 100755 --- a/setup.py +++ b/setup.py @@ -147,7 +147,7 @@ def _c(m): setup( name='nti.externalization', - version='3.0.1.dev0', + version='3.1.0.dev0', author='Jason Madden', author_email='jason@seecoresoftware.com', description="NTI Externalization", diff --git a/src/nti/externalization/__init__.py b/src/nti/externalization/__init__.py index 3571f4f..7af6b71 100644 --- a/src/nti/externalization/__init__.py +++ b/src/nti/externalization/__init__.py @@ -22,3 +22,8 @@ from nti.externalization.representation import to_json_representation from nti.externalization.internalization import new_from_external_object from nti.externalization.internalization import update_from_external_object + +# BWC hacks +import sys +import nti.externalization.datetime_ext as datetime +sys.modules['nti.externalization.datetime'] = datetime diff --git a/src/nti/externalization/_base_interfaces.py b/src/nti/externalization/_base_interfaces.py index 913e8fe..15448ea 100644 --- a/src/nti/externalization/_base_interfaces.py +++ b/src/nti/externalization/_base_interfaces.py @@ -9,10 +9,6 @@ This module is **PRIVATE** to this package. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import decimal @@ -64,7 +60,7 @@ def update_from_other(self, other): return dict_update(self, other) -def make_external_dict(): +def make_external_dict() -> LocatedExternalDict: # This layer of indirection is for cython; it can't cimport # types when the extension name doesn't match the # pxd name. But it can cimport functions that are cpdef to return @@ -187,7 +183,7 @@ def get_standard_external_fields(): )) -def isSyntheticKey(k): +def isSyntheticKey(k:str) -> bool: """ Deprecated. Prefer to test against StandardExternalFields.EXTERNAL_KEYS """ @@ -241,7 +237,7 @@ def __init__(self): _standard_internal_fields = StandardInternalFields() -def get_standard_internal_fields(): +def get_standard_internal_fields() -> StandardInternalFields: return _standard_internal_fields # Note that we DO NOT include ``numbers.Number`` @@ -308,7 +304,7 @@ def __repr__(self): # pragma: no cover #: The default externalization policy. DEFAULT_EXTERNALIZATION_POLICY = ExternalizationPolicy() -def get_default_externalization_policy(): +def get_default_externalization_policy() -> ExternalizationPolicy: return DEFAULT_EXTERNALIZATION_POLICY from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position diff --git a/src/nti/externalization/_compat.py b/src/nti/externalization/_compat.py index 6606310..e300dc9 100644 --- a/src/nti/externalization/_compat.py +++ b/src/nti/externalization/_compat.py @@ -7,25 +7,24 @@ import os import sys import logging +from typing import overload text_type = str -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] >= 3 -PYPY = hasattr(sys, 'pypy_version_info') WIN = sys.platform.startswith("win") LINUX = sys.platform.startswith('linux') OSX = sys.platform == 'darwin' -PURE_PYTHON = PYPY or os.getenv('PURE_PYTHON') or os.getenv("NTI_EXT_PURE_PYTHON") +PURE_PYTHON = os.getenv('PURE_PYTHON') or os.getenv("NTI_EXT_PURE_PYTHON") try: from zope.dublincore.interfaces import IDCTimes # pylint: disable=unused-import except ModuleNotFoundError: from zope.interface import Interface - class IDCTimes(Interface): # pylint: disable=inherit-non-class + #pylint: disable-next=inherit-non-class + class IDCTimes(Interface): # type:ignore[no-redef] """Mock""" try: @@ -34,7 +33,7 @@ class IDCTimes(Interface): # pylint: disable=inherit-non-class TRACE = 5 logging.addLevelName(TRACE, "TRACE") -def to_unicode(s, encoding='utf-8', err='strict'): +def to_unicode(s, encoding:str='utf-8', err:str='strict') -> str|None: """ Decode a byte sequence and unicode result """ @@ -44,8 +43,15 @@ def to_unicode(s, encoding='utf-8', err='strict'): text_ = to_unicode +@overload +def bytes_(s:str, encoding:str='', errors:str='') -> bytes: + ... -def bytes_(s, encoding='utf-8', errors='strict'): +@overload +def bytes_(s:None, encoding:str='', errors:str='') -> None: + ... + +def bytes_(s, encoding:str='utf-8', errors:str='strict') -> bytes|None: """ If ``s`` is an instance of ``text_type``, return ``s.encode(encoding, errors)``, otherwise return ``s`` diff --git a/src/nti/externalization/_interface_cache.py b/src/nti/externalization/_interface_cache.py index a4b6cef..73e63df 100644 --- a/src/nti/externalization/_interface_cache.py +++ b/src/nti/externalization/_interface_cache.py @@ -4,16 +4,11 @@ A cache based on the interfaces provided by an object. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - - from weakref import WeakSet from zope.interface import providedBy -cache_instances = WeakSet() +cache_instances: WeakSet["InterfaceCache"] = WeakSet() class InterfaceCache(object): @@ -41,7 +36,7 @@ def __init__(self): self.modified_event_attributes = {} -def cache_for_key_in_providedBy(key, provided_by): # type: (object, object) -> InterfaceCache +def cache_for_key_in_providedBy(key, provided_by) -> InterfaceCache: # The Declaration objects returned from ``providedBy(obj)`` maintain a _v_attrs that # gets blown away on changes to themselves or their # dependents, including adding interfaces dynamically to an instance @@ -60,7 +55,7 @@ def cache_for_key_in_providedBy(key, provided_by): # type: (object, object) -> I return cache -def cache_for(externalizer, ext_self): # type: (object, object) -> InterfaceCache +def cache_for(externalizer, ext_self) -> InterfaceCache: return cache_for_key_in_providedBy(type(externalizer), providedBy(ext_self)) @@ -82,4 +77,5 @@ def _cache_cleanUp(instances): # pylint:disable=wrong-import-position from nti.externalization._compat import import_c_accel + import_c_accel(globals(), 'nti.externalization.__interface_cache') diff --git a/src/nti/externalization/_threadlocal.py b/src/nti/externalization/_threadlocal.py index 2e7434f..6a23cc0 100644 --- a/src/nti/externalization/_threadlocal.py +++ b/src/nti/externalization/_threadlocal.py @@ -4,34 +4,35 @@ Thread local utilities. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - # stdlib imports import threading +from collections.abc import Callable +from typing import Generic +from typing import TypeVar + +T = TypeVar("T") # This cannot be optimized (much) with cython, threading.local could be monkey-patched by gevent, # so this cannot be a cdef class -class ThreadLocalManager(threading.local): +class ThreadLocalManager(threading.local, Generic[T]): - def __init__(self, default): + def __init__(self, default:Callable[[], T]): # This is called once in each thread, the first time the object # is used in the thread. The super class does nothing. We use lots # of threads/greenlets, so save the time. # pylint:disable=super-init-not-called - self.stack = [] + self.stack: list[T] = [] self.default = default - def push(self, info): + def push(self, info:T) -> None: self.stack.append(info) set = push # b/c - def pop(self): + def pop(self) -> T|None: return self.stack.pop() if self.stack else None - def get(self): + def get(self) -> T: stack = self.stack if not stack: return self.default() # Note we're not storing it! diff --git a/src/nti/externalization/autopackage.py b/src/nti/externalization/autopackage.py index 68aff56..1467eca 100644 --- a/src/nti/externalization/autopackage.py +++ b/src/nti/externalization/autopackage.py @@ -6,17 +6,17 @@ """ import logging +from collections.abc import Iterable from zope import interface - from zope.dottedname import resolve as dottedname from zope.mimetype.interfaces import IContentTypeAware from nti.schema.interfaces import find_most_derived_interface + from ._compat import TRACE from .datastructures import ModuleScopedInterfaceObjectIO - logger = logging.getLogger(__name__) # If we extend ExtensionClass.Base, __class_init__ is called automatically @@ -24,7 +24,6 @@ # is probably not what we want # import ExtensionClass - class _ClassNameRegistry(object): __name__ = '' @@ -74,7 +73,7 @@ def _ap_compute_external_class_name_from_interface_and_instance(cls, unused_ifac return cls._ap_compute_external_class_name_from_concrete_class(impl.__class__) @classmethod - def _ap_compute_external_class_name_from_concrete_class(cls, a_type): + def _ap_compute_external_class_name_from_concrete_class(cls, a_type) -> str: """ Return the string value of the external class name. @@ -87,7 +86,7 @@ def _ap_compute_external_class_name_from_concrete_class(cls, a_type): return getattr(a_type, '__external_class_name__', a_type.__name__) @classmethod - def _ap_compute_external_mimetype(cls, package_name, unused_a_type, ext_class_name): + def _ap_compute_external_mimetype(cls, package_name, unused_a_type, ext_class_name) -> str: """ Return the string value of the external mime type for the given type in the given package having the given external name (probably @@ -105,7 +104,7 @@ def _ap_compute_external_mimetype(cls, package_name, unused_a_type, ext_class_na @classmethod # TODO: We can probably do something with this - def _ap_enumerate_externalizable_root_interfaces(cls, interfaces): + def _ap_enumerate_externalizable_root_interfaces(cls, interfaces) -> Iterable: """ Return an iterable of the root interfaces in this package that should be externalized. @@ -115,7 +114,7 @@ def _ap_enumerate_externalizable_root_interfaces(cls, interfaces): raise NotImplementedError() @classmethod - def _ap_enumerate_module_names(cls): + def _ap_enumerate_module_names(cls) -> Iterable[str]: """ Return an iterable of module names in this package that should be searched to find factories. @@ -125,7 +124,7 @@ def _ap_enumerate_module_names(cls): raise NotImplementedError() @classmethod - def _ap_find_potential_factories_in_module(cls, module): + def _ap_find_potential_factories_in_module(cls, module) -> Iterable[type]: """ Given a module that we're supposed to examine, iterate over the types that could be factories. @@ -249,7 +248,7 @@ def _ap_handle_one_potential_factory_class(cls, namespace, package_name, impleme implementation_class.containerId = None @classmethod - def _ap_find_package_name(cls): + def _ap_find_package_name(cls) -> str: """ Return the package name to search for modules. diff --git a/src/nti/externalization/configure.zcml b/src/nti/externalization/configure.zcml index ba1d962..7481958 100644 --- a/src/nti/externalization/configure.zcml +++ b/src/nti/externalization/configure.zcml @@ -48,16 +48,16 @@ - - - - - + + + + + - - - + + + diff --git a/src/nti/externalization/datastructures.py b/src/nti/externalization/datastructures.py index 9efcca1..e3eb719 100644 --- a/src/nti/externalization/datastructures.py +++ b/src/nti/externalization/datastructures.py @@ -4,52 +4,47 @@ Datastructures to help externalization. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function # There are a *lot* of fixme (XXX and the like) in this file. # Turn those off in general so we can see through the noise. # pylint:disable=fixme # pylint:disable=keyword-arg-before-vararg - # stdlib imports import numbers import warnings +from collections.abc import Collection +from collections.abc import Mapping +from collections.abc import MutableMapping from zope import interface from zope import schema from zope.component import getUtility -from zope.schema.interfaces import SchemaNotProvided from zope.schema.interfaces import IDict from zope.schema.interfaces import IObject +from zope.schema.interfaces import SchemaNotProvided from nti.schema.interfaces import find_most_derived_interface -from .interfaces import IInternalObjectExternalizer -from .interfaces import IInternalObjectIO -from .interfaces import IInternalObjectIOFinder -from .interfaces import IAnonymousObjectFactory -from .interfaces import StandardInternalFields - +from ._base_interfaces import NotGiven +from ._base_interfaces import get_default_externalization_policy +from ._base_interfaces import get_standard_external_fields +from ._base_interfaces import get_standard_internal_fields +from ._interface_cache import cache_for # Things imported from cython with matching cimport -from .externalization.dictionary import to_minimal_standard_external_dictionary from .externalization.dictionary import internal_to_standard_external_dictionary +from .externalization.dictionary import to_minimal_standard_external_dictionary # Must rename this so it doesn't conflict with method defs; # that breaks cython from .externalization.externalizer import to_external_object as _toExternalObject - +from .factory import AnonymousObjectFactory +from .interfaces import IAnonymousObjectFactory +from .interfaces import IInternalObjectExternalizer +from .interfaces import IInternalObjectIO +from .interfaces import IInternalObjectIOFinder +from .interfaces import StandardInternalFields from .internalization import validate_named_field_value from .internalization.factories import find_factory_for from .representation import make_repr -from .factory import AnonymousObjectFactory - -from ._base_interfaces import get_standard_external_fields -from ._base_interfaces import get_standard_internal_fields -from ._base_interfaces import get_default_externalization_policy -from ._base_interfaces import NotGiven - -from ._interface_cache import cache_for StandardExternalFields = get_standard_external_fields() StandardInternalFields = get_standard_internal_fields() @@ -83,7 +78,9 @@ def _ext_replacement(self): """ return self - def _ext_standard_external_dictionary(self, replacement, mergeFrom=None, **kwargs): + def _ext_standard_external_dictionary(self, replacement, + mergeFrom: Mapping|None = None, + **kwargs) -> MutableMapping: if self.__external_use_minimal_base__: return to_minimal_standard_external_dictionary(replacement, mergeFrom=mergeFrom) @@ -97,7 +94,8 @@ def _ext_standard_external_dictionary(self, replacement, mergeFrom=None, **kwarg policy=kwargs.get("policy", DEFAULT_EXTERNALIZATION_POLICY), ) - def toExternalDictionary(self, mergeFrom=None, *unused_args, **kwargs): + def toExternalDictionary(self, mergeFrom:Mapping|None = None, + *unused_args, **kwargs) -> MutableMapping: """ Produce the standard external dictionary for this object. @@ -193,7 +191,7 @@ class AbstractDynamicObjectIO(ExternalizableDictionaryMixin): StandardExternalFields.CLASS, StandardInternalFields.CONTAINER_ID }) - _ext_primitive_out_ivars_ = frozenset() + _ext_primitive_out_ivars_:Collection[str] = frozenset() _prefer_oid_ = False def find_factory_for_named_value(self, key, value): # pylint:disable=unused-argument @@ -313,7 +311,8 @@ def toExternalDictionary(self, mergeFrom=None, *unused_args, **kwargs): result[StandardExternalFields.ID] = result[StandardExternalFields.OID] return result - def toExternalObject(self, mergeFrom=None, *args, **kwargs): + def toExternalObject(self, mergeFrom: MutableMapping|None = None, + *args, **kwargs) -> MutableMapping: return self.toExternalDictionary(mergeFrom, *args, **kwargs) def _ext_accept_update_key(self, k, ext_self, ext_keys): # pylint:disable=unused-argument @@ -651,7 +650,7 @@ def _ext_getattr(self, ext_self, k, default=NotGiven): def _ext_setattr(self, ext_self, k, value): validate_named_field_value(ext_self, self._iface, k, value)() - def _ext_accept_external_id(self, ext_self, parsed): + def _ext_accept_external_id(self, ext_self, parsed) -> bool: """ If the interface we're working from has a tagged value of ``__external_accept_id__`` on the ``id`` field, then @@ -660,7 +659,7 @@ def _ext_accept_external_id(self, ext_self, parsed): cache = cache_for(self, ext_self) if cache.ext_accept_external_id is None: try: - field = cache.iface['id'] + field = cache.iface['id'] # type:ignore[index] cache.ext_accept_external_id = field.getTaggedValue('__external_accept_id__') except KeyError: cache.ext_accept_external_id = False @@ -782,7 +781,8 @@ def _validate_after_update(self, iface, ext_self): e.args = (errors[0][0],) raise - def toExternalObject(self, mergeFrom=None, **kwargs): # pylint:disable=arguments-differ + def toExternalObject(self, mergeFrom: MutableMapping|None = None,# pylint:disable=arguments-differ + **kwargs) -> MutableMapping: ext_class_name = None # Walk up the tree, checking each one to see if ``__external__class_name__`` exists # and wants to provide a value. The walking up is what ``queryTaggedValue`` would do, @@ -868,6 +868,7 @@ def _ext_find_schema(self, ext_self, iface_upper_bound): return most_derived def _ext_schemas_to_consider(self, ext_self): + assert self._ext_search_module is not None search_module_name = self._ext_search_module.__name__ return [x for x in interface.providedBy(ext_self) if x.__module__ == search_module_name diff --git a/src/nti/externalization/datetime.py b/src/nti/externalization/datetime_ext.py similarity index 98% rename from src/nti/externalization/datetime.py rename to src/nti/externalization/datetime_ext.py index a1f9094..19391a2 100644 --- a/src/nti/externalization/datetime.py +++ b/src/nti/externalization/datetime_ext.py @@ -11,13 +11,8 @@ this package has been configured, but they can be called manually as well. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - # stdlib imports from datetime import datetime -import sys import time import isodate @@ -57,7 +52,7 @@ def _parse_with(func, string): ).with_traceback(e.__traceback__) from e -_input_type = (str if sys.version_info[0] >= 3 else basestring) # pylint:disable=undefined-variable +_input_type = str # 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', # so we're never passing ``bytes`` diff --git a/src/nti/externalization/dublincore.py b/src/nti/externalization/dublincore.py index 0b00648..33f7c17 100644 --- a/src/nti/externalization/dublincore.py +++ b/src/nti/externalization/dublincore.py @@ -14,10 +14,6 @@ """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from zope import component from zope import interface try: @@ -25,9 +21,9 @@ from zope.dublincore.interfaces import IDCExtended except ModuleNotFoundError: # pylint:disable=inherit-non-class - class IDCDescriptiveProperties(interface.Interface): + class IDCDescriptiveProperties(interface.Interface): # type:ignore[no-redef] """Mock""" - class IDCExtended(interface.Interface): + class IDCExtended(interface.Interface): # type:ignore[no-redef] """Mock""" from nti.externalization.interfaces import IExternalStandardDictionaryDecorator diff --git a/src/nti/externalization/extension_points.py b/src/nti/externalization/extension_points.py index 57c8ebe..b812e50 100644 --- a/src/nti/externalization/extension_points.py +++ b/src/nti/externalization/extension_points.py @@ -24,10 +24,6 @@ .. versionadded:: 1.0 """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from zope.hookable import hookable from ._compat import to_unicode diff --git a/src/nti/externalization/externalization/__init__.py b/src/nti/externalization/externalization/__init__.py index 6465383..95da55a 100644 --- a/src/nti/externalization/externalization/__init__.py +++ b/src/nti/externalization/externalization/__init__.py @@ -8,10 +8,11 @@ """ +from collections.abc import MutableMapping + # Our request hook function always returns None, and pylint # flags that as useless (good for it) # pylint:disable=assignment-from-none - import warnings from collections.abc import Mapping @@ -19,26 +20,20 @@ from zope import deferredimport from nti.externalization._base_interfaces import MINIMAL_SYNTHETIC_EXTERNAL_KEYS -from nti.externalization._base_interfaces import isSyntheticKey -from nti.externalization._base_interfaces import NotGiven from nti.externalization._base_interfaces import PRIMITIVES as _primitives - +from nti.externalization._base_interfaces import NotGiven +from nti.externalization._base_interfaces import isSyntheticKey from nti.externalization.extension_points import get_current_request -from .replacers import NonExternalizableObjectError - +from .decorate import decorate_external_mapping as _decorate_external_mapping +from .dictionary import to_minimal_standard_external_dictionary +from .dictionary import to_standard_external_dictionary +from .externalizer import to_external_object from .fields import choose_field - +from .replacers import NonExternalizableObjectError from .standard_fields import SYSTEM_USER_NAME -from .standard_fields import get_last_modified_time from .standard_fields import get_created_time - -from .dictionary import to_standard_external_dictionary -from .dictionary import to_minimal_standard_external_dictionary - -from .externalizer import to_external_object - -from .decorate import decorate_external_mapping as _decorate_external_mapping +from .standard_fields import get_last_modified_time __all__ = [ 'choose_field', @@ -102,12 +97,6 @@ def toExternalDictionary(*args, **kwargs): # pragma: no cover return to_standard_external_dictionary(*args, **kwargs) -def is_nonstr_iter(v): # pragma: no cover - warnings.warn("'is_nonstr_iter' will be deleted. It is broken on Python 3", - FutureWarning, stacklevel=2) - return hasattr(v, '__iter__') - - def removed_unserializable(ext): # pylint:disable=too-many-branches,too-complex # XXX: Why is this here? We don't use it anymore. @@ -122,10 +111,12 @@ def _clean(m): for k, v in list(m.items()): if _is_sequence(v): if not isinstance(v, list): + assert isinstance(m, MutableMapping) m[k] = list(v) # pylint:disable-next=confusing-consecutive-elif elif not isinstance(v, Mapping): if not isinstance(v, _primitives): + assert isinstance(m, MutableMapping) m[k] = None values = m.values() elif isinstance(m, list): diff --git a/src/nti/externalization/externalization/_externalizer.pxd b/src/nti/externalization/externalization/_externalizer.pxd index 3e208a8..edbbe1c 100644 --- a/src/nti/externalization/externalization/_externalizer.pxd +++ b/src/nti/externalization/externalization/_externalizer.pxd @@ -50,7 +50,7 @@ cdef tuple MAPPING_TYPES cdef class _ExternalizationState(object): cdef dict memo - cdef basestring name + cdef str name cdef catch_components cdef catch_component_action cdef request @@ -78,7 +78,6 @@ cdef class _RecursiveCallState(dict): cpdef to_external_object( obj, name=*, - registry=*, catch_components=*, catch_component_action=*, request=*, diff --git a/src/nti/externalization/externalization/_standard_fields.pxd b/src/nti/externalization/externalization/_standard_fields.pxd index 58e9e40..c615fbe 100644 --- a/src/nti/externalization/externalization/_standard_fields.pxd +++ b/src/nti/externalization/externalization/_standard_fields.pxd @@ -27,8 +27,8 @@ cdef tuple _CREATOR_FIELDS cdef tuple _CONTAINER_FIELDS cdef _EXT_CLASS_IGNORED_MODULES -cdef basestring _SYSTEM_USER_NAME -cdef basestring _SYSTEM_USER_ID +cdef str _SYSTEM_USER_NAME +cdef str _SYSTEM_USER_ID cdef IPrincipal_providedBy # XXX: If we use ``cdef``, then when we pass these functions to diff --git a/src/nti/externalization/externalization/dictionary.py b/src/nti/externalization/externalization/dictionary.py index 552529c..49ab104 100644 --- a/src/nti/externalization/externalization/dictionary.py +++ b/src/nti/externalization/externalization/dictionary.py @@ -7,46 +7,34 @@ # Our request hook function always returns None, and pylint # flags that as useless (good for it) # pylint:disable=assignment-from-none - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - # stdlib imports -import warnings +from collections.abc import Mapping - -from nti.externalization._base_interfaces import make_external_dict +from nti.externalization._base_interfaces import LocatedExternalDict from nti.externalization._base_interfaces import NotGiven - - -from nti.externalization.extension_points import set_external_identifiers -from nti.externalization.interfaces import IExternalStandardDictionaryDecorator - - -from nti.externalization._base_interfaces import get_standard_external_fields from nti.externalization._base_interfaces import get_default_externalization_policy - - -from nti.externalization.externalization.standard_fields import get_last_modified_time +from nti.externalization._base_interfaces import get_standard_external_fields +from nti.externalization._base_interfaces import make_external_dict +from nti.externalization.extension_points import set_external_identifiers +from nti.externalization.externalization.decorate import decorate_external_object +from nti.externalization.externalization.standard_fields import get_class +from nti.externalization.externalization.standard_fields import get_container_id from nti.externalization.externalization.standard_fields import get_created_time from nti.externalization.externalization.standard_fields import get_creator -from nti.externalization.externalization.standard_fields import get_container_id -from nti.externalization.externalization.standard_fields import get_class - -from nti.externalization.externalization.decorate import decorate_external_object +from nti.externalization.externalization.standard_fields import get_last_modified_time +from nti.externalization.interfaces import IExternalStandardDictionaryDecorator StandardExternalFields = get_standard_external_fields() DEFAULT_EXTERNALIZATION_POLICY = get_default_externalization_policy() def internal_to_standard_external_dictionary( self, - mergeFrom=None, + mergeFrom: Mapping|None = None, decorate=True, request=NotGiven, decorate_callback=NotGiven, policy=DEFAULT_EXTERNALIZATION_POLICY, -): +) -> LocatedExternalDict: # pylint:disable=too-many-positional-arguments # The real implementation of this function. Code in this # package should use this; code outside of this package *MUST NOT* @@ -73,20 +61,18 @@ def internal_to_standard_external_dictionary( return result + + def to_standard_external_dictionary( self, - mergeFrom=None, - registry=NotGiven, # Ignored + mergeFrom: Mapping|None = None, decorate=True, request=NotGiven, decorate_callback=NotGiven, policy=DEFAULT_EXTERNALIZATION_POLICY, - # These are ignored, present for BWC - name=NotGiven, - useCache=NotGiven, - **kwargs -): - """to_standard_external_dictionary(self, mergeFrom=None, decorate=True, request=NotGiven) +) -> LocatedExternalDict: + """ + to_standard_external_dictionary(self, mergeFrom=None, decorate=True, request=NotGiven) Returns a dictionary representing the standard externalization of the object *self*. This function takes care of many of the standard external fields: @@ -115,34 +101,25 @@ def to_standard_external_dictionary( type and mutating the dictionary returned from super's ``toExternalObject`` in your own implementation. - :keyword dict mergeFrom: For convenience, if *mergeFrom* is not + :keyword dict mergeFrom: For convenience, if *mergeFrom* is not `None`, then values it contains will be added to the dictionary created by this method. The keys and values in *mergeFrom* should already be external. - :type mergeFrom: dict - :keyword ExternalizationPolicy policy: The :class:`~.ExternalizationPolicy` to + :type mergeFrom: dict + :keyword ExternalizationPolicy policy: The :class:`~.ExternalizationPolicy` to use. Must not be None. - :returns: A `.LocatedExternalDict`. For further externalization, + :returns: A `.LocatedExternalDict`. For further externalization, this object should be mutated in place. - .. versionchanged:: 1.0a1 + .. versionchanged:: 1.0a1 Arbitrary keyword arguments not used by this function are deprecated and produce a warning. - .. versionchanged:: 2.1 + .. versionchanged:: 2.1 Add the *policy* keyword. + .. versionchanged:: NEXT + Remove deprecated parameters. """ # pylint:disable=too-many-positional-arguments - if ( - kwargs - or name is not NotGiven - or useCache is not NotGiven - or registry is not NotGiven - ): # pragma: no cover - for _ in range(3): - warnings.warn( - "Passing unused arguments to to_standard_external_dictionary will be an error", - FutureWarning) - return internal_to_standard_external_dictionary( self, mergeFrom, @@ -153,7 +130,8 @@ def to_standard_external_dictionary( ) -def to_minimal_standard_external_dictionary(self, mergeFrom=None): +def to_minimal_standard_external_dictionary(self, + mergeFrom:Mapping|None = None) -> LocatedExternalDict: """ Does no decoration. Useful for non-'object' types. *self* should have a *mime_type* field. """ @@ -170,5 +148,5 @@ def to_minimal_standard_external_dictionary(self, mergeFrom=None): return result -from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position,wrong-import-order +from nti.externalization._compat import import_c_accel import_c_accel(globals(), 'nti.externalization.externalization._dictionary') diff --git a/src/nti/externalization/externalization/externalizer.py b/src/nti/externalization/externalization/externalizer.py index 7fac493..9a6c4a4 100644 --- a/src/nti/externalization/externalization/externalizer.py +++ b/src/nti/externalization/externalization/externalizer.py @@ -5,51 +5,46 @@ """ -# Our request hook function always returns None, and pylint -# flags that as useless (good for it) -# pylint:disable=assignment-from-none - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -# stdlib imports +import logging import warnings -from collections.abc import Set -from collections.abc import Mapping from collections import defaultdict +from collections.abc import Callable +from collections.abc import Mapping +from collections.abc import MutableMapping +from collections.abc import Set +from typing import Any from weakref import WeakKeyDictionary - try: from persistent.list import PersistentList _PL = (PersistentList,) except ModuleNotFoundError: _PL = () -from zope.component import queryAdapter from zope.component import getUtility +from zope.component import queryAdapter from zope.interface.common.sequence import IFiniteSequence -from nti.externalization._base_interfaces import NotGiven from nti.externalization._base_interfaces import PRIMITIVES +from nti.externalization._base_interfaces import NotGiven from nti.externalization._base_interfaces import get_default_externalization_policy from nti.externalization._threadlocal import ThreadLocalManager from nti.externalization.extension_points import get_current_request - -from nti.externalization.interfaces import IInternalObjectExternalizer +from nti.externalization.externalization.decorate import decorate_external_object +from nti.externalization.externalization.dictionary import internal_to_standard_external_dictionary +from nti.externalization.externalization.replacers import DefaultNonExternalizableReplacer +from nti.externalization.interfaces import IExternalizationPolicy from nti.externalization.interfaces import IExternalObjectDecorator +from nti.externalization.interfaces import IInternalObjectExternalizer from nti.externalization.interfaces import ILocatedExternalSequence from nti.externalization.interfaces import INonExternalizableReplacementFactory -from nti.externalization.interfaces import IExternalizationPolicy - -from nti.externalization.externalization.replacers import DefaultNonExternalizableReplacer - -from nti.externalization.externalization.dictionary import internal_to_standard_external_dictionary -from nti.externalization.externalization.decorate import decorate_external_object +# Our request hook function always returns None, and pylint +# flags that as useless (good for it) +# pylint:disable=assignment-from-none +# stdlib imports -logger = __import__('logging').getLogger(__name__) +logger = logging.getLogger(__name__) DEFAULT_EXTERNALIZATION_POLICY = get_default_externalization_policy() @@ -60,7 +55,9 @@ # the name that was established at the top level. # Stores tuples (name, memos) -_manager = ThreadLocalManager(default=lambda: (NotGiven, None, DEFAULT_EXTERNALIZATION_POLICY)) +_manager: ThreadLocalManager[tuple[Any, Any, Any]] = ThreadLocalManager( + default=lambda: (NotGiven, None, DEFAULT_EXTERNALIZATION_POLICY) +) _manager_get = _manager.get _manager_pop = _manager.pop _manager_push = _manager.push @@ -102,8 +99,11 @@ class _ExternalizationState(object): '_kwargs', ) - def __init__(self, memos, - name, catch_components, catch_component_action, + def __init__(self, + memos, + name, + catch_components: tuple[type[BaseException], ...] | type[BaseException], + catch_component_action: Callable[[Any, BaseException], Any]|None, request, default_non_externalizable_replacer, decorate=True, @@ -130,7 +130,7 @@ def __init__(self, memos, self.policy = policy - self._kwargs = None + self._kwargs: dict|None = None def as_kwargs(self): if self._kwargs is None: @@ -181,7 +181,7 @@ def _externalize_sequence(obj, state): return result -_usable_externalObject_cache = WeakKeyDictionary() +_usable_externalObject_cache:MutableMapping[type, bool] = WeakKeyDictionary() _usable_externalObject_cache_get = _usable_externalObject_cache.get try: @@ -238,7 +238,7 @@ def _externalize_object(obj, state): adapter = IInternalObjectExternalizer(obj, None) if adapter is not None: - toExternalObject = adapter.toExternalObject + toExternalObject = adapter.toExternalObject # type:ignore[misc] if toExternalObject is not None: result = toExternalObject(**state.as_kwargs()) @@ -294,7 +294,9 @@ def _to_external_object_state(obj, state, top_level=False): # TODO: Should this live here, or at a higher level where the ultimate # external target/use-case is known? replacer = state.default_non_externalizable_replacer - result = INonExternalizableReplacementFactory(obj, replacer)(obj) + result = INonExternalizableReplacementFactory( # type:ignore[misc] + obj, replacer + )(obj) # type:ignore[call-arg] decorate_external_object( @@ -325,9 +327,8 @@ def _to_external_object_state(obj, state, top_level=False): def to_external_object( obj, name=NotGiven, - registry=NotGiven, - catch_components=(), - catch_component_action=None, + catch_components: tuple[type[BaseException],...]|type[BaseException] = (), + catch_component_action: Callable[[Any, BaseException], Any]|None = None, request=NotGiven, decorate=True, useCache=True, @@ -344,7 +345,7 @@ def to_external_object( :const:`SEQUENCE_TYPES` and :const:`MAPPING_TYPES` for details on what we can handle by default. - :param string name: The name of the adapter to + :param str name: The name of the adapter to :class:`~nti.externalization.interfaces.IInternalObjectExternalizer` to look for. Defaults to the empty string (the default adapter). If you provide a name, and an adapter is not found, @@ -380,6 +381,9 @@ def to_external_object( is used. :param str policy_name: If no *policy* is given, then this is used to lookup a utility. If this is used, the utility must exist. + + .. versionchanged:: NEXT + Remove the deprecated *registry* argument. """ # pylint:disable=too-many-positional-arguments # Catch the primitives up here, quickly. This catches @@ -394,11 +398,6 @@ def to_external_object( name = '' if request is NotGiven: request = get_current_request() - if registry is not NotGiven: # pragma: no cover - warnings.warn( - "The registry argument is deprecated and ignored. Call in a correct site.", - FutureWarning - ) if policy is NotGiven: @@ -425,5 +424,7 @@ def to_external_object( _manager_pop() -from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position,wrong-import-order +from nti.externalization._compat import \ + import_c_accel # pylint:disable=wrong-import-position,wrong-import-order + import_c_accel(globals(), 'nti.externalization.externalization._externalizer') diff --git a/src/nti/externalization/externalization/fields.py b/src/nti/externalization/externalization/fields.py index 20f23f0..394797d 100644 --- a/src/nti/externalization/externalization/fields.py +++ b/src/nti/externalization/externalization/fields.py @@ -4,17 +4,8 @@ Finding fields of an object. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - - - - from nti.externalization._base_interfaces import get_standard_external_fields -logger = __import__('logging').getLogger(__name__) - StandardExternalFields = get_standard_external_fields() def choose_field(result, self, ext_name, diff --git a/src/nti/externalization/externalization/replacers.py b/src/nti/externalization/externalization/replacers.py index 5fff2f2..5c1a35a 100644 --- a/src/nti/externalization/externalization/replacers.py +++ b/src/nti/externalization/externalization/replacers.py @@ -4,17 +4,16 @@ Object replacers. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from zope import interface +import logging + from zope import deprecation +from zope import interface from nti.externalization.interfaces import INonExternalizableReplacement from nti.externalization.interfaces import INonExternalizableReplacementFactory -logger = __import__('logging').getLogger(__name__) +logger = logging.getLogger(__name__) @interface.implementer(INonExternalizableReplacement) class _NonExternalizableObject(dict): diff --git a/src/nti/externalization/externalization/standard_fields.py b/src/nti/externalization/externalization/standard_fields.py index 7ed042c..647af79 100644 --- a/src/nti/externalization/externalization/standard_fields.py +++ b/src/nti/externalization/externalization/standard_fields.py @@ -4,9 +4,6 @@ Functions to find standard fields. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function # pylint:disable=inconsistent-return-statements @@ -47,7 +44,7 @@ def datetime_to_unix_time(dt): def datetime_to_string(dt): global _datetime_to_string # pylint:disable=global-statement if _datetime_to_string is None: - from nti.externalization.datetime import datetime_to_string as dts + from nti.externalization.datetime_ext import datetime_to_string as dts _datetime_to_string = dts if dt is not None: return _datetime_to_string(dt).toExternalObject() diff --git a/src/nti/externalization/externalization/tests/test_externalizer.py b/src/nti/externalization/externalization/tests/test_externalizer.py index 169ef14..cb77d5b 100644 --- a/src/nti/externalization/externalization/tests/test_externalizer.py +++ b/src/nti/externalization/externalization/tests/test_externalizer.py @@ -1,9 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 @@ -11,12 +7,13 @@ from zope.proxy import ProxyBase +from nti.externalization.externalization import to_external_object + from hamcrest import assert_that from hamcrest import has_length from hamcrest import is_ from hamcrest import same_instance -from nti.externalization.externalization import to_external_object from ..externalizer import _obj_has_usable_externalObject @@ -70,6 +67,7 @@ def __conform__(self, iface): # pylint:disable=bad-dunder-name def test_uses_directly_provided(self): from zope import interface + from nti.externalization.interfaces import IInternalObjectExternalizer class Obj(object): diff --git a/src/nti/externalization/factory.py b/src/nti/externalization/factory.py index 5f3cad1..8cacb08 100644 --- a/src/nti/externalization/factory.py +++ b/src/nti/externalization/factory.py @@ -5,16 +5,16 @@ .. versionadded:: 1.0 """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function + +from collections.abc import Callable +from typing import Any from zope import interface from zope.component.factory import Factory +from nti.externalization.interfaces import IAnonymousObjectFactory from nti.externalization.interfaces import IClassObjectFactory from nti.externalization.interfaces import IMimeObjectFactory -from nti.externalization.interfaces import IAnonymousObjectFactory _builtin_callable = callable @@ -60,7 +60,7 @@ class properties :attr:`default_factory` and (optionally) #: The default callable argument, if none is given to the #: constructor. - default_factory = None + default_factory: Callable[[Any], Any]|None = None #: The default title, if none is given to the constructor. default_title = '' #: The default description, if none is given to the constructor. diff --git a/src/nti/externalization/integer_strings.py b/src/nti/externalization/integer_strings.py index 7b58042..00852e1 100644 --- a/src/nti/externalization/integer_strings.py +++ b/src/nti/externalization/integer_strings.py @@ -23,9 +23,6 @@ """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function __all__ = [ 'to_external_string', @@ -35,11 +32,7 @@ # stdlib imports import string -try: - maketrans = str.maketrans -except AttributeError: # Python 2 - from string import maketrans # pylint:disable=no-name-in-module - +maketrans = str.maketrans translate = str.translate # In the first version of the protocol, the version marker, which would @@ -65,7 +58,7 @@ _ZERO_MARKER = '@' # Zero is special -def from_external_string(key): +def from_external_string(key) -> int: """ Turn the string in *key* into an integer. @@ -104,7 +97,7 @@ def from_external_string(key): return int_sum -def to_external_string(integer): +def to_external_string(integer) -> str: """ Turn an integer into a native string representation. diff --git a/src/nti/externalization/interfaces.py b/src/nti/externalization/interfaces.py index 04307e3..46c6dbc 100644 --- a/src/nti/externalization/interfaces.py +++ b/src/nti/externalization/interfaces.py @@ -5,34 +5,31 @@ """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function +from typing import Any +import zope.interface.common.builtins as ibuiltins from zope import interface - from zope.component.interfaces import IFactory from zope.deprecation import deprecated from zope.interface.common import collections as icollections -from zope.interface.common import sequence as legacy_isequence from zope.interface.common import mapping as legacy_imapping -import zope.interface.common.builtins as ibuiltins +from zope.interface.common import sequence as legacy_isequence from zope.interface.interfaces import IObjectEvent from zope.interface.interfaces import ObjectEvent from zope.lifecycleevent import IObjectModifiedEvent from zope.lifecycleevent import ObjectModifiedEvent from zope.location import ILocation +from ._base_interfaces import MINIMAL_SYNTHETIC_EXTERNAL_KEYS +from ._base_interfaces import ExternalizationPolicy +from ._base_interfaces import LocatedExternalDict +from ._base_interfaces import get_default_externalization_policy +from ._base_interfaces import get_standard_external_fields +from ._base_interfaces import get_standard_internal_fields # pylint:disable=inherit-non-class,no-method-argument,no-self-argument # pylint:disable=too-many-ancestors -from ._base_interfaces import LocatedExternalDict -from ._base_interfaces import get_standard_external_fields -from ._base_interfaces import get_standard_internal_fields -from ._base_interfaces import MINIMAL_SYNTHETIC_EXTERNAL_KEYS -from ._base_interfaces import ExternalizationPolicy -from ._base_interfaces import get_default_externalization_policy StandardExternalFields = get_standard_external_fields() StandardInternalFields = get_standard_internal_fields() @@ -457,7 +454,7 @@ class ObjectModifiedFromExternalEvent(ObjectModifiedEvent): Default implementation of `IObjectModifiedFromExternalEvent`. """ - kwargs = None + kwargs:dict[str,Any]|None = None external_value = None def __init__(self, obj, *descriptions, **kwargs): diff --git a/src/nti/externalization/internalization/__init__.py b/src/nti/externalization/internalization/__init__.py index 1ea7469..0d873ad 100644 --- a/src/nti/externalization/internalization/__init__.py +++ b/src/nti/externalization/internalization/__init__.py @@ -5,10 +5,6 @@ model objects. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import warnings from zope.interface.interfaces import ComponentLookupError diff --git a/src/nti/externalization/internalization/_externals.pxd b/src/nti/externalization/internalization/_externals.pxd index c7f88b0..b96868d 100644 --- a/src/nti/externalization/internalization/_externals.pxd +++ b/src/nti/externalization/internalization/_externals.pxd @@ -3,9 +3,9 @@ import cython # imports -cdef MutableSequenc -cdef component cdef IExternalReferenceResolver +cdef MutableSequence +cdef component # XXX: This is only public for testing cpdef resolve_externals(object_io, updating_object, externalObject, diff --git a/src/nti/externalization/internalization/_factories.pxd b/src/nti/externalization/internalization/_factories.pxd index 3944c21..1004425 100644 --- a/src/nti/externalization/internalization/_factories.pxd +++ b/src/nti/externalization/internalization/_factories.pxd @@ -33,8 +33,8 @@ cdef class _DefaultExternalizedObjectFactoryFinder(object): cdef _search_for_class_factory(externalized_object, class_name) cdef _search_for_mime_factory(externalized_object, mime_type) -cpdef find_factory_for_class_name(class_name) +cpdef find_factory_for_class_name(str class_name) cdef _find_factory_for_mime_or_class(externalized_object) -cpdef find_factory_for(externalized_object, registry=*) +cpdef find_factory_for(externalized_object) diff --git a/src/nti/externalization/internalization/_fields.pxd b/src/nti/externalization/internalization/_fields.pxd index 50d7283..969b273 100644 --- a/src/nti/externalization/internalization/_fields.pxd +++ b/src/nti/externalization/internalization/_fields.pxd @@ -58,12 +58,10 @@ cdef _handle_SchemaNotProvided(field_name, field, value) cdef _handle_WrongType(field_name, field, value) cdef _handle_WrongContainedType(field_name, field, value) -cdef str _as_native_str(s) - cdef tuple _CONVERTERS @cython.locals( meth_name_kind=tuple, ) -cpdef validate_field_value(self, field_name, field, value) -cpdef validate_named_field_value(self, iface, field_name, value) +cpdef validate_field_value(self, str field_name, field, value) +cpdef validate_named_field_value(self, iface, str field_name, value) diff --git a/src/nti/externalization/internalization/_legacy_factories.pxd b/src/nti/externalization/internalization/_legacy_factories.pxd index 222147d..b3675b5 100644 --- a/src/nti/externalization/internalization/_legacy_factories.pxd +++ b/src/nti/externalization/internalization/_legacy_factories.pxd @@ -22,7 +22,7 @@ cpdef list find_factories_in_module(module, case_sensitive=*) # private cdef set _ext_factory_warnings -cpdef search_for_external_factory(class_name) +cpdef search_for_external_factory(str class_name) cdef register_factories_from_search_set() cdef register_factories_from_module(module) diff --git a/src/nti/externalization/internalization/_updater.pxd b/src/nti/externalization/internalization/_updater.pxd index 3fabacc..24baba4 100644 --- a/src/nti/externalization/internalization/_updater.pxd +++ b/src/nti/externalization/internalization/_updater.pxd @@ -38,7 +38,6 @@ cdef dict _EMPTY_DICT @cython.freelist(1000) cdef class _RecallArgs(object): cdef context - cdef pre_hook cdef root cdef bint require_updater cdef bint notify @@ -61,20 +60,23 @@ cdef _update_sequence(externalObject, _RecallArgs args, cpdef _find_INamedExternalizedObjectFactoryFinder(containedObject) cdef _update_from_external_object(containedObject, externalObject, _RecallArgs args) -cpdef update_from_external_object(containedObject, - externalObject, - registry=*, - context=*, - bint require_updater=*, - bint notify=*, - pre_hook=*) +# The use of @overload prevents this from being +# cpdef, unfortunately. Cython tries to generate the +# function multiple times. +# cpdef update_from_external_object(containedObject, +# externalObject, +# registry=*, +# context=*, +# bint require_updater=*, +# bint notify=*, +# ) -cdef dict _argspec_cacheg +cdef dict _argspec_cache cdef str _UPDATE_ARGS_TWO cdef str _UPDATE_ARGS_ONE cdef str _UPDATE_ARGS_CONTEXT_KW cdef inline _get_update_signature(updater) -cdef dict _upsable_updateFromExternalObject_cache +cdef dict _usable_updateFromExternalObject_cache cdef _obj_has_usable_updateFromExternalObject(obj) diff --git a/src/nti/externalization/internalization/events.py b/src/nti/externalization/internalization/events.py index 2ffa245..17a7046 100644 --- a/src/nti/externalization/internalization/events.py +++ b/src/nti/externalization/internalization/events.py @@ -5,21 +5,18 @@ """ +from collections.abc import Collection +from collections.abc import Sequence +from typing import Any -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - - +from zope.event import notify as _zope_event_notify from zope.interface import classImplements from zope.interface import providedBy -from zope.event import notify as _zope_event_notify from zope.lifecycleevent import IAttributes -from nti.externalization.interfaces import ObjectModifiedFromExternalEvent from nti.externalization._interface_cache import cache_for_key_in_providedBy +from nti.externalization.interfaces import ObjectModifiedFromExternalEvent -logger = __import__('logging').getLogger(__name__) __all__ = [ 'notifyModified', @@ -42,7 +39,8 @@ def __init__(self, iface): classImplements(_Attributes, IAttributes) -def _make_modified_attributes(containedObject, external_keys): +def _make_modified_attributes(containedObject, + external_keys:Sequence[str]) -> Collection[_Attributes]: # Returns a sequence of fresh _Attributes objects, # one for each distinct interface (including None) that declared # any key found in *external_keys*. @@ -61,7 +59,7 @@ def _make_modified_attributes(containedObject, external_keys): # {iface -> _Attributes(iface)}. Note that iface will be None if there # is no interface that defined the key. - result = {} + result: dict[Any, _Attributes]= {} provides_get = provides.get @@ -158,5 +156,7 @@ def notifyModified(containedObject, externalObject, updater=None, external_keys= kwargs) -from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position,wrong-import-order +from nti.externalization._compat import \ + import_c_accel # pylint:disable=wrong-import-position,wrong-import-order + import_c_accel(globals(), 'nti.externalization.internalization._events') diff --git a/src/nti/externalization/internalization/externals.py b/src/nti/externalization/internalization/externals.py index dbf8d81..6bdeaaf 100644 --- a/src/nti/externalization/internalization/externals.py +++ b/src/nti/externalization/internalization/externals.py @@ -5,28 +5,13 @@ objects. """ - - - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - - -# stdlib imports -try: - from collections.abc import MutableSequence -except ImportError: # Python 2 - # pylint:disable=deprecated-class - from collections import MutableSequence +from collections.abc import MutableSequence from zope import component - from nti.externalization.interfaces import IExternalReferenceResolver - __all__ = [ 'resolve_externals', ] @@ -81,5 +66,5 @@ def resolve_externals(object_io, updating_object, externalObject, -from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position,wrong-import-order +from nti.externalization._compat import import_c_accel import_c_accel(globals(), 'nti.externalization.internalization._externals') diff --git a/src/nti/externalization/internalization/factories.py b/src/nti/externalization/internalization/factories.py index 8573b66..c55fc03 100644 --- a/src/nti/externalization/internalization/factories.py +++ b/src/nti/externalization/internalization/factories.py @@ -11,32 +11,25 @@ # There are a *lot* of fixme (XXX and the like) in this file. # Turn those off in general so we can see through the noise. # pylint:disable=fixme - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import warnings +import logging +from collections.abc import Callable from zope import component from zope import interface -from nti.externalization._base_interfaces import NotGiven from nti.externalization.interfaces import IClassObjectFactory from nti.externalization.interfaces import IExternalizedObjectFactoryFinder from nti.externalization.interfaces import IFactory from nti.externalization.interfaces import IMimeObjectFactory -from .legacy_factories import search_for_external_factory - from .._base_interfaces import get_standard_external_fields - +from .legacy_factories import search_for_external_factory StandardExternalFields = get_standard_external_fields() component_queryAdapter = component.queryAdapter component_queryUtility = component.queryUtility -logger = __import__('logging').getLogger(__name__) +logger = logging.getLogger(__name__) __all__ = [ 'default_externalized_object_factory_finder', @@ -131,7 +124,7 @@ def default_externalized_object_factory_finder_factory(unused_externalized_objec return default_externalized_object_factory_finder -def find_factory_for_class_name(class_name): +def find_factory_for_class_name(class_name:str) -> Callable|None: factory = component_queryUtility(IClassObjectFactory, class_name) if factory is None: factory = search_for_external_factory(class_name) @@ -141,7 +134,7 @@ def find_factory_for_class_name(class_name): return factory -def find_factory_for(externalized_object, registry=NotGiven): +def find_factory_for(externalized_object) -> Callable|None: """ find_factory_for(externalized_object) -> factory @@ -163,17 +156,16 @@ def find_factory_for(externalized_object, registry=NotGiven): .. versionchanged:: 1.0a10 The ``registry`` argument is deprecated and ignored. + + .. versionchanged:: NEXT + Remove the registry argument. """ - if registry is not NotGiven: # pragma: no cover - warnings.warn( - "The registry argument is deprecated and ignored", - FutureWarning - ) factory_finder = IExternalizedObjectFactoryFinder(externalized_object, None) if factory_finder is not None: - return factory_finder.find_factory(externalized_object) # pylint:disable=too-many-function-args + # pylint:disable-next=too-many-function-args + return factory_finder.find_factory(externalized_object) # type:ignore[call-arg,misc] # We do it this way instead of using # ``default_externalized_object_factory_finder`` as the default in @@ -181,5 +173,7 @@ def find_factory_for(externalized_object, registry=NotGiven): return _find_factory_for_mime_or_class(externalized_object) -from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position,wrong-import-order +from nti.externalization._compat import \ + import_c_accel # pylint:disable=wrong-import-position,wrong-import-order + import_c_accel(globals(), 'nti.externalization.internalization._factories') diff --git a/src/nti/externalization/internalization/fields.py b/src/nti/externalization/internalization/fields.py index 5229088..7b83b7b 100644 --- a/src/nti/externalization/internalization/fields.py +++ b/src/nti/externalization/internalization/fields.py @@ -6,31 +6,24 @@ """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function +from collections.abc import Callable # pylint:disable=protected-access - # stdlib imports from sys import exc_info as get_exc_info - +from zope.event import notify from zope.interface import implementedBy - +from zope.schema.fieldproperty import NO_VALUE +from zope.schema.fieldproperty import FieldProperty +from zope.schema.fieldproperty import FieldUpdatedEvent from zope.schema.interfaces import IField -from zope.schema.interfaces import SchemaNotProvided from zope.schema.interfaces import SchemaNotCorrectlyImplemented +from zope.schema.interfaces import SchemaNotProvided from zope.schema.interfaces import ValidationError from zope.schema.interfaces import WrongContainedType from zope.schema.interfaces import WrongType -from zope.schema.fieldproperty import FieldProperty -from zope.schema.fieldproperty import NO_VALUE -from zope.schema.fieldproperty import FieldUpdatedEvent - -from zope.event import notify - IField_providedBy = IField.providedBy __all__ = [ @@ -116,13 +109,13 @@ def _FieldProperty__set__valid(self, inst, value): else: _FieldProperty_orig_set(self, inst, value) -_FieldProperty__set__valid.orig_func = _FieldProperty_orig_set +_FieldProperty__set__valid.orig_func = _FieldProperty_orig_set # type:ignore[attr-defined] # Detect the case that we're in Cython compiled code, where # we've already replaced the __set__ function with our own. if FieldProperty.__set__.__name__ == _FieldProperty__set__valid.__name__: # pragma: no cover _FieldProperty_orig_set = FieldProperty.__set__.orig_func # pylint:disable=no-member - _FieldProperty__set__valid.org_func = _FieldProperty_orig_set + _FieldProperty__set__valid.orig_func = _FieldProperty_orig_set # type:ignore[attr-defined] FieldProperty.__set__ = _FieldProperty__set__valid @@ -134,7 +127,7 @@ class CannotConvertSequenceError(TypeError): but we don't know how. """ -def _adapt_sequence(field, value): +def _adapt_sequence(field, value) -> list: # IObject provides `schema`, which is an interface, so we can adapt # using it. Some other things do not, for example nti.schema.field.Variant # They might provide a `fromObject` function to do the conversion @@ -174,7 +167,7 @@ def _handle_SchemaNotProvided(field_name, field, value): # pylint:disable=unused # The object doesn't implement the required interface. # Can we adapt the provided object to the desired interface? # First, capture the details so we can reraise if needed - exc_info = get_exc_info() + exc_type, exc_val, _exc_tb = get_exc_info() try: value = field.schema(value) @@ -183,24 +176,25 @@ def _handle_SchemaNotProvided(field_name, field, value): # pylint:disable=unused # 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[1] if exc_info[1] is not None else exc_info[0]() from None + raise exc_val if exc_val is not None else exc_type() from None # type:ignore[misc] return value def _handle_WrongType(field_name, field, value): # pylint:disable=unused-argument # Like SchemaNotProvided, but for a primitive type, # most commonly a date # Can we adapt? - exc_info = get_exc_info() + _exc_type, exc_value, _exc_tb = get_exc_info() + assert isinstance(exc_value, WrongType) - if not exc_info[1].expected_type: # pragma: no cover - raise exc_info[1] + if not exc_value.expected_type: # pragma: no cover + raise exc_value - exp_type = exc_info[1].expected_type + exp_type = exc_value.expected_type implemented_by_type = list(implementedBy(exp_type)) # If the type unambiguously implements an interface (one interface) # that's our target. IDate does this if len(implemented_by_type) != 1: # pragma: no cover - raise exc_info[1] + raise exc_value schema = implemented_by_type[0] @@ -209,7 +203,7 @@ def _handle_WrongType(field_name, field, value): # pylint:disable=unused-argumen result = schema(value) except (LookupError, TypeError): # No registered adapter, darn - raise exc_info[1] if exc_info[1] is not None else exc_info[0]() from None + raise exc_value from None except ValidationError as e: # Found an adapter, but it does its own validation, # and that validation failed (eg, IDate below) @@ -226,10 +220,11 @@ def _handle_WrongContainedType(field_name, field, value): # pylint:disable=unuse # types. # Try to adapt each value to what the sequence wants, just as above, # if the error is one that may be solved via simple adaptation - exc_info = get_exc_info() + _exc_type, exc_val, _exc_tb = get_exc_info() + assert isinstance(exc_val, WrongContainedType) - if not exc_info[1].errors or not _all_SchemaNotProvided(exc_info[1].errors): - raise exc_info[1] + if not exc_val.errors or not _all_SchemaNotProvided(exc_val.errors): + raise exc_val # IObject provides `schema`, which is an interface, so we can adapt # using it. Some other things do not, for example nti.schema.field.Variant @@ -244,7 +239,7 @@ def _handle_WrongContainedType(field_name, field, value): # pylint:disable=unuse # 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[1] from None + raise exc_val from None # Now try to validate the converted value try: @@ -253,7 +248,7 @@ def _handle_WrongContainedType(field_name, field, value): # pylint:disable=unuse # 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[1] from None + raise exc_val from None return value @@ -263,7 +258,7 @@ def _handle_WrongContainedType(field_name, field, value): # pylint:disable=unuse ('fromObject', object) ) -def validate_field_value(self, field_name, field, value): +def validate_field_value(self, field_name:str, field, value) -> Callable[[], None]: """ Given a :class:`zope.schema.interfaces.IField` object from a schema implemented by `self`, validates that the proposed value can be @@ -312,7 +307,7 @@ def validate_field_value(self, field_name, field, value): if value is not None: # First time through we get to set it, but we must bypass # the field - _do_set = SetattrSet(self, _as_native_str(field_name), value) + _do_set = SetattrSet(self, field_name, value) else: _do_set = noop else: @@ -321,7 +316,7 @@ def validate_field_value(self, field_name, field, value): return _do_set -def validate_named_field_value(self, iface, field_name, value): +def validate_named_field_value(self, iface, field_name:str, value): """ Given a :class:`zope.interface.Interface` and the name of one of its attributes, validate that the given ``value`` is appropriate to set. See :func:`validate_field_value` @@ -334,7 +329,6 @@ def validate_named_field_value(self, iface, field_name, value): :return: A callable of no arguments to call to actually set the value. """ - field_name = _as_native_str(field_name) field = iface[field_name] if IField_providedBy(field): # pylint:disable=no-value-for-parameter return validate_field_value(self, field_name, field, value) @@ -342,11 +336,5 @@ def validate_named_field_value(self, iface, field_name, value): return SetattrSet(self, field_name, value) -def _as_native_str(s): - if isinstance(s, str): - return s - return s.encode('ascii') # Python 2 - - -from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position,wrong-import-order +from nti.externalization._compat import import_c_accel import_c_accel(globals(), 'nti.externalization.internalization._fields') diff --git a/src/nti/externalization/internalization/legacy_factories.py b/src/nti/externalization/internalization/legacy_factories.py index 7d7e568..56bf6ae 100644 --- a/src/nti/externalization/internalization/legacy_factories.py +++ b/src/nti/externalization/internalization/legacy_factories.py @@ -5,31 +5,24 @@ .. deprecated:: 1.0 """ -## Implementation of legacy search modules. - -# We go through the global component registry, using a local -# interface. We treat the registry as a cache and we will only -# look at any given module object one time. We can detect duplicates -# in this fashion. (For cython compilation, this lives in interfaces.) - - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -# stdlib imports +import logging import types import warnings from zope import component - from zope.dottedname.resolve import resolve -from nti.externalization.interfaces import _ILegacySearchModuleFactory from nti.externalization._base_interfaces import NotGiven +from nti.externalization.interfaces import _ILegacySearchModuleFactory +## Implementation of legacy search modules. +# We go through the global component registry, using a local +# interface. We treat the registry as a cache and we will only +# look at any given module object one time. We can detect duplicates +# in this fashion. (For cython compilation, this lives in interfaces.) -logger = __import__('logging').getLogger(__name__) + +logger = logging.getLogger(__name__) __all__ = [ 'register_legacy_search_module', @@ -38,10 +31,10 @@ #: .. deprecated:: 1.0 #: This is legacy functionality, please do not access directly. #: The public interface is through :func:`register_legacy_search_module` -LEGACY_FACTORY_SEARCH_MODULES = set() +LEGACY_FACTORY_SEARCH_MODULES = set[str|types.ModuleType]() try: - from zope.testing.cleanup import addCleanUp # pylint: disable=ungrouped-imports + from zope.testing.cleanup import addCleanUp # pylint: disable=ungrouped-imports except ImportError: # pragma: no cover pass else: @@ -69,9 +62,9 @@ def register_legacy_search_module(module_name): if module_name: LEGACY_FACTORY_SEARCH_MODULES.add(module_name) -_ext_factory_warnings = set() +_ext_factory_warnings = set[str]() -def search_for_external_factory(typeName): +def search_for_external_factory(typeName:str): """ Deprecated, legacy functionality. Given the name of a type, optionally ending in 's' for plural, attempt to locate that type. @@ -201,4 +194,5 @@ def find_factories_in_module(module, # pylint:disable=wrong-import-position,wrong-import-order from nti.externalization._compat import import_c_accel + import_c_accel(globals(), 'nti.externalization.internalization._legacy_factories') diff --git a/src/nti/externalization/internalization/tests/test_externals_wo_class_mimetype.py b/src/nti/externalization/internalization/tests/test_externals_wo_class_mimetype.py index 16490e1..610e185 100644 --- a/src/nti/externalization/internalization/tests/test_externals_wo_class_mimetype.py +++ b/src/nti/externalization/internalization/tests/test_externals_wo_class_mimetype.py @@ -4,9 +4,6 @@ the conventions of this package, e.g., missing Class and MimeType values. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 @@ -14,26 +11,24 @@ import sys import unittest -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 - -from zope.schema import Object +from zope import interface +from zope.schema import Dict from zope.schema import Int from zope.schema import List -from zope.schema import Dict - +from zope.schema import Object from zope.testing.cleanup import CleanUp from nti.externalization import interfaces +from nti.externalization.datastructures import InterfaceObjectIO from nti.externalization.internalization import update_from_external_object from nti.externalization.internalization import updater -from nti.externalization.datastructures import InterfaceObjectIO + +from hamcrest import assert_that +from hamcrest import contains_string +from hamcrest import has_length +from hamcrest import has_property as has_attr +from hamcrest import is_ class TestExternals(CleanUp, diff --git a/src/nti/externalization/internalization/tests/test_fields.py b/src/nti/externalization/internalization/tests/test_fields.py index c61a34d..c6cc122 100644 --- a/src/nti/externalization/internalization/tests/test_fields.py +++ b/src/nti/externalization/internalization/tests/test_fields.py @@ -1,31 +1,25 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function import unittest from zope import interface from zope.component import eventtesting - - +from zope.schema import Object +from zope.schema.fieldproperty import createFieldProperties from zope.schema.interfaces import IBeforeObjectAssignedEvent from zope.schema.interfaces import IFieldUpdatedEvent from zope.schema.interfaces import SchemaNotProvided -from zope.schema import Object -from zope.schema.fieldproperty import createFieldProperties - from zope.testing.cleanup import CleanUp +from nti.externalization.internalization.fields import validate_field_value +from nti.externalization.internalization.fields import validate_named_field_value + from hamcrest import assert_that from hamcrest import has_length from hamcrest import has_property as has_attr from hamcrest import is_ from hamcrest import same_instance -from nti.externalization.internalization.fields import validate_named_field_value -from nti.externalization.internalization.fields import validate_field_value - # pylint:disable=inherit-non-class,blacklisted-name class IThing(interface.Interface): @@ -127,8 +121,8 @@ class Foo(object): assert_that(foo, has_attr('field', 'text')) def test_from_bytes(self): - from zope.schema.interfaces import IFromBytes from zope.schema import Field + from zope.schema.interfaces import IFromBytes @interface.implementer(IFromBytes) class OnlyBytes(Field): _type = bytes @@ -148,8 +142,8 @@ class Foo(object): def test_raises_SchemaNotCorrectlyImplemented(self): - from zope.schema.interfaces import SchemaNotCorrectlyImplemented from zope.schema import TextLine + from zope.schema.interfaces import SchemaNotCorrectlyImplemented class IFoo(interface.Interface): field = TextLine(title='text', required=True) diff --git a/src/nti/externalization/internalization/tests/test_updater.py b/src/nti/externalization/internalization/tests/test_updater.py index ae84070..8f856c0 100644 --- a/src/nti/externalization/internalization/tests/test_updater.py +++ b/src/nti/externalization/internalization/tests/test_updater.py @@ -3,30 +3,25 @@ Tests for updater.py """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 import unittest -from zope import interface from zope import component - +from zope import interface from zope.testing.cleanup import CleanUp +from nti.externalization import interfaces +from nti.externalization.internalization import updater + from hamcrest import assert_that -from hamcrest import has_property from hamcrest import has_length +from hamcrest import has_property from hamcrest import is_ from hamcrest import same_instance -from nti.externalization import interfaces - -from nti.externalization.internalization import updater class CreateCount(object): created = 0 diff --git a/src/nti/externalization/internalization/updater.py b/src/nti/externalization/internalization/updater.py index 244c221..21d053d 100644 --- a/src/nti/externalization/internalization/updater.py +++ b/src/nti/externalization/internalization/updater.py @@ -5,40 +5,37 @@ """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - - -# stdlib imports -from collections.abc import MutableSequence -from collections.abc import MutableMapping import inspect import warnings +from collections.abc import MutableMapping +from collections.abc import MutableSequence +from typing import TypeVar +from typing import overload from zope import interface + try: from persistent.interfaces import IPersistent except ModuleNotFoundError: - class IPersistent(interface.Interface): # pylint: disable=inherit-non-class + # pylint: disable-next=inherit-non-class + class IPersistent(interface.Interface): # type:ignore[no-redef] """Mock""" from zope.event import notify as notify_event from nti.externalization._base_interfaces import PRIMITIVES -from nti.externalization._base_interfaces import NotGiven -from nti.externalization.interfaces import IInternalObjectUpdater from nti.externalization.interfaces import IInternalObjectIO +from nti.externalization.interfaces import IInternalObjectUpdater from nti.externalization.interfaces import INamedExternalizedObjectFactoryFinder from nti.externalization.interfaces import ObjectWillUpdateFromExternalEvent -from .factories import find_factory_for from .events import _notifyModified from .externals import resolve_externals +from .factories import find_factory_for +T = TypeVar('T') - -_EMPTY_DICT = {} +_EMPTY_DICT: dict = {} IPersistent_providedBy = IPersistent.providedBy @@ -47,7 +44,6 @@ class _RecallArgs(object): 'context', 'require_updater', 'notify', - 'pre_hook', 'root', ) @@ -59,7 +55,6 @@ def __init__(self): self.context = None self.require_updater = False self.notify = True - self.pre_hook = None self.root = None ## @@ -74,7 +69,7 @@ def __init__(self): # cumbersome and needs to go; we are in the deprecation period now. # See https://github.com/NextThought/nti.externalization/issues/30 -_argspec_cache = {} +_argspec_cache: dict[type, str] = {} # update(ext, context) or update(ext, context=None) or update(ext, dataserver) # exactly two arguments. It doesn't matter what the name is, we'll call it @@ -84,7 +79,7 @@ def __init__(self): _UPDATE_ARGS_ONE = "update args external only" -def _get_update_signature(updater): +def _get_update_signature(updater) -> str: kind = type(updater) spec = _argspec_cache.get(kind) @@ -132,9 +127,9 @@ def _get_update_signature(updater): return spec -_usable_updateFromExternalObject_cache = {} +_usable_updateFromExternalObject_cache: dict[type, bool] = {} -def _obj_has_usable_updateFromExternalObject(obj): +def _obj_has_usable_updateFromExternalObject(obj) -> bool: kind = type(obj) usable_from = _usable_updateFromExternalObject_cache.get(kind) @@ -157,7 +152,7 @@ def _obj_has_usable_updateFromExternalObject(obj): try: - from zope.testing import cleanup # pylint:disable=ungrouped-imports + from zope.testing import cleanup # pylint:disable=ungrouped-imports except ImportError: # pragma: no cover pass else: @@ -175,12 +170,31 @@ def find_factory_for_named_value(self, name, value): # pylint:disable=unused-arg _default_factory_finder = DefaultInternalObjectFactoryFinder() -def update_from_external_object(containedObject, externalObject, - registry=NotGiven, context=None, +@overload +def update_from_external_object(containedObject: MutableSequence, + externalObject: MutableSequence, + context=None, require_updater=False, - notify=True, - pre_hook=None): - # pylint:disable=line-too-long, too-many-positional-arguments + notify=True) -> MutableSequence: + # pylint:disable=too-many-positional-arguments + ... + +@overload +def update_from_external_object(containedObject: T, + externalObject: MutableMapping, + context=None, + require_updater=False, + notify=True) -> T: + # pylint:disable=too-many-positional-arguments + ... + +def update_from_external_object(containedObject: T|MutableSequence, + externalObject: MutableSequence|MutableMapping, + context=None, + require_updater=False, + notify=True) -> T|MutableSequence: + # pylint:disable=line-too-long + # pylint:disable=too-many-positional-arguments """ update_from_external_object(containedObject, externalObject, context=None, require_updater=False, notify=True) @@ -208,11 +222,6 @@ def update_from_external_object(containedObject, externalObject, attribute; if this is the case and we can also find an interface declaring the attribute, then the ``IAttributes`` will have the right value for ``interface`` as well). - :keyword callable pre_hook: If given, called with the before - update_from_external_object is called for every nested object. - Signature ``f(k,x)`` where ``k`` is either the key name, or - None in the case of a sequence and ``x`` is the external - object. Deprecated. :return: *containedObject* after updates from *externalObject* Notifies `~.IObjectModifiedFromExternalEvent` for each object that is modified, @@ -223,25 +232,17 @@ def update_from_external_object(containedObject, externalObject, .. versionchanged:: 1.0.0a2 Remove the ``object_hook`` parameter. .. versionchanged:: 1.1.3 - Correctly file `~.IObjectWillUpdateFromExternalEvent` before updating + Correctly fire `~.IObjectWillUpdateFromExternalEvent` before updating each object. + .. versionchanged:: NEXT + Remove the long-deprecated 'pre_hook' parameter. Remove the long-deprecated + 'registry' parameter. """ - if pre_hook is not None: # pragma: no cover - for i in range(3): - warnings.warn('pre_hook is deprecated', FutureWarning, stacklevel=i) - - if registry is not NotGiven: # pragma: no cover - warnings.warn( - "registry is deprecated and ignored. Call in a correct site.", - FutureWarning - ) - kwargs = _RecallArgs() kwargs.context = context kwargs.require_updater = require_updater kwargs.notify = notify - kwargs.pre_hook = pre_hook kwargs.root = containedObject return _update_from_external_object(containedObject, externalObject, kwargs) @@ -255,13 +256,13 @@ def _invoke_factory(factory, value): return factory() def _update_sequence( - externalObject, args, + externalObject: MutableSequence, + args: _RecallArgs, destination_name=None, - find_factory_for_named_value=_default_factory_finder.find_factory_for_named_value): + find_factory_for_named_value=_default_factory_finder.find_factory_for_named_value +) -> MutableSequence: for index, value in enumerate(externalObject): - if args.pre_hook is not None: # pragma: no cover - args.pre_hook(None, value) factory = find_factory_for_named_value(destination_name, value) if factory is not None: new_obj = _invoke_factory(factory, value) @@ -319,7 +320,9 @@ def _find_INamedExternalizedObjectFactoryFinder(containedObject): return updater -def _update_from_external_object(containedObject, externalObject, args): +def _update_from_external_object(containedObject: T, + externalObject: MutableSequence|MutableMapping, + args: _RecallArgs) -> T|MutableSequence: # Parse any contained objects # TODO: We're (deliberately?) not actually updating any contained @@ -331,7 +334,7 @@ def _update_from_external_object(containedObject, externalObject, args): if IPersistent_providedBy(containedObject): # pylint:disable=no-value-for-parameter # pylint:disable=protected-access - containedObject._v_updated_from_external_source = externalObject + containedObject._v_updated_from_external_source = externalObject # type:ignore[attr-defined] # Sequences do not represent python types, they represent collections of @@ -353,9 +356,6 @@ def _update_from_external_object(containedObject, externalObject, args): if isinstance(v, PRIMITIVES): continue - if args.pre_hook is not None: # pragma: no cover - args.pre_hook(k, v) - if isinstance(v, MutableSequence): # Update the sequence in-place _update_sequence(v, args, k, find_factory_for_named_value) @@ -390,5 +390,5 @@ def _update_from_external_object(containedObject, externalObject, args): -from nti.externalization._compat import import_c_accel # pylint:disable=wrong-import-position,wrong-import-order +from nti.externalization._compat import import_c_accel import_c_accel(globals(), 'nti.externalization.internalization._updater') diff --git a/src/nti/externalization/numbers.py b/src/nti/externalization/numbers.py index e58126f..8e0f9bd 100644 --- a/src/nti/externalization/numbers.py +++ b/src/nti/externalization/numbers.py @@ -3,10 +3,6 @@ Support for externalizing arbitrary numbers. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import fractions import decimal diff --git a/src/nti/externalization/oids.py b/src/nti/externalization/oids.py index 688cbcc..1cfed23 100644 --- a/src/nti/externalization/oids.py +++ b/src/nti/externalization/oids.py @@ -4,13 +4,10 @@ Functions for finding and parsing OIDs. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - # stdlib imports import binascii import collections +from collections.abc import Sequence try: from ZODB.interfaces import IConnection @@ -23,11 +20,11 @@ def IConnection(_): from zope.intid.interfaces import IIntIds except ModuleNotFoundError: from zope.interface import Interface - class IIntIds(Interface): # pylint: disable=inherit-non-class + # pylint: disable-next=inherit-non-class + class IIntIds(Interface): # type:ignore[no-redef] """Mock""" from nti.externalization._compat import bytes_ - from nti.externalization.integer_strings import from_external_string from nti.externalization.integer_strings import to_external_string from nti.externalization.proxy import removeAllProxies @@ -121,8 +118,9 @@ def to_external_oid(self, default=None, add_to_connection=False, pass if jar: - db_name = jar.db().database_name + db_name:str = jar.db().database_name oid = oid + b':' + binascii.hexlify(bytes_(db_name)) + intutility = component.queryUtility(IIntIds) if intutility is not None: intid = intutility.queryId(self) @@ -175,7 +173,7 @@ def from_external_oid(ext_oid): return ParsedOID(ext_oid, '', None) ext_oid = ext_oid.encode("ascii") if not isinstance(ext_oid, bytes) else ext_oid - parts = ext_oid.split(b':') if b':' in ext_oid else (ext_oid,) + parts:Sequence[bytes] = ext_oid.split(b':') if b':' in ext_oid else (ext_oid,) oid_string = parts[0] name_s = parts[1] if len(parts) > 1 else b"" intid_s = parts[2] if len(parts) > 2 else None diff --git a/src/nti/externalization/persistence.py b/src/nti/externalization/persistence.py index a74ea76..ce44def 100644 --- a/src/nti/externalization/persistence.py +++ b/src/nti/externalization/persistence.py @@ -12,10 +12,6 @@ """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - # stdlib imports from collections.abc import Sequence izip = zip @@ -33,12 +29,12 @@ assert ex.name == 'persistent' UPTODATE = None CHANGED = 'Fake Changed' - class Persistent: + class Persistent: # type:ignore[no-redef] """Mock""" PersistentList = list PersistentMapping = dict from weakref import ref - class PWeakRef(ref): + class PWeakRef(ref): # type:ignore[no-redef] __slots__ = () from zope import interface @@ -231,7 +227,7 @@ def __mul__(self, n): return self.__class__(plain) - __hash__ = None + __hash__ = None # type:ignore[assignment] def __wrap(self, obj): return obj if isinstance(obj, PWeakRef) else PWeakRef(obj) diff --git a/src/nti/externalization/proxy.py b/src/nti/externalization/proxy.py index bdb61ae..a5a1b28 100644 --- a/src/nti/externalization/proxy.py +++ b/src/nti/externalization/proxy.py @@ -11,9 +11,8 @@ It is extensible with `registerProxyUnwrapper`. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function +from collections.abc import Callable +from typing import Any from zope.dottedname import resolve as dottedname @@ -22,7 +21,7 @@ 'registerProxyUnwrapper', ] -_unwrappers = [] +_unwrappers: list[Callable[[Any], Any]] = [] def _init_unwrappers(): del _unwrappers[:] diff --git a/src/nti/externalization/py.typed b/src/nti/externalization/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/nti/externalization/representation.py b/src/nti/externalization/representation.py index de77958..3121da3 100644 --- a/src/nti/externalization/representation.py +++ b/src/nti/externalization/representation.py @@ -9,17 +9,17 @@ `YAML <.EXT_REPR_YAML>`. """ import decimal -import warnings +from typing import cast try: from persistent import Persistent except ModuleNotFoundError: - class Persistent: + class Persistent: # type:ignore[no-redef] """Mock""" - class POSError(Exception): + class POSError(Exception): # type:ignore[no-redef] """Mock""" else: - from ZODB.POSException import POSError + from ZODB.POSException import POSError # type:ignore[no-redef] import orjson import yaml from zope import component @@ -51,7 +51,7 @@ def _to_external_representation(obj, io, name=_NotGiven, return io.dump(ext, **repr_kwargs) def to_external_representation(obj, ext_format=EXT_REPR_JSON, - name=_NotGiven, registry=_NotGiven, + name=_NotGiven, **repr_kwargs) -> str|bytes: """ to_external_representation(obj, ext_format='json', name=NotGiven, **repr_kwargs) -> str|bytes @@ -72,12 +72,10 @@ def to_external_representation(obj, ext_format=EXT_REPR_JSON, .. versionchanged:: 3.0.0 Added *repr_kwargs* + .. versionchanged:: NEXT + Removed the deprecated 'registry' param """ - if registry is not _NotGiven: # pragma: no cover - warnings.warn( - "The registry argument is ignored. Call in a correct site.", - FutureWarning - ) + # It would seem nice to be able to do this in one step during # the externalization process itself, but we would wind up traversing # parts of the datastructure more than necessary. Here we traverse @@ -95,7 +93,7 @@ def to_json_representation(obj) -> str: A convenience function that calls :func:`to_external_representation` with `.EXT_REPR_JSON`. """ - return to_external_representation(obj, EXT_REPR_JSON) + return cast(str, to_external_representation(obj, EXT_REPR_JSON)) def to_json_representation_fast(obj) -> bytes: """ @@ -111,8 +109,8 @@ def to_json_representation_fast(obj) -> bytes: Now properly externalizes the object instead of relying on the second-chance externalization mechanism. """ - return _to_external_representation(obj, JsonRepresenter, - sort_keys=False, as_str=False) + return cast(bytes, _to_external_representation(obj, JsonRepresenter, + sort_keys=False, as_str=False)) def to_json_representation_sorted(obj) -> str: """ @@ -125,8 +123,8 @@ def to_json_representation_sorted(obj) -> str: .. versionadded:: NEXT """ - return _to_external_representation(obj, JsonRepresenter, - sort_keys=True) + return cast(str, _to_external_representation(obj, JsonRepresenter, + sort_keys=True)) # JSON @@ -180,7 +178,7 @@ def dump(obj, fp=None, sort_keys=False, as_str=True, **_unused) -> str|bytes: option=orjson.OPT_SORT_KEYS if sort_keys else 0, default=_second_pass_to_external_object) if as_str: - result = result.decode('utf-8') + result = result.decode('utf-8') # type:ignore[assignment] if fp: return fp.write(result) return result @@ -212,11 +210,7 @@ class _ExtDumper(yaml.SafeDumper): # requires an exact type match. _ExtDumper.add_multi_representer(list, _ExtDumper.represent_list) _ExtDumper.add_multi_representer(dict, _ExtDumper.represent_dict) -if str is bytes: # Python 2 - # pylint:disable=undefined-variable,no-member - _ExtDumper.add_multi_representer(unicode, _ExtDumper.represent_unicode) -else: # Python 3 - _ExtDumper.add_multi_representer(str, _ExtDumper.represent_str) +_ExtDumper.add_multi_representer(str, _ExtDumper.represent_str) def _yaml_represent_decimal(dumper, data): s = str(data) @@ -241,7 +235,7 @@ def _yaml_represent_decimal(dumper, data): def _yaml_represent_unknown(dumper, data): ext_obj = _second_pass_to_external_object(data) return dumper.represent_data(ext_obj) -_ExtDumper.add_multi_representer(None, _yaml_represent_unknown) +_ExtDumper.add_multi_representer(None, _yaml_represent_unknown) # type:ignore[arg-type] diff --git a/src/nti/externalization/singleton.py b/src/nti/externalization/singleton.py index db559bb..f59739a 100644 --- a/src/nti/externalization/singleton.py +++ b/src/nti/externalization/singleton.py @@ -21,10 +21,6 @@ a constant object. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - # This was originally based on code from sympy.core.singleton __all__ = [ @@ -82,6 +78,8 @@ class is instantiated. False """ + _new_instance: staticmethod + def __new__(mcs, name, bases, cls_dict): # pylint:disable=bad-mcs-classmethod-argument cls_dict['__slots__'] = () # no ivars cls_dict['__init__'] = lambda *args: None @@ -98,7 +96,7 @@ def __new__(mcs, name, bases, cls_dict): # pylint:disable=bad-mcs-classmethod-ar ctor = cls.__new__ cls._new_instance = staticmethod(ctor) - the_instance = ctor(cls) + the_instance = ctor(cls) # type: ignore[call-arg,arg-type] cls.__new__ = staticmethod(lambda *args: the_instance) @@ -106,15 +104,14 @@ def __new__(mcs, name, bases, cls_dict): # pylint:disable=bad-mcs-classmethod-ar SingletonDecorator = SingletonMetaclass # BWC -Singleton = SingletonMetaclass( - 'Singleton', (object,), - { - '__doc__': - "A base class for singletons. " - "Can be more convenient than a metaclass for Python2/Python3 compatibility." - } -) +class Singleton(metaclass=SingletonMetaclass): + """ + A base class for singletons. + + Can be more convenient than using the metaclass. + """ + from nti.externalization._compat import import_c_accel import_c_accel(globals(), 'nti.externalization._singleton') diff --git a/src/nti/externalization/testing.py b/src/nti/externalization/testing.py index 65aaaad..5232796 100644 --- a/src/nti/externalization/testing.py +++ b/src/nti/externalization/testing.py @@ -3,10 +3,6 @@ Support for testing code. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - # stdlib imports import pickle diff --git a/src/nti/externalization/tests/__init__.py b/src/nti/externalization/tests/__init__.py index 5959b38..bf38ba7 100644 --- a/src/nti/externalization/tests/__init__.py +++ b/src/nti/externalization/tests/__init__.py @@ -1,23 +1,19 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import unittest import zope.testing.cleanup import nti.testing.base -from nti.testing.layers import ConfiguringLayerMixin -from nti.testing.layers import ZopeComponentLayer - # BWC exports from nti.externalization.testing import Externalizes -from nti.externalization.testing import externalizes from nti.externalization.testing import assert_does_not_pickle +from nti.externalization.testing import externalizes +from nti.testing.layers import ConfiguringLayerMixin +from nti.testing.layers import ZopeComponentLayer + Externalizes = Externalizes externalizes = externalizes assert_does_not_pickle = assert_does_not_pickle diff --git a/src/nti/externalization/tests/benchmarks/__init__.py b/src/nti/externalization/tests/benchmarks/__init__.py index 9796905..80a7371 100644 --- a/src/nti/externalization/tests/benchmarks/__init__.py +++ b/src/nti/externalization/tests/benchmarks/__init__.py @@ -9,6 +9,3 @@ `_. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function diff --git a/src/nti/externalization/tests/benchmarks/__main__.py b/src/nti/externalization/tests/benchmarks/__main__.py index 43d0a83..e83d090 100644 --- a/src/nti/externalization/tests/benchmarks/__main__.py +++ b/src/nti/externalization/tests/benchmarks/__main__.py @@ -3,17 +3,15 @@ Main file to run when the package is specified. Primary CLI. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function import glob import os import os.path -import pyperf as perf from zope.dottedname import resolve as dottedname +import pyperf as perf + here = os.path.dirname(__file__) def find_benchmarks(): diff --git a/src/nti/externalization/tests/benchmarks/bm_simple_iface.py b/src/nti/externalization/tests/benchmarks/bm_simple_iface.py index d393156..501916c 100644 --- a/src/nti/externalization/tests/benchmarks/bm_simple_iface.py +++ b/src/nti/externalization/tests/benchmarks/bm_simple_iface.py @@ -4,27 +4,22 @@ a single text field. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys -import pyperf -from pyperf import perf_counter - from zope.configuration import xmlconfig -from nti.externalization.representation import JsonRepresenter +import nti.externalization.tests.benchmarks from nti.externalization.externalization import toExternalObject from nti.externalization.interfaces import StandardExternalFields from nti.externalization.internalization import default_externalized_object_factory_finder from nti.externalization.internalization import update_from_external_object - -import nti.externalization.tests.benchmarks +from nti.externalization.representation import JsonRepresenter from nti.externalization.tests.benchmarks.objects import DerivedWithOneTextField +import pyperf +from pyperf import perf_counter + INNER_LOOPS = 100 def to_external_object_time_func(loops, obj): @@ -59,8 +54,8 @@ def update_from_external_object_time_func(loops, ext): def profile(loops=1000, obj=None): - from cProfile import Profile import pstats + from cProfile import Profile if obj is None: obj = DerivedWithOneTextField() @@ -85,7 +80,7 @@ def profile(loops=1000, obj=None): stats.print_stats(20) def vmprofile(loops=1000, obj=None): - import vmprof # pylint:disable=import-error + import vmprof # pylint:disable=import-error if obj is None: obj = DerivedWithOneTextField() diff --git a/src/nti/externalization/tests/benchmarks/bm_simple_iface_list.py b/src/nti/externalization/tests/benchmarks/bm_simple_iface_list.py index b217fdb..6e67c8b 100644 --- a/src/nti/externalization/tests/benchmarks/bm_simple_iface_list.py +++ b/src/nti/externalization/tests/benchmarks/bm_simple_iface_list.py @@ -3,35 +3,24 @@ Extends bm_simple_iface to add a list. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function import sys -import pyperf as perf - - from zope.configuration import xmlconfig -from nti.externalization._compat import PYPY +import nti.externalization.tests.benchmarks from nti.externalization.externalization import toExternalObject from nti.externalization.interfaces import StandardExternalFields - -import nti.externalization.tests.benchmarks - +from nti.externalization.tests.benchmarks.bm_simple_iface import INNER_LOOPS +from nti.externalization.tests.benchmarks.bm_simple_iface import profile +from nti.externalization.tests.benchmarks.bm_simple_iface import to_external_object_time_func +from nti.externalization.tests.benchmarks.bm_simple_iface import \ + update_from_external_object_time_func +from nti.externalization.tests.benchmarks.bm_simple_iface import vmprofile from nti.externalization.tests.benchmarks.objects import DerivedWithOneTextField from nti.externalization.tests.benchmarks.objects import HasListOfDerived -from nti.externalization.tests.benchmarks.bm_simple_iface import ( - INNER_LOOPS, - to_external_object_time_func, - update_from_external_object_time_func, - profile, - vmprofile, -) - -logger = __import__('logging').getLogger(__name__) +import pyperf as perf def main(runner=None): @@ -47,7 +36,7 @@ def main(runner=None): return if '--vmprofile' in sys.argv: - vmprofile(100 if not PYPY else 1, obj) + vmprofile(100, obj) return mt = getattr(obj, 'mimeType') diff --git a/src/nti/externalization/tests/benchmarks/bm_simple_registered_class.py b/src/nti/externalization/tests/benchmarks/bm_simple_registered_class.py index 2959f5e..8c78fea 100644 --- a/src/nti/externalization/tests/benchmarks/bm_simple_registered_class.py +++ b/src/nti/externalization/tests/benchmarks/bm_simple_registered_class.py @@ -3,22 +3,20 @@ Benchmark for a simple registered Class factory object. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function import warnings -import pyperf as perf from zope.configuration import xmlconfig +import nti.externalization.tests.benchmarks from nti.externalization.externalization import toExternalObject from nti.externalization.interfaces import StandardExternalFields from nti.externalization.internalization import default_externalized_object_factory_finder from nti.externalization.internalization import update_from_external_object -import nti.externalization.tests.benchmarks from nti.externalization.tests.benchmarks.objects import SimplestPossibleObject +import pyperf as perf + # pylint:disable=arguments-renamed,unused-argument # pylint:disable=arguments-differ diff --git a/src/nti/externalization/tests/benchmarks/bm_singleton.py b/src/nti/externalization/tests/benchmarks/bm_singleton.py index 3100d29..a116def 100644 --- a/src/nti/externalization/tests/benchmarks/bm_singleton.py +++ b/src/nti/externalization/tests/benchmarks/bm_singleton.py @@ -3,13 +3,11 @@ Benchmark for creating singleton objects. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function + +from nti.externalization.singleton import Singleton import pyperf as perf -from nti.externalization.singleton import Singleton # These are defined to have __slots__ = () class SingletonSubclass(Singleton): diff --git a/src/nti/externalization/tests/benchmarks/bm_user_profile.py b/src/nti/externalization/tests/benchmarks/bm_user_profile.py index e26e06d..7493fa6 100644 --- a/src/nti/externalization/tests/benchmarks/bm_user_profile.py +++ b/src/nti/externalization/tests/benchmarks/bm_user_profile.py @@ -3,9 +3,6 @@ Benchmarks a realistic user profile object. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function import sys @@ -28,8 +25,6 @@ profile ) -logger = __import__('logging').getLogger(__name__) - def main(runner=None): diff --git a/src/nti/externalization/tests/benchmarks/bootstrapinterfaces.py b/src/nti/externalization/tests/benchmarks/bootstrapinterfaces.py index 5265f94..ff2e336 100644 --- a/src/nti/externalization/tests/benchmarks/bootstrapinterfaces.py +++ b/src/nti/externalization/tests/benchmarks/bootstrapinterfaces.py @@ -3,15 +3,10 @@ Interfaces for objects we will benchmark with. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function import re from zope.interface import Interface - - from zope.schema.interfaces import InvalidValue # pylint:disable=inherit-non-class diff --git a/src/nti/externalization/tests/benchmarks/interfaces.py b/src/nti/externalization/tests/benchmarks/interfaces.py index cb90057..01d48c6 100644 --- a/src/nti/externalization/tests/benchmarks/interfaces.py +++ b/src/nti/externalization/tests/benchmarks/interfaces.py @@ -3,26 +3,21 @@ Interfaces for objects we will benchmark with. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function from zope.interface import Interface from zope.interface import taggedValue - from zope.schema import List from zope.schema import Object -from nti.schema.field import TextLine - from nti.externalization.tests.benchmarks.bootstrapinterfaces import IRootInterface -from nti.externalization.tests.benchmarks.profileinterfaces import IFriendlyNamed +from nti.externalization.tests.benchmarks.profileinterfaces import IAddress from nti.externalization.tests.benchmarks.profileinterfaces import IAvatarURL from nti.externalization.tests.benchmarks.profileinterfaces import IBackgroundURL +from nti.externalization.tests.benchmarks.profileinterfaces import IFriendlyNamed from nti.externalization.tests.benchmarks.profileinterfaces import IProfileAvatarURL -from nti.externalization.tests.benchmarks.profileinterfaces import IAddress from nti.externalization.tests.benchmarks.profileinterfaces import IUserContactProfile from nti.externalization.tests.benchmarks.profileinterfaces import IUserProfile +from nti.schema.field import TextLine __all__ = [ 'IRootInterface', diff --git a/src/nti/externalization/tests/benchmarks/objects.py b/src/nti/externalization/tests/benchmarks/objects.py index 948b1ea..8fea439 100644 --- a/src/nti/externalization/tests/benchmarks/objects.py +++ b/src/nti/externalization/tests/benchmarks/objects.py @@ -3,20 +3,15 @@ Implementations of interfaces. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function from zope import interface - from zope.schema.fieldproperty import createFieldProperties from nti.externalization.datastructures import ExternalizableInstanceDict from nti.externalization.representation import WithRepr - +from nti.schema.eqhash import EqHash from nti.schema.fieldproperty import createDirectFieldProperties from nti.schema.schema import SchemaConfigured -from nti.schema.eqhash import EqHash from . import interfaces @@ -36,6 +31,7 @@ def __init__(self, text=''): @interface.implementer(interfaces.IHasListOfDerived) class HasListOfDerived(object): + the_objects: list createDirectFieldProperties(interfaces.IHasListOfDerived) diff --git a/src/nti/externalization/tests/benchmarks/profileinterfaces.py b/src/nti/externalization/tests/benchmarks/profileinterfaces.py index 521fa51..49783c7 100644 --- a/src/nti/externalization/tests/benchmarks/profileinterfaces.py +++ b/src/nti/externalization/tests/benchmarks/profileinterfaces.py @@ -2,24 +2,20 @@ """ A rich user profile. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function from zope.interface import Interface from zope.interface import taggedValue - -from zope.schema import Object from zope.schema import URI +from zope.schema import Object +from nti.externalization.tests.benchmarks.bootstrapinterfaces import IRootInterface +from nti.externalization.tests.benchmarks.bootstrapinterfaces import checkEmailAddress +from nti.externalization.tests.benchmarks.bootstrapinterfaces import checkRealname +from nti.schema.field import DecodingValidTextLine from nti.schema.field import Dict from nti.schema.field import TextLine from nti.schema.field import ValidTextLine -from nti.schema.field import DecodingValidTextLine -from nti.externalization.tests.benchmarks.bootstrapinterfaces import IRootInterface -from nti.externalization.tests.benchmarks.bootstrapinterfaces import checkEmailAddress -from nti.externalization.tests.benchmarks.bootstrapinterfaces import checkRealname # pylint:disable=inherit-non-class class IFriendlyNamed(Interface): diff --git a/src/nti/externalization/tests/test__compat.py b/src/nti/externalization/tests/test__compat.py index cf8e915..145cb92 100644 --- a/src/nti/externalization/tests/test__compat.py +++ b/src/nti/externalization/tests/test__compat.py @@ -3,11 +3,6 @@ Tests for _compat.py """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - - # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 diff --git a/src/nti/externalization/tests/test_autopackage.py b/src/nti/externalization/tests/test_autopackage.py index 066ca4d..c4db68e 100644 --- a/src/nti/externalization/tests/test_autopackage.py +++ b/src/nti/externalization/tests/test_autopackage.py @@ -3,11 +3,7 @@ Tests for autopackage.py """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import unittest from zope import interface @@ -15,15 +11,15 @@ from nti.testing.matchers import implements -from ..autopackage import AutoPackageSearchingScopedInterfaceObjectIO as AutoPackage - from hamcrest import assert_that +from hamcrest import contains_string from hamcrest import has_key from hamcrest import has_property from hamcrest import is_ from hamcrest import is_not as does_not from hamcrest import none -from hamcrest import contains_string + +from ..autopackage import AutoPackageSearchingScopedInterfaceObjectIO as AutoPackage # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 diff --git a/src/nti/externalization/tests/test_datastructures.py b/src/nti/externalization/tests/test_datastructures.py index 96c1ef4..a5ea890 100644 --- a/src/nti/externalization/tests/test_datastructures.py +++ b/src/nti/externalization/tests/test_datastructures.py @@ -1,11 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import sys import unittest @@ -13,17 +9,16 @@ from zope.testing.cleanup import CleanUp from nti.externalization.tests import ExternalizationLayerTest - from nti.testing.matchers import is_false from nti.testing.matchers import is_true from nti.testing.matchers import verifiably_provides from hamcrest import assert_that +from hamcrest import contains_string from hamcrest import has_property from hamcrest import is_ from hamcrest import is_not as does_not from hamcrest import none -from hamcrest import contains_string is_not = does_not @@ -54,11 +49,12 @@ def test_ext_replacement_getattr_default(self): is_(none())) def test_standard_dates_policy(self): - from ..interfaces import ExternalizationPolicy - from ..datetime import datetime_to_string from datetime import datetime as DateTime from datetime import timezone + from ..datetime_ext import datetime_to_string + from ..interfaces import ExternalizationPolicy + iso_policy = ExternalizationPolicy(use_iso8601_for_unix_timestamp=True) class X(object): @@ -769,15 +765,15 @@ def register(self, obj): class TestStandardInternalObjectExternalizer(unittest.TestCase): def test_provides(self): - from nti.externalization.interfaces import IInternalObjectExternalizer from nti.externalization.datastructures import StandardInternalObjectExternalizer + from nti.externalization.interfaces import IInternalObjectExternalizer o = StandardInternalObjectExternalizer(object()) assert_that(o, verifiably_provides(IInternalObjectExternalizer)) def test_subclass(self): - from nti.externalization.interfaces import IInternalObjectExternalizer - from nti.externalization.datastructures import StandardInternalObjectExternalizer from nti.externalization._compat import PURE_PYTHON + from nti.externalization.datastructures import StandardInternalObjectExternalizer + from nti.externalization.interfaces import IInternalObjectExternalizer class X(StandardInternalObjectExternalizer): def __init__(self, context): diff --git a/src/nti/externalization/tests/test_datetime.py b/src/nti/externalization/tests/test_datetime.py index bf082fa..349b580 100644 --- a/src/nti/externalization/tests/test_datetime.py +++ b/src/nti/externalization/tests/test_datetime.py @@ -1,27 +1,23 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports -from contextlib import contextmanager -from datetime import date -from datetime import timedelta import os import time import unittest +from contextlib import contextmanager +from datetime import date +from datetime import timedelta +from zope.configuration import xmlconfig from zope.interface.common.idatetime import IDate from zope.interface.common.idatetime import IDateTime from zope.schema.interfaces import InvalidValue -from zope.configuration import xmlconfig from zope.testing import cleanup import nti.externalization -from nti.externalization.datetime import datetime_to_string -from nti.externalization.datetime import datetime_from_string +from nti.externalization.datetime_ext import datetime_from_string +from nti.externalization.datetime_ext import datetime_to_string from nti.externalization.tests import ExternalizationLayerTest from nti.externalization.tests import externalizes @@ -124,8 +120,9 @@ def test_datetime_from_timestamp(self): class TestTzinfo(unittest.TestCase): def test_invalid_local_name_in_dst_uses_system_settings(self): + from nti.externalization.datetime_ext import _local_tzinfo + import pytz - from nti.externalization.datetime import _local_tzinfo with environ_tz(): os.environ['TZ'] = 'CST+06CDT+05,0,365' time.tzset() diff --git a/src/nti/externalization/tests/test_docs.py b/src/nti/externalization/tests/test_docs.py index 46b7912..c51edfe 100644 --- a/src/nti/externalization/tests/test_docs.py +++ b/src/nti/externalization/tests/test_docs.py @@ -3,18 +3,14 @@ Tests for the sphinx documentation using `Manuel `_. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import doctest import os.path import re import unittest -from zope.testing import renormalizing from zope.testing import cleanup +from zope.testing import renormalizing import manuel.capture import manuel.codeblock diff --git a/src/nti/externalization/tests/test_dublincore.py b/src/nti/externalization/tests/test_dublincore.py index 99f6fb6..d504a2b 100644 --- a/src/nti/externalization/tests/test_dublincore.py +++ b/src/nti/externalization/tests/test_dublincore.py @@ -3,19 +3,15 @@ Tests for dublincore.py """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import unittest -from . import ExternalizationLayerTest - from hamcrest import assert_that from hamcrest import is_ from hamcrest import same_instance +from . import ExternalizationLayerTest + # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 # pylint: disable=attribute-defined-outside-init @@ -104,13 +100,13 @@ class Original(object): class TestConfigured(ExternalizationLayerTest): def test_decorate(self): - from ..dublincore import IDCDescriptiveProperties - from ..dublincore import IDCExtended - from zope import interface from nti.externalization.externalization import decorate_external_mapping + from ..dublincore import IDCDescriptiveProperties + from ..dublincore import IDCExtended + @interface.implementer(IDCExtended, IDCDescriptiveProperties) class O(object): creators = ('abc',) diff --git a/src/nti/externalization/tests/test_externalization.py b/src/nti/externalization/tests/test_externalization.py index 6c731a0..d6dad49 100644 --- a/src/nti/externalization/tests/test_externalization.py +++ b/src/nti/externalization/tests/test_externalization.py @@ -1,55 +1,64 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import datetime import json -from numbers import Number import unittest - +from numbers import Number try: - from ZODB.broken import Broken from persistent import CHANGED from persistent import UPTODATE from zope.dublincore import interfaces as dub_interfaces + + from ZODB.broken import Broken except ModuleNotFoundError: Broken = None dub_interfaces = None from ..persistence import CHANGED from ..persistence import UPTODATE +from collections import UserDict + from zope import component from zope import interface - from zope.testing.cleanup import CleanUp from nti.externalization.externalization import stripSyntheticKeysFromExternalDictionary -from nti.testing.matchers import verifiably_provides -from nti.testing.matchers import is_true from nti.testing.matchers import is_false +from nti.testing.matchers import is_true +from nti.testing.matchers import verifiably_provides + +from hamcrest import assert_that +from hamcrest import calling +from hamcrest import contains_exactly +from hamcrest import has_entry +from hamcrest import has_items +from hamcrest import has_key +from hamcrest import has_property as has_attr +from hamcrest import is_ +from hamcrest import is_not +from hamcrest import none +from hamcrest import raises +from hamcrest import same_instance -from . import ExternalizationLayerTest from ..datastructures import ExternalizableDictionaryMixin from ..datastructures import ExternalizableInstanceDict +from ..extension_points import set_external_identifiers from ..externalization import NonExternalizableObjectError -from ..externalization.replacers import DevmodeNonExternalizableObjectReplacementFactory from ..externalization import catch_replace_action from ..externalization import choose_field from ..externalization import isSyntheticKey from ..externalization import removed_unserializable -from ..extension_points import set_external_identifiers from ..externalization import to_standard_external_dictionary from ..externalization import toExternalObject +from ..externalization.replacers import DevmodeNonExternalizableObjectReplacementFactory from ..externalization.standard_fields import get_creator from ..interfaces import EXT_REPR_JSON from ..interfaces import EXT_REPR_YAML -from ..interfaces import IInternalObjectExternalizer as IExternalObject from ..interfaces import IExternalObjectDecorator +from ..interfaces import IInternalObjectExternalizer as IExternalObject from ..interfaces import LocatedExternalDict from ..interfaces import LocatedExternalList from ..interfaces import StandardExternalFields @@ -60,22 +69,7 @@ from ..persistence import getPersistentState from ..representation import to_external_representation from ..testing import assert_does_not_pickle - -from hamcrest import assert_that -from hamcrest import calling -from hamcrest import contains_exactly -from hamcrest import has_entry -from hamcrest import has_items -from hamcrest import has_key -from hamcrest import is_ -from hamcrest import is_not -from hamcrest import none -from hamcrest import raises -from hamcrest import same_instance -from hamcrest import has_property as has_attr - -from collections import UserDict - +from . import ExternalizationLayerTest # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 @@ -276,10 +270,11 @@ class WithSystemUser(object): assert_that(result, is_({StandardExternalFields.CREATOR: WithSystemUser.user})) def test_get_creator_system_user(self): - from nti.externalization.externalization import SYSTEM_USER_NAME from zope.security.interfaces import IPrincipal from zope.security.management import system_user + from nti.externalization.externalization import SYSTEM_USER_NAME + @interface.implementer(IPrincipal) class MySystemUser(object): id = system_user.id @@ -300,8 +295,8 @@ class TestDecorators(CleanUp, unittest.TestCase): def test_decorate_external_mapping(self): - from nti.externalization.interfaces import IExternalStandardDictionaryDecorator from nti.externalization.externalization import decorate_external_mapping + from nti.externalization.interfaces import IExternalStandardDictionaryDecorator class IRequest(interface.Interface): pass @@ -417,9 +412,10 @@ def __init__(self): self.lastModified = 8675309 def to_str(ts): # pylint:disable=no-self-argument - from ..datetime import datetime_to_string from datetime import datetime as DateTime from datetime import timezone + + from ..datetime_ext import datetime_to_string return datetime_to_string(DateTime.fromtimestamp(ts, timezone.utc)).toExternalObject() created_string = to_str(X().createdTime) modified_string = to_str(X().lastModified) @@ -639,8 +635,8 @@ class X(object): def test_to_stand_dict_uses_dubcore_iso8601(self): if dub_interfaces is None: self.skipTest('zope.dublincore not installed') + from ..datetime_ext import datetime_to_string from ..interfaces import ExternalizationPolicy - from ..datetime import datetime_to_string policy = ExternalizationPolicy(use_iso8601_for_unix_timestamp=True) @interface.implementer(dub_interfaces.IDCTimes) @@ -660,9 +656,10 @@ class X(object): def test_to_stand_dict_prefers_direct_fields_iso8601(self): if dub_interfaces is None: self.skipTest('zope.dublincore not installed') - from ..interfaces import ExternalizationPolicy - from ..datetime import datetime_to_string from datetime import timezone + + from ..datetime_ext import datetime_to_string + from ..interfaces import ExternalizationPolicy policy = ExternalizationPolicy(use_iso8601_for_unix_timestamp=True) @interface.implementer(dub_interfaces.IDCTimes) @@ -901,9 +898,10 @@ def test_decorator(self): class TestDeprecatedImports(unittest.TestCase): def test_SEF(self): + import warnings + from nti.externalization import externalization from nti.externalization import interfaces - import warnings with warnings.catch_warnings(record=True): sef = getattr(externalization, 'StandardExternalFields') @@ -911,9 +909,10 @@ def test_SEF(self): assert_that(sef, is_(same_instance(interfaces.StandardExternalFields))) def test_SIF(self): + import warnings + from nti.externalization import externalization from nti.externalization import interfaces - import warnings with warnings.catch_warnings(record=True): sif = getattr(externalization, 'StandardInternalFields') diff --git a/src/nti/externalization/tests/test_factory.py b/src/nti/externalization/tests/test_factory.py index e003c32..5db9340 100644 --- a/src/nti/externalization/tests/test_factory.py +++ b/src/nti/externalization/tests/test_factory.py @@ -3,22 +3,17 @@ Tests for factory.py """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function # stdlib imports import doctest import unittest +from nti.testing.matchers import validly_provides from hamcrest import assert_that +from hamcrest import has_property from hamcrest import is_ from hamcrest import is_not -from hamcrest import has_property -from nti.testing.matchers import validly_provides - -logger = __import__('logging').getLogger(__name__) # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 diff --git a/src/nti/externalization/tests/test_integer_strings.py b/src/nti/externalization/tests/test_integer_strings.py index 9a5d46b..505d79d 100644 --- a/src/nti/externalization/tests/test_integer_strings.py +++ b/src/nti/externalization/tests/test_integer_strings.py @@ -1,11 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import sys import unittest diff --git a/src/nti/externalization/tests/test_interfaces.py b/src/nti/externalization/tests/test_interfaces.py index 8085d64..0ae5da5 100644 --- a/src/nti/externalization/tests/test_interfaces.py +++ b/src/nti/externalization/tests/test_interfaces.py @@ -3,19 +3,17 @@ Tests for interfaces.py """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 import unittest +from nti.externalization.interfaces import LocatedExternalDict + from hamcrest import assert_that from hamcrest import has_property -from nti.externalization.interfaces import LocatedExternalDict class TestLocatedExternalDict(unittest.TestCase): diff --git a/src/nti/externalization/tests/test_internalization.py b/src/nti/externalization/tests/test_internalization.py index 6cb8046..12ab0eb 100644 --- a/src/nti/externalization/tests/test_internalization.py +++ b/src/nti/externalization/tests/test_internalization.py @@ -1,11 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import unittest import warnings from unittest.mock import patch as Patch @@ -13,16 +9,11 @@ from zope import component from zope import interface from zope.interface.common.idatetime import IDate -from zope.testing.cleanup import CleanUp from zope.schema.interfaces import InvalidValue +from zope.testing.cleanup import CleanUp from nti.externalization.tests import ExternalizationLayerTest - -from .. import internalization as INT -from ..interfaces import IClassObjectFactory -from ..interfaces import IMimeObjectFactory - from hamcrest import assert_that from hamcrest import contains_exactly from hamcrest import contains_string @@ -37,6 +28,10 @@ from hamcrest import none from hamcrest import same_instance +from .. import internalization as INT +from ..interfaces import IClassObjectFactory +from ..interfaces import IMimeObjectFactory + # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 # pylint: disable=inherit-non-class @@ -55,8 +50,8 @@ class TestEvents(CleanUp, unittest.TestCase): def _doIt(self, *args, **kwargs): - from nti.externalization.internalization import notifyModified from nti.externalization.interfaces import ObjectModifiedFromExternalEvent + from nti.externalization.internalization import notifyModified with warnings.catch_warnings(record=True): event = notifyModified(*args, **kwargs) @@ -614,8 +609,8 @@ def __conform__(self, iface): # pylint:disable=bad-dunder-name def test_wrong_type_adapts(self): from zope.schema import Field - from zope.schema.interfaces import WrongType from zope.schema.interfaces import ValidationError + from zope.schema.interfaces import WrongType class Iface(interface.Interface): pass @@ -656,8 +651,8 @@ def __conform__(self, iface): # pylint:disable=bad-dunder-name assert_that(exc.exception, has_property('field', field)) def test_wrong_contained_type_object_field_adapts(self): - from zope.schema import Object from zope.schema import List + from zope.schema import Object class IThing(interface.Interface): @@ -677,9 +672,9 @@ def __conform__(self, iface): # pylint:disable=bad-dunder-name assert_that(bag, has_property('field', contains_exactly(is_(O)))) def test_wrong_contained_type_object_field_adapts_fails(self): - from zope.schema.interfaces import WrongContainedType - from zope.schema import Object from zope.schema import List + from zope.schema import Object + from zope.schema.interfaces import WrongContainedType class IThing(interface.Interface): @@ -712,8 +707,8 @@ def bind(self, _): def test_wrong_contained_type_field(self): - from zope.schema import Object from zope.schema import List + from zope.schema import Object from zope.schema.interfaces import WrongContainedType @@ -740,8 +735,8 @@ def __conform__(self, iface): # pylint:disable=bad-dunder-name assert_that(ex.errors[0], has_property('value', is_(Conforms))) def test_wrong_contained_type_value_type_fromObject(self): - from zope.schema import Object from zope.schema import List + from zope.schema import Object class IThing(interface.Interface): pass diff --git a/src/nti/externalization/tests/test_oids.py b/src/nti/externalization/tests/test_oids.py index 6cb93b5..5a4ee90 100644 --- a/src/nti/externalization/tests/test_oids.py +++ b/src/nti/externalization/tests/test_oids.py @@ -4,21 +4,17 @@ Tests for oids.py. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import unittest from zope.testing.cleanup import CleanUp -from ..oids import fromExternalOID -from ..oids import toExternalOID - from hamcrest import assert_that from hamcrest import is_ +from ..oids import fromExternalOID +from ..oids import toExternalOID + class TestToExternalOID(CleanUp, unittest.TestCase): @@ -53,8 +49,8 @@ class Persistent(object): assert_that(result, is_('default')) def test_intid(self): - from zope.interface import implementer from zope import component + from zope.interface import implementer try: from zope.intid.interfaces import IIntIds except ModuleNotFoundError: diff --git a/src/nti/externalization/tests/test_persistence.py b/src/nti/externalization/tests/test_persistence.py index dff8252..370c06a 100644 --- a/src/nti/externalization/tests/test_persistence.py +++ b/src/nti/externalization/tests/test_persistence.py @@ -1,19 +1,15 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import unittest import warnings try: + from persistent import CHANGED + from persistent import UPTODATE from persistent import Persistent from persistent.wref import WeakRef as PWeakRef - from persistent import UPTODATE - from persistent import CHANGED except ModuleNotFoundError: class Persistent: """Mock""" @@ -22,20 +18,20 @@ class Persistent: from ..persistence import UPTODATE from ..persistence import CHANGED -from ..persistence import PersistentExternalizableList -from ..persistence import PersistentExternalizableDictionary -from ..persistence import PersistentExternalizableWeakList -from ..persistence import getPersistentState -from ..persistence import setPersistentStateChanged -from ..persistence import NoPickle -from . import ExternalizationLayerTest - from hamcrest import assert_that from hamcrest import calling +from hamcrest import has_length from hamcrest import is_ from hamcrest import is_not from hamcrest import raises -from hamcrest import has_length + +from ..persistence import NoPickle +from ..persistence import PersistentExternalizableDictionary +from ..persistence import PersistentExternalizableList +from ..persistence import PersistentExternalizableWeakList +from ..persistence import getPersistentState +from ..persistence import setPersistentStateChanged +from . import ExternalizationLayerTest # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 diff --git a/src/nti/externalization/tests/test_proxy.py b/src/nti/externalization/tests/test_proxy.py index bc1a5b2..8d72085 100644 --- a/src/nti/externalization/tests/test_proxy.py +++ b/src/nti/externalization/tests/test_proxy.py @@ -1,11 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import unittest try: diff --git a/src/nti/externalization/tests/test_singleton.py b/src/nti/externalization/tests/test_singleton.py index fd9e957..adf14ec 100644 --- a/src/nti/externalization/tests/test_singleton.py +++ b/src/nti/externalization/tests/test_singleton.py @@ -1,11 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - - from nti.externalization.singleton import SingletonDecorator from nti.externalization.tests import ExternalizationLayerTest @@ -57,8 +52,8 @@ class Z(Y): def test_suite(): - import unittest import doctest + import unittest suite = unittest.defaultTestLoader.loadTestsFromName(__name__) return unittest.TestSuite([ diff --git a/src/nti/externalization/tests/test_testing.py b/src/nti/externalization/tests/test_testing.py index 77a9bab..322b97c 100644 --- a/src/nti/externalization/tests/test_testing.py +++ b/src/nti/externalization/tests/test_testing.py @@ -3,11 +3,7 @@ Tests for testing.py """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import unittest from nti.externalization.testing import externalizes diff --git a/src/nti/externalization/tests/test_zcml.py b/src/nti/externalization/tests/test_zcml.py index cebc4b4..df3f4e9 100644 --- a/src/nti/externalization/tests/test_zcml.py +++ b/src/nti/externalization/tests/test_zcml.py @@ -3,11 +3,7 @@ Tests for zcml.py. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -# stdlib imports import unittest from zope import component @@ -21,13 +17,13 @@ from nti.testing.matchers import is_empty from hamcrest import assert_that -from hamcrest import same_instance from hamcrest import equal_to -from hamcrest import is_ -from hamcrest import is_not from hamcrest import has_length from hamcrest import has_property +from hamcrest import is_ +from hamcrest import is_not from hamcrest import none +from hamcrest import same_instance # disable: accessing protected members, too many methods # pylint: disable=W0212,R0904 @@ -196,7 +192,9 @@ def test_scan_package_inherited(self): # Issue #97: When interface tags are inherited, # we don't double register. import sys + from zope.interface.interfaces import IInterface + from ..interfaces import IInternalObjectIOFinder class IPublic(interface.Interface): diff --git a/src/nti/externalization/zcml.py b/src/nti/externalization/zcml.py index b3defd1..be872a5 100644 --- a/src/nti/externalization/zcml.py +++ b/src/nti/externalization/zcml.py @@ -191,16 +191,16 @@ def autoPackageExternalization(_context, root_interfaces, modules, package_name = ext_module_name.rsplit('.', 1)[0] root_interfaces = frozenset(root_interfaces) - @classmethod + @classmethod # type:ignore[misc] def _ap_enumerate_externalizable_root_interfaces(_cls, unused_ifaces): return root_interfaces module_names = frozenset([m.__name__.split('.')[-1] for m in modules]) - @classmethod + @classmethod # type:ignore[misc] def _ap_enumerate_module_names(_cls): return module_names - @classmethod + @classmethod # type:ignore[misc] def _ap_find_package_name(_cls): return package_name @@ -216,9 +216,10 @@ def _ap_find_package_name(_cls): if iobase: bases = (iobase,) + bases - cls_iio = type('AutoPackageSearchingScopedInterfaceObjectIO', - bases, - cls_dict) + cls_iio: type[AutoPackageSearchingScopedInterfaceObjectIO] = type( + 'AutoPackageSearchingScopedInterfaceObjectIO', + bases, + cls_dict) # If we don't set the __module__, it defaults to this module, # which would be very confusing. cls_iio.__module__ = _context.package.__name__ if _context.package else '__dynamic__'