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
4497f635d5fdbd38ebb841be4869fbfa2bbfdbb6 0.11.1.alpha
7bc36a0f81723117a19f92ffde1676a0884fef65 0.11.1.beta
6454db601984145f38e28d34176fca8a3a22329c 0.11.1
af6f1bed8cd40a2edefb57d3eacbc9274a8788b4 0.11.2.rc1
......@@ -15,12 +15,6 @@ except NameError:
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):
text = textwrap.dedent(text)
if reindent > 0:
......@@ -240,29 +234,32 @@ def put_unpack_buffer_aux_into_scope(buffer_aux, mode, code):
def put_acquire_arg_buffer(entry, code, pos):
code.globalstate.use_utility_code(acquire_utility_code)
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
code.putln(code.error_goto_if("%s((PyObject*)%s, &%s, %s, %d, %d) == -1" % (
getbuffer_cname,
entry.cname,
entry.buffer_aux.buffer_info_var.cname,
get_flags(buffer_aux, entry.type),
entry.type.ndim,
int(entry.type.cast)), pos))
code.putln("{")
code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % entry.type.dtype.struct_nesting_depth())
code.putln(code.error_goto_if("%s == -1" % getbuffer, pos))
code.putln("}")
# An exception raised in arg parsing cannot be catched, so no
# need to care about the buffer then.
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):
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,
is_initialized, pos, code):
"""
......@@ -283,12 +280,10 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
bufstruct = buffer_aux.buffer_info_var.cname
flags = get_flags(buffer_aux, buffer_type)
getbuffer = "%s((PyObject*)%%s, &%s, %s, %d, %d)" % (get_getbuffer_code(buffer_type.dtype, code),
# note: object is filled in later (%%s)
bufstruct,
flags,
buffer_type.ndim,
int(buffer_type.cast))
code.putln("{") # Set up necesarry stack for getbuffer
code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % buffer_type.dtype.struct_nesting_depth())
getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below
if is_initialized:
# Release any existing buffer
......@@ -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)
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
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
"""
bufaux = entry.buffer_aux
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.
# We allocate a temporary which is initialized to -1, meaning OK (!).
# 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
params.append(s.cname)
# 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
ptrcode = "%s(%s, %s.buf, %s)" % (funcname,
......@@ -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)])
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):
# Emulation of PyObject_GetBuffer and PyBuffer_Release for Python 2.
......@@ -779,9 +569,92 @@ def use_py2_buffer_functions(env):
#endif
"""), 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
......@@ -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
# buffers match our assumptions. We only need to check ndim and
# the format string; the access mode/flags is checked by the
# exporter.
#
acquire_utility_code = UtilityCode(
proto = """\
# The alignment code is copied from _struct.c in Python.
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_ZeroBuffer(Py_buffer* buf); /*proto*/
static INLINE const char* __Pyx_ConsumeWhitespace(const char* ts); /*proto*/
static void __Pyx_BufferNdimError(Py_buffer* buffer, int expected_ndim); /*proto*/
static const char* __Pyx_DescribeTokenInFormatString(const char* ts); /*proto*/
""",
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 int __Pyx_GetBufferAndValidate(Py_buffer* buf, PyObject* obj, __Pyx_TypeInfo* dtype, int flags, int nd, int cast, __Pyx_BufFmt_StackElem* stack);
""", impl="""
static INLINE int __Pyx_IsLittleEndian(void) {
unsigned int n = 1;
return *(unsigned char*)(&n) != 0;
}
static INLINE void __Pyx_ZeroBuffer(Py_buffer* buf) {
buf->buf = NULL;
buf->obj = NULL;
buf->strides = __Pyx_zeros;
buf->shape = __Pyx_zeros;
buf->suboffsets = __Pyx_minusones;
typedef struct {
__Pyx_StructField root;
__Pyx_BufFmt_StackElem* head;
size_t fmt_offset;
int new_count, enc_count;
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) {
while (1) {
switch (*ts) {
case '@':
case 10:
case 13:
case ' ':
++ts;
break;
case '=':
case '<':
case '>':
case '!':
PyErr_SetString(PyExc_ValueError, "Buffer acquisition error: Only native byte order, size and alignment supported.");
return NULL;
default:
return ts;
static int __Pyx_BufFmt_ParseNumber(const char** ts) {
int count;
const char* t = *ts;
if (*t < '0' || *t > '9') {
return -1;
} else {
count = *t++ - '0';
while (*t >= '0' && *t < '9') {
count *= 10;
count += *t++ - '0';
}
}
}
*ts = t;
return count;
}
static void __Pyx_BufferNdimError(Py_buffer* buffer, int expected_ndim) {
PyErr_Format(PyExc_ValueError,
"Buffer has wrong number of dimensions (expected %d, got %d)",
expected_ndim, buffer->ndim);
static void __Pyx_BufFmt_RaiseUnexpectedChar(char ch) {
char msg[] = {ch, 0};
PyErr_Format(PyExc_ValueError, "Unexpected format string character: '%s'", msg);
}
static const char* __Pyx_DescribeTokenInFormatString(const char* ts) {
switch (*ts) {
case 'b': return "char";
case 'B': return "unsigned char";
case 'h': return "short";
case 'H': return "unsigned short";
case 'i': return "int";
case 'I': return "unsigned int";
case 'l': return "long";
case 'L': return "unsigned long";
case 'q': return "long long";
case 'Q': return "unsigned long long";
case 'f': return "float";
case 'd': return "double";
case 'g': return "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";
}
static const char* __Pyx_BufFmt_DescribeTypeChar(char ch, int is_complex) {
switch (ch) {
case 'b': return "'char'";
case 'B': return "'unsigned char'";
case 'h': return "'short'";
case 'H': return "'unsigned short'";
case 'i': return "'int'";
case 'I': return "'unsigned int'";
case 'l': return "'long'";
case 'L': return "'unsigned long'";
case 'q': return "'long long'";
case 'Q': return "'unsigned long long'";
case 'f': return (is_complex ? "'complex float'" : "'float'");
case 'd': return (is_complex ? "'complex double'" : "'double'");
case 'g': return (is_complex ? "'complex long double'" : "'long double'");
case 'T': return "a struct";
case 'O': return "Python object";
case 'P': return "a pointer";
case 0: return "end";
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(
proto = """
static INLINE const char* __Pyx_ParseTypestringRepeat(const char* ts, int* out_count); /*proto*/
""",
impl = """
static INLINE const char* __Pyx_ParseTypestringRepeat(const char* ts, int* out_count) {
int count;
if (*ts < '0' || *ts > '9') {
count = 1;
typedef struct { char c; short x; } __Pyx_st_short;
typedef struct { char c; int x; } __Pyx_st_int;
typedef struct { char c; long x; } __Pyx_st_long;
typedef struct { char c; float x; } __Pyx_st_float;
typedef struct { char c; double x; } __Pyx_st_double;
typedef struct { char c; long double x; } __Pyx_st_longdouble;
typedef struct { char c; void *x; } __Pyx_st_void_p;
#ifdef HAVE_LONG_LONG
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 {
count = *ts++ - '0';
while (*ts >= '0' && *ts < '9') {
count *= 10;
count += *ts++ - '0';
expected = ctx->head->field->type->name;
quote = "'";
}
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;
}
}
*out_count = count;
return ts;
} while (ctx->enc_count);
ctx->enc_type = 0;
ctx->is_complex = 0;
return 0;
}
""")
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!");
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;
}
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;
}
}
}
}
}
static INLINE void __Pyx_ZeroBuffer(Py_buffer* buf) {
buf->buf = NULL;
buf->obj = NULL;
buf->strides = __Pyx_zeros;
buf->shape = __Pyx_zeros;
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,
"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 = [
#('round', "", "", ""),
('setattr', "OOO", "r", "PyObject_SetAttr"),
#('sum', "", "", ""),
('type', "O", "O", "PyObject_Type"),
#('unichr', "", "", ""),
#('unicode', "", "", ""),
#('vars', "", "", ""),
......
......@@ -153,5 +153,9 @@ def parse_command_line(args):
sys.exit(1)
if len(sources) == 0 and not options.show_version:
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
......@@ -9,6 +9,7 @@ import Options
import StringEncoding
from Cython import Utils
from PyrexTypes import py_object_type, typecast
import PyrexTypes
from TypeSlots import method_coexist
from Scanning import SourceDescriptor
from Cython.StringIOTree import StringIOTree
......@@ -293,9 +294,7 @@ class GlobalState(object):
# to create this output C code. This is
# used to annotate the comments.
#
# used_utility_code set(string|int) Ids of used utility code (to avoid reinsertion)
# utilprotowriter CCodeWriter
# utildefwriter CCodeWriter
# utility_codes set IDs of used utility code (to avoid reinsertion)
#
# declared_cnames {string:Entry} used in a transition phase to merge pxd-declared
# constants etc. into the pyx-declared ones (i.e,
......@@ -306,6 +305,8 @@ class GlobalState(object):
# const_cname_counter int global counter for constant identifiers
#
# parts {string:CCodeWriter}
# interned_strings
# consts
......@@ -319,24 +320,44 @@ class GlobalState(object):
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_list = []
self.input_file_contents = {}
self.used_utility_code = set()
self.utility_codes = set()
self.declared_cnames = {}
self.in_utility_code_generation = False
self.emit_linenums = emit_linenums
self.parts = {}
self.const_cname_counter = 1
self.string_const_index = {}
self.int_const_index = {}
self.py_constants = []
def initwriters(self, rootwriter):
self.utilprotowriter = rootwriter.new_writer()
self.utildefwriter = rootwriter.new_writer()
self.decls_writer = rootwriter.new_writer()
assert writer.globalstate is None
writer.globalstate = self
self.rootwriter = 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.init_cached_builtins_writer = rootwriter.new_writer()
self.initwriter = rootwriter.new_writer()
......@@ -354,12 +375,36 @@ class GlobalState(object):
self.cleanupwriter.putln("")
self.cleanupwriter.putln("static void __Pyx_CleanupGlobals(void) {")
#
# utility_code_def
#
code = self.parts['utility_code_def']
if self.emit_linenums:
code.write('\n#line 1 "cython_utility"\n')
code.putln("")
code.putln("/* Runtime support code */")
code.putln("")
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 insert_global_var_declarations_into(self, code):
code.insert(self.decls_writer)
def close_global_decls(self):
# This is called when it is known that no more global declarations will
# declared (but can be called before or after insert_XXX).
......@@ -393,7 +438,7 @@ class GlobalState(object):
code.insert(self.cleanupwriter)
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
......@@ -485,8 +530,9 @@ class GlobalState(object):
consts = [ (len(c.cname), c.cname, c)
for c in self.py_constants ]
consts.sort()
decls_writer = self.parts['decls']
for _, cname, c in consts:
self.decls_writer.putln(
decls_writer.putln(
"static %s;" % c.type.declaration_code(cname))
def generate_string_constants(self):
......@@ -494,8 +540,10 @@ class GlobalState(object):
for c in self.string_const_index.itervalues() ]
c_consts.sort()
py_strings = []
decls_writer = self.parts['decls']
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))
if c.py_strings is not None:
for py_string in c.py_strings.itervalues():
......@@ -510,7 +558,7 @@ class GlobalState(object):
self.pystring_table.putln("static __Pyx_StringTabEntry %s[] = {" %
Naming.stringtab_cname)
for c_cname, _, py_string in py_strings:
self.decls_writer.putln(
decls_writer.putln(
"static PyObject *%s;" % py_string.cname)
self.pystring_table.putln(
"{&%s, %s, sizeof(%s), %d, %d, %d}," % (
......@@ -533,9 +581,10 @@ class GlobalState(object):
consts = [ (len(c.value), c.value, c.is_long, c)
for c in self.int_const_index.itervalues() ]
consts.sort()
decls_writer = self.parts['decls']
for _, value, longness, c in consts:
cname = c.cname
self.decls_writer.putln("static PyObject *%s;" % cname)
decls_writer.putln("static PyObject *%s;" % cname)
if longness:
function = '%s = PyLong_FromString((char *)"%s", 0, 0); %s;'
else:
......@@ -602,55 +651,18 @@ class GlobalState(object):
code twice. Otherwise, id(codetup) is used as such an identifier.
"""
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:
for dependency in utility_code.requires:
self.use_utility_code(dependency)
if utility_code.proto:
self.utilprotowriter.put(utility_code.proto)
self.parts['utility_code_proto'].put(utility_code.proto)
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_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 get(self):
......@@ -692,6 +704,8 @@ class CCodeWriter(object):
# pyclass_stack list used during recursive code generation to pass information
# about the current class one is in
globalstate = None
def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None):
if buffer is None: buffer = StringIOTree()
self.buffer = buffer
......@@ -704,12 +718,8 @@ class CCodeWriter(object):
self.level = 0
self.call_level = 0
self.bol = 1
if create_from is None:
# Root CCodeWriter
self.globalstate = GlobalState(self, emit_linenums=emit_linenums)
self.globalstate.initwriters(self)
# ^^^ need seperate step because this will reference self.globalstate
else:
if create_from is not None:
# Use same global state
self.globalstate = create_from.globalstate
# Clone formatting state
......@@ -717,7 +727,7 @@ class CCodeWriter(object):
self.level = create_from.level
self.bol = create_from.bol
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
else:
self.emit_linenums = emit_linenums
......@@ -725,7 +735,8 @@ class CCodeWriter(object):
def create_new(self, create_from, buffer, copy_formatting):
# polymorphic constructor -- very slightly more versatile
# than using __class__
return CCodeWriter(create_from, buffer, copy_formatting)
result = CCodeWriter(create_from, buffer, copy_formatting)
return result
def copyto(self, f):
self.buffer.copyto(f)
......@@ -807,13 +818,16 @@ class CCodeWriter(object):
# code generation
def putln(self, code = ""):
def putln(self, code = "", safe=False):
if self.marker and self.bol:
self.emit_marker()
if self.emit_linenums and self.last_marker_line != 0:
self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc))
if code:
self.put(code)
if safe:
self.put_safe(code)
else:
self.put(code)
self.write("\n");
self.bol = 1
......
......@@ -503,10 +503,15 @@ class ExprNode(Node):
src = PyTypeTestNode(src, dst_type, env)
elif src.type.is_pyobject:
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
# Added the string comparison, since for c types that
# 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)):
error(self.pos, "Cannot assign type '%s' to '%s'" %
(src.type, dst_type))
......@@ -683,7 +688,7 @@ class IntNode(ConstNode):
type = PyrexTypes.c_long_type
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
return self
# Arrange for a Python version of the number to be pre-allocated
......@@ -870,6 +875,8 @@ class ImagNode(AtomicExprNode):
# Imaginary number literal
#
# value float imaginary part
type = PyrexTypes.c_double_complex_type
def calculate_constant_result(self):
self.constant_result = complex(0.0, self.value)
......@@ -878,18 +885,39 @@ class ImagNode(AtomicExprNode):
return complex(0.0, self.value)
def analyse_types(self, env):
self.type = py_object_type
self.is_temp = 1
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.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"
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):
code.putln(
"%s = PyComplex_FromDoubles(0.0, %r); %s" % (
self.result(),
float(self.value),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
if self.type.is_pyobject:
code.putln(
"%s = PyComplex_FromDoubles(0.0, %r); %s" % (
self.result(),
float(self.value),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
else:
self.c99_complex = code.globalstate.directives['c99_complex']
......@@ -1844,7 +1872,7 @@ class IndexNode(ExprNode):
return Buffer.put_buffer_lookup_code(entry=self.base.entry,
index_signeds=[i.type.signed for i in self.indices],
index_cnames=index_temps,
options=code.globalstate.directives,
directives=code.globalstate.directives,
pos=self.pos, code=code)
def put_nonecheck(self, code):
......@@ -2809,6 +2837,8 @@ class AttributeNode(ExprNode):
obj.type.vtabslot_cname, self.member)
else:
return self.member
elif obj.type.is_complex:
return "__Pyx_%s_PART(%s)" % (self.member.upper(), obj_code)
else:
return "%s%s%s" % (obj_code, self.op, self.member)
......@@ -3709,6 +3739,7 @@ class UnopNode(ExprNode):
# - Allocate temporary for result if needed.
subexprs = ['operand']
infix = True
def calculate_constant_result(self):
func = compile_time_unary_operators[self.operator]
......@@ -3823,13 +3854,17 @@ class UnaryMinusNode(UnopNode):
self.type = self.operand.type
else:
self.type_error()
if self.type.is_complex:
self.infix = env.directives['c99_complex']
def py_operation_function(self):
return "PyNumber_Negative"
def calculate_result_code(self):
return "(-%s)" % self.operand.result()
if self.infix:
return "(-%s)" % self.operand.result()
else:
return "%s(%s)" % (self.operand.type.unary_op('-'), self.operand.result())
class TildeNode(UnopNode):
# unary '~' operator
......@@ -3926,15 +3961,18 @@ class TypecastNode(ExprNode):
error(self.pos, "Casting temporary Python object to non-numeric non-Python type")
if to_py and not from_py:
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.operand = self.operand.coerce_to_pyobject(env)
else:
warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.operand.type, self.type))
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))
self.operand = self.operand.coerce_to_simple(env)
elif from_py and not to_py:
if self.type.from_py_function:
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:
warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.type, self.operand.type))
elif from_py and to_py:
......@@ -4190,12 +4228,20 @@ class BinopNode(ExprNode):
class NumBinopNode(BinopNode):
# Binary operation taking numeric arguments.
infix = True
def analyse_c_operation(self, env):
type1 = self.operand1.type
type2 = self.operand2.type
self.type = self.compute_c_result_type(type1, type2)
if not self.type:
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):
if self.c_types_okay(type1, type2):
......@@ -4217,10 +4263,19 @@ class NumBinopNode(BinopNode):
and (type2.is_numeric or type2.is_enum)
def calculate_result_code(self):
return "(%s %s %s)" % (
self.operand1.result(),
self.operator,
self.operand2.result())
if self.infix:
return "(%s %s %s)" % (
self.operand1.result(),
self.operator,
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):
return self.py_functions[self.operator]
......@@ -4322,7 +4377,7 @@ class DivNode(NumBinopNode):
return "float division"
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:
self.cdivision = (code.globalstate.directives['cdivision']
or not self.type.signed
......@@ -4335,7 +4390,11 @@ class DivNode(NumBinopNode):
def generate_div_warning_code(self, code):
if not self.type.is_pyobject:
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(code.error_goto(self.pos))
code.putln("}")
......@@ -4348,7 +4407,7 @@ class DivNode(NumBinopNode):
code.putln('PyErr_Format(PyExc_OverflowError, "value too large to perform division");')
code.putln(code.error_goto(self.pos))
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.putln("if ((%s < 0) ^ (%s < 0)) {" % (
self.operand1.result(),
......@@ -4359,7 +4418,9 @@ class DivNode(NumBinopNode):
code.putln("}")
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)" % (
self.operand1.result(),
self.operand2.result())
......@@ -4423,7 +4484,10 @@ class PowNode(NumBinopNode):
def 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"
else:
self.pow_func = "__Pyx_pow_%s" % self.type.declaration_code('').replace(' ', '_')
......@@ -4686,7 +4750,13 @@ class CmpNode(object):
or (self.cascade and self.cascade.is_python_result()))
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)" %
(self.operator, operand1.type, operand2.type))
......@@ -4735,6 +4805,16 @@ class CmpNode(object):
richcmp_constants[op],
code.error_goto_if_null(result_code, self.pos)))
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:
type1 = operand1.type
type2 = operand2.type
......@@ -4850,10 +4930,21 @@ class PrimaryCmpNode(ExprNode, CmpNode):
self.not_const()
def calculate_result_code(self):
return "(%s %s %s)" % (
self.operand1.result(),
self.c_operator(self.operator),
self.operand2.result())
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)" % (
self.operand1.result(),
self.c_operator(self.operator),
self.operand2.result())
def generate_evaluation_code(self, code):
self.operand1.generate_evaluation_code(code)
......@@ -5129,7 +5220,7 @@ class CoerceToPyTypeNode(CoercionNode):
CoercionNode.__init__(self, arg)
self.type = py_object_type
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,
"Cannot convert '%s' to Python object" % arg.type)
......@@ -5167,7 +5258,7 @@ class CoerceFromPyTypeNode(CoercionNode):
CoercionNode.__init__(self, arg)
self.type = result_type
self.is_temp = 1
if not result_type.from_py_function:
if not result_type.create_from_py_utility_code(env):
error(arg.pos,
"Cannot convert Python object to '%s'" % result_type)
if self.type.is_string and self.arg.is_ephemeral():
......@@ -5224,6 +5315,29 @@ class CoerceToBooleanNode(CoercionNode):
self.arg.py_result(),
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):
# This node is used to force the result of another node
......
......@@ -125,6 +125,7 @@ class Context(object):
_check_c_declarations,
AnalyseExpressionsTransform(self),
OptimizeBuiltinCalls(),
ConstantFolding(),
IterationTransform(),
SwitchTransform(),
FinalOptimizePhase(self),
......
......@@ -100,6 +100,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if h_types or h_vars or h_funcs or h_extension_types:
result.h_file = replace_suffix(result.c_file, ".h")
h_code = Code.CCodeWriter()
Code.GlobalState(h_code)
if options.generate_pxi:
result.i_file = replace_suffix(result.c_file, ".pxi")
i_code = Code.PyrexCodeWriter(result.i_file)
......@@ -161,6 +162,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if api_funcs or has_api_extension_types:
result.api_file = replace_suffix(result.c_file, "_api.h")
h_code = Code.CCodeWriter()
Code.GlobalState(h_code)
name = self.api_name(env)
guard = Naming.api_guard_prefix + name
h_code.put_h_guard(guard)
......@@ -237,23 +239,31 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def generate_c_code(self, env, options, result):
modules = self.referenced_modules
if Options.annotate or options.annotate:
code = Annotate.AnnotationCCodeWriter()
emit_linenums = False
rootwriter = Annotate.AnnotationCCodeWriter()
else:
code = Code.CCodeWriter(emit_linenums=options.emit_linenums)
h_code = code.insertion_point()
emit_linenums = options.emit_linenums
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)
code.globalstate.module_pos = self.pos
code.globalstate.directives = self.directives
globalstate.module_pos = self.pos
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("int %s%s = %s;" % (Naming.module_is_main, self.full_module_name.replace('.', '__'), int(Options.embed)))
code.putln("")
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.body.generate_function_definitions(env, code)
......@@ -270,20 +280,21 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if Options.embed:
self.generate_main_method(env, 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')
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)
code.copyto(f)
rootwriter.copyto(f)
f.close()
result.c_file_generated = 1
if Options.annotate or options.annotate:
self.annotate(code)
code.save_annotation(result.main_source_file, result.c_file)
self.annotate(rootwriter)
rootwriter.save_annotation(result.main_source_file, result.c_file)
def find_referenced_modules(self, env, module_list, modules_seen):
if env not in modules_seen:
......@@ -381,18 +392,20 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_exttype_vtable_struct(entry, code)
self.generate_exttype_vtabptr_declaration(entry, code)
def generate_declarations_for_modules(self, env, modules, code):
code.putln("")
code.putln("/* Type declarations */")
def generate_declarations_for_modules(self, env, modules, globalstate):
typecode = globalstate['type_declarations']
typecode.putln("")
typecode.putln("/* Type declarations */")
vtab_list, vtabslot_list = self.sort_type_hierarchy(modules, env)
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:
defined_here = module is env
code.putln("/* Module declarations from %s */" %
modulecode.putln("/* Module declarations from %s */" %
module.qualified_name.encode("ASCII", "ignore"))
self.generate_global_declarations(module, code, defined_here)
self.generate_cfunction_predeclarations(module, code, defined_here)
self.generate_global_declarations(module, modulecode, defined_here)
self.generate_cfunction_predeclarations(module, modulecode, defined_here)
def generate_module_preamble(self, env, cimported_modules, code):
code.putln('/* Generated by Cython %s on %s */' % (
......@@ -508,6 +521,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln(" #ifndef __cdecl")
code.putln(" #define __cdecl")
code.putln(" #endif")
code.putln(" #ifndef __fastcall")
code.putln(" #define __fastcall")
code.putln(" #endif")
code.putln("#else")
code.putln(" #define _USE_MATH_DEFINES")
code.putln("#endif")
......@@ -534,6 +550,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("#include <math.h>")
code.putln("#define %s" % Naming.api_guard_prefix + self.api_name(env))
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.put(Nodes.utility_function_predeclarations)
code.put(PyrexTypes.type_conversion_predeclarations)
......@@ -1805,14 +1826,17 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
env.module_cname,
Naming.builtins_cname,
code.error_goto(self.pos)))
if Options.embed:
__main__name = code.globalstate.get_py_string_const(
EncodedString("__main__"), identifier=True)
code.putln(
'if (__Pyx_SetAttrString(%s, "__name__", %s) < 0) %s;' % (
env.module_cname,
__main__name.cname,
code.error_goto(self.pos)))
__main__name = code.globalstate.get_py_string_const(
EncodedString("__main__"), identifier=True)
code.putln("if (%s%s) {" % (Naming.module_is_main, self.full_module_name.replace('.', '__')))
code.putln(
'if (__Pyx_SetAttrString(%s, "__name__", %s) < 0) %s;' % (
env.module_cname,
__main__name.cname,
code.error_goto(self.pos)))
code.putln("}")
if Options.pre_import is not None:
code.putln(
'%s = PyImport_AddModule(__Pyx_NAMESTR("%s"));' % (
......@@ -2040,22 +2064,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"%s = &%s;" % (
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
......@@ -2460,7 +2468,11 @@ static __Pyx_RefnannyAPIStruct *__Pyx_Refnanny = NULL;
main_method = UtilityCode(
impl = """
#if PY_MAJOR_VERSION < 3 || (!defined(WIN32) && !defined(MS_WINDOWS))
int main(int argc, char** argv) {
#else
int wmain(int argc, wchar_t **argv) {
#endif
int r = 0;
PyObject* m = NULL;
Py_SetProgramName(argv[0]);
......
......@@ -44,6 +44,7 @@ vtabptr_prefix = pyrex_prefix + "vtabptr_"
vtabstruct_prefix = pyrex_prefix + "vtabstruct_"
opt_arg_prefix = pyrex_prefix + "opt_args_"
convert_func_prefix = pyrex_prefix + "convert_"
module_is_main = pyrex_prefix + "module_is_main_"
args_cname = pyrex_prefix + "args"
pykwdlist_cname = pyrex_prefix + "pyargnames"
......
......@@ -569,6 +569,13 @@ class CFuncDeclaratorNode(CDeclaratorNode):
nogil = self.nogil, with_gil = self.with_gil, is_overridable = self.overridable)
if self.optional_arg_count:
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)
......@@ -650,6 +657,7 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
# is_basic_c_type boolean
# signed boolean
# longness integer
# complex boolean
# is_self_arg boolean Is self argument of C method
child_attrs = []
......@@ -690,6 +698,11 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
self.arg_name = self.name
else:
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:
return type
else:
......@@ -763,15 +776,26 @@ class CVarDefNode(StatNode):
dest_scope = env
self.dest_scope = dest_scope
base_type = self.base_type.analyse(env)
need_property = False
if (dest_scope.is_c_class_scope
and self.visibility == 'public'
and base_type.is_pyobject
and (base_type.is_builtin_type or base_type.is_extension_type)):
self.need_properties = []
and self.visibility == 'public'
and base_type.is_pyobject
and (base_type.is_builtin_type or base_type.is_extension_type)):
# If the field is settable and extension type, then the CPython mechanism does
# not do enough type-checking for us.
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'
self.need_properties = []
else:
need_property = False
visibility = self.visibility
for declarator in self.declarators:
......@@ -1867,6 +1891,10 @@ class DefNode(FuncDefNode):
has_star_or_kw_args = self.star_arg is not None \
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 has_star_or_kw_args:
error(self.pos, "This method cannot have * or keyword arguments")
......@@ -3069,10 +3097,10 @@ class InPlaceAssignmentNode(AssignmentNode):
if c_op == "//":
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
if isinstance(self.lhs, ExprNodes.IndexNode) and self.lhs.is_buffer_access:
self.lhs.generate_buffer_setitem_code(self.rhs, code, c_op)
......
......@@ -54,7 +54,7 @@ c_line_in_traceback = 1
embed = False
# Declare pragmas
# Declare compiler directives
option_defaults = {
'boundscheck' : True,
'nonecheck' : False,
......@@ -64,6 +64,9 @@ option_defaults = {
'cdivision': True, # Will be False in 0.12
'cdivision_warnings': 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
......@@ -94,6 +97,11 @@ def parse_option_value(name, value):
if value == "True": return True
elif value == "False": return False
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:
assert False
......
......@@ -435,6 +435,11 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
raise PostParseError(dec.function.pos,
'The %s option takes one compile-time boolean argument' % optname)
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:
if len(args) != 0:
raise PostParseError(dec.function.pos,
......@@ -589,62 +594,6 @@ class WithTransform(CythonTransform, SkipDeclarations):
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):
def visit_DefNode(self, func_node):
......@@ -676,6 +625,12 @@ property NAME:
ATTR = value
""", level='c_class')
readonly_property = TreeFragment(u"""
property NAME:
def __get__(self):
return ATTR
""", level='c_class')
def __call__(self, root):
self.env_stack = [root.scope]
# needed to determine if a cdef var is declared after it's used.
......@@ -752,7 +707,7 @@ property NAME:
# mechanism for them.
stats = []
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)
self.visit(property)
stats.append(property)
......@@ -760,8 +715,12 @@ property NAME:
else:
return None
def create_Property(self, entry):
property = self.basic_property.substitute({
def create_Property(self, entry, readonly):
if readonly:
template = self.readonly_property
else:
template = self.basic_property
property = template.substitute({
u"ATTR": AttributeNode(pos=entry.pos,
obj=NameNode(pos=entry.pos, name="self"),
attribute=entry.name),
......
......@@ -1753,7 +1753,7 @@ def p_calling_convention(s):
else:
return ""
calling_convention_words = ("__stdcall", "__cdecl")
calling_convention_words = ("__stdcall", "__cdecl", "__fastcall")
def p_c_complex_base_type(s):
# s.sy == '('
......@@ -1770,6 +1770,7 @@ def p_c_simple_base_type(s, self_flag, nonempty):
is_basic = 0
signed = 1
longness = 0
complex = 0
module_path = []
pos = s.position()
if not s.sy == 'IDENT':
......@@ -1788,6 +1789,9 @@ def p_c_simple_base_type(s, self_flag, nonempty):
s.next()
else:
name = 'int'
if s.sy == 'IDENT' and s.systring == 'complex':
complex = 1
s.next()
elif looking_at_dotted_name(s):
#print "p_c_simple_base_type: looking_at_type_name at", s.position()
name = s.systring
......@@ -1816,7 +1820,8 @@ def p_c_simple_base_type(s, self_flag, nonempty):
type_node = Nodes.CSimpleBaseTypeNode(pos,
name = name, module_path = module_path,
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
......@@ -2561,7 +2566,7 @@ def p_code(s, level=None):
repr(s.sy), repr(s.systring)))
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):
result = {}
......@@ -2571,10 +2576,10 @@ def p_compiler_directive_comments(s):
name = m.group(1)
try:
value = Options.parse_option_value(str(name), str(m.group(2).strip()))
if value is not None: # can be False!
result[name] = value
except ValueError, e:
s.error(e.args[0], fatal=False)
if value is not None: # can be False!
result[name] = value
s.next()
return result
......
......@@ -33,6 +33,7 @@ class PyrexType(BaseType):
# is_int boolean Is a C integer type
# is_longlong boolean Is a long long or unsigned long long.
# 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_array boolean Is a C array type
# is_ptr boolean Is a C pointer type
......@@ -83,6 +84,7 @@ class PyrexType(BaseType):
is_int = 0
is_longlong = 0
is_float = 0
is_complex = 0
is_void = 0
is_array = 0
is_ptr = 0
......@@ -140,9 +142,15 @@ class PyrexType(BaseType):
return 1
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)
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):
#
# Pseudo-type defined with a ctypedef statement in a
......@@ -153,12 +161,15 @@ class CTypedefType(BaseType):
# qualified_name string
# typedef_cname string
# typedef_base_type PyrexType
# typedef_is_external bool
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_base_type = base_type
self.typedef_is_external = is_external
def resolve(self):
return self.typedef_base_type.resolve()
......@@ -434,8 +445,11 @@ class CType(PyrexType):
exception_value = None
exception_check = 1
def create_convert_utility_code(self, env):
return True
def create_to_py_utility_code(self, env):
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):
conds = []
......@@ -693,7 +707,191 @@ class CFloatType(CNumericType):
self.math_h_modifier = math_h_modifier
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):
......@@ -928,6 +1126,15 @@ class CFuncType(CType):
return 1
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'
sc2 = other.calling_convention == '__stdcall'
return sc1 == sc2
......@@ -1050,19 +1257,20 @@ class CStructOrUnionType(CType):
self._convert_code = None
self.packed = packed
def create_convert_utility_code(self, env):
def create_to_py_utility_code(self, env):
if env.outer_scope is None:
return False
if self._convert_code is None:
import Code
code = Code.CCodeWriter()
Code.GlobalState(code)
header = "static PyObject* %s(%s)" % (self.to_py_function, self.declaration_code('s'))
code.putln("%s {" % header)
code.putln("PyObject* res;")
code.putln("PyObject* member;")
code.putln("res = PyDict_New(); if (res == NULL) return NULL;")
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)
env.add_py_string(interned_name)
code.putln("member = %s(s.%s); if (member == NULL) goto bad;" % (
......@@ -1122,10 +1330,18 @@ class CStructOrUnionType(CType):
return self.is_complete()
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
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):
# name string
......@@ -1228,7 +1444,10 @@ class ErrorType(PyrexType):
to_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
def declaration_code(self, entity_code,
......@@ -1287,6 +1506,8 @@ c_float_type = CFloatType(7, "T_FLOAT", math_h_modifier='f')
c_double_type = CFloatType(8, "T_DOUBLE")
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_char_array_type = CCharArrayType(None)
c_char_ptr_type = CCharPtrType()
......@@ -1379,6 +1600,15 @@ modifiers_and_name_to_type = {
def widest_numeric_type(type1, type2):
# Given two numeric types, return the narrowest type
# 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:
return c_int_type
elif type1 is type2:
......@@ -1535,14 +1765,14 @@ static INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) {
return Py_INCREF(x), x;
m = Py_TYPE(x)->tp_as_number;
#if PY_VERSION_HEX < 0x03000000
if (m && m->nb_long) {
name = "long";
res = PyNumber_Long(x);
}
else if (m && m->nb_int) {
if (m && m->nb_int) {
name = "int";
res = PyNumber_Int(x);
}
else if (m && m->nb_long) {
name = "long";
res = PyNumber_Long(x);
}
#else
if (m && m->nb_int) {
name = "int";
......@@ -1605,3 +1835,5 @@ static INLINE size_t __Pyx_PyInt_AsSize_t(PyObject* x) {
}
""" + type_conversion_functions
......@@ -335,7 +335,7 @@ class Scope(object):
cname = name
else:
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)
type.qualified_name = entry.qualified_name
return entry
......
......@@ -154,7 +154,14 @@ class SlotDescriptor(object):
code.putln("#if PY_MAJOR_VERSION >= 3")
if 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))
if py3k == '<RESERVED>':
code.putln("#endif")
if flag or (not py3k or not py2) or self.ifdef:
code.putln("#endif")
......@@ -212,10 +219,13 @@ class MethodSlot(SlotDescriptor):
def slot_code(self, scope):
entry = scope.lookup_here(self.method_name)
if entry:
if entry and entry.func_cname:
return entry.func_cname
else:
return "0"
if self.default is not None:
entry = scope.lookup_here(self.default)
if entry and entry.func_cname:
return entry.func_cname
return "0"
class InternalMethodSlot(SlotDescriptor):
......@@ -557,8 +567,8 @@ PyNumberMethods = (
MethodSlot(binaryfunc, "nb_xor", "__xor__"),
MethodSlot(binaryfunc, "nb_or", "__or__"),
EmptySlot("nb_coerce", py3k = False),
MethodSlot(unaryfunc, "nb_int", "__int__"),
MethodSlot(unaryfunc, "nb_long", "__long__"),
MethodSlot(unaryfunc, "nb_int", "__int__", default="__long__"),
MethodSlot(unaryfunc, "nb_long", "__long__", default="__int__", py3k = "<RESERVED>"),
MethodSlot(unaryfunc, "nb_float", "__float__"),
MethodSlot(unaryfunc, "nb_oct", "__oct__", 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 stdlib
cdef extern from "Python.h":
ctypedef int Py_intptr_t
cdef extern from "numpy/arrayobject.h":
ctypedef Py_intptr_t npy_intp
......@@ -29,6 +42,8 @@ cdef extern from "numpy/arrayobject.h":
ctypedef class numpy.dtype [object PyArray_Descr]:
cdef int type_num
cdef int itemsize "elsize"
cdef char byteorder
cdef object fields
cdef object names
......@@ -53,6 +68,9 @@ cdef extern from "numpy/arrayobject.h":
# In particular strided access is always provided regardless
# of flags
cdef int copy_shape, i, ndim
cdef int endian_detector = 1
cdef bint little_endian = ((<char*>&endian_detector)[0] != 0)
ndim = PyArray_NDIM(self)
if sizeof(npy_intp) != sizeof(Py_ssize_t):
......@@ -89,17 +107,10 @@ cdef extern from "numpy/arrayobject.h":
cdef char* f = NULL
cdef dtype descr = self.descr
cdef list stack
cdef int offset
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:
# do not call releasebuffer
info.obj = None
......@@ -109,6 +120,9 @@ cdef extern from "numpy/arrayobject.h":
if not hasfields:
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"
elif t == NPY_UBYTE: f = "B"
elif t == NPY_SHORT: f = "h"
......@@ -131,8 +145,12 @@ cdef extern from "numpy/arrayobject.h":
info.format = f
return
else:
info.format = <char*>stdlib.malloc(255) # static size
f = _util_dtypestring(descr, info.format, info.format + 255)
info.format = <char*>stdlib.malloc(_buffer_format_string_len)
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
def __releasebuffer__(ndarray self, Py_buffer* info):
......@@ -155,53 +173,59 @@ cdef extern from "numpy/arrayobject.h":
cdef int PyDataType_HASFIELDS(dtype obj)
ctypedef signed int npy_byte
ctypedef signed int npy_short
ctypedef signed int npy_int
ctypedef signed int npy_long
ctypedef signed int npy_longlong
ctypedef signed char npy_byte
ctypedef signed short npy_short
ctypedef signed int npy_int
ctypedef signed long npy_long
ctypedef signed long long npy_longlong
ctypedef unsigned int npy_ubyte
ctypedef unsigned int npy_ushort
ctypedef unsigned int npy_uint
ctypedef unsigned int npy_ulong
ctypedef unsigned int npy_ulonglong
ctypedef unsigned char npy_ubyte
ctypedef unsigned short npy_ushort
ctypedef unsigned int npy_uint
ctypedef unsigned long npy_ulong
ctypedef unsigned long long npy_ulonglong
ctypedef float npy_float
ctypedef float npy_double
ctypedef float npy_longdouble
ctypedef signed int npy_int8
ctypedef signed int npy_int16
ctypedef signed int npy_int32
ctypedef signed int npy_int64
ctypedef signed int npy_int96
ctypedef signed int npy_int128
ctypedef unsigned int npy_uint8
ctypedef unsigned int npy_uint16
ctypedef unsigned int npy_uint32
ctypedef unsigned int npy_uint64
ctypedef unsigned int npy_uint96
ctypedef unsigned int npy_uint128
ctypedef double npy_double
ctypedef long double npy_longdouble
ctypedef signed char npy_int8
ctypedef signed short npy_int16
ctypedef signed int npy_int32
ctypedef signed long long npy_int64
ctypedef signed long long npy_int96
ctypedef signed long long npy_int128
ctypedef unsigned char npy_uint8
ctypedef unsigned short npy_uint16
ctypedef unsigned int npy_uint32
ctypedef unsigned long long npy_uint64
ctypedef unsigned long long npy_uint96
ctypedef unsigned long long npy_uint128
ctypedef float npy_float32
ctypedef float npy_float64
ctypedef float npy_float80
ctypedef float npy_float96
ctypedef float npy_float128
ctypedef double npy_float64
ctypedef long double npy_float80
ctypedef long double npy_float96
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:
float real
float imag
ctypedef struct npy_cdouble:
double real
double imag
float real
float imag
ctypedef struct npy_clongdouble:
long double real
long double imag
float real
float imag
# Typedefs that matches the runtime dtype objects in
# the numpy module.
......@@ -228,11 +252,15 @@ ctypedef npy_float64 float64_t
#ctypedef npy_float80 float80_t
#ctypedef npy_float128 float128_t
ctypedef npy_complex64 complex64_t
ctypedef npy_complex128 complex128_t
# The int types are mapped a bit surprising --
# numpy.int corresponds to 'l' and numpy.long to 'q'
ctypedef npy_long int_t
ctypedef npy_longlong long_t
ctypedef npy_ulong uint_t
ctypedef npy_ulonglong ulong_t
......@@ -244,18 +272,47 @@ ctypedef npy_cfloat cfloat_t
ctypedef npy_cdouble cdouble_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
# string. The new location in the format string is returned.
cdef dtype child
cdef int delta_offset
cdef tuple i
cdef int endian_detector = 1
cdef bint little_endian = ((<char*>&endian_detector)[0] != 0)
for i in descr.fields.itervalues():
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):
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.")
# 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
elif t == NPY_FLOAT: f[0] = 102 #"f"
elif t == NPY_DOUBLE: f[0] = 100 #"d"
elif t == NPY_LONGDOUBLE: f[0] = 103 #"g"
elif t == NPY_CFLOAT: f[0] = 90; f[1] = 102; f += 1
elif t == NPY_CDOUBLE: f[0] = 90; f[1] = 100; f += 1
elif t == NPY_CLONGDOUBLE: f[0] = 90; f[1] = 103; 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 # Zd
elif t == NPY_CLONGDOUBLE: f[0] = 90; f[1] = 103; f += 1 # Zg
elif t == NPY_OBJECT: f[0] = 79 #"O"
else:
raise ValueError("unknown dtype code in numpy.pxd (%d)" % t)
f += 1
else:
f[0] = 84 #"T"
f[1] = 123 #"{"
f += 2
f = _util_dtypestring(child, f, end)
f[0] = 125 #"}"
f += 1
# Cython ignores struct boundary information ("T{...}"),
# so don't output it
f = _util_dtypestring(child, f, end, offset)
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):
cdef int *p2
i1 = p1 # error
p2 = obj # error
obj = p2 # error
_ERRORS = u"""
5:16: Cannot assign type 'char *' 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
def foo(obj):
cdef int *p
p = <int *>blarg # okay
p = <int *>(foo + blarg) # error - temporary
cdef void *p
p = <void *>blarg # ok
p = <void *>(obj + blarg) # error - temporary
_ERRORS = u"""
6:5: Casting temporary Python object to non-numeric non-Python type
"""
......@@ -25,7 +25,7 @@ cdef object m():
i = 42
obj = None
17L
7j
<object>7j
help
`"Hello"`
import fred
......@@ -85,7 +85,7 @@ _ERRORS = u"""
15: 5: 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
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
30: 8: Backquote expression 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():
x = None
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
#
......@@ -532,38 +478,25 @@ def no_negative_indices(object[int, negative_indices=False] buf, int idx):
"""
return buf[idx]
#
# Buffer type mismatch examples. Varying the type and access
# method simultaneously, the odds of an interaction is virtually
# zero.
#
@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
@testcase
def fmtst2(object[int] buf):
"""
>>> fmtst2(FloatMockBuffer("A", range(3)))
Again, the most interesting thing here is to inspect the C source.
>>> A = IntMockBuffer(None, range(4))
>>> wraparound_directive(A, 2, -1)
5
>>> wraparound_directive(A, -1, 2)
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.
......@@ -860,8 +793,7 @@ def printbuf_td_cy_int(object[td_cy_int] buf, shape):
>>> printbuf_td_cy_int(ShortMockBuffer(None, range(3)), (3,))
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
for i in range(shape[0]):
......@@ -876,7 +808,7 @@ def printbuf_td_h_short(object[td_h_short] buf, shape):
>>> printbuf_td_h_short(IntMockBuffer(None, range(3)), (3,))
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
for i in range(shape[0]):
......@@ -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,))
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
for i in range(shape[0]):
......@@ -906,7 +838,7 @@ def printbuf_td_h_ushort(object[td_h_ushort] buf, shape):
>>> printbuf_td_h_ushort(ShortMockBuffer(None, range(3)), (3,))
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
for i in range(shape[0]):
......@@ -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,))
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
for i in range(shape[0]):
......@@ -1033,7 +965,7 @@ def buffer_cast_fails(object[char, cast=True] buf):
>>> buffer_cast_fails(IntMockBuffer(None, [0]))
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]
......@@ -1366,48 +1298,31 @@ cdef class NestedStructMockBuffer(MockBuffer):
@testcase
def basic_struct(object[MyStruct] buf):
"""
See also buffmt.pyx
>>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)]))
1 2 3 4 5
>>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="bbqii"))
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
@testcase
def nested_struct(object[NestedStruct] buf):
"""
See also buffmt.pyx
>>> nested_struct(NestedStructMockBuffer(None, [(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"))
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
cdef struct LongComplex:
long double real
long double imag
cdef struct MixedComplex:
long double real
float imag
cdef class LongComplexMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1:
cdef LongComplex* s
......@@ -1418,24 +1333,33 @@ cdef class LongComplexMockBuffer(MockBuffer):
cdef get_itemsize(self): return sizeof(LongComplex)
cdef get_default_format(self): return b"Zg"
#cdef extern from "complex.h":
# pass
@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_struct_dtype(LongComplexMockBuffer(None, [(0, -1)]))
0.0 -1.0
>>> complex_dtype(LongComplexMockBuffer(None, [(0, -1)]))
-1j
"""
print buf[0].real, buf[0].imag
print buf[0]
@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.
>>> mixed_complex_struct_dtype(LongComplexMockBuffer(None, [(0, -1)]))
Traceback (most recent call last):
...
ValueError: Cannot store complex number in 'MixedComplex' as 'long double' differs from 'float' in size.
>>> complex_inplace(LongComplexMockBuffer(None, [(0, -1)]))
(1+1j)
"""
buf[0] = buf[0] + 1 + 2j
print buf[0]
@testcase
def complex_struct_dtype(object[LongComplex] buf):
"""
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
......@@ -1448,7 +1372,7 @@ def complex_struct_inplace(object[LongComplex] buf):
buf[0].real += 1
buf[0].imag += 2
print buf[0].real, buf[0].imag
#
# Nogil
#
......
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):
def __int__(self):
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):
u"""
>>> test_convert_pyint(None)
......
__doc__ = u'''
>>> no_cdef()
>>> with_cdef()
<<<<<<< local
>>> 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]
<<<<<<< local
>>> test_list(range(11), "invalid index", None)
=======
>>> test_list(list(range(11)), "invalid index", None) #doctest: +ELLIPSIS
>>>>>>> other
Traceback (most recent call last):
...
TypeError: list indices must be integers, not str
TypeError: list indices must be integers...
'''
def no_cdef():
lst = range(11)
lst = list(range(11))
ob = 10L
lst[ob] = -10
dd = {}
dd[ob] = -10
def with_cdef():
cdef list lst = range(11)
cdef list lst = list(range(11))
ob = 10L
lst[ob] = -10
cdef dict dd = {}
......
......@@ -14,11 +14,11 @@ cdef char* s = 'abcdef'
def global_c_and_s():
pys = s
print c
print pys.decode('ASCII')
print (pys.decode(u'ASCII'))
def local_c_and_s():
cdef char c = 'b'
cdef char* s = 'bcdefg'
pys = s
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"""
def double_target(a, b):
cdef double x
for x from a <= x < b:
print "at", x
print u"at", x
return x
def double_step(a, b, dx):
cdef double x
for x from a <= x < b by dx:
print "at", x
print u"at", x
return x
def double_step_typed(a, b, double dx):
cdef double x
for x from a <= x < b by dx:
print "at", x
print u"at", x
return x
def double_step_py_target(a, b, double dx):
cdef object x
for x from a <= x < b by dx:
print "at", x
print u"at", x
return x
def int_step_py_target(a, b, int dx):
cdef object x
for x from a <= x < b by dx:
print "at", x
print u"at", x
return x
......@@ -2,6 +2,17 @@
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:
import numpy as np
__doc__ = u"""
......@@ -111,6 +122,7 @@ try:
>>> test_dtype('I', inc1_uint)
>>> test_dtype('l', inc1_long)
>>> test_dtype('L', inc1_ulong)
>>> test_dtype('f', inc1_float)
>>> test_dtype('d', inc1_double)
>>> test_dtype('g', inc1_longdouble)
......@@ -118,6 +130,9 @@ try:
>>> test_dtype('F', inc1_cfloat) # numpy format codes differ from buffer ones here
>>> test_dtype('D', inc1_cdouble)
>>> 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.long, inc1_long_t)
......@@ -129,34 +144,62 @@ try:
>>> test_dtype(np.int32, inc1_int32_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_nested_dtypes(np.zeros((3,), dtype=np.dtype([\
>>> print(test_nested_dtypes(np.zeros((3,), dtype=np.dtype([\
('a', np.dtype('i,i')),\
('b', np.dtype('i,i'))\
])))
]))))
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')),\
('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):
...
ValueError: Buffer dtype mismatch (expected int, got float)
ValueError: Buffer dtype mismatch; next field is at offset 1 but 4 expected
>>> test_good_cast()
True
>>> test_bad_cast()
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:
__doc__ = u""
def ndarray_str(arr):
u"""
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
def inc1_double(np.ndarray[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].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].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
x = arr[1].real + 1
arr[1].real = x
......@@ -297,7 +344,7 @@ def test_nested_dtypes(obj):
arr[1].b.x = arr[0].a.y + 1
arr[1].b.y = 4
arr[2] = arr[1]
return arr
return repr(arr).replace('<', '!').replace('>', '!')
def test_bad_nested_dtypes():
cdef object[BadNestedStruct] arr
......@@ -310,4 +357,22 @@ def test_good_cast():
def test_bad_cast():
# 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
5
"""
cdef int get_bound(int m):
print "get_bound(%s)"%m
print u"get_bound(%s)"%m
return m
def for_from_range(a, b):
cdef int i = 100
print "range(%s)" % a
print u"range(%s)" % a
for i in range(a):
print "at", i
print "range(%s, %s)" % (a, b)
print u"at", i
print u"range(%s, %s)" % (a, b)
for i in range(a, b):
print "at", i
print "range(%s, %s, %s)" % (a, b, 2)
print u"at", i
print u"range(%s, %s, %s)" % (a, b, 2)
for i in range(a, b, 2):
print "at", i
print u"at", i
return i
def for_from_bound_reassignment(int bound, int fake_bound):
cdef int i = 100
for i from 0 <= i < bound:
print "at", i
print u"at", i
bound = fake_bound
return i
def for_from_step_reassignment(int bound, int step, int fake_step):
cdef int i = 100
for i from 0 <= i < bound by step:
print "at", i
print u"at", i
step = fake_step
return i
def for_from_target_reassignment(int bound, int factor):
cdef int i = 100
for i from 0 <= i < bound:
print "at", i
print u"at", i
i *= factor
return i
def for_from_py_target_reassignment(int bound, int factor):
cdef object i
for i from 0 <= i < bound:
print "at", i
print u"at", i
i *= factor
return i
def for_from_py_global_target_reassignment(int bound, int factor):
global g_var
for g_var from 0 <= g_var < bound:
print "at", g_var
print u"at", g_var
g_var *= factor
return g_var
def for_in_target_reassignment(int bound, int factor):
cdef int i = 100
for i in range(bound):
print "at", i
print u"at", i
i *= factor
return i
def test_func(int n):
cdef int i = 100
for i from 0 <= i < get_bound(n):
print "at", i
print u"at", 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"""
>>> slice_list_assign(l2, dict(zip(l,l)))
[1, 1, 2, 3, 4, 4]
>>> slice_charp('abcdefg')
'bc'
>>> slice_charp_repeat('abcdefg')
'cd'
>>> print("%s" % slice_charp('abcdefg'))
bc
>>> print("%s" % slice_charp_repeat('abcdefg'))
cd
"""
def slice_list(list l):
......@@ -51,12 +51,14 @@ def slice_list_assign(list l, value):
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
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 str slice_val = s[1:6]
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