Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jedi/inference/value/module.py: 35%

130 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1import os 

2from pathlib import Path 

3from typing import Optional 

4 

5from jedi.inference.cache import inference_state_method_cache 

6from jedi.inference.names import AbstractNameDefinition, ModuleName 

7from jedi.inference.filters import GlobalNameFilter, ParserTreeFilter, DictFilter, MergedFilter 

8from jedi.inference import compiled 

9from jedi.inference.base_value import TreeValue 

10from jedi.inference.names import SubModuleName 

11from jedi.inference.helpers import values_from_qualified_names 

12from jedi.inference.compiled import create_simple_object 

13from jedi.inference.base_value import ValueSet 

14from jedi.inference.context import ModuleContext 

15 

16 

17class _ModuleAttributeName(AbstractNameDefinition): 

18 """ 

19 For module attributes like __file__, __str__ and so on. 

20 """ 

21 api_type = 'instance' 

22 

23 def __init__(self, parent_module, string_name, string_value=None): 

24 self.parent_context = parent_module 

25 self.string_name = string_name 

26 self._string_value = string_value 

27 

28 def infer(self): 

29 if self._string_value is not None: 

30 s = self._string_value 

31 return ValueSet([ 

32 create_simple_object(self.parent_context.inference_state, s) 

33 ]) 

34 return compiled.get_string_value_set(self.parent_context.inference_state) 

35 

36 

37class SubModuleDictMixin: 

38 @inference_state_method_cache() 

39 def sub_modules_dict(self): 

40 """ 

41 Lists modules in the directory of this module (if this module is a 

42 package). 

43 """ 

44 names = {} 

45 if self.is_package(): 

46 mods = self.inference_state.compiled_subprocess.iter_module_names( 

47 self.py__path__() 

48 ) 

49 for name in mods: 

50 # It's obviously a relative import to the current module. 

51 names[name] = SubModuleName(self.as_context(), name) 

52 

53 # In the case of an import like `from x.` we don't need to 

54 # add all the variables, this is only about submodules. 

55 return names 

56 

57 

58class ModuleMixin(SubModuleDictMixin): 

59 _module_name_class = ModuleName 

60 

61 def get_filters(self, origin_scope=None): 

62 yield MergedFilter( 

63 ParserTreeFilter( 

64 parent_context=self.as_context(), 

65 origin_scope=origin_scope 

66 ), 

67 GlobalNameFilter(self.as_context()), 

68 ) 

69 yield DictFilter(self.sub_modules_dict()) 

70 yield DictFilter(self._module_attributes_dict()) 

71 yield from self.iter_star_filters() 

72 

73 def py__class__(self): 

74 c, = values_from_qualified_names(self.inference_state, 'types', 'ModuleType') 

75 return c 

76 

77 def is_module(self): 

78 return True 

79 

80 def is_stub(self): 

81 return False 

82 

83 @property # type: ignore[misc] 

84 @inference_state_method_cache() 

85 def name(self): 

86 return self._module_name_class(self, self.string_names[-1]) 

87 

88 @inference_state_method_cache() 

89 def _module_attributes_dict(self): 

90 names = ['__package__', '__doc__', '__name__'] 

91 # All the additional module attributes are strings. 

92 dct = dict((n, _ModuleAttributeName(self, n)) for n in names) 

93 path = self.py__file__() 

94 if path is not None: 

95 dct['__file__'] = _ModuleAttributeName(self, '__file__', str(path)) 

96 return dct 

97 

98 def iter_star_filters(self): 

99 for star_module in self.star_imports(): 

100 f = next(star_module.get_filters(), None) 

101 assert f is not None 

102 yield f 

103 

104 # I'm not sure if the star import cache is really that effective anymore 

105 # with all the other really fast import caches. Recheck. Also we would need 

106 # to push the star imports into InferenceState.module_cache, if we reenable this. 

107 @inference_state_method_cache([]) 

108 def star_imports(self): 

109 from jedi.inference.imports import Importer 

110 

111 modules = [] 

112 module_context = self.as_context() 

113 for i in self.tree_node.iter_imports(): 

114 if i.is_star_import(): 

115 new = Importer( 

116 self.inference_state, 

117 import_path=i.get_paths()[-1], 

118 module_context=module_context, 

119 level=i.level 

120 ).follow() 

121 

122 for module in new: 

123 if isinstance(module, ModuleValue): 

124 modules += module.star_imports() 

125 modules += new 

126 return modules 

127 

128 def get_qualified_names(self): 

129 """ 

130 A module doesn't have a qualified name, but it's important to note that 

131 it's reachable and not `None`. With this information we can add 

132 qualified names on top for all value children. 

133 """ 

134 return () 

135 

136 

137class ModuleValue(ModuleMixin, TreeValue): 

138 api_type = 'module' 

139 

140 def __init__(self, inference_state, module_node, code_lines, file_io=None, 

141 string_names=None, is_package=False): 

142 super().__init__( 

143 inference_state, 

144 parent_context=None, 

145 tree_node=module_node 

146 ) 

147 self.file_io = file_io 

148 if file_io is None: 

149 self._path: Optional[Path] = None 

150 else: 

151 self._path = file_io.path 

152 self.string_names = string_names # Optional[Tuple[str, ...]] 

153 self.code_lines = code_lines 

154 self._is_package = is_package 

155 

156 def is_stub(self): 

157 if self._path is not None and self._path.suffix == '.pyi': 

158 # Currently this is the way how we identify stubs when e.g. goto is 

159 # used in them. This could be changed if stubs would be identified 

160 # sooner and used as StubModuleValue. 

161 return True 

162 return super().is_stub() 

163 

164 def py__name__(self): 

165 if self.string_names is None: 

166 return None 

167 return '.'.join(self.string_names) 

168 

169 def py__file__(self) -> Optional[Path]: 

170 """ 

171 In contrast to Python's __file__ can be None. 

172 """ 

173 if self._path is None: 

174 return None 

175 

176 return self._path.absolute() 

177 

178 def is_package(self): 

179 return self._is_package 

180 

181 def py__package__(self): 

182 if self.string_names is None: 

183 return [] 

184 

185 if self._is_package: 

186 return self.string_names 

187 return self.string_names[:-1] 

188 

189 def py__path__(self): 

190 """ 

191 In case of a package, this returns Python's __path__ attribute, which 

192 is a list of paths (strings). 

193 Returns None if the module is not a package. 

194 """ 

195 if not self._is_package: 

196 return None 

197 

198 # A namespace package is typically auto generated and ~10 lines long. 

199 first_few_lines = ''.join(self.code_lines[:50]) 

200 # these are strings that need to be used for namespace packages, 

201 # the first one is ``pkgutil``, the second ``pkg_resources``. 

202 options = ('declare_namespace(__name__)', 'extend_path(__path__') 

203 if options[0] in first_few_lines or options[1] in first_few_lines: 

204 # It is a namespace, now try to find the rest of the 

205 # modules on sys_path or whatever the search_path is. 

206 paths = set() 

207 for s in self.inference_state.get_sys_path(): 

208 other = os.path.join(s, self.name.string_name) 

209 if os.path.isdir(other): 

210 paths.add(other) 

211 if paths: 

212 return list(paths) 

213 # Nested namespace packages will not be supported. Nobody ever 

214 # asked for it and in Python 3 they are there without using all the 

215 # crap above. 

216 

217 # Default to the of this file. 

218 file = self.py__file__() 

219 assert file is not None # Shouldn't be a package in the first place. 

220 return [os.path.dirname(file)] 

221 

222 def _as_context(self): 

223 return ModuleContext(self) 

224 

225 def __repr__(self): 

226 return "<%s: %s@%s-%s is_stub=%s>" % ( 

227 self.__class__.__name__, self.py__name__(), 

228 self.tree_node.start_pos[0], self.tree_node.end_pos[0], 

229 self.is_stub() 

230 )