Commit ee7ae0b9 authored by Vladimir Matveev's avatar Vladimir Matveev Committed by GitHub

Preserve default arguments values on methods for introspection whenever possible (GH-4118)

Closes https://github.com/cython/cython/issues/4061
parent 73521046
...@@ -9371,9 +9371,9 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -9371,9 +9371,9 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
# so their optional arguments must be static, too. # so their optional arguments must be static, too.
# TODO: change CyFunction implementation to pass both function object and owning object for method calls # TODO: change CyFunction implementation to pass both function object and owning object for method calls
must_use_constants = env.is_c_class_scope or (self.def_node.is_wrapper and env.is_module_scope) must_use_constants = env.is_c_class_scope or (self.def_node.is_wrapper and env.is_module_scope)
for arg in self.def_node.args: for arg in self.def_node.args:
if arg.default and not must_use_constants: if arg.default:
if not must_use_constants:
if not arg.default.is_literal: if not arg.default.is_literal:
arg.is_dynamic = True arg.is_dynamic = True
if arg.type.is_pyobject: if arg.type.is_pyobject:
...@@ -9382,6 +9382,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -9382,6 +9382,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
nonliteral_other.append(arg) nonliteral_other.append(arg)
else: else:
arg.default = DefaultLiteralArgNode(arg.pos, arg.default) arg.default = DefaultLiteralArgNode(arg.pos, arg.default)
if arg.default.type and arg.default.type.can_coerce_to_pyobject(env):
if arg.kw_only: if arg.kw_only:
default_kwargs.append(arg) default_kwargs.append(arg)
else: else:
......
...@@ -251,3 +251,51 @@ def test_func_default_scope_local(): ...@@ -251,3 +251,51 @@ def test_func_default_scope_local():
return arg return arg
print i # genexprs don't leak print i # genexprs don't leak
return func return func
cdef class C:
def f1(self, a, b=1, c=[]):
pass
def f2(self, a, b=1,/, c=[1]):
pass
def f3(self, a, /, b=1, *, c=[1]):
pass
cpdef f4(self, a, char*c=NULL):
pass
cpdef f5(self, a, str s = "123"):
pass
cpdef f6(self, a, int s = 4):
pass
cpdef f7(self, a, dict s = {'a':22}):
pass
cpdef f8(self, a, list s = [15]):
pass
def check_defaults_on_methods_for_introspection():
"""
>>> C.f1.__defaults__
(1, [])
>>> C.f1.__kwdefaults__
>>> C.f2.__defaults__
(1, [1])
>>> C.f2.__kwdefaults__
>>> C.f3.__defaults__
(1,)
>>> C.f3.__kwdefaults__
{'c': [1]}
>>> C.f4.__defaults__
>>> C.f4.__kwdefaults__
>>> C.f5.__defaults__
('123',)
>>> C.f5.__kwdefaults__
>>> C.f6.__defaults__
(4,)
>>> C.f6.__kwdefaults__
>>> C.f7.__defaults__
({'a': 22},)
>>> C.f7.__kwdefaults__
>>> C.f8.__defaults__
([15],)
>>> C.f8.__kwdefaults__
"""
pass
...@@ -101,19 +101,17 @@ cpdef cp1(a, b): ...@@ -101,19 +101,17 @@ cpdef cp1(a, b):
""" """
# Currently broken, see GH #1864
cpdef cp2(a, b=True): cpdef cp2(a, b=True):
""" """
>>> def py_cp2(a, b=True): pass >>> def py_cp2(a, b=True): pass
#>>> signatures_match(cp2, py_cp2) >>> signatures_match(cp2, py_cp2)
""" """
# Currently broken, see GH #1864
cpdef cp3(a=1, b=True): cpdef cp3(a=1, b=True):
""" """
>>> def py_cp3(a=1, b=True): pass >>> def py_cp3(a=1, b=True): pass
#>>> signatures_match(cp3, py_cp3) >>> signatures_match(cp3, py_cp3)
""" """
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment