Commit cc9f1333 authored by Stefan Behnel's avatar Stefan Behnel

support 'not None' and 'or None' on any Python argument

parent 12ad333d
...@@ -1405,6 +1405,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1405,6 +1405,7 @@ class FuncDefNode(StatNode, BlockNode):
def generate_arg_type_test(self, arg, code): def generate_arg_type_test(self, arg, code):
# Generate type test for one argument. # Generate type test for one argument.
if arg.type.typeobj_is_available(): if arg.type.typeobj_is_available():
code.globalstate.use_utility_code(arg_type_test_utility_code)
typeptr_cname = arg.type.typeptr_cname typeptr_cname = arg.type.typeptr_cname
arg_code = "((PyObject *)%s)" % arg.entry.cname arg_code = "((PyObject *)%s)" % arg.entry.cname
code.putln( code.putln(
...@@ -1419,6 +1420,15 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1419,6 +1420,15 @@ class FuncDefNode(StatNode, BlockNode):
error(arg.pos, "Cannot test type of extern C class " error(arg.pos, "Cannot test type of extern C class "
"without type object name specification") "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): def generate_wrapper_functions(self, code):
pass pass
...@@ -1667,6 +1677,8 @@ class CFuncDefNode(FuncDefNode): ...@@ -1667,6 +1677,8 @@ class CFuncDefNode(FuncDefNode):
for arg in self.type.args: for arg in self.type.args:
if arg.needs_type_test: if arg.needs_type_test:
self.generate_arg_type_test(arg, code) 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): def error_value(self):
if self.return_type.is_pyobject: if self.return_type.is_pyobject:
...@@ -1880,23 +1892,25 @@ class DefNode(FuncDefNode): ...@@ -1880,23 +1892,25 @@ class DefNode(FuncDefNode):
arg.needs_conversion = 0 arg.needs_conversion = 0
arg.needs_type_test = 0 arg.needs_type_test = 0
arg.is_generic = 1 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: if arg.or_none:
arg.accept_none = True arg.accept_none = True
elif arg.not_none: elif arg.not_none:
arg.accept_none = False arg.accept_none = False
else: elif arg.type.is_extension_type or arg.type.is_builtin_type:
# default depends on compiler directive # default depends on compiler directive
arg.accept_none = allow_none_for_extension_args arg.accept_none = allow_none_for_extension_args
else: else:
# probably just a plain 'object'
arg.accept_none = True arg.accept_none = True
else:
arg.accept_none = True # won't be used, but must be there
if arg.not_none: 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: 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): def analyse_signature(self, env):
any_type_tests_needed = 0
if self.entry.is_special: if self.entry.is_special:
self.entry.trivial_signature = len(self.args) == 1 and not (self.star_arg or self.starstar_arg) 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): elif not env.directives['always_allow_keywords'] and not (self.star_arg or self.starstar_arg):
...@@ -1941,7 +1955,6 @@ class DefNode(FuncDefNode): ...@@ -1941,7 +1955,6 @@ class DefNode(FuncDefNode):
if not arg.type.same_as(arg.hdr_type): if not arg.type.same_as(arg.hdr_type):
if arg.hdr_type.is_pyobject and arg.type.is_pyobject: if arg.hdr_type.is_pyobject and arg.type.is_pyobject:
arg.needs_type_test = 1 arg.needs_type_test = 1
any_type_tests_needed = 1
else: else:
arg.needs_conversion = 1 arg.needs_conversion = 1
if arg.needs_conversion: if arg.needs_conversion:
...@@ -1959,9 +1972,6 @@ class DefNode(FuncDefNode): ...@@ -1959,9 +1972,6 @@ class DefNode(FuncDefNode):
if arg.is_generic and \ if arg.is_generic and \
(arg.type.is_extension_type or arg.type.is_builtin_type): (arg.type.is_extension_type or arg.type.is_builtin_type):
arg.needs_type_test = 1 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): def bad_signature(self):
sig = self.entry.signature sig = self.entry.signature
...@@ -2657,6 +2667,8 @@ class DefNode(FuncDefNode): ...@@ -2657,6 +2667,8 @@ class DefNode(FuncDefNode):
for arg in self.args: for arg in self.args:
if arg.needs_type_test: if arg.needs_type_test:
self.generate_arg_type_test(arg, code) 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): def error_value(self):
return self.entry.signature.error_value return self.entry.signature.error_value
......
def eggs(int x not None, y not None): def eggs(int x not None, char* y not None):
pass pass
_ERRORS = u""" _ERRORS = u"""
1:0: Only extension type arguments can have 'not None' 1:0: Only Python 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'
""" """
...@@ -15,7 +15,7 @@ cdef attr(MyExtType x): ...@@ -15,7 +15,7 @@ cdef attr(MyExtType x):
# defaults, without 'not/or None' # defaults, without 'not/or None'
def ext_default(MyExtType x): def ext_default(MyExtType x): # currently behaves like 'or None'
""" """
>>> ext_default(MyExtType()) >>> ext_default(MyExtType())
123 123
...@@ -76,7 +76,7 @@ cdef litem(list L, int item): ...@@ -76,7 +76,7 @@ cdef litem(list L, int item):
# defaults, without 'not/or None' # defaults, without 'not/or None'
def builtin_default(list L): def builtin_default(list L): # currently behaves like 'or None'
""" """
>>> builtin_default([123]) >>> builtin_default([123])
123 123
...@@ -129,23 +129,75 @@ def builtin_not_none(list L not None): ...@@ -129,23 +129,75 @@ def builtin_not_none(list L not None):
return litem(L, 0) return litem(L, 0)
## builtin type 'object' (which includes None!) - currently unclear! ## builtin type 'object' - isinstance(None, object) is True!
## def object_or_none(object o or None): def object_default(object o): # behaves like 'or None'
## """ """
## >>> object_or_none([]) >>> object_default(object())
## list 'object'
## >>> object_or_none(None) >>> object_default([])
## NoneType 'list'
## """ >>> object_default(None)
## return type(o).__name__ 'NoneType'
"""
## def object_not_none(object o not None): return type(o).__name__
## """
## >>> object_not_none([123]) def object_or_none(object o or None):
## 123 """
## >>> object_not_none(None) >>> object_or_none(object())
## Traceback (most recent call last): 'object'
## TypeError: Argument 'o' has incorrect type (expected object, got NoneType) >>> object_or_none([])
## """ 'list'
## return type(o).__name__ >>> 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__
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