Changes
3.2.2 (unreleased)
Nothing changed yet.
3.2.1 (2026-06-02)
Make internalizing anonymous dictionary objects support
IMappingfields, rather than only its subclassIDict.
3.2.0 (2026-05-19)
Drop support for Python 3.10.
Add support for Python 3.15.
Move the
orjsondependency to theorjsonextra for compatibility with free-threaded Python. If you’re using regular Python, it is highly recommended to install this extra.Add support for free-threaded CPython. However, note that some dependencies, most notably orjson, cannot currently be installed on free-threaded CPython, and other dependencies may currently require enabling the GIL.
3.1.0 (2026-05-08)
Document the
to_json_representationvariants 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)
Switch to using
orjsonfor JSON dumping/loading, fromsimplejson. This introduces the following changes:Floating nan and negative and positive infinity can no longer be represented; they are serialized as null.
decimal.Decimalobjects may be represented with reduced precision.Whitespace has changed (
orjsonelides unnecessary whitespace).
Remove deprecated uses of
datetime.datetime.utcfromtimestamp. Now, instead of being “naive” parsed dates from timestamps will have atzinfoof UTC.Support for PyPy has been removed (
orjsondoesn’t run on PyPy).
2.6.0 (2026-02-09)
Remove the automatic hooking of
get_current_requesttopyramid.threadlocal.get_current_request. If you want this behaviour, you must do so yourself.
2.5.0 (2025-11-14)
Reduce the logging level for recursive invocations. We handle this case correctly, it did not need to be a warning.
Add support for Python 3.14.
Add the new ‘zodb’ extra, which installs optional dependencies that use the ZODB ecosystem: persistent, BTrees, zope.intid, zope.container, etc. These dependencies are no longer installed by default.
No longer build binary wheels for the legacy ‘manylinux2014’ standard, only 2_28. Similarly, switch from musllinux_1_1 to 1_2.
2.4.0 (2024-11-11)
Drop support for anything older than Python 3.10.
Use native namespace packages.
Start publishing manylinux binary wheels.
Remove some legacy code for Python 2 support to enable compiling with Cython 3.1.
2.3.0 (2021-08-02)
Add a new base class,
StandardInternalObjectExternalizer. See PR 120 and issue 117.Rename
IExternalMappingDecoratortoIExternalStandardDictionaryDecoratorto emphasize that it is only used if you (directly or through a super class) callto_standard_external_dictionary. A compatibility alias remains. See PR 120 and issue 118.Docs-deprecated aliases in
nti.externalization.interfacesnow also emit deprecation warnings at runtime.Other documentation improvements. Sphinx can now run all the doctests (on Python 3); many doctests are still run an Python 2.
2.2.0 (2021-04-14)
Add support for Python 3.9.
Depend on BTrees 4.8 and above. This simplifies externalization checks. See issue 111.
2.1.0 (2020-08-03)
Add support for “externalization policies.” These are instances of
ExternalizationPolicythat can be used to tweak certain low-level details of externalization without replacing externalization objects wholesale. They are intended to have a very low performance impact.The only supported detail that can be changed right now is whether the standard created and last modified fields are externalized as Unix timestamps (the default) or as ISO 8601 strings.
See https://github.com/NextThought/nti.externalization/issues/109
2.0.0 (2020-07-02)
Change
ILocatedExternalMapping: Previously it extended the legacyzope.interface.common.mapping.IFullMapping. Now it extends the modernzope.interface.common.collections.IMapping. Note that this does not require mutability unlike the older interface. (TheLocatedExternalDictclass provided by this package is fully mutable and implementsIMutableMapping. It also continues to implementIFullMapping, but use of that interface is discouraged.)Change
ILocatedExternalSequence: Previously it extended the legacyzope.interface.common.sequence.ISequence. Now it extends the modernzope.interface.common.collections.ISequence. Note that this does not require mutability unlike the older interface. (TheLocatedExternalListclass provided by this package is fully mutable and implementsIMutableSequence.)Fix the interface resolution order for
LocatedExternalList. Previously, with zope.interface 5, it began implementing bothIMutableSequence(the new interface fromzope.interface.common.collections) as well as the older interfaceISequence(fromzope.interface.common.sequence); the two have inconsistent resolution orders. Now, it only implementsIMutableSequenceand a subset of the legacy interfaces that do not conflict. See issue 105.
1.1.3 (2020-06-25)
Correctly fire
IObjectWillUpdateFromExternalEventevents before updating an object.
1.1.2 (2020-04-07)
Adapt to a change in zope.container 4.4.0 that exposed unsafe assumptions that
providedBy(obj)would return the exact same object with the exact same state on a subsequent call. This was always a bug in the case of concurrency (e.g., if a different thread calleddirectlyProvideson the same object, or adjusted the__bases__of some interface in the IRO); the zope.container changes made it possible without concurrency. See https://github.com/zopefoundation/zope.container/issues/38 and https://github.com/NextThought/nti.externalization/issues/104.
1.1.1 (2020-03-27)
Fix a faulty assertion error. See issue 102.
1.1.0 (2020-03-27)
Make instances of
fractions.Fractionexternalize as a string such as"1/3". When received by a schema field that can parse this format, such aszope.schema.Rational(or higher on the numeric tower), this means fractions can be round-tripped.Support externalizing
decimal.Decimalobjects in the YAML representation.
1.0.0 (2020-03-19)
Add compatibility with, and require, zope.interface 5.0.
Document which tagged values are inherited and which are not.
Stop inheriting
_ext_is_marker_interface.
1.0.0a14 (2019-11-13)
Build with Cython 0.29.14 using ‘3str’ as the language level.
Add support for Python 3.8.
Update PyYAML to 5.1 and change the default output style slightly.
Fix tests with Persistent 4.4.3 and above.
Support zope.interface 4.7, which lets tagged values on interfaces be inherited, when using
<registerAutoPackageIO>on a module that had multiple objects implementing a derived interface. See issue 97.
1.0.0a13 (2018-09-20)
Support
IFromBytesfields introduced by zope.schema 4.8.0. See issue 92.Make
validate_field_value(and by extensionInterfaceObjectIO.update_from_external_object) callfromObjectdefined by any fields for non-byte and non-text data. Previously, only if the field raised aWrongContainedTypeErrorwouldfromObjectbe called.
1.0.0a12 (2018-09-11)
Add support for zope.schema 4.7.0 and nti.schema 1.5.0. Drop support for older versions, which includes dropping support for
dm.zope.schema.Objectfields.
1.0.0a11 (2018-08-29)
The
@WithReprdecorator takes into account the updated default repr of Persistent objects with persistent 4.4 and doesn’t hide it.Subclasses of
ExternalizableInstanceDictthat have non-str (unicode on Python 2, bytes on Python 3) keys in their__dict__do not throwTypeErrorwhen externalizing. Instead, the non-str values are converted to strs (using ASCII encoding) and the_p_changedattribute, if any, is set.
1.0.0a10 (2018-08-21)
The
registryargument to most functions is deprecated and ignored. Instead of making calls toregistry.queryAdapter, we now invoke the interface directly. For example,IInternalObjectExternalizer(containedObject). This lets individual objects have a say if they already provide the interface without going through the legacy code paths (it also calls__conform__on the object if needed).
1.0.0a9 (2018-08-20)
Allow subclasses of
InterfaceObjectIOto have non-frozenset values for_ext_primitive_out_ivars_. This issues a warning and in the future will be a TypeError.
1.0.0a8 (2018-08-16)
Better support for internalizing anonymous value objects discovered in a
Dictvalue. Now, they won’t raise aComponentLookupErrorwhenrequire_updateris True, and they will be given aMimeTypebased on the schema (if they don’t have one).
1.0.0a7 (2018-07-31)
Avoid a
TypeErrorfromvalidate_named_field_valuewhen external objects have unicode keys.LocatedExternalDictobjects accept more constructor arguments and allow arbitrary attributes.
1.0.0a6 (2018-07-31)
InterfaceObjectIOonly returns an anonymous factory forIDictfields when it wants objects for the value.StandardExternalFieldsandStandardInternalFieldsare deprecated aliases innti.externalization.externalization.update_from_external_objectproperly handles the case whereINamedExternalizedObjectFactoryFinderandIInternalObjectUpdaterare registered with different levels of specificity, and the finder also implementsIInternalObjectUpdater. Before, the finder would, perhaps incorrectly, be used as the updater.
1.0.0a5 (2018-07-30)
Objects inheriting from
InterfaceObjectIOand registered with the component registry (in ZCML) forIInternalObjectIOcan still be found and used asINamedExternalizedObjectFactoryFinder, an interface implemented byInterfaceObjectIOthroughIInternalObjectIOFinder. A warning will be issued to update the registration (which generally means removing theprovidesline in ZCML).ExternalizableInstanceDictno longer inherits fromAbstractDynamicIO, it just implements the same interface (with the exception of many of the_extmethods). This class is deprecated.Formally document the
notify_modifiedmember ofnti.externalization.internalization.notifyModifiedis a deprecated alias.
1.0.0a4 (2018-07-30)
Make
InterfaceObjectIO._ext_selfreadable from Python, even though that is not documented (and may change again in the future). Document the intended API,_ext_replacement(). See issue 73.Make
AbstractDynamicObjectIO._ext_getattrhandle a default value, and add_ext_replacement_getattrfor when it will only be called once. See issue 73.
1.0.0a3 (2018-07-28)
The
@NoPickledecorator also works withPersistentsubclasses (and may or may not work with multiple-inheritance subclasses ofPersistent, depending on the MRO, but that’s always been the case for regular objects). APersistentsubclass being decorated with@NoPickledoesn’t make much sense, so aRuntimeWarningis issued. A warning is also issued if the class directly implements one of the pickle protocol methods.Updating objects that use
createFieldPropertiesor otherwise haveFieldPropertyobjects in their type is at least 10% faster thanks to avoiding double-validation due to a small monkey-patch onFieldProperty. See issue 67.Proxies around objects that implement
toExternalObjectare allowed again; the proxied object’stoExternalObjectwill be called.The signature for
updateFromExternalObject()has been tightened. It should be(self, external_object, context, **kwargs), where**kwargsis optional, as is context.**kwargscurrently contains nothing useful. Uses ofdataserver=Nonein the signature will generate a warning. This may be tightened further in the future. See issue 30.__ext_ignore_updateFromExternalObject__is officially deprecated and generates a warning.update_from_external_objectcaches certain information about the types of the updater objects, making it 8-25% faster.update_from_external_objectmutates sequences contained in a dict in-place instead of overwriting with a new list.update_from_external_objectmutates sequences at the top level instead of returning a new list.Add support for finding factories for incoming data which do not specify a MIME type or class field based on the key they are assigned to. This aids in consuming data produced by foreign systems or using
Dictschema fields that require modelled values. See issue 51 and PR 68.Schemas that use
InterfaceObjectIO(including through the ZCML directiveregisterAutoPackageIO) can useDictfields more easily on internalization (externalization has always worked): They automatically internalize their values by treating theDictas anonymous external data.Strings can automatically be adapted into
ITimeDeltaobjects.
1.0.0a2 (2018-07-05)
The low levels of externalization no longer catch and hide POSKeyError. This indicates a problem with the database. See https://github.com/NextThought/nti.externalization/issues/60
Remove support for
object_hookinupdate_from_external_object. See https://github.com/NextThought/nti.externalization/issues/29.A number of deprecated aliases for moved functions have been removed.
On CPython, some of the modules are compiled as extension modules using Cython for a 10-30% increase in speed. Set the
PURE_PYTHONenvironment variable to disable this at runtime.The unused, undocumented method
stripSyntheticKeysFromExternalDictionarywas removed from instances ofExternalizableDictionaryMixin. Use the import instead.Unused keyword arguments for
to_standard_external_dictionaryandto_minimal_standard_external_dictionarynow produce a warning. In the future, extra keyword arguments will be an error.notifyModifiedno longer accepts theeventFactoryargument.The
notify_modifiedalias fornotifyModifiedhas been removed.Decorating external mappings and external objects handled
decorate_callbackdifferently. This argument is only used whendecorateis false. This argument is also confusing and should be considered deprecated.choose_fieldno longer has the undocumented conversion behaviour for the CREATOR external field name.
1.0.0a1 (2017-09-29)
First PyPI release.
Add support for Python 3.
Drop support for externalizing to plists. See https://github.com/NextThought/nti.externalization/issues/21
Reach 100% test coverage and ensure we remain there through CI.
Introduce
nti.externalization.extension_pointsto hold hook functions. Move the Pyramid integration there (and deprecate that). Also move the NTIID support there (but the old name works too). See https://github.com/NextThought/nti.externalization/issues/27Deprecate
nti.externalization.internalization.register_legacy_search_module. See https://github.com/NextThought/nti.externalization/issues/35Stop
ext:registerAutoPackageIOfrom registering the legacy class-name based factories by default. If you need class-name based factories, there are two options. The first is to explicitly registerIClassObjectFactoryobjects in ZCML (we could add a scanning directive to make that more convenient for large numbers of classes), and the second is to setregister_legacy_search_moduleto a true value in the ZCML directive forext:registerAutoPackageIO. Note that we expect the behaviour of this attribute to change in the near future. See https://github.com/NextThought/nti.externalization/issues/33Make
ext:registerAutoPackageIOperform legacy class registrations when the configuration context executes, not when the directive runs. This means that conflicts in legacy class names will be detected at configuration time. It also means that legacy class names can be registered locally withz3c.baseregistry(previously they were always registered in the global site manager). See https://github.com/NextThought/nti.externalization/issues/28Drop dependency on
zope.preferenceandzope.annotation. They were not used by this package, although ourconfigure.zcmldid include them. If you usezope.preferenceorzope.annotation, please include them in your own ZCML file.Drop hard dependency on Acquisition. It is still used if available and is used in test mode.
Add public implementations of
IMimeObjectFactoryandIClassObjectFactoryinnti.externalization.factory.Drop dependency on
nti.zodband itsPersistentPropertyHolder. The datastructures innti.externalization.persistenceno longer extend that class; if you have further subclasses that addnti.zodb.peristentproperty.PropertyHoldingPersistentproperties, you’ll need to be sure to mixin this class now. See https://github.com/NextThought/nti.externalization/issues/43Add the
<ext:classObjectFactory>directive for registeringClassbased factories. (Note: MIME factories are preferred.)Callers of
to_standard_external_dictionary(which includes AutoPackageScopedInterfaceIO) will now automatically get aMimeTypevalue if one can be found. Previously only callers ofto_minimal_standard_external_dictionarywould.