import TypeSlots
import Symtab
import Options
+import Naming
from Code import UtilityCode
from StringEncoding import EncodedString, BytesLiteral
method_handler = self._find_handler(
"method_%s_%s" % (type_name, function.attribute), kwargs)
if method_handler is None:
- return node
+ method_handler = self._find_handler(
+ "methodany_%s" % function.attribute, kwargs)
+ if method_handler is None:
+ return node
if self_arg is not None:
arg_list = [self_arg] + list(arg_list)
if kwargs:
)
return node
+ ### special methods
+
+ def _handle_simple_methodany___new__(self, node, args, is_unbound_method):
+ """Replace 'exttype.__new__(exttype)' by a call to exttype->tp_new()
+ """
+ obj = node.function.obj
+ if not is_unbound_method or len(args) != 1:
+ return node
+ type_arg = args[0]
+ if not obj.is_name or not type_arg.is_name:
+ # play safe
+ return node
+ if obj.type != Builtin.type_type or type_arg.type != Builtin.type_type:
+ # not a known type, play safe
+ return node
+ if not type_arg.type_entry or not obj.type_entry:
+ if obj.name != type_arg.name:
+ return node
+ # otherwise, we know it's a type and we know it's the same
+ # type for both - that should do
+ elif type_arg.type_entry != obj.type_entry:
+ # different types - do what CPython does at runtime
+ error(type_arg.pos, "%s.__new__(%s) is not safe, use %s.__new__()" %
+ (obj.type_entry.name, type_arg.type_entry.name,
+ type_arg.type_entry.name))
+ return node
+
+ return_type = None
+ if obj.type_entry:
+ return_type = obj.type_entry.type
+ if return_type is None and type_arg.type_entry:
+ return_type = type_arg.type_entry.type
+ if return_type is None:
+ return_type = PyrexTypes.py_object_type
+
+ # FIXME: we could potentially look up the actual tp_new C method
+ # of the extension type and call that instead of the generic slot
+ func_type = PyrexTypes.CFuncType(
+ return_type, [
+ PyrexTypes.CFuncTypeArg("type", PyrexTypes.py_object_type, None)
+ ])
+
+ return ExprNodes.PythonCapiCallNode(
+ node.pos, "__Pyx_tp_new", func_type,
+ args = args,
+ utility_code = tpnew_utility_code,
+ is_temp = node.is_temp
+ )
+
### methods of builtin types
PyObject_Append_func_type = PyrexTypes.CFuncType(
)
+tpnew_utility_code = UtilityCode(
+proto = """
+static INLINE PyObject* __Pyx_tp_new(PyObject* type_obj) {
+ return (PyObject*) (((PyTypeObject*)(type_obj))->tp_new(
+ (PyTypeObject*)(type_obj), %(TUPLE)s, NULL));
+}
+""" % {'TUPLE' : Naming.empty_tuple}
+)
+
+
class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
"""Calculate the result of constant expressions to store it in
``expr_node.constant_result``, and replace trivial cases by their
--- /dev/null
+
+cdef class MyType:
+ def __init__(self):
+ print "INIT"
+
+cdef class MySubType(MyType):
+ def __init__(self):
+ print "INIT"
+
+cdef class MyOtherType:
+ def __init__(self):
+ print "INIT"
+
+def make_new():
+ m = MyType.__new__(MyType)
+ m = MyOtherType.__new__(MyOtherType)
+ return m
+
+def make_new_error():
+ m = MySubType.__new__(MyType)
+ m = MyOtherType.__new__(MyType)
+ m = MyOtherType.__new__(MySubType)
+ return m
+
+_ERRORS = """
+20:32: MySubType.__new__(MyType) is not safe, use MyType.__new__()
+21:34: MyOtherType.__new__(MyType) is not safe, use MyType.__new__()
+22:37: MyOtherType.__new__(MySubType) is not safe, use MySubType.__new__()
+"""
--- /dev/null
+
+cimport cython
+
+cdef class MyType:
+ def __init__(self):
+ print "INIT"
+
+cdef class MySubType(MyType):
+ def __init__(self):
+ print "INIT"
+
+class MyClass(object):
+ def __init__(self):
+ print "INIT"
+
+class MyTypeSubClass(MyType):
+ def __init__(self):
+ print "INIT"
+
+# only this can be safely optimised:
+
+@cython.test_assert_path_exists('//PythonCapiCallNode')
+@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
+def make_new():
+ """
+ >>> isinstance(make_new(), MyType)
+ True
+ """
+ m = MyType.__new__(MyType)
+ return m
+
+@cython.test_assert_path_exists('//PythonCapiCallNode')
+@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
+def make_new_builtin():
+ """
+ >>> isinstance(make_new_builtin(), tuple)
+ True
+ """
+ m = dict.__new__(dict)
+ m = list.__new__(list)
+ m = tuple.__new__(tuple)
+ return m
+
+# these cannot:
+
+@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
+@cython.test_fail_if_path_exists('//PythonCapiCallNode')
+def make_new_pyclass():
+ """
+ >>> isinstance(make_new_pyclass(), MyTypeSubClass)
+ True
+ """
+ m = MyClass.__new__(MyClass)
+ m = MyTypeSubClass.__new__(MyTypeSubClass)
+ return m
+
+@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
+@cython.test_fail_if_path_exists('//PythonCapiCallNode')
+def make_new_args(type t1=None, type t2=None):
+ """
+ >>> isinstance(make_new_args(), MyType)
+ True
+ >>> isinstance(make_new_args(MyType), MyType)
+ True
+ >>> isinstance(make_new_args(MyType, MyType), MyType)
+ True
+
+ >>> isinstance(make_new_args(MyType, MySubType), MySubType)
+ Traceback (most recent call last):
+ TypeError: tp_new.MyType.__new__(tp_new.MySubType) is not safe, use tp_new.MySubType.__new__()
+ >>> isinstance(make_new_args(MySubType, MyType), MyType)
+ Traceback (most recent call last):
+ TypeError: tp_new.MySubType.__new__(tp_new.MyType): tp_new.MyType is not a subtype of tp_new.MySubType
+ """
+ if t1 is None:
+ t1 = MyType
+ if t2 is None:
+ t2 = MyType
+ m = t1.__new__(t2)
+ return m
+
+@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
+@cython.test_fail_if_path_exists('//PythonCapiCallNode')
+def make_new_none(type t1=None, type t2=None):
+ """
+ >>> isinstance(make_new_none(), MyType)
+ Traceback (most recent call last):
+ TypeError: object.__new__(X): X is not a type object (NoneType)
+ """
+ m = t1.__new__(t2)
+ return m
+
+@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
+@cython.test_fail_if_path_exists('//PythonCapiCallNode')
+def make_new_none_typed(tuple t1=None, tuple t2=None):
+ """
+ >>> isinstance(make_new_none(), MyType)
+ Traceback (most recent call last):
+ TypeError: object.__new__(X): X is not a type object (NoneType)
+ """
+ m = t1.__new__(t2)
+ return m
+
+@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
+@cython.test_fail_if_path_exists('//PythonCapiCallNode')
+def make_new_untyped(t):
+ """
+ >>> make_new_untyped(None)
+ Traceback (most recent call last):
+ TypeError: object.__new__(X): X is not a type object (NoneType)
+ """
+ m = t.__new__(t)
+ return m