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
Пример #2
0
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
Пример #3
0
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
Пример #4
0
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
Пример #5
0
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