~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/registry.py

  • Committer: Jelmer Vernooij
  • Date: 2011-02-26 15:39:49 UTC
  • mto: (5691.1.1 bzr.dev)
  • mto: This revision was merged to the branch mainline in revision 5692.
  • Revision ID: jelmer@samba.org-20110226153949-o0fk909b30g7z570
Fix the use of "bzr tags" in branches with ghosts in their mainline /and/ tags on revisions not in the branch ancestry.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Classes to provide name-to-object registry-like support."""
18
18
 
19
19
 
 
20
from bzrlib.pyutils import get_named_object
 
21
 
 
22
 
 
23
class _ObjectGetter(object):
 
24
    """Maintain a reference to an object, and return the object on request.
 
25
 
 
26
    This is used by Registry to make plain objects function similarly
 
27
    to lazily imported objects.
 
28
 
 
29
    Objects can be any sort of python object (class, function, module,
 
30
    instance, etc)
 
31
    """
 
32
 
 
33
    __slots__ = ['_obj']
 
34
 
 
35
    def __init__(self, obj):
 
36
        self._obj = obj
 
37
 
 
38
    def get_module(self):
 
39
        """Get the module the object was loaded from."""
 
40
        return self._obj.__module__
 
41
 
 
42
    def get_obj(self):
 
43
        """Get the object that was saved at creation time"""
 
44
        return self._obj
 
45
 
 
46
 
 
47
class _LazyObjectGetter(_ObjectGetter):
 
48
    """Keep a record of a possible object.
 
49
 
 
50
    When requested, load and return it.
 
51
    """
 
52
 
 
53
    __slots__ = ['_module_name', '_member_name', '_imported']
 
54
 
 
55
    def __init__(self, module_name, member_name):
 
56
        self._module_name = module_name
 
57
        self._member_name = member_name
 
58
        self._imported = False
 
59
        super(_LazyObjectGetter, self).__init__(None)
 
60
 
 
61
    def get_module(self):
 
62
        """Get the module the referenced object will be loaded from.
 
63
        """
 
64
        return self._module_name
 
65
 
 
66
    def get_obj(self):
 
67
        """Get the referenced object.
 
68
 
 
69
        Upon first request, the object will be imported. Future requests will
 
70
        return the imported object.
 
71
        """
 
72
        if not self._imported:
 
73
            self._obj = get_named_object(self._module_name, self._member_name)
 
74
            self._imported = True
 
75
        return super(_LazyObjectGetter, self).get_obj()
 
76
 
 
77
    def __repr__(self):
 
78
        return "<%s.%s object at %x, module=%r attribute=%r imported=%r>" % (
 
79
            self.__class__.__module__, self.__class__.__name__, id(self),
 
80
            self._module_name, self._member_name, self._imported)
 
81
 
 
82
 
20
83
class Registry(object):
21
84
    """A class that registers objects to a name.
22
85
 
23
 
    This is designed such that you can register objects in a lazy fashion,
24
 
    so that they can be imported later. While still having the help text
25
 
    available right away.
 
86
    There are many places that want to collect related objects and access them
 
87
    by a key. This class is designed to allow registering the mapping from key
 
88
    to object. It goes one step further, and allows registering a name to a
 
89
    hypothetical object which has not been imported yet. It also supports
 
90
    adding additional information at registration time so that decisions can be
 
91
    made without having to import the object (which may be expensive).
 
92
 
 
93
    The functions 'get', 'get_info', and 'get_help' also support a
 
94
    'default_key' (settable through my_registry.default_key = XXX, XXX must
 
95
    already be registered.) Calling my_registry.get() or my_registry.get(None),
 
96
    will return the entry for the default key.
26
97
    """
27
98
 
28
99
    def __init__(self):
33
104
        self._help_dict = {}
34
105
        self._info_dict = {}
35
106
 
36
 
    def register(self, key, object, help=None, info=None,
 
107
    def register(self, key, obj, help=None, info=None,
37
108
                 override_existing=False):
38
109
        """Register a new object to a name.
39
110
 
40
111
        :param key: This is the key to use to request the object later.
41
 
        :param object: The object to register.
 
112
        :param obj: The object to register.
