Source code for apptools.persistence.version_registry
# (C) Copyright 2005-2024 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
"""A version registry that manages handlers for different state
versions.
"""
# Standard library imports.
import sys
import inspect
import logging
logger = logging.getLogger(__name__)
######################################################################
# Utility functions.
######################################################################
[docs]def get_version(obj):
"""Walks the class hierarchy and obtains the versions of the
various classes and returns a list of tuples of the form
((class_name, module), version) in reverse order of the MRO.
"""
res = []
for cls in inspect.getmro(obj.__class__):
class_name, module = cls.__name__, cls.__module__
if module in ["__builtin__"]:
# No point in versioning builtins.
continue
try:
version = cls.__version__
except AttributeError:
version = -1
res.append(((class_name, module), version))
res.reverse()
return res
######################################################################
# `HandlerRegistry` class.
######################################################################
[docs]class HandlerRegistry:
"""A simple version conversion handler registry. Classes register
handlers in order to convert the state version to the latest
version. When an object's state is about to be set, the `update`
method of the registy is called. This in turn calls any handlers
registered for the class/module and this handler is then called
with the state and the version of the state. The state is
modified in-place by the handlers.
"""
def __init__(self):
# The version conversion handlers.
# Key: (class_name, module), value: handler
self.handlers = {}
[docs] def register(self, class_name, module, handler):
"""Register `handler` that handles versioning for class having
class name (`class_name`) and module name (`module`). The
handler function will be passed the state and its version to fix.
"""
key = (class_name, module)
if key in self.handlers:
msg = "Overwriting version handler for (%s, %s)" % (key[0], key[1])
logger.warn(msg)
self.handlers[(class_name, module)] = handler
[docs] def unregister(self, class_name, module):
"""Unregisters any handlers for a class and module."""
self.handlers.pop((class_name, module))
[docs] def update(self, state):
"""Updates the given state using the handlers. Note that the
state is modified in-place.
"""
if (not self.handlers) or (not hasattr(state, "__metadata__")):
return
versions = state.__metadata__["version"]
for ver in versions:
key = ver[0]
try:
self.handlers[key](state, ver[1])
except KeyError:
pass
def _create_registry():
"""Creates a reload safe, singleton registry."""
registry = None
for key in sys.modules.keys():
if "version_registry" in key:
mod = sys.modules[key]
if hasattr(mod, "registry"):
registry = mod.registry
break
if not registry:
registry = HandlerRegistry()
return registry
# The singleton registry.
registry = _create_registry()