forked from VimPlug/jedi
Better signature calculation
This commit is contained in:
@@ -92,7 +92,7 @@ def safe_getattr(obj, name, default=_sentinel):
|
|||||||
|
|
||||||
SignatureParam = namedtuple(
|
SignatureParam = namedtuple(
|
||||||
'SignatureParam',
|
'SignatureParam',
|
||||||
'name has_default default has_annotation annotation kind_name'
|
'name has_default default default_string has_annotation annotation annotation_string kind_name'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -382,15 +382,14 @@ class DirectObjectAccess(object):
|
|||||||
name=p.name,
|
name=p.name,
|
||||||
has_default=p.default is not p.empty,
|
has_default=p.default is not p.empty,
|
||||||
default=self._create_access_path(p.default),
|
default=self._create_access_path(p.default),
|
||||||
|
default_string=str(p.default),
|
||||||
has_annotation=p.annotation is not p.empty,
|
has_annotation=p.annotation is not p.empty,
|
||||||
annotation=self._create_access_path(p.annotation),
|
annotation=self._create_access_path(p.annotation),
|
||||||
|
annotation_string=str(p.default),
|
||||||
kind_name=str(p.kind)
|
kind_name=str(p.kind)
|
||||||
) for p in self._get_signature().parameters.values()
|
) for p in self._get_signature().parameters.values()
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_signature_text(self):
|
|
||||||
return str(self._get_signature())
|
|
||||||
|
|
||||||
def _get_signature(self):
|
def _get_signature(self):
|
||||||
obj = self._obj
|
obj = self._obj
|
||||||
if py_version < 33:
|
if py_version < 33:
|
||||||
|
|||||||
@@ -123,21 +123,15 @@ class CompiledObject(Context):
|
|||||||
if self.access_handle.ismethoddescriptor():
|
if self.access_handle.ismethoddescriptor():
|
||||||
tokens.insert(0, 'self')
|
tokens.insert(0, 'self')
|
||||||
for p in tokens:
|
for p in tokens:
|
||||||
parts = p.strip().split('=')
|
name, _, default = p.strip().partition('=')
|
||||||
yield UnresolvableParamName(self, parts[0])
|
yield UnresolvableParamName(self, name, default)
|
||||||
else:
|
else:
|
||||||
for signature_param in signature_params:
|
for signature_param in signature_params:
|
||||||
yield SignatureParamName(self, signature_param)
|
yield SignatureParamName(self, signature_param)
|
||||||
|
|
||||||
def get_signature_text(self):
|
|
||||||
try:
|
|
||||||
return self.access_handle.get_signature_text()
|
|
||||||
except ValueError:
|
|
||||||
params_str, ret = self._parse_function_doc()
|
|
||||||
return '(' + params_str + ')' + (ret and ' -> ' + ret)
|
|
||||||
|
|
||||||
def get_signatures(self):
|
def get_signatures(self):
|
||||||
return [BuiltinSignature(self)]
|
_, return_string = self._parse_function_doc()
|
||||||
|
return [BuiltinSignature(self, return_string)]
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<%s: %s>' % (self.__class__.__name__, self.access_handle.get_repr())
|
return '<%s: %s>' % (self.__class__.__name__, self.access_handle.get_repr())
|
||||||
@@ -295,6 +289,14 @@ class SignatureParamName(AbstractNameDefinition, ParamNameInterface):
|
|||||||
def string_name(self):
|
def string_name(self):
|
||||||
return self._signature_param.name
|
return self._signature_param.name
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
s = self.string_name
|
||||||
|
if self._signature_param.has_annotation:
|
||||||
|
s += ': ' + self._signature_param.annotation_string
|
||||||
|
if self._signature_param.has_default:
|
||||||
|
s += '=' + self._signature_param.default_string
|
||||||
|
return s
|
||||||
|
|
||||||
def get_kind(self):
|
def get_kind(self):
|
||||||
return getattr(Parameter, self._signature_param.kind_name)
|
return getattr(Parameter, self._signature_param.kind_name)
|
||||||
|
|
||||||
@@ -313,16 +315,23 @@ class SignatureParamName(AbstractNameDefinition, ParamNameInterface):
|
|||||||
return contexts
|
return contexts
|
||||||
|
|
||||||
|
|
||||||
class UnresolvableParamName(AbstractNameDefinition):
|
class UnresolvableParamName(AbstractNameDefinition, ParamNameInterface):
|
||||||
api_type = u'param'
|
api_type = u'param'
|
||||||
|
|
||||||
def __init__(self, compiled_obj, name):
|
def __init__(self, compiled_obj, name, default):
|
||||||
self.parent_context = compiled_obj.parent_context
|
self.parent_context = compiled_obj.parent_context
|
||||||
self.string_name = name
|
self.string_name = name
|
||||||
|
self._default = default
|
||||||
|
|
||||||
def get_kind(self):
|
def get_kind(self):
|
||||||
return Parameter.POSITIONAL_ONLY
|
return Parameter.POSITIONAL_ONLY
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
string = self.string_name
|
||||||
|
if self._default:
|
||||||
|
string += '=' + self._default
|
||||||
|
return string
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
return NO_CONTEXTS
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
from jedi._compatibility import Parameter
|
||||||
|
|
||||||
|
|
||||||
class AbstractSignature(object):
|
class AbstractSignature(object):
|
||||||
def __init__(self, context, is_bound=False):
|
def __init__(self, context, is_bound=False):
|
||||||
self.context = context
|
self.context = context
|
||||||
@@ -8,15 +11,33 @@ class AbstractSignature(object):
|
|||||||
return self.context.name
|
return self.context.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def annotation(self):
|
def annotation_string(self):
|
||||||
return None
|
return ''
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
param_code = ', '.join(n.to_string() for n in self.get_param_names())
|
def param_strings():
|
||||||
s = self.name.string_name + '(' + param_code + ')'
|
is_positional = False
|
||||||
annotation = self.annotation
|
is_kw_only = False
|
||||||
if annotation is not None:
|
for n in self.get_param_names():
|
||||||
s += ' -> ' + annotation.get_code(include_prefix=False)
|
kind = n.get_kind()
|
||||||
|
is_positional |= kind == Parameter.POSITIONAL_ONLY
|
||||||
|
if is_positional and kind != Parameter.POSITIONAL_ONLY:
|
||||||
|
yield '/'
|
||||||
|
is_positional = False
|
||||||
|
|
||||||
|
if kind == Parameter.KEYWORD_ONLY and not is_kw_only:
|
||||||
|
yield '*'
|
||||||
|
is_kw_only = True
|
||||||
|
|
||||||
|
yield n.to_string()
|
||||||
|
|
||||||
|
if is_positional:
|
||||||
|
yield '/'
|
||||||
|
|
||||||
|
s = self.name.string_name + '(' + ', '.join(param_strings()) + ')'
|
||||||
|
annotation = self.annotation_string
|
||||||
|
if annotation:
|
||||||
|
s += ' -> ' + annotation
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def bind(self, context):
|
def bind(self, context):
|
||||||
@@ -38,21 +59,33 @@ class TreeSignature(AbstractSignature):
|
|||||||
return TreeSignature(context, self._function_context, is_bound=True)
|
return TreeSignature(context, self._function_context, is_bound=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def annotation(self):
|
def _annotation(self):
|
||||||
# Classes don't need annotations, even if __init__ has one. They always
|
# Classes don't need annotations, even if __init__ has one. They always
|
||||||
# return themselves.
|
# return themselves.
|
||||||
if self.context.is_class():
|
if self.context.is_class():
|
||||||
return None
|
return None
|
||||||
return self._function_context.tree_node.annotation
|
return self._function_context.tree_node.annotation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def annotation_string(self):
|
||||||
|
a = self._annotation
|
||||||
|
if a is None:
|
||||||
|
return ''
|
||||||
|
return a.get_code(include_prefix=False)
|
||||||
|
|
||||||
|
|
||||||
class BuiltinSignature(AbstractSignature):
|
class BuiltinSignature(AbstractSignature):
|
||||||
|
def __init__(self, context, return_string, is_bound=False):
|
||||||
|
super(BuiltinSignature, self).__init__(context, is_bound)
|
||||||
|
self._return_string = return_string
|
||||||
|
|
||||||
|
@property
|
||||||
|
def annotation_string(self):
|
||||||
|
return self._return_string
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _function_context(self):
|
def _function_context(self):
|
||||||
return self.context
|
return self.context
|
||||||
|
|
||||||
def to_string(self):
|
|
||||||
return self.name.string_name + self.context.get_signature_text()
|
|
||||||
|
|
||||||
def bind(self, context):
|
def bind(self, context):
|
||||||
raise NotImplementedError('pls implement, need test case, %s' % context)
|
raise NotImplementedError('pls implement, need test case, %s' % context)
|
||||||
|
|||||||
@@ -6,19 +6,18 @@ from jedi.evaluate.gradual.conversion import stub_to_actual_context_set
|
|||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'code, sig, names, op, version', [
|
'code, sig, names, op, version', [
|
||||||
('import math; math.cos', 'cos(x)', ['x'], lt, (3, 7)),
|
('import math; math.cos', 'cos(x, /)', ['x'], ge, (2, 7)),
|
||||||
('import math; math.cos', 'cos(x, /)', ['x'], ge, (3, 7)),
|
|
||||||
|
|
||||||
('next', 'next(iterator, default=None)', ['iterator', 'default'], ge, (2, 7)),
|
('next', 'next(iterator, default=None, /)', ['iterator', 'default'], ge, (2, 7)),
|
||||||
|
|
||||||
('pow', 'pow(x, y, z=None) -> number', ['x', 'y', 'z'], lt, (3, 5)),
|
('pow', 'pow(x, y, z=None, /) -> number', ['x', 'y', 'z'], lt, (3, 5)),
|
||||||
('pow', 'pow(x, y, z=None, /)', ['x', 'y', 'z'], ge, (3, 5)),
|
('pow', 'pow(x, y, z=None, /)', ['x', 'y', 'z'], ge, (3, 5)),
|
||||||
|
|
||||||
('bytes.partition', 'partition(self, sep) -> (head, sep, tail)', ['self', 'sep'], lt, (3, 5)),
|
('bytes.partition', 'partition(self, sep, /) -> (head, sep, tail)', ['self', 'sep'], lt, (3, 5)),
|
||||||
('bytes.partition', 'partition(self, sep, /)', ['self', 'sep'], ge, (3, 5)),
|
('bytes.partition', 'partition(self, sep, /)', ['self', 'sep'], ge, (3, 5)),
|
||||||
|
|
||||||
('bytes().partition', 'partition(sep) -> (head, sep, tail)', ['sep'], lt, (3, 5)),
|
('bytes().partition', 'partition(sep, /) -> (head, sep, tail)', ['sep'], lt, (3, 5)),
|
||||||
('bytes().partition', 'partition(self, sep, /)', ['sep'], ge, (3, 5)),
|
('bytes().partition', 'partition(sep, /)', ['sep'], ge, (3, 5)),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_compiled_signature(Script, environment, code, sig, names, op, version):
|
def test_compiled_signature(Script, environment, code, sig, names, op, version):
|
||||||
@@ -31,4 +30,3 @@ def test_compiled_signature(Script, environment, code, sig, names, op, version):
|
|||||||
signature, = compiled.get_signatures()
|
signature, = compiled.get_signatures()
|
||||||
assert signature.to_string() == sig
|
assert signature.to_string() == sig
|
||||||
assert [n.string_name for n in signature.get_param_names()] == names
|
assert [n.string_name for n in signature.get_param_names()] == names
|
||||||
assert signature.annotation is None
|
|
||||||
|
|||||||
Reference in New Issue
Block a user