mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-18 03:25:55 +08:00
Fix partial signatures for MixedObject
Now a MixedObject return the signatures of its CompiledObject all the time, fixes #1371
This commit is contained in:
@@ -175,6 +175,18 @@ def _force_unicode_decorator(func):
|
|||||||
return lambda *args, **kwargs: force_unicode(func(*args, **kwargs))
|
return lambda *args, **kwargs: force_unicode(func(*args, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_type(obj):
|
||||||
|
if inspect.isclass(obj):
|
||||||
|
return u'class'
|
||||||
|
elif inspect.ismodule(obj):
|
||||||
|
return u'module'
|
||||||
|
elif inspect.isbuiltin(obj) or inspect.ismethod(obj) \
|
||||||
|
or inspect.ismethoddescriptor(obj) or inspect.isfunction(obj):
|
||||||
|
return u'function'
|
||||||
|
# Everything else...
|
||||||
|
return u'instance'
|
||||||
|
|
||||||
|
|
||||||
class DirectObjectAccess(object):
|
class DirectObjectAccess(object):
|
||||||
def __init__(self, evaluator, obj):
|
def __init__(self, evaluator, obj):
|
||||||
self._evaluator = evaluator
|
self._evaluator = evaluator
|
||||||
@@ -352,16 +364,7 @@ class DirectObjectAccess(object):
|
|||||||
raise ValueError("Object is type %s and not simple" % type(self._obj))
|
raise ValueError("Object is type %s and not simple" % type(self._obj))
|
||||||
|
|
||||||
def get_api_type(self):
|
def get_api_type(self):
|
||||||
obj = self._obj
|
return get_api_type(self._obj)
|
||||||
if self.is_class():
|
|
||||||
return u'class'
|
|
||||||
elif inspect.ismodule(obj):
|
|
||||||
return u'module'
|
|
||||||
elif inspect.isbuiltin(obj) or inspect.ismethod(obj) \
|
|
||||||
or inspect.ismethoddescriptor(obj) or inspect.isfunction(obj):
|
|
||||||
return u'function'
|
|
||||||
# Everything else...
|
|
||||||
return u'instance'
|
|
||||||
|
|
||||||
def get_access_path_tuples(self):
|
def get_access_path_tuples(self):
|
||||||
accesses = [create_access(self._evaluator, o) for o in self._get_objects_path()]
|
accesses = [create_access(self._evaluator, o) for o in self._get_objects_path()]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from jedi.evaluate.context import ModuleContext
|
|||||||
from jedi.evaluate.cache import evaluator_function_cache
|
from jedi.evaluate.cache import evaluator_function_cache
|
||||||
from jedi.evaluate.compiled.getattr_static import getattr_static
|
from jedi.evaluate.compiled.getattr_static import getattr_static
|
||||||
from jedi.evaluate.compiled.access import compiled_objects_cache, \
|
from jedi.evaluate.compiled.access import compiled_objects_cache, \
|
||||||
ALLOWED_GETITEM_TYPES
|
ALLOWED_GETITEM_TYPES, get_api_type
|
||||||
from jedi.evaluate.compiled.context import create_cached_compiled_object
|
from jedi.evaluate.compiled.context import create_cached_compiled_object
|
||||||
from jedi.evaluate.gradual.conversion import to_stub
|
from jedi.evaluate.gradual.conversion import to_stub
|
||||||
|
|
||||||
@@ -50,6 +50,11 @@ class MixedObject(ContextWrapper):
|
|||||||
def get_filters(self, *args, **kwargs):
|
def get_filters(self, *args, **kwargs):
|
||||||
yield MixedObjectFilter(self.evaluator, self)
|
yield MixedObjectFilter(self.evaluator, self)
|
||||||
|
|
||||||
|
def get_signatures(self):
|
||||||
|
# Prefer `inspect.signature` over somehow analyzing Python code. It
|
||||||
|
# should be very precise, especially for stuff like `partial`.
|
||||||
|
return self.compiled_object.get_signatures()
|
||||||
|
|
||||||
def py__call__(self, arguments):
|
def py__call__(self, arguments):
|
||||||
return (to_stub(self._wrapped_context) or self._wrapped_context).py__call__(arguments)
|
return (to_stub(self._wrapped_context) or self._wrapped_context).py__call__(arguments)
|
||||||
|
|
||||||
@@ -151,6 +156,7 @@ def _get_object_to_check(python_object):
|
|||||||
|
|
||||||
|
|
||||||
def _find_syntax_node_name(evaluator, python_object):
|
def _find_syntax_node_name(evaluator, python_object):
|
||||||
|
original_object = python_object
|
||||||
try:
|
try:
|
||||||
python_object = _get_object_to_check(python_object)
|
python_object = _get_object_to_check(python_object)
|
||||||
path = inspect.getsourcefile(python_object)
|
path = inspect.getsourcefile(python_object)
|
||||||
@@ -214,7 +220,13 @@ def _find_syntax_node_name(evaluator, python_object):
|
|||||||
# completions at some points but will lead to mostly correct type
|
# completions at some points but will lead to mostly correct type
|
||||||
# inference, because people tend to define a public name in a module only
|
# inference, because people tend to define a public name in a module only
|
||||||
# once.
|
# once.
|
||||||
return module_node, names[-1].parent, file_io, code_lines
|
tree_node = names[-1].parent
|
||||||
|
if tree_node.type == 'funcdef' and get_api_type(original_object) == 'instance':
|
||||||
|
# If an instance is given and we're landing on a function (e.g.
|
||||||
|
# partial in 3.5), something is completely wrong and we should not
|
||||||
|
# return that.
|
||||||
|
return None
|
||||||
|
return module_node, tree_node, file_io, code_lines
|
||||||
|
|
||||||
|
|
||||||
@compiled_objects_cache('mixed_cache')
|
@compiled_objects_cache('mixed_cache')
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ def test_property_error_newstyle():
|
|||||||
assert lst == []
|
assert lst == []
|
||||||
|
|
||||||
|
|
||||||
def test_param_completion():
|
def test_param_completion(skip_python2):
|
||||||
def foo(bar):
|
def foo(bar):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -275,7 +275,7 @@ def test_completion_param_annotations():
|
|||||||
assert d.name == 'bytes'
|
assert d.name == 'bytes'
|
||||||
|
|
||||||
|
|
||||||
def test_keyword_argument():
|
def test_keyword_argument(skip_python2):
|
||||||
def f(some_keyword_argument):
|
def f(some_keyword_argument):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -440,7 +440,33 @@ def test__wrapped__():
|
|||||||
# Apparently the function starts on the line where the decorator starts.
|
# Apparently the function starts on the line where the decorator starts.
|
||||||
assert c.line == syslogs_to_df.__wrapped__.__code__.co_firstlineno + 1
|
assert c.line == syslogs_to_df.__wrapped__.__code__.co_firstlineno + 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('module_name', ['sys', 'time'])
|
@pytest.mark.parametrize('module_name', ['sys', 'time'])
|
||||||
def test_core_module_completes(module_name):
|
def test_core_module_completes(module_name):
|
||||||
module = import_module(module_name)
|
module = import_module(module_name)
|
||||||
assert jedi.Interpreter(module_name + '.\n', [locals()]).completions()
|
assert jedi.Interpreter(module_name + '.\n', [locals()]).completions()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'code, expected, index', [
|
||||||
|
('a(', ['a', 'b', 'c'], 0),
|
||||||
|
('b(', ['b', 'c'], 0),
|
||||||
|
# Might or might not be correct, because c is given as a keyword
|
||||||
|
# argument as well, but that is just what inspect.signature returns.
|
||||||
|
('c(', ['b', 'c'], 0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_partial_signatures(code, expected, index, skip_python2):
|
||||||
|
import functools
|
||||||
|
|
||||||
|
def func(a, b, c):
|
||||||
|
pass
|
||||||
|
|
||||||
|
a = functools.partial(func)
|
||||||
|
b = functools.partial(func, 1)
|
||||||
|
c = functools.partial(func, 1, c=2)
|
||||||
|
|
||||||
|
sig, = jedi.Interpreter(code, [locals()]).call_signatures()
|
||||||
|
assert sig.name == 'partial'
|
||||||
|
assert [p.name for p in sig.params] == expected
|
||||||
|
assert index == sig.index
|
||||||
|
|||||||
Reference in New Issue
Block a user