Commit efc2e33d authored by Mark Florisson's avatar Mark Florisson

initializedcheck directive + uninitialized memslice checking + memoryview...

initializedcheck directive + uninitialized memslice checking + memoryview indexing object<->dtype conversion
parent 71e92014
...@@ -1422,6 +1422,7 @@ class NameNode(AtomicExprNode): ...@@ -1422,6 +1422,7 @@ class NameNode(AtomicExprNode):
self.result_ctype = py_object_type self.result_ctype = py_object_type
def analyse_types(self, env): def analyse_types(self, env):
self.initialized_check = env.directives['initializedcheck']
if self.entry is None: if self.entry is None:
self.entry = env.lookup(self.name) self.entry = env.lookup(self.name)
if not self.entry: if not self.entry:
...@@ -1455,15 +1456,21 @@ class NameNode(AtomicExprNode): ...@@ -1455,15 +1456,21 @@ class NameNode(AtomicExprNode):
#print "Entry:", self.entry.__dict__ ### #print "Entry:", self.entry.__dict__ ###
self.analyse_entry(env) self.analyse_entry(env)
entry = self.entry entry = self.entry
if entry.is_declared_generic: if entry.is_declared_generic:
self.result_ctype = py_object_type self.result_ctype = py_object_type
if entry.is_pyglobal or entry.is_builtin: if entry.is_pyglobal or entry.is_builtin:
if entry.is_builtin and entry.is_const: if entry.is_builtin and entry.is_const:
self.is_temp = 0 self.is_temp = 0
else: else:
self.is_temp = 1 self.is_temp = 1
env.use_utility_code(get_name_interned_utility_code) env.use_utility_code(get_name_interned_utility_code)
self.is_used_as_rvalue = 1 self.is_used_as_rvalue = 1
elif entry.type.is_memoryviewslice:
self.is_temp = False
self.is_used_as_rvalue = True
def nogil_check(self, env): def nogil_check(self, env):
self.nogil = True self.nogil = True
...@@ -1474,6 +1481,10 @@ class NameNode(AtomicExprNode): ...@@ -1474,6 +1481,10 @@ class NameNode(AtomicExprNode):
self.gil_error() self.gil_error()
elif entry.is_pyglobal: elif entry.is_pyglobal:
self.gil_error() self.gil_error()
elif self.entry.type.is_memoryviewslice:
if self.cf_is_null or self.cf_maybe_null:
import MemoryView
MemoryView.err_if_nogil_initialized_check(self.pos, env)
gil_message = "Accessing Python global or builtin" gil_message = "Accessing Python global or builtin"
...@@ -1614,9 +1625,13 @@ class NameNode(AtomicExprNode): ...@@ -1614,9 +1625,13 @@ class NameNode(AtomicExprNode):
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
elif entry.is_local or entry.in_closure or entry.from_closure: elif entry.is_local or entry.in_closure or entry.from_closure:
if entry.type.check_for_null_code(entry.cname): # Raise UnboundLocalError for objects and memoryviewslices
if (self.cf_maybe_null or self.cf_is_null) \ raise_unbound = (
and not self.allow_null: (self.cf_maybe_null or self.cf_is_null) and not self.allow_null)
null_code = entry.type.check_for_null_code(entry.cname)
memslice_check = entry.type.is_memoryviewslice and self.initialized_check
if null_code and raise_unbound and (entry.type.is_pyobject or memslice_check):
code.put_error_if_unbound(self.pos, entry) code.put_error_if_unbound(self.pos, entry)
def generate_assignment_code(self, rhs, code): def generate_assignment_code(self, rhs, code):
...@@ -2401,18 +2416,23 @@ class IndexNode(ExprNode): ...@@ -2401,18 +2416,23 @@ class IndexNode(ExprNode):
if not x.type.is_int: if not x.type.is_int:
buffer_access = False buffer_access = False
if buffer_access: if buffer_access and not self.base.type.is_memoryviewslice:
assert hasattr(self.base, "entry") # Must be a NameNode-like node assert hasattr(self.base, "entry") # Must be a NameNode-like node
# On cloning, indices is cloned. Otherwise, unpack index into indices # On cloning, indices is cloned. Otherwise, unpack index into indices
assert not (buffer_access and isinstance(self.index, CloneNode)) assert not (buffer_access and isinstance(self.index, CloneNode))
self.nogil = env.nogil
if buffer_access: if buffer_access:
if self.base.type.is_memoryviewslice and not self.base.is_name:
self.base = self.base.coerce_to_temp(env)
self.indices = indices self.indices = indices
self.index = None self.index = None
self.type = self.base.type.dtype self.type = self.base.type.dtype
self.is_buffer_access = True self.is_buffer_access = True
self.buffer_type = self.base.entry.type self.buffer_type = self.base.type #self.base.entry.type
if getting and self.type.is_pyobject: if getting and self.type.is_pyobject:
self.is_temp = True self.is_temp = True
...@@ -2734,9 +2754,14 @@ class IndexNode(ExprNode): ...@@ -2734,9 +2754,14 @@ class IndexNode(ExprNode):
# Generate buffer access code using these temps # Generate buffer access code using these temps
import Buffer, MemoryView import Buffer, MemoryView
# The above could happen because child_attrs is wrong somewhere so that
# options are not propagated. if self.base.is_name:
entry = self.base.entry entry = self.base.entry
else:
assert self.base.is_temp
cname = self.base.result()
entry = Symtab.Entry(cname, cname, self.base.type, self.base.pos)
if entry.type.is_buffer: if entry.type.is_buffer:
buffer_entry = Buffer.BufferEntry(entry) buffer_entry = Buffer.BufferEntry(entry)
negative_indices = entry.type.negative_indices negative_indices = entry.type.negative_indices
...@@ -3345,20 +3370,27 @@ class SimpleCallNode(CallNode): ...@@ -3345,20 +3370,27 @@ class SimpleCallNode(CallNode):
if i > 0 or i == 1 and self.self is not None: # skip first arg if i > 0 or i == 1 and self.self is not None: # skip first arg
warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0) warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
break break
# Calc result type and code fragment # Calc result type and code fragment
if isinstance(self.function, NewExprNode): if isinstance(self.function, NewExprNode):
self.type = PyrexTypes.CPtrType(self.function.class_type) self.type = PyrexTypes.CPtrType(self.function.class_type)
else: else:
self.type = func_type.return_type self.type = func_type.return_type
if self.function.is_name or self.function.is_attribute: if self.function.is_name or self.function.is_attribute:
if self.function.entry and self.function.entry.utility_code: if self.function.entry and self.function.entry.utility_code:
self.is_temp = 1 # currently doesn't work for self.calculate_result_code() self.is_temp = 1 # currently doesn't work for self.calculate_result_code()
if self.type.is_pyobject: if self.type.is_pyobject:
self.result_ctype = py_object_type self.result_ctype = py_object_type
self.is_temp = 1 self.is_temp = 1
elif func_type.exception_value is not None \ elif func_type.exception_value is not None \
or func_type.exception_check: or func_type.exception_check:
self.is_temp = 1 self.is_temp = 1
elif self.type.is_memoryviewslice:
self.is_temp = 1
# func_type.exception_check = True
# Called in 'nogil' context? # Called in 'nogil' context?
self.nogil = env.nogil self.nogil = env.nogil
if (self.nogil and if (self.nogil and
...@@ -3436,6 +3468,9 @@ class SimpleCallNode(CallNode): ...@@ -3436,6 +3468,9 @@ class SimpleCallNode(CallNode):
exc_checks = [] exc_checks = []
if self.type.is_pyobject and self.is_temp: if self.type.is_pyobject and self.is_temp:
exc_checks.append("!%s" % self.result()) exc_checks.append("!%s" % self.result())
elif self.type.is_memoryviewslice:
assert self.is_temp
exc_checks.append(self.type.error_condition(self.result()))
else: else:
exc_val = func_type.exception_value exc_val = func_type.exception_value
exc_check = func_type.exception_check exc_check = func_type.exception_check
...@@ -3758,6 +3793,7 @@ class AttributeNode(ExprNode): ...@@ -3758,6 +3793,7 @@ class AttributeNode(ExprNode):
self.analyse_types(env, target = 1) self.analyse_types(env, target = 1)
def analyse_types(self, env, target = 0): def analyse_types(self, env, target = 0):
self.initialized_check = env.directives['initializedcheck']
if self.analyse_as_cimported_attribute(env, target): if self.analyse_as_cimported_attribute(env, target):
self.entry.used = True self.entry.used = True
elif not target and self.analyse_as_unbound_cmethod(env): elif not target and self.analyse_as_unbound_cmethod(env):
...@@ -3862,6 +3898,8 @@ class AttributeNode(ExprNode): ...@@ -3862,6 +3898,8 @@ class AttributeNode(ExprNode):
self.result_ctype = py_object_type self.result_ctype = py_object_type
elif target and self.obj.type.is_builtin_type: elif target and self.obj.type.is_builtin_type:
error(self.pos, "Assignment to an immutable object field") error(self.pos, "Assignment to an immutable object field")
#elif self.type.is_memoryviewslice and not target:
# self.is_temp = True
def analyse_attribute(self, env, obj_type = None): def analyse_attribute(self, env, obj_type = None):
# Look up attribute and set self.type and self.member. # Look up attribute and set self.type and self.member.
...@@ -3933,6 +3971,8 @@ class AttributeNode(ExprNode): ...@@ -3933,6 +3971,8 @@ class AttributeNode(ExprNode):
def nogil_check(self, env): def nogil_check(self, env):
if self.is_py_attr: if self.is_py_attr:
self.gil_error() self.gil_error()
import MemoryView
MemoryView.err_if_nogil_initialized_check(self.pos, env, 'attribute')
gil_message = "Accessing Python attribute" gil_message = "Accessing Python attribute"
...@@ -3987,6 +4027,16 @@ class AttributeNode(ExprNode): ...@@ -3987,6 +4027,16 @@ class AttributeNode(ExprNode):
code.intern_identifier(self.attribute), code.intern_identifier(self.attribute),
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
elif self.type.is_memoryviewslice:
if self.initialized_check:
code.putln(textwrap.dedent('''
if (unlikely(!%s.memview)) {
PyErr_SetString(PyExc_AttributeError,
"Memoryview is not initialized");
%s
}''' % (self.result(), code.error_goto(self.pos))))
#code.putln("%s = %s;" % (self.result(),
# self.calculate_result_code()))
else: else:
# result_code contains what is needed, but we may need to insert # result_code contains what is needed, but we may need to insert
# a check and raise an exception # a check and raise an exception
...@@ -7860,6 +7910,8 @@ class CoerceToPyTypeNode(CoercionNode): ...@@ -7860,6 +7910,8 @@ class CoerceToPyTypeNode(CoercionNode):
# FIXME: check that the target type and the resulting type are compatible # FIXME: check that the target type and the resulting type are compatible
pass pass
self.env = env
gil_message = "Converting to Python object" gil_message = "Converting to Python object"
def may_be_none(self): def may_be_none(self):
...@@ -7887,7 +7939,7 @@ class CoerceToPyTypeNode(CoercionNode): ...@@ -7887,7 +7939,7 @@ class CoerceToPyTypeNode(CoercionNode):
def generate_result_code(self, code): def generate_result_code(self, code):
if self.arg.type.is_memoryviewslice: if self.arg.type.is_memoryviewslice:
funccall = self.arg.type.get_to_py_function(self.arg) funccall = self.arg.type.get_to_py_function(self.env, self.arg)
else: else:
funccall = "%s(%s)" % (self.arg.type.to_py_function, funccall = "%s(%s)" % (self.arg.type.to_py_function,
self.arg.result()) self.arg.result())
......
from Errors import CompileError from Errors import CompileError, error
import ExprNodes import ExprNodes
from ExprNodes import IntNode, NoneNode, IntBinopNode, NameNode, AttributeNode from ExprNodes import IntNode, NoneNode, IntBinopNode, NameNode, AttributeNode
from Visitor import CythonTransform from Visitor import CythonTransform
...@@ -17,6 +17,12 @@ BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous." ...@@ -17,6 +17,12 @@ BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous."
INVALID_ERR = "Invalid axis specification." INVALID_ERR = "Invalid axis specification."
EXPR_ERR = "no expressions allowed in axis spec, only names and literals." EXPR_ERR = "no expressions allowed in axis spec, only names and literals."
CF_ERR = "Invalid axis specification for a C/Fortran contiguous array." CF_ERR = "Invalid axis specification for a C/Fortran contiguous array."
ERR_UNINITIALIZED = ("Cannot check if memoryview %s is initialized without the "
"GIL, consider using initializedcheck(False)")
def err_if_nogil_initialized_check(pos, env, name='variable'):
if env.nogil and env.directives['initializedcheck']:
error(pos, ERR_UNINITIALIZED % name)
def concat_flags(*flags): def concat_flags(*flags):
return "(%s)" % "|".join(flags) return "(%s)" % "|".join(flags)
...@@ -85,7 +91,8 @@ def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code, ...@@ -85,7 +91,8 @@ def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code,
rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False) rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False)
code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type))) code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type)))
code.putln(code.error_goto_if_null("%s.memview" % rhstmp, lhs_pos)) # Allow uninitialized assignment
#code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry))
put_assign_to_memviewslice(lhs_cname, rhstmp, lhs_type, code, incref_rhs) put_assign_to_memviewslice(lhs_cname, rhstmp, lhs_type, code, incref_rhs)
if not pretty_rhs: if not pretty_rhs:
...@@ -739,9 +746,15 @@ def _resolve_AttributeNode(env, node): ...@@ -739,9 +746,15 @@ def _resolve_AttributeNode(env, node):
modnames = path[:-1] modnames = path[:-1]
# must be at least 1 module name, o/w not an AttributeNode. # must be at least 1 module name, o/w not an AttributeNode.
assert modnames assert modnames
scope = env.lookup(modnames[0]).as_module
for modname in modnames[1:]: scope = env
scope = scope.lookup(modname).as_module for modname in modnames:
mod = scope.lookup(modname)
if not mod:
raise CompileError(
node.pos, "undeclared name not builtin: %s" % modname)
scope = mod.as_module
return scope.lookup(path[-1]) return scope.lookup(path[-1])
def load_memview_cy_utility(util_code_name, context=None, **kwargs): def load_memview_cy_utility(util_code_name, context=None, **kwargs):
......
...@@ -10,7 +10,7 @@ cython.declare(sys=object, os=object, time=object, copy=object, ...@@ -10,7 +10,7 @@ cython.declare(sys=object, os=object, time=object, copy=object,
CppClassScope=object, UtilityCode=object, EncodedString=object, CppClassScope=object, UtilityCode=object, EncodedString=object,
absolute_path_length=cython.Py_ssize_t) absolute_path_length=cython.Py_ssize_t)
import sys, os, time, copy import sys, os, time, copy, textwrap
import Builtin import Builtin
from Errors import error, warning, InternalError, CompileError from Errors import error, warning, InternalError, CompileError
...@@ -1501,7 +1501,12 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1501,7 +1501,12 @@ class FuncDefNode(StatNode, BlockNode):
# code.put_xdecref_memoryviewslice(entry.cname) # code.put_xdecref_memoryviewslice(entry.cname)
code.putln("__Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}") code.putln("__Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);}")
if self.return_type.is_memoryviewslice:
MemoryView.put_init_entry(Naming.retval_cname, code)
err_val = Naming.retval_cname
else:
err_val = self.error_value() err_val = self.error_value()
exc_check = self.caller_will_check_exceptions() exc_check = self.caller_will_check_exceptions()
if err_val is not None or exc_check: if err_val is not None or exc_check:
# TODO: Fix exception tracing (though currently unused by cProfile). # TODO: Fix exception tracing (though currently unused by cProfile).
...@@ -1552,6 +1557,26 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1552,6 +1557,26 @@ class FuncDefNode(StatNode, BlockNode):
Buffer.put_release_buffer_code(code, entry) Buffer.put_release_buffer_code(code, entry)
if is_getbuffer_slot: if is_getbuffer_slot:
self.getbuffer_normal_cleanup(code) self.getbuffer_normal_cleanup(code)
if self.return_type.is_memoryviewslice:
# See if our return value is uninitialized on non-error return
# import MemoryView
# MemoryView.err_if_nogil_initialized_check(self.pos, env)
cond = code.unlikely(self.return_type.error_condition(
Naming.retval_cname))
code.putln(
'if (%s) {' % cond)
if env.nogil:
code.put_ensure_gil()
code.putln(
'PyErr_SetString('
'PyExc_TypeError,'
'"Memoryview return value is not initialized");')
if env.nogil:
code.put_release_ensured_gil()
code.putln(
'}')
# ----- Return cleanup for both error and no-error return # ----- Return cleanup for both error and no-error return
code.put_label(code.return_from_error_cleanup_label) code.put_label(code.return_from_error_cleanup_label)
...@@ -4312,8 +4337,6 @@ class ReturnStatNode(StatNode): ...@@ -4312,8 +4337,6 @@ class ReturnStatNode(StatNode):
self.return_type) self.return_type)
elif self.return_type.is_memoryviewslice: elif self.return_type.is_memoryviewslice:
code.put_xdecref_memoryviewslice(Naming.retval_cname) code.put_xdecref_memoryviewslice(Naming.retval_cname)
#code.put_xdecref("%s.memview" % Naming.retval_cname,
# self.return_type)
if self.value: if self.value:
self.value.generate_evaluation_code(code) self.value.generate_evaluation_code(code)
...@@ -7042,7 +7065,12 @@ class CnameDecoratorNode(StatNode): ...@@ -7042,7 +7065,12 @@ class CnameDecoratorNode(StatNode):
for name, entry in scope.entries.iteritems(): for name, entry in scope.entries.iteritems():
if entry.func_cname: if entry.func_cname:
entry.func_cname = '%s_%s' % (self.cname, entry.cname) cname = entry.cname
if '.' in cname:
# remove __pyx_base from func_cname
cname = cname.split('.')[-1]
entry.func_cname = '%s_%s' % (self.cname, cname)
def analyse_expressions(self, env): def analyse_expressions(self, env):
self.node.analyse_expressions(env) self.node.analyse_expressions(env)
......
...@@ -75,6 +75,7 @@ buffer_max_dims = 32 ...@@ -75,6 +75,7 @@ buffer_max_dims = 32
directive_defaults = { directive_defaults = {
'boundscheck' : True, 'boundscheck' : True,
'nonecheck' : False, 'nonecheck' : False,
'initializedcheck' : True,
'embedsignature' : False, 'embedsignature' : False,
'locals' : {}, 'locals' : {},
'auto_cpdef': False, 'auto_cpdef': False,
......
...@@ -387,6 +387,8 @@ class MemoryViewSliceType(PyrexType): ...@@ -387,6 +387,8 @@ class MemoryViewSliceType(PyrexType):
self.mode = MemoryView.get_mode(axes) self.mode = MemoryView.get_mode(axes)
self.writable_needed = False self.writable_needed = False
self.dtype_name = MemoryView.mangle_dtype_name(self.dtype)
def same_as_resolved_type(self, other_type): def same_as_resolved_type(self, other_type):
return ((other_type.is_memoryviewslice and return ((other_type.is_memoryviewslice and
self.dtype.same_as(other_type.dtype) and self.dtype.same_as(other_type.dtype) and
...@@ -500,9 +502,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -500,9 +502,7 @@ class MemoryViewSliceType(PyrexType):
return True return True
def specialization_suffix(self): def specialization_suffix(self):
import MemoryView return "%s_%s" % (self.axes_to_name(), self.dtype_name)
dtype_name = MemoryView.mangle_dtype_name(self.dtype)
return "%s_%s" % (self.axes_to_name(), dtype_name)
#def global_init_code(self, entry, code): #def global_init_code(self, entry, code):
# code.putln("%s.data = NULL;" % entry.cname) # code.putln("%s.data = NULL;" % entry.cname)
...@@ -552,9 +552,49 @@ class MemoryViewSliceType(PyrexType): ...@@ -552,9 +552,49 @@ class MemoryViewSliceType(PyrexType):
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
return True return True
def get_to_py_function(self, obj): def get_to_py_function(self, env, obj):
return "__pyx_memoryview_fromslice(&%s, %s.memview->obj, %s, %s);" % ( to_py_func, from_py_func = self.dtype_object_conversion_funcs(env)
obj.result(), obj.result(), self.flags, self.ndim) to_py_func = "(PyObject *(*)(char *)) " + to_py_func
from_py_func = "(int (*)(char *, PyObject *)) " + from_py_func
tup = (obj.result(), obj.result(), self.flags, self.ndim,
to_py_func, from_py_func)
return ("__pyx_memoryview_fromslice(&%s, %s.memview->obj, "
"%s, %s, %s, %s);" % tup)
def dtype_object_conversion_funcs(self, env):
import MemoryView, Code
get_function = "__pyx_memview_get_%s" % self.dtype_name
set_function = "__pyx_memview_set_%s" % self.dtype_name
context = dict(
get_function = get_function,
set_function = set_function,
)
if self.dtype.is_pyobject:
utility_name = "MemviewObjectToObject"
else:
if not (self.dtype.create_to_py_utility_code(env) and
self.dtype.create_from_py_utility_code(env)):
print "cannot convert %s" % self.dtype
return "NULL", "NULL"
utility_name = "MemviewDtypeToObject"
error_condition = (self.dtype.error_condition('value') or
'PyErr_Occurred()')
context.update(
to_py_function = self.dtype.to_py_function,
from_py_function = self.dtype.from_py_function,
dtype = self.dtype.declaration_code(""),
error_condition = error_condition,
)
utility = Code.ContentHashingUtilityCode.load(
utility_name, "MemoryView_C.c", context=context)
env.use_utility_code(utility)
return get_function, set_function
def axes_to_code(self): def axes_to_code(self):
"Return a list of code constants for each axis" "Return a list of code constants for each axis"
......
...@@ -64,7 +64,7 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -64,7 +64,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
is_cython_utility = True is_cython_utility = True
def __init__(self, impl, name="__pyxutil", prefix="", requires=None, def __init__(self, impl, name="__pyxutil", prefix="", requires=None,
file=None): file=None, from_scope=None):
# 1) We need to delay the parsing/processing, so that all modules can be # 1) We need to delay the parsing/processing, so that all modules can be
# imported without import loops # imported without import loops
# 2) The same utility code object can be used for multiple source files; # 2) The same utility code object can be used for multiple source files;
...@@ -76,6 +76,7 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -76,6 +76,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
self.file = file self.file = file
self.prefix = prefix self.prefix = prefix
self.requires = requires or [] self.requires = requires or []
self.from_scope = from_scope
def get_tree(self, entries_only=False): def get_tree(self, entries_only=False):
from AnalysedTreeTransforms import AutoTestDictTransform from AnalysedTreeTransforms import AutoTestDictTransform
...@@ -108,6 +109,15 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -108,6 +109,15 @@ class CythonUtilityCode(Code.UtilityCodeBase):
pipeline = Pipeline.insert_into_pipeline(pipeline, transform, pipeline = Pipeline.insert_into_pipeline(pipeline, transform,
before=before) before=before)
if self.from_scope:
def scope_transform(module_node):
module_node.scope.merge_in(self.from_scope)
return module_node
transform = ParseTreeTransforms.AnalyseDeclarationsTransform
pipeline = Pipeline.insert_into_pipeline(pipeline, transform,
before=scope_transform)
(err, tree) = Pipeline.run_pipeline(pipeline, tree) (err, tree) = Pipeline.run_pipeline(pipeline, tree)
assert not err, err assert not err, err
return tree return tree
......
...@@ -217,18 +217,10 @@ cdef class memoryview(object): ...@@ -217,18 +217,10 @@ cdef class memoryview(object):
if self.lock != NULL: if self.lock != NULL:
PyThread_free_lock(self.lock) PyThread_free_lock(self.lock)
@cname('__pyx_memoryview_getitem') cdef char *get_item_pointer(memoryview self, object index) except NULL:
def __getitem__(memoryview self, object index): cdef Py_ssize_t dim
# cdef Py_ssize_t idx cdef Py_buffer view = self.view
cdef char *itemp = <char *> self.view.buf cdef char *itemp = <char *> view.buf
cdef bytes bytesitem
cdef str fmt = self.view.format
import struct
try:
itemsize = struct.calcsize(fmt)
except struct.error:
raise TypeError("Unsupported format: %r" % fmt)
if index is Ellipsis: if index is Ellipsis:
return self return self
...@@ -252,17 +244,50 @@ cdef class memoryview(object): ...@@ -252,17 +244,50 @@ cdef class memoryview(object):
for dim, idx in enumerate(tup): for dim, idx in enumerate(tup):
_check_index(idx) _check_index(idx)
itemp = pybuffer_index(&self.view, itemp, idx, dim + 1) itemp = pybuffer_index(&self.view, itemp, idx, dim)
return itemp
@cname('__pyx_memoryview_getitem')
def __getitem__(memoryview self, object index):
cdef char *itemp = self.get_item_pointer(index)
return self.convert_item_to_object(itemp)
@cname('__pyx_memoryview_setitem')
def __setitem__(memoryview self, object index, object value):
cdef char *itemp = self.get_item_pointer(index)
self.assign_item_from_object(itemp, value)
cdef convert_item_to_object(self, char *itemp):
"""Only used if instantiated manually by the user, or if Cython doesn't
know how to convert the type"""
import struct
cdef bytes bytesitem
# Do a manual and complete check here instead of this easy hack # Do a manual and complete check here instead of this easy hack
bytesitem = itemp[:self.view.itemsize] bytesitem = itemp[:self.view.itemsize]
return struct.unpack(fmt, bytesitem) return struct.unpack(self.view.format, bytesitem)
cdef assign_item_from_object(self, char *itemp, object value):
"""Only used if instantiated manually by the user, or if Cython doesn't
know how to convert the type"""
import struct
cdef char c
cdef bytes bytesvalue
cdef Py_ssize_t i
if isinstance(value, tuple):
bytesvalue = struct.pack(self.view.format, *value)
else:
bytesvalue = struct.pack(self.view.format, value)
for i, c in enumerate(bytesvalue):
itemp[i] = c
def __repr__(self): def __repr__(self):
return "<MemoryView of %s at 0x%x>" % (self.obj.__class__.__name__, id(self)) return "<MemoryView of %r at 0x%x>" % (self.obj.__class__.__name__, id(self))
def __str__(self): def __str__(self):
return "<MemoryView of %r at 0x%x>" % (self.obj, id(self)) return "<MemoryView of %r object>" % (self.obj.__class__.__name__,)
@cname('__pyx_memoryviewslice') @cname('__pyx_memoryviewslice')
...@@ -274,28 +299,47 @@ cdef class _memoryviewslice(memoryview): ...@@ -274,28 +299,47 @@ cdef class _memoryviewslice(memoryview):
# Restore the original Py_buffer before releasing # Restore the original Py_buffer before releasing
cdef Py_buffer orig_view cdef Py_buffer orig_view
cdef object (*to_object_func)(char *)
cdef int (*to_dtype_func)(char *, object) except 0
def __cinit__(self, object obj, int flags): def __cinit__(self, object obj, int flags):
self.orig_view = self.view self.orig_view = self.view
def __dealloc__(self): def __dealloc__(self):
self.view = self.orig_view self.view = self.orig_view
cdef convert_item_to_object(self, char *itemp):
if self.to_object_func != NULL:
self.to_object_func(itemp)
else:
memoryview.convert_item_to_object(self, itemp)
cdef assign_item_from_object(self, char *itemp, object value):
if self.to_dtype_func != NULL:
self.to_dtype_func(itemp, value)
else:
memoryview.assign_item_from_object(self, itemp, value)
@cname('__pyx_memoryview_new') @cname('__pyx_memoryview_new')
cdef memoryview_cwrapper(object o, int flags): cdef memoryview_cwrapper(object o, int flags):
return memoryview(o, flags) return memoryview(o, flags)
@cname('__pyx_memoryview_fromslice') @cname('__pyx_memoryview_fromslice')
cdef memoryview_from_memview_cwrapper({{memviewslice_name}} *memviewslice, cdef memoryview_from_memslice_cwrapper(
object orig_obj, int flags, int new_ndim): {{memviewslice_name}} *memviewslice, object orig_obj, int flags, int cur_ndim,
object (*to_object_func)(char *), int (*to_dtype_func)(char *, object)):
cdef _memoryviewslice result = _memoryviewslice(orig_obj, flags) cdef _memoryviewslice result = _memoryviewslice(orig_obj, flags)
cdef int new_ndim = result.view.ndim - cur_ndim
result.from_slice = memviewslice[0] result.from_slice = memviewslice[0]
result.view.shape = <Py_ssize_t *> (&result.from_slice.shape + new_ndim) result.view.shape = <Py_ssize_t *> (&result.from_slice.shape + new_ndim)
result.view.strides = <Py_ssize_t *> (&result.from_slice.strides + new_ndim) result.view.strides = <Py_ssize_t *> (&result.from_slice.strides + new_ndim)
result.view.suboffsets = <Py_ssize_t *> (&result.from_slice.suboffsets + new_ndim) result.view.suboffsets = <Py_ssize_t *> (&result.from_slice.suboffsets + new_ndim)
result.view.ndim = new_ndim result.view.ndim = cur_ndim
result.to_object_func = to_object_func
return result return result
...@@ -336,14 +380,14 @@ cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index, int dim ...@@ -336,14 +380,14 @@ cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index, int dim
if index < 0: if index < 0:
index += view.shape[dim] index += view.shape[dim]
if index < 0: if index < 0:
raise IndexError("Out of bounds in dimension %d" % dim) raise IndexError("Out of bounds on buffer access (axis %d)" % dim)
if index > shape: if index > shape:
raise IndexError("Out of bounds in dimension %d" % dim) raise IndexError("Out of bounds on buffer access (axis %d)" % dim)
resultp = bufp + index * stride resultp = bufp + index * stride
if suboffset >= 0: if suboffset >= 0:
resultp = (<char **> resultp)[0] + suboffset resultp = (<char **> resultp)[0] + suboffset
return resultp return resultp
...@@ -49,10 +49,7 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *, int, int); ...@@ -49,10 +49,7 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *, int, int);
static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *, int, int); static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *, int, int);
/////////////// MemviewSliceIndex.proto /////////////// /////////////// MemviewSliceIndex.proto ///////////////
static CYTHON_INLINE char *__pyx_memviewslice_index_full(const char *bufp, Py_ssize_t idx, Py_ssize_t stride, Py_ssize_t suboffset);
static CYTHON_INLINE char *__pyx_memviewslice_index_full(char *bufp, Py_ssize_t idx, Py_ssize_t stride, Py_ssize_t suboffset);
static CYTHON_INLINE char *__pyx_memviewslice_index_full_contig(char *bufp, Py_ssize_t suboffset);
/////////////// ObjectToMemviewSlice /////////////// /////////////// ObjectToMemviewSlice ///////////////
...@@ -173,10 +170,6 @@ static int __Pyx_ValidateAndInit_memviewslice( ...@@ -173,10 +170,6 @@ static int __Pyx_ValidateAndInit_memviewslice(
} }
} }
if (spec & (__Pyx_MEMVIEW_PTR|__Pyx_MEMVIEW_FULL) && !buf->suboffsets) {
memviewslice->suboffsets[i] = -1;
}
if (spec & __Pyx_MEMVIEW_PTR) { if (spec & __Pyx_MEMVIEW_PTR) {
if (buf->suboffsets && buf->suboffsets[i] < 0) { if (buf->suboffsets && buf->suboffsets[i] < 0) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
...@@ -238,16 +231,16 @@ static int __Pyx_init_memviewslice( ...@@ -238,16 +231,16 @@ static int __Pyx_init_memviewslice(
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"buf is NULL."); "buf is NULL.");
goto fail; goto fail;
} else if(memviewslice->memview || memviewslice->data) { } else if (memviewslice->memview || memviewslice->data) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"memviewslice is already initialized!"); "memviewslice is already initialized!");
goto fail; goto fail;
} }
for(i=0; i<ndim; i++) { for (i = 0; i < ndim; i++) {
memviewslice->strides[i] = buf->strides[i]; memviewslice->strides[i] = buf->strides[i];
memviewslice->shape[i] = buf->shape[i]; memviewslice->shape[i] = buf->shape[i];
if(buf->suboffsets) { if (buf->suboffsets) {
memviewslice->suboffsets[i] = buf->suboffsets[i]; memviewslice->suboffsets[i] = buf->suboffsets[i];
} else { } else {
memviewslice->suboffsets[i] = -1; memviewslice->suboffsets[i] = -1;
...@@ -294,15 +287,16 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice, ...@@ -294,15 +287,16 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice,
int first_time; int first_time;
struct {{memview_struct_name}} *memview = memslice->memview; struct {{memview_struct_name}} *memview = memslice->memview;
if (!memview) if (!memview)
__pyx_fatalerror("memoryslice is not initialized (line %d)", lineno); return; /* allow uninitialized memoryview assignment */
/* __pyx_fatalerror("memoryslice is not initialized (line %d)", lineno); */
if (memview->acquisition_count <= 0) if (memview->acquisition_count <= 0)
__pyx_fatalerror("Acquisition count is %d (line %d)", __pyx_fatalerror("Acquisition count is %d (line %d)",
memview->acquisition_count, lineno); memview->acquisition_count, lineno);
PyThread_acquire_lock(memview->lock, 1); //PyThread_acquire_lock(memview->lock, 1);
first_time = (memview->acquisition_count++ == 0); first_time = (memview->acquisition_count++ == 0);
PyThread_release_lock(memview->lock); //PyThread_release_lock(memview->lock);
if (first_time) { if (first_time) {
if (have_gil) { if (have_gil) {
...@@ -327,9 +321,9 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice, ...@@ -327,9 +321,9 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
__pyx_fatalerror("Acquisition count is %d (line %d)", __pyx_fatalerror("Acquisition count is %d (line %d)",
memview->acquisition_count, lineno); memview->acquisition_count, lineno);
PyThread_acquire_lock(memview->lock, 1); //PyThread_acquire_lock(memview->lock, 1);
last_time = (memview->acquisition_count-- == 1); last_time = (memview->acquisition_count-- == 1);
PyThread_release_lock(memview->lock); //PyThread_release_lock(memview->lock);
if (last_time) { if (last_time) {
if (have_gil) { if (have_gil) {
...@@ -418,10 +412,46 @@ no_fail: ...@@ -418,10 +412,46 @@ no_fail:
/////////////// MemviewSliceIndex /////////////// /////////////// MemviewSliceIndex ///////////////
static CYTHON_INLINE char *__pyx_memviewslice_index_full(char *bufp, Py_ssize_t idx, Py_ssize_t stride, Py_ssize_t suboffset) { static CYTHON_INLINE char *__pyx_memviewslice_index_full(const char *bufp, Py_ssize_t idx, Py_ssize_t stride, Py_ssize_t suboffset) {
bufp = bufp + idx * stride; bufp = bufp + idx * stride;
if (suboffset >= 0) { if (suboffset >= 0) {
bufp = *((char **) bufp) + suboffset; bufp = *((char **) bufp) + suboffset;
} }
return bufp; return bufp;
} }
/////////////// MemviewDtypeToObject.proto ///////////////
PyObject *{{get_function}}(const char *itemp); /* proto */
int {{set_function}}(const char *itemp, PyObject *obj); /* proto */
/////////////// MemviewDtypeToObject ///////////////
{{#__pyx_memview_<dtype_name>_to_object}}
PyObject *{{get_function}}(const char *itemp) {
return (PyObject *) {{to_py_function}}(*({{dtype}} *) itemp);
}
int {{set_function}}(const char *itemp, PyObject *obj) {
{{dtype}} value = {{from_py_function}}(obj);
if ({{error_condition}})
return 0;
*({{dtype}} *) itemp = value;
return 1;
}
/////////////// MemviewObjectToObject.proto ///////////////
PyObject *{{get_function}}(const char *itemp); /* proto */
int {{set_function}}(const char *itemp, PyObject *obj); /* proto */
/////////////// MemviewObjectToObject ///////////////
PyObject *{{get_function}}(const char *itemp) {
PyObject *result = *(PyObject **) itemp;
Py_INCREF(result);
return result;
}
int {{set_function}}(const char *itemp, PyObject *obj) {
Py_INCREF(obj);
*(PyObject **) itemp = obj;
return 1;
}
This diff is collapsed.
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