microsoft / python-language-server

Microsoft Language Server for Python
Apache License 2.0
912 stars 131 forks source link

concurrent.futures.ThreadPoolExecutor doesn't autocomplete #1979

Open fireattack opened 4 years ago

fireattack commented 4 years ago

Environment data

Expected behaviour

It shows "ThreadPoolExecutor"

Actual behaviour

image

Logs

https://gist.github.com/fireattack/599faa0f0f02d8e29bfd44bee094d4ba

Code Snippet / Additional lnformation

import concurrent.futures  

concurrent.futures.t
fireattack commented 4 years ago

Same problem goes to ProcessPoolExecutor.

Not very familiar with how it works, but at least they are included in __all__ of lib\concurrent\futures\__init__.py.

jakebailey commented 4 years ago

Seems like a bug, given ThreadPoolExecutor and ProcessPoolExecutor are present in the stubs.

MikhailArkhipov commented 4 years ago

3.6 and 3.8 yield me

image

fireattack commented 4 years ago

@MikhailArkhipov

thanks for testing. However, I can still reproduce this bug with newest insider.

image image

Python 3.8.1 64-bit

fireattack commented 4 years ago

Can also reproduce the bug on

image

MikhailArkhipov commented 4 years ago

Module code is based on dynamic assignment. Since LS does not run the code, it does not know about ThreadPoolExecutor.

    if name == 'ThreadPoolExecutor':
        from .thread import ThreadPoolExecutor as te
        ThreadPoolExecutor = te
        return te

static analysis could pull the name from __all__ but it would not know its members. The type is present in stubs, but it is in thread.pyi which is used if thread is imported explicitly.

Related: https://github.com/microsoft/python-language-server/issues/1371

__all__ = (
    'FIRST_COMPLETED',
    'FIRST_EXCEPTION',
    'ALL_COMPLETED',
    'CancelledError',
    'TimeoutError',
    'BrokenExecutor',
    'Future',
    'Executor',
    'wait',
    'as_completed',
    'ProcessPoolExecutor',
    'ThreadPoolExecutor',
)

def __dir__():
    return __all__ + ('__author__', '__doc__')

def __getattr__(name):
    global ProcessPoolExecutor, ThreadPoolExecutor

    if name == 'ProcessPoolExecutor':
        from .process import ProcessPoolExecutor as pe
        ProcessPoolExecutor = pe
        return pe

    if name == 'ThreadPoolExecutor':
        from .thread import ThreadPoolExecutor as te
        ThreadPoolExecutor = te
        return te