From 99b3b9f18aac047e31b708567f5da8bf2012d0fe Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sun, 18 May 2008 08:59:16 +0200 Subject: [PATCH] fix keyword arguments in function calls: must be unicode in Py3 and strings in Py2, just like identifiers --- Cython/Compiler/ExprNodes.py | 12 +++ Cython/Compiler/Nodes.py | 2 +- Cython/Compiler/Parsing.py | 3 +- tests/run/kwonlyargscall.pyx | 165 +++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 tests/run/kwonlyargscall.pyx diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index e8354575..c2ae18b4 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -736,6 +736,18 @@ class StringNode(ConstNode): return self.entry.cname +class KeywordNameNode(ConstNode): + # A keyword in a Python function call: a string that behaves like + # an identifier + type = PyrexTypes.py_object_type + + def analyse_types(self, env): + self.cname = env.intern_identifier(self.value) + + def calculate_result_code(self): + return self.cname + + class LongNode(AtomicExprNode): # Python long integer literal # diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index bd99380f..8d0f2e06 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -4347,7 +4347,7 @@ static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/ static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) { while (t->p) { #if PY_MAJOR_VERSION < 3 - if (t->is_unicode) { + if (t->is_unicode && (!t->is_identifier)) { *t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL); } else if (t->intern) { *t->p = PyString_InternFromString(t->s); diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index c52b19b9..a2f9f38f 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -284,8 +284,7 @@ def p_call(s, function): s.error("Expected an identifier before '='", pos = arg.pos) encoded_name = Utils.EncodedString(arg.name) - encoded_name.encoding = s.source_encoding - keyword = ExprNodes.StringNode(arg.pos, + keyword = ExprNodes.KeywordNameNode(arg.pos, value = encoded_name) arg = p_simple_expr(s) keyword_args.append((keyword, arg)) diff --git a/tests/run/kwonlyargscall.pyx b/tests/run/kwonlyargscall.pyx new file mode 100644 index 00000000..3f23dcd1 --- /dev/null +++ b/tests/run/kwonlyargscall.pyx @@ -0,0 +1,165 @@ +__doc__ = u""" + >>> call3(b) + >>> call4(b) + Traceback (most recent call last): + TypeError: function takes exactly 3 arguments (4 given) + + >>> call2(c) + >>> call3(c) + >>> call4(c) + Traceback (most recent call last): + TypeError: function takes at most 3 arguments (4 given) + + >>> call2(d) + >>> call2c(d) + + >>> call3(d) + Traceback (most recent call last): + TypeError: function takes at most 2 positional arguments (3 given) + >>> call2d(d) + Traceback (most recent call last): + TypeError: 'd' is an invalid keyword argument for this function + + >>> call2(e) + >>> call2c(e) + >>> call2d(e) + >>> call2cde(e) + >>> call3(e) + >>> call4(e) + Traceback (most recent call last): + TypeError: function takes at most 3 positional arguments (4 given) + + >>> call2c(f) + >>> call2cd(f) + + >>> call3(f) + Traceback (most recent call last): + TypeError: function takes at most 2 positional arguments (3 given) + >>> call2(f) + Traceback (most recent call last): + TypeError: required keyword argument 'c' is missing + >>> call2ce(f) + Traceback (most recent call last): + TypeError: 'e' is an invalid keyword argument for this function + + >>> call2cf(g) + >>> call2cefd(g) + >>> call2cfex(g) + + >>> call3(g) + Traceback (most recent call last): + TypeError: function takes at most 2 positional arguments (3 given) + >>> call2(g) + Traceback (most recent call last): + TypeError: required keyword argument 'c' is missing + >>> call2c(g) + Traceback (most recent call last): + TypeError: required keyword argument 'f' is missing + + >>> call2cf(h) + >>> call2cfe(h) + >>> call6cf(h) + >>> call6cfexy(h) + + >>> call3(h) + Traceback (most recent call last): + TypeError: required keyword argument 'c' is missing + >>> call3d(h) + Traceback (most recent call last): + TypeError: required keyword argument 'c' is missing + + >>> call2cf(k) + >>> call2cfe(k) + >>> call6df(k) + >>> call6dfexy(k) + + >>> call3(k) + Traceback (most recent call last): + TypeError: required keyword argument 'f' is missing + >>> call2d(k) + Traceback (most recent call last): + TypeError: required keyword argument 'f' is missing +""" + +import sys, re +if sys.version_info[0] >= 3: + __doc__ = re.sub(u"Error: (.*)exactly(.*)", u"Error: \\1at most\\2", __doc__) + +# the calls: + +def call2(f): + f(1,2) + +def call3(f): + f(1,2,3) + +def call4(f): + f(1,2,3,4) + +def call2c(f): + f(1,2, c=1) + +def call2d(f): + f(1,2, d=1) + +def call3d(f): + f(1,2,3, d=1) + +def call2cd(f): + f(1,2, c=1, d=2) + +def call2ce(f): + f(1,2, c=1, e=2) + +def call2cde(f): + f(1,2, c=1, d=2, e=3) + +def call2cf(f): + f(1,2, c=1, f=2) + +def call6cf(f): + f(1,2,3,4,5,6, c=1, f=2) + +def call6df(f): + f(1,2,3,4,5,6, d=1, f=2) + +def call2cfe(f): + f(1,2, c=1, f=2, e=3) + +def call2cefd(f): + f(1,2, c=1, e=0, f=2, d=11) + +def call2cfex(f): + f(1,2, c=1, f=2, e=0, x=25) + +def call6cfexy(f): + f(1,2,3,4,5,6, c=1, f=2, e=3, x=25, y=11) + +def call6dfexy(f): + f(1,2,3,4,5,6, d=1, f=2, e=3, x=25, y=11) + +# the called functions: + +def b(a, b, c): + pass + +def c(a, b, c=1): + pass + +def d(a, b, *, c = 88): + pass + +def e(a, b, c = 88, **kwds): + pass + +def f(a, b, *, c, d = 42): + pass + +def g(a, b, *, c, d = 42, e = 17, f, **kwds): + pass + +def h(a, b, *args, c, d = 42, e = 17, f, **kwds): + pass + +def k(a, b, c=1, *args, d = 42, e = 17, f, **kwds): + pass -- 2.26.2