From: Stefan Behnel Date: Thu, 15 Apr 2010 09:43:57 +0000 (+0200) Subject: support 'not None' and 'or None' on any Python argument X-Git-Tag: 0.13.beta0~206 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=cc9f1333fa1bbfd0dae34a2aaf1154db28c51923;p=cython.git support 'not None' and 'or None' on any Python argument --- diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 8781cc1e..7395064c 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1405,6 +1405,7 @@ class FuncDefNode(StatNode, BlockNode): def generate_arg_type_test(self, arg, code): # Generate type test for one argument. if arg.type.typeobj_is_available(): + code.globalstate.use_utility_code(arg_type_test_utility_code) typeptr_cname = arg.type.typeptr_cname arg_code = "((PyObject *)%s)" % arg.entry.cname code.putln( @@ -1418,6 +1419,15 @@ class FuncDefNode(StatNode, BlockNode): else: error(arg.pos, "Cannot test type of extern C class " "without type object name specification") + + def generate_arg_none_check(self, arg, code): + # Generate None check for one argument. + code.globalstate.use_utility_code(arg_type_test_utility_code) + code.putln('if (unlikely(((PyObject *)%s) == Py_None)) {' % arg.entry.cname) + code.putln('''PyErr_Format(PyExc_TypeError, "Argument '%s' must not be None"); %s''' % ( + arg.name, + code.error_goto(arg.pos))) + code.putln('}') def generate_wrapper_functions(self, code): pass @@ -1667,6 +1677,8 @@ class CFuncDefNode(FuncDefNode): for arg in self.type.args: if arg.needs_type_test: self.generate_arg_type_test(arg, code) + elif arg.type.is_pyobject and not arg.accept_none: + self.generate_arg_none_check(arg, code) def error_value(self): if self.return_type.is_pyobject: @@ -1880,23 +1892,25 @@ class DefNode(FuncDefNode): arg.needs_conversion = 0 arg.needs_type_test = 0 arg.is_generic = 1 - if arg.type.is_extension_type or arg.type.is_builtin_type: + if arg.type.is_pyobject: if arg.or_none: arg.accept_none = True elif arg.not_none: arg.accept_none = False - else: + elif arg.type.is_extension_type or arg.type.is_builtin_type: # default depends on compiler directive arg.accept_none = allow_none_for_extension_args + else: + # probably just a plain 'object' + arg.accept_none = True else: - arg.accept_none = True + arg.accept_none = True # won't be used, but must be there if arg.not_none: - error(self.pos, "Only extension type arguments can have 'not None'") + error(self.pos, "Only Python type arguments can have 'not None'") if arg.or_none: - error(self.pos, "Only extension type arguments can have 'or None'") + error(self.pos, "Only Python type arguments can have 'or None'") def analyse_signature(self, env): - any_type_tests_needed = 0 if self.entry.is_special: self.entry.trivial_signature = len(self.args) == 1 and not (self.star_arg or self.starstar_arg) elif not env.directives['always_allow_keywords'] and not (self.star_arg or self.starstar_arg): @@ -1941,7 +1955,6 @@ class DefNode(FuncDefNode): if not arg.type.same_as(arg.hdr_type): if arg.hdr_type.is_pyobject and arg.type.is_pyobject: arg.needs_type_test = 1 - any_type_tests_needed = 1 else: arg.needs_conversion = 1 if arg.needs_conversion: @@ -1959,9 +1972,6 @@ class DefNode(FuncDefNode): if arg.is_generic and \ (arg.type.is_extension_type or arg.type.is_builtin_type): arg.needs_type_test = 1 - any_type_tests_needed = 1 - if any_type_tests_needed: - env.use_utility_code(arg_type_test_utility_code) def bad_signature(self): sig = self.entry.signature @@ -2657,7 +2667,9 @@ class DefNode(FuncDefNode): for arg in self.args: if arg.needs_type_test: self.generate_arg_type_test(arg, code) - + elif not arg.accept_none and arg.type.is_pyobject: + self.generate_arg_none_check(arg, code) + def error_value(self): return self.entry.signature.error_value diff --git a/tests/errors/e_notnone2.pyx b/tests/errors/e_notnone2.pyx index 60e4781f..cb2defd7 100644 --- a/tests/errors/e_notnone2.pyx +++ b/tests/errors/e_notnone2.pyx @@ -1,6 +1,6 @@ -def eggs(int x not None, y not None): +def eggs(int x not None, char* y not None): pass _ERRORS = u""" -1:0: Only extension type arguments can have 'not None' -1:0: Only extension type arguments can have 'not None' +1:0: Only Python type arguments can have 'not None' +1:0: Only Python type arguments can have 'not None' """ diff --git a/tests/run/ext_type_none_arg.pyx b/tests/run/ext_type_none_arg.pyx index fab9ed37..93dd40d5 100644 --- a/tests/run/ext_type_none_arg.pyx +++ b/tests/run/ext_type_none_arg.pyx @@ -15,7 +15,7 @@ cdef attr(MyExtType x): # defaults, without 'not/or None' -def ext_default(MyExtType x): +def ext_default(MyExtType x): # currently behaves like 'or None' """ >>> ext_default(MyExtType()) 123 @@ -76,7 +76,7 @@ cdef litem(list L, int item): # defaults, without 'not/or None' -def builtin_default(list L): +def builtin_default(list L): # currently behaves like 'or None' """ >>> builtin_default([123]) 123 @@ -129,23 +129,75 @@ def builtin_not_none(list L not None): return litem(L, 0) -## builtin type 'object' (which includes None!) - currently unclear! - -## def object_or_none(object o or None): -## """ -## >>> object_or_none([]) -## list -## >>> object_or_none(None) -## NoneType -## """ -## return type(o).__name__ - -## def object_not_none(object o not None): -## """ -## >>> object_not_none([123]) -## 123 -## >>> object_not_none(None) -## Traceback (most recent call last): -## TypeError: Argument 'o' has incorrect type (expected object, got NoneType) -## """ -## return type(o).__name__ +## builtin type 'object' - isinstance(None, object) is True! + +def object_default(object o): # behaves like 'or None' + """ + >>> object_default(object()) + 'object' + >>> object_default([]) + 'list' + >>> object_default(None) + 'NoneType' + """ + return type(o).__name__ + +def object_or_none(object o or None): + """ + >>> object_or_none(object()) + 'object' + >>> object_or_none([]) + 'list' + >>> object_or_none(None) + 'NoneType' + """ + return type(o).__name__ + +def object_not_none(object o not None): + """ + >>> object_not_none(object()) + 'object' + >>> object_not_none([]) + 'list' + >>> object_not_none(None) + Traceback (most recent call last): + TypeError: Argument 'o' must not be None + """ + return type(o).__name__ + + +## untyped 'object' - isinstance(None, object) is True! + +def notype_default(o): # behaves like 'or None' + """ + >>> notype_default(object()) + 'object' + >>> notype_default([]) + 'list' + >>> notype_default(None) + 'NoneType' + """ + return type(o).__name__ + +def notype_or_none(o or None): + """ + >>> notype_or_none(object()) + 'object' + >>> notype_or_none([]) + 'list' + >>> notype_or_none(None) + 'NoneType' + """ + return type(o).__name__ + +def notype_not_none(o not None): + """ + >>> notype_not_none(object()) + 'object' + >>> notype_not_none([]) + 'list' + >>> notype_not_none(None) + Traceback (most recent call last): + TypeError: Argument 'o' must not be None + """ + return type(o).__name__