Coverage for python/lsst/utils/doImport.py : 8%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of utils.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
23import importlib
25__all__ = ("doImport",)
28def doImport(importable):
29 """Import a python object given an importable string and return it.
31 Parameters
32 ----------
33 importable : `str`
34 String containing dot-separated path of a Python class, module,
35 or member function.
37 Returns
38 -------
39 type : `type`
40 Type object. Either a module or class or a function.
42 Raises
43 ------
44 TypeError
45 ``importable`` is not a `str`.
46 ModuleNotFoundError
47 No module in the supplied import string could be found.
48 ImportError
49 ``importable`` is found but can not be imported or the requested
50 item could not be retrieved from the imported module.
51 """
52 if not isinstance(importable, str):
53 raise TypeError(f"Unhandled type of importable, val: {importable}")
55 def tryImport(module, fromlist, previousError):
56 pytype = importlib.import_module(module)
57 # Can have functions inside classes inside modules
58 for f in fromlist:
59 try:
60 pytype = getattr(pytype, f)
61 except AttributeError:
62 extra = f"({previousError})" if previousError is not None else ""
63 raise ImportError(f"Could not get attribute '{f}' from '{module}' {extra}")
64 return pytype
66 # Go through the import path attempting to load the module
67 # and retrieve the class or function as an attribute. Shift components
68 # from the module list to the attribute list until something works.
69 moduleComponents = importable.split(".")
70 infileComponents = []
71 previousError = None
73 while moduleComponents:
74 try:
75 pytype = tryImport(".".join(moduleComponents), infileComponents, previousError)
76 if not infileComponents and hasattr(pytype, moduleComponents[-1]):
77 # This module has an attribute with the same name as the
78 # module itself (like doImport.doImport, actually!).
79 # If that attribute was lifted to the package, we should
80 # return the attribute, not the module.
81 try:
82 return tryImport(".".join(moduleComponents[:-1]), moduleComponents[-1:], previousError)
83 except ModuleNotFoundError:
84 pass
85 return pytype
86 except ModuleNotFoundError as e:
87 previousError = str(e)
88 # Move element from module to file and try again
89 infileComponents.insert(0, moduleComponents.pop())
91 raise ModuleNotFoundError(f"Unable to import {importable}")