From 68e1429c9c879eeb5f6627fea100aa3c56c7e4b9 Mon Sep 17 00:00:00 2001 From: Dag Sverre Seljebotn Date: Fri, 18 Jul 2008 12:40:26 +0200 Subject: [PATCH] Introduced BufferType, start of numpy-independent testcase, GetBuffer improvements --- Cython/Compiler/Buffer.py | 13 +++--- Cython/Compiler/ExprNodes.py | 6 +-- Cython/Compiler/ModuleNode.py | 25 +++++++++-- Cython/Compiler/Nodes.py | 3 +- Cython/Compiler/PyrexTypes.py | 40 ++++++++++-------- tests/run/bufaccess.pyx | 78 +++++++++++++++++++++++++++++++++++ 6 files changed, 134 insertions(+), 31 deletions(-) create mode 100644 tests/run/bufaccess.pyx diff --git a/Cython/Compiler/Buffer.py b/Cython/Compiler/Buffer.py index 49db82f5..dc1d5de2 100644 --- a/Cython/Compiler/Buffer.py +++ b/Cython/Compiler/Buffer.py @@ -9,6 +9,8 @@ import PyrexTypes from sets import Set as set class PureCFuncNode(Node): + child_attrs = [] + def __init__(self, pos, cname, type, c_code, visibility='private'): self.pos = pos self.cname = cname @@ -97,14 +99,14 @@ class BufferTransform(CythonTransform): # on the buffer entry bufvars = [(name, entry) for name, entry in scope.entries.iteritems() - if entry.type.buffer_options is not None] + if entry.type.is_buffer] for name, entry in bufvars: - bufopts = entry.type.buffer_options + buftype = entry.type # Get or make a type string checker - tschecker = self.tschecker(bufopts.dtype) + tschecker = self.tschecker(buftype.dtype) # Declare auxiliary vars bufinfo = scope.declare_var(temp_name_handle(u"%s_bufinfo" % name), @@ -116,7 +118,7 @@ class BufferTransform(CythonTransform): stridevars = [] shapevars = [] - for idx in range(bufopts.ndim): + for idx in range(buftype.ndim): # stride varname = temp_name_handle(u"%s_%s%d" % (name, "stride", idx)) var = scope.declare_var(varname, PyrexTypes.c_int_type, node.pos, is_cdef=True) @@ -216,7 +218,7 @@ class BufferTransform(CythonTransform): expr = AddNode(pos, operator='+', operand1=expr, operand2=next) casted = TypecastNode(pos, operand=expr, - type=PyrexTypes.c_ptr_type(node.base.entry.type.buffer_options.dtype)) + type=PyrexTypes.c_ptr_type(node.base.entry.type.dtype)) result = IndexNode(pos, base=casted, index=IntNode(pos, value='0')) return result @@ -412,3 +414,4 @@ class BufferTransform(CythonTransform): # TODO: # - buf must be NULL before getting new buffer + diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 7d7149bf..5b039690 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -1302,12 +1302,12 @@ class IndexNode(ExprNode): skip_child_analysis = False buffer_access = False - if self.base.type.buffer_options is not None: + if self.base.type.is_buffer: if isinstance(self.index, TupleNode): indices = self.index.args else: indices = [self.index] - if len(indices) == self.base.type.buffer_options.ndim: + if len(indices) == self.base.type.ndim: buffer_access = True skip_child_analysis = True for x in indices: @@ -1320,7 +1320,7 @@ class IndexNode(ExprNode): # for x in indices] self.indices = indices self.index = None - self.type = self.base.type.buffer_options.dtype + self.type = self.base.type.dtype self.is_temp = 1 self.is_buffer_access = True diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 3dbdf90d..04fd9e2b 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2002,9 +2002,25 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) { """) except KeyError: pass + + # Search all types for __getbuffer__ overloads + types = [] + def find_buffer_types(scope): + for m in scope.cimported_modules: + find_buffer_types(m) + for e in scope.type_entries: + t = e.type + if t.is_extension_type: + release = get = None + for x in t.scope.pyfunc_entries: + if x.name == u"__getbuffer__": get = x.func_cname + elif x.name == u"__releasebuffer__": release = x.func_cname + if get: + types.append((t.typeptr_cname, get, release)) + + find_buffer_types(self.scope) # For now, hard-code numpy imported as "numpy" - types = [] try: ndarrtype = env.entries[u'numpy'].as_module.entries['ndarray'].type types.append((ndarrtype.typeptr_cname, "numpy_getbuffer", "numpy_releasebuffer")) @@ -2015,7 +2031,7 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) { if len(types) > 0: clause = "if" for t, get, release in types: - code.putln("%s (__Pyx_TypeTest(obj, %s)) return %s(obj, view, flags);" % (clause, t, get)) + code.putln("%s (PyObject_TypeCheck(obj, %s)) return %s(obj, view, flags);" % (clause, t, get)) clause = "else if" code.putln("else {") code.putln("PyErr_Format(PyExc_TypeError, \"'%100s' does not have the buffer interface\", Py_TYPE(obj)->tp_name);") @@ -2027,8 +2043,9 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) { if len(types) > 0: clause = "if" for t, get, release in types: - code.putln("%s (__Pyx_TypeTest(obj, %s)) %s(obj, view);" % (clause, t, release)) - clause = "else if" + if release: + code.putln("%s (PyObject_TypeCheck(obj, %s)) %s(obj, view);" % (clause, t, release)) + clause = "else if" code.putln("}") code.putln("") code.putln("#endif") diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index d389bd6c..00769e13 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -627,8 +627,7 @@ class CBufferAccessTypeNode(Node): def analyse(self, env): base_type = self.base_type_node.analyse(env) dtype = self.dtype_node.analyse(env) - options = PyrexTypes.BufferOptions(dtype=dtype, ndim=self.ndim) - self.type = PyrexTypes.create_buffer_type(base_type, options) + self.type = PyrexTypes.BufferType(base_type, dtype=dtype, ndim=self.ndim) return self.type class CComplexBaseTypeNode(CBaseTypeNode): diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 87e46a21..98bd39b4 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -6,21 +6,6 @@ from Cython import Utils import Naming import copy -class BufferOptions: - # dtype PyrexType - # ndim int - def __init__(self, dtype, ndim): - self.dtype = dtype - self.ndim = ndim - - -def create_buffer_type(base_type, buffer_options): - # Make a shallow copy of base_type and then annotate it - # with the buffer information - result = copy.copy(base_type) - result.buffer_options = buffer_options - return result - class BaseType: # @@ -57,6 +42,7 @@ class PyrexType(BaseType): # is_unicode boolean Is a UTF-8 encoded C char * type # is_returncode boolean Is used only to signal exceptions # is_error boolean Is the dummy error type + # is_buffer boolean Is buffer access type # has_attributes boolean Has C dot-selectable attributes # default_value string Initial value # parsetuple_format string Format char for PyArg_ParseTuple @@ -106,11 +92,11 @@ class PyrexType(BaseType): is_unicode = 0 is_returncode = 0 is_error = 0 + is_buffer = 0 has_attributes = 0 default_value = "" parsetuple_format = "" pymemberdef_typecode = None - buffer_options = None # can contain a BufferOptions instance def resolve(self): # If a typedef, returns the base type. @@ -202,6 +188,26 @@ class CTypedefType(BaseType): def __getattr__(self, name): return getattr(self.typedef_base_type, name) +class BufferType(BaseType): + # + # Delegates most attribute + # lookups to the base type. ANYTHING NOT DEFINED + # HERE IS DELEGATED! + + # dtype PyrexType + # ndim int + + is_buffer = 1 + + def __init__(self, base, dtype, ndim): + self.base = base + self.dtype = dtype + self.ndim = ndim + + def __getattr__(self, name): + return getattr(self.base, name) + + class PyObjectType(PyrexType): # # Base class for all Python object types (reference-counted). @@ -927,7 +933,7 @@ class CEnumType(CType): # name string # cname string or None # typedef_flag boolean - + is_enum = 1 signed = 1 rank = -1 # Ranks below any integer type diff --git a/tests/run/bufaccess.pyx b/tests/run/bufaccess.pyx new file mode 100644 index 00000000..0468b228 --- /dev/null +++ b/tests/run/bufaccess.pyx @@ -0,0 +1,78 @@ +cimport __cython__ + +__doc__ = u""" + >>> fb = MockBuffer("=f", "f", [1.0, 1.25, 0.75, 1.0], (2,2)) + >>> printbuf_float(fb, (2,2)) + 1.0 1.25 + 0.75 1.0 +""" + + +def printbuf_float(o, shape): + # should make shape builtin + cdef object[float, 2] buf + buf = o + cdef int i, j + for i in range(shape[0]): + for j in range(shape[1]): + print buf[i, j], + print + + + +sizes = { + 'f': sizeof(float) +} + +cimport stdlib + +cdef class MockBuffer: + cdef object format + cdef char* buffer + cdef int len, itemsize, ndim + cdef Py_ssize_t* strides + cdef Py_ssize_t* shape + + def __init__(self, format, typechar, data, shape=None, strides=None): + self.itemsize = sizes[typechar] + if shape is None: shape = (len(data),) + if strides is None: + strides = [] + cumprod = 1 + for s in shape: + strides.append(cumprod) + cumprod *= s + strides.reverse() + strides = [x * self.itemsize for x in strides] + self.format = format + self.len = len(data) * self.itemsize + self.buffer = stdlib.malloc(self.len) + self.fill_buffer(typechar, data) + self.ndim = len(shape) + self.strides = stdlib.malloc(self.ndim * sizeof(Py_ssize_t)) + for i, x in enumerate(strides): + self.strides[i] = x + self.shape = stdlib.malloc(self.ndim * sizeof(Py_ssize_t)) + + def __getbuffer__(MockBuffer self, Py_buffer* buffer, int flags): + if buffer is NULL: + print u"locking!" + return + buffer.buf = self.buffer + buffer.len = self.len + buffer.readonly = 0 + buffer.format = self.format + buffer.ndim = self.ndim + buffer.shape = self.shape + buffer.strides = self.strides + buffer.suboffsets = NULL + buffer.itemsize = self.itemsize + buffer.internal = NULL + + cdef fill_buffer(self, typechar, object data): + cdef int idx = 0 + for value in data: + ((self.buffer + idx))[0] = value + idx += sizeof(float) + + -- 2.26.2