diff options
| author | da-woods <dw-git@d-woods.co.uk> | 2020-04-15 19:34:39 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-04-15 20:34:39 +0200 |
| commit | 192925d8800421a791a783307118246b9c3d922f (patch) | |
| tree | 79d043253a6b7270875bef7567997dcf400066c9 | |
| parent | c1f3fd2831083b675acd6b0ee73dddcec5671d31 (diff) | |
| download | cython-192925d8800421a791a783307118246b9c3d922f.tar.gz | |
Prevent fused dispatcher optional arguments being overwritten (GH-3519)
* Prevent fused dispatcher optional arguments being overwritten
If the 5th argument of the regular functions was optional it'd
overwrite the _fused_sigindex default of the dispatcher, causing
type errors at runtime.
closes https://github.com/cython/cython/issues/3511
| -rw-r--r-- | Cython/Compiler/ExprNodes.py | 22 | ||||
| -rw-r--r-- | Cython/Compiler/FusedNode.py | 3 | ||||
| -rw-r--r-- | tests/run/fused_def.pyx | 18 |
3 files changed, 26 insertions, 17 deletions
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 187187af5..1ad7a3ad2 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -9449,15 +9449,19 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): if self.defaults_tuple: code.putln('__Pyx_CyFunction_SetDefaultsTuple(%s, %s);' % ( self.result(), self.defaults_tuple.py_result())) - if self.defaults_kwdict: - code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % ( - self.result(), self.defaults_kwdict.py_result())) - if def_node.defaults_getter: - code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % ( - self.result(), def_node.defaults_getter.entry.pyfunc_cname)) - if self.annotations_dict: - code.putln('__Pyx_CyFunction_SetAnnotationsDict(%s, %s);' % ( - self.result(), self.annotations_dict.py_result())) + if not self.specialized_cpdefs: + # disable introspection functions for fused dispatcher function since the user never sees it + # TODO: this is mostly disabled because the attributes end up pointing to ones belonging + # to the specializations - ideally this would be fixed instead + if self.defaults_kwdict: + code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % ( + self.result(), self.defaults_kwdict.py_result())) + if def_node.defaults_getter: + code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % ( + self.result(), def_node.defaults_getter.entry.pyfunc_cname)) + if self.annotations_dict: + code.putln('__Pyx_CyFunction_SetAnnotationsDict(%s, %s);' % ( + self.result(), self.annotations_dict.py_result())) class InnerFunctionNode(PyCFunctionNode): diff --git a/Cython/Compiler/FusedNode.py b/Cython/Compiler/FusedNode.py index 456103a5e..0072174c3 100644 --- a/Cython/Compiler/FusedNode.py +++ b/Cython/Compiler/FusedNode.py @@ -839,7 +839,8 @@ class FusedCFuncDefNode(StatListNode): for i, stat in enumerate(self.stats): stat = self.stats[i] = stat.analyse_expressions(env) - if isinstance(stat, FuncDefNode): + if isinstance(stat, FuncDefNode) and stat is not self.py_func: + # the dispatcher specifically doesn't want its defaults overriding for arg, default in zip(stat.args, defaults): if default is not None: arg.default = CloneNode(default).coerce_to(arg.type, env) diff --git a/tests/run/fused_def.pyx b/tests/run/fused_def.pyx index f0e1be4c8..efd578298 100644 --- a/tests/run/fused_def.pyx +++ b/tests/run/fused_def.pyx @@ -54,9 +54,13 @@ f = 5.6 i = 9 -def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7): +def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7, + another_opt = 2, yet_another_opt=3): """ - Test runtime dispatch, indexing of various kinds and optional arguments + Test runtime dispatch, indexing of various kinds and optional arguments. + Use 5 arguments because at one point the optional argument from the + 5th argument was overwriting that of the __pyx_fused dispatcher. + https://github.com/cython/cython/issues/3511 >>> opt_func("spam", f, i) str object double long @@ -121,9 +125,9 @@ def opt_func(fused_t obj, cython.floating myf = 1.2, cython.integral myi = 7): >>> opt_func() Traceback (most recent call last): TypeError: Expected at least 1 argument, got 0 - >>> opt_func("abc", f, i, 5) # doctest: +ELLIPSIS + >>> opt_func("abc", f, i, 5, 5, 5) # doctest: +ELLIPSIS Traceback (most recent call last): - TypeError: ...at most 3... + TypeError: ...at most 5... >>> opt_func[ExtClassA, cy.float, cy.long](object(), f) Traceback (most recent call last): TypeError: Argument 'obj' has incorrect type (expected fused_def.ExtClassA, got object) @@ -154,19 +158,19 @@ def test_opt_func(): def test_opt_func_introspection(): """ >>> opt_func.__defaults__ - (1.2, 7) + (1.2, 7, 2, 3) >>> opt_func.__kwdefaults__ >>> opt_func.__annotations__ {} >>> opt_func[str, float, int].__defaults__ - (1.2, 7) + (1.2, 7, 2, 3) >>> opt_func[str, float, int].__kwdefaults__ >>> opt_func[str, float, int].__annotations__ {} >>> opt_func[str, cy.double, cy.long].__defaults__ - (1.2, 7) + (1.2, 7, 2, 3) >>> opt_func[str, cy.double, cy.long].__kwdefaults__ >>> opt_func[str, cy.double, cy.long].__annotations__ {} |