42
113
        :param help: Help text for this entry. This may be a string or
43
 
                a callable. If it is a callable, it should take a
44
 
                single parameter, which is the key that the help was
45
 
                registered under.
 
114
                a callable. If it is a callable, it should take two
 
115
                parameters (registry, key): this registry and the key that
 
116
                the help was registered under.
46
117
        :param info: More information for this entry. Registry.get_info()
47
 
                can be used to get this information. It is meant as an
48
 
                opaque storage location.
49
 
        :param override_existing: If True, replace the existing object
50
 
                with the new one. If False, if there is already something
51
 
                registered with the same key, raise a KeyError
 
118
                can be used to get this information. Registry treats this as an
 
119
                opaque storage location (it is defined by the caller).
 
120
        :param override_existing: Raise KeyErorr if False and something has
 
121
                already been registered for that key. If True, ignore if there
 
122
                is an existing key (always register the new value).
52
123
        """
53
124
        if not override_existing:
54
125
            if key in self._dict:
55
126
                raise KeyError('Key %r already registered' % key)
56
 
        self._dict[key] = (False, object)
 
127
        self._dict[key] = _ObjectGetter(obj)
57
128
        self._add_help_and_info(key, help=help, info=info)
58
129
 
59
130
    def register_lazy(self, key, module_name, member_name,
62
133
        """Register a new object to be loaded on request.
63
134
 
64
135
        :param module_name: The python path to the module. Such as 'os.path'.
65
 
        :param member_name: The member of the module to return, if empty or 
66
 
                None get() will return the module itself.
 
136
        :param member_name: The member of the module to return.  If empty or
 
137
                None, get() will return the module itself.
67
138
        :param help: Help text for this entry. This may be a string or
68
139
                a callable.
69
 
        :param info: More information for this entry. Registry 
 
140
        :param info: More information for this entry. Registry
70
141
        :param override_existing: If True, replace the existing object
71
142
                with the new one. If False, if there is already something
72
143
                registered with the same key, raise a KeyError
74
145
        if not override_existing:
75
146
            if key in self._dict:
76
147
                raise KeyError('Key %r already registered' % key)
77
 
        self._dict[key] = (True, (module_name, member_name))
 
148
        self._dict[key] = _LazyObjectGetter(module_name, member_name)
78
149
        self._add_help_and_info(key, help=help, info=info)
79
150
 
80
151
    def _add_help_and_info(self, key, help=None, info=None):
86
157
        """Return the object register()'ed to the given key.
87
158
 
88
159
        May raise ImportError if the object was registered lazily and
89
 
        there are any problems, or AttributeError if the module does not 
 
160
        there are any problems, or AttributeError if the module does not
90
161
        have the supplied member.
91
162
 
92
163
        :param key: The key to obtain the object for. If no object has been
94
165
            will be returned instead, if it exists. Otherwise KeyError will be
95
166
            raised.
96
167
        :return: The previously registered object.
97
 
        """
98
 
        return self._get_one(self._get_key_or_default(key))
 
168
        :raises ImportError: If the object was registered lazily, and there are
 
169
            problems during import.
 
170
        :raises AttributeError: If registered lazily, and the module does not
 
171
            contain the registered member.
 
172
        """
 
173
        return self._dict[self._get_key_or_default(key)].get_obj()
 
174
 
 
175
    def _get_module(self, key):
 
176
        """Return the module the object will be or was loaded from.
 
177
 
 
178
        :param key: The key to obtain the module for.
 
179
        :return: The name of the module
 
180
        """
 
181
        return self._dict[key].get_module()
 
182
 
 
183
    def get_prefix(self, fullname):
 
184
        """Return an object whose key is a prefix of the supplied value.
 
185
 
 
186
        :fullname: The name to find a prefix for
 
187
        :return: a tuple of (object, remainder), where the remainder is the
 
188
            portion of the name that did not match the key.
 
189
        """
 
190
        for key in self.keys():
 
191
            if fullname.startswith(key):
 
192
                return self.get(key), fullname[len(key):]
99
193
 
100
194
    def _get_key_or_default(self, key=None):
101
195
        """Return either 'key' or the default key if key is None"""
106
200
        else:
107
201
            return self.default_key
108
202
 
109
 
    __getitem__ = get
110
 
 
111
 
    def _get_one(self, key):
112
 
        """Attempt to return a single entry.
