Spaces:
Build error
Build error
from __future__ import absolute_import | |
from .Errors import CompileError, error | |
from . import ExprNodes | |
from .ExprNodes import IntNode, NameNode, AttributeNode | |
from . import Options | |
from .Code import UtilityCode, TempitaUtilityCode | |
from .UtilityCode import CythonUtilityCode | |
from . import Buffer | |
from . import PyrexTypes | |
from . import ModuleNode | |
START_ERR = "Start must not be given." | |
STOP_ERR = "Axis specification only allowed in the 'step' slot." | |
STEP_ERR = "Step must be omitted, 1, or a valid specifier." | |
BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous." | |
INVALID_ERR = "Invalid axis specification." | |
NOT_CIMPORTED_ERR = "Variable was not cimported from cython.view" | |
EXPR_ERR = "no expressions allowed in axis spec, only names and literals." | |
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)") | |
format_flag = "PyBUF_FORMAT" | |
memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT)" | |
memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT)" | |
memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT)" | |
memview_full_access = "PyBUF_FULL_RO" | |
#memview_strided_access = "PyBUF_STRIDED_RO" | |
memview_strided_access = "PyBUF_RECORDS_RO" | |
MEMVIEW_DIRECT = '__Pyx_MEMVIEW_DIRECT' | |
MEMVIEW_PTR = '__Pyx_MEMVIEW_PTR' | |
MEMVIEW_FULL = '__Pyx_MEMVIEW_FULL' | |
MEMVIEW_CONTIG = '__Pyx_MEMVIEW_CONTIG' | |
MEMVIEW_STRIDED= '__Pyx_MEMVIEW_STRIDED' | |
MEMVIEW_FOLLOW = '__Pyx_MEMVIEW_FOLLOW' | |
_spec_to_const = { | |
'direct' : MEMVIEW_DIRECT, | |
'ptr' : MEMVIEW_PTR, | |
'full' : MEMVIEW_FULL, | |
'contig' : MEMVIEW_CONTIG, | |
'strided': MEMVIEW_STRIDED, | |
'follow' : MEMVIEW_FOLLOW, | |
} | |
_spec_to_abbrev = { | |
'direct' : 'd', | |
'ptr' : 'p', | |
'full' : 'f', | |
'contig' : 'c', | |
'strided' : 's', | |
'follow' : '_', | |
} | |
memslice_entry_init = "{ 0, 0, { 0 }, { 0 }, { 0 } }" | |
memview_name = u'memoryview' | |
memview_typeptr_cname = '__pyx_memoryview_type' | |
memview_objstruct_cname = '__pyx_memoryview_obj' | |
memviewslice_cname = u'__Pyx_memviewslice' | |
def put_init_entry(mv_cname, code): | |
code.putln("%s.data = NULL;" % mv_cname) | |
code.putln("%s.memview = NULL;" % mv_cname) | |
#def axes_to_str(axes): | |
# return "".join([access[0].upper()+packing[0] for (access, packing) in axes]) | |
def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code, | |
have_gil=False, first_assignment=True): | |
"We can avoid decreffing the lhs if we know it is the first assignment" | |
assert rhs.type.is_memoryviewslice | |
pretty_rhs = rhs.result_in_temp() or rhs.is_simple() | |
if pretty_rhs: | |
rhstmp = rhs.result() | |
else: | |
rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False) | |
code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type))) | |
# Allow uninitialized assignment | |
#code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry)) | |
put_assign_to_memviewslice(lhs_cname, rhs, rhstmp, lhs_type, code, | |
have_gil=have_gil, first_assignment=first_assignment) | |
if not pretty_rhs: | |
code.funcstate.release_temp(rhstmp) | |
def put_assign_to_memviewslice(lhs_cname, rhs, rhs_cname, memviewslicetype, code, | |
have_gil=False, first_assignment=False): | |
if lhs_cname == rhs_cname: | |
# self assignment is tricky because memoryview xdecref clears the memoryview | |
# thus invalidating both sides of the assignment. Therefore make it actually do nothing | |
code.putln("/* memoryview self assignment no-op */") | |
return | |
if not first_assignment: | |
code.put_xdecref(lhs_cname, memviewslicetype, | |
have_gil=have_gil) | |
if not rhs.result_in_temp(): | |
rhs.make_owned_memoryviewslice(code) | |
code.putln("%s = %s;" % (lhs_cname, rhs_cname)) | |
def get_buf_flags(specs): | |
is_c_contig, is_f_contig = is_cf_contig(specs) | |
if is_c_contig: | |
return memview_c_contiguous | |
elif is_f_contig: | |
return memview_f_contiguous | |
access, packing = zip(*specs) | |
if 'full' in access or 'ptr' in access: | |
return memview_full_access | |
else: | |
return memview_strided_access | |
def insert_newaxes(memoryviewtype, n): | |
axes = [('direct', 'strided')] * n | |
axes.extend(memoryviewtype.axes) | |
return PyrexTypes.MemoryViewSliceType(memoryviewtype.dtype, axes) | |
def broadcast_types(src, dst): | |
n = abs(src.ndim - dst.ndim) | |
if src.ndim < dst.ndim: | |
return insert_newaxes(src, n), dst | |
else: | |
return src, insert_newaxes(dst, n) | |
def valid_memslice_dtype(dtype, i=0): | |
""" | |
Return whether type dtype can be used as the base type of a | |
memoryview slice. | |
We support structs, numeric types and objects | |
""" | |
if dtype.is_complex and dtype.real_type.is_int: | |
return False | |
if dtype is PyrexTypes.c_bint_type: | |
return False | |
if dtype.is_struct and dtype.kind == 'struct': | |
for member in dtype.scope.var_entries: | |
if not valid_memslice_dtype(member.type): | |
return False | |
return True | |
return ( | |
dtype.is_error or | |
# Pointers are not valid (yet) | |
# (dtype.is_ptr and valid_memslice_dtype(dtype.base_type)) or | |
(dtype.is_array and i < 8 and | |
valid_memslice_dtype(dtype.base_type, i + 1)) or | |
dtype.is_numeric or | |
dtype.is_pyobject or | |
dtype.is_fused or # accept this as it will be replaced by specializations later | |
(dtype.is_typedef and valid_memslice_dtype(dtype.typedef_base_type)) | |
) | |
class MemoryViewSliceBufferEntry(Buffer.BufferEntry): | |
""" | |
May be used during code generation time to be queried for | |
shape/strides/suboffsets attributes, or to perform indexing or slicing. | |
""" | |
def __init__(self, entry): | |
self.entry = entry | |
self.type = entry.type | |
self.cname = entry.cname | |
self.buf_ptr = "%s.data" % self.cname | |
dtype = self.entry.type.dtype | |
self.buf_ptr_type = PyrexTypes.CPtrType(dtype) | |
self.init_attributes() | |
def get_buf_suboffsetvars(self): | |
return self._for_all_ndim("%s.suboffsets[%d]") | |
def get_buf_stridevars(self): | |
return self._for_all_ndim("%s.strides[%d]") | |
def get_buf_shapevars(self): | |
return self._for_all_ndim("%s.shape[%d]") | |
def generate_buffer_lookup_code(self, code, index_cnames): | |
axes = [(dim, index_cnames[dim], access, packing) | |
for dim, (access, packing) in enumerate(self.type.axes)] | |
return self._generate_buffer_lookup_code(code, axes) | |
def _generate_buffer_lookup_code(self, code, axes, cast_result=True): | |
""" | |
Generate a single expression that indexes the memory view slice | |
in each dimension. | |
""" | |
bufp = self.buf_ptr | |
type_decl = self.type.dtype.empty_declaration_code() | |
for dim, index, access, packing in axes: | |
shape = "%s.shape[%d]" % (self.cname, dim) | |
stride = "%s.strides[%d]" % (self.cname, dim) | |
suboffset = "%s.suboffsets[%d]" % (self.cname, dim) | |
flag = get_memoryview_flag(access, packing) | |
if flag in ("generic", "generic_contiguous"): | |
# Note: we cannot do cast tricks to avoid stride multiplication | |
# for generic_contiguous, as we may have to do (dtype *) | |
# or (dtype **) arithmetic, we won't know which unless | |
# we check suboffsets | |
code.globalstate.use_utility_code(memviewslice_index_helpers) | |
bufp = ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' % | |
(bufp, index, stride, suboffset)) | |
elif flag == "indirect": | |
bufp = "(%s + %s * %s)" % (bufp, index, stride) | |
bufp = ("(*((char **) %s) + %s)" % (bufp, suboffset)) | |
elif flag == "indirect_contiguous": | |
# Note: we do char ** arithmetic | |
bufp = "(*((char **) %s + %s) + %s)" % (bufp, index, suboffset) | |
elif flag == "strided": | |
bufp = "(%s + %s * %s)" % (bufp, index, stride) | |
else: | |
assert flag == 'contiguous', flag | |
bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index) | |
bufp = '( /* dim=%d */ %s )' % (dim, bufp) | |
if cast_result: | |
return "((%s *) %s)" % (type_decl, bufp) | |
return bufp | |
def generate_buffer_slice_code(self, code, indices, dst, dst_type, have_gil, | |
have_slices, directives): | |
""" | |
Slice a memoryviewslice. | |
indices - list of index nodes. If not a SliceNode, or NoneNode, | |
then it must be coercible to Py_ssize_t | |
Simply call __pyx_memoryview_slice_memviewslice with the right | |
arguments, unless the dimension is omitted or a bare ':', in which | |
case we copy over the shape/strides/suboffsets attributes directly | |
for that dimension. | |
""" | |
src = self.cname | |
code.putln("%(dst)s.data = %(src)s.data;" % locals()) | |
code.putln("%(dst)s.memview = %(src)s.memview;" % locals()) | |
code.put_incref_memoryviewslice(dst, dst_type, have_gil=have_gil) | |
all_dimensions_direct = all(access == 'direct' for access, packing in self.type.axes) | |
suboffset_dim_temp = [] | |
def get_suboffset_dim(): | |
# create global temp variable at request | |
if not suboffset_dim_temp: | |
suboffset_dim = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False) | |
code.putln("%s = -1;" % suboffset_dim) | |
suboffset_dim_temp.append(suboffset_dim) | |
return suboffset_dim_temp[0] | |
dim = -1 | |
new_ndim = 0 | |
for index in indices: | |
if index.is_none: | |
# newaxis | |
for attrib, value in [('shape', 1), ('strides', 0), ('suboffsets', -1)]: | |
code.putln("%s.%s[%d] = %d;" % (dst, attrib, new_ndim, value)) | |
new_ndim += 1 | |
continue | |
dim += 1 | |
access, packing = self.type.axes[dim] | |
if index.is_slice: | |
# slice, unspecified dimension, or part of ellipsis | |
d = dict(locals()) | |
for s in "start stop step".split(): | |
idx = getattr(index, s) | |
have_idx = d['have_' + s] = not idx.is_none | |
d[s] = idx.result() if have_idx else "0" | |
if not (d['have_start'] or d['have_stop'] or d['have_step']): | |
# full slice (:), simply copy over the extent, stride | |
# and suboffset. Also update suboffset_dim if needed | |
d['access'] = access | |
util_name = "SimpleSlice" | |
else: | |
util_name = "ToughSlice" | |
d['error_goto'] = code.error_goto(index.pos) | |
new_ndim += 1 | |
else: | |
# normal index | |
idx = index.result() | |
indirect = access != 'direct' | |
if indirect: | |
generic = access == 'full' | |
if new_ndim != 0: | |
return error(index.pos, | |
"All preceding dimensions must be " | |
"indexed and not sliced") | |
d = dict( | |
locals(), | |
wraparound=int(directives['wraparound']), | |
boundscheck=int(directives['boundscheck']), | |
) | |
if d['boundscheck']: | |
d['error_goto'] = code.error_goto(index.pos) | |
util_name = "SliceIndex" | |
_, impl = TempitaUtilityCode.load_as_string(util_name, "MemoryView_C.c", context=d) | |
code.put(impl) | |
if suboffset_dim_temp: | |
code.funcstate.release_temp(suboffset_dim_temp[0]) | |
def empty_slice(pos): | |
none = ExprNodes.NoneNode(pos) | |
return ExprNodes.SliceNode(pos, start=none, | |
stop=none, step=none) | |
def unellipsify(indices, ndim): | |
result = [] | |
seen_ellipsis = False | |
have_slices = False | |
newaxes = [newaxis for newaxis in indices if newaxis.is_none] | |
n_indices = len(indices) - len(newaxes) | |
for index in indices: | |
if isinstance(index, ExprNodes.EllipsisNode): | |
have_slices = True | |
full_slice = empty_slice(index.pos) | |
if seen_ellipsis: | |
result.append(full_slice) | |
else: | |
nslices = ndim - n_indices + 1 | |
result.extend([full_slice] * nslices) | |
seen_ellipsis = True | |
else: | |
have_slices = have_slices or index.is_slice or index.is_none | |
result.append(index) | |
result_length = len(result) - len(newaxes) | |
if result_length < ndim: | |
have_slices = True | |
nslices = ndim - result_length | |
result.extend([empty_slice(indices[-1].pos)] * nslices) | |
return have_slices, result, newaxes | |
def get_memoryview_flag(access, packing): | |
if access == 'full' and packing in ('strided', 'follow'): | |
return 'generic' | |
elif access == 'full' and packing == 'contig': | |
return 'generic_contiguous' | |
elif access == 'ptr' and packing in ('strided', 'follow'): | |
return 'indirect' | |
elif access == 'ptr' and packing == 'contig': | |
return 'indirect_contiguous' | |
elif access == 'direct' and packing in ('strided', 'follow'): | |
return 'strided' | |
else: | |
assert (access, packing) == ('direct', 'contig'), (access, packing) | |
return 'contiguous' | |
def get_is_contig_func_name(contig_type, ndim): | |
assert contig_type in ('C', 'F') | |
return "__pyx_memviewslice_is_contig_%s%d" % (contig_type, ndim) | |
def get_is_contig_utility(contig_type, ndim): | |
assert contig_type in ('C', 'F') | |
C = dict(context, ndim=ndim, contig_type=contig_type) | |
utility = load_memview_c_utility("MemviewSliceCheckContig", C, requires=[is_contig_utility]) | |
return utility | |
def slice_iter(slice_type, slice_result, ndim, code, force_strided=False): | |
if (slice_type.is_c_contig or slice_type.is_f_contig) and not force_strided: | |
return ContigSliceIter(slice_type, slice_result, ndim, code) | |
else: | |
return StridedSliceIter(slice_type, slice_result, ndim, code) | |
class SliceIter(object): | |
def __init__(self, slice_type, slice_result, ndim, code): | |
self.slice_type = slice_type | |
self.slice_result = slice_result | |
self.code = code | |
self.ndim = ndim | |
class ContigSliceIter(SliceIter): | |
def start_loops(self): | |
code = self.code | |
code.begin_block() | |
type_decl = self.slice_type.dtype.empty_declaration_code() | |
total_size = ' * '.join("%s.shape[%d]" % (self.slice_result, i) | |
for i in range(self.ndim)) | |
code.putln("Py_ssize_t __pyx_temp_extent = %s;" % total_size) | |
code.putln("Py_ssize_t __pyx_temp_idx;") | |
code.putln("%s *__pyx_temp_pointer = (%s *) %s.data;" % ( | |
type_decl, type_decl, self.slice_result)) | |
code.putln("for (__pyx_temp_idx = 0; " | |
"__pyx_temp_idx < __pyx_temp_extent; " | |
"__pyx_temp_idx++) {") | |
return "__pyx_temp_pointer" | |
def end_loops(self): | |
self.code.putln("__pyx_temp_pointer += 1;") | |
self.code.putln("}") | |
self.code.end_block() | |
class StridedSliceIter(SliceIter): | |
def start_loops(self): | |
code = self.code | |
code.begin_block() | |
for i in range(self.ndim): | |
t = i, self.slice_result, i | |
code.putln("Py_ssize_t __pyx_temp_extent_%d = %s.shape[%d];" % t) | |
code.putln("Py_ssize_t __pyx_temp_stride_%d = %s.strides[%d];" % t) | |
code.putln("char *__pyx_temp_pointer_%d;" % i) | |
code.putln("Py_ssize_t __pyx_temp_idx_%d;" % i) | |
code.putln("__pyx_temp_pointer_0 = %s.data;" % self.slice_result) | |
for i in range(self.ndim): | |
if i > 0: | |
code.putln("__pyx_temp_pointer_%d = __pyx_temp_pointer_%d;" % (i, i - 1)) | |
code.putln("for (__pyx_temp_idx_%d = 0; " | |
"__pyx_temp_idx_%d < __pyx_temp_extent_%d; " | |
"__pyx_temp_idx_%d++) {" % (i, i, i, i)) | |
return "__pyx_temp_pointer_%d" % (self.ndim - 1) | |
def end_loops(self): | |
code = self.code | |
for i in range(self.ndim - 1, -1, -1): | |
code.putln("__pyx_temp_pointer_%d += __pyx_temp_stride_%d;" % (i, i)) | |
code.putln("}") | |
code.end_block() | |
def copy_c_or_fortran_cname(memview): | |
if memview.is_c_contig: | |
c_or_f = 'c' | |
else: | |
c_or_f = 'f' | |
return "__pyx_memoryview_copy_slice_%s_%s" % ( | |
memview.specialization_suffix(), c_or_f) | |
def get_copy_new_utility(pos, from_memview, to_memview): | |
if (from_memview.dtype != to_memview.dtype and | |
not (from_memview.dtype.is_cv_qualified and from_memview.dtype.cv_base_type == to_memview.dtype)): | |
error(pos, "dtypes must be the same!") | |
return | |
if len(from_memview.axes) != len(to_memview.axes): | |
error(pos, "number of dimensions must be same") | |
return | |
if not (to_memview.is_c_contig or to_memview.is_f_contig): | |
error(pos, "to_memview must be c or f contiguous.") | |
return | |
for (access, packing) in from_memview.axes: | |
if access != 'direct': | |
error(pos, "cannot handle 'full' or 'ptr' access at this time.") | |
return | |
if to_memview.is_c_contig: | |
mode = 'c' | |
contig_flag = memview_c_contiguous | |
else: | |
assert to_memview.is_f_contig | |
mode = 'fortran' | |
contig_flag = memview_f_contiguous | |
return load_memview_c_utility( | |
"CopyContentsUtility", | |
context=dict( | |
context, | |
mode=mode, | |
dtype_decl=to_memview.dtype.empty_declaration_code(), | |
contig_flag=contig_flag, | |
ndim=to_memview.ndim, | |
func_cname=copy_c_or_fortran_cname(to_memview), | |
dtype_is_object=int(to_memview.dtype.is_pyobject)), | |
requires=[copy_contents_new_utility]) | |
def get_axes_specs(env, axes): | |
''' | |
get_axes_specs(env, axes) -> list of (access, packing) specs for each axis. | |
access is one of 'full', 'ptr' or 'direct' | |
packing is one of 'contig', 'strided' or 'follow' | |
''' | |
cythonscope = env.global_scope().context.cython_scope | |
cythonscope.load_cythonscope() | |
viewscope = cythonscope.viewscope | |
access_specs = tuple([viewscope.lookup(name) | |
for name in ('full', 'direct', 'ptr')]) | |
packing_specs = tuple([viewscope.lookup(name) | |
for name in ('contig', 'strided', 'follow')]) | |
is_f_contig, is_c_contig = False, False | |
default_access, default_packing = 'direct', 'strided' | |
cf_access, cf_packing = default_access, 'follow' | |
axes_specs = [] | |
# analyse all axes. | |
for idx, axis in enumerate(axes): | |
if not axis.start.is_none: | |
raise CompileError(axis.start.pos, START_ERR) | |
if not axis.stop.is_none: | |
raise CompileError(axis.stop.pos, STOP_ERR) | |
if axis.step.is_none: | |
axes_specs.append((default_access, default_packing)) | |
elif isinstance(axis.step, IntNode): | |
# the packing for the ::1 axis is contiguous, | |
# all others are cf_packing. | |
if axis.step.compile_time_value(env) != 1: | |
raise CompileError(axis.step.pos, STEP_ERR) | |
axes_specs.append((cf_access, 'cfcontig')) | |
elif isinstance(axis.step, (NameNode, AttributeNode)): | |
entry = _get_resolved_spec(env, axis.step) | |
if entry.name in view_constant_to_access_packing: | |
axes_specs.append(view_constant_to_access_packing[entry.name]) | |
else: | |
raise CompileError(axis.step.pos, INVALID_ERR) | |
else: | |
raise CompileError(axis.step.pos, INVALID_ERR) | |
# First, find out if we have a ::1 somewhere | |
contig_dim = 0 | |
is_contig = False | |
for idx, (access, packing) in enumerate(axes_specs): | |
if packing == 'cfcontig': | |
if is_contig: | |
raise CompileError(axis.step.pos, BOTH_CF_ERR) | |
contig_dim = idx | |
axes_specs[idx] = (access, 'contig') | |
is_contig = True | |
if is_contig: | |
# We have a ::1 somewhere, see if we're C or Fortran contiguous | |
if contig_dim == len(axes) - 1: | |
is_c_contig = True | |
else: | |
is_f_contig = True | |
if contig_dim and not axes_specs[contig_dim - 1][0] in ('full', 'ptr'): | |
raise CompileError(axes[contig_dim].pos, | |
"Fortran contiguous specifier must follow an indirect dimension") | |
if is_c_contig: | |
# Contiguous in the last dimension, find the last indirect dimension | |
contig_dim = -1 | |
for idx, (access, packing) in enumerate(reversed(axes_specs)): | |
if access in ('ptr', 'full'): | |
contig_dim = len(axes) - idx - 1 | |
# Replace 'strided' with 'follow' for any dimension following the last | |
# indirect dimension, the first dimension or the dimension following | |
# the ::1. | |
# int[::indirect, ::1, :, :] | |
# ^ ^ | |
# int[::indirect, :, :, ::1] | |
# ^ ^ | |
start = contig_dim + 1 | |
stop = len(axes) - is_c_contig | |
for idx, (access, packing) in enumerate(axes_specs[start:stop]): | |
idx = contig_dim + 1 + idx | |
if access != 'direct': | |
raise CompileError(axes[idx].pos, | |
"Indirect dimension may not follow " | |
"Fortran contiguous dimension") | |
if packing == 'contig': | |
raise CompileError(axes[idx].pos, | |
"Dimension may not be contiguous") | |
axes_specs[idx] = (access, cf_packing) | |
if is_c_contig: | |
# For C contiguity, we need to fix the 'contig' dimension | |
# after the loop | |
a, p = axes_specs[-1] | |
axes_specs[-1] = a, 'contig' | |
validate_axes_specs([axis.start.pos for axis in axes], | |
axes_specs, | |
is_c_contig, | |
is_f_contig) | |
return axes_specs | |
def validate_axes(pos, axes): | |
if len(axes) >= Options.buffer_max_dims: | |
error(pos, "More dimensions than the maximum number" | |
" of buffer dimensions were used.") | |
return False | |
return True | |
def is_cf_contig(specs): | |
is_c_contig = is_f_contig = False | |
if len(specs) == 1 and specs == [('direct', 'contig')]: | |
is_c_contig = True | |
elif (specs[-1] == ('direct','contig') and | |
all(axis == ('direct','follow') for axis in specs[:-1])): | |
# c_contiguous: 'follow', 'follow', ..., 'follow', 'contig' | |
is_c_contig = True | |
elif (len(specs) > 1 and | |
specs[0] == ('direct','contig') and | |
all(axis == ('direct','follow') for axis in specs[1:])): | |
# f_contiguous: 'contig', 'follow', 'follow', ..., 'follow' | |
is_f_contig = True | |
return is_c_contig, is_f_contig | |
def get_mode(specs): | |
is_c_contig, is_f_contig = is_cf_contig(specs) | |
if is_c_contig: | |
return 'c' | |
elif is_f_contig: | |
return 'fortran' | |
for access, packing in specs: | |
if access in ('ptr', 'full'): | |
return 'full' | |
return 'strided' | |
view_constant_to_access_packing = { | |
'generic': ('full', 'strided'), | |
'strided': ('direct', 'strided'), | |
'indirect': ('ptr', 'strided'), | |
'generic_contiguous': ('full', 'contig'), | |
'contiguous': ('direct', 'contig'), | |
'indirect_contiguous': ('ptr', 'contig'), | |
} | |
def validate_axes_specs(positions, specs, is_c_contig, is_f_contig): | |
packing_specs = ('contig', 'strided', 'follow') | |
access_specs = ('direct', 'ptr', 'full') | |
# is_c_contig, is_f_contig = is_cf_contig(specs) | |
has_contig = has_follow = has_strided = has_generic_contig = False | |
last_indirect_dimension = -1 | |
for idx, (access, packing) in enumerate(specs): | |
if access == 'ptr': | |
last_indirect_dimension = idx | |
for idx, (pos, (access, packing)) in enumerate(zip(positions, specs)): | |
if not (access in access_specs and | |
packing in packing_specs): | |
raise CompileError(pos, "Invalid axes specification.") | |
if packing == 'strided': | |
has_strided = True | |
elif packing == 'contig': | |
if has_contig: | |
raise CompileError(pos, "Only one direct contiguous " | |
"axis may be specified.") | |
valid_contig_dims = last_indirect_dimension + 1, len(specs) - 1 | |
if idx not in valid_contig_dims and access != 'ptr': | |
if last_indirect_dimension + 1 != len(specs) - 1: | |
dims = "dimensions %d and %d" % valid_contig_dims | |
else: | |
dims = "dimension %d" % valid_contig_dims[0] | |
raise CompileError(pos, "Only %s may be contiguous and direct" % dims) | |
has_contig = access != 'ptr' | |
elif packing == 'follow': | |
if has_strided: | |
raise CompileError(pos, "A memoryview cannot have both follow and strided axis specifiers.") | |
if not (is_c_contig or is_f_contig): | |
raise CompileError(pos, "Invalid use of the follow specifier.") | |
if access in ('ptr', 'full'): | |
has_strided = False | |
def _get_resolved_spec(env, spec): | |
# spec must be a NameNode or an AttributeNode | |
if isinstance(spec, NameNode): | |
return _resolve_NameNode(env, spec) | |
elif isinstance(spec, AttributeNode): | |
return _resolve_AttributeNode(env, spec) | |
else: | |
raise CompileError(spec.pos, INVALID_ERR) | |
def _resolve_NameNode(env, node): | |
try: | |
resolved_name = env.lookup(node.name).name | |
except AttributeError: | |
raise CompileError(node.pos, INVALID_ERR) | |
viewscope = env.global_scope().context.cython_scope.viewscope | |
entry = viewscope.lookup(resolved_name) | |
if entry is None: | |
raise CompileError(node.pos, NOT_CIMPORTED_ERR) | |
return entry | |
def _resolve_AttributeNode(env, node): | |
path = [] | |
while isinstance(node, AttributeNode): | |
path.insert(0, node.attribute) | |
node = node.obj | |
if isinstance(node, NameNode): | |
path.insert(0, node.name) | |
else: | |
raise CompileError(node.pos, EXPR_ERR) | |
modnames = path[:-1] | |
# must be at least 1 module name, o/w not an AttributeNode. | |
assert modnames | |
scope = env | |
for modname in modnames: | |
mod = scope.lookup(modname) | |
if not mod or not mod.as_module: | |
raise CompileError( | |
node.pos, "undeclared name not builtin: %s" % modname) | |
scope = mod.as_module | |
entry = scope.lookup(path[-1]) | |
if not entry: | |
raise CompileError(node.pos, "No such attribute '%s'" % path[-1]) | |
return entry | |
# | |
### Utility loading | |
# | |
def load_memview_cy_utility(util_code_name, context=None, **kwargs): | |
return CythonUtilityCode.load(util_code_name, "MemoryView.pyx", | |
context=context, **kwargs) | |
def load_memview_c_utility(util_code_name, context=None, **kwargs): | |
if context is None: | |
return UtilityCode.load(util_code_name, "MemoryView_C.c", **kwargs) | |
else: | |
return TempitaUtilityCode.load(util_code_name, "MemoryView_C.c", | |
context=context, **kwargs) | |
def use_cython_array_utility_code(env): | |
cython_scope = env.global_scope().context.cython_scope | |
cython_scope.load_cythonscope() | |
cython_scope.viewscope.lookup('array_cwrapper').used = True | |
context = { | |
'memview_struct_name': memview_objstruct_cname, | |
'max_dims': Options.buffer_max_dims, | |
'memviewslice_name': memviewslice_cname, | |
'memslice_init': PyrexTypes.MemoryViewSliceType.default_value, | |
'THREAD_LOCKS_PREALLOCATED': 8, | |
} | |
memviewslice_declare_code = load_memview_c_utility( | |
"MemviewSliceStruct", | |
context=context, | |
requires=[]) | |
atomic_utility = load_memview_c_utility("Atomics", context) | |
memviewslice_init_code = load_memview_c_utility( | |
"MemviewSliceInit", | |
context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims), | |
requires=[memviewslice_declare_code, | |
atomic_utility], | |
) | |
memviewslice_index_helpers = load_memview_c_utility("MemviewSliceIndex") | |
typeinfo_to_format_code = load_memview_cy_utility( | |
"BufferFormatFromTypeInfo", requires=[Buffer._typeinfo_to_format_code]) | |
is_contig_utility = load_memview_c_utility("MemviewSliceIsContig", context) | |
overlapping_utility = load_memview_c_utility("OverlappingSlices", context) | |
copy_contents_new_utility = load_memview_c_utility( | |
"MemviewSliceCopyTemplate", | |
context, | |
requires=[], # require cython_array_utility_code | |
) | |
view_utility_code = load_memview_cy_utility( | |
"View.MemoryView", | |
context=context, | |
requires=[Buffer.GetAndReleaseBufferUtilityCode(), | |
Buffer.buffer_struct_declare_code, | |
Buffer.buffer_formats_declare_code, | |
memviewslice_init_code, | |
is_contig_utility, | |
overlapping_utility, | |
copy_contents_new_utility, | |
], | |
) | |
view_utility_allowlist = ('array', 'memoryview', 'array_cwrapper', | |
'generic', 'strided', 'indirect', 'contiguous', | |
'indirect_contiguous') | |
memviewslice_declare_code.requires.append(view_utility_code) | |
copy_contents_new_utility.requires.append(view_utility_code) | |