[merge] Registry

Martin Pool mbp at canonical.com
Thu Oct 12 09:43:41 BST 2006


On 27 Sep 2006, John Arbash Meinel <john at arbash-meinel.com> wrote:
> Robert Collins wrote:
> > On Thu, 2006-09-21 at 16:28 -0500, John Arbash Meinel wrote:
> > 
> > John and I talked on IRC and we are mostly in agreement now... sometime
> > soon should see a new patch :)
> > 
> > -Rob
> 
> Attached is an updated Registry, which defers to helper classes for
> handling whether to lazy import or not.
> 
> And it lessens how much Registry tries to look like a dictionary.

+1, except for a couple of grammatical fixes.  You have a habit of
separating your clauses into sentences, as in this quoted section.  I
don't mind how you write your email, but we should try to stick to
standard English in documentation, and I'm pretty sure this isn't
standard.

(It's only because the abstraction is so elegant already that I mention
this.  Perhaps it sounds trivial but we might as well get it right)

> +class _ObjectGetter(object):
> +    """Hang onto an object, and return it on request.

'on to'

> +
> +    This is used by Registry to make plain objects function similarly
> +    to lazily imported objects.
> +
> +    Objects can be any sort of python object (class, function, module,
> +    instance, etc)
> +    """
> +
> +    __slots__ = ['_obj']
> +
> +    def __init__(self, obj):
> +        self._obj = obj
> +
> +    def get_obj(self):
> +        """Get the object that was saved at creation time"""
> +        return self._obj
> +
> +
> +class _LazyObjectGetter(_ObjectGetter):
> +    """Keep a record of a future object, and on request load it."""
> +
> +    __slots__ = ['_module_name', '_member_name', '_imported']
> +
> +    def __init__(self, module_name, member_name):
> +        self._module_name = module_name
> +        self._member_name = member_name
> +        self._imported = False
> +        super(_LazyObjectGetter, self).__init__(None)
> +
> +    def get_obj(self):
> +        """Get the referenced object.
> +
> +        If not imported yet, this will import the requested module.
> +        Further requests will just return the already imported object.
> +        """
> +        if not self._imported:
> +            self._do_import()
> +        return super(_LazyObjectGetter, self).get_obj()
> +
> +    def _do_import(self):
> +        obj = __import__(self._module_name, globals(), locals(),
> +                         [self._member_name])
> +        if member_name:
> +            obj = getattr(obj, member_name)
> +        self._obj = obj
> +        self._imported = True
> +
> +
> +class Registry(object):
> +    """A class that registers objects to a name.
> +
> +    This is designed such that you can register objects in a lazy fashion,
> +    so that they can be imported later. While still having the help text
> +    available right away.
> +    """

'be imported later, while still having'

I would like for something to be said about the default_key in the class
docstring.

> +
> +    def __init__(self):
> +        """Create a new Registry."""
> +        self._default_key = None
> +        # Map from key => (is_lazy, info)


> +        self._dict = {}
> +        self._help_dict = {}
> +        self._info_dict = {}
> +
> +    def register(self, key, obj, help=None, info=None,
> +                 override_existing=False):
> +        """Register a new object to a name.
> +
> +        :param key: This is the key to use to request the object later.
> +        :param obj: The object to register.
> +        :param help: Help text for this entry. This may be a string or
> +                a callable. If it is a callable, it should take two
> +                parameters, this registry and the key that the help was
> +                registered under.
> +        :param info: More information for this entry. Registry.get_info()
> +                can be used to get this information. It is meant as an
> +                opaque storage location.
> +        :param override_existing: If True, replace the existing object
> +                with the new one. If False, if there is already something
> +                registered with the same key, raise a KeyError
> +        """
> +        if not override_existing:
> +            if key in self._dict:
> +                raise KeyError('Key %r already registered' % key)
> +        self._dict[key] = _ObjectGetter(obj)
> +        self._add_help_and_info(key, help=help, info=info)
> +
> +    def register_lazy(self, key, module_name, member_name,
> +                      help=None, info=None,
> +                      override_existing=False):
> +        """Register a new object to be loaded on request.
> +
> +        :param module_name: The python path to the module. Such as 'os.path'.
> +        :param member_name: The member of the module to return, if empty or 
> +                None get() will return the module itself.
> +        :param help: Help text for this entry. This may be a string or
> +                a callable.
> +        :param info: More information for this entry. Registry 
> +        :param override_existing: If True, replace the existing object
> +                with the new one. If False, if there is already something
> +                registered with the same key, raise a KeyError
> +        """
> +        if not override_existing:
> +            if key in self._dict:
> +                raise KeyError('Key %r already registered' % key)
> +        self._dict[key] = _LazyObjectGetter(module_name, member_name)
> +        self._add_help_and_info(key, help=help, info=info)
> +
> +    def _add_help_and_info(self, key, help=None, info=None):
> +        """Add the help and information about this key"""
> +        self._help_dict[key] = help
> +        self._info_dict[key] = info
> +
> +    def get(self, key=None):
> +        """Return the object register()'ed to the given key.
> +
> +        May raise ImportError if the object was registered lazily and
> +        there are any problems, or AttributeError if the module does not 
> +        have the supplied member.

May be better as

:raises: ImportError if the object was registered lazily and
there are any problems
:raises: AttributeError if the module does not 
have the supplied member.                                        > +

> +    default_key = property(_get_default_key, _set_default_key,
> +                            doc="Current value of the default key."
> +                                "Can be set to any existing key.")

OK but why?

Can we also add something to HACKING on the lines of

  Registries
  ----------

  Several places in Bazaar use (or will use) a registry, which is a 
  mapping from names to objects or classes.  The registry allows for 
  loading in registered code only when it's needed, and keeping
  associated information such as a help string or description.

just to point folks in the right direction.

-- 
Martin




More information about the bazaar mailing list