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",
......
...@@ -247,14 +247,7 @@ class ExprNode(Node): ...@@ -247,14 +247,7 @@ class ExprNode(Node):
# whether this node with a memoryview type should be broadcast # whether this node with a memoryview type should be broadcast
memslice_broadcast = False memslice_broadcast = False
try: child_attrs = property(fget=operator.attrgetter('subexprs'))
_get_child_attrs = operator.attrgetter('subexprs')
except AttributeError:
# Python 2.3
def __get_child_attrs(self):
return self.subexprs
_get_child_attrs = __get_child_attrs
child_attrs = property(fget=_get_child_attrs)
def not_implemented(self, method_name): def not_implemented(self, method_name):
print_call_chain(method_name, "not implemented") ### print_call_chain(method_name, "not implemented") ###
...@@ -304,8 +297,6 @@ class ExprNode(Node): ...@@ -304,8 +297,6 @@ class ExprNode(Node):
return typecast(type, py_object_type, self.result()) return typecast(type, py_object_type, self.result())
return typecast(type, self.ctype(), self.result()) return typecast(type, self.ctype(), self.result())
return typecast(type, self.ctype(), self.result())
def py_result(self): def py_result(self):
# Return the result code cast to PyObject *. # Return the result code cast to PyObject *.
return self.result_as(py_object_type) return self.result_as(py_object_type)
...@@ -434,7 +425,7 @@ class ExprNode(Node): ...@@ -434,7 +425,7 @@ class ExprNode(Node):
def analyse_as_extension_type(self, env): def analyse_as_extension_type(self, env):
# If this node can be interpreted as a reference to an # If this node can be interpreted as a reference to an
# extension type, return its type, else None. # extension type or builtin type, return its type, else None.
return None return None
def analyse_types(self, env): def analyse_types(self, env):
...@@ -806,6 +797,23 @@ class ExprNode(Node): ...@@ -806,6 +797,23 @@ class ExprNode(Node):
else: else:
return self return self
@classmethod
def from_node(cls, node, **kwargs):
"""Instantiate this node class from another node, properly
copying over all attributes that one would forget otherwise.
"""
attributes = "cf_state cf_maybe_null cf_is_null".split()
for attr_name in attributes:
if attr_name in kwargs:
continue
try:
value = getattr(node, attr_name)
except AttributeError:
pass
else:
kwargs[attr_name] = value
return cls(node.pos, **kwargs)
class AtomicExprNode(ExprNode): class AtomicExprNode(ExprNode):
# Abstract base class for expression nodes which have # Abstract base class for expression nodes which have
...@@ -1083,6 +1091,15 @@ class BytesNode(ConstNode): ...@@ -1083,6 +1091,15 @@ class BytesNode(ConstNode):
# start off as Python 'bytes' to support len() in O(1) # start off as Python 'bytes' to support len() in O(1)
type = bytes_type type = bytes_type
def calculate_constant_result(self):
self.constant_result = self.value
def as_sliced_node(self, start, stop, step=None):
value = StringEncoding.BytesLiteral(self.value[start:stop:step])
value.encoding = self.value.encoding
return BytesNode(
self.pos, value=value, constant_result=value)
def compile_time_value(self, denv): def compile_time_value(self, denv):
return self.value return self.value
...@@ -1164,6 +1181,25 @@ class UnicodeNode(PyConstNode): ...@@ -1164,6 +1181,25 @@ class UnicodeNode(PyConstNode):
bytes_value = None bytes_value = None
type = unicode_type type = unicode_type
def calculate_constant_result(self):
self.constant_result = self.value
def as_sliced_node(self, start, stop, step=None):
if _string_contains_surrogates(self.value[:stop]):
# this is unsafe as it may give different results in different runtimes
return None
value = StringEncoding.EncodedString(self.value[start:stop:step])
value.encoding = self.value.encoding
if self.bytes_value is not None:
bytes_value = StringEncoding.BytesLiteral(
self.bytes_value[start:stop:step])
bytes_value.encoding = self.bytes_value.encoding
else:
bytes_value = None
return UnicodeNode(
self.pos, value=value, bytes_value=bytes_value,
constant_result=value)
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
if dst_type is self.type: if dst_type is self.type:
pass pass
...@@ -1190,21 +1226,7 @@ class UnicodeNode(PyConstNode): ...@@ -1190,21 +1226,7 @@ class UnicodeNode(PyConstNode):
## and (0xDC00 <= self.value[1] <= 0xDFFF)) ## and (0xDC00 <= self.value[1] <= 0xDFFF))
def contains_surrogates(self): def contains_surrogates(self):
# Check if the unicode string contains surrogate code points return _string_contains_surrogates(self.value)
# on a CPython platform with wide (UCS-4) or narrow (UTF-16)
# Unicode, i.e. characters that would be spelled as two
# separate code units on a narrow platform.
for c in map(ord, self.value):
if c > 65535: # can only happen on wide platforms
return True
# We only look for the first code unit (D800-DBFF) of a
# surrogate pair - if we find one, the other one
# (DC00-DFFF) is likely there, too. If we don't find it,
# any second code unit cannot make for a surrogate pair by
# itself.
if 0xD800 <= c <= 0xDBFF:
return True
return False
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
self.result_code = code.get_py_string_const(self.value) self.result_code = code.get_py_string_const(self.value)
...@@ -1229,6 +1251,24 @@ class StringNode(PyConstNode): ...@@ -1229,6 +1251,24 @@ class StringNode(PyConstNode):
is_identifier = None is_identifier = None
unicode_value = None unicode_value = None
def calculate_constant_result(self):
self.constant_result = self.value
def as_sliced_node(self, start, stop, step=None):
value = type(self.value)(self.value[start:stop:step])
value.encoding = self.value.encoding
if self.unicode_value is not None:
if _string_contains_surrogates(self.unicode_value[:stop]):
# this is unsafe as it may give different results in different runtimes
return None
unicode_value = StringEncoding.EncodedString(
self.unicode_value[start:stop:step])
else:
unicode_value = None
return StringNode(
self.pos, value=value, unicode_value=unicode_value,
constant_result=value, is_identifier=self.is_identifier)
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
if dst_type is not py_object_type and not str_type.subtype_of(dst_type): if dst_type is not py_object_type and not str_type.subtype_of(dst_type):
# if dst_type is Builtin.bytes_type: # if dst_type is Builtin.bytes_type:
...@@ -1263,35 +1303,24 @@ class IdentifierStringNode(StringNode): ...@@ -1263,35 +1303,24 @@ class IdentifierStringNode(StringNode):
is_identifier = True is_identifier = True
class LongNode(AtomicExprNode): def _string_contains_surrogates(ustring):
# Python long integer literal """
# Check if the unicode string contains surrogate code points
# value string on a CPython platform with wide (UCS-4) or narrow (UTF-16)
Unicode, i.e. characters that would be spelled as two
type = py_object_type separate code units on a narrow platform.
"""
def calculate_constant_result(self): for c in map(ord, ustring):
self.constant_result = Utils.str_to_number(self.value) if c > 65535: # can only happen on wide platforms
return True
def compile_time_value(self, denv): # We only look for the first code unit (D800-DBFF) of a
return Utils.str_to_number(self.value) # surrogate pair - if we find one, the other one
# (DC00-DFFF) is likely there, too. If we don't find it,
def analyse_types(self, env): # any second code unit cannot make for a surrogate pair by
self.is_temp = 1 # itself.
return self if 0xD800 <= c <= 0xDBFF:
return True
def may_be_none(self): return False
return False
gil_message = "Constructing Python long int"
def generate_result_code(self, code):
code.putln(
'%s = PyLong_FromString((char *)"%s", 0, 0); %s' % (
self.result(),
self.value,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class ImagNode(AtomicExprNode): class ImagNode(AtomicExprNode):
...@@ -1499,10 +1528,10 @@ class NameNode(AtomicExprNode): ...@@ -1499,10 +1528,10 @@ class NameNode(AtomicExprNode):
entry = self.entry entry = self.entry
if not entry: if not entry:
entry = env.lookup(self.name) entry = env.lookup(self.name)
if entry and entry.is_type and entry.type.is_extension_type: if entry and entry.is_type:
return entry.type if entry.type.is_extension_type or entry.type.is_builtin_type:
else: return entry.type
return None return None
def analyse_target_declaration(self, env): def analyse_target_declaration(self, env):
if not self.entry: if not self.entry:
...@@ -1580,6 +1609,7 @@ class NameNode(AtomicExprNode): ...@@ -1580,6 +1609,7 @@ class NameNode(AtomicExprNode):
self.is_temp = False self.is_temp = False
self.is_used_as_rvalue = True self.is_used_as_rvalue = True
self.use_managed_ref = True self.use_managed_ref = True
return self
def nogil_check(self, env): def nogil_check(self, env):
self.nogil = True self.nogil = True
...@@ -2611,7 +2641,7 @@ class IndexNode(ExprNode): ...@@ -2611,7 +2641,7 @@ class IndexNode(ExprNode):
return py_object_type return py_object_type
index_type = self.index.infer_type(env) index_type = self.index.infer_type(env)
if index_type and index_type.is_int or isinstance(self.index, (IntNode, LongNode)): if index_type and index_type.is_int or isinstance(self.index, IntNode):
# indexing! # indexing!
if base_type is unicode_type: if base_type is unicode_type:
# Py_UCS4 will automatically coerce to a unicode string # Py_UCS4 will automatically coerce to a unicode string
...@@ -2655,18 +2685,18 @@ class IndexNode(ExprNode): ...@@ -2655,18 +2685,18 @@ class IndexNode(ExprNode):
return py_object_type return py_object_type
def analyse_types(self, env): def analyse_types(self, env):
self.analyse_base_and_index_types(env, getting = 1) return self.analyse_base_and_index_types(env, getting=True)
return self
def analyse_target_types(self, env): def analyse_target_types(self, env):
self.analyse_base_and_index_types(env, setting = 1) node = self.analyse_base_and_index_types(env, setting=True)
if self.type.is_const: if node.type.is_const:
error(self.pos, "Assignment to const dereference") error(self.pos, "Assignment to const dereference")
if not self.is_lvalue(): if not node.is_lvalue():
error(self.pos, "Assignment to non-lvalue of type '%s'" % self.type) error(self.pos, "Assignment to non-lvalue of type '%s'" % node.type)
return self return node
def analyse_base_and_index_types(self, env, getting = 0, setting = 0, analyse_base = True): def analyse_base_and_index_types(self, env, getting=False, setting=False,
analyse_base=True):
# Note: This might be cleaned up by having IndexNode # Note: This might be cleaned up by having IndexNode
# parsed in a saner way and only construct the tuple if # parsed in a saner way and only construct the tuple if
# needed. # needed.
...@@ -2690,7 +2720,7 @@ class IndexNode(ExprNode): ...@@ -2690,7 +2720,7 @@ class IndexNode(ExprNode):
# Do not visit child tree if base is undeclared to avoid confusing # Do not visit child tree if base is undeclared to avoid confusing
# error messages # error messages
self.type = PyrexTypes.error_type self.type = PyrexTypes.error_type
return return self
is_slice = isinstance(self.index, SliceNode) is_slice = isinstance(self.index, SliceNode)
...@@ -2739,9 +2769,10 @@ class IndexNode(ExprNode): ...@@ -2739,9 +2769,10 @@ class IndexNode(ExprNode):
if len(indices) - len(newaxes) > self.base.type.ndim: if len(indices) - len(newaxes) > self.base.type.ndim:
self.type = error_type self.type = error_type
return error(indices[self.base.type.ndim].pos, error(indices[self.base.type.ndim].pos,
"Too many indices specified for type %s" % "Too many indices specified for type %s" %
self.base.type) self.base.type)
return self
axis_idx = 0 axis_idx = 0
for i, index in enumerate(indices[:]): for i, index in enumerate(indices[:]):
...@@ -2784,7 +2815,8 @@ class IndexNode(ExprNode): ...@@ -2784,7 +2815,8 @@ class IndexNode(ExprNode):
else: else:
self.type = error_type self.type = error_type
return error(index.pos, "Invalid index for memoryview specified") error(index.pos, "Invalid index for memoryview specified")
return self
self.memslice_index = self.memslice_index and not self.memslice_slice self.memslice_index = self.memslice_index and not self.memslice_slice
self.original_indices = indices self.original_indices = indices
...@@ -2849,7 +2881,7 @@ class IndexNode(ExprNode): ...@@ -2849,7 +2881,7 @@ class IndexNode(ExprNode):
if not MemoryView.validate_axes(self.pos, axes): if not MemoryView.validate_axes(self.pos, axes):
self.type = error_type self.type = error_type
return return self
self.type = PyrexTypes.MemoryViewSliceType( self.type = PyrexTypes.MemoryViewSliceType(
self.base.type.dtype, axes) self.base.type.dtype, axes)
...@@ -2920,15 +2952,15 @@ class IndexNode(ExprNode): ...@@ -2920,15 +2952,15 @@ class IndexNode(ExprNode):
PyrexTypes.c_py_ssize_t_type, env) PyrexTypes.c_py_ssize_t_type, env)
elif not self.index.type.is_int: elif not self.index.type.is_int:
error(self.pos, error(self.pos,
"Invalid index type '%s'" % "Invalid index type '%s'" %
self.index.type) self.index.type)
elif base_type.is_cpp_class: elif base_type.is_cpp_class:
function = env.lookup_operator("[]", [self.base, self.index]) function = env.lookup_operator("[]", [self.base, self.index])
if function is None: if function is None:
error(self.pos, "Indexing '%s' not supported for index type '%s'" % (base_type, self.index.type)) error(self.pos, "Indexing '%s' not supported for index type '%s'" % (base_type, self.index.type))
self.type = PyrexTypes.error_type self.type = PyrexTypes.error_type
self.result_code = "<error>" self.result_code = "<error>"
return return self
func_type = function.type func_type = function.type
if func_type.is_ptr: if func_type.is_ptr:
func_type = func_type.base_type func_type = func_type.base_type
...@@ -2940,11 +2972,12 @@ class IndexNode(ExprNode): ...@@ -2940,11 +2972,12 @@ class IndexNode(ExprNode):
self.parse_indexed_fused_cdef(env) self.parse_indexed_fused_cdef(env)
else: else:
error(self.pos, error(self.pos,
"Attempting to index non-array type '%s'" % "Attempting to index non-array type '%s'" %
base_type) base_type)
self.type = PyrexTypes.error_type self.type = PyrexTypes.error_type
self.wrap_in_nonecheck_node(env, getting) self.wrap_in_nonecheck_node(env, getting)
return self
def wrap_in_nonecheck_node(self, env, getting): def wrap_in_nonecheck_node(self, env, getting):
if not env.directives['nonecheck'] or not self.base.may_be_none(): if not env.directives['nonecheck'] or not self.base.may_be_none():
...@@ -3455,23 +3488,15 @@ class SliceIndexNode(ExprNode): ...@@ -3455,23 +3488,15 @@ class SliceIndexNode(ExprNode):
self.base = self.base.analyse_types(env) self.base = self.base.analyse_types(env)
if self.base.type.is_memoryviewslice: if self.base.type.is_memoryviewslice:
# Gross hack here! But we do not know the type until this point,
# and we cannot create and return a new node. So we change the
# type...
none_node = NoneNode(self.pos) none_node = NoneNode(self.pos)
index = SliceNode(self.pos, index = SliceNode(self.pos,
start=self.start or none_node, start=self.start or none_node,
stop=self.stop or none_node, stop=self.stop or none_node,
step=none_node) step=none_node)
del self.start index_node = IndexNode(self.pos, index, base=self.base)
del self.stop return index_node.analyse_base_and_index_types(
self.index = index env, getting=getting, setting=not getting,
self.__class__ = IndexNode analyse_base=False)
self.analyse_base_and_index_types(env,
getting=getting,
setting=not getting,
analyse_base=False)
return self
if self.start: if self.start:
self.start = self.start.analyse_types(env) self.start = self.start.analyse_types(env)
...@@ -3929,23 +3954,6 @@ class SimpleCallNode(CallNode): ...@@ -3929,23 +3954,6 @@ class SimpleCallNode(CallNode):
self.is_temp = 1 self.is_temp = 1
else: else:
self.args = [ arg.analyse_types(env) for arg in self.args ] self.args = [ arg.analyse_types(env) for arg in self.args ]
if self.self and func_type.args:
# Coerce 'self' to the type expected by the method.
self_arg = func_type.args[0]
if self_arg.not_none: # C methods must do the None test for self at *call* time
self.self = self.self.as_none_safe_node(
"'NoneType' object has no attribute '%s'",
error = 'PyExc_AttributeError',
format_args = [self.function.entry.name])
expected_type = self_arg.type
if self_arg.accept_builtin_subtypes:
self.coerced_self = CMethodSelfCloneNode(self.self)
else:
self.coerced_self = CloneNode(self.self)
self.coerced_self = self.coerced_self.coerce_to(expected_type, env)
# Insert coerced 'self' argument into argument list.
self.args.insert(0, self.coerced_self)
self.analyse_c_function_call(env) self.analyse_c_function_call(env)
return self return self
...@@ -3965,6 +3973,11 @@ class SimpleCallNode(CallNode): ...@@ -3965,6 +3973,11 @@ class SimpleCallNode(CallNode):
self.type = error_type self.type = error_type
return return
if self.self:
args = [self.self] + self.args
else:
args = self.args
if self.function.type.is_cpp_class: if self.function.type.is_cpp_class:
overloaded_entry = self.function.type.scope.lookup("operator()") overloaded_entry = self.function.type.scope.lookup("operator()")
if overloaded_entry is None: if overloaded_entry is None:
...@@ -3986,7 +3999,7 @@ class SimpleCallNode(CallNode): ...@@ -3986,7 +3999,7 @@ class SimpleCallNode(CallNode):
else: else:
alternatives = overloaded_entry.all_alternatives() alternatives = overloaded_entry.all_alternatives()
entry = PyrexTypes.best_match(self.args, alternatives, self.pos, env) entry = PyrexTypes.best_match(args, alternatives, self.pos, env)
if not entry: if not entry:
self.type = PyrexTypes.error_type self.type = PyrexTypes.error_type
...@@ -3998,24 +4011,60 @@ class SimpleCallNode(CallNode): ...@@ -3998,24 +4011,60 @@ class SimpleCallNode(CallNode):
self.function.type = entry.type self.function.type = entry.type
func_type = self.function_type() func_type = self.function_type()
else: else:
entry = None
func_type = self.function_type() func_type = self.function_type()
if not func_type.is_cfunction: if not func_type.is_cfunction:
error(self.pos, "Calling non-function type '%s'" % func_type) error(self.pos, "Calling non-function type '%s'" % func_type)
self.type = PyrexTypes.error_type self.type = PyrexTypes.error_type
self.result_code = "<error>" self.result_code = "<error>"
return return
# Check no. of args # Check no. of args
max_nargs = len(func_type.args) max_nargs = len(func_type.args)
expected_nargs = max_nargs - func_type.optional_arg_count expected_nargs = max_nargs - func_type.optional_arg_count
actual_nargs = len(self.args) actual_nargs = len(args)
if func_type.optional_arg_count and expected_nargs != actual_nargs: if func_type.optional_arg_count and expected_nargs != actual_nargs:
self.has_optional_args = 1 self.has_optional_args = 1
self.is_temp = 1 self.is_temp = 1
# check 'self' argument
if entry and entry.is_cmethod and func_type.args:
formal_arg = func_type.args[0]
arg = args[0]
if formal_arg.not_none:
if self.self:
self.self = self.self.as_none_safe_node(
"'NoneType' object has no attribute '%s'",
error='PyExc_AttributeError',
format_args=[entry.name])
else:
# unbound method
arg = arg.as_none_safe_node(
"descriptor '%s' requires a '%s' object but received a 'NoneType'",
format_args=[entry.name, formal_arg.type.name])
if self.self:
if formal_arg.accept_builtin_subtypes:
arg = CMethodSelfCloneNode(self.self)
else:
arg = CloneNode(self.self)
arg = self.coerced_self = arg.coerce_to(formal_arg.type, env)
elif formal_arg.type.is_builtin_type:
# special case: unbound methods of builtins accept subtypes
arg = arg.coerce_to(formal_arg.type, env)
if arg.type.is_builtin_type and isinstance(arg, PyTypeTestNode):
arg.exact_builtin_type = False
args[0] = arg
# Coerce arguments # Coerce arguments
some_args_in_temps = False some_args_in_temps = False
for i in xrange(min(max_nargs, actual_nargs)): for i in xrange(min(max_nargs, actual_nargs)):
formal_type = func_type.args[i].type formal_arg = func_type.args[i]
arg = self.args[i].coerce_to(formal_type, env) formal_type = formal_arg.type
arg = args[i].coerce_to(formal_type, env)
if formal_arg.not_none:
# C methods must do the None checks at *call* time
arg = arg.as_none_safe_node(
"cannot pass None into a C function argument that is declared 'not None'")
if arg.is_temp: if arg.is_temp:
if i > 0: if i > 0:
# first argument in temp doesn't impact subsequent arguments # first argument in temp doesn't impact subsequent arguments
...@@ -4035,19 +4084,21 @@ class SimpleCallNode(CallNode): ...@@ -4035,19 +4084,21 @@ class SimpleCallNode(CallNode):
if i > 0: # first argument doesn't matter if i > 0: # first argument doesn't matter
some_args_in_temps = True some_args_in_temps = True
arg = arg.coerce_to_temp(env) arg = arg.coerce_to_temp(env)
self.args[i] = arg args[i] = arg
# handle additional varargs parameters # handle additional varargs parameters
for i in xrange(max_nargs, actual_nargs): for i in xrange(max_nargs, actual_nargs):
arg = self.args[i] arg = args[i]
if arg.type.is_pyobject: if arg.type.is_pyobject:
arg_ctype = arg.type.default_coerced_ctype() arg_ctype = arg.type.default_coerced_ctype()
if arg_ctype is None: if arg_ctype is None:
error(self.args[i].pos, error(self.args[i].pos,
"Python object cannot be passed as a varargs parameter") "Python object cannot be passed as a varargs parameter")
else: else:
self.args[i] = arg = arg.coerce_to(arg_ctype, env) args[i] = arg = arg.coerce_to(arg_ctype, env)
if arg.is_temp and i > 0: if arg.is_temp and i > 0:
some_args_in_temps = True some_args_in_temps = True
if some_args_in_temps: if some_args_in_temps:
# if some args are temps and others are not, they may get # if some args are temps and others are not, they may get
# constructed in the wrong order (temps first) => make # constructed in the wrong order (temps first) => make
...@@ -4057,7 +4108,7 @@ class SimpleCallNode(CallNode): ...@@ -4057,7 +4108,7 @@ class SimpleCallNode(CallNode):
for i in xrange(actual_nargs-1): for i in xrange(actual_nargs-1):
if i == 0 and self.self is not None: if i == 0 and self.self is not None:
continue # self is ok continue # self is ok
arg = self.args[i] arg = args[i]
if arg.nonlocally_immutable(): if arg.nonlocally_immutable():
# locals, C functions, unassignable types are safe. # locals, C functions, unassignable types are safe.
pass pass
...@@ -4076,6 +4127,8 @@ class SimpleCallNode(CallNode): ...@@ -4076,6 +4127,8 @@ class SimpleCallNode(CallNode):
warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0) warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
break break
self.args[:] = args
# Calc result type and code fragment # Calc result type and code fragment
if isinstance(self.function, NewExprNode): if isinstance(self.function, NewExprNode):
self.type = PyrexTypes.CPtrType(self.function.class_type) self.type = PyrexTypes.CPtrType(self.function.class_type)
...@@ -4697,19 +4750,21 @@ class AttributeNode(ExprNode): ...@@ -4697,19 +4750,21 @@ class AttributeNode(ExprNode):
return self.obj.type_dependencies(env) return self.obj.type_dependencies(env)
def infer_type(self, env): def infer_type(self, env):
if self.analyse_as_cimported_attribute(env, 0): # FIXME: this is way too redundant with analyse_types()
return self.entry.type node = self.analyse_as_cimported_attribute_node(env, target=False)
elif self.analyse_as_unbound_cmethod(env): if node is not None:
return self.entry.type return node.entry.type
else: node = self.analyse_as_unbound_cmethod_node(env)
obj_type = self.obj.infer_type(env) if node is not None:
self.analyse_attribute(env, obj_type = obj_type) return node.entry.type
if obj_type.is_builtin_type and self.type.is_cfunction: obj_type = self.obj.infer_type(env)
# special case: C-API replacements for C methods of self.analyse_attribute(env, obj_type=obj_type)
# builtin types cannot be inferred as C functions as if obj_type.is_builtin_type and self.type.is_cfunction:
# that would prevent their use as bound methods # special case: C-API replacements for C methods of
return py_object_type # builtin types cannot be inferred as C functions as
return self.type # that would prevent their use as bound methods
return py_object_type
return self.type
def analyse_target_declaration(self, env): def analyse_target_declaration(self, env):
pass pass
...@@ -4724,21 +4779,19 @@ class AttributeNode(ExprNode): ...@@ -4724,21 +4779,19 @@ class AttributeNode(ExprNode):
def analyse_types(self, env, target = 0): def analyse_types(self, env, target = 0):
self.initialized_check = env.directives['initializedcheck'] self.initialized_check = env.directives['initializedcheck']
if self.analyse_as_cimported_attribute(env, target): node = self.analyse_as_cimported_attribute_node(env, target)
self.entry.used = True if node is None and not target:
elif not target and self.analyse_as_unbound_cmethod(env): node = self.analyse_as_unbound_cmethod_node(env)
self.entry.used = True if node is None:
else: node = self.analyse_as_ordinary_attribute_node(env, target)
self.analyse_as_ordinary_attribute(env, target) assert node is not None
if self.entry: if node.entry:
self.entry.used = True node.entry.used = True
if node.is_attribute:
# may be mutated in a namenode now :) node.wrap_obj_in_nonecheck(env)
if self.is_attribute: return node
self.wrap_obj_in_nonecheck(env)
return self
def analyse_as_cimported_attribute(self, env, target): def analyse_as_cimported_attribute_node(self, env, target):
# Try to interpret this as a reference to an imported # Try to interpret this as a reference to an imported
# C const, type, var or function. If successful, mutates # C const, type, var or function. If successful, mutates
# this node into a NameNode and returns 1, otherwise # this node into a NameNode and returns 1, otherwise
...@@ -4747,33 +4800,36 @@ class AttributeNode(ExprNode): ...@@ -4747,33 +4800,36 @@ class AttributeNode(ExprNode):
if module_scope: if module_scope:
entry = module_scope.lookup_here(self.attribute) entry = module_scope.lookup_here(self.attribute)
if entry and ( if entry and (
entry.is_cglobal or entry.is_cfunction entry.is_cglobal or entry.is_cfunction
or entry.is_type or entry.is_const): or entry.is_type or entry.is_const):
self.mutate_into_name_node(env, entry, target) # FIXME return self.as_name_node(env, entry, target)
entry.used = 1 return None
return 1
return 0
def analyse_as_unbound_cmethod(self, env): def analyse_as_unbound_cmethod_node(self, env):
# Try to interpret this as a reference to an unbound # Try to interpret this as a reference to an unbound
# C method of an extension type. If successful, mutates # C method of an extension type or builtin type. If successful,
# this node into a NameNode and returns 1, otherwise # creates a corresponding NameNode and returns it, otherwise
# returns 0. # returns None.
type = self.obj.analyse_as_extension_type(env) type = self.obj.analyse_as_extension_type(env)
if type: if type:
entry = type.scope.lookup_here(self.attribute) entry = type.scope.lookup_here(self.attribute)
if entry and entry.is_cmethod: if entry and entry.is_cmethod:
# Create a temporary entry describing the C method if type.is_builtin_type:
# as an ordinary function. if not self.is_called:
ubcm_entry = Symtab.Entry(entry.name, # must handle this as Python object
"%s->%s" % (type.vtabptr_cname, entry.cname), return None
entry.type) ubcm_entry = entry
ubcm_entry.is_cfunction = 1 else:
ubcm_entry.func_cname = entry.func_cname # Create a temporary entry describing the C method
ubcm_entry.is_unbound_cmethod = 1 # as an ordinary function.
self.mutate_into_name_node(env, ubcm_entry, None) # FIXME ubcm_entry = Symtab.Entry(entry.name,
return 1 "%s->%s" % (type.vtabptr_cname, entry.cname),
return 0 entry.type)
ubcm_entry.is_cfunction = 1
ubcm_entry.func_cname = entry.func_cname
ubcm_entry.is_unbound_cmethod = 1
return self.as_name_node(env, ubcm_entry, target=False)
return None
def analyse_as_type(self, env): def analyse_as_type(self, env):
module_scope = self.obj.analyse_as_module(env) module_scope = self.obj.analyse_as_module(env)
...@@ -4791,8 +4847,9 @@ class AttributeNode(ExprNode): ...@@ -4791,8 +4847,9 @@ class AttributeNode(ExprNode):
module_scope = self.obj.analyse_as_module(env) module_scope = self.obj.analyse_as_module(env)
if module_scope: if module_scope:
entry = module_scope.lookup_here(self.attribute) entry = module_scope.lookup_here(self.attribute)
if entry and entry.is_type and entry.type.is_extension_type: if entry and entry.is_type:
return entry.type if entry.type.is_extension_type or entry.type.is_builtin_type:
return entry.type
return None return None
def analyse_as_module(self, env): def analyse_as_module(self, env):
...@@ -4805,20 +4862,18 @@ class AttributeNode(ExprNode): ...@@ -4805,20 +4862,18 @@ class AttributeNode(ExprNode):
return entry.as_module return entry.as_module
return None return None
def mutate_into_name_node(self, env, entry, target): def as_name_node(self, env, entry, target):
# Mutate this node into a NameNode and complete the # Create a corresponding NameNode from this node and complete the
# analyse_types phase. # analyse_types phase.
self.__class__ = NameNode node = NameNode.from_node(self, name=self.attribute, entry=entry)
self.name = self.attribute
self.entry = entry
del self.obj
del self.attribute
if target: if target:
NameNode.analyse_target_types(self, env) # FIXME node = node.analyse_target_types(env)
else: else:
NameNode.analyse_rvalue_entry(self, env) node = node.analyse_rvalue_entry(env)
node.entry.used = 1
return node
def analyse_as_ordinary_attribute(self, env, target): def analyse_as_ordinary_attribute_node(self, env, target):
self.obj = self.obj.analyse_types(env) self.obj = self.obj.analyse_types(env)
self.analyse_attribute(env) self.analyse_attribute(env)
if self.entry and self.entry.is_cmethod and not self.is_called: if self.entry and self.entry.is_cmethod and not self.is_called:
...@@ -4835,6 +4890,7 @@ class AttributeNode(ExprNode): ...@@ -4835,6 +4890,7 @@ class AttributeNode(ExprNode):
error(self.pos, "Assignment to an immutable object field") error(self.pos, "Assignment to an immutable object field")
#elif self.type.is_memoryviewslice and not target: #elif self.type.is_memoryviewslice and not target:
# self.is_temp = True # self.is_temp = True
return self
def analyse_attribute(self, env, obj_type = None): def analyse_attribute(self, env, obj_type = None):
# Look up attribute and set self.type and self.member. # Look up attribute and set self.type and self.member.
...@@ -6760,14 +6816,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -6760,14 +6816,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
def analyse_types(self, env): def analyse_types(self, env):
if self.binding: if self.binding:
if self.specialized_cpdefs or self.is_specialization:
env.use_utility_code(
UtilityCode.load_cached("FusedFunction", "CythonFunction.c"))
else:
env.use_utility_code(
UtilityCode.load_cached("CythonFunction", "CythonFunction.c"))
self.analyse_default_args(env) self.analyse_default_args(env)
#TODO(craig,haoyu) This should be moved to a better place #TODO(craig,haoyu) This should be moved to a better place
self.set_qualified_name(env, self.def_node.name) self.set_qualified_name(env, self.def_node.name)
return self return self
...@@ -6867,14 +6916,18 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -6867,14 +6916,18 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
def generate_cyfunction_code(self, code): def generate_cyfunction_code(self, code):
def_node = self.def_node
if self.specialized_cpdefs: if self.specialized_cpdefs:
constructor = "__pyx_FusedFunction_NewEx"
def_node = self.specialized_cpdefs[0] def_node = self.specialized_cpdefs[0]
elif self.is_specialization: else:
def_node = self.def_node
if self.specialized_cpdefs or self.is_specialization:
code.globalstate.use_utility_code(
UtilityCode.load_cached("FusedFunction", "CythonFunction.c"))
constructor = "__pyx_FusedFunction_NewEx" constructor = "__pyx_FusedFunction_NewEx"
else: else:
code.globalstate.use_utility_code(
UtilityCode.load_cached("CythonFunction", "CythonFunction.c"))
constructor = "__Pyx_CyFunction_NewEx" constructor = "__Pyx_CyFunction_NewEx"
if self.code_object: if self.code_object:
...@@ -9199,6 +9252,11 @@ class CmpNode(object): ...@@ -9199,6 +9252,11 @@ class CmpNode(object):
self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyDictContains", "ObjectHandling.c") self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyDictContains", "ObjectHandling.c")
self.special_bool_cmp_function = "__Pyx_PyDict_Contains" self.special_bool_cmp_function = "__Pyx_PyDict_Contains"
return True return True
elif self.operand2.type is Builtin.unicode_type:
self.operand2 = self.operand2.as_none_safe_node("'NoneType' object is not iterable")
self.special_bool_cmp_utility_code = UtilityCode.load_cached("PyUnicodeContains", "StringTools.c")
self.special_bool_cmp_function = "__Pyx_PyUnicode_Contains"
return True
else: else:
if not self.operand2.type.is_pyobject: if not self.operand2.type.is_pyobject:
self.operand2 = self.operand2.coerce_to_pyobject(env) self.operand2 = self.operand2.coerce_to_pyobject(env)
...@@ -9717,6 +9775,8 @@ class PyTypeTestNode(CoercionNode): ...@@ -9717,6 +9775,8 @@ class PyTypeTestNode(CoercionNode):
# object is an instance of a particular extension type. # object is an instance of a particular extension type.
# This node borrows the result of its argument node. # This node borrows the result of its argument node.
exact_builtin_type = True
def __init__(self, arg, dst_type, env, notnone=False): def __init__(self, arg, dst_type, env, notnone=False):
# The arg is know to be a Python object, and # The arg is know to be a Python object, and
# the dst_type is known to be an extension type. # the dst_type is known to be an extension type.
...@@ -9758,12 +9818,17 @@ class PyTypeTestNode(CoercionNode): ...@@ -9758,12 +9818,17 @@ class PyTypeTestNode(CoercionNode):
def generate_result_code(self, code): def generate_result_code(self, code):
if self.type.typeobj_is_available(): if self.type.typeobj_is_available():
if not self.type.is_builtin_type: if self.type.is_builtin_type:
code.globalstate.use_utility_code(UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c")) type_test = self.type.type_test_code(
code.putln( self.arg.py_result(),
"if (!(%s)) %s" % ( self.notnone, exact=self.exact_builtin_type)
self.type.type_test_code(self.arg.py_result(), self.notnone), else:
code.error_goto(self.pos))) type_test = self.type.type_test_code(
self.arg.py_result(), self.notnone)
code.globalstate.use_utility_code(
UtilityCode.load_cached("ExtTypeTest", "ObjectHandling.c"))
code.putln("if (!(%s)) %s" % (
type_test, code.error_goto(self.pos)))
else: else:
error(self.pos, "Cannot test type of extern C class " error(self.pos, "Cannot test type of extern C class "
"without type object name specification") "without type object name specification")
......
...@@ -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(".")
......
from Cython.Compiler import TypeSlots
from Cython.Compiler.ExprNodes import not_a_constant
import cython import cython
cython.declare(UtilityCode=object, EncodedString=object, BytesLiteral=object, cython.declare(UtilityCode=object, EncodedString=object, BytesLiteral=object,
Nodes=object, ExprNodes=object, PyrexTypes=object, Builtin=object, Nodes=object, ExprNodes=object, PyrexTypes=object, Builtin=object,
...@@ -1787,13 +1789,14 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -1787,13 +1789,14 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
node = ExprNodes.PythonCapiCallNode( node = ExprNodes.PythonCapiCallNode(
coerce_node.pos, "__Pyx_PyBytes_GetItemInt", coerce_node.pos, "__Pyx_PyBytes_GetItemInt",
self.PyBytes_GetItemInt_func_type, self.PyBytes_GetItemInt_func_type,
args = [ args=[
arg.base.as_none_safe_node("'NoneType' object is not subscriptable"), arg.base.as_none_safe_node("'NoneType' object is not subscriptable"),
index_node.coerce_to(PyrexTypes.c_py_ssize_t_type, env), index_node.coerce_to(PyrexTypes.c_py_ssize_t_type, env),
bound_check_node, bound_check_node,
], ],
is_temp = True, is_temp=True,
utility_code=load_c_utility('bytes_index')) utility_code=UtilityCode.load_cached(
'bytes_index', 'StringTools.c'))
if coerce_node.type is not PyrexTypes.c_char_type: if coerce_node.type is not PyrexTypes.c_char_type:
node = node.coerce_to(coerce_node.type, env) node = node.coerce_to(coerce_node.type, env)
return node return node
...@@ -2140,14 +2143,22 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2140,14 +2143,22 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
Pyx_tp_new_func_type = PyrexTypes.CFuncType( Pyx_tp_new_func_type = PyrexTypes.CFuncType(
PyrexTypes.py_object_type, [ PyrexTypes.py_object_type, [
PyrexTypes.CFuncTypeArg("type", Builtin.type_type, None) PyrexTypes.CFuncTypeArg("type", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("args", Builtin.tuple_type, None),
]) ])
def _handle_simple_slot__new__(self, node, args, is_unbound_method): Pyx_tp_new_kwargs_func_type = PyrexTypes.CFuncType(
"""Replace 'exttype.__new__(exttype)' by a call to exttype->tp_new() PyrexTypes.py_object_type, [
PyrexTypes.CFuncTypeArg("type", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("args", Builtin.tuple_type, None),
PyrexTypes.CFuncTypeArg("kwargs", Builtin.dict_type, None),
])
def _handle_any_slot__new__(self, node, args, is_unbound_method, kwargs=None):
"""Replace 'exttype.__new__(exttype, ...)' by a call to exttype->tp_new()
""" """
obj = node.function.obj obj = node.function.obj
if not is_unbound_method or len(args) != 1: if not is_unbound_method or len(args) < 1:
return node return node
type_arg = args[0] type_arg = args[0]
if not obj.is_name or not type_arg.is_name: if not obj.is_name or not type_arg.is_name:
...@@ -2165,21 +2176,53 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2165,21 +2176,53 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
# different types - may or may not lead to an error at runtime # different types - may or may not lead to an error at runtime
return node return node
# FIXME: we could potentially look up the actual tp_new C args_tuple = ExprNodes.TupleNode(node.pos, args=args[1:])
# method of the extension type and call that instead of the args_tuple = args_tuple.analyse_types(
# generic slot. That would also allow us to pass parameters self.current_env(), skip_children=True)
# efficiently.
if not type_arg.type_entry: if type_arg.type_entry:
ext_type = type_arg.type_entry.type
if ext_type.is_extension_type and ext_type.typeobj_cname:
tp_slot = TypeSlots.ConstructorSlot("tp_new", '__new__')
slot_func_cname = TypeSlots.get_slot_function(ext_type.scope, tp_slot)
if slot_func_cname:
cython_scope = self.context.cython_scope
PyTypeObjectPtr = PyrexTypes.CPtrType(
cython_scope.lookup('PyTypeObject').type)
pyx_tp_new_kwargs_func_type = PyrexTypes.CFuncType(
PyrexTypes.py_object_type, [
PyrexTypes.CFuncTypeArg("type", PyTypeObjectPtr, None),
PyrexTypes.CFuncTypeArg("args", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("kwargs", PyrexTypes.py_object_type, None),
])
type_arg = ExprNodes.CastNode(type_arg, PyTypeObjectPtr)
if not kwargs:
kwargs = ExprNodes.NullNode(node.pos, type=PyrexTypes.py_object_type) # hack?
return ExprNodes.PythonCapiCallNode(
node.pos, slot_func_cname,
pyx_tp_new_kwargs_func_type,
args=[type_arg, args_tuple, kwargs],
is_temp=True)
else:
# arbitrary variable, needs a None check for safety # arbitrary variable, needs a None check for safety
type_arg = type_arg.as_none_safe_node( type_arg = type_arg.as_none_safe_node(
"object.__new__(X): X is not a type object (NoneType)") "object.__new__(X): X is not a type object (NoneType)")
return ExprNodes.PythonCapiCallNode( utility_code = UtilityCode.load_cached('tp_new', 'ObjectHandling.c')
node.pos, "__Pyx_tp_new", self.Pyx_tp_new_func_type, if kwargs:
args = [type_arg], return ExprNodes.PythonCapiCallNode(
utility_code = tpnew_utility_code, node.pos, "__Pyx_tp_new_kwargs", self.Pyx_tp_new_kwargs_func_type,
is_temp = node.is_temp args=[type_arg, args_tuple, kwargs],
utility_code=utility_code,
is_temp=node.is_temp
)
else:
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_tp_new", self.Pyx_tp_new_func_type,
args=[type_arg, args_tuple],
utility_code=utility_code,
is_temp=node.is_temp
) )
### methods of builtin types ### methods of builtin types
...@@ -2341,7 +2384,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2341,7 +2384,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
method_name = node.function.attribute method_name = node.function.attribute
if method_name == 'istitle': if method_name == 'istitle':
# istitle() doesn't directly map to Py_UNICODE_ISTITLE() # istitle() doesn't directly map to Py_UNICODE_ISTITLE()
utility_code = load_c_utility("py_unicode_istitle") utility_code = UtilityCode.load_cached(
"py_unicode_istitle", "StringTools.c")
function_name = '__Pyx_Py_UNICODE_ISTITLE' function_name = '__Pyx_Py_UNICODE_ISTITLE'
else: else:
utility_code = None utility_code = None
...@@ -2897,41 +2941,9 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2897,41 +2941,9 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
args[arg_index] = args[arg_index].coerce_to_boolean(self.current_env()) args[arg_index] = args[arg_index].coerce_to_boolean(self.current_env())
unicode_tailmatch_utility_code = load_c_utility('unicode_tailmatch') unicode_tailmatch_utility_code = UtilityCode.load_cached('unicode_tailmatch', 'StringTools.c')
bytes_tailmatch_utility_code = UtilityCode.load_cached('bytes_tailmatch', 'StringTools.c')
bytes_tailmatch_utility_code = load_c_utility('bytes_tailmatch') str_tailmatch_utility_code = UtilityCode.load_cached('str_tailmatch', 'StringTools.c')
str_tailmatch_utility_code = UtilityCode(
proto = '''
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.
impl = '''
static CYTHON_INLINE int __Pyx_PyStr_Tailmatch(PyObject* self, PyObject* arg, Py_ssize_t start,
Py_ssize_t end, int direction)
{
if (PY_MAJOR_VERSION < 3)
return __Pyx_PyBytes_Tailmatch(self, arg, start, end, direction);
else
return __Pyx_PyUnicode_Tailmatch(self, arg, start, end, direction);
}
''',
requires=[unicode_tailmatch_utility_code, bytes_tailmatch_utility_code]
)
tpnew_utility_code = UtilityCode(
proto = """
static CYTHON_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): class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
...@@ -2991,8 +3003,8 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): ...@@ -2991,8 +3003,8 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
import traceback, sys import traceback, sys
traceback.print_exc(file=sys.stdout) traceback.print_exc(file=sys.stdout)
NODE_TYPE_ORDER = [ExprNodes.CharNode, ExprNodes.IntNode, NODE_TYPE_ORDER = [ExprNodes.BoolNode, ExprNodes.CharNode,
ExprNodes.LongNode, ExprNodes.FloatNode] ExprNodes.IntNode, ExprNodes.FloatNode]
def _widest_node_class(self, *nodes): def _widest_node_class(self, *nodes):
try: try:
...@@ -3011,13 +3023,13 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): ...@@ -3011,13 +3023,13 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
return node return node
if not node.operand.is_literal: if not node.operand.is_literal:
return node return node
if isinstance(node, ExprNodes.NotNode): if node.operator == '!':
return ExprNodes.BoolNode(node.pos, value = bool(node.constant_result), return ExprNodes.BoolNode(node.pos, value=bool(node.constant_result),
constant_result = bool(node.constant_result)) constant_result=bool(node.constant_result))
elif isinstance(node.operand, ExprNodes.BoolNode): elif isinstance(node.operand, ExprNodes.BoolNode):
return ExprNodes.IntNode(node.pos, value = str(int(node.constant_result)), return ExprNodes.IntNode(node.pos, value=str(int(node.constant_result)),
type = PyrexTypes.c_int_type, type=PyrexTypes.c_int_type,
constant_result = int(node.constant_result)) constant_result=int(node.constant_result))
elif node.operator == '+': elif node.operator == '+':
return self._handle_UnaryPlusNode(node) return self._handle_UnaryPlusNode(node)
elif node.operator == '-': elif node.operator == '-':
...@@ -3025,20 +3037,24 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): ...@@ -3025,20 +3037,24 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
return node return node
def _handle_UnaryMinusNode(self, node): def _handle_UnaryMinusNode(self, node):
if isinstance(node.operand, ExprNodes.LongNode): def _negate(value):
return ExprNodes.LongNode(node.pos, value = '-' + node.operand.value, if value.startswith('-'):
constant_result = node.constant_result) value = value[1:]
else:
value = '-' + value
return value
if isinstance(node.operand, ExprNodes.FloatNode): if isinstance(node.operand, ExprNodes.FloatNode):
# this is a safe operation # this is a safe operation
return ExprNodes.FloatNode(node.pos, value = '-' + node.operand.value, return ExprNodes.FloatNode(node.pos, value=_negate(node.operand.value),
constant_result = node.constant_result) constant_result=node.constant_result)
node_type = node.operand.type node_type = node.operand.type
if node_type.is_int and node_type.signed or \ if node_type.is_int and node_type.signed or \
isinstance(node.operand, ExprNodes.IntNode) and node_type.is_pyobject: isinstance(node.operand, ExprNodes.IntNode) and node_type.is_pyobject:
return ExprNodes.IntNode(node.pos, value = '-' + node.operand.value, return ExprNodes.IntNode(node.pos, value=_negate(node.operand.value),
type = node_type, type=node_type,
longness = node.operand.longness, longness=node.operand.longness,
constant_result = node.constant_result) constant_result=node.constant_result)
return node return node
def _handle_UnaryPlusNode(self, node): def _handle_UnaryPlusNode(self, node):
...@@ -3083,18 +3099,26 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): ...@@ -3083,18 +3099,26 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
widest_type = PyrexTypes.widest_numeric_type(type1, type2) widest_type = PyrexTypes.widest_numeric_type(type1, type2)
else: else:
widest_type = PyrexTypes.py_object_type widest_type = PyrexTypes.py_object_type
target_class = self._widest_node_class(operand1, operand2) target_class = self._widest_node_class(operand1, operand2)
if target_class is None: if target_class is None:
return node return node
elif target_class is ExprNodes.IntNode: if target_class is ExprNodes.BoolNode and node.operator in '+-//<<%**>>':
# C arithmetic results in at least an int type
target_class = ExprNodes.IntNode
if target_class is ExprNodes.CharNode and node.operator in '+-//<<%**>>&|^':
# C arithmetic results in at least an int type
target_class = ExprNodes.IntNode
if target_class is ExprNodes.IntNode:
unsigned = getattr(operand1, 'unsigned', '') and \ unsigned = getattr(operand1, 'unsigned', '') and \
getattr(operand2, 'unsigned', '') getattr(operand2, 'unsigned', '')
longness = "LL"[:max(len(getattr(operand1, 'longness', '')), longness = "LL"[:max(len(getattr(operand1, 'longness', '')),
len(getattr(operand2, 'longness', '')))] len(getattr(operand2, 'longness', '')))]
new_node = ExprNodes.IntNode(pos=node.pos, new_node = ExprNodes.IntNode(pos=node.pos,
unsigned = unsigned, longness = longness, unsigned=unsigned, longness=longness,
value = str(node.constant_result), value=str(int(node.constant_result)),
constant_result = node.constant_result) constant_result=int(node.constant_result))
# IntNode is smart about the type it chooses, so we just # IntNode is smart about the type it chooses, so we just
# make sure we were not smarter this time # make sure we were not smarter this time
if widest_type.is_pyobject or new_node.type.is_pyobject: if widest_type.is_pyobject or new_node.type.is_pyobject:
...@@ -3102,7 +3126,7 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): ...@@ -3102,7 +3126,7 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
else: else:
new_node.type = PyrexTypes.widest_numeric_type(widest_type, new_node.type) new_node.type = PyrexTypes.widest_numeric_type(widest_type, new_node.type)
else: else:
if isinstance(node, ExprNodes.BoolNode): if target_class is ExprNodes.BoolNode:
node_value = node.constant_result node_value = node.constant_result
else: else:
node_value = str(node.constant_result) node_value = str(node.constant_result)
...@@ -3178,10 +3202,24 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): ...@@ -3178,10 +3202,24 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
def visit_SliceIndexNode(self, node): def visit_SliceIndexNode(self, node):
self._calculate_const(node) self._calculate_const(node)
# normalise start/stop values # normalise start/stop values
if node.start and node.start.constant_result is None: if node.start is None or node.start.constant_result is None:
node.start = None start = node.start = None
if node.stop and node.stop.constant_result is None: else:
node.stop = None start = node.start.constant_result
if node.stop is None or node.stop.constant_result is None:
stop = node.stop = None
else:
stop = node.stop.constant_result
# cut down sliced constant sequences
if node.constant_result is not not_a_constant:
base = node.base
if base.is_sequence_constructor:
base.args = base.args[start:stop]
return base
elif base.is_string_literal:
base = base.as_sliced_node(start, stop)
if base is not None:
return base
return node return node
def visit_ForInStatNode(self, node): def visit_ForInStatNode(self, node):
......
...@@ -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