1
0
forked from VimPlug/jedi

Merge pull request #1642 from PeterJCLaw/mypy

Add an initial mypy config
This commit is contained in:
Dave Halter
2020-08-05 01:09:49 +02:00
committed by GitHub
28 changed files with 120 additions and 60 deletions

View File

@@ -30,8 +30,8 @@ matrix:
install: install:
- 'pip install .[qa]' - 'pip install .[qa]'
script: script:
# Ignore F401, which are unused imports. flake8 is a primitive tool and is sometimes wrong. - 'flake8 jedi setup.py'
- 'flake8 --extend-ignore F401 jedi setup.py' - 'mypy jedi sith.py'
install: install:
- sudo apt-get -y install python3-venv - sudo apt-get -y install python3-venv
- pip install .[testing] - pip install .[testing]

View File

@@ -15,18 +15,18 @@ the interesting information about all operations.
""" """
import re import re
import warnings import warnings
from pathlib import Path
from typing import Optional from typing import Optional
from parso.python.tree import search_ancestor from parso.tree import search_ancestor
from jedi import settings from jedi import settings
from jedi import debug from jedi import debug
from jedi.inference.utils import unite from jedi.inference.utils import unite
from jedi.cache import memoize_method from jedi.cache import memoize_method
from jedi.inference import imports
from jedi.inference.imports import ImportName
from jedi.inference.compiled.mixed import MixedName from jedi.inference.compiled.mixed import MixedName
from jedi.inference.gradual.typeshed import StubModuleValue from jedi.inference.names import ImportName, SubModuleName
from jedi.inference.gradual.stub_value import StubModuleValue
from jedi.inference.gradual.conversion import convert_names, convert_values from jedi.inference.gradual.conversion import convert_names, convert_values
from jedi.inference.base_value import ValueSet from jedi.inference.base_value import ValueSet
from jedi.api.keywords import KeywordName from jedi.api.keywords import KeywordName
@@ -93,17 +93,15 @@ class BaseName:
return self._name.get_root_context() return self._name.get_root_context()
@property @property
def module_path(self) -> Optional[str]: def module_path(self) -> Optional[Path]:
""" """
Shows the file path of a module. e.g. ``/usr/lib/python3.9/os.py`` Shows the file path of a module. e.g. ``/usr/lib/python3.9/os.py``
:rtype: str or None
""" """
module = self._get_module_context() module = self._get_module_context()
if module.is_stub() or not module.is_compiled(): if module.is_stub() or not module.is_compiled():
# Compiled modules should not return a module path even if they # Compiled modules should not return a module path even if they
# have one. # have one.
path = self._get_module_context().py__file__() path: Optional[Path] = self._get_module_context().py__file__()
if path is not None: if path is not None:
return path return path
@@ -186,7 +184,7 @@ class BaseName:
tree_name.is_definition(): tree_name.is_definition():
resolve = True resolve = True
if isinstance(self._name, imports.SubModuleName) or resolve: if isinstance(self._name, SubModuleName) or resolve:
for value in self._name.infer(): for value in self._name.infer():
return value.api_type return value.api_type
return self._name.api_type return self._name.api_type
@@ -497,7 +495,7 @@ class BaseName:
return [self if n == self._name else Name(self._inference_state, n) return [self if n == self._name else Name(self._inference_state, n)
for n in resulting_names] for n in resulting_names]
@property @property # type: ignore[misc]
@memoize_method @memoize_method
def params(self): def params(self):
warnings.warn( warnings.warn(

View File

@@ -1,7 +1,13 @@
_cache = {} from typing import Dict, Tuple, Callable
CacheValues = Tuple[str, str, str]
CacheValuesCallback = Callable[[], CacheValues]
def save_entry(module_name, name, cache): _cache: Dict[str, Dict[str, CacheValues]] = {}
def save_entry(module_name: str, name: str, cache: CacheValues) -> None:
try: try:
module_cache = _cache[module_name] module_cache = _cache[module_name]
except KeyError: except KeyError:
@@ -9,8 +15,8 @@ def save_entry(module_name, name, cache):
module_cache[name] = cache module_cache[name] = cache
def _create_get_from_cache(number): def _create_get_from_cache(number: int) -> Callable[[str, str, CacheValuesCallback], str]:
def _get_from_cache(module_name, name, get_cache_values): def _get_from_cache(module_name: str, name: str, get_cache_values: CacheValuesCallback) -> str:
try: try:
return _cache[module_name][name][number] return _cache[module_name][name][number]
except KeyError: except KeyError:

View File

@@ -384,7 +384,8 @@ def _get_executable_path(path, safe=True):
def _get_executables_from_windows_registry(version): def _get_executables_from_windows_registry(version):
import winreg # https://github.com/python/typeshed/pull/3794 adds winreg
import winreg # type: ignore[import]
# TODO: support Python Anaconda. # TODO: support Python Anaconda.
sub_keys = [ sub_keys = [

View File

@@ -1,10 +1,13 @@
import pydoc import pydoc
from contextlib import suppress from contextlib import suppress
from typing import Dict, Optional
from jedi.inference.names import AbstractArbitraryName from jedi.inference.names import AbstractArbitraryName
try: try:
from pydoc_data import topics as pydoc_topics # https://github.com/python/typeshed/pull/4351 adds pydoc_data
from pydoc_data import topics # type: ignore[import]
pydoc_topics: Optional[Dict[str, str]] = topics.topics
except ImportError: except ImportError:
# Python 3.6.8 embeddable does not have pydoc_data. # Python 3.6.8 embeddable does not have pydoc_data.
pydoc_topics = None pydoc_topics = None
@@ -44,6 +47,6 @@ def imitate_pydoc(string):
return '' return ''
try: try:
return pydoc_topics.topics[label].strip() if pydoc_topics else '' return pydoc_topics[label].strip() if pydoc_topics else ''
except KeyError: except KeyError:
return '' return ''

View File

@@ -13,14 +13,15 @@ these variables are being cleaned after every API usage.
""" """
import time import time
from functools import wraps from functools import wraps
from typing import Any, Dict, Tuple
from jedi import settings from jedi import settings
from parso.cache import parser_cache from parso.cache import parser_cache
_time_caches = {} _time_caches: Dict[str, Dict[Any, Tuple[float, Any]]] = {}
def clear_time_caches(delete_all=False): def clear_time_caches(delete_all: bool = False) -> None:
""" Jedi caches many things, that should be completed after each completion """ Jedi caches many things, that should be completed after each completion
finishes. finishes.

View File

@@ -1,6 +1,7 @@
import os import os
import time import time
from contextlib import contextmanager from contextlib import contextmanager
from typing import Callable, Optional
_inited = False _inited = False
@@ -20,7 +21,7 @@ try:
raise ImportError raise ImportError
else: else:
# Use colorama for nicer console output. # Use colorama for nicer console output.
from colorama import Fore, init from colorama import Fore, init # type: ignore[import]
from colorama import initialise from colorama import initialise
def _lazy_colorama_init(): # noqa: F811 def _lazy_colorama_init(): # noqa: F811
@@ -45,7 +46,7 @@ try:
_inited = True _inited = True
except ImportError: except ImportError:
class Fore: class Fore: # type: ignore[no-redef]
RED = '' RED = ''
GREEN = '' GREEN = ''
YELLOW = '' YELLOW = ''
@@ -62,7 +63,7 @@ enable_warning = False
enable_notice = False enable_notice = False
# callback, interface: level, str # callback, interface: level, str
debug_function = None debug_function: Optional[Callable[[str, str], None]] = None
_debug_indent = 0 _debug_indent = 0
_start_time = time.time() _start_time = time.time()

View File

@@ -120,14 +120,15 @@ class InferenceState:
debug.dbg('execute result: %s in %s', value_set, value) debug.dbg('execute result: %s in %s', value_set, value)
return value_set return value_set
@property # mypy doesn't suppport decorated propeties (https://github.com/python/mypy/issues/1362)
@property # type: ignore[misc]
@inference_state_function_cache() @inference_state_function_cache()
def builtins_module(self): def builtins_module(self):
module_name = 'builtins' module_name = 'builtins'
builtins_module, = self.import_module((module_name,), sys_path=()) builtins_module, = self.import_module((module_name,), sys_path=())
return builtins_module return builtins_module
@property @property # type: ignore[misc]
@inference_state_function_cache() @inference_state_function_cache()
def typing_module(self): def typing_module(self):
typing_module, = self.import_module(('typing',)) typing_module, = self.import_module(('typing',))

View File

@@ -1,3 +1,6 @@
# This file also re-exports symbols for wider use. We configure mypy and flake8
# to be aware that this file does this.
from jedi.inference.compiled.value import CompiledValue, CompiledName, \ from jedi.inference.compiled.value import CompiledValue, CompiledName, \
CompiledValueFilter, CompiledValueName, create_from_access_path CompiledValueFilter, CompiledValueName, create_from_access_path
from jedi.inference.base_value import LazyValueWrapper from jedi.inference.base_value import LazyValueWrapper

View File

@@ -29,19 +29,19 @@ _MAIN_PATH = os.path.join(os.path.dirname(__file__), '__main__.py')
PICKLE_PROTOCOL = 4 PICKLE_PROTOCOL = 4
class _GeneralizedPopen(subprocess.Popen): def _GeneralizedPopen(*args, **kwargs):
def __init__(self, *args, **kwargs): if os.name == 'nt':
if os.name == 'nt': try:
try: # Was introduced in Python 3.7.
# Was introduced in Python 3.7. CREATE_NO_WINDOW = subprocess.CREATE_NO_WINDOW
CREATE_NO_WINDOW = subprocess.CREATE_NO_WINDOW except AttributeError:
except AttributeError: CREATE_NO_WINDOW = 0x08000000
CREATE_NO_WINDOW = 0x08000000 kwargs['creationflags'] = CREATE_NO_WINDOW
kwargs['creationflags'] = CREATE_NO_WINDOW # The child process doesn't need file descriptors except 0, 1, 2.
# The child process doesn't need file descriptors except 0, 1, 2. # This is unix only.
# This is unix only. kwargs['close_fds'] = 'posix' in sys.builtin_module_names
kwargs['close_fds'] = 'posix' in sys.builtin_module_names
super().__init__(*args, **kwargs) return subprocess.Popen(*args, **kwargs)
def _enqueue_output(out, queue_): def _enqueue_output(out, queue_):

View File

@@ -1,5 +1,6 @@
import os import os
import sys import sys
from importlib.abc import MetaPathFinder
from importlib.machinery import PathFinder from importlib.machinery import PathFinder
# Remove the first entry, because it's simply a directory entry that equals # Remove the first entry, because it's simply a directory entry that equals
@@ -16,7 +17,7 @@ def _get_paths():
return {'jedi': _jedi_path, 'parso': _parso_path} return {'jedi': _jedi_path, 'parso': _parso_path}
class _ExactImporter: class _ExactImporter(MetaPathFinder):
def __init__(self, path_dct): def __init__(self, path_dct):
self._path_dct = path_dct self._path_dct = path_dct

View File

@@ -50,7 +50,7 @@ def _get_numpy_doc_string_cls():
global _numpy_doc_string_cache global _numpy_doc_string_cache
if isinstance(_numpy_doc_string_cache, (ImportError, SyntaxError)): if isinstance(_numpy_doc_string_cache, (ImportError, SyntaxError)):
raise _numpy_doc_string_cache raise _numpy_doc_string_cache
from numpydoc.docscrape import NumpyDocString from numpydoc.docscrape import NumpyDocString # type: ignore[import]
_numpy_doc_string_cache = NumpyDocString _numpy_doc_string_cache = NumpyDocString
return _numpy_doc_string_cache return _numpy_doc_string_cache

View File

@@ -3,9 +3,11 @@ Filters are objects that you can use to filter names in different scopes. They
are needed for name resolution. are needed for name resolution.
""" """
from abc import abstractmethod from abc import abstractmethod
from typing import List, MutableMapping, Type
import weakref import weakref
from parso.tree import search_ancestor from parso.tree import search_ancestor
from parso.python.tree import Name, UsedNamesMapping
from jedi.inference import flow_analysis from jedi.inference import flow_analysis
from jedi.inference.base_value import ValueSet, ValueWrapper, \ from jedi.inference.base_value import ValueSet, ValueWrapper, \
@@ -13,8 +15,9 @@ from jedi.inference.base_value import ValueSet, ValueWrapper, \
from jedi.parser_utils import get_cached_parent_scope from jedi.parser_utils import get_cached_parent_scope
from jedi.inference.utils import to_list from jedi.inference.utils import to_list
from jedi.inference.names import TreeNameDefinition, ParamName, \ from jedi.inference.names import TreeNameDefinition, ParamName, \
AnonymousParamName, AbstractNameDefinition AnonymousParamName, AbstractNameDefinition, NameWrapper
_definition_name_cache: MutableMapping[UsedNamesMapping, List[Name]]
_definition_name_cache = weakref.WeakKeyDictionary() _definition_name_cache = weakref.WeakKeyDictionary()
@@ -36,7 +39,7 @@ class AbstractFilter:
class FilterWrapper: class FilterWrapper:
name_wrapper_class = None name_wrapper_class: Type[NameWrapper]
def __init__(self, wrapped_filter): def __init__(self, wrapped_filter):
self._wrapped_filter = wrapped_filter self._wrapped_filter = wrapped_filter

View File

@@ -1,12 +1,14 @@
from typing import Dict, Optional
from jedi.parser_utils import get_flow_branch_keyword, is_scope, get_parent_scope from jedi.parser_utils import get_flow_branch_keyword, is_scope, get_parent_scope
from jedi.inference.recursion import execution_allowed from jedi.inference.recursion import execution_allowed
from jedi.inference.helpers import is_big_annoying_library from jedi.inference.helpers import is_big_annoying_library
class Status: class Status:
lookup_table = {} lookup_table: Dict[Optional[bool], 'Status'] = {}
def __init__(self, value, name): def __init__(self, value: Optional[bool], name: str) -> None:
self._value = value self._value = value
self._name = name self._name = name
Status.lookup_table[value] = self Status.lookup_table[value] = self

View File

@@ -2,6 +2,7 @@ import os
import re import re
from functools import wraps from functools import wraps
from collections import namedtuple from collections import namedtuple
from typing import Dict, Mapping, Tuple
from pathlib import Path from pathlib import Path
from jedi import settings from jedi import settings
@@ -74,7 +75,7 @@ def _get_typeshed_directories(version_info):
yield PathInfo(str(base_path.joinpath(check_version)), is_third_party) yield PathInfo(str(base_path.joinpath(check_version)), is_third_party)
_version_cache = {} _version_cache: Dict[Tuple[int, int], Mapping[str, PathInfo]] = {}
def _cache_stub_file_map(version_info): def _cache_stub_file_map(version_info):

View File

@@ -1,4 +1,3 @@
import os
from pathlib import Path from pathlib import Path
from jedi.inference.gradual.typeshed import TYPESHED_PATH, create_stub_module from jedi.inference.gradual.typeshed import TYPESHED_PATH, create_stub_module

View File

@@ -1,5 +1,6 @@
from abc import abstractmethod from abc import abstractmethod
from inspect import Parameter from inspect import Parameter
from typing import Optional, Tuple
from parso.tree import search_ancestor from parso.tree import search_ancestor
@@ -24,8 +25,8 @@ def _merge_name_docs(names):
class AbstractNameDefinition: class AbstractNameDefinition:
start_pos = None start_pos: Optional[Tuple[int, int]] = None
string_name = None string_name: str
parent_context = None parent_context = None
tree_name = None tree_name = None
is_value_name = True is_value_name = True

View File

@@ -5,7 +5,8 @@ from parso import python_bytes_to_unicode
from jedi.debug import dbg from jedi.debug import dbg
from jedi.file_io import KnownContentFileIO from jedi.file_io import KnownContentFileIO
from jedi.inference.imports import SubModuleName, load_module_from_path from jedi.inference.names import SubModuleName
from jedi.inference.imports import load_module_from_path
from jedi.inference.filters import ParserTreeFilter from jedi.inference.filters import ParserTreeFilter
from jedi.inference.gradual.conversion import convert_names from jedi.inference.gradual.conversion import convert_names

View File

@@ -14,8 +14,8 @@ from jedi import debug
_BUILDOUT_PATH_INSERTION_LIMIT = 10 _BUILDOUT_PATH_INSERTION_LIMIT = 10
def _abs_path(module_context, path: str): def _abs_path(module_context, str_path: str):
path = Path(path) path = Path(str_path)
if path.is_absolute(): if path.is_absolute():
return path return path

View File

@@ -1,3 +1,6 @@
# Re-export symbols for wider use. We configure mypy and flake8 to be aware that
# this file does this.
from jedi.inference.value.module import ModuleValue from jedi.inference.value.module import ModuleValue
from jedi.inference.value.klass import ClassValue from jedi.inference.value.klass import ClassValue
from jedi.inference.value.function import FunctionValue, \ from jedi.inference.value.function import FunctionValue, \

View File

@@ -1,6 +1,6 @@
from abc import abstractproperty from abc import abstractproperty
from parso.python.tree import search_ancestor from parso.tree import search_ancestor
from jedi import debug from jedi import debug
from jedi import settings from jedi import settings

View File

@@ -1,5 +1,6 @@
import os import os
from pathlib import Path from pathlib import Path
from typing import Optional
from jedi.inference.cache import inference_state_method_cache from jedi.inference.cache import inference_state_method_cache
from jedi.inference.names import AbstractNameDefinition, ModuleName from jedi.inference.names import AbstractNameDefinition, ModuleName
@@ -79,7 +80,7 @@ class ModuleMixin(SubModuleDictMixin):
def is_stub(self): def is_stub(self):
return False return False
@property @property # type: ignore[misc]
@inference_state_method_cache() @inference_state_method_cache()
def name(self): def name(self):
return self._module_name_class(self, self.string_names[-1]) return self._module_name_class(self, self.string_names[-1])
@@ -145,7 +146,7 @@ class ModuleValue(ModuleMixin, TreeValue):
) )
self.file_io = file_io self.file_io = file_io
if file_io is None: if file_io is None:
self._path = None self._path: Optional[Path] = None
else: else:
self._path = Path(file_io.path) self._path = Path(file_io.path)
self.string_names = string_names # Optional[Tuple[str, ...]] self.string_names = string_names # Optional[Tuple[str, ...]]
@@ -165,7 +166,7 @@ class ModuleValue(ModuleMixin, TreeValue):
return None return None
return '.'.join(self.string_names) return '.'.join(self.string_names)
def py__file__(self) -> Path: def py__file__(self) -> Optional[Path]:
""" """
In contrast to Python's __file__ can be None. In contrast to Python's __file__ can be None.
""" """

View File

@@ -38,7 +38,7 @@ class ImplicitNamespaceValue(Value, SubModuleDictMixin):
def get_qualified_names(self): def get_qualified_names(self):
return () return ()
@property @property # type: ignore[misc]
@inference_state_method_cache() @inference_state_method_cache()
def name(self): def name(self):
string_name = self.py__package__()[-1] string_name = self.py__package__()[-1]

View File

@@ -1,6 +1,6 @@
from pathlib import Path from pathlib import Path
from parso.python.tree import search_ancestor from parso.tree import search_ancestor
from jedi.inference.cache import inference_state_method_cache from jedi.inference.cache import inference_state_method_cache
from jedi.inference.imports import load_module_from_path from jedi.inference.imports import load_module_from_path
from jedi.inference.filters import ParserTreeFilter from jedi.inference.filters import ParserTreeFilter

View File

@@ -2,7 +2,7 @@
Utilities for end-users. Utilities for end-users.
""" """
import __main__ import __main__ # type: ignore[import]
from collections import namedtuple from collections import namedtuple
import logging import logging
import traceback import traceback

View File

@@ -15,7 +15,40 @@ ignore =
W503, W503,
# Single letter loop variables are often fine # Single letter loop variables are often fine
E741, E741,
per-file-ignores =
# Ignore apparently unused imports in files where we're (implicitly)
# re-exporting them.
jedi/__init__.py:F401
jedi/inference/compiled/__init__.py:F401
jedi/inference/value/__init__.py:F401
exclude = jedi/third_party/* .tox/* exclude = jedi/third_party/* .tox/*
[pycodestyle] [pycodestyle]
max-line-length = 100 max-line-length = 100
[mypy]
# Ensure generics are explicit about what they are (e.g: `List[str]` rather than
# just `List`)
disallow_any_generics = True
disallow_subclassing_any = True
# Avoid creating future gotchas emerging from bad typing
warn_redundant_casts = True
warn_unused_ignores = True
warn_return_any = True
warn_unused_configs = True
warn_unreachable = True
# Require values to be explicitly re-exported; this makes things easier for
# Flake8 too and avoids accidentally importing thing from the "wrong" place
# (which helps avoid circular imports)
implicit_reexport = False
strict_equality = True
[mypy-jedi,jedi.inference.compiled,jedi.inference.value]
# Various __init__.py files which contain re-exports we want to implicitly make.
implicit_reexport = True

View File

@@ -44,6 +44,7 @@ setup(name='jedi',
], ],
'qa': [ 'qa': [
'flake8==3.8.3', 'flake8==3.8.3',
'mypy==0.782',
], ],
}, },
package_data={'jedi': ['*.pyi', 'third_party/typeshed/LICENSE', package_data={'jedi': ['*.pyi', 'third_party/typeshed/LICENSE',

View File

@@ -44,7 +44,7 @@ Options:
--pudb Launch pudb when error is raised. --pudb Launch pudb when error is raised.
""" """
from docopt import docopt from docopt import docopt # type: ignore[import]
import json import json
import os import os