Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AssertionError completing a trailer with a space after dot #1954

Open
K-410 opened this issue Sep 12, 2023 · 1 comment
Open

AssertionError completing a trailer with a space after dot #1954

K-410 opened this issue Sep 12, 2023 · 1 comment
Labels

Comments

@K-410
Copy link

K-410 commented Sep 12, 2023

>>> from jedi import __version__
>>> __version__
'0.19.0'

Also tested on 0.18.2.

Test code. See traceback at bottom:

src = "object. \n"

from jedi.api import Interpreter
Interpreter(src, []).complete(1, 8)

This seems to happen because in Completion._complete_python,

            elif nonterminals[-1] in ('trailer', 'dotted_name') and nodes[-1] == '.':
                dot = self._module_node.get_leaf_for_position(self._position)
                if dot.type == "endmarker":
                    # This is a bit of a weird edge case, maybe we can somehow
                    # generalize this.
                    dot = leaf.get_previous_leaf()
                cached_name, n = self._complete_trailer(dot.get_previous_leaf())

Specifically:

dot = self._module_node.get_leaf_for_position(self._position)

is gives us the newline leaf I'm assuming Jedi expects dot to be "." operator. But we added a space, which is still valid python. And there's a newline, but jedi only accounts for endmarker.

>>> eval("object.      mro")
>>> <built-in method mro of type object at 0x00007FFFA9EEBBB0>

When self._complete_trailer is called with dot.get_previous_leaf(), what's actually being passed is the dot operator.

cached_name, n = self._complete_trailer(dot.get_previous_leaf())  # dot.get_previous_leaf() == the dot operator.

This results in the following branch in syntax_tree._infer_node() to be taken, throwing AssertionError.

    elif typ == 'operator':
        # Must be an ellipsis, other operators are not inferred.
        if element.value != '...':
            origin = element.parent
            raise AssertionError("unhandled operator %s in %s " % (repr(element.value), origin))
        return ValueSet([compiled.builtin_from_name(inference_state, 'Ellipsis')])

The traceback

Traceback (most recent call last):
  File "Z:\py_jedi\main.py", line 7, in <module>
    Interpreter(src, []).complete(1, 8)
  File "z:\py_jedi\Lib\site-packages\jedi\api\helpers.py", line 487, in wrapper  
    return func(self, line, column, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\api\__init__.py", line 214, in complete
    return completion.complete()
           ^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\api\completion.py", line 170, in complete
    cached_name, completion_names = self._complete_python(leaf)
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\api\completion.py", line 284, in _complete_python
    cached_name, n = self._complete_trailer(dot.get_previous_leaf())
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\api\completion.py", line 385, in _complete_trailer
    values = infer_call_of_leaf(inferred_context, previous_leaf)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\inference\helpers.py", line 79, in infer_call_of_leaf
    return context.infer_node(leaf)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\inference\context.py", line 224, in infer_node
    return infer_node(self, node)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\inference\syntax_tree.py", line 157, in infer_node
    return _infer_node_if_inferred(context, element)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\inference\syntax_tree.py", line 170, in _infer_node_if_inferred
    return _infer_node_cached(context, element)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\inference\cache.py", line 44, in wrapper
    rv = function(obj, *args, **kwargs)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\inference\syntax_tree.py", line 175, in _infer_node_cached
    return _infer_node(context, element)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\debug.py", line 81, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\inference\syntax_tree.py", line 83, in wrapper
    return func(context, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "z:\py_jedi\Lib\site-packages\jedi\inference\syntax_tree.py", line 230, in _infer_node
    raise AssertionError("unhandled operator %s in %s " % (repr(element.value), origin))
AssertionError: unhandled operator '.' in <PythonErrorNode: object.@1,0>

Possible fix could be to check if dot.type is either "endmarker" or "newline" before trying the previous leaf. I don't have tests setup.

                dot = self._module_node.get_leaf_for_position(self._position)
                if dot.type in {"endmarker", "newline"}:
                    dot = leaf.get_previous_leaf()
@davidhalter
Copy link
Owner

I suspect that the reason for this is that parso's error recovery is a bit weird and unaccounted for. Would have to check this in detail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants