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

Shortcuts 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

37 statements  

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. 

11 

12__all__ = ("doImport", "doImportType") 

13 

14import importlib 

15import types 

16from typing import Type, List, Optional, Union 

17 

18 

19def doImport(importable: str) -> Union[types.ModuleType, Type]: 

20 """Import a python object given an importable string and return it. 

21 

22 Parameters 

23 ---------- 

24 importable : `str` 

25 String containing dot-separated path of a Python class, module, 

26 or member function. 

27 

28 Returns 

29 ------- 

30 type : `type` 

31 Type object. Either a module or class or a function. 

32 

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}") 

45 

46 def tryImport(module: str, fromlist: List[str], 

47 previousError: Optional[str]) -> Union[types.ModuleType, Type]: 

48 pytype = importlib.import_module(module) 

49 # Can have functions inside classes inside modules 

50 for f in fromlist: 

51 try: 

52 pytype = getattr(pytype, f) 

53 except AttributeError: 

54 extra = f"({previousError})" if previousError is not None else "" 

55 raise ImportError(f"Could not get attribute '{f}' from '{module}' {extra}") 

56 return pytype 

57 

58 # Go through the import path attempting to load the module 

59 # and retrieve the class or function as an attribute. Shift components 

60 # from the module list to the attribute list until something works. 

61 moduleComponents = importable.split(".") 

62 infileComponents: List[str] = [] 

63 previousError = None 

64 

65 while moduleComponents: 

66 try: 

67 pytype = tryImport(".".join(moduleComponents), infileComponents, previousError) 

68 if not infileComponents and hasattr(pytype, moduleComponents[-1]): 

69 # This module has an attribute with the same name as the 

70 # module itself (like doImport.doImport, actually!). 

71 # If that attribute was lifted to the package, we should 

72 # return the attribute, not the module. 

73 try: 

74 return tryImport(".".join(moduleComponents[:-1]), moduleComponents[-1:], previousError) 

75 except ModuleNotFoundError: 

76 pass 

77 return pytype 

78 except ModuleNotFoundError as e: 

79 previousError = str(e) 

80 # Move element from module to file and try again 

81 infileComponents.insert(0, moduleComponents.pop()) 

82 

83 raise ModuleNotFoundError(f"Unable to import {importable}") 

84 

85 

86def doImportType(importable: str) -> Type: 

87 """Import a python type given an importable string and return it. 

88 

89 Parameters 

90 ---------- 

91 importable : `str` 

92 String containing dot-separated path of a Python class, 

93 or member function. 

94 

95 Returns 

96 ------- 

97 type : `type` 

98 Type object. Can not return a module. 

99 

100 Raises 

101 ------ 

102 TypeError 

103 ``importable`` is not a `str` or the imported type is a module. 

104 ModuleNotFoundError 

105 No module in the supplied import string could be found. 

106 ImportError 

107 ``importable`` is found but can not be imported or the requested 

108 item could not be retrieved from the imported module. 

109 """ 

110 imported = doImport(importable) 

111 if isinstance(imported, types.ModuleType): 

112 raise TypeError(f"Import of {importable} returned a module and not a type.") 

113 return imported