Commit 98fc9f1a authored by da-woods's avatar da-woods Committed by GitHub

Warn when cyfunction.__[kw]defaults__ is changed (GH-4121)

* Warn when cyfunction.__[kw]defaults__ is changed

Cython can't make use of these new defaults so it seems sensible
to alert the user (even if we're choosing to allow the assignment
for compatibility reasons).

I know the warning mechanism is somewhat heavy. I think this is
OK because it's unlikely that users will be repeatedly assigning
to these attributes in a time-critical loop.

Doesn't fix https://github.com/cython/cython/issues/2650, but hopefully alerts users to it.
parent 0dffecfa
......@@ -320,6 +320,8 @@ __Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value, void
"__defaults__ must be set to a tuple object");
return -1;
}
PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__defaults__ will not "
"currently affect the values used in function calls", 1);
Py_INCREF(value);
__Pyx_Py_XDECREF_SET(op->defaults_tuple, value);
return 0;
......@@ -352,6 +354,8 @@ __Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value, voi
"__kwdefaults__ must be set to a dict object");
return -1;
}
PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__kwdefaults__ will not "
"currently affect the values used in function calls", 1);
Py_INCREF(value);
__Pyx_Py_XDECREF_SET(op->defaults_kwdict, value);
return 0;
......
......@@ -86,10 +86,49 @@ def test_defaults_nonliteral_func_call(f):
return a
return func
def assign_defaults_and_check_warnings(func, value=None, delete=False):
import warnings
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
if delete:
del func.__defaults__
else:
func.__defaults__ = value
assert len(w) == 1, len(w)
assert issubclass(w[0].category, RuntimeWarning), w[0].category
assert "changes to cyfunction.__defaults__" in str(w[0].message), str(w[0].message)
def test_assign_defaults():
"""
>>> f = test_assign_defaults()
>>> f.__defaults__
(5, 10)
>>> assign_defaults_and_check_warnings(f, value=())
>>> f.__defaults__
()
>>> assign_defaults_and_check_warnings(f, delete=True)
>>> f.__defaults__
>>> f.__defaults__ = "Not a tuple"
Traceback (most recent call last):
TypeError: __defaults__ must be set to a tuple object
"""
def func(a=5, b=10):
return a, b
return func
def cy_kwonly_default_args(a, x=1, *, b=2):
l = m = 1
def assign_kwdefaults_and_check_warnings(func, value):
import warnings
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
func.__kwdefaults__ = value
assert len(w) == 1, len(w)
assert issubclass(w[0].category, RuntimeWarning), w[0].category
assert "changes to cyfunction.__kwdefaults__" in str(w[0].message), str(w[0].message)
def test_kwdefaults(value):
"""
>>> cy_kwonly_default_args.__defaults__
......@@ -111,12 +150,12 @@ def test_kwdefaults(value):
>>> f.__kwdefaults__ = ()
Traceback (most recent call last):
TypeError: __kwdefaults__ must be set to a dict object
>>> f.__kwdefaults__ = None
>>> assign_kwdefaults_and_check_warnings(f, None)
>>> f.__kwdefaults__
>>> f.__kwdefaults__ = {}
>>> assign_kwdefaults_and_check_warnings(f, {})
>>> f.__kwdefaults__
{}
>>> f.__kwdefaults__ = {'a': 2}
>>> assign_kwdefaults_and_check_warnings(f, {'a': 2})
>>> f.__kwdefaults__
{'a': 2}
"""
......
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