Commit 8c4f0c5b authored by Dag Sverre Seljebotn's avatar Dag Sverre Seljebotn

Merge

parents 9f86b4fd df78cdc7
...@@ -30,8 +30,8 @@ class AnnotationCCodeWriter(CCodeWriter): ...@@ -30,8 +30,8 @@ class AnnotationCCodeWriter(CCodeWriter):
self.annotations = create_from.annotations self.annotations = create_from.annotations
self.code = create_from.code self.code = create_from.code
def create_new(self, create_from, buffer): def create_new(self, create_from, buffer, copy_formatting):
return AnnotationCCodeWriter(create_from, buffer) return AnnotationCCodeWriter(create_from, buffer, copy_formatting)
def write(self, s): def write(self, s):
CCodeWriter.write(self, s) CCodeWriter.write(self, s)
......
...@@ -33,7 +33,6 @@ class IntroduceBufferAuxiliaryVars(CythonTransform): ...@@ -33,7 +33,6 @@ class IntroduceBufferAuxiliaryVars(CythonTransform):
node.scope.include_files.append("endian.h") node.scope.include_files.append("endian.h")
use_py2_buffer_functions(node.scope) use_py2_buffer_functions(node.scope)
use_empty_bufstruct_code(node.scope, self.max_ndim) use_empty_bufstruct_code(node.scope, self.max_ndim)
node.scope.use_utility_code(access_utility_code)
return result return result
...@@ -60,9 +59,6 @@ class IntroduceBufferAuxiliaryVars(CythonTransform): ...@@ -60,9 +59,6 @@ class IntroduceBufferAuxiliaryVars(CythonTransform):
if buftype.ndim > self.max_ndim: if buftype.ndim > self.max_ndim:
self.max_ndim = buftype.ndim self.max_ndim = buftype.ndim
# Get or make a type string checker
tschecker = buffer_type_checker(buftype.dtype, scope)
# Declare auxiliary vars # Declare auxiliary vars
cname = scope.mangle(Naming.bufstruct_prefix, name) cname = scope.mangle(Naming.bufstruct_prefix, name)
bufinfo = scope.declare_var(name="$%s" % cname, cname=cname, bufinfo = scope.declare_var(name="$%s" % cname, cname=cname,
...@@ -83,17 +79,13 @@ class IntroduceBufferAuxiliaryVars(CythonTransform): ...@@ -83,17 +79,13 @@ class IntroduceBufferAuxiliaryVars(CythonTransform):
stridevars = [var(Naming.bufstride_prefix, i, "0") for i in range(entry.type.ndim)] stridevars = [var(Naming.bufstride_prefix, i, "0") for i in range(entry.type.ndim)]
shapevars = [var(Naming.bufshape_prefix, i, "0") for i in range(entry.type.ndim)] shapevars = [var(Naming.bufshape_prefix, i, "0") for i in range(entry.type.ndim)]
entry.buffer_aux = Symtab.BufferAux(bufinfo, stridevars, shapevars, tschecker)
mode = entry.type.mode mode = entry.type.mode
if mode == 'full': if mode == 'full':
suboffsetvars = [var(Naming.bufsuboffset_prefix, i, "-1") for i in range(entry.type.ndim)] suboffsetvars = [var(Naming.bufsuboffset_prefix, i, "-1") for i in range(entry.type.ndim)]
entry.buffer_aux.lookup = get_buf_lookup_full(scope, entry.type.ndim)
elif mode == 'strided': elif mode == 'strided':
suboffsetvars = None suboffsetvars = None
entry.buffer_aux.lookup = get_buf_lookup_strided(scope, entry.type.ndim)
entry.buffer_aux.suboffsetvars = suboffsetvars entry.buffer_aux = Symtab.BufferAux(bufinfo, stridevars, shapevars, suboffsetvars)
entry.buffer_aux.get_buffer_cname = tschecker
scope.buffer_entries = bufvars scope.buffer_entries = bufvars
self.scope = scope self.scope = scope
...@@ -144,24 +136,19 @@ def put_unpack_buffer_aux_into_scope(buffer_aux, mode, code): ...@@ -144,24 +136,19 @@ def put_unpack_buffer_aux_into_scope(buffer_aux, mode, code):
(s.cname, bufstruct, field, idx) (s.cname, bufstruct, field, idx)
for idx, s in enumerate(vars)])) for idx, s in enumerate(vars)]))
def getbuffer_cond_code(obj_cname, buffer_aux, flags, ndim):
bufstruct = buffer_aux.buffer_info_var.cname
return "%s(%s, &%s, %s, %d) == -1" % (
buffer_aux.get_buffer_cname, obj_cname, bufstruct, flags, ndim)
def put_acquire_arg_buffer(entry, code, pos): def put_acquire_arg_buffer(entry, code, pos):
code.globalstate.use_utility_code(acquire_utility_code)
buffer_aux = entry.buffer_aux buffer_aux = entry.buffer_aux
cname = entry.cname getbuffer_cname = get_getbuffer_code(entry.type.dtype, code)
bufstruct = buffer_aux.buffer_info_var.cname
flags = get_flags(buffer_aux, entry.type)
# Acquire any new buffer # Acquire any new buffer
code.putln(code.error_goto_if(getbuffer_cond_code(cname, code.putln(code.error_goto_if("%s(%s, &%s, %s, %d) == -1" % (
buffer_aux, getbuffer_cname,
flags, entry.cname,
entry.type.ndim), entry.buffer_aux.buffer_info_var.cname,
pos)) get_flags(buffer_aux, entry.type),
entry.type.ndim), pos))
# An exception raised in arg parsing cannot be catched, so no # An exception raised in arg parsing cannot be catched, so no
# need to do care about the buffer then. # need to care about the buffer then.
put_unpack_buffer_aux_into_scope(buffer_aux, entry.type.mode, code) put_unpack_buffer_aux_into_scope(buffer_aux, entry.type.mode, code)
#def put_release_buffer_normal(entry, code): #def put_release_buffer_normal(entry, code):
...@@ -192,11 +179,12 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type, ...@@ -192,11 +179,12 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
(which may or may not succeed). (which may or may not succeed).
""" """
code.globalstate.use_utility_code(acquire_utility_code)
bufstruct = buffer_aux.buffer_info_var.cname bufstruct = buffer_aux.buffer_info_var.cname
flags = get_flags(buffer_aux, buffer_type) flags = get_flags(buffer_aux, buffer_type)
getbuffer = "%s(%%s, &%s, %s, %d)" % (buffer_aux.get_buffer_cname, getbuffer = "%s(%%s, &%s, %s, %d)" % (get_getbuffer_code(buffer_type.dtype, code),
# note: object is filled in later # note: object is filled in later (%%s)
bufstruct, bufstruct,
flags, flags,
buffer_type.ndim) buffer_type.ndim)
...@@ -209,7 +197,7 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type, ...@@ -209,7 +197,7 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
lhs_cname, bufstruct)) lhs_cname, bufstruct))
code.end_block() code.end_block()
# Acquire # Acquire
retcode_cname = code.func.allocate_temp(PyrexTypes.c_int_type) retcode_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type)
code.putln("%s = %s;" % (retcode_cname, getbuffer % rhs_cname)) code.putln("%s = %s;" % (retcode_cname, getbuffer % rhs_cname))
code.putln('if (%s) ' % (code.unlikely("%s < 0" % retcode_cname))) code.putln('if (%s) ' % (code.unlikely("%s < 0" % retcode_cname)))
# If acquisition failed, attempt to reacquire the old buffer # If acquisition failed, attempt to reacquire the old buffer
...@@ -217,7 +205,7 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type, ...@@ -217,7 +205,7 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
# will cause the reacquisition exception to be reported, one # will cause the reacquisition exception to be reported, one
# can consider working around this later. # can consider working around this later.
code.begin_block() code.begin_block()
type, value, tb = [code.func.allocate_temp(PyrexTypes.py_object_type) type, value, tb = [code.funcstate.allocate_temp(PyrexTypes.py_object_type)
for i in range(3)] for i in range(3)]
code.putln('PyErr_Fetch(&%s, &%s, &%s);' % (type, value, tb)) code.putln('PyErr_Fetch(&%s, &%s, &%s);' % (type, value, tb))
code.put('if (%s) ' % code.unlikely("%s == -1" % (getbuffer % lhs_cname))) code.put('if (%s) ' % code.unlikely("%s == -1" % (getbuffer % lhs_cname)))
...@@ -227,13 +215,13 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type, ...@@ -227,13 +215,13 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
code.putln('} else {') code.putln('} else {')
code.putln('PyErr_Restore(%s, %s, %s);' % (type, value, tb)) code.putln('PyErr_Restore(%s, %s, %s);' % (type, value, tb))
for t in (type, value, tb): for t in (type, value, tb):
code.func.release_temp(t) code.funcstate.release_temp(t)
code.end_block() code.end_block()
# Unpack indices # Unpack indices
code.end_block() code.end_block()
put_unpack_buffer_aux_into_scope(buffer_aux, buffer_type.mode, code) put_unpack_buffer_aux_into_scope(buffer_aux, buffer_type.mode, code)
code.putln(code.error_goto_if_neg(retcode_cname, pos)) code.putln(code.error_goto_if_neg(retcode_cname, pos))
code.func.release_temp(retcode_cname) code.funcstate.release_temp(retcode_cname)
else: else:
# Our entry had no previous value, so set to None when acquisition fails. # Our entry had no previous value, so set to None when acquisition fails.
# In this case, auxiliary vars should be set up right in initialization to a zero-buffer, # In this case, auxiliary vars should be set up right in initialization to a zero-buffer,
...@@ -256,12 +244,14 @@ def put_access(entry, index_signeds, index_cnames, pos, code): ...@@ -256,12 +244,14 @@ def put_access(entry, index_signeds, index_cnames, pos, code):
body. The lookup however is delegated to a inline function that is instantiated body. The lookup however is delegated to a inline function that is instantiated
once per ndim (lookup with suboffsets tend to get quite complicated). once per ndim (lookup with suboffsets tend to get quite complicated).
""" """
code.globalstate.use_utility_code(access_utility_code)
bufaux = entry.buffer_aux bufaux = entry.buffer_aux
bufstruct = bufaux.buffer_info_var.cname bufstruct = bufaux.buffer_info_var.cname
# Check bounds and fix negative indices # Check bounds and fix negative indices
boundscheck = True boundscheck = True
nonegs = True nonegs = True
tmp_cname = code.func.allocate_temp(PyrexTypes.c_int_type) tmp_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type)
if boundscheck: if boundscheck:
code.putln("%s = -1;" % tmp_cname) code.putln("%s = -1;" % tmp_cname)
for idx, (signed, cname, shape) in enumerate(zip(index_signeds, index_cnames, for idx, (signed, cname, shape) in enumerate(zip(index_signeds, index_cnames,
...@@ -288,21 +278,33 @@ def put_access(entry, index_signeds, index_cnames, pos, code): ...@@ -288,21 +278,33 @@ def put_access(entry, index_signeds, index_cnames, pos, code):
code.putln('__Pyx_BufferIndexError(%s);' % tmp_cname) code.putln('__Pyx_BufferIndexError(%s);' % tmp_cname)
code.putln(code.error_goto(pos)) code.putln(code.error_goto(pos))
code.end_block() code.end_block()
code.func.release_temp(tmp_cname) code.funcstate.release_temp(tmp_cname)
# Create buffer lookup and return it # Create buffer lookup and return it
params = [] params = []
nd = entry.type.ndim
if entry.type.mode == 'full': if entry.type.mode == 'full':
for i, s, o in zip(index_cnames, bufaux.stridevars, bufaux.suboffsetvars): for i, s, o in zip(index_cnames, bufaux.stridevars, bufaux.suboffsetvars):
params.append(i) params.append(i)
params.append(s.cname) params.append(s.cname)
params.append(o.cname) params.append(o.cname)
funcname = "__Pyx_BufPtrFull%dd" % nd
funcgen = buf_lookup_full_code
else: else:
for i, s in zip(index_cnames, bufaux.stridevars): for i, s in zip(index_cnames, bufaux.stridevars):
params.append(i) params.append(i)
params.append(s.cname) params.append(s.cname)
ptrcode = "%s(%s.buf, %s)" % (bufaux.lookup, bufstruct, funcname = "__Pyx_BufPtrStrided%dd" % nd
", ".join(params)) funcgen = buf_lookup_strided_code
code.globalstate.use_generated_code(funcgen, name=funcname, nd=nd)
ptrcode = "%s(%s.buf, %s)" % (funcname, bufstruct, ", ".join(params))
valuecode = "*%s" % entry.type.buffer_ptr_type.cast_code(ptrcode) valuecode = "*%s" % entry.type.buffer_ptr_type.cast_code(ptrcode)
return valuecode return valuecode
...@@ -313,54 +315,35 @@ def use_empty_bufstruct_code(env, max_ndim): ...@@ -313,54 +315,35 @@ def use_empty_bufstruct_code(env, max_ndim):
Py_ssize_t __Pyx_zeros[] = {%s}; Py_ssize_t __Pyx_zeros[] = {%s};
Py_ssize_t __Pyx_minusones[] = {%s}; Py_ssize_t __Pyx_minusones[] = {%s};
""") % (", ".join(["0"] * max_ndim), ", ".join(["-1"] * max_ndim)) """) % (", ".join(["0"] * max_ndim), ", ".join(["-1"] * max_ndim))
env.use_utility_code([code, ""]) env.use_utility_code([code, ""], "empty_bufstruct_code")
def get_buf_lookup_strided(env, nd): def buf_lookup_strided_code(proto, defin, name, nd):
""" """
Generates and registers as utility a buffer lookup function for the right number Generates a buffer lookup function for the right number
of dimensions. The function gives back a void* at the right location. of dimensions. The function gives back a void* at the right location.
""" """
name = "__Pyx_BufPtrStrided_%dd" % nd # _i_ndex, _s_tride
if not env.has_utility_code(name): args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
# _i_ndex, _s_tride offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd)])
args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)]) proto.putln("#define %s(buf, %s) ((char*)buf + %s)" % (name, args, offset))
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd)])
proto = dedent("""\
#define %s(buf, %s) ((char*)buf + %s)
""") % (name, args, offset)
env.use_utility_code([proto, ""], name=name)
return name
def buf_lookup_full_code(proto, defin, name, nd):
def get_buf_lookup_full(env, nd):
""" """
Generates and registers as utility a buffer lookup function for the right number Generates a buffer lookup function for the right number
of dimensions. The function gives back a void* at the right location. of dimensions. The function gives back a void* at the right location.
""" """
name = "__Pyx_BufPtrFull_%dd" % nd # _i_ndex, _s_tride, sub_o_ffset
if not env.has_utility_code(name): args = ", ".join(["Py_ssize_t i%d, Py_ssize_t s%d, Py_ssize_t o%d" % (i, i, i) for i in range(nd)])
# _i_ndex, _s_tride, sub_o_ffset proto.putln("static INLINE void* %s(void* buf, %s);" % (name, args))
args = ", ".join(["Py_ssize_t i%d, Py_ssize_t s%d, Py_ssize_t o%d" % (i, i, i) for i in range(nd)]) defin.putln(dedent("""
proto = dedent("""\
static INLINE void* %s(void* buf, %s);
""") % (name, args)
func = dedent("""
static INLINE void* %s(void* buf, %s) { static INLINE void* %s(void* buf, %s) {
char* ptr = (char*)buf; char* ptr = (char*)buf;
""") % (name, args) + "".join([dedent("""\ """) % (name, args) + "".join([dedent("""\
ptr += s%d * i%d; ptr += s%d * i%d;
if (o%d >= 0) ptr = *((char**)ptr) + o%d; if (o%d >= 0) ptr = *((char**)ptr) + o%d;
""") % (i, i, i, i) for i in range(nd)] """) % (i, i, i, i) for i in range(nd)]
) + "\nreturn ptr;\n}" ) + "\nreturn ptr;\n}")
env.use_utility_code([proto, func], name=name)
return name
# #
...@@ -375,11 +358,11 @@ def mangle_dtype_name(dtype): ...@@ -375,11 +358,11 @@ def mangle_dtype_name(dtype):
prefix = "" prefix = ""
return prefix + dtype.declaration_code("").replace(" ", "_") return prefix + dtype.declaration_code("").replace(" ", "_")
def get_ts_check_item(dtype, env): def get_ts_check_item(dtype, writer):
# See if we can consume one (unnamed) dtype as next item # See if we can consume one (unnamed) dtype as next item
# Put native types and structs in seperate namespaces (as one could create a struct named unsigned_int...) # Put native types and structs in seperate namespaces (as one could create a struct named unsigned_int...)
name = "__Pyx_BufferTypestringCheck_item_%s" % mangle_dtype_name(dtype) name = "__Pyx_BufferTypestringCheck_item_%s" % mangle_dtype_name(dtype)
if not env.has_utility_code(name): if not writer.globalstate.has_utility_code(name):
char = dtype.typestring char = dtype.typestring
if char is not None: if char is not None:
# Can use direct comparison # Can use direct comparison
...@@ -415,7 +398,7 @@ def get_ts_check_item(dtype, env): ...@@ -415,7 +398,7 @@ def get_ts_check_item(dtype, env):
return NULL; return NULL;
} else return ts + 1; } else return ts + 1;
""", 2) """, 2)
env.use_utility_code([dedent("""\ writer.globalstate.use_utility_code([dedent("""\
static const char* %s(const char* ts); /*proto*/ static const char* %s(const char* ts); /*proto*/
""") % name, dedent(""" """) % name, dedent("""
static const char* %s(const char* ts) { static const char* %s(const char* ts) {
...@@ -425,7 +408,7 @@ def get_ts_check_item(dtype, env): ...@@ -425,7 +408,7 @@ def get_ts_check_item(dtype, env):
return name return name
def get_getbuffer_code(dtype, env): def get_getbuffer_code(dtype, code):
""" """
Generate a utility function for getting a buffer for the given dtype. Generate a utility function for getting a buffer for the given dtype.
The function will: The function will:
...@@ -436,9 +419,9 @@ def get_getbuffer_code(dtype, env): ...@@ -436,9 +419,9 @@ def get_getbuffer_code(dtype, env):
""" """
name = "__Pyx_GetBuffer_%s" % mangle_dtype_name(dtype) name = "__Pyx_GetBuffer_%s" % mangle_dtype_name(dtype)
if not env.has_utility_code(name): if not code.globalstate.has_utility_code(name):
env.use_utility_code(acquire_utility_code) code.globalstate.use_utility_code(acquire_utility_code)
itemchecker = get_ts_check_item(dtype, env) itemchecker = get_ts_check_item(dtype, code)
utilcode = [dedent(""" utilcode = [dedent("""
static int %s(PyObject* obj, Py_buffer* buf, int flags, int nd); /*proto*/ static int %s(PyObject* obj, Py_buffer* buf, int flags, int nd); /*proto*/
""") % name, dedent(""" """) % name, dedent("""
...@@ -473,72 +456,21 @@ def get_getbuffer_code(dtype, env): ...@@ -473,72 +456,21 @@ def get_getbuffer_code(dtype, env):
__Pyx_ZeroBuffer(buf); __Pyx_ZeroBuffer(buf);
return -1; return -1;
}""") % locals()] }""") % locals()]
env.use_utility_code(utilcode, name) code.globalstate.use_utility_code(utilcode, name)
return name return name
def buffer_type_checker(dtype, env): def buffer_type_checker(dtype, code):
# Creates a type checker function for the given type. # Creates a type checker function for the given type.
if dtype.is_struct_or_union: if dtype.is_struct_or_union:
assert False assert False
elif dtype.is_int or dtype.is_float: elif dtype.is_int or dtype.is_float:
# This includes simple typedef-ed types # This includes simple typedef-ed types
funcname = get_getbuffer_code(dtype, env) funcname = get_getbuffer_code(dtype, code)
else: else:
assert False assert False
return funcname return funcname
def use_py2_buffer_functions(env): def use_py2_buffer_functions(env):
# will be refactored
try:
env.entries[u'numpy']
env.use_utility_code(["","""
static int numpy_getbuffer(PyObject *obj, Py_buffer *view, int flags) {
/* This function is always called after a type-check; safe to cast */
PyArrayObject *arr = (PyArrayObject*)obj;
PyArray_Descr *type = (PyArray_Descr*)arr->descr;
int typenum = PyArray_TYPE(obj);
if (!PyTypeNum_ISNUMBER(typenum)) {
PyErr_Format(PyExc_TypeError, "Only numeric NumPy types currently supported.");
return -1;
}
/*
NumPy format codes doesn't completely match buffer codes;
seems safest to retranslate.
01234567890123456789012345*/
const char* base_codes = "?bBhHiIlLqQfdgfdgO";
char* format = (char*)malloc(4);
char* fp = format;
*fp++ = type->byteorder;
if (PyTypeNum_ISCOMPLEX(typenum)) *fp++ = 'Z';
*fp++ = base_codes[typenum];
*fp = 0;
view->buf = arr->data;
view->readonly = !PyArray_ISWRITEABLE(obj);
view->ndim = PyArray_NDIM(arr);
view->strides = PyArray_STRIDES(arr);
view->shape = PyArray_DIMS(arr);
view->suboffsets = NULL;
view->format = format;
view->itemsize = type->elsize;
view->internal = 0;
return 0;
}
static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) {
free((char*)view->format);
view->format = NULL;
}
"""])
except KeyError:
pass
codename = "PyObject_GetBuffer" # just a representative unique key codename = "PyObject_GetBuffer" # just a representative unique key
# Search all types for __getbuffer__ overloads # Search all types for __getbuffer__ overloads
...@@ -562,6 +494,7 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) { ...@@ -562,6 +494,7 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) {
try: try:
ndarrtype = env.entries[u'numpy'].as_module.entries['ndarray'].type ndarrtype = env.entries[u'numpy'].as_module.entries['ndarray'].type
types.append((ndarrtype.typeptr_cname, "numpy_getbuffer", "numpy_releasebuffer")) types.append((ndarrtype.typeptr_cname, "numpy_getbuffer", "numpy_releasebuffer"))
env.use_utility_code(numpy_code)
except KeyError: except KeyError:
pass pass
...@@ -602,7 +535,7 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) { ...@@ -602,7 +535,7 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) {
static int PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags); static int PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags);
static void PyObject_ReleaseBuffer(PyObject *obj, Py_buffer *view); static void PyObject_ReleaseBuffer(PyObject *obj, Py_buffer *view);
#endif #endif
""") ,code], codename) """), code], codename)
# #
# Static utility code # Static utility code
...@@ -690,3 +623,53 @@ static void __Pyx_RaiseBufferFallbackError(void) { ...@@ -690,3 +623,53 @@ static void __Pyx_RaiseBufferFallbackError(void) {
} }
"""] """]
numpy_code = ["""
static int numpy_getbuffer(PyObject *obj, Py_buffer *view, int flags);
static void numpy_releasebuffer(PyObject *obj, Py_buffer *view);
""","""
static int numpy_getbuffer(PyObject *obj, Py_buffer *view, int flags) {
/* This function is always called after a type-check; safe to cast */
PyArrayObject *arr = (PyArrayObject*)obj;
PyArray_Descr *type = (PyArray_Descr*)arr->descr;
int typenum = PyArray_TYPE(obj);
if (!PyTypeNum_ISNUMBER(typenum)) {
PyErr_Format(PyExc_TypeError, "Only numeric NumPy types currently supported.");
return -1;
}
/*
NumPy format codes doesn't completely match buffer codes;
seems safest to retranslate.
01234567890123456789012345*/
const char* base_codes = "?bBhHiIlLqQfdgfdgO";
char* format = (char*)malloc(4);
char* fp = format;
*fp++ = type->byteorder;
if (PyTypeNum_ISCOMPLEX(typenum)) *fp++ = 'Z';
*fp++ = base_codes[typenum];
*fp = 0;
view->buf = arr->data;
view->readonly = !PyArray_ISWRITEABLE(obj);
view->ndim = PyArray_NDIM(arr);
view->strides = PyArray_STRIDES(arr);
view->shape = PyArray_DIMS(arr);
view->suboffsets = NULL;
view->format = format;
view->itemsize = type->elsize;
view->internal = 0;
return 0;
}
static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) {
free((char*)view->format);
view->format = NULL;
}
"""]
...@@ -12,7 +12,16 @@ from Scanning import SourceDescriptor ...@@ -12,7 +12,16 @@ from Scanning import SourceDescriptor
from Cython.StringIOTree import StringIOTree from Cython.StringIOTree import StringIOTree
from sets import Set as set from sets import Set as set
class FunctionContext(object): class FunctionState(object):
# return_label string function return point label
# error_label string error catch point label
# continue_label string loop continue point label
# break_label string loop break point label
# return_from_error_cleanup_label string
# label_counter integer counter for naming labels
# in_try_finally boolean inside try of try...finally
# exc_vars (string * 3) exception variables for reraise, or None
# Not used for now, perhaps later # Not used for now, perhaps later
def __init__(self, names_taken=set()): def __init__(self, names_taken=set()):
self.names_taken = names_taken self.names_taken = names_taken
...@@ -25,6 +34,9 @@ class FunctionContext(object): ...@@ -25,6 +34,9 @@ class FunctionContext(object):
self.continue_label = None self.continue_label = None
self.break_label = None self.break_label = None
self.in_try_finally = 0
self.exc_vars = None
self.temps_allocated = [] # of (name, type) self.temps_allocated = [] # of (name, type)
self.temps_free = {} # type -> list of free vars self.temps_free = {} # type -> list of free vars
self.temps_used_type = {} # name -> type self.temps_used_type = {} # name -> type
...@@ -115,14 +127,104 @@ class FunctionContext(object): ...@@ -115,14 +127,104 @@ class FunctionContext(object):
freelist = self.temps_free.get(type) freelist = self.temps_free.get(type)
if freelist is None: if freelist is None:
freelist = [] freelist = []
self.temps_free[type] = freelist self.temps_free[type] = freelist
freelist.append(name) freelist.append(name)
class GlobalState(object):
# filename_table {string : int} for finding filename table indexes
# filename_list [string] filenames in filename table order
# input_file_contents dict contents (=list of lines) of any file that was used as input
# 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
def __init__(self):
self.filename_table = {}
self.filename_list = []
self.input_file_contents = {}
self.used_utility_code = set()
def lookup_filename(self, filename):
try:
index = self.filename_table[filename]
except KeyError:
index = len(self.filename_list)
self.filename_list.append(filename)
self.filename_table[filename] = index
return index
def commented_file_contents(self, source_desc):
try:
return self.input_file_contents[source_desc]
except KeyError:
F = [u' * ' + line.rstrip().replace(
u'*/', u'*[inserted by cython to avoid comment closer]/'
).encode('ASCII', 'replace') # + Py2 auto-decode to unicode
for line in source_desc.get_lines()]
self.input_file_contents[source_desc] = F
return F
def use_utility_code(self, codetup, name=None):
"""
Adds the given utility code to the C file if needed.
codetup should unpack into one prototype code part and one
definition code part, both strings inserted directly in C.
If name is provided, it is used as an identifier to avoid inserting
code twice. Otherwise, id(codetup) is used as such an identifier.
"""
if name is None: name = id(codetup)
if self.check_utility_code_needed_and_register(name):
proto, _def = codetup
self.utilprotowriter.put(proto)
self.utildefwriter.put(_def)
def has_utility_code(self, name):
return name in self.used_utility_code
def use_generated_code(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):
writer.insert(self.utildefwriter)
def funccontext_property(name): def funccontext_property(name):
def get(self): def get(self):
return getattr(self.func, name) return getattr(self.funcstate, name)
def set(self, value): def set(self, value):
setattr(self.func, name, value) setattr(self.funcstate, name, value)
return property(get, set) return property(get, set)
class CCodeWriter(object): class CCodeWriter(object):
...@@ -148,52 +250,39 @@ class CCodeWriter(object): ...@@ -148,52 +250,39 @@ class CCodeWriter(object):
# level int indentation level # level int indentation level
# bol bool beginning of line? # bol bool beginning of line?
# marker string comment to emit before next line # marker string comment to emit before next line
# return_label string function return point label # funcstate FunctionState contains state local to a C function used for code
# error_label string error catch point label # generation (labels and temps state etc.)
# continue_label string loop continue point label # globalstate GlobalState contains state global for a C file (input file info,
# break_label string loop break point label # utility code, declared constants etc.)
# return_from_error_cleanup_label string
# label_counter integer counter for naming labels
# in_try_finally boolean inside try of try...finally
# filename_table {string : int} for finding filename table indexes
# filename_list [string] filenames in filename table order
# exc_vars (string * 3) exception variables for reraise, or None
# input_file_contents dict contents (=list of lines) of any file that was used as input
# to create this output C code. This is
# used to annotate the comments.
# func FunctionContext contains labels and temps context info
in_try_finally = 0 def __init__(self, create_from=None, buffer=None, copy_formatting=False):
def __init__(self, create_from=None, buffer=None):
if buffer is None: buffer = StringIOTree() if buffer is None: buffer = StringIOTree()
self.buffer = buffer self.buffer = buffer
self.marker = None self.marker = None
self.last_marker_line = 0 self.last_marker_line = 0
self.func = None
self.funcstate = None
self.level = 0
self.bol = 1
if create_from is None: if create_from is None:
# Root CCodeWriter # Root CCodeWriter
self.level = 0 self.globalstate = GlobalState()
self.bol = 1 # These needs to be constructed after all state is set, as
self.filename_table = {} # the construction copies over the state
self.filename_list = [] self.globalstate.utilprotowriter = self.new_writer()
self.exc_vars = None self.globalstate.utildefwriter = self.new_writer()
self.input_file_contents = {}
else: else:
# Use same global state
self.globalstate = create_from.globalstate
# Clone formatting state # Clone formatting state
c = create_from if copy_formatting:
self.level = c.level self.level = create_from.level
self.bol = c.bol self.bol = create_from.bol
# Note: NOT copying but sharing instance
self.filename_table = c.filename_table def create_new(self, create_from, buffer, copy_formatting):
self.filename_list = []
self.input_file_contents = c.input_file_contents
# Leave other state alone
def create_new(self, create_from, buffer):
# polymorphic constructor -- very slightly more versatile # polymorphic constructor -- very slightly more versatile
# than using __class__ # than using __class__
return CCodeWriter(create_from, buffer) return CCodeWriter(create_from, buffer, copy_formatting)
def copyto(self, f): def copyto(self, f):
self.buffer.copyto(f) self.buffer.copyto(f)
...@@ -205,9 +294,25 @@ class CCodeWriter(object): ...@@ -205,9 +294,25 @@ class CCodeWriter(object):
self.buffer.write(s) self.buffer.write(s)
def insertion_point(self): def insertion_point(self):
other = self.create_new(create_from=self, buffer=self.buffer.insertion_point()) other = self.create_new(create_from=self, buffer=self.buffer.insertion_point(), copy_formatting=True)
return other return other
def new_writer(self):
"""
Creates a new CCodeWriter connected to the same global state, which
can later be inserted using insert.
"""
return CCodeWriter(create_from=self)
def insert(self, writer):
"""
Inserts the contents of another code writer (created with
the same global state) in the current location.
It is ok to write to the inserted writer also after insertion.
"""
assert writer.globalstate is self.globalstate
self.buffer.insert(writer.buffer)
# Properties delegated to function scope # Properties delegated to function scope
label_counter = funccontext_property("label_counter") label_counter = funccontext_property("label_counter")
...@@ -216,26 +321,26 @@ class CCodeWriter(object): ...@@ -216,26 +321,26 @@ class CCodeWriter(object):
labels_used = funccontext_property("labels_used") labels_used = funccontext_property("labels_used")
continue_label = funccontext_property("continue_label") continue_label = funccontext_property("continue_label")
break_label = funccontext_property("break_label") break_label = funccontext_property("break_label")
return_from_error_cleanup_label = funccontext_property("return_from_error_cleanup_label")
# Functions delegated to function scope # Functions delegated to function scope
def new_label(self): return self.func.new_label() def new_label(self): return self.funcstate.new_label()
def new_error_label(self): return self.func.new_error_label() def new_error_label(self): return self.funcstate.new_error_label()
def get_loop_labels(self): return self.func.get_loop_labels() def get_loop_labels(self): return self.funcstate.get_loop_labels()
def set_loop_labels(self, labels): return self.func.set_loop_labels(labels) def set_loop_labels(self, labels): return self.funcstate.set_loop_labels(labels)
def new_loop_labels(self): return self.func.new_loop_labels() def new_loop_labels(self): return self.funcstate.new_loop_labels()
def get_all_labels(self): return self.func.get_all_labels() def get_all_labels(self): return self.funcstate.get_all_labels()
def set_all_labels(self, labels): return self.func.set_all_labels(labels) def set_all_labels(self, labels): return self.funcstate.set_all_labels(labels)
def all_new_labels(self): return self.func.all_new_labels() def all_new_labels(self): return self.funcstate.all_new_labels()
def use_label(self, lbl): return self.func.use_label(lbl) def use_label(self, lbl): return self.funcstate.use_label(lbl)
def label_used(self, lbl): return self.func.label_used(lbl) def label_used(self, lbl): return self.funcstate.label_used(lbl)
def enter_cfunc_scope(self): def enter_cfunc_scope(self):
self.func = FunctionContext() self.funcstate = FunctionState()
def exit_cfunc_scope(self): def exit_cfunc_scope(self):
self.func = None self.funcstate = None
def putln(self, code = ""): def putln(self, code = ""):
if self.marker and self.bol: if self.marker and self.bol:
...@@ -287,17 +392,6 @@ class CCodeWriter(object): ...@@ -287,17 +392,6 @@ class CCodeWriter(object):
def get_py_version_hex(self, pyversion): def get_py_version_hex(self, pyversion):
return "0x%02X%02X%02X%02X" % (tuple(pyversion) + (0,0,0,0))[:4] return "0x%02X%02X%02X%02X" % (tuple(pyversion) + (0,0,0,0))[:4]
def commented_file_contents(self, source_desc):
try:
return self.input_file_contents[source_desc]
except KeyError:
F = [u' * ' + line.rstrip().replace(
u'*/', u'*[inserted by cython to avoid comment closer]/'
).encode('ASCII', 'replace') # + Py2 auto-decode to unicode
for line in source_desc.get_lines()]
self.input_file_contents[source_desc] = F
return F
def mark_pos(self, pos): def mark_pos(self, pos):
if pos is None: if pos is None:
return return
...@@ -305,7 +399,7 @@ class CCodeWriter(object): ...@@ -305,7 +399,7 @@ class CCodeWriter(object):
if self.last_marker_line == line: if self.last_marker_line == line:
return return
assert isinstance(source_desc, SourceDescriptor) assert isinstance(source_desc, SourceDescriptor)
contents = self.commented_file_contents(source_desc) contents = self.globalstate.commented_file_contents(source_desc)
lines = contents[max(0,line-3):line] # line numbers start at 1 lines = contents[max(0,line-3):line] # line numbers start at 1
lines[-1] += u' # <<<<<<<<<<<<<<' lines[-1] += u' # <<<<<<<<<<<<<<'
...@@ -317,11 +411,11 @@ class CCodeWriter(object): ...@@ -317,11 +411,11 @@ class CCodeWriter(object):
def put_label(self, lbl): def put_label(self, lbl):
if lbl in self.func.labels_used: if lbl in self.funcstate.labels_used:
self.putln("%s:;" % lbl) self.putln("%s:;" % lbl)
def put_goto(self, lbl): def put_goto(self, lbl):
self.func.use_label(lbl) self.funcstate.use_label(lbl)
self.putln("goto %s;" % lbl) self.putln("goto %s;" % lbl)
def put_var_declarations(self, entries, static = 0, dll_linkage = None, def put_var_declarations(self, entries, static = 0, dll_linkage = None,
...@@ -481,8 +575,8 @@ class CCodeWriter(object): ...@@ -481,8 +575,8 @@ class CCodeWriter(object):
return cond return cond
def error_goto(self, pos): def error_goto(self, pos):
lbl = self.func.error_label lbl = self.funcstate.error_label
self.func.use_label(lbl) self.funcstate.use_label(lbl)
if Options.c_line_in_traceback: if Options.c_line_in_traceback:
cinfo = " %s = %s;" % (Naming.clineno_cname, Naming.line_c_macro) cinfo = " %s = %s;" % (Naming.clineno_cname, Naming.line_c_macro)
else: else:
...@@ -509,13 +603,7 @@ class CCodeWriter(object): ...@@ -509,13 +603,7 @@ class CCodeWriter(object):
return self.error_goto_if("PyErr_Occurred()", pos) return self.error_goto_if("PyErr_Occurred()", pos)
def lookup_filename(self, filename): def lookup_filename(self, filename):
try: return self.globalstate.lookup_filename(filename)
index = self.filename_table[filename]
except KeyError:
index = len(self.filename_list)
self.filename_list.append(filename)
self.filename_table[filename] = index
return index
class PyrexCodeWriter: class PyrexCodeWriter:
......
...@@ -1065,7 +1065,7 @@ class NameNode(AtomicExprNode): ...@@ -1065,7 +1065,7 @@ class NameNode(AtomicExprNode):
rhs.generate_post_assignment_code(code) rhs.generate_post_assignment_code(code)
def generate_acquire_buffer(self, rhs, code): def generate_acquire_buffer(self, rhs, code):
rhstmp = code.func.allocate_temp(self.entry.type) rhstmp = code.funcstate.allocate_temp(self.entry.type)
buffer_aux = self.entry.buffer_aux buffer_aux = self.entry.buffer_aux
bufstruct = buffer_aux.buffer_info_var.cname bufstruct = buffer_aux.buffer_info_var.cname
code.putln('%s = %s;' % (rhstmp, rhs.result_as(self.ctype()))) code.putln('%s = %s;' % (rhstmp, rhs.result_as(self.ctype())))
...@@ -1075,7 +1075,7 @@ class NameNode(AtomicExprNode): ...@@ -1075,7 +1075,7 @@ class NameNode(AtomicExprNode):
is_initialized=not self.skip_assignment_decref, is_initialized=not self.skip_assignment_decref,
pos=self.pos, code=code) pos=self.pos, code=code)
code.putln("%s = 0;" % rhstmp) code.putln("%s = 0;" % rhstmp)
code.func.release_temp(rhstmp) code.funcstate.release_temp(rhstmp)
def generate_deletion_code(self, code): def generate_deletion_code(self, code):
if self.entry is None: if self.entry is None:
...@@ -1520,7 +1520,7 @@ class IndexNode(ExprNode): ...@@ -1520,7 +1520,7 @@ class IndexNode(ExprNode):
def buffer_access_code(self, code): def buffer_access_code(self, code):
# Assign indices to temps # Assign indices to temps
index_temps = [code.func.allocate_temp(i.type) for i in self.indices] index_temps = [code.funcstate.allocate_temp(i.type) for i in self.indices]
for temp, index in zip(index_temps, self.indices): for temp, index in zip(index_temps, self.indices):
code.putln("%s = %s;" % (temp, index.result_code)) code.putln("%s = %s;" % (temp, index.result_code))
# Generate buffer access code using these temps # Generate buffer access code using these temps
......
...@@ -529,8 +529,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -529,8 +529,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def generate_filename_table(self, code): def generate_filename_table(self, code):
code.putln("") code.putln("")
code.putln("static const char *%s[] = {" % Naming.filenames_cname) code.putln("static const char *%s[] = {" % Naming.filenames_cname)
if code.filename_list: if code.globalstate.filename_list:
for source_desc in code.filename_list: for source_desc in code.globalstate.filename_list:
filename = os.path.basename(source_desc.get_filenametable_entry()) filename = os.path.basename(source_desc.get_filenametable_entry())
escaped_filename = filename.replace("\\", "\\\\").replace('"', r'\"') escaped_filename = filename.replace("\\", "\\\\").replace('"', r'\"')
code.putln('"%s",' % code.putln('"%s",' %
...@@ -1610,7 +1610,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1610,7 +1610,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln('}') code.putln('}')
tempdecl_code.put_var_declarations(env.temp_entries) tempdecl_code.put_var_declarations(env.temp_entries)
tempdecl_code.put_temp_declarations(code.func) tempdecl_code.put_temp_declarations(code.funcstate)
code.exit_cfunc_scope() code.exit_cfunc_scope()
...@@ -1950,6 +1950,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1950,6 +1950,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type.typeptr_cname, type.typeobj_cname)) type.typeptr_cname, type.typeobj_cname))
def generate_utility_functions(self, env, code, h_code): 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("")
code.putln("/* Runtime support code */") code.putln("/* Runtime support code */")
code.putln("") code.putln("")
...@@ -1957,9 +1961,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1957,9 +1961,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("%s = %s;" % code.putln("%s = %s;" %
(Naming.filetable_cname, Naming.filenames_cname)) (Naming.filetable_cname, Naming.filenames_cname))
code.putln("}") code.putln("}")
for utility_code in env.utility_code_used: code.globalstate.put_utility_code_defs(code)
h_code.put(utility_code[0])
code.put(utility_code[1])
code.put(PyrexTypes.type_conversion_functions) code.put(PyrexTypes.type_conversion_functions)
code.putln("") code.putln("")
......
...@@ -969,7 +969,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -969,7 +969,7 @@ class FuncDefNode(StatNode, BlockNode):
code.putln("}") code.putln("}")
# ----- Go back and insert temp variable declarations # ----- Go back and insert temp variable declarations
tempvardecl_code.put_var_declarations(lenv.temp_entries) tempvardecl_code.put_var_declarations(lenv.temp_entries)
tempvardecl_code.put_temp_declarations(code.func) tempvardecl_code.put_temp_declarations(code.funcstate)
# ----- Python version # ----- Python version
code.exit_cfunc_scope() code.exit_cfunc_scope()
if self.py_func: if self.py_func:
...@@ -2634,7 +2634,7 @@ class ContinueStatNode(StatNode): ...@@ -2634,7 +2634,7 @@ class ContinueStatNode(StatNode):
pass pass
def generate_execution_code(self, code): def generate_execution_code(self, code):
if code.in_try_finally: if code.funcstate.in_try_finally:
error(self.pos, "continue statement inside try of try...finally") error(self.pos, "continue statement inside try of try...finally")
elif not code.continue_label: elif not code.continue_label:
error(self.pos, "continue statement not inside loop") error(self.pos, "continue statement not inside loop")
...@@ -2797,7 +2797,7 @@ class ReraiseStatNode(StatNode): ...@@ -2797,7 +2797,7 @@ class ReraiseStatNode(StatNode):
gil_message = "Raising exception" gil_message = "Raising exception"
def generate_execution_code(self, code): def generate_execution_code(self, code):
vars = code.exc_vars vars = code.funcstate.exc_vars
if vars: if vars:
code.putln("__Pyx_Raise(%s, %s, %s);" % tuple(vars)) code.putln("__Pyx_Raise(%s, %s, %s);" % tuple(vars))
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
...@@ -3478,10 +3478,10 @@ class ExceptClauseNode(Node): ...@@ -3478,10 +3478,10 @@ class ExceptClauseNode(Node):
self.excinfo_tuple.generate_evaluation_code(code) self.excinfo_tuple.generate_evaluation_code(code)
self.excinfo_target.generate_assignment_code(self.excinfo_tuple, code) self.excinfo_target.generate_assignment_code(self.excinfo_tuple, code)
old_exc_vars = code.exc_vars old_exc_vars = code.funcstate.exc_vars
code.exc_vars = self.exc_vars code.funcstate.exc_vars = self.exc_vars
self.body.generate_execution_code(code) self.body.generate_execution_code(code)
code.exc_vars = old_exc_vars code.funcstate.exc_vars = old_exc_vars
for var in self.exc_vars: for var in self.exc_vars:
code.putln("Py_DECREF(%s); %s = 0;" % (var, var)) code.putln("Py_DECREF(%s); %s = 0;" % (var, var))
code.put_goto(end_label) code.put_goto(end_label)
...@@ -3556,11 +3556,11 @@ class TryFinallyStatNode(StatNode): ...@@ -3556,11 +3556,11 @@ class TryFinallyStatNode(StatNode):
code.putln( code.putln(
"/*try:*/ {") "/*try:*/ {")
if self.disallow_continue_in_try_finally: if self.disallow_continue_in_try_finally:
was_in_try_finally = code.in_try_finally was_in_try_finally = code.funcstate.in_try_finally
code.in_try_finally = 1 code.funcstate.in_try_finally = 1
self.body.generate_execution_code(code) self.body.generate_execution_code(code)
if self.disallow_continue_in_try_finally: if self.disallow_continue_in_try_finally:
code.in_try_finally = was_in_try_finally code.funcstate.in_try_finally = was_in_try_finally
code.putln( code.putln(
"}") "}")
code.putln( code.putln(
......
...@@ -23,11 +23,12 @@ nice_identifier = re.compile('^[a-zA-Z0-0_]+$').match ...@@ -23,11 +23,12 @@ nice_identifier = re.compile('^[a-zA-Z0-0_]+$').match
class BufferAux: class BufferAux:
writable_needed = False writable_needed = False
def __init__(self, buffer_info_var, stridevars, shapevars, tschecker): def __init__(self, buffer_info_var, stridevars, shapevars,
suboffsetvars):
self.buffer_info_var = buffer_info_var self.buffer_info_var = buffer_info_var
self.stridevars = stridevars self.stridevars = stridevars
self.shapevars = shapevars self.shapevars = shapevars
self.tschecker = tschecker self.suboffsetvars = suboffsetvars
def __repr__(self): def __repr__(self):
return "<BufferAux %r>" % self.__dict__ return "<BufferAux %r>" % self.__dict__
...@@ -620,9 +621,6 @@ class Scope: ...@@ -620,9 +621,6 @@ class Scope:
def use_utility_code(self, new_code, name=None): def use_utility_code(self, new_code, name=None):
self.global_scope().use_utility_code(new_code, name) self.global_scope().use_utility_code(new_code, name)
def has_utility_code(self, name):
return self.global_scope().has_utility_code(name)
def generate_library_function_declarations(self, code): def generate_library_function_declarations(self, code):
# Generate extern decls for C library funcs used. # Generate extern decls for C library funcs used.
...@@ -742,8 +740,7 @@ class ModuleScope(Scope): ...@@ -742,8 +740,7 @@ class ModuleScope(Scope):
# doc string Module doc string # doc string Module doc string
# doc_cname string C name of module doc string # doc_cname string C name of module doc string
# const_counter integer Counter for naming constants # const_counter integer Counter for naming constants
# utility_code_used [string] Utility code to be included # utility_code_list [((string, string), string)] Queuing utility codes for forwarding to Code.py
# utility_code_names set(string) (Optional) names for named (often generated) utility code
# default_entries [Entry] Function argument default entries # default_entries [Entry] Function argument default entries
# python_include_files [string] Standard Python headers to be included # python_include_files [string] Standard Python headers to be included
# include_files [string] Other C headers to be included # include_files [string] Other C headers to be included
...@@ -777,8 +774,7 @@ class ModuleScope(Scope): ...@@ -777,8 +774,7 @@ class ModuleScope(Scope):
self.doc = "" self.doc = ""
self.doc_cname = Naming.moddoc_cname self.doc_cname = Naming.moddoc_cname
self.const_counter = 1 self.const_counter = 1
self.utility_code_used = [] self.utility_code_list = []
self.utility_code_names = set()
self.default_entries = [] self.default_entries = []
self.module_entries = {} self.module_entries = {}
self.python_include_files = ["Python.h", "structmember.h"] self.python_include_files = ["Python.h", "structmember.h"]
...@@ -938,25 +934,8 @@ class ModuleScope(Scope): ...@@ -938,25 +934,8 @@ class ModuleScope(Scope):
return "%s%s%d" % (Naming.const_prefix, prefix, n) return "%s%s%d" % (Naming.const_prefix, prefix, n)
def use_utility_code(self, new_code, name=None): def use_utility_code(self, new_code, name=None):
# Add string to list of utility code to be included, self.utility_code_list.append((new_code, name))
# if not already there (tested using the provided name,
# or 'is' if name=None -- if the utility code is dynamically
# generated, use the name, otherwise it is not needed).
if name is not None:
if name in self.utility_code_names:
return
for old_code in self.utility_code_used:
if old_code is new_code:
return
self.utility_code_used.append(new_code)
self.utility_code_names.add(name)
def has_utility_code(self, name):
# Checks if utility code (that is registered by name) has
# previously been registered. This is useful if the utility code
# is dynamically generated to avoid re-generation.
return name in self.utility_code_names
def declare_c_class(self, name, pos, defining = 0, implementing = 0, def declare_c_class(self, name, pos, defining = 0, implementing = 0,
module_name = None, base_type = None, objstruct_cname = None, module_name = None, base_type = None, objstruct_cname = None,
typeobj_cname = None, visibility = 'private', typedef_flag = 0, api = 0): typeobj_cname = None, visibility = 'private', typedef_flag = 0, api = 0):
......
...@@ -7,8 +7,7 @@ class StringIOTree(object): ...@@ -7,8 +7,7 @@ class StringIOTree(object):
def __init__(self, stream=None): def __init__(self, stream=None):
self.prepended_children = [] self.prepended_children = []
if stream is None: stream = StringIO() self.stream = stream # if set to None, it will be constructed on first write
self.stream = stream
def getvalue(self): def getvalue(self):
return ("".join([x.getvalue() for x in self.prepended_children]) + return ("".join([x.getvalue() for x in self.prepended_children]) +
...@@ -19,20 +18,44 @@ class StringIOTree(object): ...@@ -19,20 +18,44 @@ class StringIOTree(object):
needs to happen.""" needs to happen."""
for child in self.prepended_children: for child in self.prepended_children:
child.copyto(target) child.copyto(target)
target.write(self.stream.getvalue()) if self.stream:
target.write(self.stream.getvalue())
def write(self, what): def write(self, what):
if not self.stream:
self.stream = StringIO()
self.stream.write(what) self.stream.write(what)
def commit(self):
# Save what we have written until now so that the buffer
# itself is empty -- this makes it ready for insertion
if self.stream:
self.prepended_children.append(StringIOTree(self.stream))
self.stream = None
def insert(self, iotree):
"""
Insert a StringIOTree (and all of its contents) at this location.
Further writing to self appears after what is inserted.
"""
self.commit()
self.prepended_children.append(iotree)
def insertion_point(self): def insertion_point(self):
"""
Returns a new StringIOTree, which is left behind at the current position
(it what is written to the result will appear right before whatever is
next written to self).
Calling getvalue() or copyto() on the result will only return the
contents written to it.
"""
# Save what we have written until now # Save what we have written until now
# (would it be more efficient to check with len(self.stream.getvalue())? # This is so that getvalue on the result doesn't include it.
# leaving it out for now) self.commit()
self.prepended_children.append(StringIOTree(self.stream))
# Construct the new forked object to return # Construct the new forked object to return
other = StringIOTree() other = StringIOTree()
self.prepended_children.append(other) self.prepended_children.append(other)
self.stream = StringIO()
return other return other
__doc__ = r""" __doc__ = r"""
...@@ -65,12 +88,16 @@ beta ...@@ -65,12 +88,16 @@ beta
gamma gamma
<BLANKLINE> <BLANKLINE>
>>> i = StringIOTree()
>>> d.insert(i)
>>> i.write('inserted\n')
>>> out = StringIO() >>> out = StringIO()
>>> a.copyto(out) >>> a.copyto(out)
>>> print out.getvalue() >>> print out.getvalue()
first first
second second
alpha alpha
inserted
beta beta
gamma gamma
third third
......
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