pappasam / coc-jedi

coc.nvim wrapper for https://github.com/pappasam/jedi-language-server

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"SyntaxError: no binding for nonlocal 'X' found" when X is a parameter

Terseus opened this issue · comments

Observed behavior

When Jedi finds a function parameter declared as nonlocal inside an inner function, the "nonlocal" it's marked wrongly as a syntax error.

This code snippet reproduce the issue:

def function_with_nonlocal_param(param=None):
    def inner():
        nonlocal param  # Jedi: SyntaxError: no binding for nonlocal 'param' found
        if param is None:
            param = "None (string)"
        print(f"Param (inner): {param}")

    inner()
    print(f"Param (outer): {param}")


def function_with_nonlocal_value(param=None):
    value = param

    def inner():
        nonlocal value  # No Jedi error here
        if value is None:
            value = "None (string)"
        print(f"Value (inner): {value}")

    inner()
    print(f"Value (outer): {value}")


if __name__ == '__main__':
    function_with_nonlocal_param(10)
    function_with_nonlocal_param()
    function_with_nonlocal_value(10)
    function_with_nonlocal_value()

The output with Python 3.7 or greater shows that both nonlocal directive uses are legal and works as expected:

$ python jedi-nonlocal-poc.py
Param (inner): 10
Param (outer): 10
Param (inner): None (string)
Param (outer): None (string)
Value (inner): 10
Value (outer): 10
Value (inner): None (string)
Value (outer): None (string)

Expected behavior

Jedi should handle correctly the legal "nonlocal" uses and don't mark it as errors.

Additional notes

I'm using:

  • Python 3.9.1
  • Neovim 0.4.4
  • coc-jedi 0.18.1
  • jedi-language-server 0.23.1

Here's the pip freeze output from the jedi-language-server virtualenv:

click==7.1.2
jedi==0.18.0
jedi-language-server==0.23.1
parso==0.8.1
pygls==0.9.1

Interesting... I've just tried with the latest version of jedi-language-server / coc-jedi and I can't reproduce (I'm on Python 3.8, neovim 0.5.0-dev).

Which Python version are you using with your editor? Maybe Jedi is installed / observing a virtual environment or system installation of Python that precedes 3.7 while you're editing?

In my work maybe but I've reproduced the bug in my computer, which only have Python 3.9, so no it's not because it's seeing a Python version without nonlocal support, or partial support, or anything like that.
I even checked out the coc-jedi venv Python version, it's 3.9.1:

$ pwd
/home/terseus/.config/coc/extensions/node_modules/coc-jedi
$ which python
/home/terseus/.config/coc/extensions/node_modules/coc-jedi/.venv/bin/python
$ python --version
Python 3.9.1

However I cloned the jedi repo and wrote the following static analysis test and the bug doesn't reproduce, so doesn't look jedi's fault:

def nonlocal_variable():
    def inner():
        nonlocal value
        value = 10

    value = 20


def nonlocal_parameter(param):
    def inner():
        nonlocal param
        param = 10

Now, I've just tried it again while writing this and noticed that with this code, the error doesn't happen in my editor either:

def nonlocal_parameter(param):
    def inner():
        nonlocal param
        param = 10

    param = 20    # Thanks to this line the error is gone!

I suspect that jedi-language-server it's not recognizing the param parameter to be available in the inner context but it does when it's a variable explicitly declared in the outer context, I'll try to reproduce the bug directly with jedi-language-server and get you back.

@pappasam I've added the pip freeze output from the coc-jedi virtualenv, just in case.

@Terseus thanks for the detail, hopefully we can get to the bottom of this soon!

I've done my best to replicate the important parts of your environment:

Python: 3.9.1
Jedi: 0.18.1

Once set up, I've written and run a small script that (on my end) appears to prove that this is a jedi issue (not to say it's not important to me, just that there's little I can do to fix it within jedi-language-server itself).

Please copy/paste the following code into a source file, make sure you're running Python 3.9.1 with jedi 0.18.1 installed, and run it.

import jedi

code = """
def nonlocal_variable():
    def inner():
        nonlocal value
        value = 10

    value = 20


def nonlocal_parameter(param):
    def inner():
        nonlocal param
        param = 10
"""

script = jedi.Script(code=code)

errors = script.get_syntax_errors()
print(errors)
print(errors[0].get_message())

The following lines should print to the console:

[<SyntaxError from=(12, 17) to=(12, 22)>]
SyntaxError: no binding for nonlocal 'param' found

This, to me, demonstrates that jedi is returning the syntax error message without any help (or hindrance) from jedi-language-server.

Please let me know if this is what you observe. If not, we may be running into an operating system issue... I'm on Ubuntu 20.0.4.

@pappasam Yep you're right, definitely it's a Jedi bug.

Sorry, I clearly made a mistake thinking a static analysis test (like these) should have exposed the error and didn't know enough of the Jedi API to write a simpler test like yours.

I've opened an issue in Jedi with this problem: https://github.com/davidhalter/jedi/issues/1753

Thanks for your help ^_^