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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

140 statements  

1import os 

2from pathlib import Path 

3from typing import Optional, TYPE_CHECKING, Any 

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 

16if TYPE_CHECKING: 

17 from jedi.inference import InferenceState 

18 

19 

20class _ModuleAttributeName(AbstractNameDefinition): 

21 """ 

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

23 """ 

24 api_type = 'instance' 

25 

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

27 self.parent_context = parent_module 

28 self.string_name = string_name 

29 self._string_value = string_value 

30 

31 def infer(self): 

32 if self._string_value is not None: 

33 s = self._string_value 

34 return ValueSet([ 

35 create_simple_object(self.parent_context.inference_state, s) 

36 ]) 

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

38 

39 

40class SubModuleDictMixin: 

41 inference_state: "InferenceState" 

42 is_package: Any 

43 py__path__: Any 

44 as_context: Any 

45 

46 @inference_state_method_cache() 

47 def sub_modules_dict(self): 

48 """ 

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

50 package). 

51 """ 

52 names = {} 

53 if self.is_package(): 

54 mods = self.inference_state.compiled_subprocess.iter_module_names( 

55 self.py__path__() 

56 ) 

57 for name in mods: 

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

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

60 

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

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

63 return names 

64 

65 

66class ModuleMixin(SubModuleDictMixin): 

67 _module_name_class = ModuleName 

68 tree_node: Any 

69 string_names: Any 

70 sub_modules_dict: Any 

71 py__file__: Any 

72 

73 def get_filters(self, origin_scope=None): 

74 yield MergedFilter( 

75 ParserTreeFilter( 

76 parent_context=self.as_context(), 

77 origin_scope=origin_scope 

78 ), 

79 GlobalNameFilter(self.as_context()), 

80 ) 

81 yield DictFilter(self.sub_modules_dict()) 

82 yield DictFilter(self._module_attributes_dict()) 

83 yield from self.iter_star_filters() 

84 

85 def py__class__(self): 

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

87 return c 

88 

89 def is_module(self): 

90 return True 

91 

92 def is_stub(self): 

93 return False 

94 

95 @property 

96 @inference_state_method_cache() 

97 def name(self): 

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

99 

100 @inference_state_method_cache() 

101 def _module_attributes_dict(self): 

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

103 # All the additional module attributes are strings. 

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

105 path = self.py__file__() 

106 if path is not None: 

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

108 return dct 

109 

110 def iter_star_filters(self): 

111 for star_module in self.star_imports(): 

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

113 assert f is not None 

114 yield f 

115 

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

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

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

119 @inference_state_method_cache([]) 

120 def star_imports(self): 

121 from jedi.inference.imports import Importer 

122 

123 modules = [] 

124 module_context = self.as_context() 

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

126 if i.is_star_import(): 

127 new = Importer( 

128 self.inference_state, 

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

130 module_context=module_context, 

131 level=i.level 

132 ).follow() 

133 

134 for module in new: 

135 if isinstance(module, ModuleValue): 

136 modules += module.star_imports() 

137 modules += new 

138 return modules 

139 

140 def get_qualified_names(self): 

141 """ 

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

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

144 qualified names on top for all value children. 

145 """ 

146 return () 

147 

148 

149class ModuleValue(ModuleMixin, TreeValue): 

150 api_type = 'module' 

151 

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

153 string_names=None, is_package=False) -> None: 

154 super().__init__( 

155 inference_state, 

156 parent_context=None, 

157 tree_node=module_node 

158 ) 

159 self.file_io = file_io 

160 if file_io is None: 

161 self._path: Optional[Path] = None 

162 else: 

163 self._path = file_io.path 

164 self.string_names: Optional[tuple[str, ...]] = string_names 

165 self.code_lines = code_lines 

166 self._is_package = is_package 

167 

168 def is_stub(self): 

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

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

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

172 # sooner and used as StubModuleValue. 

173 return True 

174 return super().is_stub() 

175 

176 def py__name__(self): 

177 if self.string_names is None: 

178 return None 

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

180 

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

182 """ 

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

184 """ 

185 if self._path is None: 

186 return None 

187 

188 return self._path.absolute() 

189 

190 def is_package(self): 

191 return self._is_package 

192 

193 def py__package__(self): 

194 if self.string_names is None: 

195 return [] 

196 

197 if self._is_package: 

198 return self.string_names 

199 return self.string_names[:-1] 

200 

201 def py__path__(self): 

202 """ 

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

204 is a list of paths (strings). 

205 Returns None if the module is not a package. 

206 """ 

207 if not self._is_package: 

208 return None 

209 

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

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

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

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

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

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

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

217 # modules on sys_path or whatever the search_path is. 

218 paths = set() 

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

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

221 if os.path.isdir(other): 

222 paths.add(other) 

223 if paths: 

224 return list(paths) 

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

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

227 # crap above. 

228 

229 # Default to the of this file. 

230 file = self.py__file__() 

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

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

233 

234 def _as_context(self): 

235 return ModuleContext(self) 

236 

237 def __repr__(self): 

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

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

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

241 self.is_stub() 

242 )