1
# Copyright (C) 2006-2010 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Classes to provide name-to-object registry-like support."""
19
from __future__ import absolute_import
21
from .pyutils import get_named_object
23
from .sixish import viewitems
26
class _ObjectGetter(object):
27
"""Maintain a reference to an object, and return the object on request.
29
This is used by Registry to make plain objects function similarly
30
to lazily imported objects.
32
Objects can be any sort of python object (class, function, module,
38
def __init__(self, obj):
42
"""Get the module the object was loaded from."""
43
return self._obj.__module__
46
"""Get the object that was saved at creation time"""
50
class _LazyObjectGetter(_ObjectGetter):
51
"""Keep a record of a possible object.
53
When requested, load and return it.
56
__slots__ = ['_module_name', '_member_name', '_imported']
58
def __init__(self, module_name, member_name):
59
self._module_name = module_name
60
self._member_name = member_name
61
self._imported = False
62
super(_LazyObjectGetter, self).__init__(None)
65
"""Get the module the referenced object will be loaded from.
67
return self._module_name
70
"""Get the referenced object.
72
Upon first request, the object will be imported. Future requests will
73
return the imported object.
75
if not self._imported:
76
self._obj = get_named_object(self._module_name, self._member_name)
78
return super(_LazyObjectGetter, self).get_obj()
81
return "<%s.%s object at %x, module=%r attribute=%r imported=%r>" % (
82
self.__class__.__module__, self.__class__.__name__, id(self),
83
self._module_name, self._member_name, self._imported)
86
class Registry(object):
87
"""A class that registers objects to a name.
89
There are many places that want to collect related objects and access them
90
by a key. This class is designed to allow registering the mapping from key
91
to object. It goes one step further, and allows registering a name to a
92
hypothetical object which has not been imported yet. It also supports
93
adding additional information at registration time so that decisions can be
94
made without having to import the object (which may be expensive).
96
The functions 'get', 'get_info', and 'get_help' also support a
97
'default_key' (settable through my_registry.default_key = XXX, XXX must
98
already be registered.) Calling my_registry.get() or my_registry.get(None),
99
will return the entry for the default key.
103
"""Create a new Registry."""
104
self._default_key = None
105
# Map from key => (is_lazy, info)
112
"""Return a set of the format names which are aliases."""
113
return dict(viewitems(self._aliases))
117
for alias, target in viewitems(self._aliases):
118
ret.setdefault(target, []).append(alias)
121
def register(self, key, obj, help=None, info=None,
122
override_existing=False):
123
"""Register a new object to a name.
125
:param key: This is the key to use to request the object later.
126
:param obj: The object to register.
127
:param help: Help text for this entry. This may be a string or
128
a callable. If it is a callable, it should take two
129
parameters (registry, key): this registry and the key that
130
the help was registered under.
131
:param info: More information for this entry. Registry.get_info()
132
can be used to get this information. Registry treats this as an
133
opaque storage location (it is defined by the caller).
134
:param override_existing: Raise KeyErorr if False and something has
135
already been registered for that key. If True, ignore if there
136
is an existing key (always register the new value).
138
if not override_existing:
139
if key in self._dict:
140
raise KeyError('Key %r already registered' % key)
141
self._dict[key] = _ObjectGetter(obj)
142
self._add_help_and_info(key, help=help, info=info)
144
def register_lazy(self, key, module_name, member_name,
145
help=None, info=None,
146
override_existing=False):
147
"""Register a new object to be loaded on request.
149
:param key: This is the key to use to request the object later.
150
:param module_name: The python path to the module. Such as 'os.path'.
151
:param member_name: The member of the module to return. If empty or
152
None, get() will return the module itself.
153
:param help: Help text for this entry. This may be a string or
155
:param info: More information for this entry. Registry.get_info()
156
can be used to get this information. Registry treats this as an
157
opaque storage location (it is defined by the caller).
158
:param override_existing: If True, replace the existing object
159
with the new one. If False, if there is already something
160
registered with the same key, raise a KeyError
162
if not override_existing:
163
if key in self._dict:
164
raise KeyError('Key %r already registered' % key)
165
self._dict[key] = _LazyObjectGetter(module_name, member_name)
166
self._add_help_and_info(key, help=help, info=info)
168
def register_alias(self, key, target, info=None):
169
"""Register an alias.
171
:param key: Alias name
172
:param target: Target key name
174
if key in self._dict and key not in self._aliases:
175
raise KeyError('Key %r already registered and not an alias' % key)
176
self._dict[key] = self._dict[target]
177
self._aliases[key] = target
179
info = self._info_dict[target]
180
self._add_help_and_info(key, help=self._help_dict[target], info=info)
182
def _add_help_and_info(self, key, help=None, info=None):
183
"""Add the help and information about this key"""
184
self._help_dict[key] = help
185
self._info_dict[key] = info
187
def get(self, key=None):
188
"""Return the object register()'ed to the given key.
190
May raise ImportError if the object was registered lazily and
191
there are any problems, or AttributeError if the module does not
192
have the supplied member.
194
:param key: The key to obtain the object for. If no object has been
195
registered to that key, the object registered for self.default_key
196
will be returned instead, if it exists. Otherwise KeyError will be
198
:return: The previously registered object.
199
:raises ImportError: If the object was registered lazily, and there are
200
problems during import.
201
:raises AttributeError: If registered lazily, and the module does not
202
contain the registered member.
204
return self._dict[self._get_key_or_default(key)].get_obj()
206
def _get_module(self, key):
207
"""Return the module the object will be or was loaded from.
209
:param key: The key to obtain the module for.
210
:return: The name of the module
212
return self._dict[key].get_module()
214
def get_prefix(self, fullname):
215
"""Return an object whose key is a prefix of the supplied value.
217
:fullname: The name to find a prefix for
218
:return: a tuple of (object, remainder), where the remainder is the
219
portion of the name that did not match the key.
221
for key in self.keys():
222
if fullname.startswith(key):
223
return self.get(key), fullname[len(key):]
225
def _get_key_or_default(self, key=None):
226
"""Return either 'key' or the default key if key is None"""
229
if self.default_key is None:
230
raise KeyError('Key is None, and no default key is set')
232
return self.default_key
234
def get_help(self, key=None):
235
"""Get the help text associated with the given key"""
236
the_help = self._help_dict[self._get_key_or_default(key)]
237
if callable(the_help):
238
return the_help(self, key)
241
def get_info(self, key=None):
242
"""Get the extra information associated with the given key"""
243
return self._info_dict[self._get_key_or_default(key)]
245
def remove(self, key):
246
"""Remove a registered entry.
248
This is mostly for the test suite, but it can be used by others
252
def __contains__(self, key):
253
return key in self._dict
256
"""Get a list of registered entries"""
257
return sorted(self._dict)
260
for key in self._dict:
261
yield key, self._dict[key].get_obj()
264
# We should not use the iteritems() implementation below (see bug
266
return [(key, self._dict[key].get_obj()) for key in self.keys()]
268
def _set_default_key(self, key):
269
if key not in self._dict:
270
raise KeyError('No object registered under key %s.' % key)
272
self._default_key = key
274
def _get_default_key(self):
275
return self._default_key
277
default_key = property(_get_default_key, _set_default_key,
278
doc="Current value of the default key."
279
" Can be set to any existing key.")
282
class FormatRegistry(Registry):
283
"""Registry specialised for handling formats."""
285
def __init__(self, other_registry=None):
286
Registry.__init__(self)
287
self._other_registry = other_registry
289
def register(self, key, obj, help=None, info=None,
290
override_existing=False):
291
Registry.register(self, key, obj, help=help, info=info,
292
override_existing=override_existing)
293
if self._other_registry is not None:
294
self._other_registry.register(key, obj, help=help,
295
info=info, override_existing=override_existing)
297
def register_lazy(self, key, module_name, member_name,
298
help=None, info=None,
299
override_existing=False):
300
# Overridden to allow capturing registrations to two seperate
301
# registries in a single call.
302
Registry.register_lazy(self, key, module_name, member_name,
303
help=help, info=info, override_existing=override_existing)
304
if self._other_registry is not None:
305
self._other_registry.register_lazy(key, module_name, member_name,
306
help=help, info=info, override_existing=override_existing)
308
def remove(self, key):
309
Registry.remove(self, key)
310
if self._other_registry is not None:
311
self._other_registry.remove(key)
313
def get(self, format_string):
314
r = Registry.get(self, format_string)