Commit 7ef9c72f authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'release'

Conflicts:
	tests/run/numpy_common.pxi
parents c711c46a d093dcc0
...@@ -269,7 +269,10 @@ def parse_dependencies(source_filename): ...@@ -269,7 +269,10 @@ def parse_dependencies(source_filename):
if '\t' in source: if '\t' in source:
source = source.replace('\t', ' ') source = source.replace('\t', ' ')
# TODO: pure mode # TODO: pure mode
dependancy = re.compile(r"(cimport +([0-9a-zA-Z_.]+)\b)|(from +([0-9a-zA-Z_.]+) +cimport)|(include +'([^']+)')|(cdef +extern +from +'([^']+)')") dependancy = re.compile(r"(cimport +([0-9a-zA-Z_.]+)\b)|"
"(from +([0-9a-zA-Z_.]+) +cimport)|"
"(include +['\"]([^'\"]+)['\"])|"
"(cdef +extern +from +['\"]([^'\"]+)['\"])")
cimports = [] cimports = []
includes = [] includes = []
externs = [] externs = []
......
...@@ -1903,6 +1903,26 @@ class CCodeWriter(object): ...@@ -1903,6 +1903,26 @@ class CCodeWriter(object):
self.putln(string) self.putln(string)
self.putln("#endif /* _OPENMP */") self.putln("#endif /* _OPENMP */")
def undef_builtin_expect(self, cond):
"""
Redefine the macros likely() and unlikely to no-ops, depending on
condition 'cond'
"""
self.putln("#if %s" % cond)
self.putln(" #undef likely")
self.putln(" #undef unlikely")
self.putln(" #define likely(x) (x)")
self.putln(" #define unlikely(x) (x)")
self.putln("#endif")
def redef_builtin_expect(self, cond):
self.putln("#if %s" % cond)
self.putln(" #undef likely")
self.putln(" #undef unlikely")
self.putln(" #define likely(x) __builtin_expect(!!(x), 1)")
self.putln(" #define unlikely(x) __builtin_expect(!!(x), 0)")
self.putln("#endif")
class PyrexCodeWriter(object): class PyrexCodeWriter(object):
# f file output file # f file output file
# level int indentation level # level int indentation level
......
...@@ -1363,16 +1363,9 @@ class NameNode(AtomicExprNode): ...@@ -1363,16 +1363,9 @@ class NameNode(AtomicExprNode):
allow_null = False allow_null = False
nogil = False nogil = False
def create_analysed_rvalue(pos, env, entry):
node = NameNode(pos)
node.analyse_types(env, entry=entry)
return node
def as_cython_attribute(self): def as_cython_attribute(self):
return self.cython_attribute return self.cython_attribute
create_analysed_rvalue = staticmethod(create_analysed_rvalue)
def type_dependencies(self, env): def type_dependencies(self, env):
if self.entry is None: if self.entry is None:
self.entry = env.lookup(self.name) self.entry = env.lookup(self.name)
...@@ -4435,6 +4428,17 @@ class AttributeNode(ExprNode): ...@@ -4435,6 +4428,17 @@ class AttributeNode(ExprNode):
# method of an extension type, so we treat it like a Python # method of an extension type, so we treat it like a Python
# attribute. # attribute.
pass pass
# NumPy hack
if obj_type.is_extension_type and obj_type.objstruct_cname == 'PyArrayObject':
from NumpySupport import numpy_transform_attribute_node
replacement_node = numpy_transform_attribute_node(self)
# Since we can't actually replace our node yet, we only grasp its
# type, and then the replacement happens in
# AnalyseExpresssionsTransform...
self.type = replacement_node.type
if replacement_node is not self:
return
# If we get here, the base object is not a struct/union/extension # If we get here, the base object is not a struct/union/extension
# type, or it is an extension type and the attribute is either not # type, or it is an extension type and the attribute is either not
# declared or is declared as a Python method. Treat it as a Python # declared or is declared as a Python method. Treat it as a Python
......
...@@ -7494,6 +7494,8 @@ class ParallelStatNode(StatNode, ParallelNode): ...@@ -7494,6 +7494,8 @@ class ParallelStatNode(StatNode, ParallelNode):
self.begin_of_parallel_control_block_point = code.insertion_point() self.begin_of_parallel_control_block_point = code.insertion_point()
self.begin_of_parallel_control_block_point_after_decls = code.insertion_point() self.begin_of_parallel_control_block_point_after_decls = code.insertion_point()
self.undef_builtin_expect_apple_gcc_bug(code)
def begin_parallel_block(self, code): def begin_parallel_block(self, code):
""" """
Each OpenMP thread in a parallel section that contains a with gil block Each OpenMP thread in a parallel section that contains a with gil block
...@@ -7786,6 +7788,24 @@ class ParallelStatNode(StatNode, ParallelNode): ...@@ -7786,6 +7788,24 @@ class ParallelStatNode(StatNode, ParallelNode):
"}") # end if "}") # end if
code.end_block() # end parallel control flow block code.end_block() # end parallel control flow block
self.redef_builtin_expect_apple_gcc_bug(code)
# FIXME: improve with version number for OS X Lion
buggy_platform_macro_condition = "(defined(__APPLE__) || defined(__OSX__))"
have_expect_condition = "(defined(__GNUC__) && " \
"(__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))))"
redef_condition = "(%s && %s)" % (buggy_platform_macro_condition, have_expect_condition)
def undef_builtin_expect_apple_gcc_bug(self, code):
"""
A bug on OS X Lion disallows __builtin_expect macros. This code avoids them
"""
if not self.parent:
code.undef_builtin_expect(self.redef_condition)
def redef_builtin_expect_apple_gcc_bug(self, code):
if not self.parent:
code.redef_builtin_expect(self.redef_condition)
class ParallelWithBlockNode(ParallelStatNode): class ParallelWithBlockNode(ParallelStatNode):
......
# The hacks that are specific for NumPy. These were introduced because
# the NumPy ABI changed so that the shape, ndim, strides, etc. fields were
# no longer available, however the use of these were so entrenched in
# Cython codes
import PyrexTypes
import ExprNodes
from StringEncoding import EncodedString
def numpy_transform_attribute_node(node):
assert isinstance(node, ExprNodes.AttributeNode)
if node.obj.type.objstruct_cname != 'PyArrayObject':
return node
pos = node.pos
numpy_pxd_scope = node.obj.entry.type.scope.parent_scope
def macro_call_node(numpy_macro_name):
array_node = node.obj
func_entry = numpy_pxd_scope.entries[numpy_macro_name]
function_name_node = ExprNodes.NameNode(
name=EncodedString(numpy_macro_name),
pos=pos,
entry=func_entry,
is_called=1,
type=func_entry.type,
cf_maybe_null=False,
cf_is_null=False)
call_node = ExprNodes.SimpleCallNode(
pos=pos,
function=function_name_node,
name=EncodedString(numpy_macro_name),
args=[array_node],
type=func_entry.type.return_type,
analysed=True)
return call_node
if node.attribute == u'ndim':
result = macro_call_node(u'PyArray_NDIM')
elif node.attribute == u'data':
call_node = macro_call_node(u'PyArray_DATA')
cast_node = ExprNodes.TypecastNode(pos,
type=PyrexTypes.c_char_ptr_type,
operand=call_node)
result = cast_node
elif node.attribute == u'shape':
result = macro_call_node(u'PyArray_DIMS')
elif node.attribute == u'strides':
result = macro_call_node(u'PyArray_STRIDES')
else:
result = node
return result
...@@ -563,6 +563,9 @@ class IterationTransform(Visitor.VisitorTransform): ...@@ -563,6 +563,9 @@ class IterationTransform(Visitor.VisitorTransform):
if step_value == 0: if step_value == 0:
# will lead to an error elsewhere # will lead to an error elsewhere
return node return node
if reversed and step_value not in (1, -1):
# FIXME: currently broken - requires calculation of the correct bounds
return node
if not isinstance(step, ExprNodes.IntNode): if not isinstance(step, ExprNodes.IntNode):
step = ExprNodes.IntNode(step_pos, value=str(step_value), step = ExprNodes.IntNode(step_pos, value=str(step_value),
constant_result=step_value) constant_result=step_value)
......
...@@ -1744,6 +1744,7 @@ if VALUE is not None: ...@@ -1744,6 +1744,7 @@ if VALUE is not None:
class AnalyseExpressionsTransform(CythonTransform): class AnalyseExpressionsTransform(CythonTransform):
# Also handles NumPy
def visit_ModuleNode(self, node): def visit_ModuleNode(self, node):
self.env_stack = [node.scope] self.env_stack = [node.scope]
...@@ -1785,9 +1786,18 @@ class AnalyseExpressionsTransform(CythonTransform): ...@@ -1785,9 +1786,18 @@ class AnalyseExpressionsTransform(CythonTransform):
elif node.memslice_ellipsis_noop: elif node.memslice_ellipsis_noop:
# memoryviewslice[...] expression, drop the IndexNode # memoryviewslice[...] expression, drop the IndexNode
node = node.base node = node.base
return node return node
def visit_AttributeNode(self, node):
# Note: Expression analysis for attributes has already happened
# at this point (by recursive calls starting from FuncDefNode)
#print node.dump()
#return node
type = node.obj.type
if type.is_extension_type and type.objstruct_cname == 'PyArrayObject':
from NumpySupport import numpy_transform_attribute_node
node = numpy_transform_attribute_node(node)
return node
class FindInvalidUseOfFusedTypes(CythonTransform): class FindInvalidUseOfFusedTypes(CythonTransform):
......
...@@ -188,6 +188,7 @@ def create_pipeline(context, mode, exclude_classes=()): ...@@ -188,6 +188,7 @@ def create_pipeline(context, mode, exclude_classes=()):
_check_c_declarations, _check_c_declarations,
InlineDefNodeCalls(context), InlineDefNodeCalls(context),
AnalyseExpressionsTransform(context), AnalyseExpressionsTransform(context),
# AnalyseExpressionsTransform also contains the NumPy-specific support
FindInvalidUseOfFusedTypes(context), FindInvalidUseOfFusedTypes(context),
CreateClosureClasses(context), ## After all lookups and type inference CreateClosureClasses(context), ## After all lookups and type inference
ExpandInplaceOperators(context), ExpandInplaceOperators(context),
......
...@@ -151,6 +151,9 @@ cdef extern from "numpy/arrayobject.h": ...@@ -151,6 +151,9 @@ cdef extern from "numpy/arrayobject.h":
ctypedef void (*PyArray_VectorUnaryFunc)(void *, void *, npy_intp, void *, void *) ctypedef void (*PyArray_VectorUnaryFunc)(void *, void *, npy_intp, void *, void *)
ctypedef struct PyArray_Descr:
pass
ctypedef class numpy.dtype [object PyArray_Descr]: ctypedef class numpy.dtype [object PyArray_Descr]:
# Use PyDataType_* macros when possible, however there are no macros # Use PyDataType_* macros when possible, however there are no macros
# for accessing some of the fields, so some are defined. Please # for accessing some of the fields, so some are defined. Please
...@@ -177,15 +180,11 @@ cdef extern from "numpy/arrayobject.h": ...@@ -177,15 +180,11 @@ cdef extern from "numpy/arrayobject.h":
ctypedef class numpy.ndarray [object PyArrayObject]: ctypedef class numpy.ndarray [object PyArrayObject]:
cdef __cythonbufferdefaults__ = {"mode": "strided"} cdef __cythonbufferdefaults__ = {"mode": "strided"}
cdef: # Note: The fields are no longer defined, please use accessor
# Only taking a few of the most commonly used and stable fields. # functions. Cython special-cases/hacks the data, ndim, shape
# One should use PyArray_* macros instead to access the C fields. # and stride attributes of the ndarray to use accessor
char *data # functions for backwards compatability and convenience.
int ndim "nd"
npy_intp *shape "dimensions"
npy_intp *strides
dtype descr
PyObject* base
# Note: This syntax (function definition in pxd files) is an # Note: This syntax (function definition in pxd files) is an
# experimental exception made for __getbuffer__ and __releasebuffer__ # experimental exception made for __getbuffer__ and __releasebuffer__
...@@ -236,7 +235,7 @@ cdef extern from "numpy/arrayobject.h": ...@@ -236,7 +235,7 @@ cdef extern from "numpy/arrayobject.h":
cdef int t cdef int t
cdef char* f = NULL cdef char* f = NULL
cdef dtype descr = self.descr cdef dtype descr = get_array_dtype(self)
cdef list stack cdef list stack
cdef int offset cdef int offset
...@@ -376,20 +375,29 @@ cdef extern from "numpy/arrayobject.h": ...@@ -376,20 +375,29 @@ cdef extern from "numpy/arrayobject.h":
bint PyArray_ISWRITEABLE(ndarray m) bint PyArray_ISWRITEABLE(ndarray m)
bint PyArray_ISALIGNED(ndarray m) bint PyArray_ISALIGNED(ndarray m)
int PyArray_NDIM(ndarray) int PyArray_NDIM(ndarray) nogil
bint PyArray_ISONESEGMENT(ndarray) bint PyArray_ISONESEGMENT(ndarray)
bint PyArray_ISFORTRAN(ndarray) bint PyArray_ISFORTRAN(ndarray)
int PyArray_FORTRANIF(ndarray) int PyArray_FORTRANIF(ndarray)
void* PyArray_DATA(ndarray) void* PyArray_DATA(ndarray) nogil
char* PyArray_BYTES(ndarray) char* PyArray_BYTES(ndarray) nogil
npy_intp* PyArray_DIMS(ndarray) npy_intp* PyArray_DIMS(ndarray) nogil
npy_intp* PyArray_STRIDES(ndarray) npy_intp* PyArray_STRIDES(ndarray) nogil
npy_intp PyArray_DIM(ndarray, size_t) npy_intp PyArray_DIM(ndarray, size_t) nogil
npy_intp PyArray_STRIDE(ndarray, size_t) npy_intp PyArray_STRIDE(ndarray, size_t) nogil
# The two functions below return borrowed references and should
# be used with care; often you will want to use get_array_base
# or get_array_dtype (define below) instead from Cython.
PyObject* PyArray_BASE(ndarray)
# Cython API of the function below might change! PyArray_DESCR
# actually returns PyArray_Descr* == pointer-version of dtype,
# which appears to be difficult to declare properly in Cython;
# protect it with trailing underscore for now just to avoid having
# user code depend on it without reading this note.
PyArray_Descr * PyArray_DESCR_ "PyArray_DESCR"(ndarray)
# object PyArray_BASE(ndarray) wrong refcount semantics
# dtype PyArray_DESCR(ndarray) wrong refcount semantics
int PyArray_FLAGS(ndarray) int PyArray_FLAGS(ndarray)
npy_intp PyArray_ITEMSIZE(ndarray) npy_intp PyArray_ITEMSIZE(ndarray)
int PyArray_TYPE(ndarray arr) int PyArray_TYPE(ndarray arr)
...@@ -961,18 +969,34 @@ cdef extern from "numpy/ufuncobject.h": ...@@ -961,18 +969,34 @@ cdef extern from "numpy/ufuncobject.h":
void import_ufunc() void import_ufunc()
cdef inline void set_array_base(ndarray arr, object base): # The ability to set the base field of an ndarray seems to be
cdef PyObject* baseptr # deprecated in NumPy 1.7 (no PyArray_SET_BASE seems to be
if base is None: # available). Remove this support and see who complains and how their
baseptr = NULL # case could be fixed in 1.7...
else: #
Py_INCREF(base) # important to do this before decref below! #cdef inline void set_array_base(ndarray arr, object base):
baseptr = <PyObject*>base # cdef PyObject* baseptr
Py_XDECREF(arr.base) # if base is None:
arr.base = baseptr # baseptr = NULL
# else:
# Py_INCREF(base) # important to do this before decref below!
# baseptr = <PyObject*>base
# Py_XDECREF(arr.base)
# arr.base = baseptr
cdef inline object get_array_base(ndarray arr): cdef inline object get_array_base(ndarray arr):
if arr.base is NULL: cdef PyObject *pobj = PyArray_BASE(arr)
if pobj != NULL:
obj = <object>pobj
Py_INCREF(obj)
return obj
else:
return None return None
cdef inline dtype get_array_dtype(ndarray arr):
if PyArray_DESCR_(arr) != NULL:
obj = <object>PyArray_DESCR_(arr)
Py_INCREF(obj)
return obj
else: else:
return <object>arr.base return None
...@@ -134,6 +134,8 @@ static int __Pyx_GetBuffer(PyObject *obj, Py_buffer *view, int flags) { ...@@ -134,6 +134,8 @@ static int __Pyx_GetBuffer(PyObject *obj, Py_buffer *view, int flags) {
goto fail; goto fail;
return func(obj, view, flags); return func(obj, view, flags);
} else {
PyErr_Clear();
} }
#endif #endif
...@@ -182,6 +184,8 @@ static void __Pyx_ReleaseBuffer(Py_buffer *view) { ...@@ -182,6 +184,8 @@ static void __Pyx_ReleaseBuffer(Py_buffer *view) {
func(obj, view); func(obj, view);
return; return;
} else {
PyErr_Clear();
} }
#endif #endif
......
# tag: numpy
import numpy as np
cimport numpy as np
def f():
"""
>>> f()
ndim 2
data 1
shape 3 2
shape[1] 2
strides 16 8
"""
cdef np.ndarray x = np.ones((3, 2), dtype=np.int64)
cdef int i
cdef Py_ssize_t j, k
cdef char *p
# todo: int * p: 23:13: Cannot assign type 'char *' to 'int *'
with nogil:
i = x.ndim
print 'ndim', i
with nogil:
p = x.data
print 'data', (<np.int64_t*>p)[0]
with nogil:
j = x.shape[0]
k = x.shape[1]
print 'shape', j, k
# Check that non-typical uses still work
cdef np.npy_intp *shape
with nogil:
shape = x.shape + 1
print 'shape[1]', shape[0]
with nogil:
j = x.strides[0]
k = x.strides[1]
print 'strides', j, k
# This file is to avoid "unused function" warnings. # (disabled) hack to avoid C compiler warnings about unused functions in the NumPy header files
cdef extern from *: ##cdef extern from *:
bint FALSE "0" ## bint FALSE "0"
void import_array() ## void import_array()
void import_umath() ## void import_umath1(void* ret)
##
if FALSE: ##if FALSE:
import_array() ## import_array()
import_umath() ## import_umath1(NULL)
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
Test slicing for memoryviews and memoryviewslices Test slicing for memoryviews and memoryviewslices
""" """
import sys
cimport numpy as np cimport numpy as np
import numpy as np import numpy as np
cimport cython cimport cython
...@@ -422,9 +424,14 @@ cdef packed struct StructArray: ...@@ -422,9 +424,14 @@ cdef packed struct StructArray:
@testcase_numpy_1_5 @testcase_numpy_1_5
def test_memslice_structarray(data, dtype): def test_memslice_structarray(data, dtype):
""" """
>>> data = [(range(4), 'spam\\0'), (range(4, 8), 'ham\\0\\0'), (range(8, 12), 'eggs\\0')] >>> def b(s): return s.encode('ascii')
>>> def to_byte_values(b):
... if sys.version_info[0] >= 3: return list(b)
... else: return map(ord, b)
>>> data = [(range(4), b('spam\\0')), (range(4, 8), b('ham\\0\\0')), (range(8, 12), b('eggs\\0'))]
>>> dtype = np.dtype([('a', '4i'), ('b', '5b')]) >>> dtype = np.dtype([('a', '4i'), ('b', '5b')])
>>> test_memslice_structarray([(L, map(ord, s)) for L, s in data], dtype) >>> test_memslice_structarray([(L, to_byte_values(s)) for L, s in data], dtype)
0 0
1 1
2 2
...@@ -468,7 +475,7 @@ def test_memslice_structarray(data, dtype): ...@@ -468,7 +475,7 @@ def test_memslice_structarray(data, dtype):
for i in range(3): for i in range(3):
for j in range(4): for j in range(4):
print myslice[i].a[j] print myslice[i].a[j]
print myslice[i].b print myslice[i].b.decode('ASCII')
@testcase_numpy_1_5 @testcase_numpy_1_5
def test_structarray_errors(StructArray[:] a): def test_structarray_errors(StructArray[:] a):
...@@ -520,8 +527,9 @@ def stringtest(String[:] view): ...@@ -520,8 +527,9 @@ def stringtest(String[:] view):
@testcase_numpy_1_5 @testcase_numpy_1_5
def test_string_invalid_dims(): def test_string_invalid_dims():
""" """
>>> def b(s): return s.encode('ascii')
>>> dtype = np.dtype([('a', 'S4')]) >>> dtype = np.dtype([('a', 'S4')])
>>> data = ['spam', 'eggs'] >>> data = [b('spam'), b('eggs')]
>>> stringstructtest(np.array(data, dtype=dtype)) >>> stringstructtest(np.array(data, dtype=dtype))
Traceback (most recent call last): Traceback (most recent call last):
... ...
......
...@@ -211,10 +211,13 @@ try: ...@@ -211,10 +211,13 @@ try:
""" """
if np.__version__ >= '1.6': if np.__version__ >= '1.6' and False:
__doc__ += u""" __doc__ += u"""
The following expose bugs in Numpy (versions prior to 2011-04-02): Tests are DISABLED as the buffer format parser does not align members
of aligned structs in padded structs in relation to the possibly
unaligned initial offset.
The following expose bugs in Numpy (versions prior to 2011-04-02):
>>> print(test_partially_packed_align(np.zeros((1,), dtype=np.dtype([('a', 'b'), ('b', 'i'), ('sub', np.dtype('b,i')), ('c', 'i')], align=True)))) >>> print(test_partially_packed_align(np.zeros((1,), dtype=np.dtype([('a', 'b'), ('b', 'i'), ('sub', np.dtype('b,i')), ('c', 'i')], align=True))))
array([(22, 23, (24, 25), 26)], array([(22, 23, (24, 25), 26)],
dtype=[('a', '|i1'), ('', '|V3'), ('b', '!i4'), ('sub', [('f0', '|i1'), ('f1', '!i4')]), ('', '|V3'), ('c', '!i4')]) dtype=[('a', '|i1'), ('', '|V3'), ('b', '!i4'), ('sub', [('f0', '|i1'), ('f1', '!i4')]), ('', '|V3'), ('c', '!i4')])
......
...@@ -133,6 +133,25 @@ def reversed_range_step_neg(int a, int b): ...@@ -133,6 +133,25 @@ def reversed_range_step_neg(int a, int b):
result.append(i) result.append(i)
return result, i return result, i
#@cython.test_assert_path_exists('//ForFromStatNode')
def reversed_range_step3(int a, int b):
"""
>>> [ i for i in _reversed(range(0, 5, 3)) ]
[3, 0]
>>> reversed_range_step3(0, 5)
([3, 0], 0)
>>> [ i for i in _reversed(range(5, 0, 3)) ]
[]
>>> reversed_range_step3(5, 0)
([], 99)
"""
cdef int i = 99
result = []
for i in reversed(range(a, b, 3)):
result.append(i)
return result, i
unicode_string = u"abcDEF" unicode_string = u"abcDEF"
@cython.test_assert_path_exists('//ForFromStatNode') @cython.test_assert_path_exists('//ForFromStatNode')
......
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