__init__.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. """
  2. Type inference of Python code in |jedi| is based on three assumptions:
  3. * The code uses as least side effects as possible. Jedi understands certain
  4. list/tuple/set modifications, but there's no guarantee that Jedi detects
  5. everything (list.append in different modules for example).
  6. * No magic is being used:
  7. - metaclasses
  8. - ``setattr()`` / ``__import__()``
  9. - writing to ``globals()``, ``locals()``, ``object.__dict__``
  10. * The programmer is not a total dick, e.g. like `this
  11. <https://github.com/davidhalter/jedi/issues/24>`_ :-)
  12. The actual algorithm is based on a principle I call lazy type inference. That
  13. said, the typical entry point for static analysis is calling
  14. ``infer_expr_stmt``. There's separate logic for autocompletion in the API, the
  15. inference_state is all about inferring an expression.
  16. TODO this paragraph is not what jedi does anymore, it's similar, but not the
  17. same.
  18. Now you need to understand what follows after ``infer_expr_stmt``. Let's
  19. make an example::
  20. import datetime
  21. datetime.date.toda# <-- cursor here
  22. First of all, this module doesn't care about completion. It really just cares
  23. about ``datetime.date``. At the end of the procedure ``infer_expr_stmt`` will
  24. return the ``date`` class.
  25. To *visualize* this (simplified):
  26. - ``InferenceState.infer_expr_stmt`` doesn't do much, because there's no assignment.
  27. - ``Context.infer_node`` cares for resolving the dotted path
  28. - ``InferenceState.find_types`` searches for global definitions of datetime, which
  29. it finds in the definition of an import, by scanning the syntax tree.
  30. - Using the import logic, the datetime module is found.
  31. - Now ``find_types`` is called again by ``infer_node`` to find ``date``
  32. inside the datetime module.
  33. Now what would happen if we wanted ``datetime.date.foo.bar``? Two more
  34. calls to ``find_types``. However the second call would be ignored, because the
  35. first one would return nothing (there's no foo attribute in ``date``).
  36. What if the import would contain another ``ExprStmt`` like this::
  37. from foo import bar
  38. Date = bar.baz
  39. Well... You get it. Just another ``infer_expr_stmt`` recursion. It's really
  40. easy. Python can obviously get way more complicated then this. To understand
  41. tuple assignments, list comprehensions and everything else, a lot more code had
  42. to be written.
  43. Jedi has been tested very well, so you can just start modifying code. It's best
  44. to write your own test first for your "new" feature. Don't be scared of
  45. breaking stuff. As long as the tests pass, you're most likely to be fine.
  46. I need to mention now that lazy type inference is really good because it
  47. only *inferes* what needs to be *inferred*. All the statements and modules
  48. that are not used are just being ignored.
  49. """
  50. import parso
  51. from jedi.file_io import FileIO
  52. from jedi import debug
  53. from jedi import settings
  54. from jedi.inference import imports
  55. from jedi.inference import recursion
  56. from jedi.inference.cache import inference_state_function_cache
  57. from jedi.inference import helpers
  58. from jedi.inference.names import TreeNameDefinition
  59. from jedi.inference.base_value import ContextualizedNode, \
  60. ValueSet, iterate_values
  61. from jedi.inference.value import ClassValue, FunctionValue
  62. from jedi.inference.syntax_tree import infer_expr_stmt, \
  63. check_tuple_assignments, tree_name_to_values
  64. from jedi.inference.imports import follow_error_node_imports_if_possible
  65. from jedi.plugins import plugin_manager
  66. class InferenceState:
  67. def __init__(self, project, environment=None, script_path=None):
  68. if environment is None:
  69. environment = project.get_environment()
  70. self.environment = environment
  71. self.script_path = script_path
  72. self.compiled_subprocess = environment.get_inference_state_subprocess(self)
  73. self.grammar = environment.get_grammar()
  74. self.latest_grammar = parso.load_grammar(version='3.13')
  75. self.memoize_cache = {} # for memoize decorators
  76. self.module_cache = imports.ModuleCache() # does the job of `sys.modules`.
  77. self.stub_module_cache = {} # Dict[Tuple[str, ...], Optional[ModuleValue]]
  78. self.compiled_cache = {} # see `inference.compiled.create()`
  79. self.inferred_element_counts = {}
  80. self.mixed_cache = {} # see `inference.compiled.mixed._create()`
  81. self.analysis = []
  82. self.dynamic_params_depth = 0
  83. self.do_dynamic_params_search = settings.dynamic_params
  84. self.is_analysis = False
  85. self.project = project
  86. self.access_cache = {}
  87. self.allow_unsafe_executions = False
  88. self.flow_analysis_enabled = True
  89. self.reset_recursion_limitations()
  90. def import_module(self, import_names, sys_path=None, prefer_stubs=True):
  91. return imports.import_module_by_names(
  92. self, import_names, sys_path, prefer_stubs=prefer_stubs)
  93. @staticmethod
  94. @plugin_manager.decorate()
  95. def execute(value, arguments):
  96. debug.dbg('execute: %s %s', value, arguments)
  97. with debug.increase_indent_cm():
  98. value_set = value.py__call__(arguments=arguments)
  99. debug.dbg('execute result: %s in %s', value_set, value)
  100. return value_set
  101. # mypy doesn't suppport decorated propeties (https://github.com/python/mypy/issues/1362)
  102. @property # type: ignore[misc]
  103. @inference_state_function_cache()
  104. def builtins_module(self):
  105. module_name = 'builtins'
  106. builtins_module, = self.import_module((module_name,), sys_path=[])
  107. return builtins_module
  108. @property # type: ignore[misc]
  109. @inference_state_function_cache()
  110. def typing_module(self):
  111. typing_module, = self.import_module(('typing',))
  112. return typing_module
  113. def reset_recursion_limitations(self):
  114. self.recursion_detector = recursion.RecursionDetector()
  115. self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self)
  116. def get_sys_path(self, **kwargs):
  117. """Convenience function"""
  118. return self.project._get_sys_path(self, **kwargs)
  119. def infer(self, context, name):
  120. def_ = name.get_definition(import_name_always=True)
  121. if def_ is not None:
  122. type_ = def_.type
  123. is_classdef = type_ == 'classdef'
  124. if is_classdef or type_ == 'funcdef':
  125. if is_classdef:
  126. c = ClassValue(self, context, name.parent)
  127. else:
  128. c = FunctionValue.from_context(context, name.parent)
  129. return ValueSet([c])
  130. if type_ == 'expr_stmt':
  131. is_simple_name = name.parent.type not in ('power', 'trailer')
  132. if is_simple_name:
  133. return infer_expr_stmt(context, def_, name)
  134. if type_ == 'for_stmt':
  135. container_types = context.infer_node(def_.children[3])
  136. cn = ContextualizedNode(context, def_.children[3])
  137. for_types = iterate_values(container_types, cn)
  138. n = TreeNameDefinition(context, name)
  139. return check_tuple_assignments(n, for_types)
  140. if type_ in ('import_from', 'import_name'):
  141. return imports.infer_import(context, name)
  142. if type_ == 'with_stmt':
  143. return tree_name_to_values(self, context, name)
  144. elif type_ == 'param':
  145. return context.py__getattribute__(name.value, position=name.end_pos)
  146. elif type_ == 'namedexpr_test':
  147. return context.infer_node(def_)
  148. else:
  149. result = follow_error_node_imports_if_possible(context, name)
  150. if result is not None:
  151. return result
  152. return helpers.infer_call_of_leaf(context, name)
  153. def parse_and_get_code(self, code=None, path=None,
  154. use_latest_grammar=False, file_io=None, **kwargs):
  155. if code is None:
  156. if file_io is None:
  157. file_io = FileIO(path)
  158. code = file_io.read()
  159. # We cannot just use parso, because it doesn't use errors='replace'.
  160. code = parso.python_bytes_to_unicode(code, encoding='utf-8', errors='replace')
  161. if len(code) > settings._cropped_file_size:
  162. code = code[:settings._cropped_file_size]
  163. grammar = self.latest_grammar if use_latest_grammar else self.grammar
  164. return grammar.parse(code=code, path=path, file_io=file_io, **kwargs), code
  165. def parse(self, *args, **kwargs):
  166. return self.parse_and_get_code(*args, **kwargs)[0]