Hide keyboard shortcuts

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/>. 

21 

22 

23import importlib 

24 

25__all__ = ("doImport",) 

26 

27 

28def doImport(importable): 

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

30 

31 Parameters 

32 ---------- 

33 importable : `str` 

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

35 or member function. 

36 

37 Returns 

38 ------- 

39 type : `type` 

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

41 

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

54 

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 

65 

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 

72 

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()) 

90 

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