python — obtain completions Link to heading

Today, let’s have a look at how to obtain completions from Python static analyzer. By completions, I mean the suggestions you get when you…

Completions example

Jedi Link to heading

The easiest way is to use Jedi, a static analysis tool for Python that is typically used in IDEs/editors plugins. Jedi has a focus on autocompletion and goto functionality.

Assuming you have jedi installed in the Python environment,

import jedi

lines = [
    "import sys",
    "print(sys.s"
]

script = jedi.Script("\n".join(lines))
# NOTE: line starts at 1, not 0
completions = script.complete(line=len(lines), column=len(lines[-1]))
for completion in completions:
    print(completion.complete, completion.name)

That’s it! The only caveat is that the line parameter for complete() method expects 1-based line numbering. This will result in

et_asyncgen_hooks set_asyncgen_hooks
etdlopenflags setdlopenflags
etprofile setprofile
etrecursionlimit setrecursionlimit
etswitchinterval setswitchinterval
ettrace settrace
tderr stderr
tdin stdin
tdout stdout

Here, we are printing the completion excluding the prefix s and then the full completion with the prefix.

MultiLspy Link to heading

Multilspy a research library that makes it easy to get some useful information from LSP-compliant servers. Though not all LSP servers are supported, it should be relatively easy to extend support for one.

We would like to obtain completions at the final character in the second line. To do so, install multilspy

pip install https://github.com/microsoft/multilspy/archive/main.zip

and create the following main.py

from multilspy import SyncLanguageServer
from multilspy.multilspy_config import MultilspyConfig
from multilspy.multilspy_logger import MultilspyLogger

config = MultilspyConfig.from_dict({"code_language": "python"}) # Also supports "python", "rust", "csharp"
logger = MultilspyLogger()
lsp = SyncLanguageServer.create(config, logger, "/abs/path/to/project") # where test.py is in

with lsp.start_server():
    completions = lsp.request_completions(
        "test.py", # relative path
        line=1, # this is 0-based number
        column=len("print(sys."), # must stop at "."
        allow_incomplete=True
    )

for c in completions:
    if c['completionText'].startswith('s'):
        print(c)

Don’t run it just yet! It will sit still as of today. For whatever the reason is, you need to add the following line into JediServer::start_server

# multilspy/src/multilspy/language_servers/jedi_language_server/jedi_server.py
@@ -113,6 +113,7 @@
         }

         self.server.notify.initialized({})
+        self.completions_available.set()

         yield self

Another caveat is that we can request autocompletion only at the trigger points, such as . character. When run successful, one would obtain

{'completionText': 'setprofile', 'kind': 3}
{'completionText': 'set_asyncgen_hooks', 'kind': 3}
{'completionText': 'stdout', 'kind': 6}
{'completionText': 'setrecursionlimit', 'kind': 3}
{'completionText': 'setswitchinterval', 'kind': 3}
{'completionText': 'stdin', 'kind': 6}
{'completionText': 'setdlopenflags', 'kind': 3}
{'completionText': 'stderr', 'kind': 6}
{'completionText': 'settrace', 'kind': 3}

It is the caller’s responsibility to filter out the completion that starts with a given prefix.

Note that the two results are actually the same with different order. That is because multilspy uses jedi in the backed to get the completion. With multilspy, one should be able to easily swap its LSP server and also add support for different languages, besides java, rust, python, and csharp that are already supported.

References Link to heading

Language Server Protocol Language Server Protocol documentation and specification page. microsoft.github.io

Jedi - an awesome autocompletion, static analysis and refactoring library for Python - Jedi 0.19.1… Jedi is a static analysis tool for Python that is typically used in IDEs/editors plugins. Jedi has a focus on… jedi.readthedocs.io

GitHub - microsoft/multilspy: multispy is a lsp client library in Python intended to be used to… multispy is a lsp client library in Python intended to be used to build applications around language servers. … github.com