1
0
forked from VimPlug/jedi

Better signature calculation

This commit is contained in:
Dave Halter
2019-05-23 01:36:51 +02:00
parent b2b08ab432
commit 9aa8f6bcf2
4 changed files with 74 additions and 35 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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)

View File

@@ -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