Commit 20fe9ab6 authored by Dag Sverre Seljebotn's avatar Dag Sverre Seljebotn

Big merge of -devel

parents 082156ee afb437d5
...@@ -13,3 +13,4 @@ a4abf0156540db4d3ebaa95712b65811c43c5acb 0.11-beta ...@@ -13,3 +13,4 @@ a4abf0156540db4d3ebaa95712b65811c43c5acb 0.11-beta
4497f635d5fdbd38ebb841be4869fbfa2bbfdbb6 0.11.1.alpha 4497f635d5fdbd38ebb841be4869fbfa2bbfdbb6 0.11.1.alpha
7bc36a0f81723117a19f92ffde1676a0884fef65 0.11.1.beta 7bc36a0f81723117a19f92ffde1676a0884fef65 0.11.1.beta
6454db601984145f38e28d34176fca8a3a22329c 0.11.1 6454db601984145f38e28d34176fca8a3a22329c 0.11.1
af6f1bed8cd40a2edefb57d3eacbc9274a8788b4 0.11.2.rc1
...@@ -15,12 +15,6 @@ except NameError: ...@@ -15,12 +15,6 @@ except NameError:
import textwrap import textwrap
# Code cleanup ideas:
# - One could be more smart about casting in some places
# - Start using CCodeWriters to generate utility functions
# - Create a struct type per ndim rather than keeping loose local vars
def dedent(text, reindent=0): def dedent(text, reindent=0):
text = textwrap.dedent(text) text = textwrap.dedent(text)
if reindent > 0: if reindent > 0:
...@@ -240,29 +234,32 @@ def put_unpack_buffer_aux_into_scope(buffer_aux, mode, code): ...@@ -240,29 +234,32 @@ def put_unpack_buffer_aux_into_scope(buffer_aux, mode, code):
def put_acquire_arg_buffer(entry, code, pos): def put_acquire_arg_buffer(entry, code, pos):
code.globalstate.use_utility_code(acquire_utility_code) code.globalstate.use_utility_code(acquire_utility_code)
buffer_aux = entry.buffer_aux buffer_aux = entry.buffer_aux
getbuffer_cname = get_getbuffer_code(entry.type.dtype, code) getbuffer = get_getbuffer_call(code, entry.cname, buffer_aux, entry.type)
# Acquire any new buffer # Acquire any new buffer
code.putln(code.error_goto_if("%s((PyObject*)%s, &%s, %s, %d, %d) == -1" % ( code.putln("{")
getbuffer_cname, code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % entry.type.dtype.struct_nesting_depth())
entry.cname, code.putln(code.error_goto_if("%s == -1" % getbuffer, pos))
entry.buffer_aux.buffer_info_var.cname, code.putln("}")
get_flags(buffer_aux, entry.type),
entry.type.ndim,
int(entry.type.cast)), pos))
# An exception raised in arg parsing cannot be catched, so no # An exception raised in arg parsing cannot be catched, so no
# need to care about the buffer then. # need to care about the buffer then.
put_unpack_buffer_aux_into_scope(buffer_aux, entry.type.mode, code) put_unpack_buffer_aux_into_scope(buffer_aux, entry.type.mode, code)
#def put_release_buffer_normal(entry, code):
# code.putln("if (%s != Py_None) PyObject_ReleaseBuffer(%s, &%s);" % (
# entry.cname,
# entry.cname,
# entry.buffer_aux.buffer_info_var.cname))
def get_release_buffer_code(entry): def get_release_buffer_code(entry):
return "__Pyx_SafeReleaseBuffer(&%s)" % entry.buffer_aux.buffer_info_var.cname return "__Pyx_SafeReleaseBuffer(&%s)" % entry.buffer_aux.buffer_info_var.cname
def get_getbuffer_call(code, obj_cname, buffer_aux, buffer_type):
ndim = buffer_type.ndim
cast = int(buffer_type.cast)
flags = get_flags(buffer_aux, buffer_type)
bufstruct = buffer_aux.buffer_info_var.cname
dtype_typeinfo = get_type_information_cname(code, buffer_type.dtype)
return ("__Pyx_GetBufferAndValidate(&%(bufstruct)s, "
"(PyObject*)%(obj_cname)s, &%(dtype_typeinfo)s, %(flags)s, %(ndim)d, "
"%(cast)d, __pyx_stack)" % locals())
def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type, def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
is_initialized, pos, code): is_initialized, pos, code):
""" """
...@@ -283,12 +280,10 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type, ...@@ -283,12 +280,10 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
bufstruct = buffer_aux.buffer_info_var.cname bufstruct = buffer_aux.buffer_info_var.cname
flags = get_flags(buffer_aux, buffer_type) flags = get_flags(buffer_aux, buffer_type)
getbuffer = "%s((PyObject*)%%s, &%s, %s, %d, %d)" % (get_getbuffer_code(buffer_type.dtype, code), code.putln("{") # Set up necesarry stack for getbuffer
# note: object is filled in later (%%s) code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % buffer_type.dtype.struct_nesting_depth())
bufstruct,
flags, getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below
buffer_type.ndim,
int(buffer_type.cast))
if is_initialized: if is_initialized:
# Release any existing buffer # Release any existing buffer
...@@ -333,8 +328,9 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type, ...@@ -333,8 +328,9 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
put_unpack_buffer_aux_into_scope(buffer_aux, buffer_type.mode, code) put_unpack_buffer_aux_into_scope(buffer_aux, buffer_type.mode, code)
code.putln('}') code.putln('}')
code.putln("}") # Release stack
def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, code): def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives, pos, code):
""" """
Generates code to process indices and calculate an offset into Generates code to process indices and calculate an offset into
a buffer. Returns a C string which gives a pointer which can be a buffer. Returns a C string which gives a pointer which can be
...@@ -349,9 +345,9 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod ...@@ -349,9 +345,9 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod
""" """
bufaux = entry.buffer_aux bufaux = entry.buffer_aux
bufstruct = bufaux.buffer_info_var.cname bufstruct = bufaux.buffer_info_var.cname
negative_indices = entry.type.negative_indices negative_indices = directives['wraparound'] and entry.type.negative_indices
if options['boundscheck']: if directives['boundscheck']:
# Check bounds and fix negative indices. # Check bounds and fix negative indices.
# We allocate a temporary which is initialized to -1, meaning OK (!). # We allocate a temporary which is initialized to -1, meaning OK (!).
# If an error occurs, the temp is set to the dimension index the # If an error occurs, the temp is set to the dimension index the
...@@ -417,7 +413,11 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod ...@@ -417,7 +413,11 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod
params.append(s.cname) params.append(s.cname)
# Make sure the utility code is available # Make sure the utility code is available
code.globalstate.use_code_from(funcgen, name=funcname, nd=nd) if funcname not in code.globalstate.utility_codes:
code.globalstate.utility_codes.add(funcname)
protocode = code.globalstate['utility_code_proto']
defcode = code.globalstate['utility_code_def']
funcgen(protocode, defcode, name=funcname, nd=nd)
ptr_type = entry.type.buffer_ptr_type ptr_type = entry.type.buffer_ptr_type
ptrcode = "%s(%s, %s.buf, %s)" % (funcname, ptrcode = "%s(%s, %s.buf, %s)" % (funcname,
...@@ -489,216 +489,6 @@ def buf_lookup_fortran_code(proto, defin, name, nd): ...@@ -489,216 +489,6 @@ def buf_lookup_fortran_code(proto, defin, name, nd):
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(1, nd)]) offset = " + ".join(["i%d * s%d" % (i, i) for i in range(1, nd)])
proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, 0)) proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, 0))
#
# Utils for creating type string checkers
#
def mangle_dtype_name(dtype):
# Use prefixes to seperate user defined types from builtins
# (consider "typedef float unsigned_int")
if dtype.is_pyobject:
return "object"
elif dtype.is_ptr:
return "ptr"
else:
if dtype.is_typedef or dtype.is_struct_or_union:
prefix = "nn_"
else:
prefix = ""
return prefix + dtype.declaration_code("").replace(" ", "_")
def get_typestringchecker(code, dtype):
"""
Returns the name of a typestring checker with the given type; emitting
it to code if needed.
"""
name = "__Pyx_CheckTypestring_%s" % mangle_dtype_name(dtype)
code.globalstate.use_code_from(create_typestringchecker,
name,
dtype=dtype)
return name
def create_typestringchecker(protocode, defcode, name, dtype):
def put_assert(cond, msg):
defcode.putln("if (!(%s)) {" % cond)
defcode.putln('PyErr_Format(PyExc_ValueError, "Buffer dtype mismatch (%s)", __Pyx_DescribeTokenInFormatString(ts));' % msg)
defcode.putln("return NULL;")
defcode.putln("}")
if dtype.is_error: return
simple = dtype.is_simple_buffer_dtype()
complex_possible = dtype.is_struct_or_union and dtype.can_be_complex()
# Cannot add utility code recursively...
if not simple:
dtype_t = dtype.declaration_code("")
protocode.globalstate.use_utility_code(parse_typestring_repeat_code)
fields = dtype.scope.var_entries
# divide fields into blocks of equal type (for repeat count)
field_blocks = [] # of (n, type, checkerfunc)
n = 0
prevtype = None
for f in fields:
if n and f.type != prevtype:
field_blocks.append((n, prevtype, get_typestringchecker(protocode, prevtype)))
n = 0
prevtype = f.type
n += 1
field_blocks.append((n, f.type, get_typestringchecker(protocode, f.type)))
protocode.putln("static const char* %s(const char* ts); /*proto*/" % name)
defcode.putln("static const char* %s(const char* ts) {" % name)
if simple:
defcode.putln("int ok;")
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
defcode.putln("if (*ts == '1') ++ts;")
if dtype.is_pyobject:
defcode.putln("ok = (*ts == 'O');")
else:
# Cannot trust declared size; but rely on int vs float and
# signed/unsigned to be correctly declared. Use a switch statement
# on all possible format codes to validate that the size is ok.
# (Note that many codes may map to same size, e.g. 'i' and 'l'
# may both be four bytes).
ctype = dtype.declaration_code("")
defcode.putln("switch (*ts) {")
if dtype.is_int:
types = [
('b', 'char'), ('h', 'short'), ('i', 'int'),
('l', 'long'), ('q', 'long long')
]
elif dtype.is_float:
types = [('f', 'float'), ('d', 'double'), ('g', 'long double')]
else:
assert False
if dtype.signed == 0:
for char, against in types:
defcode.putln("case '%s': ok = (sizeof(%s) == sizeof(unsigned %s) && (%s)-1 > 0); break;" %
(char.upper(), ctype, against, ctype))
else:
for char, against in types:
defcode.putln("case '%s': ok = (sizeof(%s) == sizeof(%s) && (%s)-1 < 0); break;" %
(char, ctype, against, ctype))
defcode.putln("default: ok = 0;")
defcode.putln("}")
put_assert("ok", "expected %s, got %%s" % dtype)
defcode.putln("++ts;")
elif complex_possible:
# Could be a struct representing a complex number, so allow
# for parsing a "Zf" spec.
real_t, imag_t = [x.type for x in fields]
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
defcode.putln("if (*ts == '1') ++ts;")
defcode.putln("if (*ts == 'Z') {")
if len(field_blocks) == 2:
# Different float type, sizeof check needed
defcode.putln("if (sizeof(%s) != sizeof(%s)) {" % (
real_t.declaration_code(""),
imag_t.declaration_code("")))
defcode.putln('PyErr_SetString(PyExc_ValueError, "Cannot store complex number in \'%s\' as \'%s\' differs from \'%s\' in size.");' % (
dtype, real_t, imag_t))
defcode.putln("return NULL;")
defcode.putln("}")
check_real, check_imag = [x[2] for x in field_blocks]
else:
assert len(field_blocks) == 1
check_real = check_imag = field_blocks[0][2]
defcode.putln("ts = %s(ts + 1); if (!ts) return NULL;" % check_real)
defcode.putln("} else {")
defcode.putln("ts = %s(ts); if (!ts) return NULL;" % check_real)
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
defcode.putln("ts = %s(ts); if (!ts) return NULL;" % check_imag)
defcode.putln("}")
else:
defcode.putln("int n, count;")
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
next_types = [x[1] for x in field_blocks[1:]] + ["end"]
for (n, type, checker), next_type in zip(field_blocks, next_types):
if n == 1:
defcode.putln("if (*ts == '1') ++ts;")
else:
defcode.putln("n = %d;" % n);
defcode.putln("do {")
defcode.putln("ts = __Pyx_ParseTypestringRepeat(ts, &count); n -= count;")
put_assert("n >= 0", "expected %s, got %%s" % next_type)
simple = type.is_simple_buffer_dtype()
if not simple:
put_assert("*ts == 'T' && *(ts+1) == '{'", "expected %s, got %%s" % type)
defcode.putln("ts += 2;")
defcode.putln("ts = %s(ts); if (!ts) return NULL;" % checker)
if not simple:
put_assert("*ts == '}'", "expected end of %s struct, got %%s" % type)
defcode.putln("++ts;")
if n > 1:
defcode.putln("} while (n > 0);");
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
defcode.putln("return ts;")
defcode.putln("}")
def get_getbuffer_code(dtype, code):
"""
Generate a utility function for getting a buffer for the given dtype.
The function will:
- Call PyObject_GetBuffer
- Check that ndim matched the expected value
- Check that the format string is right
- Set suboffsets to all -1 if it is returned as NULL.
"""
name = "__Pyx_GetBuffer_%s" % mangle_dtype_name(dtype)
if not code.globalstate.has_code(name):
code.globalstate.use_utility_code(acquire_utility_code)
typestringchecker = get_typestringchecker(code, dtype)
dtype_name = str(dtype)
dtype_cname = dtype.declaration_code("")
utilcode = UtilityCode(proto = dedent("""
static int %s(PyObject* obj, Py_buffer* buf, int flags, int nd, int cast); /*proto*/
""") % name, impl = dedent("""
static int %(name)s(PyObject* obj, Py_buffer* buf, int flags, int nd, int cast) {
const char* ts;
if (obj == Py_None) {
__Pyx_ZeroBuffer(buf);
return 0;
}
buf->buf = NULL;
if (__Pyx_GetBuffer(obj, buf, flags) == -1) goto fail;
if (buf->ndim != nd) {
__Pyx_BufferNdimError(buf, nd);
goto fail;
}
if (!cast) {
ts = buf->format;
ts = __Pyx_ConsumeWhitespace(ts);
if (!ts) goto fail;
ts = %(typestringchecker)s(ts);
if (!ts) goto fail;
ts = __Pyx_ConsumeWhitespace(ts);
if (!ts) goto fail;
if (*ts != 0) {
PyErr_Format(PyExc_ValueError,
"Buffer dtype mismatch (expected end, got %%s)",
__Pyx_DescribeTokenInFormatString(ts));
goto fail;
}
} else {
if (buf->itemsize != sizeof(%(dtype_cname)s)) {
PyErr_SetString(PyExc_ValueError,
"Attempted cast of buffer to datatype of different size.");
goto fail;
}
}
if (buf->suboffsets == NULL) buf->suboffsets = __Pyx_minusones;
return 0;
fail:;
__Pyx_ZeroBuffer(buf);
return -1;
}""") % locals())
code.globalstate.use_utility_code(utilcode, name)
return name
def use_py2_buffer_functions(env): def use_py2_buffer_functions(env):
# Emulation of PyObject_GetBuffer and PyBuffer_Release for Python 2. # Emulation of PyObject_GetBuffer and PyBuffer_Release for Python 2.
...@@ -779,9 +569,92 @@ def use_py2_buffer_functions(env): ...@@ -779,9 +569,92 @@ def use_py2_buffer_functions(env):
#endif #endif
"""), impl = code), codename) """), impl = code), codename)
#
# Static utility code def mangle_dtype_name(dtype):
# # Use prefixes to seperate user defined types from builtins
# (consider "typedef float unsigned_int")
if dtype.is_pyobject:
return "object"
elif dtype.is_ptr:
return "ptr"
else:
if dtype.is_typedef or dtype.is_struct_or_union:
prefix = "nn_"
else:
prefix = ""
return prefix + dtype.declaration_code("").replace(" ", "_")
def get_type_information_cname(code, dtype, maxdepth=None):
# Output the run-time type information (__Pyx_TypeInfo) for given dtype,
# and return the name of the type info struct.
#
# Structs with two floats of the same size are encoded as complex numbers.
# One can seperate between complex numbers declared as struct or with native
# encoding by inspecting to see if the fields field of the type is
# filled in.
namesuffix = mangle_dtype_name(dtype)
name = "__Pyx_TypeInfo_%s" % namesuffix
structinfo_name = "__Pyx_StructFields_%s" % namesuffix
if dtype.is_error: return "<error>"
# It's critical that walking the type info doesn't use more stack
# depth than dtype.struct_nesting_depth() returns, so use an assertion for this
if maxdepth is None: maxdepth = dtype.struct_nesting_depth()
if maxdepth <= 0:
assert False
if name not in code.globalstate.utility_codes:
code.globalstate.utility_codes.add(name)
typecode = code.globalstate['typeinfo']
complex_possible = dtype.is_struct_or_union and dtype.can_be_complex()
declcode = dtype.declaration_code("")
if dtype.is_simple_buffer_dtype():
structinfo_name = "NULL"
elif dtype.is_struct:
fields = dtype.scope.var_entries
# Must pre-call all used types in order not to recurse utility code
# writing.
assert len(fields) > 0
types = [get_type_information_cname(code, f.type, maxdepth - 1)
for f in fields]
typecode.putln("static __Pyx_StructField %s[] = {" % structinfo_name, safe=True)
for f, typeinfo in zip(fields, types):
typecode.putln(' {&%s, "%s", offsetof(%s, %s)},' %
(typeinfo, f.name, dtype.declaration_code(""), f.cname), safe=True)
typecode.putln(' {NULL, NULL, 0}', safe=True)
typecode.putln("};", safe=True)
else:
assert False
rep = str(dtype)
if dtype.is_int:
if dtype.signed == 0:
typegroup = 'U'
else:
typegroup = 'I'
elif complex_possible or dtype.is_complex:
typegroup = 'C'
elif dtype.is_float:
typegroup = 'R'
elif dtype.is_struct:
typegroup = 'S'
elif dtype.is_pyobject:
typegroup = 'O'
else:
print dtype
assert False
typecode.putln(('static __Pyx_TypeInfo %s = { "%s", %s, sizeof(%s), \'%s\' };'
) % (name,
rep,
structinfo_name,
declcode,
typegroup,
), safe=True)
return name
# Utility function to set the right exception # Utility function to set the right exception
...@@ -798,122 +671,503 @@ static void __Pyx_RaiseBufferIndexError(int axis) { ...@@ -798,122 +671,503 @@ static void __Pyx_RaiseBufferIndexError(int axis) {
""") """)
parse_typestring_repeat_code = UtilityCode(
proto = """
""",
impl = """
""")
raise_buffer_fallback_code = UtilityCode(
proto = """
static void __Pyx_RaiseBufferFallbackError(void); /*proto*/
""",
impl = """
static void __Pyx_RaiseBufferFallbackError(void) {
PyErr_Format(PyExc_ValueError,
"Buffer acquisition failed on assignment; and then reacquiring the old buffer failed too!");
}
""")
#
# Buffer format string checking
# #
# Buffer type checking. Utility code for checking that acquired # Buffer type checking. Utility code for checking that acquired
# buffers match our assumptions. We only need to check ndim and # buffers match our assumptions. We only need to check ndim and
# the format string; the access mode/flags is checked by the # the format string; the access mode/flags is checked by the
# exporter. # exporter.
# #
acquire_utility_code = UtilityCode( # The alignment code is copied from _struct.c in Python.
proto = """\ acquire_utility_code = UtilityCode(proto="""
/* Run-time type information about structs used with buffers */
struct __Pyx_StructField_;
typedef struct {
const char* name; /* for error messages only */
struct __Pyx_StructField_* fields;
size_t size; /* sizeof(type) */
char typegroup; /* _R_eal, _C_omplex, Signed _I_nt, _U_nsigned int, _S_truct, _P_ointer, _O_bject */
} __Pyx_TypeInfo;
typedef struct __Pyx_StructField_ {
__Pyx_TypeInfo* type;
const char* name;
size_t offset;
} __Pyx_StructField;
typedef struct {
__Pyx_StructField* field;
size_t parent_offset;
} __Pyx_BufFmt_StackElem;
static INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info); static INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info);
static INLINE void __Pyx_ZeroBuffer(Py_buffer* buf); /*proto*/ static int __Pyx_GetBufferAndValidate(Py_buffer* buf, PyObject* obj, __Pyx_TypeInfo* dtype, int flags, int nd, int cast, __Pyx_BufFmt_StackElem* stack);
static INLINE const char* __Pyx_ConsumeWhitespace(const char* ts); /*proto*/ """, impl="""
static void __Pyx_BufferNdimError(Py_buffer* buffer, int expected_ndim); /*proto*/ static INLINE int __Pyx_IsLittleEndian(void) {
static const char* __Pyx_DescribeTokenInFormatString(const char* ts); /*proto*/ unsigned int n = 1;
""", return *(unsigned char*)(&n) != 0;
impl = """
static INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info) {
if (info->buf == NULL) return;
if (info->suboffsets == __Pyx_minusones) info->suboffsets = NULL;
__Pyx_ReleaseBuffer(info);
} }
static INLINE void __Pyx_ZeroBuffer(Py_buffer* buf) { typedef struct {
buf->buf = NULL; __Pyx_StructField root;
buf->obj = NULL; __Pyx_BufFmt_StackElem* head;
buf->strides = __Pyx_zeros; size_t fmt_offset;
buf->shape = __Pyx_zeros; int new_count, enc_count;
buf->suboffsets = __Pyx_minusones; int is_complex;
char enc_type;
char packmode;
} __Pyx_BufFmt_Context;
static void __Pyx_BufFmt_Init(__Pyx_BufFmt_Context* ctx,
__Pyx_BufFmt_StackElem* stack,
__Pyx_TypeInfo* type) {
stack[0].field = &ctx->root;
stack[0].parent_offset = 0;
ctx->root.type = type;
ctx->root.name = "buffer dtype";
ctx->root.offset = 0;
ctx->head = stack;
ctx->head->field = &ctx->root;
ctx->fmt_offset = 0;
ctx->head->parent_offset = 0;
ctx->packmode = '@';
ctx->new_count = 1;
ctx->enc_count = 0;
ctx->enc_type = 0;
ctx->is_complex = 0;
while (type->typegroup == 'S') {
++ctx->head;
ctx->head->field = type->fields;
ctx->head->parent_offset = 0;
type = type->fields->type;
}
} }
static INLINE const char* __Pyx_ConsumeWhitespace(const char* ts) { static int __Pyx_BufFmt_ParseNumber(const char** ts) {
while (1) { int count;
switch (*ts) { const char* t = *ts;
case '@': if (*t < '0' || *t > '9') {
case 10: return -1;
case 13: } else {
case ' ': count = *t++ - '0';
++ts; while (*t >= '0' && *t < '9') {
break; count *= 10;
case '=': count += *t++ - '0';
case '<':
case '>':
case '!':
PyErr_SetString(PyExc_ValueError, "Buffer acquisition error: Only native byte order, size and alignment supported.");
return NULL;
default:
return ts;
} }
} }
*ts = t;
return count;
} }
static void __Pyx_BufferNdimError(Py_buffer* buffer, int expected_ndim) { static void __Pyx_BufFmt_RaiseUnexpectedChar(char ch) {
PyErr_Format(PyExc_ValueError, char msg[] = {ch, 0};
"Buffer has wrong number of dimensions (expected %d, got %d)", PyErr_Format(PyExc_ValueError, "Unexpected format string character: '%s'", msg);
expected_ndim, buffer->ndim);
} }
static const char* __Pyx_DescribeTokenInFormatString(const char* ts) { static const char* __Pyx_BufFmt_DescribeTypeChar(char ch, int is_complex) {
switch (*ts) { switch (ch) {
case 'b': return "char"; case 'b': return "'char'";
case 'B': return "unsigned char"; case 'B': return "'unsigned char'";
case 'h': return "short"; case 'h': return "'short'";
case 'H': return "unsigned short"; case 'H': return "'unsigned short'";
case 'i': return "int"; case 'i': return "'int'";
case 'I': return "unsigned int"; case 'I': return "'unsigned int'";
case 'l': return "long"; case 'l': return "'long'";
case 'L': return "unsigned long"; case 'L': return "'unsigned long'";
case 'q': return "long long"; case 'q': return "'long long'";
case 'Q': return "unsigned long long"; case 'Q': return "'unsigned long long'";
case 'f': return "float"; case 'f': return (is_complex ? "'complex float'" : "'float'");
case 'd': return "double"; case 'd': return (is_complex ? "'complex double'" : "'double'");
case 'g': return "long double"; case 'g': return (is_complex ? "'complex long double'" : "'long double'");
case 'Z': switch (*(ts+1)) {
case 'f': return "complex float";
case 'd': return "complex double";
case 'g': return "complex long double";
default: return "unparseable format string";
}
case 'T': return "a struct"; case 'T': return "a struct";
case 'O': return "Python object"; case 'O': return "Python object";
case 'P': return "a pointer"; case 'P': return "a pointer";
case 0: return "end";
default: return "unparseable format string"; default: return "unparseable format string";
} }
} }
""") static size_t __Pyx_BufFmt_TypeCharToStandardSize(char ch, int is_complex) {
switch (ch) {
case '?': case 'c': case 'b': case 'B': return 1;
case 'h': case 'H': return 2;
case 'i': case 'I': case 'l': case 'L': return 4;
case 'q': case 'Q': return 8;
case 'f': return (is_complex ? 8 : 4);
case 'd': return (is_complex ? 16 : 8);
case 'g': {
PyErr_SetString(PyExc_ValueError, "Python does not define a standard format string size for long double ('g')..");
return 0;
}
case 'O': case 'P': return sizeof(void*);
default:
__Pyx_BufFmt_RaiseUnexpectedChar(ch);
return 0;
}
}
static size_t __Pyx_BufFmt_TypeCharToNativeSize(char ch, int is_complex) {
switch (ch) {
case 'c': case 'b': case 'B': return 1;
case 'h': case 'H': return sizeof(short);
case 'i': case 'I': return sizeof(int);
case 'l': case 'L': return sizeof(long);
#ifdef HAVE_LONG_LONG
case 'q': case 'Q': return sizeof(PY_LONG_LONG);
#endif
case 'f': return sizeof(float) * (is_complex ? 2 : 1);
case 'd': return sizeof(double) * (is_complex ? 2 : 1);
case 'g': return sizeof(long double) * (is_complex ? 2 : 1);
case 'O': case 'P': return sizeof(void*);
default: {
__Pyx_BufFmt_RaiseUnexpectedChar(ch);
return 0;
}
}
}
parse_typestring_repeat_code = UtilityCode( typedef struct { char c; short x; } __Pyx_st_short;
proto = """ typedef struct { char c; int x; } __Pyx_st_int;
static INLINE const char* __Pyx_ParseTypestringRepeat(const char* ts, int* out_count); /*proto*/ typedef struct { char c; long x; } __Pyx_st_long;
""", typedef struct { char c; float x; } __Pyx_st_float;
impl = """ typedef struct { char c; double x; } __Pyx_st_double;
static INLINE const char* __Pyx_ParseTypestringRepeat(const char* ts, int* out_count) { typedef struct { char c; long double x; } __Pyx_st_longdouble;
int count; typedef struct { char c; void *x; } __Pyx_st_void_p;
if (*ts < '0' || *ts > '9') { #ifdef HAVE_LONG_LONG
count = 1; typedef struct { char c; PY_LONG_LONG x; } __Pyx_s_long_long;
#endif
static size_t __Pyx_BufFmt_TypeCharToAlignment(char ch, int is_complex) {
switch (ch) {
case '?': case 'c': case 'b': case 'B': return 1;
case 'h': case 'H': return sizeof(__Pyx_st_short) - sizeof(short);
case 'i': case 'I': return sizeof(__Pyx_st_int) - sizeof(int);
case 'l': case 'L': return sizeof(__Pyx_st_long) - sizeof(long);
#ifdef HAVE_LONG_LONG
case 'q': case 'Q': return sizeof(__Pyx_s_long_long) - sizeof(PY_LONG_LONG);
#endif
case 'f': return sizeof(__Pyx_st_float) - sizeof(float);
case 'd': return sizeof(__Pyx_st_double) - sizeof(double);
case 'g': return sizeof(__Pyx_st_longdouble) - sizeof(long double);
case 'P': case 'O': return sizeof(__Pyx_st_void_p) - sizeof(void*);
default:
__Pyx_BufFmt_RaiseUnexpectedChar(ch);
return 0;
}
}
static size_t __Pyx_BufFmt_TypeCharToGroup(char ch, int is_complex) {
switch (ch) {
case 'c': case 'b': case 'h': case 'i': case 'l': case 'q': return 'I';
case 'B': case 'H': case 'I': case 'L': case 'Q': return 'U';
case 'f': case 'd': case 'g': return (is_complex ? 'C' : 'R');
case 'O': return 'O';
case 'P': return 'P';
default: {
__Pyx_BufFmt_RaiseUnexpectedChar(ch);
return 0;
}
}
}
static void __Pyx_BufFmt_RaiseExpected(__Pyx_BufFmt_Context* ctx) {
if (ctx->head == NULL || ctx->head->field == &ctx->root) {
const char* expected;
const char* quote;
if (ctx->head == NULL) {
expected = "end";
quote = "";
} else { } else {
count = *ts++ - '0'; expected = ctx->head->field->type->name;
while (*ts >= '0' && *ts < '9') { quote = "'";
count *= 10; }
count += *ts++ - '0'; PyErr_Format(PyExc_ValueError,
"Buffer dtype mismatch, expected %s%s%s but got %s",
quote, expected, quote,
__Pyx_BufFmt_DescribeTypeChar(ctx->enc_type, ctx->is_complex));
} else {
__Pyx_StructField* field = ctx->head->field;
__Pyx_StructField* parent = (ctx->head - 1)->field;
PyErr_Format(PyExc_ValueError,
"Buffer dtype mismatch, expected '%s' but got %s in '%s.%s'",
field->type->name, __Pyx_BufFmt_DescribeTypeChar(ctx->enc_type, ctx->is_complex),
parent->type->name, field->name);
} }
}
static int __Pyx_BufFmt_ProcessTypeChunk(__Pyx_BufFmt_Context* ctx) {
char group;
size_t size, offset;
if (ctx->enc_type == 0) return 0;
group = __Pyx_BufFmt_TypeCharToGroup(ctx->enc_type, ctx->is_complex);
do {
__Pyx_StructField* field = ctx->head->field;
__Pyx_TypeInfo* type = field->type;
if (ctx->packmode == '@' || ctx->packmode == '^') {
size = __Pyx_BufFmt_TypeCharToNativeSize(ctx->enc_type, ctx->is_complex);
} else {
size = __Pyx_BufFmt_TypeCharToStandardSize(ctx->enc_type, ctx->is_complex);
}
if (ctx->packmode == '@') {
int align_at = __Pyx_BufFmt_TypeCharToAlignment(ctx->enc_type, ctx->is_complex);
int align_mod_offset;
if (align_at == 0) return -1;
align_mod_offset = ctx->fmt_offset % align_at;
if (align_mod_offset > 0) ctx->fmt_offset += align_at - align_mod_offset;
}
if (type->size != size || type->typegroup != group) {
if (type->typegroup == 'C' && type->fields != NULL) {
/* special case -- treat as struct rather than complex number */
size_t parent_offset = ctx->head->parent_offset + field->offset;
++ctx->head;
ctx->head->field = type->fields;
ctx->head->parent_offset = parent_offset;
continue;
}
__Pyx_BufFmt_RaiseExpected(ctx);
return -1;
}
offset = ctx->head->parent_offset + field->offset;
if (ctx->fmt_offset != offset) {
PyErr_Format(PyExc_ValueError,
"Buffer dtype mismatch; next field is at offset %"PY_FORMAT_SIZE_T"d "
"but %"PY_FORMAT_SIZE_T"d expected", ctx->fmt_offset, offset);
return -1;
}
ctx->fmt_offset += size;
--ctx->enc_count; /* Consume from buffer string */
/* Done checking, move to next field, pushing or popping struct stack if needed */
while (1) {
if (field == &ctx->root) {
ctx->head = NULL;
if (ctx->enc_count != 0) {
__Pyx_BufFmt_RaiseExpected(ctx);
return -1;
}
break; /* breaks both loops as ctx->enc_count == 0 */
}
ctx->head->field = ++field;
if (field->type == NULL) {
--ctx->head;
field = ctx->head->field;
continue;
} else if (field->type->typegroup == 'S') {
size_t parent_offset = ctx->head->parent_offset + field->offset;
if (field->type->fields->type == NULL) continue; /* empty struct */
field = field->type->fields;
++ctx->head;
ctx->head->field = field;
ctx->head->parent_offset = parent_offset;
break;
} else {
break;
}
}
} while (ctx->enc_count);
ctx->enc_type = 0;
ctx->is_complex = 0;
return 0;
}
static int __Pyx_BufFmt_FirstPack(__Pyx_BufFmt_Context* ctx) {
if (ctx->enc_type != 0 || ctx->packmode != '@') {
PyErr_SetString(PyExc_ValueError, "Buffer packing mode currently only allowed at beginning of format string (this is a defect)");
return -1;
}
return 0;
}
static const char* __Pyx_BufFmt_CheckString(__Pyx_BufFmt_Context* ctx, const char* ts) {
int got_Z = 0;
while (1) {
switch(*ts) {
case 0:
if (ctx->enc_type != 0 && ctx->head == NULL) {
__Pyx_BufFmt_RaiseExpected(ctx);
return NULL;
}
if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL;
if (ctx->head != NULL) {
__Pyx_BufFmt_RaiseExpected(ctx);
return NULL;
} }
*out_count = count;
return ts; return ts;
case ' ':
case 10:
case 13:
++ts;
break;
case '<':
if (!__Pyx_IsLittleEndian()) {
PyErr_SetString(PyExc_ValueError, "Little-endian buffer not supported on big-endian compiler");
return NULL;
}
if (__Pyx_BufFmt_FirstPack(ctx) == -1) return NULL;
ctx->packmode = '=';
++ts;
break;
case '>':
case '!':
if (__Pyx_IsLittleEndian()) {
PyErr_SetString(PyExc_ValueError, "Big-endian buffer not supported on little-endian compiler");
return NULL;
}
if (__Pyx_BufFmt_FirstPack(ctx) == -1) return NULL;
ctx->packmode = '=';
++ts;
break;
case '=':
case '@':
case '^':
if (__Pyx_BufFmt_FirstPack(ctx) == -1) return NULL;
ctx->packmode = *ts++;
break;
case 'T': /* substruct */
{
int i;
const char* ts_after_sub;
int struct_count = ctx->new_count;
ctx->new_count = 1;
++ts;
if (*ts != '{') {
PyErr_SetString(PyExc_ValueError, "Buffer acquisition: Expected '{' after 'T'");
return NULL;
}
++ts;
ts_after_sub = ts;
for (i = 0; i != struct_count; ++i) {
ts_after_sub = __Pyx_BufFmt_CheckString(ctx, ts);
if (!ts_after_sub) return NULL;
}
ts = ts_after_sub;
}
break;
case '}': /* end of substruct; either repeat or move on */
++ts;
return ts;
case 'x':
if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL;
ctx->fmt_offset += ctx->new_count;
ctx->new_count = 1;
ctx->enc_count = 0;
ctx->enc_type = 0;
++ts;
break;
case 'Z':
got_Z = 1;
++ts;
if (*ts != 'f' && *ts != 'd' && *ts != 'g') {
__Pyx_BufFmt_RaiseUnexpectedChar('Z');
return NULL;
} /* fall through */
case 'c': case 'b': case 'B': case 'h': case 'H': case 'i': case 'I':
case 'l': case 'L': case 'q': case 'Q':
case 'f': case 'd': case 'g':
case 'O':
if (ctx->enc_type == *ts && got_Z == ctx->is_complex) {
/* Continue pooling same type */
ctx->enc_count += ctx->new_count;
} else {
/* New type */
if (__Pyx_BufFmt_ProcessTypeChunk(ctx) == -1) return NULL;
ctx->enc_count = ctx->new_count;
ctx->enc_type = *ts;
ctx->is_complex = got_Z;
}
++ts;
ctx->new_count = 1;
got_Z = 0;
break;
default:
{
ctx->new_count = __Pyx_BufFmt_ParseNumber(&ts);
if (ctx->new_count == -1) { /* First char was not a digit */
char msg[2] = { *ts, 0 };
PyErr_Format(PyExc_ValueError,
"Does not understand character buffer dtype format string ('%s')", msg);
return NULL;
}
}
}
}
} }
""")
raise_buffer_fallback_code = UtilityCode( static INLINE void __Pyx_ZeroBuffer(Py_buffer* buf) {
proto = """ buf->buf = NULL;
static void __Pyx_RaiseBufferFallbackError(void); /*proto*/ buf->obj = NULL;
""", buf->strides = __Pyx_zeros;
impl = """ buf->shape = __Pyx_zeros;
static void __Pyx_RaiseBufferFallbackError(void) { buf->suboffsets = __Pyx_minusones;
}
static int __Pyx_GetBufferAndValidate(Py_buffer* buf, PyObject* obj, __Pyx_TypeInfo* dtype, int flags, int nd, int cast, __Pyx_BufFmt_StackElem* stack) {
if (obj == Py_None) {
__Pyx_ZeroBuffer(buf);
return 0;
}
buf->buf = NULL;
if (__Pyx_GetBuffer(obj, buf, flags) == -1) goto fail;
if (buf->ndim != nd) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"Buffer acquisition failed on assignment; and then reacquiring the old buffer failed too!"); "Buffer has wrong number of dimensions (expected %d, got %d)",
nd, buf->ndim);
goto fail;
}
if (!cast) {
__Pyx_BufFmt_Context ctx;
__Pyx_BufFmt_Init(&ctx, stack, dtype);
if (!__Pyx_BufFmt_CheckString(&ctx, buf->format)) goto fail;
}
if ((unsigned)buf->itemsize != dtype->size) {
PyErr_Format(PyExc_ValueError,
"Item size of buffer (%"PY_FORMAT_SIZE_T"d byte%s) does not match size of '%s' (%"PY_FORMAT_SIZE_T"d byte%s)",
buf->itemsize, (buf->itemsize > 1) ? "s" : "",
dtype->name,
dtype->size, (dtype->size > 1) ? "s" : "");
goto fail;
}
if (buf->suboffsets == NULL) buf->suboffsets = __Pyx_minusones;
return 0;
fail:;
__Pyx_ZeroBuffer(buf);
return -1;
} }
static INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info) {
if (info->buf == NULL) return;
if (info->suboffsets == __Pyx_minusones) info->suboffsets = NULL;
__Pyx_ReleaseBuffer(info);
}
""") """)
...@@ -50,6 +50,7 @@ builtin_function_table = [ ...@@ -50,6 +50,7 @@ builtin_function_table = [
#('round', "", "", ""), #('round', "", "", ""),
('setattr', "OOO", "r", "PyObject_SetAttr"), ('setattr', "OOO", "r", "PyObject_SetAttr"),
#('sum', "", "", ""), #('sum', "", "", ""),
('type', "O", "O", "PyObject_Type"),
#('unichr', "", "", ""), #('unichr', "", "", ""),
#('unicode', "", "", ""), #('unicode', "", "", ""),
#('vars', "", "", ""), #('vars', "", "", ""),
......
...@@ -153,5 +153,9 @@ def parse_command_line(args): ...@@ -153,5 +153,9 @@ def parse_command_line(args):
sys.exit(1) sys.exit(1)
if len(sources) == 0 and not options.show_version: if len(sources) == 0 and not options.show_version:
bad_usage() bad_usage()
if Options.embed and len(sources) > 1:
sys.stderr.write(
"cython: Only one source file allowed when using -embed\n")
sys.exit(1)
return options, sources return options, sources
...@@ -9,6 +9,7 @@ import Options ...@@ -9,6 +9,7 @@ import Options
import StringEncoding import StringEncoding
from Cython import Utils from Cython import Utils
from PyrexTypes import py_object_type, typecast from PyrexTypes import py_object_type, typecast
import PyrexTypes
from TypeSlots import method_coexist from TypeSlots import method_coexist
from Scanning import SourceDescriptor from Scanning import SourceDescriptor
from Cython.StringIOTree import StringIOTree from Cython.StringIOTree import StringIOTree
...@@ -293,9 +294,7 @@ class GlobalState(object): ...@@ -293,9 +294,7 @@ class GlobalState(object):
# to create this output C code. This is # to create this output C code. This is
# used to annotate the comments. # used to annotate the comments.
# #
# used_utility_code set(string|int) Ids of used utility code (to avoid reinsertion) # utility_codes set IDs of used utility code (to avoid reinsertion)
# utilprotowriter CCodeWriter
# utildefwriter CCodeWriter
# #
# declared_cnames {string:Entry} used in a transition phase to merge pxd-declared # declared_cnames {string:Entry} used in a transition phase to merge pxd-declared
# constants etc. into the pyx-declared ones (i.e, # constants etc. into the pyx-declared ones (i.e,
...@@ -306,6 +305,8 @@ class GlobalState(object): ...@@ -306,6 +305,8 @@ class GlobalState(object):
# const_cname_counter int global counter for constant identifiers # const_cname_counter int global counter for constant identifiers
# #
# parts {string:CCodeWriter}
# interned_strings # interned_strings
# consts # consts
...@@ -319,24 +320,44 @@ class GlobalState(object): ...@@ -319,24 +320,44 @@ class GlobalState(object):
directives = {} directives = {}
def __init__(self, rootwriter, emit_linenums=False): code_layout = [
'h_code',
'utility_code_proto',
'type_declarations',
'module_declarations',
'typeinfo',
'before_global_var',
'global_var',
'decls',
'all_the_rest',
'utility_code_def'
]
def __init__(self, writer, emit_linenums=False):
self.filename_table = {} self.filename_table = {}
self.filename_list = [] self.filename_list = []
self.input_file_contents = {} self.input_file_contents = {}
self.used_utility_code = set() self.utility_codes = set()
self.declared_cnames = {} self.declared_cnames = {}
self.in_utility_code_generation = False self.in_utility_code_generation = False
self.emit_linenums = emit_linenums self.emit_linenums = emit_linenums
self.parts = {}
self.const_cname_counter = 1 self.const_cname_counter = 1
self.string_const_index = {} self.string_const_index = {}
self.int_const_index = {} self.int_const_index = {}
self.py_constants = [] self.py_constants = []
def initwriters(self, rootwriter): assert writer.globalstate is None
self.utilprotowriter = rootwriter.new_writer() writer.globalstate = self
self.utildefwriter = rootwriter.new_writer() self.rootwriter = writer
self.decls_writer = rootwriter.new_writer()
def initialize_main_c_code(self):
rootwriter = self.rootwriter
for part in self.code_layout:
self.parts[part] = rootwriter.insertion_point()
self.pystring_table = rootwriter.new_writer() self.pystring_table = rootwriter.new_writer()
self.init_cached_builtins_writer = rootwriter.new_writer() self.init_cached_builtins_writer = rootwriter.new_writer()
self.initwriter = rootwriter.new_writer() self.initwriter = rootwriter.new_writer()
...@@ -355,11 +376,35 @@ class GlobalState(object): ...@@ -355,11 +376,35 @@ class GlobalState(object):
self.cleanupwriter.putln("static void __Pyx_CleanupGlobals(void) {") self.cleanupwriter.putln("static void __Pyx_CleanupGlobals(void) {")
# #
# Global constants, interned objects, etc. # utility_code_def
# #
def insert_global_var_declarations_into(self, code): code = self.parts['utility_code_def']
code.insert(self.decls_writer) if self.emit_linenums:
code.write('\n#line 1 "cython_utility"\n')
code.putln("")
code.putln("/* Runtime support code */")
code.putln("")
code.putln("static void %s(void) {" % Naming.fileinit_cname)
code.putln("%s = %s;" %
(Naming.filetable_cname, Naming.filenames_cname))
code.putln("}")
def finalize_main_c_code(self):
self.close_global_decls()
#
# utility_code_def
#
code = self.parts['utility_code_def']
code.put(PyrexTypes.type_conversion_functions)
code.putln("")
def __getitem__(self, key):
return self.parts[key]
#
# Global constants, interned objects, etc.
#
def close_global_decls(self): def close_global_decls(self):
# This is called when it is known that no more global declarations will # This is called when it is known that no more global declarations will
# declared (but can be called before or after insert_XXX). # declared (but can be called before or after insert_XXX).
...@@ -393,7 +438,7 @@ class GlobalState(object): ...@@ -393,7 +438,7 @@ class GlobalState(object):
code.insert(self.cleanupwriter) code.insert(self.cleanupwriter)
def put_pyobject_decl(self, entry): def put_pyobject_decl(self, entry):
self.decls_writer.putln("static PyObject *%s;" % entry.cname) self['global_var'].putln("static PyObject *%s;" % entry.cname)
# constant handling at code generation time # constant handling at code generation time
...@@ -485,8 +530,9 @@ class GlobalState(object): ...@@ -485,8 +530,9 @@ class GlobalState(object):
consts = [ (len(c.cname), c.cname, c) consts = [ (len(c.cname), c.cname, c)
for c in self.py_constants ] for c in self.py_constants ]
consts.sort() consts.sort()
decls_writer = self.parts['decls']
for _, cname, c in consts: for _, cname, c in consts:
self.decls_writer.putln( decls_writer.putln(
"static %s;" % c.type.declaration_code(cname)) "static %s;" % c.type.declaration_code(cname))
def generate_string_constants(self): def generate_string_constants(self):
...@@ -494,8 +540,10 @@ class GlobalState(object): ...@@ -494,8 +540,10 @@ class GlobalState(object):
for c in self.string_const_index.itervalues() ] for c in self.string_const_index.itervalues() ]
c_consts.sort() c_consts.sort()
py_strings = [] py_strings = []
decls_writer = self.parts['decls']
for _, cname, c in c_consts: for _, cname, c in c_consts:
self.decls_writer.putln('static char %s[] = "%s";' % ( decls_writer.putln('static char %s[] = "%s";' % (
cname, c.escaped_value)) cname, c.escaped_value))
if c.py_strings is not None: if c.py_strings is not None:
for py_string in c.py_strings.itervalues(): for py_string in c.py_strings.itervalues():
...@@ -510,7 +558,7 @@ class GlobalState(object): ...@@ -510,7 +558,7 @@ class GlobalState(object):
self.pystring_table.putln("static __Pyx_StringTabEntry %s[] = {" % self.pystring_table.putln("static __Pyx_StringTabEntry %s[] = {" %
Naming.stringtab_cname) Naming.stringtab_cname)
for c_cname, _, py_string in py_strings: for c_cname, _, py_string in py_strings:
self.decls_writer.putln( decls_writer.putln(
"static PyObject *%s;" % py_string.cname) "static PyObject *%s;" % py_string.cname)
self.pystring_table.putln( self.pystring_table.putln(
"{&%s, %s, sizeof(%s), %d, %d, %d}," % ( "{&%s, %s, sizeof(%s), %d, %d, %d}," % (
...@@ -533,9 +581,10 @@ class GlobalState(object): ...@@ -533,9 +581,10 @@ class GlobalState(object):
consts = [ (len(c.value), c.value, c.is_long, c) consts = [ (len(c.value), c.value, c.is_long, c)
for c in self.int_const_index.itervalues() ] for c in self.int_const_index.itervalues() ]
consts.sort() consts.sort()
decls_writer = self.parts['decls']
for _, value, longness, c in consts: for _, value, longness, c in consts:
cname = c.cname cname = c.cname
self.decls_writer.putln("static PyObject *%s;" % cname) decls_writer.putln("static PyObject *%s;" % cname)
if longness: if longness:
function = '%s = PyLong_FromString((char *)"%s", 0, 0); %s;' function = '%s = PyLong_FromString((char *)"%s", 0, 0); %s;'
else: else:
...@@ -602,55 +651,18 @@ class GlobalState(object): ...@@ -602,55 +651,18 @@ class GlobalState(object):
code twice. Otherwise, id(codetup) is used as such an identifier. code twice. Otherwise, id(codetup) is used as such an identifier.
""" """
if name is None: name = id(utility_code) if name is None: name = id(utility_code)
if self.check_utility_code_needed_and_register(name): if name not in self.utility_codes:
self.utility_codes.add(name)
if utility_code.requires: if utility_code.requires:
for dependency in utility_code.requires: for dependency in utility_code.requires:
self.use_utility_code(dependency) self.use_utility_code(dependency)
if utility_code.proto: if utility_code.proto:
self.utilprotowriter.put(utility_code.proto) self.parts['utility_code_proto'].put(utility_code.proto)
if utility_code.impl: if utility_code.impl:
self.utildefwriter.put(utility_code.impl) self.parts['utility_code_def'].put(utility_code.impl)
utility_code.write_init_code(self.initwriter, self.module_pos) utility_code.write_init_code(self.initwriter, self.module_pos)
utility_code.write_cleanup_code(self.cleanupwriter, self.module_pos) utility_code.write_cleanup_code(self.cleanupwriter, self.module_pos)
def has_code(self, name):
return name in self.used_utility_code
def use_code_from(self, func, name, *args, **kw):
"""
Requests that the utility code that func can generate is used in the C
file. func is called like this:
func(proto, definition, name, *args, **kw)
where proto and definition are two CCodeWriter instances; the
former should have the prototype written to it and the other the definition.
The call might happen at some later point (if compiling multiple modules
into a cache for instance), and will only happen once per utility code.
name is used to identify the utility code, so that it isn't regenerated
when the same code is requested again.
"""
if self.check_utility_code_needed_and_register(name):
func(self.utilprotowriter, self.utildefwriter,
name, *args, **kw)
def check_utility_code_needed_and_register(self, name):
if name in self.used_utility_code:
return False
else:
self.used_utility_code.add(name)
return True
def put_utility_code_protos(self, writer):
writer.insert(self.utilprotowriter)
def put_utility_code_defs(self, writer):
if self.emit_linenums:
writer.write('\n#line 1 "cython_utility"\n')
writer.insert(self.utildefwriter)
def funccontext_property(name): def funccontext_property(name):
def get(self): def get(self):
...@@ -692,6 +704,8 @@ class CCodeWriter(object): ...@@ -692,6 +704,8 @@ class CCodeWriter(object):
# pyclass_stack list used during recursive code generation to pass information # pyclass_stack list used during recursive code generation to pass information
# about the current class one is in # about the current class one is in
globalstate = None
def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None): def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None):
if buffer is None: buffer = StringIOTree() if buffer is None: buffer = StringIOTree()
self.buffer = buffer self.buffer = buffer
...@@ -704,12 +718,8 @@ class CCodeWriter(object): ...@@ -704,12 +718,8 @@ class CCodeWriter(object):
self.level = 0 self.level = 0
self.call_level = 0 self.call_level = 0
self.bol = 1 self.bol = 1
if create_from is None:
# Root CCodeWriter if create_from is not None:
self.globalstate = GlobalState(self, emit_linenums=emit_linenums)
self.globalstate.initwriters(self)
# ^^^ need seperate step because this will reference self.globalstate
else:
# Use same global state # Use same global state
self.globalstate = create_from.globalstate self.globalstate = create_from.globalstate
# Clone formatting state # Clone formatting state
...@@ -717,7 +727,7 @@ class CCodeWriter(object): ...@@ -717,7 +727,7 @@ class CCodeWriter(object):
self.level = create_from.level self.level = create_from.level
self.bol = create_from.bol self.bol = create_from.bol
self.call_level = create_from.call_level self.call_level = create_from.call_level
if emit_linenums is None: if emit_linenums is None and self.globalstate:
self.emit_linenums = self.globalstate.emit_linenums self.emit_linenums = self.globalstate.emit_linenums
else: else:
self.emit_linenums = emit_linenums self.emit_linenums = emit_linenums
...@@ -725,7 +735,8 @@ class CCodeWriter(object): ...@@ -725,7 +735,8 @@ class CCodeWriter(object):
def create_new(self, create_from, buffer, copy_formatting): def create_new(self, create_from, buffer, copy_formatting):
# polymorphic constructor -- very slightly more versatile # polymorphic constructor -- very slightly more versatile
# than using __class__ # than using __class__
return CCodeWriter(create_from, buffer, copy_formatting) result = CCodeWriter(create_from, buffer, copy_formatting)
return result
def copyto(self, f): def copyto(self, f):
self.buffer.copyto(f) self.buffer.copyto(f)
...@@ -807,12 +818,15 @@ class CCodeWriter(object): ...@@ -807,12 +818,15 @@ class CCodeWriter(object):
# code generation # code generation
def putln(self, code = ""): def putln(self, code = "", safe=False):
if self.marker and self.bol: if self.marker and self.bol:
self.emit_marker() self.emit_marker()
if self.emit_linenums and self.last_marker_line != 0: if self.emit_linenums and self.last_marker_line != 0:
self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc)) self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc))
if code: if code:
if safe:
self.put_safe(code)
else:
self.put(code) self.put(code)
self.write("\n"); self.write("\n");
self.bol = 1 self.bol = 1
......
...@@ -503,10 +503,15 @@ class ExprNode(Node): ...@@ -503,10 +503,15 @@ class ExprNode(Node):
src = PyTypeTestNode(src, dst_type, env) src = PyTypeTestNode(src, dst_type, env)
elif src.type.is_pyobject: elif src.type.is_pyobject:
src = CoerceFromPyTypeNode(dst_type, src, env) src = CoerceFromPyTypeNode(dst_type, src, env)
elif (dst_type.is_complex
and src_type != dst_type
and dst_type.assignable_from(src_type)
and not env.directives['c99_complex']):
src = CoerceToComplexNode(src, dst_type, env)
else: # neither src nor dst are py types else: # neither src nor dst are py types
# Added the string comparison, since for c types that # Added the string comparison, since for c types that
# is enough, but Cython gets confused when the types are # is enough, but Cython gets confused when the types are
# in different files. # in different pxi files.
if not (str(src.type) == str(dst_type) or dst_type.assignable_from(src_type)): if not (str(src.type) == str(dst_type) or dst_type.assignable_from(src_type)):
error(self.pos, "Cannot assign type '%s' to '%s'" % error(self.pos, "Cannot assign type '%s' to '%s'" %
(src.type, dst_type)) (src.type, dst_type))
...@@ -683,7 +688,7 @@ class IntNode(ConstNode): ...@@ -683,7 +688,7 @@ class IntNode(ConstNode):
type = PyrexTypes.c_long_type type = PyrexTypes.c_long_type
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
if dst_type.is_numeric: if dst_type.is_numeric and not dst_type.is_complex:
self.type = PyrexTypes.c_long_type self.type = PyrexTypes.c_long_type
return self return self
# Arrange for a Python version of the number to be pre-allocated # Arrange for a Python version of the number to be pre-allocated
...@@ -871,6 +876,8 @@ class ImagNode(AtomicExprNode): ...@@ -871,6 +876,8 @@ class ImagNode(AtomicExprNode):
# #
# value float imaginary part # value float imaginary part
type = PyrexTypes.c_double_complex_type
def calculate_constant_result(self): def calculate_constant_result(self):
self.constant_result = complex(0.0, self.value) self.constant_result = complex(0.0, self.value)
...@@ -878,18 +885,39 @@ class ImagNode(AtomicExprNode): ...@@ -878,18 +885,39 @@ class ImagNode(AtomicExprNode):
return complex(0.0, self.value) return complex(0.0, self.value)
def analyse_types(self, env): def analyse_types(self, env):
self.type = py_object_type self.type.create_declaration_utility_code(env)
def coerce_to(self, dst_type, env):
# Arrange for a Python version of the number to be pre-allocated
# when coercing to a Python type.
if dst_type.is_pyobject:
self.is_temp = 1 self.is_temp = 1
self.type = PyrexTypes.py_object_type
# We still need to perform normal coerce_to processing on the
# result, because we might be coercing to an extension type,
# in which case a type test node will be needed.
return AtomicExprNode.coerce_to(self, dst_type, env)
gil_message = "Constructing complex number" gil_message = "Constructing complex number"
def calculate_result_code(self):
if self.type.is_pyobject:
return self.result()
elif self.c99_complex:
return "%rj" % float(self.value)
else:
return "%s(0, %r)" % (self.type.from_parts, float(self.value))
def generate_result_code(self, code): def generate_result_code(self, code):
if self.type.is_pyobject:
code.putln( code.putln(
"%s = PyComplex_FromDoubles(0.0, %r); %s" % ( "%s = PyComplex_FromDoubles(0.0, %r); %s" % (
self.result(), self.result(),
float(self.value), float(self.value),
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
else:
self.c99_complex = code.globalstate.directives['c99_complex']
...@@ -1844,7 +1872,7 @@ class IndexNode(ExprNode): ...@@ -1844,7 +1872,7 @@ class IndexNode(ExprNode):
return Buffer.put_buffer_lookup_code(entry=self.base.entry, return Buffer.put_buffer_lookup_code(entry=self.base.entry,
index_signeds=[i.type.signed for i in self.indices], index_signeds=[i.type.signed for i in self.indices],
index_cnames=index_temps, index_cnames=index_temps,
options=code.globalstate.directives, directives=code.globalstate.directives,
pos=self.pos, code=code) pos=self.pos, code=code)
def put_nonecheck(self, code): def put_nonecheck(self, code):
...@@ -2809,6 +2837,8 @@ class AttributeNode(ExprNode): ...@@ -2809,6 +2837,8 @@ class AttributeNode(ExprNode):
obj.type.vtabslot_cname, self.member) obj.type.vtabslot_cname, self.member)
else: else:
return self.member return self.member
elif obj.type.is_complex:
return "__Pyx_%s_PART(%s)" % (self.member.upper(), obj_code)
else: else:
return "%s%s%s" % (obj_code, self.op, self.member) return "%s%s%s" % (obj_code, self.op, self.member)
...@@ -3709,6 +3739,7 @@ class UnopNode(ExprNode): ...@@ -3709,6 +3739,7 @@ class UnopNode(ExprNode):
# - Allocate temporary for result if needed. # - Allocate temporary for result if needed.
subexprs = ['operand'] subexprs = ['operand']
infix = True
def calculate_constant_result(self): def calculate_constant_result(self):
func = compile_time_unary_operators[self.operator] func = compile_time_unary_operators[self.operator]
...@@ -3823,13 +3854,17 @@ class UnaryMinusNode(UnopNode): ...@@ -3823,13 +3854,17 @@ class UnaryMinusNode(UnopNode):
self.type = self.operand.type self.type = self.operand.type
else: else:
self.type_error() self.type_error()
if self.type.is_complex:
self.infix = env.directives['c99_complex']
def py_operation_function(self): def py_operation_function(self):
return "PyNumber_Negative" return "PyNumber_Negative"
def calculate_result_code(self): def calculate_result_code(self):
if self.infix:
return "(-%s)" % self.operand.result() return "(-%s)" % self.operand.result()
else:
return "%s(%s)" % (self.operand.type.unary_op('-'), self.operand.result())
class TildeNode(UnopNode): class TildeNode(UnopNode):
# unary '~' operator # unary '~' operator
...@@ -3926,15 +3961,18 @@ class TypecastNode(ExprNode): ...@@ -3926,15 +3961,18 @@ class TypecastNode(ExprNode):
error(self.pos, "Casting temporary Python object to non-numeric non-Python type") error(self.pos, "Casting temporary Python object to non-numeric non-Python type")
if to_py and not from_py: if to_py and not from_py:
if (self.operand.type.to_py_function and if (self.operand.type.to_py_function and
self.operand.type.create_convert_utility_code(env)): self.operand.type.create_to_py_utility_code(env)):
self.result_ctype = py_object_type self.result_ctype = py_object_type
self.operand = self.operand.coerce_to_pyobject(env) self.operand = self.operand.coerce_to_pyobject(env)
else: else:
if not (self.operand.type.is_ptr and self.operand.type.base_type.is_void):
warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.operand.type, self.type)) warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.operand.type, self.type))
self.operand = self.operand.coerce_to_simple(env) self.operand = self.operand.coerce_to_simple(env)
elif from_py and not to_py: elif from_py and not to_py:
if self.type.from_py_function: if self.type.from_py_function:
self.operand = self.operand.coerce_to(self.type, env) self.operand = self.operand.coerce_to(self.type, env)
elif self.type.is_ptr and not (self.type.base_type.is_void or self.type.base_type.is_struct):
error(self.pos, "Python objects cannot be casted to pointers of primitive types")
else: else:
warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.type, self.operand.type)) warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.type, self.operand.type))
elif from_py and to_py: elif from_py and to_py:
...@@ -4190,12 +4228,20 @@ class BinopNode(ExprNode): ...@@ -4190,12 +4228,20 @@ class BinopNode(ExprNode):
class NumBinopNode(BinopNode): class NumBinopNode(BinopNode):
# Binary operation taking numeric arguments. # Binary operation taking numeric arguments.
infix = True
def analyse_c_operation(self, env): def analyse_c_operation(self, env):
type1 = self.operand1.type type1 = self.operand1.type
type2 = self.operand2.type type2 = self.operand2.type
self.type = self.compute_c_result_type(type1, type2) self.type = self.compute_c_result_type(type1, type2)
if not self.type: if not self.type:
self.type_error() self.type_error()
return
if self.type.is_complex and not env.directives['c99_complex']:
self.infix = False
if not self.infix:
self.operand1 = self.operand1.coerce_to(self.type, env)
self.operand2 = self.operand2.coerce_to(self.type, env)
def compute_c_result_type(self, type1, type2): def compute_c_result_type(self, type1, type2):
if self.c_types_okay(type1, type2): if self.c_types_okay(type1, type2):
...@@ -4217,10 +4263,19 @@ class NumBinopNode(BinopNode): ...@@ -4217,10 +4263,19 @@ class NumBinopNode(BinopNode):
and (type2.is_numeric or type2.is_enum) and (type2.is_numeric or type2.is_enum)
def calculate_result_code(self): def calculate_result_code(self):
if self.infix:
return "(%s %s %s)" % ( return "(%s %s %s)" % (
self.operand1.result(), self.operand1.result(),
self.operator, self.operator,
self.operand2.result()) self.operand2.result())
else:
func = self.type.binary_op(self.operator)
if func is None:
error(self.pos, "binary operator %s not supported for %s" % (self.operator, self.type))
return "%s(%s, %s)" % (
func,
self.operand1.result(),
self.operand2.result())
def py_operation_function(self): def py_operation_function(self):
return self.py_functions[self.operator] return self.py_functions[self.operator]
...@@ -4322,7 +4377,7 @@ class DivNode(NumBinopNode): ...@@ -4322,7 +4377,7 @@ class DivNode(NumBinopNode):
return "float division" return "float division"
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
if not self.type.is_pyobject: if not self.type.is_pyobject and not self.type.is_complex:
if self.cdivision is None: if self.cdivision is None:
self.cdivision = (code.globalstate.directives['cdivision'] self.cdivision = (code.globalstate.directives['cdivision']
or not self.type.signed or not self.type.signed
...@@ -4335,7 +4390,11 @@ class DivNode(NumBinopNode): ...@@ -4335,7 +4390,11 @@ class DivNode(NumBinopNode):
def generate_div_warning_code(self, code): def generate_div_warning_code(self, code):
if not self.type.is_pyobject: if not self.type.is_pyobject:
if self.zerodivision_check: if self.zerodivision_check:
code.putln("if (unlikely(%s == 0)) {" % self.operand2.result()) if not self.infix:
zero_test = "%s(%s)" % (self.type.unary_op('zero'), self.operand2.result())
else:
zero_test = "%s == 0" % self.operand2.result()
code.putln("if (unlikely(%s)) {" % zero_test)
code.putln('PyErr_Format(PyExc_ZeroDivisionError, "%s");' % self.zero_division_message()) code.putln('PyErr_Format(PyExc_ZeroDivisionError, "%s");' % self.zero_division_message())
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
code.putln("}") code.putln("}")
...@@ -4348,7 +4407,7 @@ class DivNode(NumBinopNode): ...@@ -4348,7 +4407,7 @@ class DivNode(NumBinopNode):
code.putln('PyErr_Format(PyExc_OverflowError, "value too large to perform division");') code.putln('PyErr_Format(PyExc_OverflowError, "value too large to perform division");')
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
code.putln("}") code.putln("}")
if code.globalstate.directives['cdivision_warnings']: if code.globalstate.directives['cdivision_warnings'] and self.operator != '/':
code.globalstate.use_utility_code(cdivision_warning_utility_code) code.globalstate.use_utility_code(cdivision_warning_utility_code)
code.putln("if ((%s < 0) ^ (%s < 0)) {" % ( code.putln("if ((%s < 0) ^ (%s < 0)) {" % (
self.operand1.result(), self.operand1.result(),
...@@ -4359,7 +4418,9 @@ class DivNode(NumBinopNode): ...@@ -4359,7 +4418,9 @@ class DivNode(NumBinopNode):
code.putln("}") code.putln("}")
def calculate_result_code(self): def calculate_result_code(self):
if self.type.is_float and self.operator == '//': if self.type.is_complex:
return NumBinopNode.calculate_result_code(self)
elif self.type.is_float and self.operator == '//':
return "floor(%s / %s)" % ( return "floor(%s / %s)" % (
self.operand1.result(), self.operand1.result(),
self.operand2.result()) self.operand2.result())
...@@ -4423,7 +4484,10 @@ class PowNode(NumBinopNode): ...@@ -4423,7 +4484,10 @@ class PowNode(NumBinopNode):
def analyse_c_operation(self, env): def analyse_c_operation(self, env):
NumBinopNode.analyse_c_operation(self, env) NumBinopNode.analyse_c_operation(self, env)
if self.operand1.type.is_float or self.operand2.type.is_float: if self.type.is_complex:
error(self.pos, "complex powers not yet supported")
self.pow_func = "<error>"
elif self.type.is_float:
self.pow_func = "pow" self.pow_func = "pow"
else: else:
self.pow_func = "__Pyx_pow_%s" % self.type.declaration_code('').replace(' ', '_') self.pow_func = "__Pyx_pow_%s" % self.type.declaration_code('').replace(' ', '_')
...@@ -4686,7 +4750,13 @@ class CmpNode(object): ...@@ -4686,7 +4750,13 @@ class CmpNode(object):
or (self.cascade and self.cascade.is_python_result())) or (self.cascade and self.cascade.is_python_result()))
def check_types(self, env, operand1, op, operand2): def check_types(self, env, operand1, op, operand2):
if not self.types_okay(operand1, op, operand2): if operand1.type.is_complex or operand2.type.is_complex:
if op not in ('==', '!='):
error(self.pos, "complex types unordered")
common_type = PyrexTypes.widest_numeric_type(operand1.type, operand2.type)
self.operand1 = operand1.coerce_to(common_type, env)
self.operand2 = operand2.coerce_to(common_type, env)
elif not self.types_okay(operand1, op, operand2):
error(self.pos, "Invalid types for '%s' (%s, %s)" % error(self.pos, "Invalid types for '%s' (%s, %s)" %
(self.operator, operand1.type, operand2.type)) (self.operator, operand1.type, operand2.type))
...@@ -4735,6 +4805,16 @@ class CmpNode(object): ...@@ -4735,6 +4805,16 @@ class CmpNode(object):
richcmp_constants[op], richcmp_constants[op],
code.error_goto_if_null(result_code, self.pos))) code.error_goto_if_null(result_code, self.pos)))
code.put_gotref(result_code) code.put_gotref(result_code)
elif operand1.type.is_complex and not code.globalstate.directives['c99_complex']:
if op == "!=": negation = "!"
else: negation = ""
code.putln("%s = %s(%s%s(%s, %s));" % (
result_code,
coerce_result,
negation,
operand1.type.unary_op('eq'),
operand1.result(),
operand2.result()))
else: else:
type1 = operand1.type type1 = operand1.type
type2 = operand2.type type2 = operand2.type
...@@ -4850,6 +4930,17 @@ class PrimaryCmpNode(ExprNode, CmpNode): ...@@ -4850,6 +4930,17 @@ class PrimaryCmpNode(ExprNode, CmpNode):
self.not_const() self.not_const()
def calculate_result_code(self): def calculate_result_code(self):
if self.operand1.type.is_complex:
if self.operator == "!=":
negation = "!"
else:
negation = ""
return "(%s%s(%s, %s))" % (
negation,
self.operand1.type.binary_op('=='),
self.operand1.result(),
self.operand2.result())
else:
return "(%s %s %s)" % ( return "(%s %s %s)" % (
self.operand1.result(), self.operand1.result(),
self.c_operator(self.operator), self.c_operator(self.operator),
...@@ -5129,7 +5220,7 @@ class CoerceToPyTypeNode(CoercionNode): ...@@ -5129,7 +5220,7 @@ class CoerceToPyTypeNode(CoercionNode):
CoercionNode.__init__(self, arg) CoercionNode.__init__(self, arg)
self.type = py_object_type self.type = py_object_type
self.is_temp = 1 self.is_temp = 1
if not arg.type.to_py_function or not arg.type.create_convert_utility_code(env): if not arg.type.create_to_py_utility_code(env):
error(arg.pos, error(arg.pos,
"Cannot convert '%s' to Python object" % arg.type) "Cannot convert '%s' to Python object" % arg.type)
...@@ -5167,7 +5258,7 @@ class CoerceFromPyTypeNode(CoercionNode): ...@@ -5167,7 +5258,7 @@ class CoerceFromPyTypeNode(CoercionNode):
CoercionNode.__init__(self, arg) CoercionNode.__init__(self, arg)
self.type = result_type self.type = result_type
self.is_temp = 1 self.is_temp = 1
if not result_type.from_py_function: if not result_type.create_from_py_utility_code(env):
error(arg.pos, error(arg.pos,
"Cannot convert Python object to '%s'" % result_type) "Cannot convert Python object to '%s'" % result_type)
if self.type.is_string and self.arg.is_ephemeral(): if self.type.is_string and self.arg.is_ephemeral():
...@@ -5224,6 +5315,29 @@ class CoerceToBooleanNode(CoercionNode): ...@@ -5224,6 +5315,29 @@ class CoerceToBooleanNode(CoercionNode):
self.arg.py_result(), self.arg.py_result(),
code.error_goto_if_neg(self.result(), self.pos))) code.error_goto_if_neg(self.result(), self.pos)))
class CoerceToComplexNode(CoercionNode):
def __init__(self, arg, dst_type, env):
if arg.type.is_complex:
arg = arg.coerce_to_simple(env)
self.type = dst_type
CoercionNode.__init__(self, arg)
dst_type.create_declaration_utility_code(env)
def calculate_result_code(self):
if self.arg.type.is_complex:
real_part = "__Pyx_REAL_PART(%s)" % self.arg.result()
imag_part = "__Pyx_IMAG_PART(%s)" % self.arg.result()
else:
real_part = self.arg.result()
imag_part = "0"
return "%s(%s, %s)" % (
self.type.from_parts,
real_part,
imag_part)
def generate_result_code(self, code):
pass
class CoerceToTempNode(CoercionNode): class CoerceToTempNode(CoercionNode):
# This node is used to force the result of another node # This node is used to force the result of another node
......
...@@ -125,6 +125,7 @@ class Context(object): ...@@ -125,6 +125,7 @@ class Context(object):
_check_c_declarations, _check_c_declarations,
AnalyseExpressionsTransform(self), AnalyseExpressionsTransform(self),
OptimizeBuiltinCalls(), OptimizeBuiltinCalls(),
ConstantFolding(),
IterationTransform(), IterationTransform(),
SwitchTransform(), SwitchTransform(),
FinalOptimizePhase(self), FinalOptimizePhase(self),
......
...@@ -100,6 +100,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -100,6 +100,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if h_types or h_vars or h_funcs or h_extension_types: if h_types or h_vars or h_funcs or h_extension_types:
result.h_file = replace_suffix(result.c_file, ".h") result.h_file = replace_suffix(result.c_file, ".h")
h_code = Code.CCodeWriter() h_code = Code.CCodeWriter()
Code.GlobalState(h_code)
if options.generate_pxi: if options.generate_pxi:
result.i_file = replace_suffix(result.c_file, ".pxi") result.i_file = replace_suffix(result.c_file, ".pxi")
i_code = Code.PyrexCodeWriter(result.i_file) i_code = Code.PyrexCodeWriter(result.i_file)
...@@ -161,6 +162,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -161,6 +162,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if api_funcs or has_api_extension_types: if api_funcs or has_api_extension_types:
result.api_file = replace_suffix(result.c_file, "_api.h") result.api_file = replace_suffix(result.c_file, "_api.h")
h_code = Code.CCodeWriter() h_code = Code.CCodeWriter()
Code.GlobalState(h_code)
name = self.api_name(env) name = self.api_name(env)
guard = Naming.api_guard_prefix + name guard = Naming.api_guard_prefix + name
h_code.put_h_guard(guard) h_code.put_h_guard(guard)
...@@ -237,23 +239,31 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -237,23 +239,31 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def generate_c_code(self, env, options, result): def generate_c_code(self, env, options, result):
modules = self.referenced_modules modules = self.referenced_modules
if Options.annotate or options.annotate: if Options.annotate or options.annotate:
code = Annotate.AnnotationCCodeWriter() emit_linenums = False
rootwriter = Annotate.AnnotationCCodeWriter()
else: else:
code = Code.CCodeWriter(emit_linenums=options.emit_linenums) emit_linenums = options.emit_linenums
h_code = code.insertion_point() rootwriter = Code.CCodeWriter(emit_linenums=emit_linenums)
globalstate = Code.GlobalState(rootwriter, emit_linenums)
globalstate.initialize_main_c_code()
h_code = globalstate['h_code']
self.generate_module_preamble(env, modules, h_code) self.generate_module_preamble(env, modules, h_code)
code.globalstate.module_pos = self.pos globalstate.module_pos = self.pos
code.globalstate.directives = self.directives globalstate.directives = self.directives
code.globalstate.use_utility_code(refcount_utility_code) globalstate.use_utility_code(refcount_utility_code)
code = globalstate['before_global_var']
code.putln('#define __Pyx_MODULE_NAME "%s"' % self.full_module_name) code.putln('#define __Pyx_MODULE_NAME "%s"' % self.full_module_name)
code.putln("int %s%s = %s;" % (Naming.module_is_main, self.full_module_name.replace('.', '__'), int(Options.embed)))
code.putln("") code.putln("")
code.putln("/* Implementation of %s */" % env.qualified_name) code.putln("/* Implementation of %s */" % env.qualified_name)
code.globalstate.insert_global_var_declarations_into(code) code = globalstate['all_the_rest']
self.generate_cached_builtins_decls(env, code) self.generate_cached_builtins_decls(env, code)
self.body.generate_function_definitions(env, code) self.body.generate_function_definitions(env, code)
...@@ -270,20 +280,21 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -270,20 +280,21 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if Options.embed: if Options.embed:
self.generate_main_method(env, code) self.generate_main_method(env, code)
self.generate_filename_table(code) self.generate_filename_table(code)
self.generate_utility_functions(env, code, h_code)
self.generate_declarations_for_modules(env, modules, h_code) self.generate_declarations_for_modules(env, modules, globalstate)
h_code.write('\n') h_code.write('\n')
code.globalstate.close_global_decls() for codetup, name in env.utility_code_list:
globalstate.use_utility_code(codetup, name)
globalstate.finalize_main_c_code()
f = open_new_file(result.c_file) f = open_new_file(result.c_file)
code.copyto(f) rootwriter.copyto(f)
f.close() f.close()
result.c_file_generated = 1 result.c_file_generated = 1
if Options.annotate or options.annotate: if Options.annotate or options.annotate:
self.annotate(code) self.annotate(rootwriter)
code.save_annotation(result.main_source_file, result.c_file) rootwriter.save_annotation(result.main_source_file, result.c_file)
def find_referenced_modules(self, env, module_list, modules_seen): def find_referenced_modules(self, env, module_list, modules_seen):
if env not in modules_seen: if env not in modules_seen:
...@@ -381,18 +392,20 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -381,18 +392,20 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_exttype_vtable_struct(entry, code) self.generate_exttype_vtable_struct(entry, code)
self.generate_exttype_vtabptr_declaration(entry, code) self.generate_exttype_vtabptr_declaration(entry, code)
def generate_declarations_for_modules(self, env, modules, code): def generate_declarations_for_modules(self, env, modules, globalstate):
code.putln("") typecode = globalstate['type_declarations']
code.putln("/* Type declarations */") typecode.putln("")
typecode.putln("/* Type declarations */")
vtab_list, vtabslot_list = self.sort_type_hierarchy(modules, env) vtab_list, vtabslot_list = self.sort_type_hierarchy(modules, env)
self.generate_type_definitions( self.generate_type_definitions(
env, modules, vtab_list, vtabslot_list, code) env, modules, vtab_list, vtabslot_list, typecode)
modulecode = globalstate['module_declarations']
for module in modules: for module in modules:
defined_here = module is env defined_here = module is env
code.putln("/* Module declarations from %s */" % modulecode.putln("/* Module declarations from %s */" %
module.qualified_name.encode("ASCII", "ignore")) module.qualified_name.encode("ASCII", "ignore"))
self.generate_global_declarations(module, code, defined_here) self.generate_global_declarations(module, modulecode, defined_here)
self.generate_cfunction_predeclarations(module, code, defined_here) self.generate_cfunction_predeclarations(module, modulecode, defined_here)
def generate_module_preamble(self, env, cimported_modules, code): def generate_module_preamble(self, env, cimported_modules, code):
code.putln('/* Generated by Cython %s on %s */' % ( code.putln('/* Generated by Cython %s on %s */' % (
...@@ -508,6 +521,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -508,6 +521,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln(" #ifndef __cdecl") code.putln(" #ifndef __cdecl")
code.putln(" #define __cdecl") code.putln(" #define __cdecl")
code.putln(" #endif") code.putln(" #endif")
code.putln(" #ifndef __fastcall")
code.putln(" #define __fastcall")
code.putln(" #endif")
code.putln("#else") code.putln("#else")
code.putln(" #define _USE_MATH_DEFINES") code.putln(" #define _USE_MATH_DEFINES")
code.putln("#endif") code.putln("#endif")
...@@ -534,6 +550,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -534,6 +550,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("#include <math.h>") code.putln("#include <math.h>")
code.putln("#define %s" % Naming.api_guard_prefix + self.api_name(env)) code.putln("#define %s" % Naming.api_guard_prefix + self.api_name(env))
self.generate_includes(env, cimported_modules, code) self.generate_includes(env, cimported_modules, code)
if env.directives['c99_complex']:
code.putln("#ifndef _Complex_I")
code.putln("#include <complex.h>")
code.putln("#endif")
code.putln("#define __PYX_USE_C99_COMPLEX defined(_Complex_I)")
code.putln('') code.putln('')
code.put(Nodes.utility_function_predeclarations) code.put(Nodes.utility_function_predeclarations)
code.put(PyrexTypes.type_conversion_predeclarations) code.put(PyrexTypes.type_conversion_predeclarations)
...@@ -1805,14 +1826,17 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1805,14 +1826,17 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
env.module_cname, env.module_cname,
Naming.builtins_cname, Naming.builtins_cname,
code.error_goto(self.pos))) code.error_goto(self.pos)))
if Options.embed:
__main__name = code.globalstate.get_py_string_const( __main__name = code.globalstate.get_py_string_const(
EncodedString("__main__"), identifier=True) EncodedString("__main__"), identifier=True)
code.putln("if (%s%s) {" % (Naming.module_is_main, self.full_module_name.replace('.', '__')))
code.putln( code.putln(
'if (__Pyx_SetAttrString(%s, "__name__", %s) < 0) %s;' % ( 'if (__Pyx_SetAttrString(%s, "__name__", %s) < 0) %s;' % (
env.module_cname, env.module_cname,
__main__name.cname, __main__name.cname,
code.error_goto(self.pos))) code.error_goto(self.pos)))
code.putln("}")
if Options.pre_import is not None: if Options.pre_import is not None:
code.putln( code.putln(
'%s = PyImport_AddModule(__Pyx_NAMESTR("%s"));' % ( '%s = PyImport_AddModule(__Pyx_NAMESTR("%s"));' % (
...@@ -2040,22 +2064,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -2040,22 +2064,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"%s = &%s;" % ( "%s = &%s;" % (
type.typeptr_cname, type.typeobj_cname)) type.typeptr_cname, type.typeobj_cname))
def generate_utility_functions(self, env, code, h_code):
for codetup, name in env.utility_code_list:
code.globalstate.use_utility_code(codetup, name)
code.globalstate.put_utility_code_protos(h_code)
code.putln("")
code.putln("/* Runtime support code */")
code.putln("")
code.putln("static void %s(void) {" % Naming.fileinit_cname)
code.putln("%s = %s;" %
(Naming.filetable_cname, Naming.filenames_cname))
code.putln("}")
code.globalstate.put_utility_code_defs(code)
code.put(PyrexTypes.type_conversion_functions)
code.putln("")
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
# #
# Runtime support code # Runtime support code
...@@ -2460,7 +2468,11 @@ static __Pyx_RefnannyAPIStruct *__Pyx_Refnanny = NULL; ...@@ -2460,7 +2468,11 @@ static __Pyx_RefnannyAPIStruct *__Pyx_Refnanny = NULL;
main_method = UtilityCode( main_method = UtilityCode(
impl = """ impl = """
#if PY_MAJOR_VERSION < 3 || (!defined(WIN32) && !defined(MS_WINDOWS))
int main(int argc, char** argv) { int main(int argc, char** argv) {
#else
int wmain(int argc, wchar_t **argv) {
#endif
int r = 0; int r = 0;
PyObject* m = NULL; PyObject* m = NULL;
Py_SetProgramName(argv[0]); Py_SetProgramName(argv[0]);
......
...@@ -44,6 +44,7 @@ vtabptr_prefix = pyrex_prefix + "vtabptr_" ...@@ -44,6 +44,7 @@ vtabptr_prefix = pyrex_prefix + "vtabptr_"
vtabstruct_prefix = pyrex_prefix + "vtabstruct_" vtabstruct_prefix = pyrex_prefix + "vtabstruct_"
opt_arg_prefix = pyrex_prefix + "opt_args_" opt_arg_prefix = pyrex_prefix + "opt_args_"
convert_func_prefix = pyrex_prefix + "convert_" convert_func_prefix = pyrex_prefix + "convert_"
module_is_main = pyrex_prefix + "module_is_main_"
args_cname = pyrex_prefix + "args" args_cname = pyrex_prefix + "args"
pykwdlist_cname = pyrex_prefix + "pyargnames" pykwdlist_cname = pyrex_prefix + "pyargnames"
......
...@@ -569,6 +569,13 @@ class CFuncDeclaratorNode(CDeclaratorNode): ...@@ -569,6 +569,13 @@ class CFuncDeclaratorNode(CDeclaratorNode):
nogil = self.nogil, with_gil = self.with_gil, is_overridable = self.overridable) nogil = self.nogil, with_gil = self.with_gil, is_overridable = self.overridable)
if self.optional_arg_count: if self.optional_arg_count:
func_type.op_arg_struct = PyrexTypes.c_ptr_type(self.op_args_struct.type) func_type.op_arg_struct = PyrexTypes.c_ptr_type(self.op_args_struct.type)
callspec = env.directives['callspec']
if callspec:
current = func_type.calling_convention
if current and current != callspec:
error(self.pos, "cannot have both '%s' and '%s' "
"calling conventions" % (current, callspec))
func_type.calling_convention = callspec
return self.base.analyse(func_type, env) return self.base.analyse(func_type, env)
...@@ -650,6 +657,7 @@ class CSimpleBaseTypeNode(CBaseTypeNode): ...@@ -650,6 +657,7 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
# is_basic_c_type boolean # is_basic_c_type boolean
# signed boolean # signed boolean
# longness integer # longness integer
# complex boolean
# is_self_arg boolean Is self argument of C method # is_self_arg boolean Is self argument of C method
child_attrs = [] child_attrs = []
...@@ -690,6 +698,11 @@ class CSimpleBaseTypeNode(CBaseTypeNode): ...@@ -690,6 +698,11 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
self.arg_name = self.name self.arg_name = self.name
else: else:
error(self.pos, "'%s' is not a type identifier" % self.name) error(self.pos, "'%s' is not a type identifier" % self.name)
if self.complex:
if not type.is_numeric or type.is_complex:
error(self.pos, "can only complexify c numeric types")
type = PyrexTypes.CComplexType(type)
type.create_declaration_utility_code(env)
if type: if type:
return type return type
else: else:
...@@ -763,15 +776,26 @@ class CVarDefNode(StatNode): ...@@ -763,15 +776,26 @@ class CVarDefNode(StatNode):
dest_scope = env dest_scope = env
self.dest_scope = dest_scope self.dest_scope = dest_scope
base_type = self.base_type.analyse(env) base_type = self.base_type.analyse(env)
need_property = False
if (dest_scope.is_c_class_scope if (dest_scope.is_c_class_scope
and self.visibility == 'public' and self.visibility == 'public'
and base_type.is_pyobject and base_type.is_pyobject
and (base_type.is_builtin_type or base_type.is_extension_type)): and (base_type.is_builtin_type or base_type.is_extension_type)):
self.need_properties = [] # If the field is settable and extension type, then the CPython mechanism does
# not do enough type-checking for us.
need_property = True need_property = True
elif (base_type.is_typedef and base_type.typedef_is_external
and (self.visibility in ('public', 'readonly'))):
# If the field is an external typedef, we cannot be sure about the type,
# so do conversion ourself rather than rely on the CPython mechanism (through
# a property; made in AnalyseDeclarationsTransform).
need_property = True
if need_property:
visibility = 'private' visibility = 'private'
self.need_properties = []
else: else:
need_property = False
visibility = self.visibility visibility = self.visibility
for declarator in self.declarators: for declarator in self.declarators:
...@@ -1867,6 +1891,10 @@ class DefNode(FuncDefNode): ...@@ -1867,6 +1891,10 @@ class DefNode(FuncDefNode):
has_star_or_kw_args = self.star_arg is not None \ has_star_or_kw_args = self.star_arg is not None \
or self.starstar_arg is not None or has_kwonly_args or self.starstar_arg is not None or has_kwonly_args
for arg in self.args:
if not arg.type.is_pyobject and arg.type.from_py_function is None:
arg.type.create_from_py_utility_code(env)
if not self.signature_has_generic_args(): if not self.signature_has_generic_args():
if has_star_or_kw_args: if has_star_or_kw_args:
error(self.pos, "This method cannot have * or keyword arguments") error(self.pos, "This method cannot have * or keyword arguments")
...@@ -3069,10 +3097,10 @@ class InPlaceAssignmentNode(AssignmentNode): ...@@ -3069,10 +3097,10 @@ class InPlaceAssignmentNode(AssignmentNode):
if c_op == "//": if c_op == "//":
c_op = "/" c_op = "/"
elif c_op == "**": elif c_op == "**":
if self.lhs.type.is_int and self.rhs.type.is_int:
error(self.pos, "** with two C int types is ambiguous")
else:
error(self.pos, "No C inplace power operator") error(self.pos, "No C inplace power operator")
elif self.lhs.type.is_complex and not code.globalstate.directives['c99_complex']:
error(self.pos, "Inplace operators not implemented for complex types.")
# have to do assignment directly to avoid side-effects # have to do assignment directly to avoid side-effects
if isinstance(self.lhs, ExprNodes.IndexNode) and self.lhs.is_buffer_access: if isinstance(self.lhs, ExprNodes.IndexNode) and self.lhs.is_buffer_access:
self.lhs.generate_buffer_setitem_code(self.rhs, code, c_op) self.lhs.generate_buffer_setitem_code(self.rhs, code, c_op)
......
...@@ -54,7 +54,7 @@ c_line_in_traceback = 1 ...@@ -54,7 +54,7 @@ c_line_in_traceback = 1
embed = False embed = False
# Declare pragmas # Declare compiler directives
option_defaults = { option_defaults = {
'boundscheck' : True, 'boundscheck' : True,
'nonecheck' : False, 'nonecheck' : False,
...@@ -64,6 +64,9 @@ option_defaults = { ...@@ -64,6 +64,9 @@ option_defaults = {
'cdivision': True, # Will be False in 0.12 'cdivision': True, # Will be False in 0.12
'cdivision_warnings': False, 'cdivision_warnings': False,
'always_allow_keywords': False, 'always_allow_keywords': False,
'wraparound' : True,
'c99_complex' : False, # Don't use macro wrappers for complex arith, not sure what to name this...
'callspec' : "",
} }
# Override types possibilities above, if needed # Override types possibilities above, if needed
...@@ -94,6 +97,11 @@ def parse_option_value(name, value): ...@@ -94,6 +97,11 @@ def parse_option_value(name, value):
if value == "True": return True if value == "True": return True
elif value == "False": return False elif value == "False": return False
else: raise ValueError("%s directive must be set to True or False" % name) else: raise ValueError("%s directive must be set to True or False" % name)
elif type is int:
try:
return int(value)
except ValueError:
raise ValueError("%s directive must be set to an integer" % name)
else: else:
assert False assert False
......
...@@ -435,6 +435,11 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations): ...@@ -435,6 +435,11 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
raise PostParseError(dec.function.pos, raise PostParseError(dec.function.pos,
'The %s option takes one compile-time boolean argument' % optname) 'The %s option takes one compile-time boolean argument' % optname)
return (optname, args[0].value) return (optname, args[0].value)
elif optiontype is str:
if kwds is not None or len(args) != 1 or not isinstance(args[0], StringNode):
raise PostParseError(dec.function.pos,
'The %s option takes one compile-time string argument' % optname)
return (optname, args[0].value)
elif optiontype is dict: elif optiontype is dict:
if len(args) != 0: if len(args) != 0:
raise PostParseError(dec.function.pos, raise PostParseError(dec.function.pos,
...@@ -589,62 +594,6 @@ class WithTransform(CythonTransform, SkipDeclarations): ...@@ -589,62 +594,6 @@ class WithTransform(CythonTransform, SkipDeclarations):
return node return node
class ComprehensionTransform(VisitorTransform):
"""Prevent the target of list/set/dict comprehensions from leaking by
moving it into a temp variable. This mimics the behaviour of all
comprehensions in Py3 and of generator expressions in Py2.x.
This must run before the IterationTransform, which might replace
for-loops with while-loops. We only handle for-loops here.
"""
def visit_ModuleNode(self, node):
self.comprehension_targets = {}
self.visitchildren(node)
return node
visit_Node = VisitorTransform.recurse_to_children
def visit_ComprehensionNode(self, node):
if type(node.loop) not in (Nodes.ForInStatNode,
Nodes.ForFromStatNode):
# this should not happen!
self.visitchildren(node)
return node
outer_comprehension_targets = self.comprehension_targets
self.comprehension_targets = outer_comprehension_targets.copy()
# find all NameNodes in the loop target
target_name_collector = NameNodeCollector()
target_name_collector.visit(node.loop.target)
targets = target_name_collector.name_nodes
# create a temp variable for each target name
temps = []
for target in targets:
handle = TempHandle(target.type)
temps.append(handle)
self.comprehension_targets[target.entry.cname] = handle.ref(node.pos)
# replace name references in the loop code by their temp node
self.visitchildren(node, ['loop'])
loop = node.loop
if type(loop) is Nodes.ForFromStatNode and loop.target.type.is_numeric:
loop.loopvar_node = loop.target
node.loop = TempsBlockNode(node.pos, body=node.loop, temps=temps)
self.comprehension_targets = outer_comprehension_targets
return node
def visit_NameNode(self, node):
if node.entry:
replacement = self.comprehension_targets.get(node.entry.cname)
if replacement is not None:
return replacement
return node
class DecoratorTransform(CythonTransform, SkipDeclarations): class DecoratorTransform(CythonTransform, SkipDeclarations):
def visit_DefNode(self, func_node): def visit_DefNode(self, func_node):
...@@ -676,6 +625,12 @@ property NAME: ...@@ -676,6 +625,12 @@ property NAME:
ATTR = value ATTR = value
""", level='c_class') """, level='c_class')
readonly_property = TreeFragment(u"""
property NAME:
def __get__(self):
return ATTR
""", level='c_class')
def __call__(self, root): def __call__(self, root):
self.env_stack = [root.scope] self.env_stack = [root.scope]
# needed to determine if a cdef var is declared after it's used. # needed to determine if a cdef var is declared after it's used.
...@@ -752,7 +707,7 @@ property NAME: ...@@ -752,7 +707,7 @@ property NAME:
# mechanism for them. # mechanism for them.
stats = [] stats = []
for entry in node.need_properties: for entry in node.need_properties:
property = self.create_Property(entry) property = self.create_Property(entry, node.visibility == 'readonly')
property.analyse_declarations(node.dest_scope) property.analyse_declarations(node.dest_scope)
self.visit(property) self.visit(property)
stats.append(property) stats.append(property)
...@@ -760,8 +715,12 @@ property NAME: ...@@ -760,8 +715,12 @@ property NAME:
else: else:
return None return None
def create_Property(self, entry): def create_Property(self, entry, readonly):
property = self.basic_property.substitute({ if readonly:
template = self.readonly_property
else:
template = self.basic_property
property = template.substitute({
u"ATTR": AttributeNode(pos=entry.pos, u"ATTR": AttributeNode(pos=entry.pos,
obj=NameNode(pos=entry.pos, name="self"), obj=NameNode(pos=entry.pos, name="self"),
attribute=entry.name), attribute=entry.name),
......
...@@ -1753,7 +1753,7 @@ def p_calling_convention(s): ...@@ -1753,7 +1753,7 @@ def p_calling_convention(s):
else: else:
return "" return ""
calling_convention_words = ("__stdcall", "__cdecl") calling_convention_words = ("__stdcall", "__cdecl", "__fastcall")
def p_c_complex_base_type(s): def p_c_complex_base_type(s):
# s.sy == '(' # s.sy == '('
...@@ -1770,6 +1770,7 @@ def p_c_simple_base_type(s, self_flag, nonempty): ...@@ -1770,6 +1770,7 @@ def p_c_simple_base_type(s, self_flag, nonempty):
is_basic = 0 is_basic = 0
signed = 1 signed = 1
longness = 0 longness = 0
complex = 0
module_path = [] module_path = []
pos = s.position() pos = s.position()
if not s.sy == 'IDENT': if not s.sy == 'IDENT':
...@@ -1788,6 +1789,9 @@ def p_c_simple_base_type(s, self_flag, nonempty): ...@@ -1788,6 +1789,9 @@ def p_c_simple_base_type(s, self_flag, nonempty):
s.next() s.next()
else: else:
name = 'int' name = 'int'
if s.sy == 'IDENT' and s.systring == 'complex':
complex = 1
s.next()
elif looking_at_dotted_name(s): elif looking_at_dotted_name(s):
#print "p_c_simple_base_type: looking_at_type_name at", s.position() #print "p_c_simple_base_type: looking_at_type_name at", s.position()
name = s.systring name = s.systring
...@@ -1816,7 +1820,8 @@ def p_c_simple_base_type(s, self_flag, nonempty): ...@@ -1816,7 +1820,8 @@ def p_c_simple_base_type(s, self_flag, nonempty):
type_node = Nodes.CSimpleBaseTypeNode(pos, type_node = Nodes.CSimpleBaseTypeNode(pos,
name = name, module_path = module_path, name = name, module_path = module_path,
is_basic_c_type = is_basic, signed = signed, is_basic_c_type = is_basic, signed = signed,
longness = longness, is_self_arg = self_flag) complex = complex, longness = longness,
is_self_arg = self_flag)
# Treat trailing [] on type as buffer access if it appears in a context # Treat trailing [] on type as buffer access if it appears in a context
...@@ -2561,7 +2566,7 @@ def p_code(s, level=None): ...@@ -2561,7 +2566,7 @@ def p_code(s, level=None):
repr(s.sy), repr(s.systring))) repr(s.sy), repr(s.systring)))
return body return body
COMPILER_DIRECTIVE_COMMENT_RE = re.compile(r"^#\s*cython:\s*([a-z_]+)\s*=(.*)$") COMPILER_DIRECTIVE_COMMENT_RE = re.compile(r"^#\s*cython:\s*(\w+)\s*=(.*)$")
def p_compiler_directive_comments(s): def p_compiler_directive_comments(s):
result = {} result = {}
...@@ -2571,10 +2576,10 @@ def p_compiler_directive_comments(s): ...@@ -2571,10 +2576,10 @@ def p_compiler_directive_comments(s):
name = m.group(1) name = m.group(1)
try: try:
value = Options.parse_option_value(str(name), str(m.group(2).strip())) value = Options.parse_option_value(str(name), str(m.group(2).strip()))
except ValueError, e:
s.error(e.args[0], fatal=False)
if value is not None: # can be False! if value is not None: # can be False!
result[name] = value result[name] = value
except ValueError, e:
s.error(e.args[0], fatal=False)
s.next() s.next()
return result return result
......
...@@ -33,6 +33,7 @@ class PyrexType(BaseType): ...@@ -33,6 +33,7 @@ class PyrexType(BaseType):
# is_int boolean Is a C integer type # is_int boolean Is a C integer type
# is_longlong boolean Is a long long or unsigned long long. # is_longlong boolean Is a long long or unsigned long long.
# is_float boolean Is a C floating point type # is_float boolean Is a C floating point type
# is_complex boolean Is a C complex type
# is_void boolean Is the C void type # is_void boolean Is the C void type
# is_array boolean Is a C array type # is_array boolean Is a C array type
# is_ptr boolean Is a C pointer type # is_ptr boolean Is a C pointer type
...@@ -83,6 +84,7 @@ class PyrexType(BaseType): ...@@ -83,6 +84,7 @@ class PyrexType(BaseType):
is_int = 0 is_int = 0
is_longlong = 0 is_longlong = 0
is_float = 0 is_float = 0
is_complex = 0
is_void = 0 is_void = 0
is_array = 0 is_array = 0
is_ptr = 0 is_ptr = 0
...@@ -140,9 +142,15 @@ class PyrexType(BaseType): ...@@ -140,9 +142,15 @@ class PyrexType(BaseType):
return 1 return 1
def is_simple_buffer_dtype(self): def is_simple_buffer_dtype(self):
return (self.is_int or self.is_float or self.is_pyobject or return (self.is_int or self.is_float or self.is_complex or self.is_pyobject or
self.is_extension_type or self.is_ptr) self.is_extension_type or self.is_ptr)
def struct_nesting_depth(self):
# Returns the number levels of nested structs. This is
# used for constructing a stack for walking the run-time
# type information of the struct.
return 1
class CTypedefType(BaseType): class CTypedefType(BaseType):
# #
# Pseudo-type defined with a ctypedef statement in a # Pseudo-type defined with a ctypedef statement in a
...@@ -153,12 +161,15 @@ class CTypedefType(BaseType): ...@@ -153,12 +161,15 @@ class CTypedefType(BaseType):
# qualified_name string # qualified_name string
# typedef_cname string # typedef_cname string
# typedef_base_type PyrexType # typedef_base_type PyrexType
# typedef_is_external bool
is_typedef = 1 is_typedef = 1
typedef_is_external = 0
def __init__(self, cname, base_type): def __init__(self, cname, base_type, is_external=0):
self.typedef_cname = cname self.typedef_cname = cname
self.typedef_base_type = base_type self.typedef_base_type = base_type
self.typedef_is_external = is_external
def resolve(self): def resolve(self):
return self.typedef_base_type.resolve() return self.typedef_base_type.resolve()
...@@ -434,8 +445,11 @@ class CType(PyrexType): ...@@ -434,8 +445,11 @@ class CType(PyrexType):
exception_value = None exception_value = None
exception_check = 1 exception_check = 1
def create_convert_utility_code(self, env): def create_to_py_utility_code(self, env):
return True return self.to_py_function is not None
def create_from_py_utility_code(self, env):
return self.from_py_function is not None
def error_condition(self, result_code): def error_condition(self, result_code):
conds = [] conds = []
...@@ -693,7 +707,191 @@ class CFloatType(CNumericType): ...@@ -693,7 +707,191 @@ class CFloatType(CNumericType):
self.math_h_modifier = math_h_modifier self.math_h_modifier = math_h_modifier
def assignable_from_resolved_type(self, src_type): def assignable_from_resolved_type(self, src_type):
return src_type.is_numeric or src_type is error_type return (src_type.is_numeric and not src_type.is_complex) or src_type is error_type
class CComplexType(CNumericType):
is_complex = 1
to_py_function = "__pyx_PyObject_from_complex"
has_attributes = 1
scope = None
def __init__(self, real_type):
self.real_type = real_type
CNumericType.__init__(self, real_type.rank + 0.5, real_type.signed)
self.binops = {}
self.from_parts = "%s_from_parts" % self.specalization_name()
def __cmp__(self, other):
if isinstance(self, CComplexType) and isinstance(other, CComplexType):
return cmp(self.real_type, other.real_type)
else:
return 1
def __hash__(self):
return ~hash(self.real_type)
def sign_and_name(self):
return Naming.type_prefix + self.real_type.specalization_name() + "_complex"
def assignable_from_resolved_type(self, src_type):
return (src_type.is_complex and self.real_type.assignable_from_resolved_type(src_type.real_type)
or src_type.is_numeric and self.real_type.assignable_from_resolved_type(src_type)
or src_type is error_type)
def attributes_known(self):
if self.scope is None:
import Symtab
self.scope = Symtab.StructOrUnionScope(self.specalization_name())
self.scope.declare_var("real", self.real_type, None, "real")
self.scope.declare_var("imag", self.real_type, None, "imag")
return True
def create_declaration_utility_code(self, env):
# This must always be run, because a single CComplexType instance can be shared
# across multiple compilations (the one created in the module scope)
env.use_utility_code(complex_generic_utility_code)
env.use_utility_code(
complex_arithmatic_utility_code.specialize(self,
math_h_modifier = self.real_type.math_h_modifier,
real_type = self.real_type.declaration_code('')))
return True
def create_from_py_utility_code(self, env):
self.real_type.create_from_py_utility_code(env)
env.use_utility_code(
complex_conversion_utility_code.specialize(self,
math_h_modifier = self.real_type.math_h_modifier,
real_type = self.real_type.declaration_code(''),
type_convert = self.real_type.from_py_function))
self.from_py_function = "__pyx_PyObject_As_" + self.specalization_name()
return True
def lookup_op(self, nargs, op):
try:
return self.binops[nargs, op]
except KeyError:
pass
try:
op_name = complex_ops[nargs, op]
self.binops[nargs, op] = func_name = "%s_%s" % (self.specalization_name(), op_name)
return func_name
except KeyError:
return None
def unary_op(self, op):
return self.lookup_op(1, op)
def binary_op(self, op):
return self.lookup_op(2, op)
complex_ops = {
(1, '-'): 'neg',
(1, 'zero'): 'is_zero',
(2, '+'): 'add',
(2, '-') : 'sub',
(2, '*'): 'mul',
(2, '/'): 'div',
(2, '=='): 'eq',
}
complex_generic_utility_code = UtilityCode(
proto="""
#if __PYX_USE_C99_COMPLEX
#define __Pyx_REAL_PART(z) __real__(z)
#define __Pyx_IMAG_PART(z) __imag__(z)
#else
#define __Pyx_REAL_PART(z) ((z).real)
#define __Pyx_IMAG_PART(z) ((z).imag)
#endif
#define __pyx_PyObject_from_complex(z) PyComplex_FromDoubles((double)__Pyx_REAL_PART(z), (double)__Pyx_IMAG_PART(z))
""")
complex_conversion_utility_code = UtilityCode(
proto="""
static %(type)s __pyx_PyObject_As_%(type_name)s(PyObject* o); /* proto */
""",
impl="""
static %(type)s __pyx_PyObject_As_%(type_name)s(PyObject* o) {
if (PyComplex_Check(o)) {
return %(type_name)s_from_parts(
(%(real_type)s)((PyComplexObject *)o)->cval.real,
(%(real_type)s)((PyComplexObject *)o)->cval.imag);
}
else {
return %(type_name)s_from_parts(%(type_convert)s(o), 0);
}
}
""")
complex_arithmatic_utility_code = UtilityCode(
proto="""
#if __PYX_USE_C99_COMPLEX
typedef %(real_type)s _Complex %(type_name)s;
#define %(type_name)s_from_parts(x, y) ((x) + (y)*(%(type)s)_Complex_I)
#define %(type_name)s_is_zero(a) ((a) == 0)
#define %(type_name)s_eq(a, b) ((a) == (b))
#define %(type_name)s_add(a, b) ((a)+(b))
#define %(type_name)s_sub(a, b) ((a)-(b))
#define %(type_name)s_mul(a, b) ((a)*(b))
#define %(type_name)s_div(a, b) ((a)/(b))
#define %(type_name)s_neg(a) (-(a))
#else
typedef struct { %(real_type)s real, imag; } %(type_name)s;
#define %(type_name)s_from_parts(x, y) ((%(type_name)s){(%(real_type)s)x, (%(real_type)s)y})
static INLINE int %(type_name)s_is_zero(%(type)s a) {
return (a.real == 0) & (a.imag == 0);
}
static INLINE int %(type_name)s_eq(%(type)s a, %(type)s b) {
return (a.real == b.real) & (a.imag == b.imag);
}
static INLINE %(type)s %(type_name)s_add(%(type)s a, %(type)s b) {
%(type)s z;
z.real = a.real + b.real;
z.imag = a.imag + b.imag;
return z;
}
static INLINE %(type)s %(type_name)s_sub(%(type)s a, %(type)s b) {
%(type)s z;
z.real = a.real - b.real;
z.imag = a.imag - b.imag;
return z;
}
static INLINE %(type)s %(type_name)s_mul(%(type)s a, %(type)s b) {
%(type)s z;
z.real = a.real * b.real - a.imag * b.imag;
z.imag = a.real * b.imag + a.imag * b.real;
return z;
}
static INLINE %(type)s %(type_name)s_div(%(type)s a, %(type)s b) {
%(type)s z;
%(real_type)s denom = b.real*b.real + b.imag*b.imag;
z.real = (a.real * b.real + a.imag * b.imag) / denom;
z.imag = (a.imag * b.real - a.real * b.imag) / denom;
return z;
}
static INLINE %(type)s %(type_name)s_neg(%(type)s a) {
%(type)s z;
z.real = -a.real;
z.imag = -a.imag;
return z;
}
#endif
""")
class CArrayType(CType): class CArrayType(CType):
...@@ -928,6 +1126,15 @@ class CFuncType(CType): ...@@ -928,6 +1126,15 @@ class CFuncType(CType):
return 1 return 1
def same_calling_convention_as(self, other): def same_calling_convention_as(self, other):
## XXX Under discussion ...
## callspec_words = ("__stdcall", "__cdecl", "__fastcall")
## cs1 = self.calling_convention
## cs2 = other.calling_convention
## if (cs1 in callspec_words or
## cs2 in callspec_words):
## return cs1 == cs2
## else:
## return True
sc1 = self.calling_convention == '__stdcall' sc1 = self.calling_convention == '__stdcall'
sc2 = other.calling_convention == '__stdcall' sc2 = other.calling_convention == '__stdcall'
return sc1 == sc2 return sc1 == sc2
...@@ -1050,19 +1257,20 @@ class CStructOrUnionType(CType): ...@@ -1050,19 +1257,20 @@ class CStructOrUnionType(CType):
self._convert_code = None self._convert_code = None
self.packed = packed self.packed = packed
def create_convert_utility_code(self, env): def create_to_py_utility_code(self, env):
if env.outer_scope is None: if env.outer_scope is None:
return False return False
if self._convert_code is None: if self._convert_code is None:
import Code import Code
code = Code.CCodeWriter() code = Code.CCodeWriter()
Code.GlobalState(code)
header = "static PyObject* %s(%s)" % (self.to_py_function, self.declaration_code('s')) header = "static PyObject* %s(%s)" % (self.to_py_function, self.declaration_code('s'))
code.putln("%s {" % header) code.putln("%s {" % header)
code.putln("PyObject* res;") code.putln("PyObject* res;")
code.putln("PyObject* member;") code.putln("PyObject* member;")
code.putln("res = PyDict_New(); if (res == NULL) return NULL;") code.putln("res = PyDict_New(); if (res == NULL) return NULL;")
for member in self.scope.var_entries: for member in self.scope.var_entries:
if member.type.to_py_function and member.type.create_convert_utility_code(env): if member.type.to_py_function and member.type.create_to_py_utility_code(env):
interned_name = env.get_string_const(member.name, identifier=True) interned_name = env.get_string_const(member.name, identifier=True)
env.add_py_string(interned_name) env.add_py_string(interned_name)
code.putln("member = %s(s.%s); if (member == NULL) goto bad;" % ( code.putln("member = %s(s.%s); if (member == NULL) goto bad;" % (
...@@ -1122,10 +1330,18 @@ class CStructOrUnionType(CType): ...@@ -1122,10 +1330,18 @@ class CStructOrUnionType(CType):
return self.is_complete() return self.is_complete()
def can_be_complex(self): def can_be_complex(self):
# Does the struct consist of exactly two floats? # Does the struct consist of exactly two identical floats?
fields = self.scope.var_entries fields = self.scope.var_entries
return len(fields) == 2 and fields[0].type.is_float and fields[1].type.is_float if len(fields) != 2: return False
a, b = fields
return (a.type.is_float and b.type.is_float and
a.type.declaration_code("") ==
b.type.declaration_code(""))
def struct_nesting_depth(self):
child_depths = [x.type.struct_nesting_depth()
for x in self.scope.var_entries]
return max(child_depths) + 1
class CEnumType(CType): class CEnumType(CType):
# name string # name string
...@@ -1228,7 +1444,10 @@ class ErrorType(PyrexType): ...@@ -1228,7 +1444,10 @@ class ErrorType(PyrexType):
to_py_function = "dummy" to_py_function = "dummy"
from_py_function = "dummy" from_py_function = "dummy"
def create_convert_utility_code(self, env): def create_to_py_utility_code(self, env):
return True
def create_from_py_utility_code(self, env):
return True return True
def declaration_code(self, entity_code, def declaration_code(self, entity_code,
...@@ -1287,6 +1506,8 @@ c_float_type = CFloatType(7, "T_FLOAT", math_h_modifier='f') ...@@ -1287,6 +1506,8 @@ c_float_type = CFloatType(7, "T_FLOAT", math_h_modifier='f')
c_double_type = CFloatType(8, "T_DOUBLE") c_double_type = CFloatType(8, "T_DOUBLE")
c_longdouble_type = CFloatType(9, math_h_modifier='l') c_longdouble_type = CFloatType(9, math_h_modifier='l')
c_double_complex_type = CComplexType(c_double_type)
c_null_ptr_type = CNullPtrType(c_void_type) c_null_ptr_type = CNullPtrType(c_void_type)
c_char_array_type = CCharArrayType(None) c_char_array_type = CCharArrayType(None)
c_char_ptr_type = CCharPtrType() c_char_ptr_type = CCharPtrType()
...@@ -1379,6 +1600,15 @@ modifiers_and_name_to_type = { ...@@ -1379,6 +1600,15 @@ modifiers_and_name_to_type = {
def widest_numeric_type(type1, type2): def widest_numeric_type(type1, type2):
# Given two numeric types, return the narrowest type # Given two numeric types, return the narrowest type
# encompassing both of them. # encompassing both of them.
if type1 == type2:
return type1
if type1.is_complex:
if type2.is_complex:
return CComplexType(widest_numeric_type(type1.real_type, type2.real_type))
else:
return CComplexType(widest_numeric_type(type1.real_type, type2))
elif type2.is_complex:
return CComplexType(widest_numeric_type(type1, type2.real_type))
if type1.is_enum and type2.is_enum: if type1.is_enum and type2.is_enum:
return c_int_type return c_int_type
elif type1 is type2: elif type1 is type2:
...@@ -1535,14 +1765,14 @@ static INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) { ...@@ -1535,14 +1765,14 @@ static INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) {
return Py_INCREF(x), x; return Py_INCREF(x), x;
m = Py_TYPE(x)->tp_as_number; m = Py_TYPE(x)->tp_as_number;
#if PY_VERSION_HEX < 0x03000000 #if PY_VERSION_HEX < 0x03000000
if (m && m->nb_long) { if (m && m->nb_int) {
name = "long";
res = PyNumber_Long(x);
}
else if (m && m->nb_int) {
name = "int"; name = "int";
res = PyNumber_Int(x); res = PyNumber_Int(x);
} }
else if (m && m->nb_long) {
name = "long";
res = PyNumber_Long(x);
}
#else #else
if (m && m->nb_int) { if (m && m->nb_int) {
name = "int"; name = "int";
...@@ -1605,3 +1835,5 @@ static INLINE size_t __Pyx_PyInt_AsSize_t(PyObject* x) { ...@@ -1605,3 +1835,5 @@ static INLINE size_t __Pyx_PyInt_AsSize_t(PyObject* x) {
} }
""" + type_conversion_functions """ + type_conversion_functions
...@@ -335,7 +335,7 @@ class Scope(object): ...@@ -335,7 +335,7 @@ class Scope(object):
cname = name cname = name
else: else:
cname = self.mangle(Naming.type_prefix, name) cname = self.mangle(Naming.type_prefix, name)
type = PyrexTypes.CTypedefType(cname, base_type) type = PyrexTypes.CTypedefType(cname, base_type, (visibility == 'extern'))
entry = self.declare_type(name, type, pos, cname, visibility) entry = self.declare_type(name, type, pos, cname, visibility)
type.qualified_name = entry.qualified_name type.qualified_name = entry.qualified_name
return entry return entry
......
...@@ -154,7 +154,14 @@ class SlotDescriptor(object): ...@@ -154,7 +154,14 @@ class SlotDescriptor(object):
code.putln("#if PY_MAJOR_VERSION >= 3") code.putln("#if PY_MAJOR_VERSION >= 3")
if flag: if flag:
code.putln("#if (PY_MAJOR_VERSION >= 3) || (Py_TPFLAGS_DEFAULT & %s)" % flag) code.putln("#if (PY_MAJOR_VERSION >= 3) || (Py_TPFLAGS_DEFAULT & %s)" % flag)
if py3k == '<RESERVED>':
code.putln("#if PY_MAJOR_VERSION >= 3")
code.putln("0, /*reserved*/")
code.putln("#else")
code.putln("%s, /*%s*/" % (value, self.slot_name)) code.putln("%s, /*%s*/" % (value, self.slot_name))
if py3k == '<RESERVED>':
code.putln("#endif")
if flag or (not py3k or not py2) or self.ifdef: if flag or (not py3k or not py2) or self.ifdef:
code.putln("#endif") code.putln("#endif")
...@@ -212,9 +219,12 @@ class MethodSlot(SlotDescriptor): ...@@ -212,9 +219,12 @@ class MethodSlot(SlotDescriptor):
def slot_code(self, scope): def slot_code(self, scope):
entry = scope.lookup_here(self.method_name) entry = scope.lookup_here(self.method_name)
if entry: if entry and entry.func_cname:
return entry.func_cname
if self.default is not None:
entry = scope.lookup_here(self.default)
if entry and entry.func_cname:
return entry.func_cname return entry.func_cname
else:
return "0" return "0"
...@@ -557,8 +567,8 @@ PyNumberMethods = ( ...@@ -557,8 +567,8 @@ PyNumberMethods = (
MethodSlot(binaryfunc, "nb_xor", "__xor__"), MethodSlot(binaryfunc, "nb_xor", "__xor__"),
MethodSlot(binaryfunc, "nb_or", "__or__"), MethodSlot(binaryfunc, "nb_or", "__or__"),
EmptySlot("nb_coerce", py3k = False), EmptySlot("nb_coerce", py3k = False),
MethodSlot(unaryfunc, "nb_int", "__int__"), MethodSlot(unaryfunc, "nb_int", "__int__", default="__long__"),
MethodSlot(unaryfunc, "nb_long", "__long__"), MethodSlot(unaryfunc, "nb_long", "__long__", default="__int__", py3k = "<RESERVED>"),
MethodSlot(unaryfunc, "nb_float", "__float__"), MethodSlot(unaryfunc, "nb_float", "__float__"),
MethodSlot(unaryfunc, "nb_oct", "__oct__", py3k = False), MethodSlot(unaryfunc, "nb_oct", "__oct__", py3k = False),
MethodSlot(unaryfunc, "nb_hex", "__hex__", py3k = False), MethodSlot(unaryfunc, "nb_hex", "__hex__", py3k = False),
......
version = '0.11.1' version = '0.11.2.rc1'
# NumPy static imports for Cython
#
# This also defines backwards-compatability buffer acquisition
# code for use in Python 2.x (or Python <= 2.5 when NumPy starts
# implementing PEP-3118 directly).
# Because of laziness, the format string of the buffer is statically
# allocated. Increase the size if this is not enough, or submit a
# patch to do this properly.
DEF _buffer_format_string_len = 255
cimport python_buffer as pybuf cimport python_buffer as pybuf
cimport stdlib cimport stdlib
...@@ -29,6 +42,8 @@ cdef extern from "numpy/arrayobject.h": ...@@ -29,6 +42,8 @@ cdef extern from "numpy/arrayobject.h":
ctypedef class numpy.dtype [object PyArray_Descr]: ctypedef class numpy.dtype [object PyArray_Descr]:
cdef int type_num cdef int type_num
cdef int itemsize "elsize"
cdef char byteorder
cdef object fields cdef object fields
cdef object names cdef object names
...@@ -53,6 +68,9 @@ cdef extern from "numpy/arrayobject.h": ...@@ -53,6 +68,9 @@ cdef extern from "numpy/arrayobject.h":
# In particular strided access is always provided regardless # In particular strided access is always provided regardless
# of flags # of flags
cdef int copy_shape, i, ndim cdef int copy_shape, i, ndim
cdef int endian_detector = 1
cdef bint little_endian = ((<char*>&endian_detector)[0] != 0)
ndim = PyArray_NDIM(self) ndim = PyArray_NDIM(self)
if sizeof(npy_intp) != sizeof(Py_ssize_t): if sizeof(npy_intp) != sizeof(Py_ssize_t):
...@@ -89,17 +107,10 @@ cdef extern from "numpy/arrayobject.h": ...@@ -89,17 +107,10 @@ cdef extern from "numpy/arrayobject.h":
cdef char* f = NULL cdef char* f = NULL
cdef dtype descr = self.descr cdef dtype descr = self.descr
cdef list stack cdef list stack
cdef int offset
cdef bint hasfields = PyDataType_HASFIELDS(descr) cdef bint hasfields = PyDataType_HASFIELDS(descr)
# Ugly hack warning:
# Cython currently will not support helper functions in
# pxd files -- so we must keep our own, manual stack!
# In addition, avoid allocation of the stack in the common
# case that we are dealing with a single non-nested datatype...
# (this would look much prettier if we could use utility
# functions).
if not hasfields and not copy_shape: if not hasfields and not copy_shape:
# do not call releasebuffer # do not call releasebuffer
info.obj = None info.obj = None
...@@ -109,6 +120,9 @@ cdef extern from "numpy/arrayobject.h": ...@@ -109,6 +120,9 @@ cdef extern from "numpy/arrayobject.h":
if not hasfields: if not hasfields:
t = descr.type_num t = descr.type_num
if ((descr.byteorder == '>' and little_endian) or
(descr.byteorder == '<' and not little_endian)):
raise ValueError("Non-native byte order not supported")
if t == NPY_BYTE: f = "b" if t == NPY_BYTE: f = "b"
elif t == NPY_UBYTE: f = "B" elif t == NPY_UBYTE: f = "B"
elif t == NPY_SHORT: f = "h" elif t == NPY_SHORT: f = "h"
...@@ -131,8 +145,12 @@ cdef extern from "numpy/arrayobject.h": ...@@ -131,8 +145,12 @@ cdef extern from "numpy/arrayobject.h":
info.format = f info.format = f
return return
else: else:
info.format = <char*>stdlib.malloc(255) # static size info.format = <char*>stdlib.malloc(_buffer_format_string_len)
f = _util_dtypestring(descr, info.format, info.format + 255) info.format[0] = '^' # Native data types, manual alignment
offset = 0
f = _util_dtypestring(descr, info.format + 1,
info.format + _buffer_format_string_len,
&offset)
f[0] = 0 # Terminate format string f[0] = 0 # Terminate format string
def __releasebuffer__(ndarray self, Py_buffer* info): def __releasebuffer__(ndarray self, Py_buffer* info):
...@@ -155,53 +173,59 @@ cdef extern from "numpy/arrayobject.h": ...@@ -155,53 +173,59 @@ cdef extern from "numpy/arrayobject.h":
cdef int PyDataType_HASFIELDS(dtype obj) cdef int PyDataType_HASFIELDS(dtype obj)
ctypedef signed int npy_byte ctypedef signed char npy_byte
ctypedef signed int npy_short ctypedef signed short npy_short
ctypedef signed int npy_int ctypedef signed int npy_int
ctypedef signed int npy_long ctypedef signed long npy_long
ctypedef signed int npy_longlong ctypedef signed long long npy_longlong
ctypedef unsigned int npy_ubyte ctypedef unsigned char npy_ubyte
ctypedef unsigned int npy_ushort ctypedef unsigned short npy_ushort
ctypedef unsigned int npy_uint ctypedef unsigned int npy_uint
ctypedef unsigned int npy_ulong ctypedef unsigned long npy_ulong
ctypedef unsigned int npy_ulonglong ctypedef unsigned long long npy_ulonglong
ctypedef float npy_float ctypedef float npy_float
ctypedef float npy_double ctypedef double npy_double
ctypedef float npy_longdouble ctypedef long double npy_longdouble
ctypedef signed int npy_int8 ctypedef signed char npy_int8
ctypedef signed int npy_int16 ctypedef signed short npy_int16
ctypedef signed int npy_int32 ctypedef signed int npy_int32
ctypedef signed int npy_int64 ctypedef signed long long npy_int64
ctypedef signed int npy_int96 ctypedef signed long long npy_int96
ctypedef signed int npy_int128 ctypedef signed long long npy_int128
ctypedef unsigned int npy_uint8 ctypedef unsigned char npy_uint8
ctypedef unsigned int npy_uint16 ctypedef unsigned short npy_uint16
ctypedef unsigned int npy_uint32 ctypedef unsigned int npy_uint32
ctypedef unsigned int npy_uint64 ctypedef unsigned long long npy_uint64
ctypedef unsigned int npy_uint96 ctypedef unsigned long long npy_uint96
ctypedef unsigned int npy_uint128 ctypedef unsigned long long npy_uint128
ctypedef float npy_float32 ctypedef float npy_float32
ctypedef float npy_float64 ctypedef double npy_float64
ctypedef float npy_float80 ctypedef long double npy_float80
ctypedef float npy_float96 ctypedef long double npy_float96
ctypedef float npy_float128 ctypedef long double npy_float128
ctypedef float complex npy_complex64
ctypedef double complex npy_complex128
ctypedef long double complex npy_complex120
ctypedef long double complex npy_complex192
ctypedef long double complex npy_complex256
ctypedef struct npy_cfloat: ctypedef struct npy_cfloat:
float real float real
float imag float imag
ctypedef struct npy_cdouble: ctypedef struct npy_cdouble:
double real float real
double imag float imag
ctypedef struct npy_clongdouble: ctypedef struct npy_clongdouble:
long double real float real
long double imag float imag
# Typedefs that matches the runtime dtype objects in # Typedefs that matches the runtime dtype objects in
# the numpy module. # the numpy module.
...@@ -228,11 +252,15 @@ ctypedef npy_float64 float64_t ...@@ -228,11 +252,15 @@ ctypedef npy_float64 float64_t
#ctypedef npy_float80 float80_t #ctypedef npy_float80 float80_t
#ctypedef npy_float128 float128_t #ctypedef npy_float128 float128_t
ctypedef npy_complex64 complex64_t
ctypedef npy_complex128 complex128_t
# The int types are mapped a bit surprising -- # The int types are mapped a bit surprising --
# numpy.int corresponds to 'l' and numpy.long to 'q' # numpy.int corresponds to 'l' and numpy.long to 'q'
ctypedef npy_long int_t ctypedef npy_long int_t
ctypedef npy_longlong long_t ctypedef npy_longlong long_t
ctypedef npy_ulong uint_t ctypedef npy_ulong uint_t
ctypedef npy_ulonglong ulong_t ctypedef npy_ulonglong ulong_t
...@@ -244,18 +272,47 @@ ctypedef npy_cfloat cfloat_t ...@@ -244,18 +272,47 @@ ctypedef npy_cfloat cfloat_t
ctypedef npy_cdouble cdouble_t ctypedef npy_cdouble cdouble_t
ctypedef npy_clongdouble clongdouble_t ctypedef npy_clongdouble clongdouble_t
ctypedef npy_cdouble complex_t
cdef inline char* _util_dtypestring(dtype descr, char* f, char* end) except NULL: cdef inline char* _util_dtypestring(dtype descr, char* f, char* end, int* offset) except NULL:
# Recursive utility function used in __getbuffer__ to get format # Recursive utility function used in __getbuffer__ to get format
# string. The new location in the format string is returned. # string. The new location in the format string is returned.
cdef dtype child cdef dtype child
cdef int delta_offset
cdef tuple i cdef tuple i
cdef int endian_detector = 1
cdef bint little_endian = ((<char*>&endian_detector)[0] != 0)
for i in descr.fields.itervalues(): for i in descr.fields.itervalues():
child = i[0] child = i[0]
new_offset = i[1]
if (end - f) - (new_offset - offset[0]) < 15:
raise RuntimeError("Format string allocated too short, see comment in numpy.pxd")
if ((child.byteorder == '>' and little_endian) or
(child.byteorder == '<' and not little_endian)):
raise ValueError("Non-native byte order not supported")
# One could encode it in the format string and have Cython
# complain instead, BUT: < and > in format strings also imply
# standardized sizes for datatypes, and we rely on native in
# order to avoid reencoding data types based on their size.
#
# A proper PEP 3118 exporter for other clients than Cython
# must deal properly with this!
# Output padding bytes
while offset[0] < new_offset:
f[0] = 120 # "x"; pad byte
f += 1
offset[0] += 1
offset[0] += child.itemsize
if not PyDataType_HASFIELDS(child): if not PyDataType_HASFIELDS(child):
t = child.type_num t = child.type_num
if end - f < 15: # this should leave room for "T{" and "}" as well if end - f < 5:
raise RuntimeError("Format string allocated too short.") raise RuntimeError("Format string allocated too short.")
# Until ticket #99 is fixed, use integers to avoid warnings # Until ticket #99 is fixed, use integers to avoid warnings
...@@ -272,19 +329,16 @@ cdef inline char* _util_dtypestring(dtype descr, char* f, char* end) except NULL ...@@ -272,19 +329,16 @@ cdef inline char* _util_dtypestring(dtype descr, char* f, char* end) except NULL
elif t == NPY_FLOAT: f[0] = 102 #"f" elif t == NPY_FLOAT: f[0] = 102 #"f"
elif t == NPY_DOUBLE: f[0] = 100 #"d" elif t == NPY_DOUBLE: f[0] = 100 #"d"
elif t == NPY_LONGDOUBLE: f[0] = 103 #"g" elif t == NPY_LONGDOUBLE: f[0] = 103 #"g"
elif t == NPY_CFLOAT: f[0] = 90; f[1] = 102; f += 1 elif t == NPY_CFLOAT: f[0] = 90; f[1] = 102; f += 1 # Zf
elif t == NPY_CDOUBLE: f[0] = 90; f[1] = 100; f += 1 elif t == NPY_CDOUBLE: f[0] = 90; f[1] = 100; f += 1 # Zd
elif t == NPY_CLONGDOUBLE: f[0] = 90; f[1] = 103; f += 1 elif t == NPY_CLONGDOUBLE: f[0] = 90; f[1] = 103; f += 1 # Zg
elif t == NPY_OBJECT: f[0] = 79 #"O" elif t == NPY_OBJECT: f[0] = 79 #"O"
else: else:
raise ValueError("unknown dtype code in numpy.pxd (%d)" % t) raise ValueError("unknown dtype code in numpy.pxd (%d)" % t)
f += 1 f += 1
else: else:
f[0] = 84 #"T" # Cython ignores struct boundary information ("T{...}"),
f[1] = 123 #"{" # so don't output it
f += 2 f = _util_dtypestring(child, f, end, offset)
f = _util_dtypestring(child, f, end)
f[0] = 125 #"}"
f += 1
return f return f
cdef extern int f() cdef extern int f1()
cdef extern int __stdcall g() cdef extern int __cdecl f2()
cdef extern int __cdecl h() cdef extern int __stdcall f3()
cdef extern int (__stdcall *p)() cdef extern int __fastcall f4()
cdef extern int (*p1)()
cdef extern int (__cdecl *p2)()
cdef extern int (__stdcall *p3)()
cdef extern int (__fastcall *p4)()
p1 = f1
p2 = f2
p3 = f3
p4 = f4
...@@ -4,7 +4,11 @@ cdef void foo(obj): ...@@ -4,7 +4,11 @@ cdef void foo(obj):
cdef int *p2 cdef int *p2
i1 = p1 # error i1 = p1 # error
p2 = obj # error p2 = obj # error
obj = p2 # error
_ERRORS = u""" _ERRORS = u"""
5:16: Cannot assign type 'char *' to 'int' 5:16: Cannot assign type 'char *' to 'int'
6:17: Cannot convert Python object to 'int *' 6:17: Cannot convert Python object to 'int *'
8:17: Cannot convert 'int *' to Python object
""" """
cimport cython
@cython.callspec("")
cdef void h1(): pass
@cython.callspec("__cdecl")
cdef void __cdecl h2(): pass
@cython.callspec("__stdcall")
cdef void __stdcall h3(): pass
@cython.callspec("__fastcall")
cdef void __fastcall h4(): pass
@cython.callspec("__cdecl")
cdef void __stdcall h5(): pass # fail
@cython.callspec("__cdecl")
cdef void __fastcall h6(): pass # fail
cdef void (*p1)()
cdef void (__cdecl *p2)()
cdef void (__stdcall *p3)()
cdef void (__fastcall *p4)()
p1 = h1
p2 = h2
p3 = h3
p4 = h4
#p1 = h2 # fail
#p1 = h3 # fail
#p1 = h4 # fail
#p2 = h1 # fail
#p2 = h3 # fail
#p2 = h4 # fail
_ERRORS = u"""
16:22: cannot have both '__stdcall' and '__cdecl' calling conventions
19:23: cannot have both '__fastcall' and '__cdecl' calling conventions
"""
#31:14: Cannot assign type 'void (__cdecl )(void)' to 'void (*)(void)'
#32:14: Cannot assign type 'void (__stdcall )(void)' to 'void (*)(void)'
#33:14: Cannot assign type 'void (__fastcall )(void)' to 'void (*)(void)'
#35:14: Cannot assign type 'void (void)' to 'void (__cdecl *)(void)'
#36:14: Cannot assign type 'void (__stdcall )(void)' to 'void (__cdecl *)(void)'
#37:14: Cannot assign type 'void (__fastcall )(void)' to 'void (__cdecl *)(void)'
cdef object blarg cdef object blarg
def foo(obj): def foo(obj):
cdef int *p cdef void *p
p = <int *>blarg # okay p = <void *>blarg # ok
p = <int *>(foo + blarg) # error - temporary p = <void *>(obj + blarg) # error - temporary
_ERRORS = u""" _ERRORS = u"""
6:5: Casting temporary Python object to non-numeric non-Python type 6:5: Casting temporary Python object to non-numeric non-Python type
""" """
...@@ -25,7 +25,7 @@ cdef object m(): ...@@ -25,7 +25,7 @@ cdef object m():
i = 42 i = 42
obj = None obj = None
17L 17L
7j <object>7j
help help
`"Hello"` `"Hello"`
import fred import fred
...@@ -85,7 +85,7 @@ _ERRORS = u""" ...@@ -85,7 +85,7 @@ _ERRORS = u"""
15: 5: Calling gil-requiring function not allowed without gil 15: 5: Calling gil-requiring function not allowed without gil
24: 9: Calling gil-requiring function not allowed without gil 24: 9: Calling gil-requiring function not allowed without gil
26:12: Assignment of Python object not allowed without gil 26:12: Assignment of Python object not allowed without gil
28: 8: Constructing complex number not allowed without gil 28:16: Constructing complex number not allowed without gil
29:12: Accessing Python global or builtin not allowed without gil 29:12: Accessing Python global or builtin not allowed without gil
30: 8: Backquote expression not allowed without gil 30: 8: Backquote expression not allowed without gil
31:15: Assignment of Python object not allowed without gil 31:15: Assignment of Python object not allowed without gil
......
a = 3
cdef void* allowed = <void*>a
cdef double* disallowed = <double*>a
_ERRORS = u"""
5:26: Python objects cannot be casted to pointers of primitive types
"""
...@@ -341,60 +341,6 @@ def explicitly_release_buffer(): ...@@ -341,60 +341,6 @@ def explicitly_release_buffer():
x = None x = None
print "After release" print "After release"
#
# Format strings
#
@testcase
def alignment_string(object[int] buf):
"""
>>> alignment_string(IntMockBuffer(None, [1,2], format="@i"))
2
>>> alignment_string(IntMockBuffer(None, [1,2], format="@i@@"))
2
>>> alignment_string(IntMockBuffer(None, [1,2], format=">i"))
Traceback (most recent call last):
...
ValueError: Buffer acquisition error: Only native byte order, size and alignment supported.
>>> alignment_string(IntMockBuffer(None, [1,2], format="<i"))
Traceback (most recent call last):
...
ValueError: Buffer acquisition error: Only native byte order, size and alignment supported.
>>> alignment_string(IntMockBuffer(None, [1,2], format="=i"))
Traceback (most recent call last):
...
ValueError: Buffer acquisition error: Only native byte order, size and alignment supported.
>>> alignment_string(IntMockBuffer(None, [1,2], format="!i"))
Traceback (most recent call last):
...
ValueError: Buffer acquisition error: Only native byte order, size and alignment supported.
"""
print buf[1]
@testcase
def wrong_string(object[int] buf):
"""
>>> wrong_string(IntMockBuffer(None, [1,2], format="if"))
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch (expected end, got float)
>>> wrong_string(IntMockBuffer(None, [1,2], format="$$"))
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch (expected int, got unparseable format string)
"""
print buf[1]
@testcase
def int_and_long_are_same():
"""
>>> int_and_long_are_same()
"""
cdef object[int] intarr
cdef object[long] longarr
if sizeof(int) == sizeof(long):
intarr = IntMockBuffer(None, [1,2], format='l')
longarr = IntMockBuffer(None, [1,2])
# #
# Getting items and index bounds checking # Getting items and index bounds checking
# #
...@@ -532,38 +478,25 @@ def no_negative_indices(object[int, negative_indices=False] buf, int idx): ...@@ -532,38 +478,25 @@ def no_negative_indices(object[int, negative_indices=False] buf, int idx):
""" """
return buf[idx] return buf[idx]
#
# Buffer type mismatch examples. Varying the type and access
# method simultaneously, the odds of an interaction is virtually
# zero.
#
@testcase @testcase
def fmtst1(buf): @cython.wraparound(False)
""" def wraparound_directive(object[int] buf, int pos_idx, int neg_idx):
>>> fmtst1(IntMockBuffer("A", range(3)))
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch (expected float, got int)
""" """
cdef object[float] a = buf Again, the most interesting thing here is to inspect the C source.
@testcase >>> A = IntMockBuffer(None, range(4))
def fmtst2(object[int] buf): >>> wraparound_directive(A, 2, -1)
""" 5
>>> fmtst2(FloatMockBuffer("A", range(3))) >>> wraparound_directive(A, -1, 2)
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch (expected int, got float) IndexError: Out of bounds on buffer access (axis 0)
""" """
cdef int byneg
with cython.wraparound(True):
byneg = buf[neg_idx]
return buf[pos_idx] + byneg
@testcase
def ndim1(object[int, ndim=2] buf):
"""
>>> ndim1(IntMockBuffer("A", range(3)))
Traceback (most recent call last):
...
ValueError: Buffer has wrong number of dimensions (expected 2, got 1)
"""
# #
# Test which flags are passed. # Test which flags are passed.
...@@ -860,8 +793,7 @@ def printbuf_td_cy_int(object[td_cy_int] buf, shape): ...@@ -860,8 +793,7 @@ def printbuf_td_cy_int(object[td_cy_int] buf, shape):
>>> printbuf_td_cy_int(ShortMockBuffer(None, range(3)), (3,)) >>> printbuf_td_cy_int(ShortMockBuffer(None, range(3)), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch (expected bufaccess.td_cy_int, got short) ValueError: Buffer dtype mismatch, expected 'bufaccess.td_cy_int' but got 'short'
""" """
cdef int i cdef int i
for i in range(shape[0]): for i in range(shape[0]):
...@@ -876,7 +808,7 @@ def printbuf_td_h_short(object[td_h_short] buf, shape): ...@@ -876,7 +808,7 @@ def printbuf_td_h_short(object[td_h_short] buf, shape):
>>> printbuf_td_h_short(IntMockBuffer(None, range(3)), (3,)) >>> printbuf_td_h_short(IntMockBuffer(None, range(3)), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch (expected bufaccess.td_h_short, got int) ValueError: Buffer dtype mismatch, expected 'bufaccess.td_h_short' but got 'int'
""" """
cdef int i cdef int i
for i in range(shape[0]): for i in range(shape[0]):
...@@ -891,7 +823,7 @@ def printbuf_td_h_cy_short(object[td_h_cy_short] buf, shape): ...@@ -891,7 +823,7 @@ def printbuf_td_h_cy_short(object[td_h_cy_short] buf, shape):
>>> printbuf_td_h_cy_short(IntMockBuffer(None, range(3)), (3,)) >>> printbuf_td_h_cy_short(IntMockBuffer(None, range(3)), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch (expected bufaccess.td_h_cy_short, got int) ValueError: Buffer dtype mismatch, expected 'bufaccess.td_h_cy_short' but got 'int'
""" """
cdef int i cdef int i
for i in range(shape[0]): for i in range(shape[0]):
...@@ -906,7 +838,7 @@ def printbuf_td_h_ushort(object[td_h_ushort] buf, shape): ...@@ -906,7 +838,7 @@ def printbuf_td_h_ushort(object[td_h_ushort] buf, shape):
>>> printbuf_td_h_ushort(ShortMockBuffer(None, range(3)), (3,)) >>> printbuf_td_h_ushort(ShortMockBuffer(None, range(3)), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch (expected bufaccess.td_h_ushort, got short) ValueError: Buffer dtype mismatch, expected 'bufaccess.td_h_ushort' but got 'short'
""" """
cdef int i cdef int i
for i in range(shape[0]): for i in range(shape[0]):
...@@ -921,7 +853,7 @@ def printbuf_td_h_double(object[td_h_double] buf, shape): ...@@ -921,7 +853,7 @@ def printbuf_td_h_double(object[td_h_double] buf, shape):
>>> printbuf_td_h_double(FloatMockBuffer(None, [0.25, 1, 3.125]), (3,)) >>> printbuf_td_h_double(FloatMockBuffer(None, [0.25, 1, 3.125]), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch (expected bufaccess.td_h_double, got float) ValueError: Buffer dtype mismatch, expected 'bufaccess.td_h_double' but got 'float'
""" """
cdef int i cdef int i
for i in range(shape[0]): for i in range(shape[0]):
...@@ -1033,7 +965,7 @@ def buffer_cast_fails(object[char, cast=True] buf): ...@@ -1033,7 +965,7 @@ def buffer_cast_fails(object[char, cast=True] buf):
>>> buffer_cast_fails(IntMockBuffer(None, [0])) >>> buffer_cast_fails(IntMockBuffer(None, [0]))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Attempted cast of buffer to datatype of different size. ValueError: Item size of buffer (4 bytes) does not match size of 'char' (1 byte)
""" """
return buf[0] return buf[0]
...@@ -1366,48 +1298,31 @@ cdef class NestedStructMockBuffer(MockBuffer): ...@@ -1366,48 +1298,31 @@ cdef class NestedStructMockBuffer(MockBuffer):
@testcase @testcase
def basic_struct(object[MyStruct] buf): def basic_struct(object[MyStruct] buf):
""" """
See also buffmt.pyx
>>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)])) >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)]))
1 2 3 4 5 1 2 3 4 5
>>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="bbqii")) >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="bbqii"))
1 2 3 4 5 1 2 3 4 5
>>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="23bqii"))
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch (expected long long, got char)
>>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="i"))
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch (expected char, got int)
""" """
print buf[0].a, buf[0].b, buf[0].c, buf[0].d, buf[0].e print buf[0].a, buf[0].b, buf[0].c, buf[0].d, buf[0].e
@testcase @testcase
def nested_struct(object[NestedStruct] buf): def nested_struct(object[NestedStruct] buf):
""" """
See also buffmt.pyx
>>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)])) >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)]))
1 2 3 4 5 1 2 3 4 5
>>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{ii}T{2i}i")) >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{ii}T{2i}i"))
1 2 3 4 5 1 2 3 4 5
>>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="iiiii"))
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch (expected SmallStruct, got int)
>>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{iii}T{ii}i"))
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch (expected end of SmallStruct struct, got int)
""" """
print buf[0].x.a, buf[0].x.b, buf[0].y.a, buf[0].y.b, buf[0].z print buf[0].x.a, buf[0].x.b, buf[0].y.a, buf[0].y.b, buf[0].z
cdef struct LongComplex: cdef struct LongComplex:
long double real long double real
long double imag long double imag
cdef struct MixedComplex:
long double real
float imag
cdef class LongComplexMockBuffer(MockBuffer): cdef class LongComplexMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1: cdef int write(self, char* buf, object value) except -1:
cdef LongComplex* s cdef LongComplex* s
...@@ -1418,24 +1333,33 @@ cdef class LongComplexMockBuffer(MockBuffer): ...@@ -1418,24 +1333,33 @@ cdef class LongComplexMockBuffer(MockBuffer):
cdef get_itemsize(self): return sizeof(LongComplex) cdef get_itemsize(self): return sizeof(LongComplex)
cdef get_default_format(self): return b"Zg" cdef get_default_format(self): return b"Zg"
#cdef extern from "complex.h":
# pass
@testcase @testcase
def complex_struct_dtype(object[LongComplex] buf): def complex_dtype(object[long double complex] buf):
""" """
Note that the format string is "Zg" rather than "2g"... >>> complex_dtype(LongComplexMockBuffer(None, [(0, -1)]))
>>> complex_struct_dtype(LongComplexMockBuffer(None, [(0, -1)])) -1j
0.0 -1.0
""" """
print buf[0].real, buf[0].imag print buf[0]
@testcase @testcase
def mixed_complex_struct_dtype(object[MixedComplex] buf): def complex_inplace(object[long double complex] buf):
""" """
Triggering a specific execution path for this case. >>> complex_inplace(LongComplexMockBuffer(None, [(0, -1)]))
(1+1j)
"""
buf[0] = buf[0] + 1 + 2j
print buf[0]
>>> mixed_complex_struct_dtype(LongComplexMockBuffer(None, [(0, -1)])) @testcase
Traceback (most recent call last): def complex_struct_dtype(object[LongComplex] buf):
... """
ValueError: Cannot store complex number in 'MixedComplex' as 'long double' differs from 'float' in size. Note that the format string is "Zg" rather than "2g", yet a struct
is accessed.
>>> complex_struct_dtype(LongComplexMockBuffer(None, [(0, -1)]))
0.0 -1.0
""" """
print buf[0].real, buf[0].imag print buf[0].real, buf[0].imag
......
from __future__ import unicode_literals
# Tests buffer format string parsing.
__test__ = {}
def testcase(func):
__test__[func.__name__] = func.__doc__
return func
cimport stdlib
def little_endian():
cdef unsigned int n = 1
return (<unsigned char*>&n)[0] != 0
if little_endian():
current_endian = '<'
other_endian = '>'
else:
current_endian = '>'
other_endian = '<'
cdef struct align_of_float_helper:
char ch
float d
cdef struct align_of_int_helper:
char ch
int i
float_align = sizeof(align_of_float_helper) - sizeof(float)
int_align = sizeof(align_of_int_helper) - sizeof(int)
if float_align != 4 or sizeof(float) != 4:
raise RuntimeError("Alignment or size of float is %d on this system, please report to cython-dev for a testcase fix" % float_align)
if int_align != 4 or sizeof(int) != 4:
raise RuntimeError("Alignment or size of int is %d on this system, please report to cython-dev for a testcase fix" % int_align)
cdef class MockBuffer:
cdef Py_ssize_t zero
cdef Py_ssize_t minusone
cdef object format
cdef object itemsize
def __init__(self, format, itemsize):
self.format = unicode(format).encode(u"ASCII")
self.itemsize = itemsize
self.zero = 0
self.minusone = -1
def __getbuffer__(self, Py_buffer* info, int flags):
info.buf = NULL
info.strides = &self.zero
info.suboffsets = &self.minusone
info.shape = &self.zero
info.ndim = 1
info.format = self.format
info.itemsize = self.itemsize
@testcase
def _int(fmt):
"""
>>> _int("i")
>>> _int("b")
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch, expected 'int' but got 'char'
>>> _int("if")
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch, expected end but got 'float'
>>> _int("$$")
Traceback (most recent call last):
...
ValueError: Does not understand character buffer dtype format string ('$')
"""
cdef object[int] buf = MockBuffer(fmt, sizeof(int))
@testcase
def _ulong(fmt):
"""
>>> _ulong("L")
"""
cdef object[unsigned long] buf = MockBuffer(fmt, sizeof(unsigned long))
@testcase
def wrongsize():
"""
>>> wrongsize()
Traceback (most recent call last):
...
ValueError: Item size of buffer (1 byte) does not match size of 'float' (4 bytes)
"""
cdef object[float] buf = MockBuffer("f", 1)
@testcase
def _obj(fmt):
"""
>>> _obj("O")
>>> _obj("i")
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch, expected 'Python object' but got 'int'
"""
cdef object[object] buf = MockBuffer(fmt, sizeof(void*))
cdef struct ComplexFloat:
float real
float imag
ctypedef struct Char3Int:
char a
int b
int c
int d
cdef struct CharIntCFloat:
char a
int b
ComplexFloat c
float d
cdef struct UnpackedStruct1:
char a
int b
ComplexFloat c
float c2
Char3Int d
ctypedef struct UnpackedStruct2:
CharIntCFloat a
Char3Int b
ctypedef struct UnpackedStruct3:
CharIntCFloat a
char b
int c, d, e
cdef struct UnpackedStruct4:
char a
int b
ComplexFloat c
float c2
char d
int e, f, g
@testcase
def char3int(fmt):
"""
>>> char3int("ciii")
>>> char3int("c1i1i1i")
>>> char3int("c3i")
>>> char3int("ci2i")
#TODO > char3int("c@i@2i")
Extra pad bytes (assuming int size is 4 or more)
>>> char3int("cxiii")
>>> char3int("c3xiii")
>>> char3int("cxxxiii")
Standard alignment (assming int size is 4)
>>> char3int("=c3xiii")
>>> char3int("=ciii")
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch; next field is at offset 1 but 4 expected
#TODO char3int("=cxxx@iii")
Error:
>>> char3int("cii")
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch, expected 'int' but got end in 'Char3Int.d'
"""
obj = MockBuffer(fmt, sizeof(Char3Int))
cdef object[Char3Int, ndim=1] buf = obj
@testcase
def unpacked_struct(fmt):
"""
Native formats:
>>> unpacked_struct("biZffbiii")
>>> unpacked_struct("@bi3fb3i")
>>> unpacked_struct("@biZffbi2i")
>>> unpacked_struct("biZffT{biii}")
>>> unpacked_struct("bT{ifffb2i}i")
>>> unpacked_struct("biZffb3T{i}")
>>> unpacked_struct("T{b}T{T{iZffT{bi}}}2T{T{i}}")
"""
assert (sizeof(UnpackedStruct1) == sizeof(UnpackedStruct2)
== sizeof(UnpackedStruct3) == sizeof(UnpackedStruct4))
obj = MockBuffer(fmt, sizeof(UnpackedStruct1))
cdef object[UnpackedStruct1, ndim=1] buf1 = obj
cdef object[UnpackedStruct2, ndim=1] buf2 = obj
cdef object[UnpackedStruct3, ndim=1] buf3 = obj
cdef object[UnpackedStruct4, ndim=1] buf4 = obj
cdef struct ComplexTest:
ComplexFloat a, b, c
@testcase
def complex_test(fmt):
"""
>>> complex_test("ZfZfZf")
>>> complex_test("3Zf")
>>> complex_test("6f")
>>> complex_test("3T{Zf}")
>>> complex_test("fZfZff")
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch, expected 'float' but got 'complex float' in 'ComplexFloat.imag'
"""
obj = MockBuffer(fmt, sizeof(ComplexTest))
cdef object[ComplexTest] buf1 = obj
@testcase
def alignment_string(fmt, exc=None):
"""
>>> alignment_string("@i")
>>> alignment_string("%si" % current_endian)
>>> alignment_string("%si" % other_endian, "X-endian buffer not supported on X-endian compiler")
>>> alignment_string("=i")
"""
cdef object[int] buf
try:
buf = MockBuffer(fmt, sizeof(int))
except ValueError, e:
msg = unicode(e).replace("Big", "X").replace("Little", "X").replace("big", "X").replace("little", "X")
if msg != exc:
print msg
print " is not equal to"
print exc
return
if exc:
print "fail"
@testcase
def int_and_long_are_same():
"""
>>> int_and_long_are_same()
"""
cdef object[int] intarr
cdef object[long] longarr
if sizeof(int) == sizeof(long):
intarr = MockBuffer("l", sizeof(int))
longarr = MockBuffer("i", sizeof(int))
cdef struct MixedComplex:
double real
float imag
@testcase
def mixed_complex_struct():
"""
Triggering a specific execution path for this case.
>>> mixed_complex_struct()
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch, expected 'double' but got 'complex double' in 'MixedComplex.real'
"""
cdef object[MixedComplex] buf = MockBuffer("Zd",
sizeof(MixedComplex))
cdef packed struct PackedSubStruct:
char x
int y
cdef packed struct PackedStruct:
char a
int b
PackedSubStruct sub
@testcase
def packed_struct(fmt):
"""
Assuming int is four bytes:
>>> packed_struct("^cici")
>>> packed_struct("=cibi")
>>> packed_struct("^c@i^ci")
Traceback (most recent call last):
...
ValueError: Buffer packing mode currently only allowed at beginning of format string (this is a defect)
However aligned access won't work:
>>> packed_struct("@cici")
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch; next field is at offset 4 but 1 expected
"""
cdef object[PackedStruct] buf = MockBuffer(fmt, sizeof(PackedStruct))
# TODO: empty struct
# TODO: Incomplete structs
# TODO: mixed structs
...@@ -666,6 +666,17 @@ class MyBadInt(MyInt): ...@@ -666,6 +666,17 @@ class MyBadInt(MyInt):
def __int__(self): def __int__(self):
return u"%s" % self.value return u"%s" % self.value
class MyInt2:
def __init__(self, value):
self.value = value
def __int__(self):
print(u"MyInt.__int__()")
return self.value
class MyBadInt2(MyInt2):
def __int__(self):
return u"%s" % self.value
def test_convert_pyint(x): def test_convert_pyint(x):
u""" u"""
>>> test_convert_pyint(None) >>> test_convert_pyint(None)
......
__doc__ = u''' __doc__ = u'''
>>> no_cdef() >>> no_cdef()
>>> with_cdef() >>> with_cdef()
<<<<<<< local
>>> test_list(range(11), -2, None) >>> test_list(range(11), -2, None)
=======
>>> test_list(list(range(11)), -2, None)
>>>>>>> other
[0, 1, 2, 3, 4, 5, 6, 7, 8, None, 10] [0, 1, 2, 3, 4, 5, 6, 7, 8, None, 10]
<<<<<<< local
>>> test_list(range(11), "invalid index", None) >>> test_list(range(11), "invalid index", None)
=======
>>> test_list(list(range(11)), "invalid index", None) #doctest: +ELLIPSIS
>>>>>>> other
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: list indices must be integers, not str TypeError: list indices must be integers...
''' '''
def no_cdef(): def no_cdef():
lst = range(11) lst = list(range(11))
ob = 10L ob = 10L
lst[ob] = -10 lst[ob] = -10
dd = {} dd = {}
dd[ob] = -10 dd[ob] = -10
def with_cdef(): def with_cdef():
cdef list lst = range(11) cdef list lst = list(range(11))
ob = 10L ob = 10L
lst[ob] = -10 lst[ob] = -10
cdef dict dd = {} cdef dict dd = {}
......
...@@ -14,11 +14,11 @@ cdef char* s = 'abcdef' ...@@ -14,11 +14,11 @@ cdef char* s = 'abcdef'
def global_c_and_s(): def global_c_and_s():
pys = s pys = s
print c print c
print pys.decode('ASCII') print (pys.decode(u'ASCII'))
def local_c_and_s(): def local_c_and_s():
cdef char c = 'b' cdef char c = 'b'
cdef char* s = 'bcdefg' cdef char* s = 'bcdefg'
pys = s pys = s
print c print c
print pys.decode('ASCII') print (pys.decode(u'ASCII'))
__doc__ = u"""
>>> test_object_conversion(2)
((2+0j), (2+0j))
>>> test_object_conversion(2j - 0.5)
((-0.5+2j), (-0.5+2j))
>>> test_arithmetic(2j, 4j)
(-2j, 6j, -2j, (-8+0j), (0.5+0j))
>>> test_arithmetic(6+12j, 3j)
((-6-12j), (6+15j), (6+9j), (-36+18j), (4-2j))
>>> test_arithmetic(5-10j, 3+4j)
((-5+10j), (8-6j), (2-14j), (55-10j), (-1-2j))
>>> test_div_by_zero(4j)
-0.25j
>>> test_div_by_zero(0)
Traceback (most recent call last):
...
ZeroDivisionError: float division
>>> test_coercion(1, 1.5, 2.5, 4+1j, 10j)
(1+0j)
(1.5+0j)
(2.5+0j)
(4+1j)
10j
(9+21j)
>>> test_compare(3, 3)
(True, False)
>>> test_compare(3j, 3j)
(True, False)
>>> test_compare(3j, 4j)
(False, True)
>>> test_compare(3, 4)
(False, True)
>>> test_compare_coerce(3, 4)
(False, True)
>>> test_compare_coerce(4+1j, 4)
(False, True)
>>> test_compare_coerce(4, 4)
(True, False)
>>> test_literal()
(5j, (1-2.5j))
>>> test_real_imag(1-3j)
(1.0, -3.0)
>>> test_real_imag(5)
(5.0, 0.0)
>>> test_real_imag(1.5j)
(0.0, 1.5)
>>> test_real_imag_assignment(1, 2)
(1+2j)
>>> test_real_imag_assignment(1.5, -3.5)
(1.5-3.5j)
"""
#cdef extern from "complex.h":
# pass
cimport cython
def test_object_conversion(o):
cdef float complex a = o
cdef double complex z = o
return (a, z)
def test_arithmetic(double complex z, double complex w):
return -z, z+w, z-w, z*w, z/w
@cython.cdivision(False)
def test_div_by_zero(double complex z):
return 1/z
def test_coercion(int a, float b, double c, float complex d, double complex e):
cdef double complex z
z = a; print z
z = b; print z
z = c; print z
z = d; print z
z = e; print z
return z + a + b + c + d + e
def test_compare(double complex a, double complex b):
return a == b, a != b
def test_compare_coerce(double complex a, int b):
return a == b, a != b
def test_literal():
return 5j, 1-2.5j
def test_real_imag(double complex z):
return z.real, z.imag
def test_real_imag_assignment(object a, double b):
cdef double complex z
z.real = a
z.imag = b
return z
typedef double DoubleTypedef;
...@@ -32,29 +32,29 @@ __doc__ = u""" ...@@ -32,29 +32,29 @@ __doc__ = u"""
def double_target(a, b): def double_target(a, b):
cdef double x cdef double x
for x from a <= x < b: for x from a <= x < b:
print "at", x print u"at", x
return x return x
def double_step(a, b, dx): def double_step(a, b, dx):
cdef double x cdef double x
for x from a <= x < b by dx: for x from a <= x < b by dx:
print "at", x print u"at", x
return x return x
def double_step_typed(a, b, double dx): def double_step_typed(a, b, double dx):
cdef double x cdef double x
for x from a <= x < b by dx: for x from a <= x < b by dx:
print "at", x print u"at", x
return x return x
def double_step_py_target(a, b, double dx): def double_step_py_target(a, b, double dx):
cdef object x cdef object x
for x from a <= x < b by dx: for x from a <= x < b by dx:
print "at", x print u"at", x
return x return x
def int_step_py_target(a, b, int dx): def int_step_py_target(a, b, int dx):
cdef object x cdef object x
for x from a <= x < b by dx: for x from a <= x < b by dx:
print "at", x print u"at", x
return x return x
...@@ -2,6 +2,17 @@ ...@@ -2,6 +2,17 @@
cimport numpy as np cimport numpy as np
def little_endian():
cdef int endian_detector = 1
return (<char*>&endian_detector)[0] != 0
if little_endian():
my_endian = '<'
other_endian = '>'
else:
my_endian = '>'
other_endian = '<'
try: try:
import numpy as np import numpy as np
__doc__ = u""" __doc__ = u"""
...@@ -111,6 +122,7 @@ try: ...@@ -111,6 +122,7 @@ try:
>>> test_dtype('I', inc1_uint) >>> test_dtype('I', inc1_uint)
>>> test_dtype('l', inc1_long) >>> test_dtype('l', inc1_long)
>>> test_dtype('L', inc1_ulong) >>> test_dtype('L', inc1_ulong)
>>> test_dtype('f', inc1_float) >>> test_dtype('f', inc1_float)
>>> test_dtype('d', inc1_double) >>> test_dtype('d', inc1_double)
>>> test_dtype('g', inc1_longdouble) >>> test_dtype('g', inc1_longdouble)
...@@ -118,6 +130,9 @@ try: ...@@ -118,6 +130,9 @@ try:
>>> test_dtype('F', inc1_cfloat) # numpy format codes differ from buffer ones here >>> test_dtype('F', inc1_cfloat) # numpy format codes differ from buffer ones here
>>> test_dtype('D', inc1_cdouble) >>> test_dtype('D', inc1_cdouble)
>>> test_dtype('G', inc1_clongdouble) >>> test_dtype('G', inc1_clongdouble)
>>> test_dtype('F', inc1_cfloat_struct)
>>> test_dtype('D', inc1_cdouble_struct)
>>> test_dtype('G', inc1_clongdouble_struct)
>>> test_dtype(np.int, inc1_int_t) >>> test_dtype(np.int, inc1_int_t)
>>> test_dtype(np.long, inc1_long_t) >>> test_dtype(np.long, inc1_long_t)
...@@ -129,34 +144,62 @@ try: ...@@ -129,34 +144,62 @@ try:
>>> test_dtype(np.int32, inc1_int32_t) >>> test_dtype(np.int32, inc1_int32_t)
>>> test_dtype(np.float64, inc1_float64_t) >>> test_dtype(np.float64, inc1_float64_t)
Endian tests:
>>> test_dtype('%si' % my_endian, inc1_int)
>>> test_dtype('%si' % other_endian, inc1_int)
Traceback (most recent call last):
...
ValueError: Non-native byte order not supported
>>> test_recordarray() >>> test_recordarray()
>>> test_nested_dtypes(np.zeros((3,), dtype=np.dtype([\ >>> print(test_nested_dtypes(np.zeros((3,), dtype=np.dtype([\
('a', np.dtype('i,i')),\ ('a', np.dtype('i,i')),\
('b', np.dtype('i,i'))\ ('b', np.dtype('i,i'))\
]))) ]))))
array([((0, 0), (0, 0)), ((1, 2), (1, 4)), ((1, 2), (1, 4))], array([((0, 0), (0, 0)), ((1, 2), (1, 4)), ((1, 2), (1, 4))],
dtype=[('a', [('f0', '<i4'), ('f1', '<i4')]), ('b', [('f0', '<i4'), ('f1', '<i4')])]) dtype=[('a', [('f0', '!i4'), ('f1', '!i4')]), ('b', [('f0', '!i4'), ('f1', '!i4')])])
>>> test_nested_dtypes(np.zeros((3,), dtype=np.dtype([\ >>> print(test_nested_dtypes(np.zeros((3,), dtype=np.dtype([\
('a', np.dtype('i,f')),\ ('a', np.dtype('i,f')),\
('b', np.dtype('i,i'))\ ('b', np.dtype('i,i'))\
]))) ]))))
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch, expected 'int' but got 'float' in 'DoubleInt.y'
>>> print(test_packed_align(np.zeros((1,), dtype=np.dtype('b,i', align=False))))
array([(22, 23)],
dtype=[('f0', '|i1'), ('f1', '!i4')])
>>> print(test_unpacked_align(np.zeros((1,), dtype=np.dtype('b,i', align=True))))
array([(22, 23)],
dtype=[('f0', '|i1'), ('', '|V3'), ('f1', '!i4')])
>>> print(test_packed_align(np.zeros((1,), dtype=np.dtype('b,i', align=True))))
Traceback (most recent call last):
...
ValueError: Buffer dtype mismatch; next field is at offset 4 but 1 expected
>>> print(test_unpacked_align(np.zeros((1,), dtype=np.dtype('b,i', align=False))))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch (expected int, got float) ValueError: Buffer dtype mismatch; next field is at offset 1 but 4 expected
>>> test_good_cast() >>> test_good_cast()
True True
>>> test_bad_cast() >>> test_bad_cast()
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Attempted cast of buffer to datatype of different size. ValueError: Item size of buffer (1 byte) does not match size of 'int' (4 bytes)
""" """
except: except:
__doc__ = u"" __doc__ = u""
def ndarray_str(arr): def ndarray_str(arr):
u""" u"""
Since Py2.3 doctest don't support <BLANKLINE>, manually replace blank lines Since Py2.3 doctest don't support <BLANKLINE>, manually replace blank lines
...@@ -218,15 +261,19 @@ def inc1_float(np.ndarray[float] arr): arr[1] += 1 ...@@ -218,15 +261,19 @@ def inc1_float(np.ndarray[float] arr): arr[1] += 1
def inc1_double(np.ndarray[double] arr): arr[1] += 1 def inc1_double(np.ndarray[double] arr): arr[1] += 1
def inc1_longdouble(np.ndarray[long double] arr): arr[1] += 1 def inc1_longdouble(np.ndarray[long double] arr): arr[1] += 1
def inc1_cfloat(np.ndarray[np.cfloat_t] arr): def inc1_cfloat(np.ndarray[float complex] arr): arr[1] = arr[1] + 1 + 1j
def inc1_cdouble(np.ndarray[double complex] arr): arr[1] = (arr[1] + 1) + 1j
def inc1_clongdouble(np.ndarray[long double complex] arr): arr[1] = arr[1] + (1 + 1j)
def inc1_cfloat_struct(np.ndarray[np.cfloat_t] arr):
arr[1].real += 1 arr[1].real += 1
arr[1].imag += 1 arr[1].imag += 1
def inc1_cdouble(np.ndarray[np.cdouble_t] arr): def inc1_cdouble_struct(np.ndarray[np.cdouble_t] arr):
arr[1].real += 1 arr[1].real += 1
arr[1].imag += 1 arr[1].imag += 1
def inc1_clongdouble(np.ndarray[np.clongdouble_t] arr): def inc1_clongdouble_struct(np.ndarray[np.clongdouble_t] arr):
cdef long double x cdef long double x
x = arr[1].real + 1 x = arr[1].real + 1
arr[1].real = x arr[1].real = x
...@@ -297,7 +344,7 @@ def test_nested_dtypes(obj): ...@@ -297,7 +344,7 @@ def test_nested_dtypes(obj):
arr[1].b.x = arr[0].a.y + 1 arr[1].b.x = arr[0].a.y + 1
arr[1].b.y = 4 arr[1].b.y = 4
arr[2] = arr[1] arr[2] = arr[1]
return arr return repr(arr).replace('<', '!').replace('>', '!')
def test_bad_nested_dtypes(): def test_bad_nested_dtypes():
cdef object[BadNestedStruct] arr cdef object[BadNestedStruct] arr
...@@ -310,4 +357,22 @@ def test_good_cast(): ...@@ -310,4 +357,22 @@ def test_good_cast():
def test_bad_cast(): def test_bad_cast():
# This should raise an exception # This should raise an exception
cdef np.ndarray[long, cast=True] arr = np.array([1], dtype=b'b') cdef np.ndarray[int, cast=True] arr = np.array([1], dtype=b'b')
cdef packed struct PackedStruct:
char a
int b
cdef struct UnpackedStruct:
char a
int b
def test_packed_align(np.ndarray[PackedStruct] arr):
arr[0].a = 22
arr[0].b = 23
return repr(arr).replace('<', '!').replace('>', '!')
def test_unpacked_align(np.ndarray[UnpackedStruct] arr):
arr[0].a = 22
arr[0].b = 23
return repr(arr).replace('<', '!').replace('>', '!')
# Ensure casting still works to void*
"""
>>> o = f()
>>> print(o[0])
teststring
>>> print(o[1])
teststring
"""
cdef extern from *:
ctypedef void PyObject
def f():
cdef void* p1
cdef PyObject* p2
a = u"teststring"
p1 = <void*>a
p2 = <PyObject*>a
return (<object>p1, <object>p2)
...@@ -74,66 +74,66 @@ at 4 ...@@ -74,66 +74,66 @@ at 4
5 5
""" """
cdef int get_bound(int m): cdef int get_bound(int m):
print "get_bound(%s)"%m print u"get_bound(%s)"%m
return m return m
def for_from_range(a, b): def for_from_range(a, b):
cdef int i = 100 cdef int i = 100
print "range(%s)" % a print u"range(%s)" % a
for i in range(a): for i in range(a):
print "at", i print u"at", i
print "range(%s, %s)" % (a, b) print u"range(%s, %s)" % (a, b)
for i in range(a, b): for i in range(a, b):
print "at", i print u"at", i
print "range(%s, %s, %s)" % (a, b, 2) print u"range(%s, %s, %s)" % (a, b, 2)
for i in range(a, b, 2): for i in range(a, b, 2):
print "at", i print u"at", i
return i return i
def for_from_bound_reassignment(int bound, int fake_bound): def for_from_bound_reassignment(int bound, int fake_bound):
cdef int i = 100 cdef int i = 100
for i from 0 <= i < bound: for i from 0 <= i < bound:
print "at", i print u"at", i
bound = fake_bound bound = fake_bound
return i return i
def for_from_step_reassignment(int bound, int step, int fake_step): def for_from_step_reassignment(int bound, int step, int fake_step):
cdef int i = 100 cdef int i = 100
for i from 0 <= i < bound by step: for i from 0 <= i < bound by step:
print "at", i print u"at", i
step = fake_step step = fake_step
return i return i
def for_from_target_reassignment(int bound, int factor): def for_from_target_reassignment(int bound, int factor):
cdef int i = 100 cdef int i = 100
for i from 0 <= i < bound: for i from 0 <= i < bound:
print "at", i print u"at", i
i *= factor i *= factor
return i return i
def for_from_py_target_reassignment(int bound, int factor): def for_from_py_target_reassignment(int bound, int factor):
cdef object i cdef object i
for i from 0 <= i < bound: for i from 0 <= i < bound:
print "at", i print u"at", i
i *= factor i *= factor
return i return i
def for_from_py_global_target_reassignment(int bound, int factor): def for_from_py_global_target_reassignment(int bound, int factor):
global g_var global g_var
for g_var from 0 <= g_var < bound: for g_var from 0 <= g_var < bound:
print "at", g_var print u"at", g_var
g_var *= factor g_var *= factor
return g_var return g_var
def for_in_target_reassignment(int bound, int factor): def for_in_target_reassignment(int bound, int factor):
cdef int i = 100 cdef int i = 100
for i in range(bound): for i in range(bound):
print "at", i print u"at", i
i *= factor i *= factor
return i return i
def test_func(int n): def test_func(int n):
cdef int i = 100 cdef int i = 100
for i from 0 <= i < get_bound(n): for i from 0 <= i < get_bound(n):
print "at", i print u"at", i
return i return i
__doc__ = u"""
>>> print( "%d" % Int() )
2
>>> print( "%d" % Long() )
3
>>> print( "%d" % IntLongA() )
2
>>> print( "%d" % IntLongB() )
2
>>> print( "%d" % IntLongC() )
3
>>> getint( Int() )
2
>>> getint( Long() )
3
>>> getint( IntLongA() )
2
>>> getint( IntLongB() )
2
>>> getint( IntLongC() )
3
>>> getlong( Int() )
2
>>> getlong( Long() )
3
>>> getlong( IntLongA() )
2
>>> getlong( IntLongB() )
2
>>> getlong( IntLongC() )
3
"""
def getint(int i):
return i
def getlong(long long i):
return <int>i
cdef class Int:
def __int__(self):
return 2
cdef class Long:
def __long__(self):
return 3
cdef class IntLongA:
def __int__(self):
return 2
def __long__(self):
return 3
cdef class IntLongB:
def __int__(self):
return 2
__long__ = __int__
cdef class IntLongC:
def __long__(self):
return 3
__int__ = __long__
...@@ -22,10 +22,10 @@ __doc__ = u""" ...@@ -22,10 +22,10 @@ __doc__ = u"""
>>> slice_list_assign(l2, dict(zip(l,l))) >>> slice_list_assign(l2, dict(zip(l,l)))
[1, 1, 2, 3, 4, 4] [1, 1, 2, 3, 4, 4]
>>> slice_charp('abcdefg') >>> print("%s" % slice_charp('abcdefg'))
'bc' bc
>>> slice_charp_repeat('abcdefg') >>> print("%s" % slice_charp_repeat('abcdefg'))
'cd' cd
""" """
def slice_list(list l): def slice_list(list l):
...@@ -51,12 +51,14 @@ def slice_list_assign(list l, value): ...@@ -51,12 +51,14 @@ def slice_list_assign(list l, value):
return l return l
def slice_charp(str py_string): def slice_charp(py_string_arg):
cdef str py_string = py_string_arg.encode(u'ASCII')
cdef char* s = py_string cdef char* s = py_string
return s[1:3] return s[1:3].decode(u'ASCII')
def slice_charp_repeat(str py_string): def slice_charp_repeat(py_string_arg):
cdef str py_string = py_string_arg.encode(u'ASCII')
cdef char* s = py_string cdef char* s = py_string
cdef str slice_val = s[1:6] cdef str slice_val = s[1:6]
s = slice_val s = slice_val
return s[1:3] return s[1:3].decode(u'ASCII')
"""
>>> f()
42.0 42.0 42.0
>>> readonly()
Traceback (most recent call last):
...
AttributeError: attribute 'var_nf' of 'typedfieldbug_T303.MyClass' objects is not writable
"""
cdef extern from "external_defs.h":
ctypedef float DoubleTypedef
cdef class MyClass:
cdef readonly:
double var_d
DoubleTypedef var_nf
cdef public:
DoubleTypedef var_mutable
def __init__(self):
self.var_d = 42.0
self.var_nf = 42.0
self.var_mutable = 1
def f():
c = MyClass()
c.var_mutable = 42.0
print c.var_d, c.var_nf, c.var_mutable
def readonly():
c = MyClass()
c.var_nf = 3
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