Commit c73a9b2d authored by Robert Bradshaw's avatar Robert Bradshaw

Merge remote-tracking branch 'main/master'

Conflicts:
	Cython/Compiler/Options.py
parents 0ec2ff40 4ebc647c
...@@ -8,6 +8,15 @@ Cython Changelog ...@@ -8,6 +8,15 @@ Cython Changelog
Features added Features added
-------------- --------------
* A new class decorator ``@cython.freelist(N)`` creates a static freelist of N
instances for an extension type, thus avoiding the costly allocation step if
possible. This can speed up object instantiation by 20-30% in suitable
scenarios.
* Fast extension type instantiation using the ``Type.__new__(Type)`` idiom has
gained support for passing arguments. It is also a bit faster for types defined
inside of the module.
* The Python2-only dict methods ``.iter*()`` and ``.view*()`` (requires Python 2.7) * The Python2-only dict methods ``.iter*()`` and ``.view*()`` (requires Python 2.7)
are automatically mapped to the equivalent keys/values/items methods in Python 3 are automatically mapped to the equivalent keys/values/items methods in Python 3
for typed dictionaries. for typed dictionaries.
......
...@@ -268,20 +268,26 @@ builtin_types_table = [ ...@@ -268,20 +268,26 @@ builtin_types_table = [
BuiltinAttribute('imag', 'cval.imag', field_type = PyrexTypes.c_double_type), BuiltinAttribute('imag', 'cval.imag', field_type = PyrexTypes.c_double_type),
]), ]),
("bytes", "PyBytes_Type", []), ("bytes", "PyBytes_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
("str", "PyString_Type", []), ]),
("unicode", "PyUnicode_Type", [BuiltinMethod("join", "TO", "T", "PyUnicode_Join"), ("str", "PyString_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
]),
("unicode", "PyUnicode_Type", [BuiltinMethod("__contains__", "TO", "b", "PyUnicode_Contains"),
BuiltinMethod("join", "TO", "T", "PyUnicode_Join"),
]), ]),
("tuple", "PyTuple_Type", []), ("tuple", "PyTuple_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
]),
("list", "PyList_Type", [BuiltinMethod("insert", "TzO", "r", "PyList_Insert"), ("list", "PyList_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
BuiltinMethod("insert", "TzO", "r", "PyList_Insert"),
BuiltinMethod("reverse", "T", "r", "PyList_Reverse"), BuiltinMethod("reverse", "T", "r", "PyList_Reverse"),
BuiltinMethod("append", "TO", "r", "__Pyx_PyList_Append", BuiltinMethod("append", "TO", "r", "__Pyx_PyList_Append",
utility_code=UtilityCode.load("ListAppend", "Optimize.c")), utility_code=UtilityCode.load("ListAppend", "Optimize.c")),
]), ]),
("dict", "PyDict_Type", [BuiltinMethod("items", "T", "O", "__Pyx_PyDict_Items", ("dict", "PyDict_Type", [BuiltinMethod("__contains__", "TO", "b", "PyDict_Contains"),
BuiltinMethod("items", "T", "O", "__Pyx_PyDict_Items",
utility_code=UtilityCode.load("py_dict_items", "Builtins.c")), utility_code=UtilityCode.load("py_dict_items", "Builtins.c")),
BuiltinMethod("keys", "T", "O", "__Pyx_PyDict_Keys", BuiltinMethod("keys", "T", "O", "__Pyx_PyDict_Keys",
utility_code=UtilityCode.load("py_dict_keys", "Builtins.c")), utility_code=UtilityCode.load("py_dict_keys", "Builtins.c")),
...@@ -309,7 +315,8 @@ builtin_types_table = [ ...@@ -309,7 +315,8 @@ builtin_types_table = [
]), ]),
# ("file", "PyFile_Type", []), # not in Py3 # ("file", "PyFile_Type", []), # not in Py3
("set", "PySet_Type", [BuiltinMethod("clear", "T", "r", "PySet_Clear", ("set", "PySet_Type", [BuiltinMethod("__contains__", "TO", "b", "PySequence_Contains"),
BuiltinMethod("clear", "T", "r", "PySet_Clear",
utility_code = py_set_utility_code), utility_code = py_set_utility_code),
# discard() and remove() have a special treatment for unhashable values # discard() and remove() have a special treatment for unhashable values
# BuiltinMethod("discard", "TO", "r", "PySet_Discard", # BuiltinMethod("discard", "TO", "r", "PySet_Discard",
......
This diff is collapsed.
...@@ -1044,30 +1044,52 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1044,30 +1044,52 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
else: else:
unused_marker = 'CYTHON_UNUSED ' unused_marker = 'CYTHON_UNUSED '
need_self_cast = type.vtabslot_cname or have_entries or cpp_class_attrs if base_type:
freelist_size = 0 # not currently supported
else:
freelist_size = scope.directives.get('freelist', 0)
freelist_name = scope.mangle_internal(Naming.freelist_name)
freecount_name = scope.mangle_internal(Naming.freecount_name)
decls = code.globalstate['decls']
decls.putln("static PyObject *%s(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/" %
slot_func)
code.putln("") code.putln("")
if freelist_size:
code.putln("static %s[%d];" % (
scope.parent_type.declaration_code(freelist_name),
freelist_size))
code.putln("static int %s = 0;" % freecount_name)
code.putln("")
code.putln( code.putln(
"static PyObject *%s(PyTypeObject *t, %sPyObject *a, %sPyObject *k) {" "static PyObject *%s(PyTypeObject *t, %sPyObject *a, %sPyObject *k) {"
% (scope.mangle_internal("tp_new"), unused_marker, unused_marker)) % (slot_func, unused_marker, unused_marker))
need_self_cast = type.vtabslot_cname or have_entries or cpp_class_attrs
if need_self_cast: if need_self_cast:
code.putln( code.putln("%s;" % scope.parent_type.declaration_code("p"))
"%s;"
% scope.parent_type.declaration_code("p"))
if base_type: if base_type:
tp_new = TypeSlots.get_base_slot_function(scope, tp_slot) tp_new = TypeSlots.get_base_slot_function(scope, tp_slot)
if tp_new is None: if tp_new is None:
tp_new = "%s->tp_new" % base_type.typeptr_cname tp_new = "%s->tp_new" % base_type.typeptr_cname
code.putln( code.putln("PyObject *o = %s(t, a, k);" % tp_new)
"PyObject *o = %s(t, a, k);" % tp_new)
else: else:
code.putln( code.putln("PyObject *o;")
"PyObject *o = (*t->tp_alloc)(t, 0);") if freelist_size:
code.putln( code.putln("if ((%s > 0) & (t->tp_basicsize == sizeof(%s))) {" % (
"if (!o) return 0;") freecount_name, type.declaration_code("", deref=True)))
code.putln("o = (PyObject*)%s[--%s];" % (
freelist_name, freecount_name))
code.putln("PyObject_INIT(o, t);")
if scope.needs_gc():
code.putln("PyObject_GC_Track(o);")
code.putln("} else {")
code.putln("o = (*t->tp_alloc)(t, 0);")
code.putln("if (!o) return 0;")
if freelist_size and not base_type:
code.putln('}')
if need_self_cast: if need_self_cast:
code.putln( code.putln("p = %s;" % type.cast_code("o"))
"p = %s;"
% type.cast_code("o"))
#if need_self_cast: #if need_self_cast:
# self.generate_self_cast(scope, code) # self.generate_self_cast(scope, code)
if type.vtabslot_cname: if type.vtabslot_cname:
...@@ -1191,8 +1213,20 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1191,8 +1213,20 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.globalstate.use_utility_code( code.globalstate.use_utility_code(
UtilityCode.load_cached("CallNextTpDealloc", "ExtensionTypes.c")) UtilityCode.load_cached("CallNextTpDealloc", "ExtensionTypes.c"))
else: else:
code.putln( freelist_size = scope.directives.get('freelist', 0)
"(*Py_TYPE(o)->tp_free)(o);") if freelist_size:
freelist_name = scope.mangle_internal(Naming.freelist_name)
freecount_name = scope.mangle_internal(Naming.freecount_name)
type = scope.parent_type
code.putln("if ((%s < %d) & (Py_TYPE(o)->tp_basicsize == sizeof(%s))) {" % (
freecount_name, freelist_size, type.declaration_code("", deref=True)))
code.putln("%s[%s++] = %s;" % (
freelist_name, freecount_name, type.cast_code("o")))
code.putln("} else {")
code.putln("(*Py_TYPE(o)->tp_free)(o);")
if freelist_size:
code.putln("}")
code.putln( code.putln(
"}") "}")
...@@ -2049,6 +2083,19 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -2049,6 +2083,19 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
PyrexTypes.py_object_type, PyrexTypes.py_object_type,
clear_before_decref=True, clear_before_decref=True,
nanny=False) nanny=False)
for entry in env.c_class_entries:
cclass_type = entry.type
if cclass_type.is_external or cclass_type.base_type:
continue
if cclass_type.scope.directives.get('freelist', 0):
scope = cclass_type.scope
freelist_name = scope.mangle_internal(Naming.freelist_name)
freecount_name = scope.mangle_internal(Naming.freecount_name)
code.putln("while (%s > 0) {" % freecount_name)
code.putln("PyObject* o = (PyObject*)%s[--%s];" % (
freelist_name, freecount_name))
code.putln("(*Py_TYPE(o)->tp_free)(o);")
code.putln("}")
# for entry in env.pynum_entries: # for entry in env.pynum_entries:
# code.put_decref_clear(entry.cname, # code.put_decref_clear(entry.cname,
# PyrexTypes.py_object_type, # PyrexTypes.py_object_type,
......
...@@ -103,6 +103,8 @@ global_code_object_cache_find = pyrex_prefix + 'find_code_object' ...@@ -103,6 +103,8 @@ global_code_object_cache_find = pyrex_prefix + 'find_code_object'
global_code_object_cache_insert = pyrex_prefix + 'insert_code_object' global_code_object_cache_insert = pyrex_prefix + 'insert_code_object'
genexpr_id_ref = 'genexpr' genexpr_id_ref = 'genexpr'
freelist_name = 'freelist'
freecount_name = 'freecount'
line_c_macro = "__LINE__" line_c_macro = "__LINE__"
......
...@@ -4189,6 +4189,9 @@ class CClassDefNode(ClassDefNode): ...@@ -4189,6 +4189,9 @@ class CClassDefNode(ClassDefNode):
% base_class_entry.type.name) % base_class_entry.type.name)
else: else:
self.base_type = base_class_entry.type self.base_type = base_class_entry.type
if env.directives.get('freelist', 0) > 0:
warning(self.pos, "freelists cannot be used on subtypes, only the base class can manage them", 1)
has_body = self.body is not None has_body = self.body is not None
if self.module_name and self.visibility != 'extern': if self.module_name and self.visibility != 'extern':
module_path = self.module_name.split(".") module_path = self.module_name.split(".")
......
This diff is collapsed.
...@@ -125,7 +125,8 @@ directive_defaults = { ...@@ -125,7 +125,8 @@ directive_defaults = {
# experimental, subject to change # experimental, subject to change
'binding': None, 'binding': None,
'experimental_cpp_class_def': False 'experimental_cpp_class_def': False,
'freelist': 0,
} }
# Extra warning directives # Extra warning directives
...@@ -155,6 +156,7 @@ directive_types = { ...@@ -155,6 +156,7 @@ directive_types = {
'cclass' : None, 'cclass' : None,
'returns' : type, 'returns' : type,
'set_initial_path': str, 'set_initial_path': str,
'freelist': int,
'c_string_type': one_of('bytes', 'str', 'unicoode'), 'c_string_type': one_of('bytes', 'str', 'unicoode'),
} }
...@@ -172,7 +174,8 @@ directive_scopes = { # defaults to available everywhere ...@@ -172,7 +174,8 @@ directive_scopes = { # defaults to available everywhere
'set_initial_path' : ('module',), 'set_initial_path' : ('module',),
'test_assert_path_exists' : ('function', 'class', 'cclass'), 'test_assert_path_exists' : ('function', 'class', 'cclass'),
'test_fail_if_path_exists' : ('function', 'class', 'cclass'), 'test_fail_if_path_exists' : ('function', 'class', 'cclass'),
# Avoid scope-specific to/from_py_functions. 'freelist': ('cclass',),
# Avoid scope-specific to/from_py_functions for c_string.
'c_string_type': ('module',), 'c_string_type': ('module',),
'c_string_encoding': ('module',), 'c_string_encoding': ('module',),
} }
......
...@@ -883,6 +883,11 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations): ...@@ -883,6 +883,11 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
raise PostParseError(pos, raise PostParseError(pos,
'The %s directive takes one compile-time boolean argument' % optname) 'The %s directive takes one compile-time boolean argument' % optname)
return (optname, args[0].value) return (optname, args[0].value)
elif directivetype is int:
if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.IntNode):
raise PostParseError(pos,
'The %s directive takes one compile-time integer argument' % optname)
return (optname, int(args[0].value))
elif directivetype is str: elif directivetype is str:
if kwds is not None or len(args) != 1 or not isinstance(args[0], (ExprNodes.StringNode, if kwds is not None or len(args) != 1 or not isinstance(args[0], (ExprNodes.StringNode,
ExprNodes.UnicodeNode)): ExprNodes.UnicodeNode)):
......
...@@ -693,6 +693,8 @@ def wrap_compile_time_constant(pos, value): ...@@ -693,6 +693,8 @@ def wrap_compile_time_constant(pos, value):
rep = repr(value) rep = repr(value)
if value is None: if value is None:
return ExprNodes.NoneNode(pos) return ExprNodes.NoneNode(pos)
elif value is Ellipsis:
return ExprNodes.EllipsisNode(pos)
elif isinstance(value, bool): elif isinstance(value, bool):
return ExprNodes.BoolNode(pos, value=value) return ExprNodes.BoolNode(pos, value=value)
elif isinstance(value, int): elif isinstance(value, int):
......
...@@ -984,8 +984,8 @@ class BuiltinObjectType(PyObjectType): ...@@ -984,8 +984,8 @@ class BuiltinObjectType(PyObjectType):
def isinstance_code(self, arg): def isinstance_code(self, arg):
return '%s(%s)' % (self.type_check_function(exact=False), arg) return '%s(%s)' % (self.type_check_function(exact=False), arg)
def type_test_code(self, arg, notnone=False): def type_test_code(self, arg, notnone=False, exact=True):
type_check = self.type_check_function(exact=True) type_check = self.type_check_function(exact=exact)
check = 'likely(%s(%s))' % (type_check, arg) check = 'likely(%s(%s))' % (type_check, arg)
if not notnone: if not notnone:
check += '||((%s) == Py_None)' % arg check += '||((%s) == Py_None)' % arg
...@@ -1033,9 +1033,6 @@ class PyExtensionType(PyObjectType): ...@@ -1033,9 +1033,6 @@ class PyExtensionType(PyObjectType):
is_extension_type = 1 is_extension_type = 1
has_attributes = 1 has_attributes = 1
def needs_nonecheck(self):
return True
objtypedef_cname = None objtypedef_cname = None
def __init__(self, name, typedef_flag, base_type, is_external=0): def __init__(self, name, typedef_flag, base_type, is_external=0):
...@@ -1060,6 +1057,9 @@ class PyExtensionType(PyObjectType): ...@@ -1060,6 +1057,9 @@ class PyExtensionType(PyObjectType):
if scope: if scope:
scope.parent_type = self scope.parent_type = self
def needs_nonecheck(self):
return True
def subtype_of_resolved_type(self, other_type): def subtype_of_resolved_type(self, other_type):
if other_type.is_extension_type or other_type.is_builtin_type: if other_type.is_extension_type or other_type.is_builtin_type:
return self is other_type or ( return self is other_type or (
......
...@@ -1310,6 +1310,7 @@ class ModuleScope(Scope): ...@@ -1310,6 +1310,7 @@ class ModuleScope(Scope):
if defining or implementing: if defining or implementing:
scope = CClassScope(name = name, outer_scope = self, scope = CClassScope(name = name, outer_scope = self,
visibility = visibility) visibility = visibility)
scope.directives = self.directives.copy()
if base_type and base_type.scope: if base_type and base_type.scope:
scope.declare_inherited_c_attributes(base_type.scope) scope.declare_inherited_c_attributes(base_type.scope)
type.set_scope(scope) type.set_scope(scope)
...@@ -1891,7 +1892,7 @@ class CClassScope(ClassScope): ...@@ -1891,7 +1892,7 @@ class CClassScope(ClassScope):
def declare_cfunction(self, name, type, pos, def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0, cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None): defining = 0, modifiers = (), utility_code = None):
if get_special_method_signature(name): if get_special_method_signature(name) and not self.parent_type.is_builtin_type:
error(pos, "Special methods must be declared with 'def', not 'cdef'") error(pos, "Special methods must be declared with 'def', not 'cdef'")
args = type.args args = type.args
if not args: if not args:
......
...@@ -493,11 +493,13 @@ def get_special_method_signature(name): ...@@ -493,11 +493,13 @@ def get_special_method_signature(name):
else: else:
return None return None
def get_property_accessor_signature(name): def get_property_accessor_signature(name):
# Return signature of accessor for an extension type # Return signature of accessor for an extension type
# property, else None. # property, else None.
return property_accessor_signatures.get(name) return property_accessor_signatures.get(name)
def get_base_slot_function(scope, slot): def get_base_slot_function(scope, slot):
# Returns the function implementing this slot in the baseclass. # Returns the function implementing this slot in the baseclass.
# This is useful for enabling the compiler to optimize calls # This is useful for enabling the compiler to optimize calls
...@@ -511,6 +513,18 @@ def get_base_slot_function(scope, slot): ...@@ -511,6 +513,18 @@ def get_base_slot_function(scope, slot):
return parent_slot return parent_slot
return None return None
def get_slot_function(scope, slot):
# Returns the function implementing this slot in the baseclass.
# This is useful for enabling the compiler to optimize calls
# that recursively climb the class hierarchy.
slot_code = slot.slot_code(scope)
if slot_code != '0':
entry = scope.parent_scope.lookup_here(scope.parent_type.name)
if entry.visibility != 'extern':
return slot_code
return None
#------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------
# #
# Signatures for generic Python functions and methods. # Signatures for generic Python functions and methods.
......
...@@ -28,8 +28,13 @@ cdef class EnvTransform(CythonTransform): ...@@ -28,8 +28,13 @@ cdef class EnvTransform(CythonTransform):
cdef public list env_stack cdef public list env_stack
cdef class MethodDispatcherTransform(EnvTransform): cdef class MethodDispatcherTransform(EnvTransform):
@cython.final
cdef _visit_binop_node(self, node)
@cython.final
cdef _find_handler(self, match_name, bint has_kwargs) cdef _find_handler(self, match_name, bint has_kwargs)
@cython.final
cdef _dispatch_to_handler(self, node, function, arg_list, kwargs) cdef _dispatch_to_handler(self, node, function, arg_list, kwargs)
@cython.final
cdef _dispatch_to_method_handler(self, attr_name, self_arg, cdef _dispatch_to_method_handler(self, attr_name, self_arg,
is_unbound_method, type_name, is_unbound_method, type_name,
node, arg_list, kwargs) node, arg_list, kwargs)
......
...@@ -492,7 +492,17 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -492,7 +492,17 @@ class MethodDispatcherTransform(EnvTransform):
args = node.args args = node.args
return self._dispatch_to_handler(node, function, args, None) return self._dispatch_to_handler(node, function, args, None)
def visit_PrimaryCmpNode(self, node):
if node.cascade:
# not currently handled below
self.visitchildren(node)
return node
return self._visit_binop_node(node)
def visit_BinopNode(self, node): def visit_BinopNode(self, node):
return self._visit_binop_node(node)
def _visit_binop_node(self, node):
self.visitchildren(node) self.visitchildren(node)
# FIXME: could special case 'not_in' # FIXME: could special case 'not_in'
special_method_name = find_special_method_for_binary_operator(node.operator) special_method_name = find_special_method_for_binary_operator(node.operator)
...@@ -591,7 +601,7 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -591,7 +601,7 @@ class MethodDispatcherTransform(EnvTransform):
if self_arg is not None: if self_arg is not None:
arg_list = [self_arg] + list(arg_list) arg_list = [self_arg] + list(arg_list)
if kwargs: if kwargs:
return method_handler(node, arg_list, kwargs, is_unbound_method) return method_handler(node, arg_list, is_unbound_method, kwargs)
else: else:
return method_handler(node, arg_list, is_unbound_method) return method_handler(node, arg_list, is_unbound_method)
......
...@@ -650,3 +650,11 @@ bad: ...@@ -650,3 +650,11 @@ bad:
__Pyx_PyObject_CallMethodTuple(obj, name, PyTuple_Pack(1, arg1)) __Pyx_PyObject_CallMethodTuple(obj, name, PyTuple_Pack(1, arg1))
#define __Pyx_PyObject_CallMethod0(obj, name) \ #define __Pyx_PyObject_CallMethod0(obj, name) \
__Pyx_PyObject_CallMethodTuple(obj, name, (Py_INCREF($empty_tuple), $empty_tuple)) __Pyx_PyObject_CallMethodTuple(obj, name, (Py_INCREF($empty_tuple), $empty_tuple))
/////////////// tp_new.proto ///////////////
#define __Pyx_tp_new(type_obj, args) __Pyx_tp_new_kwargs(type_obj, args, NULL)
static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* args, PyObject* kwargs) {
return (PyObject*) (((PyTypeObject*)type_obj)->tp_new((PyTypeObject*)type_obj, args, kwargs));
}
...@@ -112,163 +112,6 @@ static PyObject* __Pyx_PyObject_PopIndex(PyObject* L, Py_ssize_t ix) { ...@@ -112,163 +112,6 @@ static PyObject* __Pyx_PyObject_PopIndex(PyObject* L, Py_ssize_t ix) {
} }
/////////////// py_unicode_istitle.proto ///////////////
// Py_UNICODE_ISTITLE() doesn't match unicode.istitle() as the latter
// additionally allows character that comply with Py_UNICODE_ISUPPER()
#if PY_VERSION_HEX < 0x030200A2
static CYTHON_INLINE int __Pyx_Py_UNICODE_ISTITLE(Py_UNICODE uchar)
#else
static CYTHON_INLINE int __Pyx_Py_UNICODE_ISTITLE(Py_UCS4 uchar)
#endif
{
return Py_UNICODE_ISTITLE(uchar) || Py_UNICODE_ISUPPER(uchar);
}
/////////////// unicode_tailmatch.proto ///////////////
// Python's unicode.startswith() and unicode.endswith() support a
// tuple of prefixes/suffixes, whereas it's much more common to
// test for a single unicode string.
static int __Pyx_PyUnicode_Tailmatch(PyObject* s, PyObject* substr,
Py_ssize_t start, Py_ssize_t end, int direction) {
if (unlikely(PyTuple_Check(substr))) {
Py_ssize_t i, count = PyTuple_GET_SIZE(substr);
for (i = 0; i < count; i++) {
int result;
#if CYTHON_COMPILING_IN_CPYTHON
result = PyUnicode_Tailmatch(s, PyTuple_GET_ITEM(substr, i),
start, end, direction);
#else
PyObject* sub = PySequence_GetItem(substr, i);
if (unlikely(!sub)) return -1;
result = PyUnicode_Tailmatch(s, sub, start, end, direction);
Py_DECREF(sub);
#endif
if (result) {
return result;
}
}
return 0;
}
return PyUnicode_Tailmatch(s, substr, start, end, direction);
}
/////////////// bytes_tailmatch.proto ///////////////
static int __Pyx_PyBytes_SingleTailmatch(PyObject* self, PyObject* arg, Py_ssize_t start,
Py_ssize_t end, int direction)
{
const char* self_ptr = PyBytes_AS_STRING(self);
Py_ssize_t self_len = PyBytes_GET_SIZE(self);
const char* sub_ptr;
Py_ssize_t sub_len;
int retval;
#if PY_VERSION_HEX >= 0x02060000
Py_buffer view;
view.obj = NULL;
#endif
if ( PyBytes_Check(arg) ) {
sub_ptr = PyBytes_AS_STRING(arg);
sub_len = PyBytes_GET_SIZE(arg);
}
#if PY_MAJOR_VERSION < 3
// Python 2.x allows mixing unicode and str
else if ( PyUnicode_Check(arg) ) {
return PyUnicode_Tailmatch(self, arg, start, end, direction);
}
#endif
else {
#if PY_VERSION_HEX < 0x02060000
if (unlikely(PyObject_AsCharBuffer(arg, &sub_ptr, &sub_len)))
return -1;
#else
if (unlikely(PyObject_GetBuffer(self, &view, PyBUF_SIMPLE) == -1))
return -1;
sub_ptr = (const char*) view.buf;
sub_len = view.len;
#endif
}
if (end > self_len)
end = self_len;
else if (end < 0)
end += self_len;
if (end < 0)
end = 0;
if (start < 0)
start += self_len;
if (start < 0)
start = 0;
if (direction > 0) {
/* endswith */
if (end-sub_len > start)
start = end - sub_len;
}
if (start + sub_len <= end)
retval = !memcmp(self_ptr+start, sub_ptr, sub_len);
else
retval = 0;
#if PY_VERSION_HEX >= 0x02060000
if (view.obj)
PyBuffer_Release(&view);
#endif
return retval;
}
static int __Pyx_PyBytes_Tailmatch(PyObject* self, PyObject* substr, Py_ssize_t start,
Py_ssize_t end, int direction)
{
if (unlikely(PyTuple_Check(substr))) {
Py_ssize_t i, count = PyTuple_GET_SIZE(substr);
for (i = 0; i < count; i++) {
int result;
#if CYTHON_COMPILING_IN_CPYTHON
result = __Pyx_PyBytes_SingleTailmatch(self, PyTuple_GET_ITEM(substr, i),
start, end, direction);
#else
PyObject* sub = PySequence_GetItem(substr, i);
if (unlikely(!sub)) return -1;
result = __Pyx_PyBytes_SingleTailmatch(self, sub, start, end, direction);
Py_DECREF(sub);
#endif
if (result) {
return result;
}
}
return 0;
}
return __Pyx_PyBytes_SingleTailmatch(self, substr, start, end, direction);
}
/////////////// bytes_index.proto ///////////////
static CYTHON_INLINE char __Pyx_PyBytes_GetItemInt(PyObject* bytes, Py_ssize_t index, int check_bounds) {
if (check_bounds) {
Py_ssize_t size = PyBytes_GET_SIZE(bytes);
if (unlikely(index >= size) | ((index < 0) & unlikely(index < -size))) {
PyErr_Format(PyExc_IndexError, "string index out of range");
return -1;
}
}
if (index < 0)
index += PyBytes_GET_SIZE(bytes);
return PyBytes_AS_STRING(bytes)[index];
}
/////////////// dict_getitem_default.proto /////////////// /////////////// dict_getitem_default.proto ///////////////
static PyObject* __Pyx_PyDict_GetItemDefault(PyObject* d, PyObject* key, PyObject* default_value); /*proto*/ static PyObject* __Pyx_PyDict_GetItemDefault(PyObject* d, PyObject* key, PyObject* default_value); /*proto*/
......
...@@ -109,6 +109,14 @@ static CYTHON_INLINE int __Pyx_PyUnicodeBufferContainsUCS4(Py_UNICODE* buffer, P ...@@ -109,6 +109,14 @@ static CYTHON_INLINE int __Pyx_PyUnicodeBufferContainsUCS4(Py_UNICODE* buffer, P
} }
//////////////////// PyUnicodeContains.proto ////////////////////
static CYTHON_INLINE int __Pyx_PyUnicode_Contains(PyObject* substring, PyObject* text, int eq) {
int result = PyUnicode_Contains(text, substring);
return unlikely(result < 0) ? result : (result == (eq == Py_EQ));
}
//////////////////// StrEquals.proto //////////////////// //////////////////// StrEquals.proto ////////////////////
//@requires: BytesEquals //@requires: BytesEquals
//@requires: UnicodeEquals //@requires: UnicodeEquals
...@@ -410,3 +418,183 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Substring( ...@@ -410,3 +418,183 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_Substring(
return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(text)+start, stop-start); return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(text)+start, stop-start);
#endif #endif
} }
/////////////// py_unicode_istitle.proto ///////////////
// Py_UNICODE_ISTITLE() doesn't match unicode.istitle() as the latter
// additionally allows character that comply with Py_UNICODE_ISUPPER()
#if PY_VERSION_HEX < 0x030200A2
static CYTHON_INLINE int __Pyx_Py_UNICODE_ISTITLE(Py_UNICODE uchar)
#else
static CYTHON_INLINE int __Pyx_Py_UNICODE_ISTITLE(Py_UCS4 uchar)
#endif
{
return Py_UNICODE_ISTITLE(uchar) || Py_UNICODE_ISUPPER(uchar);
}
/////////////// unicode_tailmatch.proto ///////////////
// Python's unicode.startswith() and unicode.endswith() support a
// tuple of prefixes/suffixes, whereas it's much more common to
// test for a single unicode string.
static int __Pyx_PyUnicode_Tailmatch(PyObject* s, PyObject* substr,
Py_ssize_t start, Py_ssize_t end, int direction) {
if (unlikely(PyTuple_Check(substr))) {
Py_ssize_t i, count = PyTuple_GET_SIZE(substr);
for (i = 0; i < count; i++) {
int result;
#if CYTHON_COMPILING_IN_CPYTHON
result = PyUnicode_Tailmatch(s, PyTuple_GET_ITEM(substr, i),
start, end, direction);
#else
PyObject* sub = PySequence_GetItem(substr, i);
if (unlikely(!sub)) return -1;
result = PyUnicode_Tailmatch(s, sub, start, end, direction);
Py_DECREF(sub);
#endif
if (result) {
return result;
}
}
return 0;
}
return PyUnicode_Tailmatch(s, substr, start, end, direction);
}
/////////////// bytes_tailmatch.proto ///////////////
static int __Pyx_PyBytes_SingleTailmatch(PyObject* self, PyObject* arg, Py_ssize_t start,
Py_ssize_t end, int direction)
{
const char* self_ptr = PyBytes_AS_STRING(self);
Py_ssize_t self_len = PyBytes_GET_SIZE(self);
const char* sub_ptr;
Py_ssize_t sub_len;
int retval;
#if PY_VERSION_HEX >= 0x02060000
Py_buffer view;
view.obj = NULL;
#endif
if ( PyBytes_Check(arg) ) {
sub_ptr = PyBytes_AS_STRING(arg);
sub_len = PyBytes_GET_SIZE(arg);
}
#if PY_MAJOR_VERSION < 3
// Python 2.x allows mixing unicode and str
else if ( PyUnicode_Check(arg) ) {
return PyUnicode_Tailmatch(self, arg, start, end, direction);
}
#endif
else {
#if PY_VERSION_HEX < 0x02060000
if (unlikely(PyObject_AsCharBuffer(arg, &sub_ptr, &sub_len)))
return -1;
#else
if (unlikely(PyObject_GetBuffer(self, &view, PyBUF_SIMPLE) == -1))
return -1;
sub_ptr = (const char*) view.buf;
sub_len = view.len;
#endif
}
if (end > self_len)
end = self_len;
else if (end < 0)
end += self_len;
if (end < 0)
end = 0;
if (start < 0)
start += self_len;
if (start < 0)
start = 0;
if (direction > 0) {
/* endswith */
if (end-sub_len > start)
start = end - sub_len;
}
if (start + sub_len <= end)
retval = !memcmp(self_ptr+start, sub_ptr, sub_len);
else
retval = 0;
#if PY_VERSION_HEX >= 0x02060000
if (view.obj)
PyBuffer_Release(&view);
#endif
return retval;
}
static int __Pyx_PyBytes_Tailmatch(PyObject* self, PyObject* substr, Py_ssize_t start,
Py_ssize_t end, int direction)
{
if (unlikely(PyTuple_Check(substr))) {
Py_ssize_t i, count = PyTuple_GET_SIZE(substr);
for (i = 0; i < count; i++) {
int result;
#if CYTHON_COMPILING_IN_CPYTHON
result = __Pyx_PyBytes_SingleTailmatch(self, PyTuple_GET_ITEM(substr, i),
start, end, direction);
#else
PyObject* sub = PySequence_GetItem(substr, i);
if (unlikely(!sub)) return -1;
result = __Pyx_PyBytes_SingleTailmatch(self, sub, start, end, direction);
Py_DECREF(sub);
#endif
if (result) {
return result;
}
}
return 0;
}
return __Pyx_PyBytes_SingleTailmatch(self, substr, start, end, direction);
}
/////////////// str_tailmatch.proto ///////////////
static CYTHON_INLINE int __Pyx_PyStr_Tailmatch(PyObject* self, PyObject* arg, Py_ssize_t start,
Py_ssize_t end, int direction);
/////////////// str_tailmatch ///////////////
//@requires: bytes_tailmatch
//@requires: unicode_tailmatch
static CYTHON_INLINE int __Pyx_PyStr_Tailmatch(PyObject* self, PyObject* arg, Py_ssize_t start,
Py_ssize_t end, int direction)
{
// We do not use a C compiler macro here to avoid "unused function"
// warnings for the *_Tailmatch() function that is not being used in
// the specific CPython version. The C compiler will generate the same
// code anyway, and will usually just remove the unused function.
if (PY_MAJOR_VERSION < 3)
return __Pyx_PyBytes_Tailmatch(self, arg, start, end, direction);
else
return __Pyx_PyUnicode_Tailmatch(self, arg, start, end, direction);
}
/////////////// bytes_index.proto ///////////////
static CYTHON_INLINE char __Pyx_PyBytes_GetItemInt(PyObject* bytes, Py_ssize_t index, int check_bounds) {
if (check_bounds) {
Py_ssize_t size = PyBytes_GET_SIZE(bytes);
if (unlikely(index >= size) | ((index < 0) & unlikely(index < -size))) {
PyErr_Format(PyExc_IndexError, "string index out of range");
return -1;
}
}
if (index < 0)
index += PyBytes_GET_SIZE(bytes);
return PyBytes_AS_STRING(bytes)[index];
}
...@@ -339,6 +339,7 @@ subtyped at the C level by foreign code. ...@@ -339,6 +339,7 @@ subtyped at the C level by foreign code.
C methods C methods
========= =========
Extension types can have C methods as well as Python methods. Like C Extension types can have C methods as well as Python methods. Like C
functions, C methods are declared using :keyword:`cdef` or :keyword:`cpdef` instead of functions, C methods are declared using :keyword:`cdef` or :keyword:`cpdef` instead of
:keyword:`def`. C methods are "virtual", and may be overridden in derived :keyword:`def`. C methods are "virtual", and may be overridden in derived
...@@ -379,6 +380,7 @@ method using the usual Python technique, i.e.:: ...@@ -379,6 +380,7 @@ method using the usual Python technique, i.e.::
Parrot.describe(self) Parrot.describe(self)
Forward-declaring extension types Forward-declaring extension types
=================================== ===================================
...@@ -405,6 +407,52 @@ definition, for example,:: ...@@ -405,6 +407,52 @@ definition, for example,::
cdef class A(B): cdef class A(B):
# attributes and methods # attributes and methods
Fast instantiation
===================
Cython provides two ways to speed up the instantiation of extension types.
The first one is a direct call to the ``__new__()`` special static method,
as known from Python. For an extension type ``Penguin``, you could use
the following code::
cdef class Penguin:
cdef object food
def __cinit__(self, food):
self.food = food
def __init__(self, food):
print("eating!")
normal_penguin = Penguin('fish')
fast_penguin = Penguin.__new__(Penguin, 'wheat') # note: not calling __init__() !
Note that the path through ``__new__()`` will *not* call the type's
``__init__()`` method (again, as known from Python). Thus, in the example
above, the first instantiation will print ``eating!``, but the second will
not. This is only one of the reasons why the ``__cinit__()`` method is
safer and preferable over the normal ``__init__()`` method for extension
types.
The second performance improvement applies to types that are often created
and deleted in a row, so that they can benefit from a freelist. Cython
provides the decorator ``@cython.freelist(N)`` for this, which creates a
statically sized freelist of ``N`` instances for a given type. Example::
cimport cython
@cython.freelist(8)
cdef class Penguin:
cdef object food
def __cinit__(self, food):
self.food = food
penguin = Penguin('fish 1')
penguin = None
penguin = Penguin('fish 2') # does not need to allocate memory!
Making extension types weak-referenceable Making extension types weak-referenceable
========================================== ==========================================
...@@ -418,6 +466,7 @@ object called :attr:`__weakref__`. For example,:: ...@@ -418,6 +466,7 @@ object called :attr:`__weakref__`. For example,::
cdef object __weakref__ cdef object __weakref__
Public and external extension types Public and external extension types
==================================== ====================================
......
...@@ -746,7 +746,8 @@ class CythonRunTestCase(CythonCompileTestCase): ...@@ -746,7 +746,8 @@ class CythonRunTestCase(CythonCompileTestCase):
self.success = False self.success = False
self.runCompileTest() self.runCompileTest()
failures, errors = len(result.failures), len(result.errors) failures, errors = len(result.failures), len(result.errors)
self.run_tests(result) if not self.cython_only:
self.run_tests(result)
if failures == len(result.failures) and errors == len(result.errors): if failures == len(result.failures) and errors == len(result.errors):
# No new errors... # No new errors...
self.success = True self.success = True
...@@ -761,8 +762,7 @@ class CythonRunTestCase(CythonCompileTestCase): ...@@ -761,8 +762,7 @@ class CythonRunTestCase(CythonCompileTestCase):
pass pass
def run_tests(self, result): def run_tests(self, result):
if not self.cython_only: self.run_doctests(self.module, result)
self.run_doctests(self.module, result)
def run_doctests(self, module_name, result): def run_doctests(self, module_name, result):
def run_test(result): def run_test(result):
......
# mode: error
# tag: freelist, werror
cimport cython
@cython.freelist(8)
cdef class ExtType:
pass
@cython.freelist(8)
cdef class ExtTypeObject(object):
pass
cdef class ExtSubTypeOk(ExtType):
pass
@cython.freelist(8)
cdef class ExtSubTypeFail(ExtType):
pass
_ERRORS = """
18:5: freelists cannot be used on subtypes, only the base class can manage them
"""
# coding=utf8
# mode: run
# tag: constant_folding
import cython
@cython.test_fail_if_path_exists(
"//UnaryMinusNode",
"//UnaryPlusNode",
)
def unop_floats():
"""
>>> unop_floats()
(False, 2.0, -2.0, False, 2.0, -2.0, -2.0)
"""
not1 = not 2.0
plus1 = + 2.0
minus1 = - 2.0
not3 = not not not 2.0
plus3 = +++ 2.0
minus3 = --- 2.0
mix = +-++-- 2.0
return not1, plus1, minus1, not3, plus3, minus3, mix
@cython.test_fail_if_path_exists(
"//UnaryMinusNode",
"//UnaryPlusNode",
)
def unop_ints():
"""
>>> unop_ints()
(False, 2, -2, False, 2, -2, -2)
"""
not1 = not 2
plus1 = + 2
minus1 = - 2
not3 = not not not 2
plus3 = +++ 2
minus3 = --- 2
mix = +-++-- 2
return not1, plus1, minus1, not3, plus3, minus3, mix
@cython.test_fail_if_path_exists(
"//UnaryMinusNode",
"//UnaryPlusNode",
"//NotNode",
)
def unop_bool():
"""
>>> unop_bool()
(False, 1, -1, False, 1, -1, -1)
"""
not1 = not True
plus1 = + True
minus1 = - True
not3 = not not not True
plus3 = +++ True
minus3 = --- True
mix = +-++-- True
return not1, plus1, minus1, not3, plus3, minus3, mix
@cython.test_fail_if_path_exists(
"//AddNode",
"//SubNode",
)
def binop_bool():
"""
>>> binop_bool()
(2, 1, 0, True, True, 1, False, 2, 2, -2, False, True, 1, False)
"""
plus1 = True + True
pmix1 = True + 0
minus1 = True - True
and1 = True & True
or1 = True | True
ormix1 = True | 0
xor1 = True ^ True
plus3 = False + True + False + True
pmix3 = False + True + 0 + True
minus3 = False - True - False - True
and3 = False & True & False & True
or3 = False | True | False | True
ormix3 = False | 0 | False | True
xor3 = False ^ True ^ False ^ True
return plus1, pmix1, minus1, and1, or1, ormix1, xor1, plus3, pmix3, minus3, and3, or3, ormix3, xor3
@cython.test_fail_if_path_exists(
"//SliceIndexNode",
)
def slicing2():
"""
>>> slicing2()
([1, 2, 3, 4], [3, 4], [1, 2, 3, 4], [3, 4], (1, 2, 3, 4), (3, 4), (1, 2, 3, 4), (3, 4))
"""
lst0 = [1, 2, 3, 4][:]
lst1 = [1, 2, 3, 4][2:]
lst2 = [1, 2, 3, 4][:4]
lst3 = [1, 2, 3, 4][2:4]
tpl0 = (1, 2, 3, 4)[:]
tpl1 = (1, 2, 3, 4)[2:]
tpl2 = (1, 2, 3, 4)[:4]
tpl3 = (1, 2, 3, 4)[2:4]
return lst0, lst1, lst2, lst3, tpl0, tpl1, tpl2, tpl3
@cython.test_fail_if_path_exists(
"//SliceIndexNode",
)
def str_slicing2():
"""
>>> a,b,c,d = str_slicing2()
>>> a == 'abc\\xE9def'[:]
True
>>> b == 'abc\\xE9def'[2:]
True
>>> c == 'abc\\xE9def'[:4]
True
>>> d == 'abc\\xE9def'[2:4]
True
"""
str0 = 'abc\xE9def'[:]
str1 = 'abc\xE9def'[2:]
str2 = 'abc\xE9def'[:4]
str3 = 'abc\xE9def'[2:4]
return str0, str1, str2, str3
# coding=utf8
# mode: run
# tag: constant_folding
cimport cython
bstring = b'abc\xE9def'
ustring = u'abc\xE9def'
surrogates_ustring = u'abc\U00010000def'
@cython.test_fail_if_path_exists(
"//SliceIndexNode",
)
def bytes_slicing2():
"""
>>> a,b,c,d = bytes_slicing2()
>>> a == bstring[:]
True
>>> b == bstring[2:]
True
>>> c == bstring[:4]
True
>>> d == bstring[2:4]
True
"""
str0 = b'abc\xE9def'[:]
str1 = b'abc\xE9def'[2:]
str2 = b'abc\xE9def'[:4]
str3 = b'abc\xE9def'[2:4]
return str0, str1, str2, str3
@cython.test_fail_if_path_exists(
"//SliceIndexNode",
)
def unicode_slicing2():
"""
>>> a,b,c,d = unicode_slicing2()
>>> a == ustring[:]
True
>>> b == ustring[2:]
True
>>> c == ustring[:4]
True
>>> d == ustring[2:4]
True
"""
str0 = u'abc\xE9def'[:]
str1 = u'abc\xE9def'[2:]
str2 = u'abc\xE9def'[:4]
str3 = u'abc\xE9def'[2:4]
return str0, str1, str2, str3
@cython.test_assert_path_exists(
"//SliceIndexNode",
)
def unicode_slicing_unsafe_surrogates2():
"""
>>> unicode_slicing_unsafe_surrogates2() == surrogates_ustring[2:]
True
"""
ustring = u'abc\U00010000def'[2:]
return ustring
@cython.test_fail_if_path_exists(
"//SliceIndexNode",
)
def unicode_slicing_safe_surrogates2():
"""
>>> unicode_slicing_safe_surrogates2() == surrogates_ustring[:2]
True
>>> print(unicode_slicing_safe_surrogates2())
ab
"""
ustring = u'abc\U00010000def'[:2]
return ustring
...@@ -29,8 +29,10 @@ DEF TRUE = TRUE_FALSE[0] ...@@ -29,8 +29,10 @@ DEF TRUE = TRUE_FALSE[0]
DEF FALSE = TRUE_FALSE[1] DEF FALSE = TRUE_FALSE[1]
DEF INT_TUPLE1 = TUPLE[:2] DEF INT_TUPLE1 = TUPLE[:2]
DEF INT_TUPLE2 = TUPLE[1:4:2] DEF INT_TUPLE2 = TUPLE[1:4:2]
DEF ELLIPSIS = ...
DEF EXPRESSION = int(float(2*2)) + int(str(2)) + int(max(1,2,3)) + sum([TWO, FIVE]) DEF EXPRESSION = int(float(2*2)) + int(str(2)) + int(max(1,2,3)) + sum([TWO, FIVE])
def c(): def c():
""" """
>>> c() >>> c()
...@@ -148,6 +150,13 @@ def false(): ...@@ -148,6 +150,13 @@ def false():
cdef bint false = FALSE cdef bint false = FALSE
return false return false
def ellipsis():
"""
>>> ellipsis()
Ellipsis
"""
return ELLIPSIS
@cython.test_assert_path_exists('//IntNode') @cython.test_assert_path_exists('//IntNode')
@cython.test_fail_if_path_exists('//AddNode') @cython.test_fail_if_path_exists('//AddNode')
def expression(): def expression():
......
# mode: run
# tag: freelist
cimport cython
@cython.freelist(8)
cdef class ExtTypeNoGC:
"""
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
"""
@cython.freelist(8)
cdef class ExtTypeWithGC:
"""
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
>>> obj = ExtTypeWithGC()
"""
cdef attribute
def __init__(self):
self.attribute = object()
def tpnew_ExtTypeWithGC():
"""
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
>>> obj = tpnew_ExtTypeWithGC()
"""
return ExtTypeWithGC.__new__(ExtTypeWithGC)
cdef class ExtSubType(ExtTypeWithGC):
"""
>>> obj = ExtSubType()
>>> obj = ExtSubType()
>>> obj = ExtSubType()
>>> obj = ExtSubType()
>>> obj = ExtSubType()
>>> obj = ExtSubType()
"""
cdef class LargerExtSubType(ExtSubType):
"""
>>> obj = LargerExtSubType()
>>> obj = LargerExtSubType()
>>> obj = LargerExtSubType()
>>> obj = LargerExtSubType()
>>> obj = LargerExtSubType()
>>> obj = LargerExtSubType()
"""
cdef attribute2
def __cinit__(self):
self.attribute2 = object()
@cython.freelist(8)
cdef class ExtTypeWithRefCycle:
"""
>>> obj = first = ExtTypeWithRefCycle()
>>> obj.attribute is None
True
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj.attribute is first
True
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj = ExtTypeWithRefCycle(obj)
>>> obj.attribute is not None
True
>>> first.attribute = obj
>>> del obj, first
"""
cdef public attribute
def __init__(self, obj=None):
self.attribute = obj
...@@ -2,28 +2,32 @@ ...@@ -2,28 +2,32 @@
cimport cython cimport cython
cdef class MyType: cdef class MyType:
def __cinit__(self): cdef public args, kwargs
def __cinit__(self, *args, **kwargs):
self.args, self.kwargs = args, kwargs
print "CINIT" print "CINIT"
def __init__(self): def __init__(self, *args, **kwargs):
print "INIT" print "INIT"
cdef class MySubType(MyType): cdef class MySubType(MyType):
def __cinit__(self): def __cinit__(self, *args, **kwargs):
self.args, self.kwargs = args, kwargs
print "CINIT(SUB)" print "CINIT(SUB)"
def __init__(self): def __init__(self, *args, **kwargs):
print "INIT" print "INIT"
class MyClass(object): class MyClass(object):
def __cinit__(self): def __cinit__(self, *args, **kwargs):
self.args, self.kwargs = args, kwargs
print "CINIT" print "CINIT"
def __init__(self): def __init__(self, *args, **kwargs):
print "INIT" print "INIT"
class MyTypeSubClass(MyType): class MyTypeSubClass(MyType):
def __cinit__(self): def __cinit__(self, *args, **kwargs):
# not called: Python class! # not called: Python class!
print "CINIT(PYSUB)" print "CINIT(PYSUB)"
def __init__(self): def __init__(self, *args, **kwargs):
print "INIT" print "INIT"
# only these can be safely optimised: # only these can be safely optimised:
...@@ -51,6 +55,36 @@ def make_new_typed_target(): ...@@ -51,6 +55,36 @@ def make_new_typed_target():
m = MyType.__new__(MyType) m = MyType.__new__(MyType)
return m return m
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new_with_args():
"""
>>> isinstance(make_new_with_args(), MyType)
CINIT
(1, 2, 3)
{}
True
"""
m = MyType.__new__(MyType, 1, 2 ,3)
print m.args
print m.kwargs
return m
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new_with_args_kwargs():
"""
>>> isinstance(make_new_with_args_kwargs(), MyType)
CINIT
(1, 2, 3)
{'a': 4}
True
"""
m = MyType.__new__(MyType, 1, 2 ,3, a=4)
print m.args
print m.kwargs
return m
@cython.test_assert_path_exists('//PythonCapiCallNode') @cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode') @cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new_builtin(): def make_new_builtin():
......
PYTHON setup.py build_ext --inplace
PYTHON -c "import tp_new_tests; tp_new_tests.test_all()"
PYTHON -c "import tp_new_tests; tp_new_tests.test_sub()"
######## setup.py ########
from Cython.Build.Dependencies import cythonize
from distutils.core import setup
setup(
ext_modules = cythonize("**/*.pyx"),
)
######## tp_new_tests.py ########
def test_all():
test_a()
test_b()
test_a_in_b()
test_sub()
def test_a():
import a
assert isinstance(a.tpnew_ExtTypeA(), a.ExtTypeA)
assert a.tpnew_ExtTypeA().attrA == 123
def test_b():
import b
assert isinstance(b.tpnew_ExtTypeB(), b.ExtTypeB)
assert b.tpnew_ExtTypeB().attrB == 234
def test_a_in_b():
import a,b
assert isinstance(b.tpnew_ExtTypeA(), a.ExtTypeA)
assert b.tpnew_ExtTypeA().attrA == 123
def test_sub():
import b
assert isinstance(b.tpnew_SubExtTypeA(), b.SubExtTypeA)
assert b.tpnew_SubExtTypeA().attrAB == 345
assert b.tpnew_SubExtTypeA().attrA == 123
######## a.pxd ########
cdef class ExtTypeA:
cdef readonly attrA
######## a.pyx ########
cdef class ExtTypeA:
def __cinit__(self):
self.attrA = 123
def tpnew_ExtTypeA():
return ExtTypeA.__new__(ExtTypeA)
######## b.pxd ########
from a cimport ExtTypeA
cdef class ExtTypeB:
cdef readonly attrB
cdef class SubExtTypeA(ExtTypeA):
cdef readonly attrAB
######## b.pyx ########
from a cimport ExtTypeA
cdef class ExtTypeB:
def __cinit__(self):
self.attrB = 234
cdef class SubExtTypeA(ExtTypeA):
def __cinit__(self):
self.attrAB = 345
def tpnew_ExtTypeA():
return ExtTypeA.__new__(ExtTypeA)
def tpnew_ExtTypeB():
return ExtTypeB.__new__(ExtTypeB)
def tpnew_SubExtTypeA():
return SubExtTypeA.__new__(SubExtTypeA)
# mode: run
# tag: special_method
cimport cython
text = u'ab jd sdflk as sa sadas asdas fsdf '
@cython.test_fail_if_path_exists(
"//CoerceFromPyTypeNode")
@cython.test_assert_path_exists(
"//CoerceToPyTypeNode",
"//AttributeNode",
"//AttributeNode[@entry.cname = 'PyUnicode_Contains']")
def unicode_contains(unicode s, substring):
"""
>>> unicode_contains(text, 'fl')
True
>>> unicode_contains(text, 'XYZ')
False
>>> unicode_contains(None, 'XYZ')
Traceback (most recent call last):
AttributeError: 'NoneType' object has no attribute '__contains__'
"""
return s.__contains__(substring)
@cython.test_fail_if_path_exists(
"//CoerceFromPyTypeNode")
@cython.test_assert_path_exists(
# "//CoerceToPyTypeNode",
"//NameNode[@entry.cname = 'PyUnicode_Contains']")
def unicode_contains_unbound(unicode s, substring):
"""
>>> unicode_contains_unbound(text, 'fl')
True
>>> unicode_contains_unbound(text, 'XYZ')
False
>>> unicode_contains_unbound(None, 'XYZ') # doctest: +ELLIPSIS
Traceback (most recent call last):
TypeError: descriptor '__contains__' requires a '...' object but received a 'NoneType'
"""
return unicode.__contains__(s, substring)
cdef class UnicodeSubclass(unicode):
"""
>>> u = UnicodeSubclass(text)
>>> 'fl' in u
False
>>> 'XYZ' in u
True
>>> u.method('fl')
False
>>> u.method('XYZ')
True
>>> u.operator('fl')
False
>>> u.operator('XYZ')
True
"""
def __contains__(self, substring):
return substring not in (self + u'x')
def method(self, other):
return self.__contains__(other)
def operator(self, other):
return other in self
# coding: utf-8 # coding: utf-8
__doc__ = u""" __doc__ = u"""
>>> do_slice1(u'abcdef', 2, 3) >>> slice_start_end(u'abcdef', 2, 3)
c c
>>> do_slice2(u'abcdef', 2, 3) >>> slice_start(u'abcdef', 2, 3)
cdef cdef
>>> do_slice3(u'abcdef', 2, 3) >>> slice_end(u'abcdef', 2, 3)
ab ab
>>> do_slice4(u'abcdef', 2, 3) >>> slice_all(u'abcdef', 2, 3)
abcdef abcdef
>>> do_slice5(u'abcdef', 2, 3) >>> slice_start_none(u'abcdef', 2, 3)
cdef cdef
>>> do_slice6(u'abcdef', 2, 3) >>> slice_none_end(u'abcdef', 2, 3)
ab ab
>>> do_slice7(u'abcdef', 2, 3) >>> slice_none_none(u'abcdef', 2, 3)
abcdef abcdef
>>> do_slice1(u'abcdef', 2, 10)
>>> slice_start_end(u'abcdef', 2, 10)
cdef cdef
>>> do_slice2(u'abcdef', 2, 10) >>> slice_start(u'abcdef', 2, 10)
cdef cdef
>>> do_slice3(u'abcdef', 2, 10) >>> slice_end(u'abcdef', 2, 10)
ab ab
>>> do_slice4(u'abcdef', 2, 10) >>> slice_all(u'abcdef', 2, 10)
abcdef abcdef
>>> do_slice1(u'abcdef', 0, 5)
>>> slice_start_end(u'abcdef', 0, 5)
abcde abcde
>>> do_slice2(u'abcdef', 0, 5) >>> slice_start(u'abcdef', 0, 5)
abcdef abcdef
>>> do_slice3(u'abcdef', 0, 5) >>> slice_end(u'abcdef', 0, 5)
<BLANKLINE> <BLANKLINE>
>>> do_slice4(u'abcdef', 0, 5) >>> slice_all(u'abcdef', 0, 5)
abcdef abcdef
>>> do_slice5(u'abcdef', 0, 5) >>> slice_start_none(u'abcdef', 0, 5)
abcdef abcdef
>>> do_slice6(u'abcdef', 0, 5) >>> slice_none_end(u'abcdef', 0, 5)
<BLANKLINE> <BLANKLINE>
>>> do_slice7(u'abcdef', 0, 5) >>> slice_none_none(u'abcdef', 0, 5)
abcdef abcdef
>>> do_slice1(u'abcdef', -6, -1)
>>> slice_start_end(u'abcdef', -6, -1)
abcde abcde
>>> do_slice2(u'abcdef', -6, -1) >>> slice_start(u'abcdef', -6, -1)
abcdef
>>> slice_end(u'abcdef', -6, -1)
<BLANKLINE>
>>> slice_all(u'abcdef', -6, -1)
abcdef
>>> slice_start_none(u'abcdef', -6, -1)
abcdef abcdef
>>> do_slice3(u'abcdef', -6, -1) >>> slice_none_end(u'abcdef', -6, -1)
<BLANKLINE> <BLANKLINE>
>>> do_slice4(u'abcdef', -6, -1) >>> slice_none_none(u'abcdef', -6, -1)
abcdef abcdef
>>> do_slice5(u'abcdef', -6, -1)
>>> slice_start_end(u'abcdef', -6, -7)
<BLANKLINE>
>>> slice_start(u'abcdef', -6, -7)
abcdef
>>> slice_end(u'abcdef', -6, -7)
<BLANKLINE>
>>> slice_all(u'abcdef', -6, -7)
abcdef
>>> slice_start_none(u'abcdef', -6, -7)
abcdef abcdef
>>> do_slice6(u'abcdef', -6, -1) >>> slice_none_end(u'abcdef', -6, -7)
<BLANKLINE> <BLANKLINE>
>>> do_slice7(u'abcdef', -6, -1) >>> slice_none_none(u'abcdef', -6, -7)
abcdef abcdef
>>> do_slice1(u'aАbБcСdДeЕfФ', 2, 8)
>>> slice_start_end(u'abcdef', -7, -7)
<BLANKLINE>
>>> slice_start(u'abcdef', -7, -7)
abcdef
>>> slice_end(u'abcdef', -7, -7)
<BLANKLINE>
>>> slice_all(u'abcdef', -7, -7)
abcdef
>>> slice_start_none(u'abcdef', -7, -7)
abcdef
>>> slice_none_end(u'abcdef', -7, -7)
<BLANKLINE>
>>> slice_none_none(u'abcdef', -7, -7)
abcdef
>>> slice_start_end(u'aАbБcСdДeЕfФ', 2, 8)
bБcСdД bБcСdД
>>> do_slice2(u'aАbБcСdДeЕfФ', 2, 8) >>> slice_start(u'aАbБcСdДeЕfФ', 2, 8)
bБcСdДeЕfФ bБcСdДeЕfФ
>>> do_slice3(u'aАbБcСdДeЕfФ', 2, 8) >>> slice_end(u'aАbБcСdДeЕfФ', 2, 8)
>>> do_slice4(u'aАbБcСdДeЕfФ', 2, 8) >>> slice_all(u'aАbБcСdДeЕfФ', 2, 8)
aАbБcСdДeЕfФ aАbБcСdДeЕfФ
>>> do_slice5(u'aАbБcСdДeЕfФ', 2, 8) >>> slice_start_none(u'aАbБcСdДeЕfФ', 2, 8)
bБcСdДeЕfФ bБcСdДeЕfФ
>>> do_slice6(u'aАbБcСdДeЕfФ', 2, 8) >>> slice_none_end(u'aАbБcСdДeЕfФ', 2, 8)
>>> do_slice7(u'aАbБcСdДeЕfФ', 2, 8) >>> slice_none_none(u'aАbБcСdДeЕfФ', 2, 8)
aАbБcСdДeЕfФ aАbБcСdДeЕfФ
>>> do_slice1(u'АБСДЕФ', 2, 4)
>>> slice_start_end(u'АБСДЕФ', 2, 4)
СД СД
>>> do_slice2(u'АБСДЕФ', 2, 4) >>> slice_start(u'АБСДЕФ', 2, 4)
СДЕФ СДЕФ
>>> do_slice3(u'АБСДЕФ', 2, 4) >>> slice_end(u'АБСДЕФ', 2, 4)
АБ АБ
>>> do_slice4(u'АБСДЕФ', 2, 4) >>> slice_all(u'АБСДЕФ', 2, 4)
АБСДЕФ АБСДЕФ
>>> do_slice5(u'АБСДЕФ', 2, 4) >>> slice_start_none(u'АБСДЕФ', 2, 4)
СДЕФ СДЕФ
>>> do_slice6(u'АБСДЕФ', 2, 4) >>> slice_none_end(u'АБСДЕФ', 2, 4)
АБ АБ
>>> do_slice7(u'АБСДЕФ', 2, 4) >>> slice_none_none(u'АБСДЕФ', 2, 4)
АБСДЕФ АБСДЕФ
>>> do_slice1(u'АБСДЕФ', -4, -2)
>>> slice_start_end(u'АБСДЕФ', -4, -2)
СД СД
>>> do_slice2(u'АБСДЕФ', -4, -2) >>> slice_start(u'АБСДЕФ', -4, -2)
СДЕФ СДЕФ
>>> do_slice3(u'АБСДЕФ', -4, -2) >>> slice_end(u'АБСДЕФ', -4, -2)
АБ АБ
>>> do_slice4(u'АБСДЕФ', -4, -2) >>> slice_all(u'АБСДЕФ', -4, -2)
АБСДЕФ АБСДЕФ
>>> do_slice5(u'АБСДЕФ', -4, -2) >>> slice_start_none(u'АБСДЕФ', -4, -2)
СДЕФ СДЕФ
>>> do_slice6(u'АБСДЕФ', -4, -2) >>> slice_none_end(u'АБСДЕФ', -4, -2)
АБ АБ
>>> do_slice7(u'АБСДЕФ', -4, -2) >>> slice_none_none(u'АБСДЕФ', -4, -2)
АБСДЕФ АБСДЕФ
>>> do_slice1(None, 2, 4)
>>> slice_start_end(None, 2, 4)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'NoneType' object is not subscriptable TypeError: 'NoneType' object is not subscriptable
>>> do_slice2(None, 2, 4) >>> slice_start(None, 2, 4)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'NoneType' object is not subscriptable TypeError: 'NoneType' object is not subscriptable
>>> do_slice3(None, 2, 4) >>> slice_end(None, 2, 4)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'NoneType' object is not subscriptable TypeError: 'NoneType' object is not subscriptable
>>> do_slice4(None, 2, 4) >>> slice_all(None, 2, 4)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'NoneType' object is not subscriptable TypeError: 'NoneType' object is not subscriptable
>>> do_slice5(None, 2, 4) >>> slice_start_none(None, 2, 4)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'NoneType' object is not subscriptable TypeError: 'NoneType' object is not subscriptable
>>> do_slice6(None, 2, 4) >>> slice_none_end(None, 2, 4)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'NoneType' object is not subscriptable TypeError: 'NoneType' object is not subscriptable
>>> do_slice7(None, 2, 4) >>> slice_none_none(None, 2, 4)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'NoneType' object is not subscriptable TypeError: 'NoneType' object is not subscriptable
""" """
...@@ -121,23 +158,23 @@ import sys ...@@ -121,23 +158,23 @@ import sys
if sys.version_info[0] >= 3: if sys.version_info[0] >= 3:
__doc__ = __doc__.replace(u"(u'", u"('").replace(u" u'", u" '") __doc__ = __doc__.replace(u"(u'", u"('").replace(u" u'", u" '")
def do_slice1(unicode s, int i, int j): def slice_start_end(unicode s, int i, int j):
print(s[i:j]) print(s[i:j])
def do_slice2(unicode s, int i, int j): def slice_start(unicode s, int i, int j):
print(s[i:]) print(s[i:])
def do_slice3(unicode s, int i, int j): def slice_end(unicode s, int i, int j):
print(s[:i]) print(s[:i])
def do_slice4(unicode s, int i, int j): def slice_all(unicode s, int i, int j):
print(s[:]) print(s[:])
def do_slice5(unicode s, int i, int j): def slice_start_none(unicode s, int i, int j):
print(s[i:None]) print(s[i:None])
def do_slice6(unicode s, int i, int j): def slice_none_end(unicode s, int i, int j):
print(s[None:i]) print(s[None:i])
def do_slice7(unicode s, int i, int j): def slice_none_none(unicode s, int i, int j):
print(s[None:None]) print(s[None:None])
...@@ -364,6 +364,25 @@ def endswith_start_end(unicode s, sub, start, end): ...@@ -364,6 +364,25 @@ def endswith_start_end(unicode s, sub, start, end):
return 'NO MATCH' return 'NO MATCH'
# unicode.__contains__(s, sub)
@cython.test_fail_if_path_exists(
"//CoerceFromPyTypeNode", "//AttributeNode")
@cython.test_assert_path_exists(
"//CoerceToPyTypeNode", "//PrimaryCmpNode")
def in_test(unicode s, substring):
"""
>>> in_test(text, 'sa')
True
>>> in_test(text, 'XYZ')
False
>>> in_test(None, 'sa')
Traceback (most recent call last):
TypeError: 'NoneType' object is not iterable
"""
return substring in s
# unicode.find(s, sub, [start, [end]]) # unicode.find(s, sub, [start, [end]])
@cython.test_fail_if_path_exists( @cython.test_fail_if_path_exists(
......
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