113
 
 
114
 
        This will import the entry if it is lazy, and replace the registry
115
 
        with the imported object.
116
 
 
117
 
        This may raise KeyError if the given key doesn't exist, or ImportError
118
 
        or AttributeError.
119
 
        """
120
 
        is_lazy, info_or_object = self._dict[key]
121
 
        if not is_lazy:
122
 
            # We have a real object to return
123
 
            return info_or_object
124
 
 
125
 
        module_name, member_name = info_or_object
126
 
        obj = __import__(module_name, globals(), locals(), [member_name])
127
 
        if member_name:
128
 
            obj = getattr(obj, member_name)
129
 
        self._dict[key] = (False, obj)
130
 
        return obj
131
 
 
132
203
    def get_help(self, key=None):
133
204
        """Get the help text associated with the given key"""
134
205
        the_help = self._help_dict[self._get_key_or_default(key)]
135
206
        if callable(the_help):
136
 
            return the_help(key)
 
207
            return the_help(self, key)
137
208
        return the_help
138
209
 
139
210
    def get_info(self, key=None):
147
218
        """
148
219
        del self._dict[key]
149
220
 
150
 
    __delitem__ = remove
151
 
 
152
221
    def __contains__(self, key):
153
222
        return key in self._dict
154
223
 
155
 
    def __len__(self):
156
 
        return len(self._dict)
157
 
 
158
224
    def keys(self):
159
225
        """Get a list of registered entries"""
160
226
        return sorted(self._dict.keys())
161
227
 
162
 
    def iterkeys(self):
163
 
        return self._dict.iterkeys()
164
 
 
165
228
    def iteritems(self):
166
 
        for key in self._dict.iterkeys():
167
 
            yield key, self._get_one(key)
 
229
        for key, getter in self._dict.iteritems():
 
230
            yield key, getter.get_obj()
168
231
 
169
232
    def items(self):
170
 
        return list(self.iteritems())
171
 
 
172
 
    def itervalues(self):
173
 
        for key in self._dict.iterkeys():
174
 
            yield self._get_one(key)
175
 
 
176
 
    def values(self):
177
 
        return list(self.itervalues())
 
233
        # We should not use the iteritems() implementation below (see bug
 
234
        # #430510)
 
235
        return sorted([(key, getter.get_obj())
 
236
                       for key, getter in self._dict.items()])
178
237
 
179
238
    def _set_default_key(self, key):
180
239
        if not self._dict.has_key(key):
187
246
 
188
247
    default_key = property(_get_default_key, _set_default_key,
189
248
                            doc="Current value of the default key."
190
 
                                "Can be set to any existing key.")
 
249
                                " Can be set to any existing key.")
 
250
 
 
251
 
 
252
class FormatRegistry(Registry):
 
253
    """Registry specialised for handling formats."""
 
254
 
 
255
    def __init__(self, other_registry=None):
 
256
        Registry.__init__(self)
 
257
        self._other_registry = other_registry
 
258
 
 
259
    def register(self, key, obj, help=None, info=None,
 
260
                 override_existing=False):
 
261
        Registry.register(self, key, obj, help=help, info=info,
 
262
            override_existing=override_existing)
 
263
        if self._other_registry is not None:
 
264
            self._other_registry.register(key, obj, help=help,
 
265
                info=info, override_existing=override_existing)
 
266
 
 
267
    def register_lazy(self, key, module_name, member_name,
 
268
                      help=None, info=None,
 
269
                      override_existing=False):
 
270
        # Overridden to allow capturing registrations to two seperate
 
271
        # registries in a single call.
 
272
        Registry.register_lazy(self, key, module_name, member_name,
 
273
                help=help, info=info, override_existing=override_existing)
 
274
        if self._other_registry is not None:
 
275
            self._other_registry.register_lazy(key, module_name, member_name,
 
276
                help=help, info=info, override_existing=override_existing)
 
277
 
 
278
    def get(self, format_string):
 
279
        r = Registry.get(self, format_string)
 
280
        if callable(r):
 
281
            r = r()
 
282
        return r