Make partial use the __doc__ of its function, fixes #1621

This commit is contained in:
Dave Halter
2020-06-25 09:32:48 +02:00
parent 737c1e5792
commit 59ccd2da93
3 changed files with 37 additions and 10 deletions

View File

@@ -18,6 +18,7 @@ def _stub_to_python_value_set(stub_value, ignore_compiled=False):
was_instance = stub_value.is_instance()
if was_instance:
arguments = getattr(stub_value, '_arguments', None)
stub_value = stub_value.py__class__()
qualified_names = stub_value.get_qualified_names()
@@ -30,11 +31,12 @@ def _stub_to_python_value_set(stub_value, ignore_compiled=False):
method_name = qualified_names[-1]
qualified_names = qualified_names[:-1]
was_instance = True
arguments = None
values = _infer_from_stub(stub_module_context, qualified_names, ignore_compiled)
if was_instance:
values = ValueSet.from_sets(
c.execute_with_values()
c.execute_with_values() if arguments is None else c.execute(arguments)
for c in values
if c.is_class()
)

View File

@@ -474,11 +474,10 @@ def collections_namedtuple(value, arguments, callback):
class PartialObject(ValueWrapper):
def __init__(self, actual_value, arguments, instance=None):
super(PartialObject, self).__init__(actual_value)
self._actual_value = actual_value
self._arguments = arguments
self._instance = instance
def _get_function(self, unpacked_arguments):
def _get_functions(self, unpacked_arguments):
key, lazy_value = next(unpacked_arguments, (None, None))
if key is not None or lazy_value is None:
debug.warning("Partial should have a proper function %s", self._arguments)
@@ -487,8 +486,8 @@ class PartialObject(ValueWrapper):
def get_signatures(self):
unpacked_arguments = self._arguments.unpack()
func = self._get_function(unpacked_arguments)
if func is None:
funcs = self._get_functions(unpacked_arguments)
if funcs is None:
return []
arg_count = 0
@@ -500,17 +499,30 @@ class PartialObject(ValueWrapper):
arg_count += 1
else:
keys.add(key)
return [PartialSignature(s, arg_count, keys) for s in func.get_signatures()]
return [PartialSignature(s, arg_count, keys) for s in funcs.get_signatures()]
def py__call__(self, arguments):
func = self._get_function(self._arguments.unpack())
if func is None:
funcs = self._get_functions(self._arguments.unpack())
if funcs is None:
return NO_VALUES
return func.execute(
return funcs.execute(
MergedPartialArguments(self._arguments, arguments, self._instance)
)
def py__doc__(self):
"""
In CPython partial does not replace the docstring. However we are still
imitating it here, because we want this docstring to be worth something
for the user.
"""
callables = self._get_functions(self._arguments.unpack())
if callables is None:
return ''
for callable_ in callables:
return callable_.py__doc__()
return ''
def py__get__(self, instance, class_value):
return ValueSet([self])
@@ -519,7 +531,7 @@ class PartialMethodObject(PartialObject):
def py__get__(self, instance, class_value):
if instance is None:
return ValueSet([self])
return ValueSet([PartialObject(self._actual_value, self._arguments, instance)])
return ValueSet([PartialObject(self._wrapped_value, self._arguments, instance)])
class PartialSignature(SignatureWrapper):

View File

@@ -422,6 +422,19 @@ def test_decorator(Script):
assert d.docstring(raw=True) == 'Nice docstring'
def test_partial(Script):
code = dedent('''
def foo():
'x y z'
from functools import partial
x = partial(foo)
x''')
p1, p2 = Script(code).infer()
assert p1.docstring(raw=True) == 'x y z'
assert p2.docstring(raw=True) == 'x y z'
def test_basic_str_init_signature(Script, disable_typeshed):
# See GH #1414 and GH #1426
code = dedent('''