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,21 +9371,22 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -9371,21 +9371,22 @@ 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 arg.default.is_literal: if not must_use_constants:
arg.is_dynamic = True if not arg.default.is_literal:
if arg.type.is_pyobject: arg.is_dynamic = True
nonliteral_objects.append(arg) if arg.type.is_pyobject:
nonliteral_objects.append(arg)
else:
nonliteral_other.append(arg)
else: else:
nonliteral_other.append(arg) arg.default = DefaultLiteralArgNode(arg.pos, arg.default)
else: if arg.default.type and arg.default.type.can_coerce_to_pyobject(env):
arg.default = DefaultLiteralArgNode(arg.pos, arg.default) if arg.kw_only:
if arg.kw_only: default_kwargs.append(arg)
default_kwargs.append(arg) else:
else: default_args.append(arg)
default_args.append(arg)
if arg.annotation: if arg.annotation:
arg.annotation = arg.annotation.analyse_types(env) arg.annotation = arg.annotation.analyse_types(env)
annotations.append((arg.pos, arg.name, arg.annotation.string)) annotations.append((arg.pos, arg.name, arg.annotation.string))
......
...@@ -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