Commit 0532e356 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'master' into ctuple

Conflicts:
	Cython/Compiler/PyrexTypes.py
	Cython/Utility/TypeConversion.c
	tests/run/arrayassign.pyx
parents ed84c09d 611f4187
......@@ -49,3 +49,6 @@ c1a18ab6b0808e87f68d2f9d914c01934510aef5 0.18b1
52beb5b16df5b8a92bb6c8c47faf42370d73cb0f 0.19b2
4818f5b68eb4b4ea6ad7e415f6672b491e2461bc 0.19rc1
48407fa3f3c9da84ab3dc103a6a2b1ca4c1beb2a 0.19
58fc9a3feb83f571623a492256885d21945decb4 0.21.1
58fc9a3feb83f571623a492256885d21945decb4 0.21.1
d05a4acd45fca9bdc12cef82890ca569fbaae1a5 0.21.1
......@@ -2,8 +2,8 @@
Cython Changelog
================
Latest
======
Latest changes
==============
Features added
--------------
......@@ -11,12 +11,31 @@ Features added
* C functions can coerce to Python functions, which allows passing them
around as callable objects.
* New ``cythonize`` option ``-a`` to generate the annotated HTML source view.
* C arrays can be assigned by value and auto-coerce from Python iterables
and to Python lists (and tuples).
* Extern C functions can now be declared as cpdef to export them to
the module's Python namespace. Extern C functions in pxd files export
their values to their own module, iff it exists.
Bugs fixed
----------
* Mismatching 'except' declarations on signatures in .pxd and .pyx files failed
to produce a compile error.
Other changes
-------------
0.21.1 (2014-10-18)
===================
Features added
--------------
* New ``cythonize`` option ``-a`` to generate the annotated HTML source view.
* Missing C-API declarations in ``cpython.unicode`` were added.
* Passing ``language='c++'`` into cythonize() globally enables C++ mode for
......@@ -35,9 +54,6 @@ Features added
Bugs fixed
----------
* Mismatching 'except' declarations on signatures in .pxd and .pyx files failed
to produce a compile error.
* Reference leak for non-simple Python expressions in boolean and/or expressions.
* To fix a name collision and to reflect availability on host platforms,
......@@ -55,6 +71,8 @@ Bugs fixed
if the already created module was used later on (e.g. through a
stale reference in sys.modules or elsewhere).
* ``cythonize.py`` script was not installed on MS-Windows.
Other changes
-------------
......
......@@ -966,14 +966,15 @@ class GlobalState(object):
w.putln("")
w.putln("static void __Pyx_CleanupGlobals(void) {")
#
# utility_code_def
#
code = self.parts['utility_code_proto']
code.putln("")
code.putln("/* --- Runtime support code (head) --- */")
code = self.parts['utility_code_def']
if self.emit_linenums:
code.write('\n#line 1 "cython_utility"\n')
code.putln("")
code.putln("/* Runtime support code */")
code.putln("/* --- Runtime support code --- */")
def finalize_main_c_code(self):
self.close_global_decls()
......
......@@ -1727,9 +1727,9 @@ class NameNode(AtomicExprNode):
def analyse_target_types(self, env):
self.analyse_entry(env, is_target=True)
if (not self.is_lvalue() and self.entry.is_cfunction and
self.entry.fused_cfunction and self.entry.as_variable):
# We need this for the fused 'def' TreeFragment
if self.entry.is_cfunction and self.entry.as_variable:
if self.entry.is_overridable or not self.is_lvalue() and self.entry.fused_cfunction:
# We need this for assigning to cpdef names and for the fused 'def' TreeFragment
self.entry = self.entry.as_variable
self.type = self.entry.type
......@@ -1738,8 +1738,7 @@ class NameNode(AtomicExprNode):
if self.type.is_reference:
error(self.pos, "Assignment to reference '%s'" % self.name)
if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue '%s'"
% self.name)
error(self.pos, "Assignment to non-lvalue '%s'" % self.name)
self.type = PyrexTypes.error_type
self.entry.used = 1
if self.entry.type.is_buffer:
......@@ -1863,9 +1862,13 @@ class NameNode(AtomicExprNode):
return True
def is_lvalue(self):
return self.entry.is_variable and \
not self.entry.type.is_array and \
return (
self.entry.is_variable and
not self.entry.is_readonly
) or (
self.entry.is_cfunction and
self.entry.is_overridable
)
def is_addressable(self):
return self.entry.is_variable and not self.type.is_memoryviewslice
......@@ -3436,9 +3439,6 @@ class IndexNode(ExprNode):
elif self.type.is_ptr:
# non-const pointers can always be reassigned
return True
elif self.type.is_array:
# fixed-sized arrays aren't l-values
return False
# Just about everything else returned by the index operator
# can be an lvalue.
return True
......@@ -3914,7 +3914,13 @@ class SliceIndexNode(ExprNode):
check_negative_indices(self.start, self.stop)
base_type = self.base.type
if base_type.is_string or base_type.is_cpp_string:
if base_type.is_array and not getting:
# cannot assign directly to C array => try to assign by making a copy
if not self.start and not self.stop:
self.type = base_type
else:
self.type = PyrexTypes.CPtrType(base_type.base_type)
elif base_type.is_string or base_type.is_cpp_string:
self.type = default_str_type(env)
elif base_type.is_pyunicode_ptr:
self.type = unicode_type
......@@ -4089,25 +4095,37 @@ class SliceIndexNode(ExprNode):
has_c_start, has_c_stop,
bool(code.globalstate.directives['wraparound'])))
else:
start_offset = ''
if self.start:
start_offset = self.start_code()
if start_offset == '0':
start_offset = ''
else:
start_offset += '+'
start_offset = self.start_code() if self.start else '0'
if rhs.type.is_array:
array_length = rhs.type.size
self.generate_slice_guard_code(code, array_length)
else:
error(self.pos,
"Slice assignments from pointers are not yet supported.")
# FIXME: fix the array size according to start/stop
array_length = self.base.type.size
for i in range(array_length):
code.putln("%s[%s%s] = %s[%d];" % (
self.base.result(), start_offset, i,
rhs.result(), i))
array_length = '%s - %s' % (self.stop_code(), start_offset)
def copy_carray(dst, src, item_type, start, count, depth):
var_name = Naming.quick_temp_cname
if depth:
var_name += str(depth)
dst_item, src_item = '{dst}[{i}+{start}] = {src}[{i}]'.format(
src=src,
dst=dst,
start=start,
i=var_name
).split(' = ')
code.putln('{')
code.putln('Py_ssize_t %s;' % var_name)
code.put('for ({i}=0; {i} < {count}; {i}++) '.format(
i=var_name,
count=count))
if item_type.is_array:
copy_carray(dst_item, src_item, item_type.base_type, 0, item_type.size, depth + 1)
else:
code.putln('%s = %s;' % (dst_item, src_item))
code.putln('}')
copy_carray(self.base.result(), rhs.result(), rhs.type.base_type,
start_offset, array_length, 0)
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
rhs.generate_disposal_code(code)
......@@ -4155,47 +4173,77 @@ class SliceIndexNode(ExprNode):
if not self.base.type.is_array:
return
slice_size = self.base.type.size
try:
total_length = slice_size = int(slice_size)
except ValueError:
total_length = None
start = stop = None
if self.stop:
stop = self.stop.result()
try:
stop = int(stop)
if stop < 0:
slice_size = self.base.type.size + stop
if total_length is None:
slice_size = '%s + %d' % (slice_size, stop)
else:
slice_size += stop
else:
slice_size = stop
stop = None
except ValueError:
pass
if self.start:
start = self.start.result()
try:
start = int(start)
if start < 0:
start = self.base.type.size + start
if total_length is None:
start = '%s + %d' % (self.base.type.size, start)
else:
start += total_length
if isinstance(slice_size, (int, long)):
slice_size -= start
else:
slice_size = '%s - (%s)' % (slice_size, start)
start = None
except ValueError:
pass
check = None
if slice_size < 0:
if target_size > 0:
runtime_check = None
compile_time_check = False
try:
int_target_size = int(target_size)
except ValueError:
int_target_size = None
else:
compile_time_check = isinstance(slice_size, (int, long))
if compile_time_check and slice_size < 0:
if int_target_size > 0:
error(self.pos, "Assignment to empty slice.")
elif start is None and stop is None:
elif compile_time_check and start is None and stop is None:
# we know the exact slice length
if target_size != slice_size:
error(self.pos, "Assignment to slice of wrong length, expected %d, got %d" % (
if int_target_size != slice_size:
error(self.pos, "Assignment to slice of wrong length, expected %s, got %s" % (
slice_size, target_size))
elif start is not None:
if stop is None:
stop = slice_size
check = "(%s)-(%s)" % (stop, start)
else: # stop is not None:
check = stop
if check:
code.putln("if (unlikely((%s) != %d)) {" % (check, target_size))
code.putln('PyErr_Format(PyExc_ValueError, "Assignment to slice of wrong length, expected %%" CYTHON_FORMAT_SSIZE_T "d, got %%" CYTHON_FORMAT_SSIZE_T "d", (Py_ssize_t)%d, (Py_ssize_t)(%s));' % (
target_size, check))
runtime_check = "(%s)-(%s)" % (stop, start)
elif stop is not None:
runtime_check = stop
else:
runtime_check = slice_size
if runtime_check:
code.putln("if (unlikely((%s) != (%s))) {" % (runtime_check, target_size))
code.putln(
'PyErr_Format(PyExc_ValueError, "Assignment to slice of wrong length,'
' expected %%" CYTHON_FORMAT_SSIZE_T "d, got %%" CYTHON_FORMAT_SSIZE_T "d",'
' (Py_ssize_t)(%s), (Py_ssize_t)(%s));' % (
target_size, runtime_check))
code.putln(code.error_goto(self.pos))
code.putln("}")
......@@ -5736,7 +5784,7 @@ class AttributeNode(ExprNode):
def is_lvalue(self):
if self.obj:
return not self.type.is_array
return True
else:
return NameNode.is_lvalue(self)
......@@ -6590,7 +6638,7 @@ class ListNode(SequenceNode):
error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
elif self.mult_factor:
error(self.pos, "Cannot coerce multiplied list to '%s'" % dst_type)
elif dst_type.is_ptr and dst_type.base_type is not PyrexTypes.c_void_type:
elif (dst_type.is_array or dst_type.is_ptr) and dst_type.base_type is not PyrexTypes.c_void_type:
base_type = dst_type.base_type
self.type = PyrexTypes.CArrayType(base_type, len(self.args))
for i in range(len(self.original_args)):
......@@ -6631,6 +6679,7 @@ class ListNode(SequenceNode):
if self.type.is_array:
# To be valid C++, we must allocate the memory on the stack
# manually and be sure not to reuse it for something else.
# Yes, this means that we leak a temp array variable.
pass
else:
SequenceNode.release_temp_result(self, env)
......@@ -11178,6 +11227,7 @@ class CoerceToPyTypeNode(CoercionNode):
# to a Python object.
type = py_object_type
target_type = py_object_type
is_temp = 1
def __init__(self, arg, env, type=py_object_type):
......@@ -11197,22 +11247,17 @@ class CoerceToPyTypeNode(CoercionNode):
self.type = unicode_type
elif arg.type.is_complex:
self.type = Builtin.complex_type
self.target_type = self.type
elif arg.type.is_string or arg.type.is_cpp_string:
if (type not in (bytes_type, bytearray_type)
and not env.directives['c_string_encoding']):
error(arg.pos,
"default encoding required for conversion from '%s' to '%s'" %
(arg.type, type))
self.type = type
self.type = self.target_type = type
else:
# FIXME: check that the target type and the resulting type are compatible
pass
if arg.type.is_memoryviewslice:
# Register utility codes at this point
arg.type.get_to_py_function(env, arg)
self.env = env
self.target_type = type
gil_message = "Converting to Python object"
......@@ -11240,21 +11285,11 @@ class CoerceToPyTypeNode(CoercionNode):
return self
def generate_result_code(self, code):
arg_type = self.arg.type
if arg_type.is_memoryviewslice:
funccall = arg_type.get_to_py_function(self.env, self.arg)
else:
func = arg_type.to_py_function
if arg_type.is_string or arg_type.is_cpp_string:
if self.type in (bytes_type, str_type, unicode_type):
func = func.replace("Object", self.type.name.title(), 1)
elif self.type is bytearray_type:
func = func.replace("Object", "ByteArray", 1)
funccall = "%s(%s)" % (func, self.arg.result())
code.putln('%s = %s; %s' % (
code.putln('%s; %s' % (
self.arg.type.to_py_call_code(
self.arg.result(),
self.result(),
funccall,
self.target_type),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
......@@ -11322,18 +11357,11 @@ class CoerceFromPyTypeNode(CoercionNode):
return self
def is_ephemeral(self):
return self.type.is_ptr and self.arg.is_ephemeral()
return (self.type.is_ptr and not self.type.is_array) and self.arg.is_ephemeral()
def generate_result_code(self, code):
function = self.type.from_py_function
operand = self.arg.py_result()
rhs = "%s(%s)" % (function, operand)
if self.type.is_enum:
rhs = typecast(self.type, c_long_type, rhs)
code.putln('%s = %s; %s' % (
self.result(),
rhs,
code.error_goto_if(self.type.error_condition(self.result()), self.pos)))
code.putln(self.type.from_py_call_code(
self.arg.py_result(), self.result(), self.pos, code))
if self.type.is_pyobject:
code.put_gotref(self.py_result())
......
......@@ -1312,10 +1312,9 @@ class CVarDefNode(StatNode):
return
if type.is_cfunction:
self.entry = dest_scope.declare_cfunction(name, type, declarator.pos,
cname = cname, visibility = self.visibility, in_pxd = self.in_pxd,
api = self.api, modifiers = self.modifiers)
cname=cname, visibility=self.visibility, in_pxd=self.in_pxd,
api=self.api, modifiers=self.modifiers, overridable=self.overridable)
if self.entry is not None:
self.entry.is_overridable = self.overridable
self.entry.directive_locals = copy.copy(self.directive_locals)
if 'staticmethod' in env.directives:
type.is_static_method = True
......@@ -2274,8 +2273,9 @@ class CFuncDefNode(FuncDefNode):
type.is_static_method = self.is_static_method
self.entry = env.declare_cfunction(
name, type, self.pos,
cname = cname, visibility = self.visibility, api = self.api,
defining = self.body is not None, modifiers = self.modifiers)
cname=cname, visibility=self.visibility, api=self.api,
defining=self.body is not None, modifiers=self.modifiers,
overridable=self.overridable)
self.entry.inline_func_in_pxd = self.inline_in_pxd
self.return_type = type.return_type
if self.return_type.is_array and self.visibility != 'extern':
......@@ -4776,14 +4776,20 @@ class SingleAssignmentNode(AssignmentNode):
self.lhs.memslice_broadcast = True
self.rhs.memslice_broadcast = True
is_index_node = isinstance(self.lhs, ExprNodes.IndexNode)
if (is_index_node and not self.rhs.type.is_memoryviewslice and
if (self.lhs.is_subscript and not self.rhs.type.is_memoryviewslice and
(self.lhs.memslice_slice or self.lhs.is_memslice_copy) and
(self.lhs.type.dtype.assignable_from(self.rhs.type) or
self.rhs.type.is_pyobject)):
# scalar slice assignment
self.lhs.is_memslice_scalar_assignment = True
dtype = self.lhs.type.dtype
elif self.lhs.type.is_array:
if not isinstance(self.lhs, ExprNodes.SliceIndexNode):
# cannot assign to C array, only to its full slice
self.lhs = ExprNodes.SliceIndexNode(
self.lhs.pos, base=self.lhs, start=None, stop=None)
self.lhs = self.lhs.analyse_target_types(env)
dtype = self.lhs.type
else:
dtype = self.lhs.type
......
......@@ -4,6 +4,7 @@
from __future__ import absolute_import
import re
import copy
import re
......@@ -430,6 +431,21 @@ class CTypedefType(BaseType):
# delegation
return self.typedef_base_type.create_from_py_utility_code(env)
def to_py_call_code(self, source_code, result_code, result_type, to_py_function=None):
if to_py_function is None:
to_py_function = self.to_py_function
return self.typedef_base_type.to_py_call_code(
source_code, result_code, result_type, to_py_function)
def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None):
if from_py_function is None:
from_py_function = self.from_py_function
if error_condition is None:
error_condition = self.error_condition(result_code)
return self.typedef_base_type.from_py_call_code(
source_code, result_code, error_pos, code, from_py_function, error_condition)
def overflow_check_binop(self, binop, env, const_rhs=False):
env.use_utility_code(UtilityCode.load("Common", "Overflow.c"))
type = self.empty_declaration_code()
......@@ -703,16 +719,18 @@ class MemoryViewSliceType(PyrexType):
return True
def create_to_py_utility_code(self, env):
self._dtype_to_py_func, self._dtype_from_py_func = self.dtype_object_conversion_funcs(env)
return True
def get_to_py_function(self, env, obj):
to_py_func, from_py_func = self.dtype_object_conversion_funcs(env)
to_py_func = "(PyObject *(*)(char *)) " + to_py_func
from_py_func = "(int (*)(char *, PyObject *)) " + from_py_func
def to_py_call_code(self, source_code, result_code, result_type, to_py_function=None):
assert self._dtype_to_py_func
assert self._dtype_from_py_func
to_py_func = "(PyObject *(*)(char *)) " + self._dtype_to_py_func
from_py_func = "(int (*)(char *, PyObject *)) " + self._dtype_from_py_func
tup = (obj.result(), self.ndim, to_py_func, from_py_func,
self.dtype.is_pyobject)
return "__pyx_memoryview_fromslice(%s, %s, %s, %s, %d);" % tup
tup = (result_code, source_code, self.ndim, to_py_func, from_py_func, self.dtype.is_pyobject)
return "%s = __pyx_memoryview_fromslice(%s, %s, %s, %s, %d);" % tup
def dtype_object_conversion_funcs(self, env):
get_function = "__pyx_memview_get_%s" % self.dtype_name
......@@ -1225,6 +1243,29 @@ class CType(PyrexType):
else:
return 0
def to_py_call_code(self, source_code, result_code, result_type, to_py_function=None):
func = self.to_py_function if to_py_function is None else to_py_function
assert func
if self.is_string or self.is_cpp_string:
if result_type.is_builtin_type:
result_type_name = result_type.name
if result_type_name in ('bytes', 'str', 'unicode'):
func = func.replace("Object", result_type_name.title(), 1)
elif result_type_name == 'bytearray':
func = func.replace("Object", "ByteArray", 1)
return '%s = %s(%s)' % (
result_code,
func,
source_code or 'NULL')
def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None):
return '%s = %s(%s); %s' % (
result_code,
from_py_function or self.from_py_function,
source_code,
code.error_goto_if(error_condition or self.error_condition(result_code), error_pos))
class CConstType(BaseType):
......@@ -1259,6 +1300,9 @@ class CConstType(BaseType):
def deduce_template_params(self, actual):
return self.const_base_type.deduce_template_params(actual)
def can_coerce_to_pyobject(self, env):
return self.const_base_type.can_coerce_to_pyobject(env)
def create_to_py_utility_code(self, env):
if self.const_base_type.create_to_py_utility_code(env):
self.to_py_function = self.const_base_type.to_py_function
......@@ -1315,6 +1359,7 @@ class CVoidType(CType):
#
is_void = 1
to_py_function = "__Pyx_void_to_None"
def __repr__(self):
return "<CVoidType>"
......@@ -1427,6 +1472,9 @@ class CIntType(CNumericType):
from_py_function = None
exception_value = -1
def can_coerce_to_pyobject(self, env):
return True
def create_to_py_utility_code(self, env):
if type(self).to_py_function is None:
self.to_py_function = "__Pyx_PyInt_From_" + self.specialization_name()
......@@ -1772,6 +1820,9 @@ class CComplexType(CNumericType):
is_float = self.real_type.is_float))
return True
def can_coerce_to_pyobject(self, env):
return True
def create_to_py_utility_code(self, env):
env.use_utility_code(complex_real_imag_utility_code)
env.use_utility_code(complex_to_py_utility_code)
......@@ -2102,15 +2153,16 @@ class CPointerBaseType(CType):
self.is_pyunicode_ptr = 1
if self.is_string and not base_type.is_error:
if base_type.signed:
self.to_py_function = "__Pyx_PyObject_FromString"
if self.is_ptr:
if base_type.signed == 2:
self.to_py_function = "__Pyx_PyObject_FromCString"
if self.is_ptr:
self.from_py_function = "__Pyx_PyObject_AsSString"
else:
elif base_type.signed:
self.to_py_function = "__Pyx_PyObject_FromString"
if self.is_ptr:
self.from_py_function = "__Pyx_PyObject_AsString"
else:
self.to_py_function = "__Pyx_PyObject_FromUString"
self.to_py_function = "__Pyx_PyObject_FromCString"
if self.is_ptr:
self.from_py_function = "__Pyx_PyObject_AsUString"
self.exception_value = "NULL"
......@@ -2139,6 +2191,7 @@ class CArrayType(CPointerBaseType):
# size integer or None Number of elements
is_array = 1
to_tuple_function = None
def __init__(self, base_type, size):
super(CArrayType, self).__init__(base_type)
......@@ -2161,8 +2214,12 @@ class CArrayType(CPointerBaseType):
or other_type is error_type)
def assignable_from_resolved_type(self, src_type):
# Can't assign to a variable of an array type
return 0
# C arrays are assigned by value, either Python containers or C arrays/pointers
if src_type.is_pyobject:
return True
if src_type.is_ptr or src_type.is_array:
return self.base_type.assignable_from(src_type.base_type)
return False
def element_ptr_type(self):
return c_ptr_type(self.base_type)
......@@ -2190,7 +2247,7 @@ class CArrayType(CPointerBaseType):
if base_type == self.base_type:
return self
else:
return CArrayType(base_type)
return CArrayType(base_type, self.size)
def deduce_template_params(self, actual):
if isinstance(actual, CArrayType):
......@@ -2198,6 +2255,74 @@ class CArrayType(CPointerBaseType):
else:
return None
def create_to_py_utility_code(self, env):
if self.to_py_function is not None:
return self.to_py_function
if not self.base_type.create_to_py_utility_code(env):
return False
base_type = self.base_type.declaration_code("", pyrex=1)
safe_typename = re.sub('[^a-zA-Z0-9]', '__', base_type)
to_py_function = "__Pyx_carray_to_py_%s" % safe_typename
to_tuple_function = "__Pyx_carray_to_tuple_%s" % safe_typename
from .UtilityCode import CythonUtilityCode
context = {
'cname': to_py_function,
'to_tuple_cname': to_tuple_function,
'base_type': base_type,
}
env.use_utility_code(CythonUtilityCode.load(
"carray.to_py", "CConvert.pyx",
outer_module_scope=env.global_scope(), # need access to types declared in module
context=context, compiler_directives=dict(env.global_scope().directives)))
self.to_tuple_function = to_tuple_function
self.to_py_function = to_py_function
return True
def to_py_call_code(self, source_code, result_code, result_type, to_py_function=None):
func = self.to_py_function if to_py_function is None else to_py_function
if self.is_string or self.is_pyunicode_ptr:
return '%s = %s(%s)' % (
result_code,
func,
source_code)
target_is_tuple = result_type.is_builtin_type and result_type.name == 'tuple'
return '%s = %s(%s, %s)' % (
result_code,
self.to_tuple_function if target_is_tuple else func,
source_code,
self.size)
def create_from_py_utility_code(self, env):
if self.from_py_function is not None:
return self.from_py_function
if not self.base_type.create_from_py_utility_code(env):
return False
base_type = self.base_type.declaration_code("", pyrex=1)
safe_typename = re.sub('[^a-zA-Z0-9]', '__', base_type)
from_py_function = "__Pyx_carray_from_py_%s" % safe_typename
from .UtilityCode import CythonUtilityCode
context = {
'cname': from_py_function,
'base_type': base_type,
}
env.use_utility_code(CythonUtilityCode.load(
"carray.from_py", "CConvert.pyx",
outer_module_scope=env.global_scope(), # need access to types declared in module
context=context, compiler_directives=dict(env.global_scope().directives)))
self.from_py_function = from_py_function
return True
def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None):
call_code = "%s(%s, %s, %s)" % (
from_py_function or self.from_py_function,
source_code, result_code, self.size)
return code.error_goto_if_neg(call_code, error_pos)
class CPtrType(CPointerBaseType):
# base_type CType Reference type
......@@ -2273,6 +2398,7 @@ class CPtrType(CPointerBaseType):
return self.base_type.find_cpp_operation_type(operator, operand_type)
return None
class CNullPtrType(CPtrType):
is_null_ptr = 1
......@@ -2404,8 +2530,7 @@ class CFuncType(CType):
# is exempt from compatibility checking (the proper check
# is performed elsewhere).
for i in range(as_cmethod, nargs):
if not self.args[i].type.same_as(
other_type.args[i].type):
if not self.args[i].type.same_as(other_type.args[i].type):
return 0
if self.has_varargs != other_type.has_varargs:
return 0
......@@ -2657,22 +2782,33 @@ class CFuncType(CType):
assert not self.is_fused
specialize_entry(entry, cname)
def create_to_py_utility_code(self, env):
# FIXME: it seems we're trying to coerce in more cases than we should
def can_coerce_to_pyobject(self, env):
# duplicating the decisions from create_to_py_utility_code() here avoids writing out unused code
if self.has_varargs or self.optional_arg_count:
return False
if self.to_py_function is not None:
return self.to_py_function
for arg in self.args:
if not arg.type.is_pyobject and not arg.type.can_coerce_to_pyobject(env):
return False
if not self.return_type.is_pyobject and not self.return_type.can_coerce_to_pyobject(env):
return False
return True
def create_to_py_utility_code(self, env):
# FIXME: it seems we're trying to coerce in more cases than we should
if self.to_py_function is not None:
return self.to_py_function
if not self.can_coerce_to_pyobject(env):
return False
from .UtilityCode import CythonUtilityCode
import re
safe_typename = re.sub('[^a-zA-Z0-9]', '__', self.declaration_code("", pyrex=1))
to_py_function = "__Pyx_CFunc_%s_to_py" % safe_typename
for arg in self.args:
if not arg.type.is_pyobject and not arg.type.create_from_py_utility_code(env):
return False
if not (self.return_type.is_pyobject or self.return_type.is_void or
self.return_type.create_to_py_utility_code(env)):
if not self.return_type.is_pyobject and not self.return_type.create_to_py_utility_code(env):
return False
def declared_type(ctype):
......@@ -2719,9 +2855,9 @@ class CFuncType(CType):
}
# FIXME: directives come from first defining environment and do not adapt for reuse
env.use_utility_code(CythonUtilityCode.load(
"cfunc.to_py", "CFuncConvert.pyx",
"cfunc.to_py", "CConvert.pyx",
outer_module_scope=env.global_scope(), # need access to types declared in module
context=context, compiler_directives=dict(env.directives)))
context=context, compiler_directives=dict(env.global_scope().directives)))
self.to_py_function = to_py_function
return True
......@@ -2871,11 +3007,12 @@ class ToPyStructUtilityCode(object):
requires = None
def __init__(self, type, forward_decl):
def __init__(self, type, forward_decl, env):
self.type = type
self.header = "static PyObject* %s(%s)" % (type.to_py_function,
type.declaration_code('s'))
self.forward_decl = forward_decl
self.env = env
def __eq__(self, other):
return isinstance(other, ToPyStructUtilityCode) and self.header == other.header
......@@ -2896,8 +3033,8 @@ class ToPyStructUtilityCode(object):
code.putln("res = PyDict_New(); if (res == NULL) return NULL;")
for member in self.type.scope.var_entries:
nameconst_cname = code.get_py_string_const(member.name, identifier=True)
code.putln("member = %s(s.%s); if (member == NULL) goto bad;" % (
member.type.to_py_function, member.cname))
code.putln("%s; if (member == NULL) goto bad;" % (
member.type.to_py_call_code('s.%s' % member.cname, 'member', member.type)))
code.putln("if (PyDict_SetItem(res, %s, member) < 0) goto bad;" % nameconst_cname)
code.putln("Py_DECREF(member);")
code.putln("return res;")
......@@ -2938,7 +3075,6 @@ class CStructOrUnionType(CType):
self.scope = scope
self.typedef_flag = typedef_flag
self.is_struct = kind == 'struct'
if self.is_struct:
self.to_py_function = "%s_to_py_%s" % (Naming.convert_func_prefix, self.cname)
self.from_py_function = "%s_from_py_%s" % (Naming.convert_func_prefix, self.cname)
self.exception_check = True
......@@ -2959,8 +3095,8 @@ class CStructOrUnionType(CType):
self.to_py_function = None
self._convert_to_py_code = False
return False
forward_decl = (self.entry.visibility != 'extern')
self._convert_to_py_code = ToPyStructUtilityCode(self, forward_decl)
forward_decl = self.entry.visibility != 'extern' and not self.typedef_flag
self._convert_to_py_code = ToPyStructUtilityCode(self, forward_decl, env)
env.use_utility_code(self._convert_to_py_code)
return True
......@@ -2980,12 +3116,15 @@ class CStructOrUnionType(CType):
return False
context = dict(
struct_type_decl=self.empty_declaration_code(),
struct_name=self.name,
var_entries=self.scope.var_entries,
funcname=self.from_py_function,
)
self._convert_from_py_code = TempitaUtilityCode.load(
"FromPyStructUtility", "TypeConversion.c", context=context)
from .UtilityCode import CythonUtilityCode
self._convert_from_py_code = CythonUtilityCode.load(
"FromPyStructUtility", "CConvert.pyx",
outer_module_scope=env.global_scope(), # need access to types declared in module
context=context)
env.use_utility_code(self._convert_from_py_code)
return True
......@@ -3152,7 +3291,8 @@ class CppClassType(CType):
'type': self.cname,
}
from .UtilityCode import CythonUtilityCode
env.use_utility_code(CythonUtilityCode.load(cls.replace('unordered_', '') + ".from_py", "CppConvert.pyx", context=context))
env.use_utility_code(CythonUtilityCode.load(
cls.replace('unordered_', '') + ".from_py", "CppConvert.pyx", context=context))
self.from_py_function = cname
return True
......@@ -3358,6 +3498,7 @@ class TemplatePlaceholderType(CType):
else:
return False
class CEnumType(CType):
# name string
# cname string or None
......@@ -3394,6 +3535,17 @@ class CEnumType(CType):
base_code = public_decl(base_code, dll_linkage)
return self.base_declaration_code(base_code, entity_code)
def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None):
rhs = "%s(%s)" % (
from_py_function or self.from_py_function,
source_code)
return '%s = %s;%s' % (
result_code,
typecast(self, c_long_type, rhs),
' %s' % code.error_goto_if(error_condition or self.error_condition(result_code), error_pos))
class CTupleType(CType):
# components [PyrexType]
......
......@@ -684,8 +684,8 @@ class Scope(object):
self.pyfunc_entries.append(entry)
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None):
cname=None, visibility='private', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False):
# Add an entry for a C function.
if not cname:
if visibility != 'private' or api:
......@@ -696,6 +696,9 @@ class Scope(object):
if entry:
if visibility != 'private' and visibility != entry.visibility:
warning(pos, "Function '%s' previously declared as '%s'" % (name, entry.visibility), 1)
if overridable != entry.is_overridable:
warning(pos, "Function '%s' previously declared as '%s'" % (
name, 'cpdef' if overridable else 'cdef'), 1)
if not entry.type.same_as(type):
if visibility == 'extern' and entry.visibility == 'extern':
can_override = False
......@@ -721,6 +724,7 @@ class Scope(object):
else:
entry = self.add_cfunction(name, type, pos, cname, visibility, modifiers)
entry.func_cname = cname
entry.is_overridable = overridable
if in_pxd and visibility != 'extern':
entry.defined_in_pxd = 1
if api:
......@@ -734,6 +738,13 @@ class Scope(object):
if utility_code:
assert not entry.utility_code, "duplicate utility code definition in entry %s (%s)" % (name, cname)
entry.utility_code = utility_code
if overridable:
# names of cpdef functions can be used as variables and can be assigned to
var_entry = Entry(name, cname, py_object_type) # FIXME: cname?
var_entry.is_variable = 1
var_entry.is_pyglobal = 1
var_entry.scope = entry.scope
entry.as_variable = var_entry
type.entry = entry
return entry
......@@ -892,13 +903,12 @@ class BuiltinScope(Scope):
else:
warning(pos, "undeclared name not builtin: %s" % name, 2)
def declare_builtin_cfunction(self, name, type, cname, python_equiv = None,
utility_code = None):
def declare_builtin_cfunction(self, name, type, cname, python_equiv=None, utility_code=None):
# If python_equiv == "*", the Python equivalent has the same name
# as the entry, otherwise it has the name specified by python_equiv.
name = EncodedString(name)
entry = self.declare_cfunction(name, type, None, cname, visibility='extern',
utility_code = utility_code)
utility_code=utility_code)
if python_equiv:
if python_equiv == "*":
python_equiv = name
......@@ -1225,8 +1235,8 @@ class ModuleScope(Scope):
return entry
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None):
cname=None, visibility='private', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False):
# Add an entry for a C function.
if not cname:
if visibility == 'extern' or (visibility == 'public' and defining):
......@@ -1243,8 +1253,9 @@ class ModuleScope(Scope):
entry.func_cname = cname
entry = Scope.declare_cfunction(
self, name, type, pos,
cname = cname, visibility = visibility, api = api, in_pxd = in_pxd,
defining = defining, modifiers = modifiers, utility_code = utility_code)
cname=cname, visibility=visibility, api=api, in_pxd=in_pxd,
defining=defining, modifiers=modifiers, utility_code=utility_code,
overridable=overridable)
return entry
def declare_global(self, name, pos):
......@@ -1683,8 +1694,10 @@ class StructOrUnionScope(Scope):
return entry
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = ()): # currently no utility code ...
cname=None, visibility='private', api=0, in_pxd=0,
defining=0, modifiers=(), overridable=False): # currently no utility code ...
if overridable:
error(pos, "C struct/union member cannot be declared 'cpdef'")
return self.declare_var(name, type, pos,
cname=cname, visibility=visibility)
......@@ -1953,8 +1966,8 @@ class CClassScope(ClassScope):
return entry
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'private', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None):
cname=None, visibility='private', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False):
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'")
args = type.args
......@@ -1989,8 +2002,7 @@ class CClassScope(ClassScope):
error(pos,
"C method '%s' not previously declared in definition part of"
" extension type" % name)
entry = self.add_cfunction(name, type, pos, cname,
visibility, modifiers)
entry = self.add_cfunction(name, type, pos, cname, visibility, modifiers)
if defining:
entry.func_cname = self.mangle(Naming.func_prefix, name)
entry.utility_code = utility_code
......@@ -2020,7 +2032,7 @@ class CClassScope(ClassScope):
# equivalent that must be accessible to support bound methods
name = EncodedString(name)
entry = self.declare_cfunction(name, type, None, cname, visibility='extern',
utility_code = utility_code)
utility_code=utility_code)
var_entry = Entry(name, name, py_object_type)
var_entry.is_variable = 1
var_entry.is_builtin = 1
......@@ -2127,40 +2139,10 @@ class CppClassScope(Scope):
"C++ class member cannot be a Python object")
return entry
def check_base_default_constructor(self, pos):
# Look for default constructors in all base classes.
if self.default_constructor is None:
entry = self.lookup(self.name)
if not entry.type.base_classes:
self.default_constructor = True
return
for base_class in entry.type.base_classes:
if base_class is PyrexTypes.error_type:
continue
temp_entry = base_class.scope.lookup_here("<init>")
found = False
if temp_entry is None:
continue
for alternative in temp_entry.all_alternatives():
type = alternative.type
if type.is_ptr:
type = type.base_type
if not type.args:
found = True
break
if not found:
self.default_constructor = temp_entry.scope.name
error(pos, "no matching function for call to " \
"%s::%s()" % (temp_entry.scope.name, temp_entry.scope.name))
elif not self.default_constructor:
error(pos, "no matching function for call to %s::%s()" %
(self.default_constructor, self.default_constructor))
def declare_cfunction(self, name, type, pos,
cname = None, visibility = 'extern', api = 0, in_pxd = 0,
defining = 0, modifiers = (), utility_code = None):
cname=None, visibility='extern', api=0, in_pxd=0,
defining=0, modifiers=(), utility_code=None, overridable=False):
if name in (self.name.split('::')[-1], '__init__') and cname is None:
self.check_base_default_constructor(pos)
cname = self.type.cname
name = '<init>'
type.return_type = PyrexTypes.InvisibleVoidType()
......@@ -2197,9 +2179,9 @@ class CppClassScope(Scope):
for base_entry in base_scope.cfunc_entries:
entry = self.declare_cfunction(base_entry.name, base_entry.type,
base_entry.pos, base_entry.cname,
base_entry.visibility, 0,
modifiers = base_entry.func_modifiers,
utility_code = base_entry.utility_code)
base_entry.visibility, api=0,
modifiers=base_entry.func_modifiers,
utility_code=base_entry.utility_code)
entry.is_inherited = 1
def specialize(self, values, type_entry):
......@@ -2218,7 +2200,7 @@ class CppClassScope(Scope):
e.type.specialize(values),
e.pos,
e.cname,
utility_code = e.utility_code)
utility_code=e.utility_code)
else:
scope.declare_var(entry.name,
entry.type.specialize(values),
......
......@@ -94,7 +94,10 @@ class CythonUtilityCode(Code.UtilityCodeBase):
return False
def _equality_params(self):
return self.impl, self.outer_module_scope, self.compiler_directives
outer_scope = self.outer_module_scope
while isinstance(outer_scope, NonManglingModuleScope):
outer_scope = outer_scope.outer_scope
return self.impl, outer_scope, self.compiler_directives
def __hash__(self):
return hash(self.impl)
......
......@@ -35,50 +35,39 @@ def test_gdb():
if have_gdb is not None:
return have_gdb
have_gdb = False
try:
p = subprocess.Popen(['gdb', '-v'], stdout=subprocess.PIPE)
have_gdb = True
p = subprocess.Popen(['gdb', '-nx', '--version'], stdout=subprocess.PIPE)
except OSError:
# gdb was not installed
have_gdb = False
# gdb not found
gdb_version = None
else:
gdb_version = p.stdout.read().decode('ascii', 'ignore')
p.wait()
p.stdout.close()
if have_gdb:
stdout, _ = p.communicate()
# Based on Lib/test/test_gdb.py
regex = "^GNU gdb [^\d]*(\d+)\.(\d+)"
gdb_version_number = list(map(int, re.search(regex, gdb_version).groups()))
regex = "GNU gdb [^\d]*(\d+)\.(\d+)"
gdb_version = re.match(regex, stdout.decode('ascii', 'ignore'))
if gdb_version:
gdb_version_number = list(map(int, gdb_version.groups()))
if gdb_version_number >= [7, 2]:
python_version_script = tempfile.NamedTemporaryFile(mode='w+')
try:
have_gdb = True
with tempfile.NamedTemporaryFile(mode='w+') as python_version_script:
python_version_script.write(
'python import sys; print("%s %s" % sys.version_info[:2])')
python_version_script.flush()
p = subprocess.Popen(['gdb', '-batch', '-x', python_version_script.name],
stdout=subprocess.PIPE)
stdout, _ = p.communicate()
try:
python_version = p.stdout.read().decode('ascii')
p.wait()
finally:
p.stdout.close()
try:
python_version_number = list(map(int, python_version.split()))
except ValueError:
internal_python_version = list(map(int, stdout.decode('ascii', 'ignore').split()))
if internal_python_version < [2, 6]:
have_gdb = False
finally:
python_version_script.close()
# Be Python 3 compatible
if (not have_gdb
or gdb_version_number < [7, 2]
or python_version_number < [2, 6]):
warnings.warn(
'Skipping gdb tests, need gdb >= 7.2 with Python >= 2.6')
except ValueError:
have_gdb = False
if not have_gdb:
warnings.warn('Skipping gdb tests, need gdb >= 7.2 with Python >= 2.6')
return have_gdb
......
......@@ -5,7 +5,7 @@
# than the standard C cast operator which can be written "<T>(expression)" in
# Cython.
cdef extern from *:
cdef extern from * nogil:
cdef T dynamic_cast[T](void *) except + # nullptr may also indicate failure
cdef T static_cast[T](void *)
cdef T reinterpret_cast[T](void *)
......
# cython.* namespace for pure mode.
__version__ = "0.21.1pre"
__version__ = "0.21.1"
# BEGIN shameless copy from Cython/minivect/minitypes.py
......@@ -365,6 +365,15 @@ to_repr = {
gs = globals()
# note: cannot simply name the unicode type here as 2to3 gets in the way and replaces it by str
try:
import __builtin__ as builtins
except ImportError: # Py3
import builtins
gs['unicode'] = typedef(getattr(builtins, 'unicode', str), 'unicode')
del builtins
for name in int_types:
reprname = to_repr(name, name)
gs[name] = typedef(py_int, reprname)
......
#################### FromPyStructUtility ####################
cdef extern from *:
ctypedef struct PyTypeObject:
char* tp_name
PyTypeObject *Py_TYPE(obj)
bint PyMapping_Check(obj)
object PyErr_Format(exc, const char *format, ...)
@cname("{{funcname}}")
cdef {{struct_name}} {{funcname}}(obj) except *:
cdef {{struct_name}} result
if not PyMapping_Check(obj):
PyErr_Format(TypeError, b"Expected %.16s, got %.200s", b"a mapping", Py_TYPE(obj).tp_name)
{{for member in var_entries:}}
try:
value = obj['{{member.name}}']
except KeyError:
raise ValueError("No value specified for struct attribute '{{member.name}}'")
result.{{member.cname}} = value
{{endfor}}
return result
#################### cfunc.to_py ####################
@cname("{{cname}}")
cdef object {{cname}}({{return_type.ctype}} (*f)({{ ', '.join(arg.type_cname for arg in args) }}) {{except_clause}}):
def wrap({{ ', '.join('{arg.ctype} {arg.name}'.format(arg=arg) for arg in args) }}):
"""wrap({{', '.join(('{arg.name}: {arg.type_displayname}'.format(arg=arg) if arg.type_displayname else arg.name) for arg in args)}}){{if return_type.type_displayname}} -> {{return_type.type_displayname}}{{endif}}"""
{{'' if return_type.type.is_void else 'return '}}f({{ ', '.join(arg.name for arg in args) }})
return wrap
#################### carray.from_py ####################
cdef extern from *:
object PyErr_Format(exc, const char *format, ...)
@cname("{{cname}}")
cdef int {{cname}}(object o, {{base_type}} *v, Py_ssize_t length) except -1:
cdef Py_ssize_t i = length
try:
i = len(o)
except (TypeError, OverflowError):
pass
if i == length:
for i, item in enumerate(o):
if i >= length:
break
v[i] = item
else:
i += 1 # convert index to length
if i == length:
return 0
PyErr_Format(
IndexError,
("too many values found during array assignment, expected %zd"
if i >= length else
"not enough values found during array assignment, expected %zd, got %zd"),
length, i)
#################### carray.to_py ####################
cdef extern from *:
void Py_INCREF(object o)
tuple PyTuple_New(Py_ssize_t size)
list PyList_New(Py_ssize_t size)
void PyTuple_SET_ITEM(object p, Py_ssize_t pos, object o)
void PyList_SET_ITEM(object p, Py_ssize_t pos, object o)
@cname("{{cname}}")
cdef inline list {{cname}}({{base_type}} *v, Py_ssize_t length):
cdef size_t i
cdef object value
l = PyList_New(length)
for i in range(<size_t>length):
value = v[i]
Py_INCREF(value)
PyList_SET_ITEM(l, i, value)
return l
@cname("{{to_tuple_cname}}")
cdef inline tuple {{to_tuple_cname}}({{base_type}} *v, Py_ssize_t length):
cdef size_t i
cdef object value
t = PyTuple_New(length)
for i in range(<size_t>length):
value = v[i]
Py_INCREF(value)
PyTuple_SET_ITEM(t, i, value)
return t
#################### cfunc.to_py ####################
@cname("{{cname}}")
cdef object {{cname}}({{return_type.ctype}} (*f)({{ ', '.join(arg.type_cname for arg in args) }}) {{except_clause}}):
def wrap({{ ', '.join('{arg.ctype} {arg.name}'.format(arg=arg) for arg in args) }}):
"""wrap({{', '.join(('{arg.name}: {arg.type_displayname}'.format(arg=arg) if arg.type_displayname else arg.name) for arg in args)}}){{if return_type.type_displayname}} -> {{return_type.type_displayname}}{{endif}}"""
{{'' if return_type.type.is_void else 'return '}}f({{ ', '.join(arg.name for arg in args) }})
return wrap
......@@ -587,7 +587,7 @@ static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject
if (size == 0)
return (*meth)(self, NULL);
PyErr_Format(PyExc_TypeError,
"%.200s() takes no arguments (%zd given)",
"%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)",
f->m_ml->ml_name, size);
return NULL;
}
......@@ -598,7 +598,7 @@ static PyObject * __Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject
if (size == 1)
return (*meth)(self, PyTuple_GET_ITEM(arg, 0));
PyErr_Format(PyExc_TypeError,
"%.200s() takes exactly one argument (%zd given)",
"%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)",
f->m_ml->ml_name, size);
return NULL;
}
......
......@@ -215,6 +215,8 @@ static CYTHON_INLINE float __PYX_NAN() {
}
#endif
#define __Pyx_void_to_None(void_result) (void_result, Py_INCREF(Py_None), Py_None)
// Work around clang bug http://stackoverflow.com/questions/21847816/c-invoke-nested-template-class-destructor
#ifdef __cplusplus
template<typename T>
......
......@@ -32,11 +32,11 @@ static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char*);
#define __Pyx_PyObject_AsSString(s) ((signed char*) __Pyx_PyObject_AsString(s))
#define __Pyx_PyObject_AsUString(s) ((unsigned char*) __Pyx_PyObject_AsString(s))
#define __Pyx_PyObject_FromUString(s) __Pyx_PyObject_FromString((const char*)s)
#define __Pyx_PyBytes_FromUString(s) __Pyx_PyBytes_FromString((const char*)s)
#define __Pyx_PyByteArray_FromUString(s) __Pyx_PyByteArray_FromString((const char*)s)
#define __Pyx_PyStr_FromUString(s) __Pyx_PyStr_FromString((const char*)s)
#define __Pyx_PyUnicode_FromUString(s) __Pyx_PyUnicode_FromString((const char*)s)
#define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s)
#define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s)
#define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s)
#define __Pyx_PyStr_FromCString(s) __Pyx_PyStr_FromString((const char*)s)
#define __Pyx_PyUnicode_FromCString(s) __Pyx_PyUnicode_FromString((const char*)s)
#if PY_MAJOR_VERSION < 3
static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u)
......@@ -311,42 +311,6 @@ static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
}
/////////////// FromPyStructUtility.proto ///////////////
static {{struct_type_decl}} {{funcname}}(PyObject *);
/////////////// FromPyStructUtility ///////////////
static {{struct_type_decl}} {{funcname}}(PyObject * o) {
{{struct_type_decl}} result;
PyObject *value = NULL;
if (!PyMapping_Check(o)) {
PyErr_Format(PyExc_TypeError, "Expected %.16s, got %.200s", "a mapping", Py_TYPE(o)->tp_name);
goto bad;
}
{{for member in var_entries:}}
{{py:attr = "result." + member.cname}}
value = PyObject_GetItem(o, PYIDENT("{{member.name}}"));
if (!value) {
PyErr_Format(PyExc_ValueError, \
"No value specified for struct attribute '%.{{max(200, len(member.name))}}s'", "{{member.name}}");
goto bad;
}
{{attr}} = {{member.type.from_py_function}}(value);
if ({{member.type.error_condition(attr)}})
goto bad;
Py_DECREF(value);
{{endfor}}
return result;
bad:
Py_XDECREF(value);
return result;
}
/////////////// ToPyCTupleUtility.proto ///////////////
static PyObject* {{funcname}}({{struct_type_decl}});
......@@ -395,6 +359,7 @@ bad:
return result;
}
/////////////// ObjectAsUCS4.proto ///////////////
static CYTHON_INLINE Py_UCS4 __Pyx_PyObject_AsPy_UCS4(PyObject*);
......
......@@ -28,6 +28,7 @@ clean:
@rm -f core */core
@rm -f Cython/Compiler/*.c
@rm -f Cython/Plex/*.c
@rm -f Cython/Tempita/*.c
@rm -f Cython/Runtime/refnanny.c
@(cd Demos; $(MAKE) clean)
......
docs/_static/cython-logo-light.png

4.68 KB | W: | H:

docs/_static/cython-logo-light.png

4.08 KB | W: | H:

docs/_static/cython-logo-light.png
docs/_static/cython-logo-light.png
docs/_static/cython-logo-light.png
docs/_static/cython-logo-light.png
  • 2-up
  • Swipe
  • Onion skin
docs/_static/cythonlogo.png

4.52 KB | W: | H:

docs/_static/cythonlogo.png

4.09 KB | W: | H:

docs/_static/cythonlogo.png
docs/_static/cythonlogo.png
docs/_static/cythonlogo.png
docs/_static/cythonlogo.png
  • 2-up
  • Swipe
  • Onion skin
docs/src/quickstart/htmlreport.png

50.4 KB | W: | H:

docs/src/quickstart/htmlreport.png

35.4 KB | W: | H:

docs/src/quickstart/htmlreport.png
docs/src/quickstart/htmlreport.png
docs/src/quickstart/htmlreport.png
docs/src/quickstart/htmlreport.png
  • 2-up
  • Swipe
  • Onion skin
docs/src/quickstart/ipython.png

125 KB | W: | H:

docs/src/quickstart/ipython.png

83.2 KB | W: | H:

docs/src/quickstart/ipython.png
docs/src/quickstart/ipython.png
docs/src/quickstart/ipython.png
docs/src/quickstart/ipython.png
  • 2-up
  • Swipe
  • Onion skin
docs/src/quickstart/sage.png

76.4 KB | W: | H:

docs/src/quickstart/sage.png

56.8 KB | W: | H:

docs/src/quickstart/sage.png
docs/src/quickstart/sage.png
docs/src/quickstart/sage.png
docs/src/quickstart/sage.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -158,7 +158,7 @@ Sharing C Functions
C functions defined at the top level of a module can be made available via
:keyword:`cimport` by putting headers for them in the ``.pxd`` file, for
example,::
example:
:file:`volume.pxd`::
......
......@@ -1714,7 +1714,7 @@ def main():
action="store_true", default=False,
help="only compile pyx to c, do not run C compiler or run the tests")
parser.add_option("--no-refnanny", dest="with_refnanny",
action="store_false", default=True,
action="store_false", default=not IS_PYPY,
help="do not regression test reference counting")
parser.add_option("--no-fork", dest="fork",
action="store_false", default=True,
......
# mode: error
ctypedef int[1] int_array
ctypedef int[2] int_array2
cdef int_array x, y
x = y # not an error
cdef int_array *x_ptr = &x
x_ptr[0] = y # not an error
cdef class A:
cdef int_array value
def __init__(self):
self.value = x # not an error
cdef int_array2 z
z = x # error
x = z # error
cdef enum:
SIZE = 2
ctypedef int[SIZE] int_array_dyn
cdef int_array_dyn d
d = z # not an error
_ERRORS = u"""
20:2: Assignment to slice of wrong length, expected 2, got 1
21:2: Assignment to slice of wrong length, expected 1, got 2
"""
......@@ -10,24 +10,7 @@ cdef void foo(obj):
obj = p2 # error
ctypedef int[1] int_array
cdef int_array x, y
x = y # error
cdef int_array *x_ptr = &x
x_ptr[0] = y # error
cdef class A:
cdef int_array value
def __init__(self):
self.value = x # error
_ERRORS = u"""
17:2: Assignment to non-lvalue 'x'
20:5: Assignment to non-lvalue of type 'int_array'
25:12: Assignment to non-lvalue of type 'int_array'
7:19: Cannot assign type 'char *' to 'int'
8:20: Cannot convert Python object to 'int *'
10:20: Cannot convert 'int *' to Python object
......
......@@ -1935,9 +1935,9 @@ def test_borrowed_slice():
5
5
"""
cdef int i, carray[10]
for i in range(10):
carray[i] = i
cdef int i
cdef int[10] carray
carray[:] = range(10)
_borrowed(carray)
_not_borrowed(carray)
_not_borrowed2(carray)
......
# this doesn't work - it would reassign the array address!
#
#def test_literal_list():
# cdef int a[5]
# a = [1,2,3,4,5]
# return (a[0], a[1], a[2], a[3], a[4])
# mode: run
def test_literal_list():
"""
>>> test_literal_list()
(1, 2, 3, 4, 5)
"""
cdef int a[5]
a = [1,2,3,4,5]
return (a[0], a[1], a[2], a[3], a[4])
def test_literal_list_slice_all():
"""
......@@ -128,20 +132,6 @@ def test_ptr_literal_list_slice_end():
a[:5] = [1,2,3,4,5]
return (a[0], a[1], a[2], a[3], a[4])
# tuples aren't supported (yet)
#
#def test_literal_tuple():
# cdef int a[5]
# a = (1,2,3,4,5)
# return (a[0], a[1], a[2], a[3], a[4])
# this would be nice to have:
#
#def test_list(list l):
# cdef int a[5]
# a[:] = l
# return (a[0], a[1], a[2], a[3], a[4])
def test_multiple_from_slice():
"""
>>> test_multiple_from_slice()
......@@ -159,3 +149,169 @@ def test_slice_from_multiple():
cdef int *a = [6,5,4,3,2,1]
a[1:4] = -1, -2, -3
return a[0], a[1], a[2], a[3], a[4], a[5]
def test_literal_tuple():
"""
>>> test_literal_tuple()
(1, 2, 3, 4, 5)
"""
cdef int a[5]
a = (1,2,3,4,5)
return (a[0], a[1], a[2], a[3], a[4])
def test_list(list l):
"""
>>> test_list([1, 2, 3, 4, 5])
(1, 2, 3, 4, 5)
"""
cdef int a[5]
a[:] = l
return (a[0], a[1], a[2], a[3], a[4])
def assign_all_from_pointer():
"""
>>> assign_all_from_pointer()
(1, 2, 3, 4, 5)
"""
cdef int *v = [1, 2, 3, 4, 5]
cdef int[5] a
a = v
return (a[0], a[1], a[2], a[3], a[4])
def assign_full_from_pointer():
"""
>>> assign_full_from_pointer()
(1, 2, 3, 4, 5)
"""
cdef int *v = [1, 2, 3, 4, 5]
cdef int[5] a
a[:] = v
return (a[0], a[1], a[2], a[3], a[4])
def assign_slice_end_from_pointer():
"""
>>> assign_slice_end_from_pointer()
(1, 2, 3, 4, 123)
"""
cdef int *v = [1, 2, 3, 4, 5]
cdef int[5] a
a[4] = 123
a[:4] = v
return (a[0], a[1], a[2], a[3], a[4])
def assign_slice_start_from_pointer():
"""
>>> assign_slice_start_from_pointer()
(123, 234, 1, 2, 3)
"""
cdef int *v = [1, 2, 3, 4, 5]
cdef int[5] a
a[0] = 123
a[1] = 234
a[2:] = v
return (a[0], a[1], a[2], a[3], a[4])
def assign_slice_start_end_from_pointer():
"""
>>> assign_slice_start_end_from_pointer()
(123, 234, 1, 2, 345)
"""
cdef int *v = [1, 2, 3, 4, 5]
cdef int[5] a
a[0] = 123
a[1] = 234
a[4] = 345
a[2:4] = v
return (a[0], a[1], a[2], a[3], a[4])
'''
# FIXME: make this work:
def assign_slice_start_end_from_sliced_pointer():
"""
>>> assign_slice_start_end_from_sliced_pointer()
(123, 234, 3, 4, 345)
"""
cdef int *v = [1, 2, 3, 4, 5]
cdef int[5] a
a[0] = 123
a[1] = 234
a[4] = 345
a[2:4] = v[2:4]
return (a[0], a[1], a[2], a[3], a[4])
def assign_from_longer_array_slice():
"""
>>> assign_from_longer_array_slice()
[3, 4, 5]
"""
cdef int[5] a
cdef int[3] b
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
b[0] = 11
b[1] = 12
b[2] = 13
b = a[2:]
return b
'''
def assign_slice_from_shorter_array():
"""
>>> assign_slice_from_shorter_array()
[1, 11, 12, 13, 5]
"""
cdef int[5] a
cdef int[3] b
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
b[0] = 11
b[1] = 12
b[2] = 13
a[1:4] = b
return a
cdef enum:
SIZE = 2
ctypedef int[SIZE] int_array_dyn
def assign_ptr_to_unknown_csize():
"""
>>> assign_ptr_to_unknown_csize()
[1, 2]
"""
cdef int* v = [1, 2, 3, 4, 5]
cdef int_array_dyn d
d = v
return d
def assign_to_wrong_csize():
"""
>>> assign_to_wrong_csize()
Traceback (most recent call last):
ValueError: Assignment to slice of wrong length, expected 3, got 2
"""
cdef int_array_dyn d
cdef int v[3]
v[0] = 1
v[1] = 2
v[2] = 3
d = v
return d
def from_int_array():
"""
>>> from_int_array()
[1, 2, 3]
"""
cdef int[3] v
v[0] = 1
v[1] = 2
v[2] = 3
return v
cpdef tuple tuple_from_int_array():
"""
>>> tuple_from_int_array()
(1, 2, 3)
"""
cdef int[3] v
v[0] = 1
v[1] = 2
v[2] = 3
assert isinstance(<tuple>v, tuple)
return v
cdef extern from "stdint.h":
ctypedef unsigned long uint32_t
def from_typedef_int_array():
"""
>>> from_typedef_int_array()
[1, 2, 3]
"""
cdef uint32_t[3] v
v[0] = 1
v[1] = 2
v[2] = 3
return v
cpdef tuple tuple_from_typedef_int_array():
"""
>>> tuple_from_typedef_int_array()
(1, 2, 3)
"""
cdef uint32_t[3] v
v[0] = 1
v[1] = 2
v[2] = 3
return v
def from_int_array_array():
"""
>>> from_int_array_array()
[[11, 12, 13], [21, 22, 23]]
"""
cdef int[2][3] v
v[0][0] = 11
v[0][1] = 12
v[0][2] = 13
v[1][0] = 21
v[1][1] = 22
v[1][2] = 23
return v
ctypedef struct MyStructType:
int x
double y
cdef struct MyStruct:
int x
double y
def from_struct_array():
"""
>>> a, b = from_struct_array()
>>> a['x'], a['y']
(1, 2.0)
>>> b['x'], b['y']
(3, 4.0)
"""
cdef MyStructType[2] v
cdef MyStruct[2] w
v[0] = MyStructType(1, 2)
v[1] = MyStructType(3, 4)
assert isinstance(<tuple>v, tuple)
assert isinstance(v, list)
w[0] = MyStruct(1, 2)
w[1] = MyStruct(3, 4)
assert (<object>w) == v
assert w == (<object>v)
return v
def to_int_array(x):
"""
>>> to_int_array([1, 2, 3])
(1, 2, 3)
>>> to_int_array([1, 2])
Traceback (most recent call last):
IndexError: not enough values found during array assignment, expected 3, got 2
>>> to_int_array([1, 2, 3, 4])
Traceback (most recent call last):
IndexError: too many values found during array assignment, expected 3
"""
cdef int[3] v = x
return v[0], v[1], v[2]
def to_int_array_array(x):
"""
>>> to_int_array_array([[1, 2, 3], [4, 5, 6]])
(1, 2, 3, 4, 5, 6)
>>> to_int_array_array(iter([[1, 2, 3], [4, 5, 6]]))
(1, 2, 3, 4, 5, 6)
>>> to_int_array_array([[1, 2, 3]])
Traceback (most recent call last):
IndexError: not enough values found during array assignment, expected 2, got 1
>>> to_int_array_array(iter([[1, 2, 3]]))
Traceback (most recent call last):
IndexError: not enough values found during array assignment, expected 2, got 1
>>> to_int_array_array([[1, 2, 3], [4, 5]])
Traceback (most recent call last):
IndexError: not enough values found during array assignment, expected 3, got 2
>>> to_int_array_array(iter([[1, 2, 3], [4, 5]]))
Traceback (most recent call last):
IndexError: not enough values found during array assignment, expected 3, got 2
>>> to_int_array_array([[1, 2, 3, 4], [5, 6, 7]])
Traceback (most recent call last):
IndexError: too many values found during array assignment, expected 3
>>> to_int_array_array(iter([[1, 2, 3, 4], [5, 6, 7]]))
Traceback (most recent call last):
IndexError: too many values found during array assignment, expected 3
"""
cdef int[2][3] v = x
return v[0][0], v[0][1], v[0][2], v[1][0], v[1][1], v[1][2]
'''
# FIXME: this isn't currently allowed
cdef enum:
SIZE_A = 2
SIZE_B = 3
def to_int_array_array_enumsize(x):
"""
>>> to_int_array_array([[1, 2, 3], [4, 5, 6]])
(1, 2, 3, 4, 5, 6)
>>> to_int_array_array(iter([[1, 2, 3], [4, 5, 6]]))
(1, 2, 3, 4, 5, 6)
>>> to_int_array([1, 2])
Traceback (most recent call last):
IndexError: not enough values found during array assignment, expected 3, got 2
>>> to_int_array([1, 2, 3, 4])
Traceback (most recent call last):
IndexError: too many values found during array assignment, expected 3
"""
cdef int[SIZE_A][SIZE_B] v = x
return v[0][0], v[0][1], v[0][2], v[1][0], v[1][1], v[1][2]
'''
'''
# FIXME: this isn't currently supported
def array_as_argument(int[2] x):
"""
>>> array_as_argument([1, 2])
(1, 2)
"""
return x[0], x[1]
'''
def to_int_array_slice(x):
"""
>>> to_int_array_slice([1, 2, 3])
(1, 2, 3)
>>> to_int_array_slice([1, 2])
Traceback (most recent call last):
IndexError: not enough values found during array assignment, expected 3, got 2
>>> to_int_array_slice([1, 2, 3, 4])
Traceback (most recent call last):
IndexError: too many values found during array assignment, expected 3
"""
cdef int[3] v
v[:] = x[:3]
assert v[0] == x[0]
assert v[1] == x[1]
assert v[2] == x[2]
v[:3] = [0, 0, 0]
assert v[0] == 0
assert v[1] == 0
assert v[2] == 0
v[:] = x
return v[0], v[1], v[2]
def iterable_to_int_array(x):
"""
>>> iterable_to_int_array(iter([1, 2, 3]))
(1, 2, 3)
>>> iterable_to_int_array(iter([1, 2]))
Traceback (most recent call last):
IndexError: not enough values found during array assignment, expected 3, got 2
>>> iterable_to_int_array(iter([1, 2, 3, 4]))
Traceback (most recent call last):
IndexError: too many values found during array assignment, expected 3
"""
cdef int[3] v
v[:] = x
return v[0], v[1], v[2]
def to_struct_array(x):
"""
>>> a, b = to_struct_array(({'x': 1, 'y': 2}, {'x': 3, 'y': 4}))
>>> a['x'], a['y']
(1, 2.0)
>>> b['x'], b['y']
(3, 4.0)
"""
cdef MyStructType[2] v
v[:] = x
cdef MyStruct[2] w
w[:] = x
assert w[0].x == v[0].x
assert w[0].y == v[0].y
assert w[1].x == v[1].x
assert w[1].y == v[1].y
return v[0], w[1]
def to_struct_array_array(x):
"""
>>> (a1, a2, a3), (b1, b2, b3) = to_struct_array_array([
... ({'x': 11, 'y': 12}, {'x': 13, 'y': 14}, {'x': 15, 'y': 16}),
... ({'x': 21, 'y': 22}, {'x': 23, 'y': 24}, {'x': 25, 'y': 26}),
... ])
>>> a1['x'], a1['y']
(11, 12.0)
>>> b3['x'], b3['y']
(25, 26.0)
"""
cdef MyStructType[2][3] v = x
return v[0], v[1]
cpdef void unraisable():
"""
>>> unraisable()
here
"""
print('here')
raise RuntimeError()
cpdef void raisable() except *:
"""
>>> raisable()
Traceback (most recent call last):
...
RuntimeError
"""
print('here')
raise RuntimeError()
......@@ -14,6 +14,8 @@ Traceback (most recent call last):
NameError: name 'log' is not defined
>>> strchr('abcabc', ord('c'))
'cabc'
>>> strchr(needle=ord('c'), haystack='abcabc')
'cabc'
"""
cdef extern from "math.h":
......
cdef extern from "math.h":
cpdef double pxd_sqrt "sqrt"(double)
"""
>>> pxd_sqrt(9)
3.0
"""
import cython
if not cython.compiled:
from math import sqrt as pxd_sqrt
@cython.test_assert_path_exists('//SimpleCallNode/NameNode[@type.is_pyobject = False]')
def call_pxd_sqrt(x):
"""
>>> call_pxd_sqrt(9)
3.0
"""
return pxd_sqrt(x)
......@@ -7,7 +7,10 @@ cdef extern from "shapes.h" namespace "shapes":
cdef cppclass Shape:
float area()
cdef cppclass Circle(Shape):
cdef cppclass Ellipse(Shape):
Ellipse(int a, int b) except +
cdef cppclass Circle(Ellipse):
int radius
Circle(int r) except +
......
......@@ -41,10 +41,16 @@ namespace shapes {
int side;
};
class Circle : public Shape {
class Ellipse : public Shape {
public:
Circle(int radius) { this->radius = radius; }
float area() const { return 3.1415926535897931f * radius; }
Ellipse(int a, int b) { this->a = a; this->b = b; }
float area() const { return 3.1415926535897931f * a * b; }
int a, b;
};
class Circle : public Ellipse {
public:
Circle(int radius) : Ellipse(radius, radius) { this->radius = radius; }
int radius;
};
......
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