~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/pyutils.py

Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.

This is used to replace various ad hoc implementations of the same logic,
notably the version used in registry's _LazyObjectGetter which had a bug when
getting a module without also getting a member.  And of course, this new
function has unit tests, unlike the replaced code.

This also adds a KnownHooksRegistry subclass to provide a more natural home for
some other logic.

I'm not thrilled about the name of the new module or the new functions, but it's
hard to think of good names for such generic functionality.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2010 Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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
 
16
 
 
17
"""Some convenience functions for general Python, such as a wrapper around
 
18
``_import__``.
 
19
"""
 
20
 
 
21
 
 
22
import sys
 
23
 
 
24
 
 
25
def get_named_object(module_name, member_name=None):
 
26
    """Get the Python object named by a given module and member name.
 
27
 
 
28
    This is usually much more convenient than dealing with ``__import__``
 
29
    directly.
 
30
 
 
31
    :param module_name: a module name, as found in sys.module.  It may contain
 
32
        dots.  e.g. 'sys' or 'os.path'.
 
33
    :param member_name: (optional) a name of an attribute in that module to
 
34
        return.  It may contain dots.  e.g. 'MyClass.some_method'.  If not
 
35
        given, the named module will be returned instead.
 
36
    :raises: ImportError or AttributeError.
 
37
    """
 
38
    # We may have just a module name, or a module name and a member name,
 
39
    # and either may contain dots.  __import__'s return value is a bit
 
40
    # unintuitive, so we need to take care to always return the object
 
41
    # specified by the full combination of module name + member name.
 
42
    if member_name:
 
43
        # Give __import__ a from_list.  It will return the last module in
 
44
        # the dotted module name.
 
45
        attr_chain = member_name.split('.')
 
46
        from_list = attr_chain[:1]
 
47
        obj = __import__(module_name, {}, {}, from_list)
 
48
        for attr in attr_chain:
 
49
            obj = getattr(obj, attr)
 
50
    else:
 
51
        # We're just importing a module, no attributes, so we have no
 
52
        # from_list.  __import__ will return the first module in the dotted
 
53
        # module name, so we look up the module from sys.modules.
 
54
        __import__(module_name, globals(), locals(), [])
 
55
        obj = sys.modules[module_name]
 
56
    return obj
 
57
 
 
58
 
 
59
def calc_parent_name(module_name, member_name=None):
 
60
    """Determine the 'parent' of a given dotted module name and (optional)
 
61
    member name.
 
62
 
 
63
    Typical use is::
 
64
 
 
65
        >>> parent_mod, parent_member, final_attr = calc_parent_name(
 
66
        ...     module_name, member_name)
 
67
        >>> parent_obj = get_named_object(parent_mod, parent_member)
 
68
 
 
69
    The idea is that ``getattr(parent_obj, final_attr)`` will equal
 
70
    get_named_object(module_name, member_name).
 
71
 
 
72
    :return: (module_name, member_name, final_attr) tuple.
 
73
    """
 
74
    if member_name is not None:
 
75
        split_name = member_name.rsplit('.', 1)
 
76
        if len(split_name) == 1:
 
77
            return (module_name, None, member_name)
 
78
        else:
 
79
            return (module_name, split_name[0], split_name[1])
 
80
    else:
 
81
        split_name = module_name.rsplit('.', 1)
 
82
        if len(split_name) == 1:
 
83
            raise AssertionError(
 
84
                'No parent object for top-level module %r' % (module_name,))
 
85
        else:
 
86
            return (split_name[0], None, split_name[1])