pappasam / jedi-language-server

A Python language server exclusively for Jedi. If Jedi supports it well, this language server should too.
MIT License
572 stars 44 forks source link

Editor opens internal typeshed file instead of real file in venv #264

Open Morikko opened 1 year ago

Morikko commented 1 year ago

Expected vs. Actual

Expected: When Ctrl-clicking on a function from a given package, the editor opens the actual file in which the function definition is found in the virtual environment.

Actual: When Ctrl-clicking on a function from a given package, the editor opens the typeshed file matching the definition installed within the extension itself.

How to reproduce

  1. Create a new folder/workspace and open vscode;
  2. Create a new Python file with eg.:
import flask

assert flask.__version__ == "2.2.3"

class Route(flask.Blueprint):
    ...

r = Route("test", "test")
r.add_url_rule

(Using Flask here as an example)

  1. Create a new venv: python -m venv env && source env/bin/activate
  2. Install flask: pip install flask
  3. Mouse hover over from_app and click.

The editor will bring up the typeshed in the extension directory, ~/.vscode-oss/extensions/ms-python.python-2023.4.1-universal/pythonFiles/lib/jedilsp/jedi/third_party/typeshed/third_party/2and3/flask/blueprints.pyi instead of opening env/lib64/python3.X/site-packages/flask/blueprints.py, which is what eg. mypy will use to check the project against.

The hover I see:

flask-hover

Technical details

The Flask v2 signature: https://github.com/pallets/flask/blob/main/src/flask/blueprints.py#L408

    def add_url_rule(
        self,
        rule: str,
        endpoint: t.Optional[str] = None,
        view_func: t.Optional[ft.RouteCallable] = None,
        provide_automatic_options: t.Optional[bool] = None,
        **options: t.Any,
    ) -> None:

While it is the old Flask v1 here: https://github.com/davidhalter/typeshed/blob/jedi/third_party/2and3/flask/blueprints.pyi#L55

    def add_url_rule(self, rule: str, endpoint: Optional[str] = ..., view_func: _ViewFunc = ..., **options: Any) -> None: ...

Jedi looks to act normally, so I believe the problem is how it is used by jedi-language-server:

import jedi

assert jedi.__version__ == '0.18.2'

def filter_function_params(completions):
    return [c for c in completions if c.complete.endswith("=")]

def get_function_function_params_completion(func):
    return filter_function_params(
        jedi.Interpreter(
            f"{func.__name__}(",
            [
                {
                    func.__name__: func,
                }
            ],
        ).complete()
    )

print(get_function_function_params_completion(r.add_url_rule))
# [<Completion: endpoint=>, <Completion: provide_automatic_options=>, <Completion: rule=>, <Completion: view_func=>]

provide_automatic_options parameter is present and only Flask v2.

Note: The issue was originally open here: https://github.com/microsoft/vscode-python/issues/20988

pappasam commented 1 year ago

I'm able to reproduce and believe this is an issue with how Jedi's Script.goto logic prioritizes the Jedi-managed typeshed stubs.

Note: we don't use jedi.Interpreter within the editing environment. Instead, we rely on jedi.Script. See: https://jedi.readthedocs.io/en/latest/docs/api.html?highlight=interpreter#script