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# -*- coding: utf-8 -*- 

2"""Utilities related to importing modules and symbols by name.""" 

3from __future__ import absolute_import, unicode_literals 

4 

5import importlib 

6import os 

7import sys 

8import warnings 

9from contextlib import contextmanager 

10 

11from kombu.utils.imports import symbol_by_name 

12 

13from celery.five import reload 

14 

15#: Billiard sets this when execv is enabled. 

16#: We use it to find out the name of the original ``__main__`` 

17#: module, so that we can properly rewrite the name of the 

18#: task to be that of ``App.main``. 

19MP_MAIN_FILE = os.environ.get('MP_MAIN_FILE') 

20 

21__all__ = ( 

22 'NotAPackage', 'qualname', 'instantiate', 'symbol_by_name', 

23 'cwd_in_path', 'find_module', 'import_from_cwd', 

24 'reload_from_cwd', 'module_file', 'gen_task_name', 

25) 

26 

27 

28class NotAPackage(Exception): 

29 """Raised when importing a package, but it's not a package.""" 

30 

31 

32if sys.version_info > (3, 3): # pragma: no cover 

33 def qualname(obj): 

34 """Return object name.""" 

35 if not hasattr(obj, '__name__') and hasattr(obj, '__class__'): 

36 obj = obj.__class__ 

37 q = getattr(obj, '__qualname__', None) 

38 if '.' not in q: 

39 q = '.'.join((obj.__module__, q)) 

40 return q 

41else: 

42 def qualname(obj): # noqa 

43 """Return object name.""" 

44 if not hasattr(obj, '__name__') and hasattr(obj, '__class__'): 

45 obj = obj.__class__ 

46 return '.'.join((obj.__module__, obj.__name__)) 

47 

48 

49def instantiate(name, *args, **kwargs): 

50 """Instantiate class by name. 

51 

52 See Also: 

53 :func:`symbol_by_name`. 

54 """ 

55 return symbol_by_name(name)(*args, **kwargs) 

56 

57 

58@contextmanager 

59def cwd_in_path(): 

60 """Context adding the current working directory to sys.path.""" 

61 cwd = os.getcwd() 

62 if cwd in sys.path: 

63 yield 

64 else: 

65 sys.path.insert(0, cwd) 

66 try: 

67 yield cwd 

68 finally: 

69 try: 

70 sys.path.remove(cwd) 

71 except ValueError: # pragma: no cover 

72 pass 

73 

74 

75def find_module(module, path=None, imp=None): 

76 """Version of :func:`imp.find_module` supporting dots.""" 

77 if imp is None: 

78 imp = importlib.import_module 

79 with cwd_in_path(): 

80 try: 

81 return imp(module) 

82 except ImportError: 

83 # Raise a more specific error if the problem is that one of the 

84 # dot-separated segments of the module name is not a package. 

85 if '.' in module: 

86 parts = module.split('.') 

87 for i, part in enumerate(parts[:-1]): 

88 package = '.'.join(parts[:i + 1]) 

89 try: 

90 mpart = imp(package) 

91 except ImportError: 

92 # Break out and re-raise the original ImportError 

93 # instead. 

94 break 

95 try: 

96 mpart.__path__ 

97 except AttributeError: 

98 raise NotAPackage(package) 

99 raise 

100 

101 

102def import_from_cwd(module, imp=None, package=None): 

103 """Import module, temporarily including modules in the current directory. 

104 

105 Modules located in the current directory has 

106 precedence over modules located in `sys.path`. 

107 """ 

108 if imp is None: 

109 imp = importlib.import_module 

110 with cwd_in_path(): 

111 return imp(module, package=package) 

112 

113 

114def reload_from_cwd(module, reloader=None): 

115 """Reload module (ensuring that CWD is in sys.path).""" 

116 if reloader is None: 

117 reloader = reload 

118 with cwd_in_path(): 

119 return reloader(module) 

120 

121 

122def module_file(module): 

123 """Return the correct original file name of a module.""" 

124 name = module.__file__ 

125 return name[:-1] if name.endswith('.pyc') else name 

126 

127 

128def gen_task_name(app, name, module_name): 

129 """Generate task name from name/module pair.""" 

130 module_name = module_name or '__main__' 

131 try: 

132 module = sys.modules[module_name] 

133 except KeyError: 

134 # Fix for manage.py shell_plus (Issue #366) 

135 module = None 

136 

137 if module is not None: 

138 module_name = module.__name__ 

139 # - If the task module is used as the __main__ script 

140 # - we need to rewrite the module part of the task name 

141 # - to match App.main. 

142 if MP_MAIN_FILE and module.__file__ == MP_MAIN_FILE: 

143 # - see comment about :envvar:`MP_MAIN_FILE` above. 

144 module_name = '__main__' 

145 if module_name == '__main__' and app.main: 

146 return '.'.join([app.main, name]) 

147 return '.'.join(p for p in (module_name, name) if p) 

148 

149 

150def load_extension_class_names(namespace): 

151 try: 

152 from pkg_resources import iter_entry_points 

153 except ImportError: # pragma: no cover 

154 return 

155 

156 for ep in iter_entry_points(namespace): 

157 yield ep.name, ':'.join([ep.module_name, ep.attrs[0]]) 

158 

159 

160def load_extension_classes(namespace): 

161 for name, class_name in load_extension_class_names(namespace): 

162 try: 

163 cls = symbol_by_name(class_name) 

164 except (ImportError, SyntaxError) as exc: 

165 warnings.warn( 

166 'Cannot load {0} extension {1!r}: {2!r}'.format( 

167 namespace, class_name, exc)) 

168 else: 

169 yield name, cls