mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 14:04:26 +08:00
Merge branch 'master' into refactor
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,3 +13,4 @@ jedi.egg-info/
|
||||
record.json
|
||||
/.cache/
|
||||
/.pytest_cache
|
||||
/venv/
|
||||
|
||||
@@ -55,5 +55,6 @@ Max Woerner Chase (@mwchase) <max.chase@gmail.com>
|
||||
Johannes Maria Frank (@jmfrank63) <jmfrank63@gmail.com>
|
||||
Shane Steinert-Threlkeld (@shanest) <ssshanest@gmail.com>
|
||||
Tim Gates (@timgates42) <tim.gates@iress.com>
|
||||
Lior Goldberg (@goldberglior)
|
||||
|
||||
Note: (@user) means a github user name.
|
||||
|
||||
@@ -43,6 +43,7 @@ the CPython REPL you have to install it.
|
||||
Jedi can currently be used with the following editors/projects:
|
||||
|
||||
- Vim (jedi-vim_, YouCompleteMe_, deoplete-jedi_, completor.vim_)
|
||||
- `Visual Studio Code`_ (via `Python Extension <https://marketplace.visualstudio.com/items?itemName=ms-python.python>`_)
|
||||
- Emacs (Jedi.el_, company-mode_, elpy_, anaconda-mode_, ycmd_)
|
||||
- Sublime Text (SublimeJEDI_ [ST2 + ST3], anaconda_ [only ST3])
|
||||
- TextMate_ (Not sure if it's actually working)
|
||||
@@ -50,7 +51,6 @@ Jedi can currently be used with the following editors/projects:
|
||||
<https://projects.kde.org/projects/kde/applications/kate/repository/show?rev=KDE%2F4.13>`_]
|
||||
- Atom_ (autocomplete-python-jedi_)
|
||||
- `GNOME Builder`_ (with support for GObject Introspection)
|
||||
- `Visual Studio Code`_ (via `Python Extension <https://marketplace.visualstudio.com/items?itemName=ms-python.python>`_)
|
||||
- Gedit (gedi_)
|
||||
- wdb_ - Web Debugger
|
||||
- `Eric IDE`_ (Available as a plugin)
|
||||
|
||||
@@ -23,6 +23,10 @@ Vim:
|
||||
- YouCompleteMe_
|
||||
- deoplete-jedi_
|
||||
|
||||
Visual Studio Code:
|
||||
|
||||
- `Python Extension`_
|
||||
|
||||
Emacs:
|
||||
|
||||
- Jedi.el_
|
||||
@@ -48,10 +52,6 @@ Kate:
|
||||
<https://projects.kde.org/projects/kde/applications/kate/repository/entry/addons/kate/pate/src/plugins/python_autocomplete_jedi.py?rev=KDE%2F4.13>`__,
|
||||
you have to enable it, though.
|
||||
|
||||
Visual Studio Code:
|
||||
|
||||
- `Python Extension`_
|
||||
|
||||
Atom:
|
||||
|
||||
- autocomplete-python-jedi_
|
||||
@@ -114,4 +114,4 @@ Using a custom ``$HOME/.pythonrc.py``
|
||||
.. _GNOME Builder: https://wiki.gnome.org/Apps/Builder/
|
||||
.. _gedi: https://github.com/isamert/gedi
|
||||
.. _Eric IDE: https://eric-ide.python-projects.org
|
||||
.. _Python Extension: https://marketplace.visualstudio.com/items?itemName=donjayamanne.python
|
||||
.. _Python Extension: https://marketplace.visualstudio.com/items?itemName=ms-python.python
|
||||
|
||||
@@ -40,7 +40,7 @@ from jedi.api import Script, Interpreter, set_debug_function, \
|
||||
from jedi import settings
|
||||
from jedi.api.environment import find_virtualenvs, find_system_environments, \
|
||||
get_default_environment, InvalidPythonEnvironment, create_environment, \
|
||||
get_system_environment
|
||||
get_system_environment, InterpreterEnvironment
|
||||
from jedi.api.project import Project, get_default_project
|
||||
from jedi.api.exceptions import InternalError, RefactoringError
|
||||
# Finally load the internal plugins. This is only internal.
|
||||
|
||||
@@ -335,7 +335,8 @@ class Script(object):
|
||||
)
|
||||
|
||||
defs = [classes.Definition(self._inference_state, d) for d in set(names)]
|
||||
return helpers.sorted_definitions(defs)
|
||||
# Avoid duplicates
|
||||
return list(set(helpers.sorted_definitions(defs)))
|
||||
|
||||
@no_py2_support
|
||||
def search(self, string, **kwargs):
|
||||
|
||||
@@ -17,6 +17,8 @@ def complete_file_name(inference_state, module_context, start_leaf, quote, strin
|
||||
like_name_length = len(os.path.basename(string))
|
||||
|
||||
addition = _get_string_additions(module_context, start_leaf)
|
||||
if string.startswith('~'):
|
||||
string = os.path.expanduser(string)
|
||||
if addition is None:
|
||||
return
|
||||
string = addition + string
|
||||
|
||||
@@ -258,6 +258,7 @@ class Value(HelperValueMixin, BaseValue):
|
||||
def _as_context(self):
|
||||
raise NotImplementedError('Not all values need to be converted to contexts: %s', self)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -484,6 +484,11 @@ class DirectObjectAccess(object):
|
||||
def needs_type_completions(self):
|
||||
return inspect.isclass(self._obj) and self._obj != type
|
||||
|
||||
def _annotation_to_str(self, annotation):
|
||||
if isinstance(annotation, type):
|
||||
return str(annotation.__name__)
|
||||
return str(annotation)
|
||||
|
||||
def get_signature_params(self):
|
||||
return [
|
||||
SignatureParam(
|
||||
@@ -493,7 +498,7 @@ class DirectObjectAccess(object):
|
||||
default_string=repr(p.default),
|
||||
has_annotation=p.annotation is not p.empty,
|
||||
annotation=self._create_access_path(p.annotation),
|
||||
annotation_string=str(p.annotation),
|
||||
annotation_string=self._annotation_to_str(p.annotation),
|
||||
kind_name=str(p.kind)
|
||||
) for p in self._get_signature().parameters.values()
|
||||
]
|
||||
|
||||
@@ -6,6 +6,7 @@ information returned to enable Jedi to make decisions.
|
||||
|
||||
import types
|
||||
|
||||
from jedi import debug
|
||||
from jedi._compatibility import py_version
|
||||
|
||||
_sentinel = object()
|
||||
@@ -54,7 +55,14 @@ def _shadowed_dict_newstyle(klass):
|
||||
|
||||
|
||||
def _static_getmro_newstyle(klass):
|
||||
return type.__dict__['__mro__'].__get__(klass)
|
||||
mro = type.__dict__['__mro__'].__get__(klass)
|
||||
if not isinstance(mro, (tuple, list)):
|
||||
# There are unfortunately no tests for this, I was not able to
|
||||
# reproduce this in pure Python. However should still solve the issue
|
||||
# raised in GH #1517.
|
||||
debug.warning('mro of %s returned %s, should be a tuple' % (klass, mro))
|
||||
return ()
|
||||
return mro
|
||||
|
||||
|
||||
if py_version >= 30:
|
||||
|
||||
@@ -297,7 +297,7 @@ class Listener(object):
|
||||
try:
|
||||
inference_state = self._inference_states[inference_state_id]
|
||||
except KeyError:
|
||||
from jedi.api.environment import InterpreterEnvironment
|
||||
from jedi import InterpreterEnvironment
|
||||
inference_state = InferenceState(
|
||||
# The project is not actually needed. Nothing should need to
|
||||
# access it.
|
||||
|
||||
@@ -130,6 +130,9 @@ def import_module_decorator(func):
|
||||
|
||||
|
||||
def try_to_load_stub_cached(inference_state, import_names, *args, **kwargs):
|
||||
if import_names is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
return inference_state.stub_module_cache[import_names]
|
||||
except KeyError:
|
||||
|
||||
@@ -5,8 +5,11 @@ values.
|
||||
|
||||
This file deals with all the typing.py cases.
|
||||
"""
|
||||
import itertools
|
||||
|
||||
from jedi._compatibility import unicode
|
||||
from jedi import debug
|
||||
from jedi.inference.compiled import builtin_from_name
|
||||
from jedi.inference.compiled import builtin_from_name, create_simple_object
|
||||
from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \
|
||||
LazyValueWrapper
|
||||
from jedi.inference.lazy_value import LazyKnownValues
|
||||
@@ -81,7 +84,8 @@ class TypingModuleName(NameWrapper):
|
||||
elif name == 'TypedDict':
|
||||
# TODO doesn't even exist in typeshed/typing.py, yet. But will be
|
||||
# added soon.
|
||||
pass
|
||||
yield TypedDictBase.create_cached(
|
||||
inference_state, self.parent_context, self.tree_name)
|
||||
elif name in ('no_type_check', 'no_type_check_decorator'):
|
||||
# This is not necessary, as long as we are not doing type checking.
|
||||
for c in self._wrapped_name.infer(): # Fuck my life Python 2
|
||||
@@ -339,3 +343,47 @@ class CastFunction(BaseTypingValue):
|
||||
@repack_with_argument_clinic('type, object, /')
|
||||
def py__call__(self, type_value_set, object_value_set):
|
||||
return type_value_set.execute_annotation()
|
||||
|
||||
|
||||
class TypedDictBase(BaseTypingValue):
|
||||
"""
|
||||
This class has no responsibilities and is just here to make sure that typed
|
||||
dicts can be identified.
|
||||
"""
|
||||
|
||||
|
||||
class TypedDict(LazyValueWrapper):
|
||||
"""Represents the instance version of ``TypedDictClass``."""
|
||||
def __init__(self, definition_class):
|
||||
self.inference_state = definition_class.inference_state
|
||||
self.parent_context = definition_class.parent_context
|
||||
self.tree_node = definition_class.tree_node
|
||||
self._definition_class = definition_class
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return ValueName(self, self.tree_node.name)
|
||||
|
||||
def py__simple_getitem__(self, index):
|
||||
if isinstance(index, unicode):
|
||||
return ValueSet.from_sets(
|
||||
name.infer()
|
||||
for filter in self._definition_class.get_filters(is_instance=True)
|
||||
for name in filter.get(index)
|
||||
)
|
||||
return NO_VALUES
|
||||
|
||||
def get_key_values(self):
|
||||
filtered_values = itertools.chain.from_iterable((
|
||||
f.values()
|
||||
for f in self._definition_class.get_filters(is_instance=True)
|
||||
))
|
||||
return ValueSet({
|
||||
create_simple_object(self.inference_state, v.string_name)
|
||||
for v in filtered_values
|
||||
})
|
||||
|
||||
def _get_wrapped_value(self):
|
||||
d, = self.inference_state.builtins_module.py__getattribute__('dict')
|
||||
result, = d.execute_with_values()
|
||||
return result
|
||||
|
||||
@@ -51,6 +51,25 @@ class ExecutedParamName(ParamName):
|
||||
|
||||
|
||||
def get_executed_param_names_and_issues(function_value, arguments):
|
||||
"""
|
||||
Return a tuple of:
|
||||
- a list of `ExecutedParamName`s corresponding to the arguments of the
|
||||
function execution `function_value`, containing the inferred value of
|
||||
those arguments (whether explicit or default)
|
||||
- a list of the issues encountered while building that list
|
||||
|
||||
For example, given:
|
||||
```
|
||||
def foo(a, b, c=None, d='d'): ...
|
||||
|
||||
foo(42, c='c')
|
||||
```
|
||||
|
||||
Then for the execution of `foo`, this will return a tuple containing:
|
||||
- a list with entries for each parameter a, b, c & d; the entries for a,
|
||||
c, & d will have their values (42, 'c' and 'd' respectively) included.
|
||||
- a list with a single entry about the lack of a value for `b`
|
||||
"""
|
||||
def too_many_args(argument):
|
||||
m = _error_argument_count(funcdef, len(unpacked_va))
|
||||
# Just report an error for the first param that is not needed (like
|
||||
@@ -207,6 +226,23 @@ def get_executed_param_names_and_issues(function_value, arguments):
|
||||
|
||||
|
||||
def get_executed_param_names(function_value, arguments):
|
||||
"""
|
||||
Return a list of `ExecutedParamName`s corresponding to the arguments of the
|
||||
function execution `function_value`, containing the inferred value of those
|
||||
arguments (whether explicit or default). Any issues building this list (for
|
||||
example required arguments which are missing in the invocation) are ignored.
|
||||
|
||||
For example, given:
|
||||
```
|
||||
def foo(a, b, c=None, d='d'): ...
|
||||
|
||||
foo(42, c='c')
|
||||
```
|
||||
|
||||
Then for the execution of `foo`, this will return a list containing entries
|
||||
for each parameter a, b, c & d; the entries for a, c, & d will have their
|
||||
values (42, 'c' and 'd' respectively) included.
|
||||
"""
|
||||
return get_executed_param_names_and_issues(function_value, arguments)[0]
|
||||
|
||||
|
||||
|
||||
@@ -356,6 +356,12 @@ def infer_atom(context, atom):
|
||||
def infer_expr_stmt(context, stmt, seek_name=None):
|
||||
with recursion.execution_allowed(context.inference_state, stmt) as allowed:
|
||||
if allowed:
|
||||
if seek_name is not None:
|
||||
pep0484_values = \
|
||||
annotation.find_type_from_comment_hint_assign(context, stmt, seek_name)
|
||||
if pep0484_values:
|
||||
return pep0484_values
|
||||
|
||||
return _infer_expr_stmt(context, stmt, seek_name)
|
||||
return NO_VALUES
|
||||
|
||||
@@ -632,23 +638,6 @@ def _infer_comparison_part(inference_state, context, left, operator, right):
|
||||
return result
|
||||
|
||||
|
||||
def _remove_statements(context, stmt, name):
|
||||
"""
|
||||
This is the part where statements are being stripped.
|
||||
|
||||
Due to lazy type inference, statements like a = func; b = a; b() have to be
|
||||
inferred.
|
||||
|
||||
TODO merge with infer_expr_stmt?
|
||||
"""
|
||||
pep0484_values = \
|
||||
annotation.find_type_from_comment_hint_assign(context, stmt, name)
|
||||
if pep0484_values:
|
||||
return pep0484_values
|
||||
|
||||
return infer_expr_stmt(context, stmt, seek_name=name)
|
||||
|
||||
|
||||
@plugin_manager.decorate()
|
||||
def tree_name_to_values(inference_state, context, tree_name):
|
||||
value_set = NO_VALUES
|
||||
@@ -713,7 +702,7 @@ def tree_name_to_values(inference_state, context, tree_name):
|
||||
n = TreeNameDefinition(context, tree_name)
|
||||
types = check_tuple_assignments(n, for_types)
|
||||
elif typ == 'expr_stmt':
|
||||
types = _remove_statements(context, node, tree_name)
|
||||
types = infer_expr_stmt(context, node, tree_name)
|
||||
elif typ == 'with_stmt':
|
||||
value_managers = context.infer_node(node.get_test_node_from_name(tree_name))
|
||||
enter_methods = value_managers.py__getattribute__(u'__enter__')
|
||||
|
||||
@@ -547,10 +547,10 @@ class InstanceClassFilter(AbstractFilter):
|
||||
self._class_filter = class_filter
|
||||
|
||||
def get(self, name):
|
||||
return self._convert(self._class_filter.get(name, from_instance=True))
|
||||
return self._convert(self._class_filter.get(name))
|
||||
|
||||
def values(self):
|
||||
return self._convert(self._class_filter.values(from_instance=True))
|
||||
return self._convert(self._class_filter.values())
|
||||
|
||||
def _convert(self, names):
|
||||
return [
|
||||
@@ -586,7 +586,7 @@ class SelfAttributeFilter(ClassFilter):
|
||||
if trailer.type == 'trailer' \
|
||||
and len(trailer.parent.children) == 2 \
|
||||
and trailer.children[0] == '.':
|
||||
if name.is_definition() and self._access_possible(name, from_instance=True):
|
||||
if name.is_definition() and self._access_possible(name):
|
||||
# TODO filter non-self assignments instead of this bad
|
||||
# filter.
|
||||
if self._is_in_right_scope(trailer.parent.children[0], name):
|
||||
|
||||
@@ -38,11 +38,11 @@ py__doc__() Returns the docstring for a value.
|
||||
"""
|
||||
from jedi import debug
|
||||
from jedi._compatibility import use_metaclass
|
||||
from jedi.parser_utils import get_cached_parent_scope
|
||||
from jedi.parser_utils import get_cached_parent_scope, expr_is_dotted
|
||||
from jedi.inference.cache import inference_state_method_cache, CachedMetaClass, \
|
||||
inference_state_method_generator_cache
|
||||
from jedi.inference import compiled
|
||||
from jedi.inference.lazy_value import LazyKnownValues
|
||||
from jedi.inference.lazy_value import LazyKnownValues, LazyTreeValue
|
||||
from jedi.inference.filters import ParserTreeFilter
|
||||
from jedi.inference.names import TreeNameDefinition, ValueName
|
||||
from jedi.inference.arguments import unpack_arglist, ValuesArguments
|
||||
@@ -104,27 +104,31 @@ class ClassFilter(ParserTreeFilter):
|
||||
node = get_cached_parent_scope(self._used_names, node)
|
||||
return False
|
||||
|
||||
def _access_possible(self, name, from_instance=False):
|
||||
def _access_possible(self, name):
|
||||
# Filter for ClassVar variables
|
||||
# TODO this is not properly done, yet. It just checks for the string
|
||||
# ClassVar in the annotation, which can be quite imprecise. If we
|
||||
# wanted to do this correct, we would have to infer the ClassVar.
|
||||
if not from_instance:
|
||||
if not self._is_instance:
|
||||
expr_stmt = name.get_definition()
|
||||
if expr_stmt is not None and expr_stmt.type == 'expr_stmt':
|
||||
annassign = expr_stmt.children[1]
|
||||
if annassign.type == 'annassign':
|
||||
# TODO this is not proper matching
|
||||
if 'ClassVar' not in annassign.children[1].get_code():
|
||||
|
||||
# If there is an =, the variable is obviously also
|
||||
# defined on the class.
|
||||
if 'ClassVar' not in annassign.children[1].get_code() \
|
||||
and '=' not in annassign.children:
|
||||
return False
|
||||
|
||||
# Filter for name mangling of private variables like __foo
|
||||
return not name.value.startswith('__') or name.value.endswith('__') \
|
||||
or self._equals_origin_scope()
|
||||
|
||||
def _filter(self, names, from_instance=False):
|
||||
def _filter(self, names):
|
||||
names = super(ClassFilter, self)._filter(names)
|
||||
return [name for name in names if self._access_possible(name, from_instance)]
|
||||
return [name for name in names if self._access_possible(name)]
|
||||
|
||||
|
||||
class ClassMixin(object):
|
||||
@@ -133,6 +137,10 @@ class ClassMixin(object):
|
||||
|
||||
def py__call__(self, arguments=None):
|
||||
from jedi.inference.value import TreeInstance
|
||||
|
||||
from jedi.inference.gradual.typing import TypedDict
|
||||
if self.is_typeddict():
|
||||
return ValueSet([TypedDict(self)])
|
||||
return ValueSet([TreeInstance(self.inference_state, self.parent_context, self, arguments)])
|
||||
|
||||
def py__class__(self):
|
||||
@@ -226,6 +234,36 @@ class ClassMixin(object):
|
||||
return 'Type[%s]' % self.py__name__()
|
||||
return self.py__name__()
|
||||
|
||||
@inference_state_method_cache(default=False)
|
||||
def is_typeddict(self):
|
||||
# TODO Do a proper mro resolution. Currently we are just listing
|
||||
# classes. However, it's a complicated algorithm.
|
||||
from jedi.inference.gradual.typing import TypedDictBase
|
||||
for lazy_cls in self.py__bases__():
|
||||
if not isinstance(lazy_cls, LazyTreeValue):
|
||||
return False
|
||||
tree_node = lazy_cls.data
|
||||
# Only resolve simple classes, stuff like Iterable[str] are more
|
||||
# intensive to resolve and if generics are involved, we know it's
|
||||
# not a TypedDict.
|
||||
if not expr_is_dotted(tree_node):
|
||||
return False
|
||||
|
||||
for cls in lazy_cls.infer():
|
||||
if isinstance(cls, TypedDictBase):
|
||||
return True
|
||||
try:
|
||||
method = cls.is_typeddict
|
||||
except AttributeError:
|
||||
# We're only dealing with simple classes, so just returning
|
||||
# here should be fine. This only happens with e.g. compiled
|
||||
# classes.
|
||||
return False
|
||||
else:
|
||||
if method():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase)):
|
||||
api_type = u'class'
|
||||
|
||||
@@ -293,6 +293,25 @@ def cut_value_at_position(leaf, position):
|
||||
return ''.join(lines)
|
||||
|
||||
|
||||
def expr_is_dotted(node):
|
||||
"""
|
||||
Checks if a path looks like `name` or `name.foo.bar` and not `name()`.
|
||||
"""
|
||||
if node.type == 'atom':
|
||||
if len(node.children) == 3 and node.children[0] == '(':
|
||||
return expr_is_dotted(node.children[1])
|
||||
return False
|
||||
if node.type == 'atom_expr':
|
||||
children = node.children
|
||||
if children[0] == 'await':
|
||||
return False
|
||||
if not expr_is_dotted(children[0]):
|
||||
return False
|
||||
# Check trailers
|
||||
return all(c.children[0] == '.' for c in children[1:])
|
||||
return node.type == 'name'
|
||||
|
||||
|
||||
def _function_is_x_method(method_name):
|
||||
def wrapper(function_node):
|
||||
"""
|
||||
|
||||
@@ -44,8 +44,6 @@ from operator import itemgetter as _itemgetter
|
||||
from collections import OrderedDict
|
||||
|
||||
class {typename}(tuple):
|
||||
'{typename}({arg_list})'
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
_fields = {field_names!r}
|
||||
|
||||
10
sith.py
10
sith.py
@@ -20,8 +20,7 @@ Run a specific operation
|
||||
|
||||
./sith.py run <operation> </path/to/source/file.py> <line> <col>
|
||||
|
||||
Where operation is one of completions, goto_assignments, goto_definitions,
|
||||
usages, or call_signatures.
|
||||
Where operation is one of complete, goto, infer, get_references or get_signatures.
|
||||
|
||||
Note: Line numbers start at 1; columns start at 0 (this is consistent with
|
||||
many text editors, including Emacs).
|
||||
@@ -95,6 +94,7 @@ class TestCase(object):
|
||||
args = json.load(f)
|
||||
return cls(*args)
|
||||
|
||||
# Changing this? Also update the module docstring above.
|
||||
operations = ['complete', 'goto', 'infer', 'get_references', 'get_signatures']
|
||||
|
||||
@classmethod
|
||||
@@ -151,13 +151,13 @@ class TestCase(object):
|
||||
# Three lines ought to be enough
|
||||
lower = lineno - show if lineno - show > 0 else 0
|
||||
prefix = ' |'
|
||||
for i, line in enumerate(self.script._source.split('\n')[lower:lineno]):
|
||||
for i, line in enumerate(self.script._code.split('\n')[lower:lineno]):
|
||||
print(prefix, lower + i + 1, line)
|
||||
print(prefix, ' ', ' ' * (column + len(str(lineno))), '^')
|
||||
print(prefix, ' ' * (column + len(str(lineno))), '^')
|
||||
|
||||
def show_operation(self):
|
||||
print("%s:\n" % self.operation.capitalize())
|
||||
if self.operation == 'completions':
|
||||
if self.operation == 'complete':
|
||||
self.show_completions()
|
||||
else:
|
||||
self.show_definitions()
|
||||
|
||||
@@ -422,3 +422,11 @@ with Foo() as f3:
|
||||
#? 6 Foo
|
||||
with Foo() as f3:
|
||||
f3
|
||||
|
||||
# -----------------
|
||||
# Avoiding multiple definitions
|
||||
# -----------------
|
||||
|
||||
some_array = ['', '']
|
||||
#! ['def upper']
|
||||
some_array[some_not_defined_index].upper
|
||||
|
||||
@@ -499,3 +499,89 @@ def dynamic_annotation(x: int):
|
||||
|
||||
#? int()
|
||||
dynamic_annotation('')
|
||||
|
||||
# -------------------------
|
||||
# TypeDict
|
||||
# -------------------------
|
||||
|
||||
# python >= 3.8
|
||||
|
||||
class Foo(typing.TypedDict):
|
||||
foo: str
|
||||
bar: typing.List[float]
|
||||
an_int: int
|
||||
#! ['foo: str']
|
||||
foo
|
||||
#? str()
|
||||
foo
|
||||
#? int()
|
||||
an_int
|
||||
|
||||
def typed_dict_test_foo(arg: Foo):
|
||||
a_string = arg['foo']
|
||||
a_list_of_floats = arg['bar']
|
||||
an_int = arg['an_int']
|
||||
|
||||
#? str()
|
||||
a_string
|
||||
#? list()
|
||||
a_list_of_floats
|
||||
#? float()
|
||||
a_list_of_floats[0]
|
||||
#? int()
|
||||
an_int
|
||||
|
||||
#? ['isupper']
|
||||
a_string.isuppe
|
||||
#? ['pop']
|
||||
a_list_of_floats.po
|
||||
#? ['as_integer_ratio']
|
||||
an_int.as_integer_rati
|
||||
|
||||
#! ['class Foo']
|
||||
d: Foo
|
||||
#? str()
|
||||
d['foo']
|
||||
#? float()
|
||||
d['bar'][0]
|
||||
#?
|
||||
d['baz']
|
||||
|
||||
#?
|
||||
d.foo
|
||||
#?
|
||||
d.bar
|
||||
#! []
|
||||
d.foo
|
||||
|
||||
#? []
|
||||
Foo.set
|
||||
#? ['setdefault']
|
||||
d.setdefaul
|
||||
#? []
|
||||
Foo.setdefaul
|
||||
|
||||
#? 5 ["'foo"]
|
||||
d['fo']
|
||||
#? 5 ['"bar"']
|
||||
d["bar"]
|
||||
|
||||
class Bar(Foo):
|
||||
another_variable: int
|
||||
|
||||
#? int()
|
||||
another_variable
|
||||
#?
|
||||
an_int
|
||||
|
||||
def typed_dict_test_foo(arg: Bar):
|
||||
#? str()
|
||||
arg['foo']
|
||||
#? list()
|
||||
arg['bar']
|
||||
#? float()
|
||||
arg['bar'][0]
|
||||
#? int()
|
||||
arg['an_int']
|
||||
#? int()
|
||||
arg['another_variable']
|
||||
|
||||
@@ -57,7 +57,7 @@ Foo.baz
|
||||
Foo().baz
|
||||
|
||||
class VarClass:
|
||||
var_instance1: int = 1
|
||||
var_instance1: int = ''
|
||||
var_instance2: float
|
||||
var_class1: typing.ClassVar[str] = 1
|
||||
var_class2: typing.ClassVar[bytes]
|
||||
@@ -77,9 +77,9 @@ class VarClass:
|
||||
self.var_
|
||||
|
||||
|
||||
#? ['var_class1', 'var_class2']
|
||||
#? ['var_class1', 'var_class2', 'var_instance1']
|
||||
VarClass.var_
|
||||
#?
|
||||
#? int()
|
||||
VarClass.var_instance1
|
||||
#?
|
||||
VarClass.var_instance2
|
||||
|
||||
@@ -91,3 +91,15 @@ class B:
|
||||
for i in self.a(i):
|
||||
#?
|
||||
yield i
|
||||
|
||||
|
||||
foo = int
|
||||
foo = foo # type: foo
|
||||
#? int
|
||||
foo
|
||||
|
||||
while True:
|
||||
bar = int
|
||||
bar = bar # type: bar
|
||||
#? int()
|
||||
bar
|
||||
|
||||
@@ -8,7 +8,7 @@ import pytest
|
||||
from . import helpers
|
||||
from . import run
|
||||
from . import refactor
|
||||
from jedi.api.environment import InterpreterEnvironment, get_system_environment
|
||||
from jedi import InterpreterEnvironment, get_system_environment
|
||||
from jedi.inference.compiled.value import create_from_access_path
|
||||
from jedi.inference.imports import _load_python_module
|
||||
from jedi.file_io import KnownContentFileIO
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from os.path import join, sep as s, dirname
|
||||
from os.path import join, sep as s, dirname, expanduser
|
||||
import os
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
@@ -7,6 +7,7 @@ import pytest
|
||||
|
||||
from ..helpers import root_dir
|
||||
from jedi.api.helpers import _start_match, _fuzzy_match
|
||||
from jedi._compatibility import scandir
|
||||
|
||||
|
||||
def test_in_whitespace(Script):
|
||||
@@ -86,6 +87,18 @@ def test_loading_unicode_files_with_bad_global_charset(Script, monkeypatch, tmpd
|
||||
s.complete(line=2, column=4)
|
||||
|
||||
|
||||
def test_complete_expanduser(Script):
|
||||
possibilities = scandir(expanduser('~'))
|
||||
non_dots = [p for p in possibilities if not p.name.startswith('.') and len(p.name) > 1]
|
||||
item = non_dots[0]
|
||||
line = "'~%s%s'" % (os.sep, item.name)
|
||||
s = Script(line, line=1, column=len(line)-1)
|
||||
expected_name = item.name
|
||||
if item.is_dir():
|
||||
expected_name += os.path.sep
|
||||
assert expected_name in [c.name for c in s.completions()]
|
||||
|
||||
|
||||
def test_fake_subnodes(Script):
|
||||
"""
|
||||
Test the number of subnodes of a fake object.
|
||||
|
||||
@@ -342,7 +342,7 @@ def test_completion_params():
|
||||
|
||||
@pytest.mark.skipif('py_version < 33', reason='inspect.signature was created in 3.3.')
|
||||
def test_completion_param_annotations():
|
||||
# Need to define this function not directly in Python. Otherwise Jedi is to
|
||||
# Need to define this function not directly in Python. Otherwise Jedi is too
|
||||
# clever and uses the Python code instead of the signature object.
|
||||
code = 'def foo(a: 1, b: str, c: int = 1.0) -> bytes: pass'
|
||||
exec_(code, locals())
|
||||
@@ -354,6 +354,10 @@ def test_completion_param_annotations():
|
||||
assert [d.name for d in b.infer()] == ['str']
|
||||
assert {d.name for d in c.infer()} == {'int', 'float'}
|
||||
|
||||
assert a.description == 'param a: 1'
|
||||
assert b.description == 'param b: str'
|
||||
assert c.description == 'param c: int=1.0'
|
||||
|
||||
d, = jedi.Interpreter('foo()', [locals()]).infer()
|
||||
assert d.name == 'bytes'
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ def test_namedtuple_infer(Script):
|
||||
|
||||
assert d1.get_line_code() == "class Foo(tuple):\n"
|
||||
assert d1.module_path is None
|
||||
assert d1.docstring() == 'Foo(id, timestamp, gps_timestamp, attributes)'
|
||||
|
||||
|
||||
def test_re_sub(Script, environment):
|
||||
|
||||
@@ -41,6 +41,9 @@ def test_completion(case, monkeypatch, environment, has_typing):
|
||||
if skip_reason is not None:
|
||||
pytest.skip(skip_reason)
|
||||
|
||||
if 'pep0484_typing' in case.path and sys.version_info[0] == 2:
|
||||
pytest.skip('ditch python 2 finally')
|
||||
|
||||
_CONTAINS_TYPING = ('pep0484_typing', 'pep0484_comments', 'pep0526_variables')
|
||||
if not has_typing and any(x in case.path for x in _CONTAINS_TYPING):
|
||||
pytest.skip('Needs the typing module installed to run this test.')
|
||||
|
||||
Reference in New Issue
Block a user