class Script(object): """ A Script is the base for completions, goto or whatever you want to do with |jedi|. You can either use the ``source`` parameter or ``path`` to read a file. Usually you're going to want to use both of them (in an editor). The script might be analyzed in a different ``sys.path`` than |jedi|: - if `sys_path` parameter is not ``None``, it will be used as ``sys.path`` for the script; - if `sys_path` parameter is ``None`` and ``VIRTUAL_ENV`` environment variable is defined, ``sys.path`` for the specified environment will be guessed (see :func:`jedi.evaluate.sys_path.get_venv_path`) and used for the script; - otherwise ``sys.path`` will match that of |jedi|. :param source: The source code of the current file, separated by newlines. :type source: str :param line: The line to perform actions on (starting with 1). :type line: int :param column: The column of the cursor (starting with 0). :type column: int :param path: The path of the file in the file system, or ``''`` if it hasn't been saved yet. :type path: str or None :param encoding: The encoding of ``source``, if it is not a ``unicode`` object (default ``'utf-8'``). :type encoding: str :param source_encoding: The encoding of ``source``, if it is not a ``unicode`` object (default ``'utf-8'``). :type encoding: str :param sys_path: ``sys.path`` to use during analysis of the script :type sys_path: list """ def __init__(self, source=None, line=None, column=None, path=None, encoding='utf-8', sys_path=None): self._orig_path = path # An empty path (also empty string) should always result in no path. self.path = os.path.abspath(path) if path else None if source is None: # TODO add a better warning than the traceback! with open(path, 'rb') as f: source = f.read() # TODO do we really want that? self._source = python_bytes_to_unicode(source, encoding, errors='replace') self._code_lines = split_lines(self._source) line = max(len(self._code_lines), 1) if line is None else line if not (0 < line <= len(self._code_lines)): raise ValueError('`line` parameter is not in a valid range.') line_len = len(self._code_lines[line - 1]) column = line_len if column is None else column if not (0 <= column <= line_len): raise ValueError('`column` parameter is not in a valid range.') self._pos = line, column self._path = path cache.clear_time_caches() debug.reset_time() # Load the Python grammar of the current interpreter. self._grammar = parso.load_grammar() if sys_path is None: venv = os.getenv('VIRTUAL_ENV') if venv: sys_path = list(get_venv_path(venv)) self._evaluator = Evaluator(self._grammar, sys_path=sys_path) debug.speed('init') @cache.memoize_method def _get_module_node(self): return self._grammar.parse( code=self._source, path=self.path, cache= False, # No disk cache, because the current script often changes. diff_cache=True, cache_path=settings.cache_directory) @cache.memoize_method def _get_module(self): module = er.ModuleContext(self._evaluator, self._get_module_node(), self.path) if self.path is not None: name = dotted_path_in_sys_path(self._evaluator.sys_path, self.path) if name is not None: imports.add_module(self._evaluator, name, module) return module def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, repr(self._orig_path)) def completions(self): """ Return :class:`classes.Completion` objects. Those objects contain information about the completions, more than just names. :return: Completion objects, sorted by name and __ comes last. :rtype: list of :class:`classes.Completion` """ debug.speed('completions start') completion = Completion(self._evaluator, self._get_module(), self._code_lines, self._pos, self.call_signatures) completions = completion.completions() debug.speed('completions end') return completions def goto_definitions(self): """ Return the definitions of a the path under the cursor. goto function! This follows complicated paths and returns the end, not the first definition. The big difference between :meth:`goto_assignments` and :meth:`goto_definitions` is that :meth:`goto_assignments` doesn't follow imports and statements. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ module_node = self._get_module_node() leaf = module_node.get_name_of_position(self._pos) if leaf is None: leaf = module_node.get_leaf_for_position(self._pos) if leaf is None: return [] context = self._evaluator.create_context(self._get_module(), leaf) definitions = helpers.evaluate_goto_definition(self._evaluator, context, leaf) names = [s.name for s in definitions] defs = [classes.Definition(self._evaluator, name) for name in names] # The additional set here allows the definitions to become unique in an # API sense. In the internals we want to separate more things than in # the API. return helpers.sorted_definitions(set(defs)) def goto_assignments(self, follow_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names, check): for name in names: if check(name): for result in filter_follow_imports(name.goto(), check): yield result else: yield name names = self._goto() if follow_imports: def check(name): if isinstance(name, er.ModuleName): return False return name.api_type == 'module' else: def check(name): return isinstance(name, imports.SubModuleName) names = filter_follow_imports(names, check) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs) def _goto(self): """ Used for goto_assignments and usages. """ name = self._get_module_node().get_name_of_position(self._pos) if name is None: return [] context = self._evaluator.create_context(self._get_module(), name) return list(self._evaluator.goto(context, name)) def usages(self, additional_module_paths=()): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :rtype: list of :class:`classes.Definition` """ temp, settings.dynamic_flow_information = \ settings.dynamic_flow_information, False try: module_node = self._get_module_node() user_stmt = get_statement_of_position(module_node, self._pos) definition_names = self._goto() if not definition_names and isinstance(user_stmt, tree.Import): # For not defined imports (goto doesn't find something, we take # the name as a definition. This is enough, because every name # points to it. name = user_stmt.get_name_of_position(self._pos) if name is None: # Must be syntax return [] definition_names = [ TreeNameDefinition(self._get_module(), name) ] if not definition_names: # Without a definition for a name we cannot find references. return [] definition_names = usages.resolve_potential_imports( self._evaluator, definition_names) modules = set([d.get_root_context() for d in definition_names]) modules.add(self._get_module()) definitions = usages.usages(self._evaluator, definition_names, modules) finally: settings.dynamic_flow_information = temp return helpers.sorted_definitions(set(definitions)) def call_signatures(self): """ Return the function object of the call you're currently in. E.g. if the cursor is here:: abs(# <-- cursor is here This would return the ``abs`` function. On the other hand:: abs()# <-- cursor is here This would return an empty list.. :rtype: list of :class:`classes.CallSignature` """ call_signature_details = \ helpers.get_call_signature_details(self._get_module_node(), self._pos) if call_signature_details is None: return [] context = self._evaluator.create_context( self._get_module(), call_signature_details.bracket_leaf) definitions = helpers.cache_call_signatures( self._evaluator, context, call_signature_details.bracket_leaf, self._code_lines, self._pos) debug.speed('func_call followed') return [ classes.CallSignature( self._evaluator, d.name, call_signature_details.bracket_leaf.start_pos, call_signature_details.call_index, call_signature_details.keyword_name_str) for d in definitions if hasattr(d, 'py__call__') ] def _analysis(self): self._evaluator.is_analysis = True module_node = self._get_module_node() self._evaluator.analysis_modules = [module_node] try: for node in get_executable_nodes(module_node): context = self._get_module().create_context(node) if node.type in ('funcdef', 'classdef'): # TODO This is stupid, should be private from jedi.evaluate.finder import _name_to_types # Resolve the decorators. _name_to_types(self._evaluator, context, node.children[1]) elif isinstance(node, tree.Import): import_names = set(node.get_defined_names()) if node.is_nested(): import_names |= set(path[-1] for path in node.get_paths()) for n in import_names: imports.infer_import(context, n) elif node.type == 'expr_stmt': types = context.eval_node(node) for testlist in node.children[:-1:2]: # Iterate tuples. unpack_tuple_to_dict(context, types, testlist) else: if node.type == 'name': defs = self._evaluator.goto_definitions(context, node) else: defs = evaluate_call_of_leaf(context, node) try_iter_content(defs) self._evaluator.reset_recursion_limitations() ana = [a for a in self._evaluator.analysis if self.path == a.path] return sorted(set(ana), key=lambda x: x.line) finally: self._evaluator.is_analysis = False
class Script(object): """ A Script is the base for completions, goto or whatever you want to do with |jedi|. You can either use the ``source`` parameter or ``path`` to read a file. Usually you're going to want to use both of them (in an editor). The script might be analyzed in a different ``sys.path`` than |jedi|: - if `sys_path` parameter is not ``None``, it will be used as ``sys.path`` for the script; - if `sys_path` parameter is ``None`` and ``VIRTUAL_ENV`` environment variable is defined, ``sys.path`` for the specified environment will be guessed (see :func:`jedi.evaluate.sys_path.get_venv_path`) and used for the script; - otherwise ``sys.path`` will match that of |jedi|. :param source: The source code of the current file, separated by newlines. :type source: str :param line: The line to perform actions on (starting with 1). :type line: int :param column: The column of the cursor (starting with 0). :type column: int :param path: The path of the file in the file system, or ``''`` if it hasn't been saved yet. :type path: str or None :param encoding: The encoding of ``source``, if it is not a ``unicode`` object (default ``'utf-8'``). :type encoding: str :param sys_path: ``sys.path`` to use during analysis of the script :type sys_path: list :param environment: TODO :type sys_path: Environment """ def __init__(self, source=None, line=None, column=None, path=None, encoding='utf-8', sys_path=None, environment=None): self._orig_path = path # An empty path (also empty string) should always result in no path. self.path = os.path.abspath(path) if path else None if source is None: # TODO add a better warning than the traceback! with open(path, 'rb') as f: source = f.read() # Load the Python grammar of the current interpreter. self._grammar = parso.load_grammar() if sys_path is not None and not is_py3: sys_path = list(map(force_unicode, sys_path)) # Load the Python grammar of the current interpreter. project = get_default_project( os.path.dirname(self.path)if path else os.getcwd() ) # TODO deprecate and remove sys_path from the Script API. if sys_path is not None: project._sys_path = sys_path self._evaluator = Evaluator( project, environment=environment, script_path=self.path ) self._project = project debug.speed('init') self._module_node, source = self._evaluator.parse_and_get_code( code=source, path=self.path, encoding=encoding, cache=False, # No disk cache, because the current script often changes. diff_cache=settings.fast_parser, cache_path=settings.cache_directory, ) debug.speed('parsed') self._code_lines = parso.split_lines(source, keepends=True) self._code = source line = max(len(self._code_lines), 1) if line is None else line if not (0 < line <= len(self._code_lines)): raise ValueError('`line` parameter is not in a valid range.') line_string = self._code_lines[line - 1] line_len = len(line_string) if line_string.endswith('\r\n'): line_len -= 1 if line_string.endswith('\n'): line_len -= 1 column = line_len if column is None else column if not (0 <= column <= line_len): raise ValueError('`column` parameter (%d) is not in a valid range ' '(0-%d) for line %d (%r).' % ( column, line_len, line, line_string)) self._pos = line, column self._path = path cache.clear_time_caches() debug.reset_time() def _get_module(self): name = '__main__' if self.path is not None: import_names = dotted_path_in_sys_path(self._evaluator.get_sys_path(), self.path) if import_names is not None: name = '.'.join(import_names) module = ModuleContext( self._evaluator, self._module_node, self.path, code_lines=self._code_lines ) imports.add_module_to_cache(self._evaluator, name, module) return module def __repr__(self): return '<%s: %s %r>' % ( self.__class__.__name__, repr(self._orig_path), self._evaluator.environment, ) def completions(self): """ Return :class:`classes.Completion` objects. Those objects contain information about the completions, more than just names. :return: Completion objects, sorted by name and __ comes last. :rtype: list of :class:`classes.Completion` """ debug.speed('completions start') completion = Completion( self._evaluator, self._get_module(), self._code_lines, self._pos, self.call_signatures ) completions = completion.completions() def iter_import_completions(): for c in completions: tree_name = c._name.tree_name if tree_name is None: continue definition = tree_name.get_definition() if definition is not None \ and definition.type in ('import_name', 'import_from'): yield c if len(list(iter_import_completions())) > 10: # For now disable completions if there's a lot of imports that # might potentially be resolved. This is the case for tensorflow # and has been fixed for it. This is obviously temporary until we # have a better solution. self._evaluator.infer_enabled = False debug.speed('completions end') return completions def goto_definitions(self): """ Return the definitions of a the path under the cursor. goto function! This follows complicated paths and returns the end, not the first definition. The big difference between :meth:`goto_assignments` and :meth:`goto_definitions` is that :meth:`goto_assignments` doesn't follow imports and statements. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ leaf = self._module_node.get_name_of_position(self._pos) if leaf is None: leaf = self._module_node.get_leaf_for_position(self._pos) if leaf is None: return [] context = self._evaluator.create_context(self._get_module(), leaf) definitions = helpers.evaluate_goto_definition(self._evaluator, context, leaf) names = [s.name for s in definitions] defs = [classes.Definition(self._evaluator, name) for name in names] # The additional set here allows the definitions to become unique in an # API sense. In the internals we want to separate more things than in # the API. return helpers.sorted_definitions(set(defs)) def goto_assignments(self, follow_imports=False, follow_builtin_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :param follow_imports: The goto call will follow imports. :param follow_builtin_imports: If follow_imports is True will decide if it follow builtin imports. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names, check): for name in names: if check(name): new_names = list(filter_follow_imports(name.goto(), check)) found_builtin = False if follow_builtin_imports: for new_name in new_names: if new_name.start_pos is None: found_builtin = True if found_builtin and not isinstance(name, imports.SubModuleName): yield name else: for new_name in new_names: yield new_name else: yield name tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: return [] context = self._evaluator.create_context(self._get_module(), tree_name) names = list(self._evaluator.goto(context, tree_name)) if follow_imports: def check(name): return name.is_import() else: def check(name): return isinstance(name, imports.SubModuleName) names = filter_follow_imports(names, check) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs) def usages(self, additional_module_paths=(), **kwargs): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :param additional_module_paths: Deprecated, never ever worked. :param include_builtins: Default True, checks if a usage is a builtin (e.g. ``sys``) and in that case does not return it. :rtype: list of :class:`classes.Definition` """ if additional_module_paths: warnings.warn( "Deprecated since version 0.12.0. This never even worked, just ignore it.", DeprecationWarning, stacklevel=2 ) def _usages(include_builtins=True): tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: # Must be syntax return [] names = usages.usages(self._get_module(), tree_name) definitions = [classes.Definition(self._evaluator, n) for n in names] if not include_builtins: definitions = [d for d in definitions if not d.in_builtin_module()] return helpers.sorted_definitions(definitions) return _usages(**kwargs) def usages_in_module(self, additional_module_paths=(), **kwargs): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :param additional_module_paths: Deprecated, never ever worked. :param include_builtins: Default True, checks if a usage is a builtin (e.g. ``sys``) and in that case does not return it. :rtype: list of :class:`classes.Definition` """ if additional_module_paths: warnings.warn( "Deprecated since version 0.12.0. This never even worked, just ignore it.", DeprecationWarning, stacklevel=2 ) def _usages_in_module(include_builtins=True): tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: # Must be syntax return [] names = usages.usages_in_module(self._get_module(), tree_name) definitions = [classes.Definition(self._evaluator, n) for n in names] if not include_builtins: definitions = [d for d in definitions if not d.in_builtin_module()] return helpers.sorted_definitions(definitions) return _usages_in_module(**kwargs) def call_signatures(self): """ Return the function object of the call you're currently in. E.g. if the cursor is here:: abs(# <-- cursor is here This would return the ``abs`` function. On the other hand:: abs()# <-- cursor is here This would return an empty list.. :rtype: list of :class:`classes.CallSignature` """ call_signature_details = \ helpers.get_call_signature_details(self._module_node, self._pos) if call_signature_details is None: return [] context = self._evaluator.create_context( self._get_module(), call_signature_details.bracket_leaf ) definitions = helpers.cache_call_signatures( self._evaluator, context, call_signature_details.bracket_leaf, self._code_lines, self._pos ) debug.speed('func_call followed') return [classes.CallSignature(self._evaluator, d.name, call_signature_details.bracket_leaf.start_pos, call_signature_details.call_index, call_signature_details.keyword_name_str) for d in definitions if hasattr(d, 'py__call__')] def _analysis(self): self._evaluator.is_analysis = True self._evaluator.analysis_modules = [self._module_node] module = self._get_module() try: for node in get_executable_nodes(self._module_node): context = module.create_context(node) if node.type in ('funcdef', 'classdef'): # Resolve the decorators. tree_name_to_contexts(self._evaluator, context, node.children[1]) elif isinstance(node, tree.Import): import_names = set(node.get_defined_names()) if node.is_nested(): import_names |= set(path[-1] for path in node.get_paths()) for n in import_names: imports.infer_import(context, n) elif node.type == 'expr_stmt': types = context.eval_node(node) for testlist in node.children[:-1:2]: # Iterate tuples. unpack_tuple_to_dict(context, types, testlist) else: if node.type == 'name': defs = self._evaluator.goto_definitions(context, node) else: defs = evaluate_call_of_leaf(context, node) try_iter_content(defs) self._evaluator.reset_recursion_limitations() ana = [a for a in self._evaluator.analysis if self.path == a.path] return sorted(set(ana), key=lambda x: x.line) finally: self._evaluator.is_analysis = False
class Script(object): """ A Script is the base for completions, goto or whatever you want to do with |jedi|. You can either use the ``source`` parameter or ``path`` to read a file. Usually you're going to want to use both of them (in an editor). The script might be analyzed in a different ``sys.path`` than |jedi|: - if `sys_path` parameter is not ``None``, it will be used as ``sys.path`` for the script; - if `sys_path` parameter is ``None`` and ``VIRTUAL_ENV`` environment variable is defined, ``sys.path`` for the specified environment will be guessed (see :func:`jedi.evaluate.sys_path.get_venv_path`) and used for the script; - otherwise ``sys.path`` will match that of |jedi|. :param source: The source code of the current file, separated by newlines. :type source: str :param line: The line to perform actions on (starting with 1). :type line: int :param column: The column of the cursor (starting with 0). :type column: int :param path: The path of the file in the file system, or ``''`` if it hasn't been saved yet. :type path: str or None :param encoding: The encoding of ``source``, if it is not a ``unicode`` object (default ``'utf-8'``). :type encoding: str :param sys_path: ``sys.path`` to use during analysis of the script :type sys_path: list :param environment: TODO :type environment: Environment """ def __init__(self, source=None, line=None, column=None, path=None, encoding='utf-8', sys_path=None, environment=None): self._orig_path = path # An empty path (also empty string) should always result in no path. self.path = os.path.abspath(path) if path else None if source is None: # TODO add a better warning than the traceback! with open(path, 'rb') as f: source = f.read() # Load the Python grammar of the current interpreter. self._grammar = parso.load_grammar() if sys_path is not None and not is_py3: sys_path = list(map(force_unicode, sys_path)) # Load the Python grammar of the current interpreter. project = get_default_project( os.path.dirname(self.path)if path else os.getcwd() ) # TODO deprecate and remove sys_path from the Script API. if sys_path is not None: project._sys_path = sys_path self._evaluator = Evaluator( project, environment=environment, script_path=self.path ) debug.speed('init') self._module_node, source = self._evaluator.parse_and_get_code( code=source, path=self.path, encoding=encoding, cache=False, # No disk cache, because the current script often changes. diff_cache=settings.fast_parser, cache_path=settings.cache_directory, ) debug.speed('parsed') self._code_lines = parso.split_lines(source, keepends=True) self._code = source line = max(len(self._code_lines), 1) if line is None else line if not (0 < line <= len(self._code_lines)): raise ValueError('`line` parameter is not in a valid range.') line_string = self._code_lines[line - 1] line_len = len(line_string) if line_string.endswith('\r\n'): line_len -= 1 if line_string.endswith('\n'): line_len -= 1 column = line_len if column is None else column if not (0 <= column <= line_len): raise ValueError('`column` parameter (%d) is not in a valid range ' '(0-%d) for line %d (%r).' % ( column, line_len, line, line_string)) self._pos = line, column self._path = path cache.clear_time_caches() debug.reset_time() def _get_module(self): name = '__main__' if self.path is not None: import_names = transform_path_to_dotted(self._evaluator.get_sys_path(), self.path) if import_names is not None: name = '.'.join(import_names) module = ModuleContext( self._evaluator, self._module_node, self.path, code_lines=self._code_lines ) imports.add_module_to_cache(self._evaluator, name, module) return module def __repr__(self): return '<%s: %s %r>' % ( self.__class__.__name__, repr(self._orig_path), self._evaluator.environment, ) def completions(self): """ Return :class:`classes.Completion` objects. Those objects contain information about the completions, more than just names. :return: Completion objects, sorted by name and __ comes last. :rtype: list of :class:`classes.Completion` """ debug.speed('completions start') completion = Completion( self._evaluator, self._get_module(), self._code_lines, self._pos, self.call_signatures ) completions = completion.completions() def iter_import_completions(): for c in completions: tree_name = c._name.tree_name if tree_name is None: continue definition = tree_name.get_definition() if definition is not None \ and definition.type in ('import_name', 'import_from'): yield c if len(list(iter_import_completions())) > 10: # For now disable completions if there's a lot of imports that # might potentially be resolved. This is the case for tensorflow # and has been fixed for it. This is obviously temporary until we # have a better solution. self._evaluator.infer_enabled = False debug.speed('completions end') return completions def goto_definitions(self): """ Return the definitions of a the path under the cursor. goto function! This follows complicated paths and returns the end, not the first definition. The big difference between :meth:`goto_assignments` and :meth:`goto_definitions` is that :meth:`goto_assignments` doesn't follow imports and statements. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ leaf = self._module_node.get_name_of_position(self._pos) if leaf is None: leaf = self._module_node.get_leaf_for_position(self._pos) if leaf is None: return [] context = self._evaluator.create_context(self._get_module(), leaf) definitions = helpers.evaluate_goto_definition(self._evaluator, context, leaf) names = [s.name for s in definitions] defs = [classes.Definition(self._evaluator, name) for name in names] # The additional set here allows the definitions to become unique in an # API sense. In the internals we want to separate more things than in # the API. return helpers.sorted_definitions(set(defs)) def goto_assignments(self, follow_imports=False, follow_builtin_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :param follow_imports: The goto call will follow imports. :param follow_builtin_imports: If follow_imports is True will decide if it follow builtin imports. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names, check): for name in names: if check(name): new_names = list(filter_follow_imports(name.goto(), check)) found_builtin = False if follow_builtin_imports: for new_name in new_names: if new_name.start_pos is None: found_builtin = True if found_builtin and not isinstance(name, imports.SubModuleName): yield name else: for new_name in new_names: yield new_name else: yield name tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: return [] context = self._evaluator.create_context(self._get_module(), tree_name) names = list(self._evaluator.goto(context, tree_name)) if follow_imports: def check(name): return name.is_import() else: def check(name): return isinstance(name, imports.SubModuleName) names = filter_follow_imports(names, check) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs) def usages(self, additional_module_paths=(), **kwargs): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :param additional_module_paths: Deprecated, never ever worked. :param include_builtins: Default True, checks if a usage is a builtin (e.g. ``sys``) and in that case does not return it. :rtype: list of :class:`classes.Definition` """ if additional_module_paths: warnings.warn( "Deprecated since version 0.12.0. This never even worked, just ignore it.", DeprecationWarning, stacklevel=2 ) def _usages(include_builtins=True): tree_name = self._module_node.get_name_of_position(self._pos) if tree_name is None: # Must be syntax return [] names = usages.usages(self._get_module(), tree_name) definitions = [classes.Definition(self._evaluator, n) for n in names] if not include_builtins: definitions = [d for d in definitions if not d.in_builtin_module()] return helpers.sorted_definitions(definitions) return _usages(**kwargs) def call_signatures(self): """ Return the function object of the call you're currently in. E.g. if the cursor is here:: abs(# <-- cursor is here This would return the ``abs`` function. On the other hand:: abs()# <-- cursor is here This would return an empty list.. :rtype: list of :class:`classes.CallSignature` """ call_signature_details = \ helpers.get_call_signature_details(self._module_node, self._pos) if call_signature_details is None: return [] context = self._evaluator.create_context( self._get_module(), call_signature_details.bracket_leaf ) definitions = helpers.cache_call_signatures( self._evaluator, context, call_signature_details.bracket_leaf, self._code_lines, self._pos ) debug.speed('func_call followed') return [classes.CallSignature(self._evaluator, d.name, call_signature_details.bracket_leaf.start_pos, call_signature_details.call_index, call_signature_details.keyword_name_str) for d in definitions if hasattr(d, 'py__call__')] def _analysis(self): self._evaluator.is_analysis = True self._evaluator.analysis_modules = [self._module_node] module = self._get_module() try: for node in get_executable_nodes(self._module_node): context = module.create_context(node) if node.type in ('funcdef', 'classdef'): # Resolve the decorators. tree_name_to_contexts(self._evaluator, context, node.children[1]) elif isinstance(node, tree.Import): import_names = set(node.get_defined_names()) if node.is_nested(): import_names |= set(path[-1] for path in node.get_paths()) for n in import_names: imports.infer_import(context, n) elif node.type == 'expr_stmt': types = context.eval_node(node) for testlist in node.children[:-1:2]: # Iterate tuples. unpack_tuple_to_dict(context, types, testlist) else: if node.type == 'name': defs = self._evaluator.goto_definitions(context, node) else: defs = evaluate_call_of_leaf(context, node) try_iter_content(defs) self._evaluator.reset_recursion_limitations() ana = [a for a in self._evaluator.analysis if self.path == a.path] return sorted(set(ana), key=lambda x: x.line) finally: self._evaluator.is_analysis = False
class Script(object): """ A Script is the base for completions, goto or whatever you want to do with |jedi|. You can either use the ``source`` parameter or ``path`` to read a file. Usually you're going to want to use both of them (in an editor). The script might be analyzed in a different ``sys.path`` than |jedi|: - if `sys_path` parameter is not ``None``, it will be used as ``sys.path`` for the script; - if `sys_path` parameter is ``None`` and ``VIRTUAL_ENV`` environment variable is defined, ``sys.path`` for the specified environment will be guessed (see :func:`jedi.evaluate.sys_path.get_venv_path`) and used for the script; - otherwise ``sys.path`` will match that of |jedi|. :param source: The source code of the current file, separated by newlines. :type source: str :param line: The line to perform actions on (starting with 1). :type line: int :param column: The column of the cursor (starting with 0). :type column: int :param path: The path of the file in the file system, or ``''`` if it hasn't been saved yet. :type path: str or None :param encoding: The encoding of ``source``, if it is not a ``unicode`` object (default ``'utf-8'``). :type encoding: str :param source_encoding: The encoding of ``source``, if it is not a ``unicode`` object (default ``'utf-8'``). :type encoding: str :param sys_path: ``sys.path`` to use during analysis of the script :type sys_path: list """ def __init__(self, source=None, line=None, column=None, path=None, encoding='utf-8', sys_path=None): self._orig_path = path # An empty path (also empty string) should always result in no path. self.path = os.path.abspath(path) if path else None if source is None: # TODO add a better warning than the traceback! with open(path, 'rb') as f: source = f.read() # TODO do we really want that? self._source = python_bytes_to_unicode(source, encoding, errors='replace') self._code_lines = split_lines(self._source) line = max(len(self._code_lines), 1) if line is None else line if not (0 < line <= len(self._code_lines)): raise ValueError('`line` parameter is not in a valid range.') line_len = len(self._code_lines[line - 1]) column = line_len if column is None else column if not (0 <= column <= line_len): raise ValueError('`column` parameter is not in a valid range.') self._pos = line, column self._path = path cache.clear_time_caches() debug.reset_time() # Load the Python grammar of the current interpreter. self._grammar = parso.load_grammar() project = Project(sys_path=sys_path) self._evaluator = Evaluator(self._grammar, project) project.add_script_path(self.path) debug.speed('init') @cache.memoize_method def _get_module_node(self): return self._grammar.parse( code=self._source, path=self.path, cache=False, # No disk cache, because the current script often changes. diff_cache=True, cache_path=settings.cache_directory ) @cache.memoize_method def _get_module(self): module = ModuleContext( self._evaluator, self._get_module_node(), self.path ) if self.path is not None: name = dotted_path_in_sys_path(self._evaluator.project.sys_path, self.path) if name is not None: imports.add_module(self._evaluator, name, module) return module def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, repr(self._orig_path)) def completions(self): """ Return :class:`classes.Completion` objects. Those objects contain information about the completions, more than just names. :return: Completion objects, sorted by name and __ comes last. :rtype: list of :class:`classes.Completion` """ debug.speed('completions start') completion = Completion( self._evaluator, self._get_module(), self._code_lines, self._pos, self.call_signatures ) completions = completion.completions() debug.speed('completions end') return completions def goto_definitions(self): """ Return the definitions of a the path under the cursor. goto function! This follows complicated paths and returns the end, not the first definition. The big difference between :meth:`goto_assignments` and :meth:`goto_definitions` is that :meth:`goto_assignments` doesn't follow imports and statements. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ module_node = self._get_module_node() leaf = module_node.get_name_of_position(self._pos) if leaf is None: leaf = module_node.get_leaf_for_position(self._pos) if leaf is None: return [] context = self._evaluator.create_context(self._get_module(), leaf) definitions = helpers.evaluate_goto_definition(self._evaluator, context, leaf) names = [s.name for s in definitions] defs = [classes.Definition(self._evaluator, name) for name in names] # The additional set here allows the definitions to become unique in an # API sense. In the internals we want to separate more things than in # the API. return helpers.sorted_definitions(set(defs)) def goto_assignments(self, follow_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names, check): for name in names: if check(name): for result in filter_follow_imports(name.goto(), check): yield result else: yield name tree_name = self._get_module_node().get_name_of_position(self._pos) if tree_name is None: return [] context = self._evaluator.create_context(self._get_module(), tree_name) names = list(self._evaluator.goto(context, tree_name)) if follow_imports: def check(name): if isinstance(name, ModuleName): return False return name.api_type == 'module' else: def check(name): return isinstance(name, imports.SubModuleName) names = filter_follow_imports(names, check) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs) def usages(self, additional_module_paths=()): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :rtype: list of :class:`classes.Definition` """ tree_name = self._get_module_node().get_name_of_position(self._pos) if tree_name is None: # Must be syntax return [] names = usages.usages(self._get_module(), tree_name) definitions = [classes.Definition(self._evaluator, n) for n in names] return helpers.sorted_definitions(definitions) def call_signatures(self): """ Return the function object of the call you're currently in. E.g. if the cursor is here:: abs(# <-- cursor is here This would return the ``abs`` function. On the other hand:: abs()# <-- cursor is here This would return an empty list.. :rtype: list of :class:`classes.CallSignature` """ call_signature_details = \ helpers.get_call_signature_details(self._get_module_node(), self._pos) if call_signature_details is None: return [] context = self._evaluator.create_context( self._get_module(), call_signature_details.bracket_leaf ) definitions = helpers.cache_call_signatures( self._evaluator, context, call_signature_details.bracket_leaf, self._code_lines, self._pos ) debug.speed('func_call followed') return [classes.CallSignature(self._evaluator, d.name, call_signature_details.bracket_leaf.start_pos, call_signature_details.call_index, call_signature_details.keyword_name_str) for d in definitions if hasattr(d, 'py__call__')] def _analysis(self): self._evaluator.is_analysis = True module_node = self._get_module_node() self._evaluator.analysis_modules = [module_node] try: for node in get_executable_nodes(module_node): context = self._get_module().create_context(node) if node.type in ('funcdef', 'classdef'): # Resolve the decorators. tree_name_to_contexts(self._evaluator, context, node.children[1]) elif isinstance(node, tree.Import): import_names = set(node.get_defined_names()) if node.is_nested(): import_names |= set(path[-1] for path in node.get_paths()) for n in import_names: imports.infer_import(context, n) elif node.type == 'expr_stmt': types = context.eval_node(node) for testlist in node.children[:-1:2]: # Iterate tuples. unpack_tuple_to_dict(context, types, testlist) else: if node.type == 'name': defs = self._evaluator.goto_definitions(context, node) else: defs = evaluate_call_of_leaf(context, node) try_iter_content(defs) self._evaluator.reset_recursion_limitations() ana = [a for a in self._evaluator.analysis if self.path == a.path] return sorted(set(ana), key=lambda x: x.line) finally: self._evaluator.is_analysis = False
class Script(object): """ A Script is the base for completions, goto or whatever you want to do with |jedi|. You can either use the ``source`` parameter or ``path`` to read a file. Usually you're going to want to use both of them (in an editor). The script might be analyzed in a different ``sys.path`` than |jedi|: - if `sys_path` parameter is not ``None``, it will be used as ``sys.path`` for the script; - if `sys_path` parameter is ``None`` and ``VIRTUAL_ENV`` environment variable is defined, ``sys.path`` for the specified environment will be guessed (see :func:`jedi.evaluate.sys_path.get_venv_path`) and used for the script; - otherwise ``sys.path`` will match that of |jedi|. :param source: The source code of the current file, separated by newlines. :type source: str :param line: The line to perform actions on (starting with 1). :type line: int :param column: The column of the cursor (starting with 0). :type column: int :param path: The path of the file in the file system, or ``''`` if it hasn't been saved yet. :type path: str or None :param encoding: The encoding of ``source``, if it is not a ``unicode`` object (default ``'utf-8'``). :type encoding: str :param source_encoding: The encoding of ``source``, if it is not a ``unicode`` object (default ``'utf-8'``). :type encoding: str :param sys_path: ``sys.path`` to use during analysis of the script :type sys_path: list """ def __init__(self, source=None, line=None, column=None, path=None, encoding='utf-8', source_path=None, source_encoding=None, sys_path=None): if source_path is not None: warnings.warn("Use path instead of source_path.", DeprecationWarning) path = source_path if source_encoding is not None: warnings.warn("Use encoding instead of source_encoding.", DeprecationWarning) encoding = source_encoding self._orig_path = path # An empty path (also empty string) should always result in no path. self.path = os.path.abspath(path) if path else None if source is None: # TODO add a better warning than the traceback! with open(path, 'rb') as f: source = f.read() self._source = common.source_to_unicode(source, encoding) self._code_lines = common.splitlines(self._source) line = max(len(self._code_lines), 1) if line is None else line if not (0 < line <= len(self._code_lines)): raise ValueError('`line` parameter is not in a valid range.') line_len = len(self._code_lines[line - 1]) column = line_len if column is None else column if not (0 <= column <= line_len): raise ValueError('`column` parameter is not in a valid range.') self._pos = line, column self._path = path cache.clear_time_caches() debug.reset_time() self._grammar = load_grammar(version='%s.%s' % sys.version_info[:2]) if sys_path is None: venv = os.getenv('VIRTUAL_ENV') if venv: sys_path = list(get_venv_path(venv)) self._evaluator = Evaluator(self._grammar, sys_path=sys_path) debug.speed('init') @cache.memoize_method def _get_module_node(self): cache.invalidate_star_import_cache(self._path) parser = FastParser(self._grammar, self._source, self.path) save_parser(self.path, parser, pickling=False) return parser.module @cache.memoize_method def _get_module(self): module = er.ModuleContext(self._evaluator, self._get_module_node()) imports.add_module(self._evaluator, module.name.string_name, module) return module @property def source_path(self): """ .. deprecated:: 0.7.0 Use :attr:`.path` instead. .. todo:: Remove! """ warnings.warn("Use path instead of source_path.", DeprecationWarning) return self.path def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, repr(self._orig_path)) def completions(self): """ Return :class:`classes.Completion` objects. Those objects contain information about the completions, more than just names. :return: Completion objects, sorted by name and __ comes last. :rtype: list of :class:`classes.Completion` """ debug.speed('completions start') completion = Completion( self._evaluator, self._get_module(), self._code_lines, self._pos, self.call_signatures ) completions = completion.completions() debug.speed('completions end') return completions def goto_definitions(self): """ Return the definitions of a the path under the cursor. goto function! This follows complicated paths and returns the end, not the first definition. The big difference between :meth:`goto_assignments` and :meth:`goto_definitions` is that :meth:`goto_assignments` doesn't follow imports and statements. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ module_node = self._get_module_node() leaf = module_node.name_for_position(self._pos) if leaf is None: leaf = module_node.get_leaf_for_position(self._pos) if leaf is None: return [] context = self._evaluator.create_context(self._get_module(), leaf) definitions = helpers.evaluate_goto_definition(self._evaluator, context, leaf) names = [s.name for s in definitions] defs = [classes.Definition(self._evaluator, name) for name in names] # The additional set here allows the definitions to become unique in an # API sense. In the internals we want to separate more things than in # the API. return helpers.sorted_definitions(set(defs)) def goto_assignments(self, follow_imports=False): """ Return the first definition found, while optionally following imports. Multiple objects may be returned, because Python itself is a dynamic language, which means depending on an option you can have two different versions of a function. :rtype: list of :class:`classes.Definition` """ def filter_follow_imports(names): for name in names: if isinstance(name, (imports.ImportName, TreeNameDefinition)): for context in name.infer(): yield context.name else: yield name names = self._goto() if follow_imports: names = filter_follow_imports(names) defs = [classes.Definition(self._evaluator, d) for d in set(names)] return helpers.sorted_definitions(defs) def _goto(self): """ Used for goto_assignments and usages. """ name = self._get_module_node().name_for_position(self._pos) if name is None: return [] context = self._evaluator.create_context(self._get_module(), name) return list(self._evaluator.goto(context, name)) def usages(self, additional_module_paths=()): """ Return :class:`classes.Definition` objects, which contain all names that point to the definition of the name under the cursor. This is very useful for refactoring (renaming), or to show all usages of a variable. .. todo:: Implement additional_module_paths :rtype: list of :class:`classes.Definition` """ temp, settings.dynamic_flow_information = \ settings.dynamic_flow_information, False try: module_node = self._get_module_node() user_stmt = module_node.get_statement_for_position(self._pos) definition_names = self._goto() if not definition_names and isinstance(user_stmt, tree.Import): # For not defined imports (goto doesn't find something, we take # the name as a definition. This is enough, because every name # points to it. name = user_stmt.name_for_position(self._pos) if name is None: # Must be syntax return [] definition_names = [TreeNameDefinition(self._get_module(), name)] if not definition_names: # Without a definition for a name we cannot find references. return [] definition_names = usages.resolve_potential_imports(self._evaluator, definition_names) modules = set([d.get_root_context() for d in definition_names]) modules.add(self._get_module()) definitions = usages.usages(self._evaluator, definition_names, modules) finally: settings.dynamic_flow_information = temp return helpers.sorted_definitions(set(definitions)) def call_signatures(self): """ Return the function object of the call you're currently in. E.g. if the cursor is here:: abs(# <-- cursor is here This would return the ``abs`` function. On the other hand:: abs()# <-- cursor is here This would return an empty list.. :rtype: list of :class:`classes.CallSignature` """ call_signature_details = \ helpers.get_call_signature_details(self._get_module_node(), self._pos) if call_signature_details is None: return [] context = self._evaluator.create_context( self._get_module(), call_signature_details.bracket_leaf ) with common.scale_speed_settings(settings.scale_call_signatures): definitions = helpers.cache_call_signatures( self._evaluator, context, call_signature_details.bracket_leaf, self._code_lines, self._pos ) debug.speed('func_call followed') return [classes.CallSignature(self._evaluator, d.name, call_signature_details.bracket_leaf.start_pos, call_signature_details.call_index, call_signature_details.keyword_name_str) for d in definitions if hasattr(d, 'py__call__')] def _analysis(self): self._evaluator.is_analysis = True module_node = self._get_module_node() self._evaluator.analysis_modules = [module_node] try: for node in module_node.nodes_to_execute(): context = self._get_module().create_context(node) if node.type in ('funcdef', 'classdef'): # TODO This is stupid, should be private from jedi.evaluate.finder import _name_to_types # Resolve the decorators. _name_to_types(self._evaluator, context, node.children[1]) elif isinstance(node, tree.Import): import_names = set(node.get_defined_names()) if node.is_nested(): import_names |= set(path[-1] for path in node.paths()) for n in import_names: imports.infer_import(context, n) elif node.type == 'expr_stmt': types = context.eval_node(node) for testlist in node.children[:-1:2]: # Iterate tuples. unpack_tuple_to_dict(self._evaluator, types, testlist) else: try_iter_content(self._evaluator.goto_definitions(context, node)) self._evaluator.reset_recursion_limitations() ana = [a for a in self._evaluator.analysis if self.path == a.path] return sorted(set(ana), key=lambda x: x.line) finally: self._evaluator.is_analysis = False