module.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import os
  2. from pathlib import Path
  3. from typing import Optional
  4. from jedi.inference.cache import inference_state_method_cache
  5. from jedi.inference.names import AbstractNameDefinition, ModuleName
  6. from jedi.inference.filters import GlobalNameFilter, ParserTreeFilter, DictFilter, MergedFilter
  7. from jedi.inference import compiled
  8. from jedi.inference.base_value import TreeValue
  9. from jedi.inference.names import SubModuleName
  10. from jedi.inference.helpers import values_from_qualified_names
  11. from jedi.inference.compiled import create_simple_object
  12. from jedi.inference.base_value import ValueSet
  13. from jedi.inference.context import ModuleContext
  14. class _ModuleAttributeName(AbstractNameDefinition):
  15. """
  16. For module attributes like __file__, __str__ and so on.
  17. """
  18. api_type = 'instance'
  19. def __init__(self, parent_module, string_name, string_value=None):
  20. self.parent_context = parent_module
  21. self.string_name = string_name
  22. self._string_value = string_value
  23. def infer(self):
  24. if self._string_value is not None:
  25. s = self._string_value
  26. return ValueSet([
  27. create_simple_object(self.parent_context.inference_state, s)
  28. ])
  29. return compiled.get_string_value_set(self.parent_context.inference_state)
  30. class SubModuleDictMixin:
  31. @inference_state_method_cache()
  32. def sub_modules_dict(self):
  33. """
  34. Lists modules in the directory of this module (if this module is a
  35. package).
  36. """
  37. names = {}
  38. if self.is_package():
  39. mods = self.inference_state.compiled_subprocess.iter_module_names(
  40. self.py__path__()
  41. )
  42. for name in mods:
  43. # It's obviously a relative import to the current module.
  44. names[name] = SubModuleName(self.as_context(), name)
  45. # In the case of an import like `from x.` we don't need to
  46. # add all the variables, this is only about submodules.
  47. return names
  48. class ModuleMixin(SubModuleDictMixin):
  49. _module_name_class = ModuleName
  50. def get_filters(self, origin_scope=None):
  51. yield MergedFilter(
  52. ParserTreeFilter(
  53. parent_context=self.as_context(),
  54. origin_scope=origin_scope
  55. ),
  56. GlobalNameFilter(self.as_context()),
  57. )
  58. yield DictFilter(self.sub_modules_dict())
  59. yield DictFilter(self._module_attributes_dict())
  60. yield from self.iter_star_filters()
  61. def py__class__(self):
  62. c, = values_from_qualified_names(self.inference_state, 'types', 'ModuleType')
  63. return c
  64. def is_module(self):
  65. return True
  66. def is_stub(self):
  67. return False
  68. @property # type: ignore[misc]
  69. @inference_state_method_cache()
  70. def name(self):
  71. return self._module_name_class(self, self.string_names[-1])
  72. @inference_state_method_cache()
  73. def _module_attributes_dict(self):
  74. names = ['__package__', '__doc__', '__name__']
  75. # All the additional module attributes are strings.
  76. dct = dict((n, _ModuleAttributeName(self, n)) for n in names)
  77. path = self.py__file__()
  78. if path is not None:
  79. dct['__file__'] = _ModuleAttributeName(self, '__file__', str(path))
  80. return dct
  81. def iter_star_filters(self):
  82. for star_module in self.star_imports():
  83. f = next(star_module.get_filters(), None)
  84. assert f is not None
  85. yield f
  86. # I'm not sure if the star import cache is really that effective anymore
  87. # with all the other really fast import caches. Recheck. Also we would need
  88. # to push the star imports into InferenceState.module_cache, if we reenable this.
  89. @inference_state_method_cache([])
  90. def star_imports(self):
  91. from jedi.inference.imports import Importer
  92. modules = []
  93. module_context = self.as_context()
  94. for i in self.tree_node.iter_imports():
  95. if i.is_star_import():
  96. new = Importer(
  97. self.inference_state,
  98. import_path=i.get_paths()[-1],
  99. module_context=module_context,
  100. level=i.level
  101. ).follow()
  102. for module in new:
  103. if isinstance(module, ModuleValue):
  104. modules += module.star_imports()
  105. modules += new
  106. return modules
  107. def get_qualified_names(self):
  108. """
  109. A module doesn't have a qualified name, but it's important to note that
  110. it's reachable and not `None`. With this information we can add
  111. qualified names on top for all value children.
  112. """
  113. return ()
  114. class ModuleValue(ModuleMixin, TreeValue):
  115. api_type = 'module'
  116. def __init__(self, inference_state, module_node, code_lines, file_io=None,
  117. string_names=None, is_package=False):
  118. super().__init__(
  119. inference_state,
  120. parent_context=None,
  121. tree_node=module_node
  122. )
  123. self.file_io = file_io
  124. if file_io is None:
  125. self._path: Optional[Path] = None
  126. else:
  127. self._path = file_io.path
  128. self.string_names = string_names # Optional[Tuple[str, ...]]
  129. self.code_lines = code_lines
  130. self._is_package = is_package
  131. def is_stub(self):
  132. if self._path is not None and self._path.suffix == '.pyi':
  133. # Currently this is the way how we identify stubs when e.g. goto is
  134. # used in them. This could be changed if stubs would be identified
  135. # sooner and used as StubModuleValue.
  136. return True
  137. return super().is_stub()
  138. def py__name__(self):
  139. if self.string_names is None:
  140. return None
  141. return '.'.join(self.string_names)
  142. def py__file__(self) -> Optional[Path]:
  143. """
  144. In contrast to Python's __file__ can be None.
  145. """
  146. if self._path is None:
  147. return None
  148. return self._path.absolute()
  149. def is_package(self):
  150. return self._is_package
  151. def py__package__(self):
  152. if self.string_names is None:
  153. return []
  154. if self._is_package:
  155. return self.string_names
  156. return self.string_names[:-1]
  157. def py__path__(self):
  158. """
  159. In case of a package, this returns Python's __path__ attribute, which
  160. is a list of paths (strings).
  161. Returns None if the module is not a package.
  162. """
  163. if not self._is_package:
  164. return None
  165. # A namespace package is typically auto generated and ~10 lines long.
  166. first_few_lines = ''.join(self.code_lines[:50])
  167. # these are strings that need to be used for namespace packages,
  168. # the first one is ``pkgutil``, the second ``pkg_resources``.
  169. options = ('declare_namespace(__name__)', 'extend_path(__path__')
  170. if options[0] in first_few_lines or options[1] in first_few_lines:
  171. # It is a namespace, now try to find the rest of the
  172. # modules on sys_path or whatever the search_path is.
  173. paths = set()
  174. for s in self.inference_state.get_sys_path():
  175. other = os.path.join(s, self.name.string_name)
  176. if os.path.isdir(other):
  177. paths.add(other)
  178. if paths:
  179. return list(paths)
  180. # Nested namespace packages will not be supported. Nobody ever
  181. # asked for it and in Python 3 they are there without using all the
  182. # crap above.
  183. # Default to the of this file.
  184. file = self.py__file__()
  185. assert file is not None # Shouldn't be a package in the first place.
  186. return [os.path.dirname(file)]
  187. def _as_context(self):
  188. return ModuleContext(self)
  189. def __repr__(self):
  190. return "<%s: %s@%s-%s is_stub=%s>" % (
  191. self.__class__.__name__, self.py__name__(),
  192. self.tree_node.start_pos[0], self.tree_node.end_pos[0],
  193. self.is_stub()
  194. )