Coverage for python/lsst/utils/doImport.py: 12%
38 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-13 02:28 -0700
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-13 02:28 -0700
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# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12__all__ = ("doImport", "doImportType")
14import importlib
15import types
16from typing import List, Optional, Type, Union
19def doImport(importable: str) -> Union[types.ModuleType, Type]:
20 """Import a python object given an importable string and return it.
22 Parameters
23 ----------
24 importable : `str`
25 String containing dot-separated path of a Python class, module,
26 or member function.
28 Returns
29 -------
30 type : `type`
31 Type object. Either a module or class or a function.
33 Raises
34 ------
35 TypeError
36 ``importable`` is not a `str`.
37 ModuleNotFoundError
38 No module in the supplied import string could be found.
39 ImportError
40 ``importable`` is found but can not be imported or the requested
41 item could not be retrieved from the imported module.
42 """
43 if not isinstance(importable, str):
44 raise TypeError(f"Unhandled type of importable, val: {importable}")
46 def tryImport(
47 module: str, fromlist: List[str], previousError: Optional[str]
48 ) -> Union[types.ModuleType, Type]:
49 pytype = importlib.import_module(module)
50 # Can have functions inside classes inside modules
51 for f in fromlist:
52 try:
53 pytype = getattr(pytype, f)
54 except AttributeError:
55 extra = f"({previousError})" if previousError is not None else ""
56 raise ImportError(
57 f"Could not get attribute '{f}' from '{module}' when importing '{importable}' {extra}"
58 )
59 return pytype
61 # Go through the import path attempting to load the module
62 # and retrieve the class or function as an attribute. Shift components
63 # from the module list to the attribute list until something works.
64 moduleComponents = importable.split(".")
65 infileComponents: List[str] = []
66 previousError = None
68 while moduleComponents:
69 try:
70 pytype = tryImport(".".join(moduleComponents), infileComponents, previousError)
71 if not infileComponents and hasattr(pytype, moduleComponents[-1]):
72 # This module has an attribute with the same name as the
73 # module itself (like doImport.doImport, actually!).
74 # If that attribute was lifted to the package, we should
75 # return the attribute, not the module.
76 try:
77 return tryImport(".".join(moduleComponents[:-1]), moduleComponents[-1:], previousError)
78 except ModuleNotFoundError:
79 pass
80 return pytype
81 except ModuleNotFoundError as e:
82 previousError = str(e)
83 # Move element from module to file and try again
84 infileComponents.insert(0, moduleComponents.pop())
86 # Fell through without success.
87 extra = f"({previousError})" if previousError is not None else ""
88 raise ModuleNotFoundError(f"Unable to import {importable!r} {extra}")
91def doImportType(importable: str) -> Type:
92 """Import a python type given an importable string and return it.
94 Parameters
95 ----------
96 importable : `str`
97 String containing dot-separated path of a Python class,
98 or member function.
100 Returns
101 -------
102 type : `type`
103 Type object. Can not return a module.
105 Raises
106 ------
107 TypeError
108 ``importable`` is not a `str` or the imported type is a module.
109 ModuleNotFoundError
110 No module in the supplied import string could be found.
111 ImportError
112 ``importable`` is found but can not be imported or the requested
113 item could not be retrieved from the imported module.
114 """
115 imported = doImport(importable)
116 if isinstance(imported, types.ModuleType):
117 raise TypeError(f"Import of {importable} returned a module and not a type.")
118 return imported