~bzr-pqm/bzr/bzr.dev

5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
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
5436.2.8 by Vincent Ladeuil
Fix module docstring
17
"""General Python convenience functions."""
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
18
19
20
import sys
21
22
23
def get_named_object(module_name, member_name=None):
24
    """Get the Python object named by a given module and member name.
25
26
    This is usually much more convenient than dealing with ``__import__``
5436.2.6 by Andrew Bennetts
Add doctest-able example to get_named_object docstring. Make doctest skip calc_parent_name docstring.
27
    directly::
28
29
        >>> doc = get_named_object('bzrlib.pyutils', 'get_named_object.__doc__')
30
        >>> doc.splitlines()[0]
31
        'Get the Python object named by a given module and member name.'
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
32
5436.2.7 by Andrew Bennetts
Docstring tweaks prompted by review.
33
    :param module_name: a module name, as would be found in sys.modules if
34
        the module is already imported.  It may contain dots.  e.g. 'sys' or
35
        'os.path'.
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
36
    :param member_name: (optional) a name of an attribute in that module to
37
        return.  It may contain dots.  e.g. 'MyClass.some_method'.  If not
38
        given, the named module will be returned instead.
39
    :raises: ImportError or AttributeError.
40
    """
41
    # We may have just a module name, or a module name and a member name,
42
    # and either may contain dots.  __import__'s return value is a bit
43
    # unintuitive, so we need to take care to always return the object
44
    # specified by the full combination of module name + member name.
45
    if member_name:
46
        # Give __import__ a from_list.  It will return the last module in
47
        # the dotted module name.
48
        attr_chain = member_name.split('.')
49
        from_list = attr_chain[:1]
50
        obj = __import__(module_name, {}, {}, from_list)
51
        for attr in attr_chain:
52
            obj = getattr(obj, attr)
53
    else:
54
        # We're just importing a module, no attributes, so we have no
55
        # from_list.  __import__ will return the first module in the dotted
56
        # module name, so we look up the module from sys.modules.
57
        __import__(module_name, globals(), locals(), [])
58
        obj = sys.modules[module_name]
59
    return obj
60
61
62
def calc_parent_name(module_name, member_name=None):
63
    """Determine the 'parent' of a given dotted module name and (optional)
64
    member name.
65
66
    The idea is that ``getattr(parent_obj, final_attr)`` will equal
67
    get_named_object(module_name, member_name).
68
69
    :return: (module_name, member_name, final_attr) tuple.
70
    """
5501.1.1 by Vincent Ladeuil
Unbreak pqm by commenting out the bogus use of doctest +SKIP not supported by python2.4
71
# +SKIP is not recognized by python2.4
72
# Typical use is::
73
# 
74
#     >>> parent_mod, parent_member, final_attr = calc_parent_name(
75
#     ...     module_name, member_name) # doctest: +SKIP
76
#     >>> parent_obj = get_named_object(parent_mod, parent_member)
77
#     ... # doctest: +SKIP
5436.2.1 by Andrew Bennetts
Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.
78
    if member_name is not None:
79
        split_name = member_name.rsplit('.', 1)
80
        if len(split_name) == 1:
81
            return (module_name, None, member_name)
82
        else:
83
            return (module_name, split_name[0], split_name[1])
84
    else:
85
        split_name = module_name.rsplit('.', 1)
86
        if len(split_name) == 1:
87
            raise AssertionError(
88
                'No parent object for top-level module %r' % (module_name,))
89
        else:
90
            return (split_name[0], None, split_name[1])