1"""A version registry that manages handlers for different state
2versions.
3"""
4# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
5# Copyright (c) 2005, Enthought, Inc.
6# License: BSD Style.
7
8# Standard library imports.
9import sys
10import inspect
11import logging
12
13
14logger = logging.getLogger(__name__)
15
16
17######################################################################
18# Utility functions.
19######################################################################
20def get_version(obj):
21    """Walks the class hierarchy and obtains the versions of the
22    various classes and returns a list of tuples of the form
23    ((class_name, module), version) in reverse order of the MRO.
24    """
25    res = []
26    for cls in inspect.getmro(obj.__class__):
27        class_name, module = cls.__name__, cls.__module__
28        if module in ['__builtin__']:
29            # No point in versioning builtins.
30            continue
31        try:
32            version = cls.__version__
33        except AttributeError:
34            version = -1
35        res.append( ( (class_name, module), version) )
36    res.reverse()
37    return res
38
39
40######################################################################
41# `HandlerRegistry` class.
42######################################################################
43class HandlerRegistry:
44    """A simple version conversion handler registry.  Classes register
45    handlers in order to convert the state version to the latest
46    version.  When an object's state is about to be set, the `update`
47    method of the registy is called.  This in turn calls any handlers
48    registered for the class/module and this handler is then called
49    with the state and the version of the state.  The state is
50    modified in-place by the handlers.
51    """
52
53    def __init__(self):
54        # The version conversion handlers.
55        # Key: (class_name, module), value: handler
56        self.handlers = {}
57
58    def register(self, class_name, module, handler):
59        """Register `handler` that handles versioning for class having
60        class name (`class_name`) and module name (`module`).  The
61        handler function will be passed the state and its version to fix.
62        """
63        key = (class_name, module)
64        if key in self.handlers:
65            msg = 'Overwriting version handler for (%s, %s)'%(key[0], key[1])
66            logger.warn(msg)
67        self.handlers[(class_name, module)] = handler
68
69    def unregister(self, class_name, module):
70        """Unregisters any handlers for a class and module.
71        """
72        self.handlers.pop((class_name, module))
73
74    def update(self, state):
75        """Updates the given state using the handlers.  Note that the
76        state is modified in-place.
77        """
78        if (not self.handlers) or  (not hasattr(state, '__metadata__')):
79            return
80        versions = state.__metadata__['version']
81        for ver in versions:
82            key = ver[0]
83            try:
84                self.handlers[key](state, ver[1])
85            except KeyError:
86                pass
87
88
89def _create_registry():
90    """Creates a reload safe, singleton registry.
91    """
92    registry = None
93    for key in sys.modules.keys():
94        if 'version_registry' in key:
95            mod = sys.modules[key]
96            if hasattr(mod, 'registry'):
97                registry = mod.registry
98                break
99    if not registry:
100        registry = HandlerRegistry()
101    return registry
102
103
104# The singleton registry.
105registry = _create_registry()
106
107