Commit b7d6e693 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge remote-tracking branch 'main/release'

parents 449e3b58 b8a87f10
...@@ -13,6 +13,7 @@ cython.declare(error=object, warning=object, warn_once=object, InternalError=obj ...@@ -13,6 +13,7 @@ cython.declare(error=object, warning=object, warn_once=object, InternalError=obj
debug_disposal_code=object, debug_temp_alloc=object, debug_coercion=object) debug_disposal_code=object, debug_temp_alloc=object, debug_coercion=object)
import sys import sys
import copy
import operator import operator
from Errors import error, warning, warn_once, InternalError, CompileError from Errors import error, warning, warn_once, InternalError, CompileError
...@@ -33,6 +34,8 @@ import Symtab ...@@ -33,6 +34,8 @@ import Symtab
import Options import Options
from Cython import Utils from Cython import Utils
from Annotate import AnnotationItem from Annotate import AnnotationItem
from NumpySupport import numpy_transform_attribute_node, \
should_apply_numpy_hack
from Cython.Debugging import print_call_chain from Cython.Debugging import print_call_chain
from DebugFlags import debug_disposal_code, debug_temp_alloc, \ from DebugFlags import debug_disposal_code, debug_temp_alloc, \
...@@ -488,11 +491,21 @@ class ExprNode(Node): ...@@ -488,11 +491,21 @@ class ExprNode(Node):
# ---------------- Code Generation ----------------- # ---------------- Code Generation -----------------
def make_owned_reference(self, code): def make_owned_reference(self, code):
# If result is a pyobject, make sure we own """
# a reference to it. If result is a pyobject, make sure we own a reference to it.
If the result is in a temp, it is already a new reference.
"""
if self.type.is_pyobject and not self.result_in_temp(): if self.type.is_pyobject and not self.result_in_temp():
code.put_incref(self.result(), self.ctype()) code.put_incref(self.result(), self.ctype())
def make_owned_memoryviewslice(self, code):
"""
Make sure we own the reference to this memoryview slice.
"""
if not self.result_in_temp():
code.put_incref_memoryviewslice(self.result(),
have_gil=self.in_nogil_context)
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
code.mark_pos(self.pos) code.mark_pos(self.pos)
...@@ -623,7 +636,7 @@ class ExprNode(Node): ...@@ -623,7 +636,7 @@ class ExprNode(Node):
return self return self
if src_type.is_fused: if src_type.is_fused:
error(self.pos, "Type is not specific") error(self.pos, "Type is not specialized")
else: else:
error(self.pos, "Cannot coerce to a type that is not specialized") error(self.pos, "Cannot coerce to a type that is not specialized")
...@@ -2586,8 +2599,9 @@ class IndexNode(ExprNode): ...@@ -2586,8 +2599,9 @@ class IndexNode(ExprNode):
self.nogil = env.nogil self.nogil = env.nogil
if buffer_access or self.memslice_index: if buffer_access or self.memslice_index:
if self.base.type.is_memoryviewslice and not self.base.is_name: #if self.base.type.is_memoryviewslice and not self.base.is_name:
self.base = self.base.coerce_to_temp(env) # self.base = self.base.coerce_to_temp(env)
self.base = self.base.coerce_to_simple(env)
self.indices = indices self.indices = indices
self.index = None self.index = None
...@@ -2724,7 +2738,7 @@ class IndexNode(ExprNode): ...@@ -2724,7 +2738,7 @@ class IndexNode(ExprNode):
specific_types = [] specific_types = []
positions = [] positions = []
if self.index.is_name: if self.index.is_name or self.index.is_attribute:
positions.append(self.index.pos) positions.append(self.index.pos)
specific_types.append(self.index.analyse_as_type(env)) specific_types.append(self.index.analyse_as_type(env))
elif isinstance(self.index, TupleNode): elif isinstance(self.index, TupleNode):
...@@ -3042,7 +3056,8 @@ class IndexNode(ExprNode): ...@@ -3042,7 +3056,8 @@ class IndexNode(ExprNode):
if self.base.is_name: if self.base.is_name:
entry = self.base.entry entry = self.base.entry
else: else:
assert self.base.is_temp # SimpleCallNode is_simple is not consistent with coerce_to_simple
assert self.base.is_simple() or self.base.is_temp
cname = self.base.result() cname = self.base.result()
entry = Symtab.Entry(cname, cname, self.base.type, self.base.pos) entry = Symtab.Entry(cname, cname, self.base.type, self.base.pos)
...@@ -3452,6 +3467,19 @@ class SliceNode(ExprNode): ...@@ -3452,6 +3467,19 @@ class SliceNode(ExprNode):
if self.is_literal: if self.is_literal:
code.put_giveref(self.py_result()) code.put_giveref(self.py_result())
def __deepcopy__(self, memo):
"""
There is a copy bug in python 2.4 for slice objects.
"""
return SliceNode(
self.pos,
start=copy.deepcopy(self.start, memo),
stop=copy.deepcopy(self.stop, memo),
step=copy.deepcopy(self.step, memo),
is_temp=self.is_temp,
is_literal=self.is_literal,
constant_result=self.constant_result)
class CallNode(ExprNode): class CallNode(ExprNode):
...@@ -4417,9 +4445,13 @@ class AttributeNode(ExprNode): ...@@ -4417,9 +4445,13 @@ class AttributeNode(ExprNode):
if entry: if entry:
if obj_type.is_extension_type and entry.name == "__weakref__": if obj_type.is_extension_type and entry.name == "__weakref__":
error(self.pos, "Illegal use of special attribute __weakref__") error(self.pos, "Illegal use of special attribute __weakref__")
# methods need the normal attribute lookup
# def methods need the normal attribute lookup
# because they do not have struct entries # because they do not have struct entries
if entry.is_variable or entry.is_cmethod: # fused function go through assignment synthesis
# (foo = pycfunction(foo_func_obj)) and need to go through
# regular Python lookup as well
if (entry.is_variable and not entry.fused_cfunction) or entry.is_cmethod:
self.type = entry.type self.type = entry.type
self.member = entry.cname self.member = entry.cname
return return
...@@ -4429,10 +4461,8 @@ class AttributeNode(ExprNode): ...@@ -4429,10 +4461,8 @@ class AttributeNode(ExprNode):
# attribute. # attribute.
pass pass
# NumPy hack # NumPy hack
if (getattr(self.obj, 'type', None) and if (getattr(self.obj, 'type', None) and obj_type.is_extension_type
obj_type.is_extension_type and and should_apply_numpy_hack(obj_type)):
obj_type.objstruct_cname == 'PyArrayObject'):
from NumpySupport import numpy_transform_attribute_node
replacement_node = numpy_transform_attribute_node(self) replacement_node = numpy_transform_attribute_node(self)
# Since we can't actually replace our node yet, we only grasp its # Since we can't actually replace our node yet, we only grasp its
# type, and then the replacement happens in # type, and then the replacement happens in
...@@ -4440,7 +4470,6 @@ class AttributeNode(ExprNode): ...@@ -4440,7 +4470,6 @@ class AttributeNode(ExprNode):
self.type = replacement_node.type self.type = replacement_node.type
if replacement_node is not self: if replacement_node is not self:
return return
# If we get here, the base object is not a struct/union/extension # If we get here, the base object is not a struct/union/extension
# type, or it is an extension type and the attribute is either not # type, or it is an extension type and the attribute is either not
# declared or is declared as a Python method. Treat it as a Python # declared or is declared as a Python method. Treat it as a Python
...@@ -7244,7 +7273,7 @@ class CythonArrayNode(ExprNode): ...@@ -7244,7 +7273,7 @@ class CythonArrayNode(ExprNode):
else: else:
return error() return error()
if not base_type.same_as(array_dtype): if not (base_type.same_as(array_dtype) or base_type.is_void):
return error(self.operand.pos, ERR_BASE_TYPE) return error(self.operand.pos, ERR_BASE_TYPE)
elif self.operand.type.is_array and len(array_dimension_sizes) != ndim: elif self.operand.type.is_array and len(array_dimension_sizes) != ndim:
return error(self.operand.pos, return error(self.operand.pos,
...@@ -8912,6 +8941,10 @@ class CoercionNode(ExprNode): ...@@ -8912,6 +8941,10 @@ class CoercionNode(ExprNode):
code.annotate((file, line, col-1), AnnotationItem(style='coerce', tag='coerce', text='[%s] to [%s]' % (self.arg.type, self.type))) code.annotate((file, line, col-1), AnnotationItem(style='coerce', tag='coerce', text='[%s] to [%s]' % (self.arg.type, self.type)))
class CoerceToMemViewSliceNode(CoercionNode): class CoerceToMemViewSliceNode(CoercionNode):
"""
Coerce an object to a memoryview slice. This holds a new reference in
a managed temp.
"""
def __init__(self, arg, dst_type, env): def __init__(self, arg, dst_type, env):
assert dst_type.is_memoryviewslice assert dst_type.is_memoryviewslice
......
...@@ -24,6 +24,10 @@ module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_ ...@@ -24,6 +24,10 @@ module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_
verbose = 0 verbose = 0
standard_include_path = os.path.abspath(os.path.normpath(
os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
class CompilationData(object): class CompilationData(object):
# Bundles the information that is passed from transform to transform. # Bundles the information that is passed from transform to transform.
# (For now, this is only) # (For now, this is only)
...@@ -70,8 +74,6 @@ class Context(object): ...@@ -70,8 +74,6 @@ class Context(object):
self.pxds = {} # full name -> node tree self.pxds = {} # full name -> node tree
standard_include_path = os.path.abspath(os.path.normpath(
os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
self.include_directories = include_directories + [standard_include_path] self.include_directories = include_directories + [standard_include_path]
self.set_language_level(language_level) self.set_language_level(language_level)
......
...@@ -86,7 +86,7 @@ def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code, ...@@ -86,7 +86,7 @@ def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code,
"We can avoid decreffing the lhs if we know it is the first assignment" "We can avoid decreffing the lhs if we know it is the first assignment"
assert rhs.type.is_memoryviewslice assert rhs.type.is_memoryviewslice
pretty_rhs = isinstance(rhs, NameNode) or rhs.result_in_temp() pretty_rhs = rhs.result_in_temp() or rhs.is_simple()
if pretty_rhs: if pretty_rhs:
rhstmp = rhs.result() rhstmp = rhs.result()
else: else:
...@@ -106,19 +106,11 @@ def put_assign_to_memviewslice(lhs_cname, rhs, rhs_cname, memviewslicetype, code ...@@ -106,19 +106,11 @@ def put_assign_to_memviewslice(lhs_cname, rhs, rhs_cname, memviewslicetype, code
if not first_assignment: if not first_assignment:
code.put_xdecref_memoryviewslice(lhs_cname, have_gil=have_gil) code.put_xdecref_memoryviewslice(lhs_cname, have_gil=have_gil)
if rhs.is_name: if not rhs.result_in_temp():
code.put_incref_memoryviewslice(rhs_cname, have_gil=have_gil) rhs.make_owned_memoryviewslice(code)
code.putln("%s = %s;" % (lhs_cname, rhs_cname)) code.putln("%s = %s;" % (lhs_cname, rhs_cname))
#code.putln("%s.memview = %s.memview;" % (lhs_cname, rhs_cname))
#code.putln("%s.data = %s.data;" % (lhs_cname, rhs_cname))
#for i in range(memviewslicetype.ndim):
# tup = (lhs_cname, i, rhs_cname, i)
# code.putln("%s.shape[%d] = %s.shape[%d];" % tup)
# code.putln("%s.strides[%d] = %s.strides[%d];" % tup)
# code.putln("%s.suboffsets[%d] = %s.suboffsets[%d];" % tup)
def get_buf_flags(specs): def get_buf_flags(specs):
is_c_contig, is_f_contig = is_cf_contig(specs) is_c_contig, is_f_contig = is_cf_contig(specs)
...@@ -888,7 +880,8 @@ context = { ...@@ -888,7 +880,8 @@ context = {
memviewslice_declare_code = load_memview_c_utility( memviewslice_declare_code = load_memview_c_utility(
"MemviewSliceStruct", "MemviewSliceStruct",
proto_block='utility_code_proto_before_types', proto_block='utility_code_proto_before_types',
context=context) context=context,
requires=[])
atomic_utility = load_memview_c_utility("Atomics", context, atomic_utility = load_memview_c_utility("Atomics", context,
proto_block='utility_code_proto_before_types') proto_block='utility_code_proto_before_types')
...@@ -931,4 +924,5 @@ view_utility_whitelist = ('array', 'memoryview', 'array_cwrapper', ...@@ -931,4 +924,5 @@ view_utility_whitelist = ('array', 'memoryview', 'array_cwrapper',
'generic', 'strided', 'indirect', 'contiguous', 'generic', 'strided', 'indirect', 'contiguous',
'indirect_contiguous') 'indirect_contiguous')
memviewslice_declare_code.requires.append(view_utility_code)
copy_contents_new_utility.requires.append(view_utility_code) copy_contents_new_utility.requires.append(view_utility_code)
\ No newline at end of file
...@@ -988,18 +988,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -988,18 +988,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type = scope.parent_type type = scope.parent_type
base_type = type.base_type base_type = type.base_type
py_attrs = [] have_entries, (py_attrs, py_buffers, memoryview_slices) = \
memviewslice_attrs = [] scope.get_refcounted_entries(include_weakref=True)
py_buffers = []
for entry in scope.var_entries:
if entry.type.is_pyobject:
py_attrs.append(entry)
elif entry.type.is_memoryviewslice:
memviewslice_attrs.append(entry)
elif entry.type == PyrexTypes.c_py_buffer_type:
py_buffers.append(entry)
need_self_cast = type.vtabslot_cname or py_attrs or memviewslice_attrs or py_buffers need_self_cast = type.vtabslot_cname or have_entries
code.putln("") code.putln("")
code.putln( code.putln(
"static PyObject *%s(PyTypeObject *t, PyObject *a, PyObject *k) {" "static PyObject *%s(PyTypeObject *t, PyObject *a, PyObject *k) {"
...@@ -1044,7 +1036,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1044,7 +1036,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
else: else:
code.put_init_var_to_py_none(entry, "p->%s", nanny=False) code.put_init_var_to_py_none(entry, "p->%s", nanny=False)
for entry in memviewslice_attrs: for entry in memoryview_slices:
code.putln("p->%s.data = NULL;" % entry.cname) code.putln("p->%s.data = NULL;" % entry.cname)
code.putln("p->%s.memview = NULL;" % entry.cname) code.putln("p->%s.memview = NULL;" % entry.cname)
...@@ -1081,18 +1073,25 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1081,18 +1073,25 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln( code.putln(
"static void %s(PyObject *o) {" "static void %s(PyObject *o) {"
% scope.mangle_internal("tp_dealloc")) % scope.mangle_internal("tp_dealloc"))
py_attrs = []
weakref_slot = scope.lookup_here("__weakref__") weakref_slot = scope.lookup_here("__weakref__")
for entry in scope.var_entries: _, (py_attrs, _, memoryview_slices) = scope.get_refcounted_entries()
if entry.type.is_pyobject and entry is not weakref_slot:
py_attrs.append(entry) if py_attrs or memoryview_slices or weakref_slot in scope.var_entries:
if py_attrs or weakref_slot in scope.var_entries:
self.generate_self_cast(scope, code) self.generate_self_cast(scope, code)
# call the user's __dealloc__
self.generate_usr_dealloc_call(scope, code) self.generate_usr_dealloc_call(scope, code)
if weakref_slot in scope.var_entries: if weakref_slot in scope.var_entries:
code.putln("if (p->__weakref__) PyObject_ClearWeakRefs(o);") code.putln("if (p->__weakref__) PyObject_ClearWeakRefs(o);")
for entry in py_attrs: for entry in py_attrs:
code.put_xdecref("p->%s" % entry.cname, entry.type, nanny=False) code.put_xdecref("p->%s" % entry.cname, entry.type, nanny=False)
for entry in memoryview_slices:
code.put_xdecref_memoryviewslice("p->%s" % entry.cname,
have_gil=True)
if base_type: if base_type:
tp_dealloc = TypeSlots.get_base_slot_function(scope, tp_slot) tp_dealloc = TypeSlots.get_base_slot_function(scope, tp_slot)
if tp_dealloc is None: if tp_dealloc is None:
...@@ -1139,13 +1138,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1139,13 +1138,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"static int %s(PyObject *o, visitproc v, void *a) {" "static int %s(PyObject *o, visitproc v, void *a) {"
% slot_func) % slot_func)
py_attrs = [] have_entries, (py_attrs, py_buffers,
py_buffers = [] memoryview_slices) = scope.get_refcounted_entries()
for entry in scope.var_entries:
if entry.type.is_pyobject and entry.name != "__weakref__":
py_attrs.append(entry)
if entry.type == PyrexTypes.c_py_buffer_type:
py_buffers.append(entry)
if base_type or py_attrs: if base_type or py_attrs:
code.putln("int e;") code.putln("int e;")
...@@ -1178,9 +1172,16 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1178,9 +1172,16 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln( code.putln(
"}") "}")
for entry in py_buffers: for entry in py_buffers + memoryview_slices:
code.putln("if (p->%s.obj) {" % entry.cname) if entry.type == PyrexTypes.c_py_buffer_type:
code.putln( "e = (*v)(p->%s.obj, a); if (e) return e;" % entry.cname) cname = entry.cname + ".obj"
else:
# traverse the memoryview object, which should traverse the
# object exposing the buffer
cname = entry.cname + ".memview"
code.putln("if (p->%s) {" % cname)
code.putln( "e = (*v)(p->%s, a); if (e) return e;" % cname)
code.putln("}") code.putln("}")
if cclass_entry.cname == '__pyx_memoryviewslice': if cclass_entry.cname == '__pyx_memoryviewslice':
......
...@@ -439,6 +439,10 @@ class CNameDeclaratorNode(CDeclaratorNode): ...@@ -439,6 +439,10 @@ class CNameDeclaratorNode(CDeclaratorNode):
else: else:
self.name = base_type.declaration_code("", for_display=1, pyrex=1) self.name = base_type.declaration_code("", for_display=1, pyrex=1)
base_type = py_object_type base_type = py_object_type
if base_type.is_fused and env.fused_to_specific:
base_type = base_type.specialize(env.fused_to_specific)
self.type = base_type self.type = base_type
return self, base_type return self, base_type
...@@ -982,6 +986,9 @@ class TemplatedTypeNode(CBaseTypeNode): ...@@ -982,6 +986,9 @@ class TemplatedTypeNode(CBaseTypeNode):
dimension = dimension) dimension = dimension)
self.type = self.array_declarator.analyse(base_type, env)[1] self.type = self.array_declarator.analyse(base_type, env)[1]
if self.type.is_fused and env.fused_to_specific:
self.type = self.type.specialize(env.fused_to_specific)
return self.type return self.type
class CComplexBaseTypeNode(CBaseTypeNode): class CComplexBaseTypeNode(CBaseTypeNode):
...@@ -1031,8 +1038,8 @@ class FusedTypeNode(CBaseTypeNode): ...@@ -1031,8 +1038,8 @@ class FusedTypeNode(CBaseTypeNode):
else: else:
types.append(type) types.append(type)
if len(self.types) == 1: # if len(self.types) == 1:
return types[0] # return types[0]
return PyrexTypes.FusedType(types, name=self.name) return PyrexTypes.FusedType(types, name=self.name)
...@@ -2279,7 +2286,6 @@ class FusedCFuncDefNode(StatListNode): ...@@ -2279,7 +2286,6 @@ class FusedCFuncDefNode(StatListNode):
else: else:
node.py_func.fused_py_func = self.py_func node.py_func.fused_py_func = self.py_func
node.entry.as_variable = self.py_func.entry node.entry.as_variable = self.py_func.entry
# Copy the nodes as AnalyseDeclarationsTransform will prepend # Copy the nodes as AnalyseDeclarationsTransform will prepend
# self.py_func to self.stats, as we only want specialized # self.py_func to self.stats, as we only want specialized
# CFuncDefNodes in self.nodes # CFuncDefNodes in self.nodes
...@@ -2294,9 +2300,9 @@ class FusedCFuncDefNode(StatListNode): ...@@ -2294,9 +2300,9 @@ class FusedCFuncDefNode(StatListNode):
Create a copy of the original def or lambda function for specialized Create a copy of the original def or lambda function for specialized
versions. versions.
""" """
fused_types = PyrexTypes.unique( fused_compound_types = PyrexTypes.unique(
[arg.type for arg in self.node.args if arg.type.is_fused]) [arg.type for arg in self.node.args if arg.type.is_fused])
permutations = PyrexTypes.get_all_specialized_permutations(fused_types) permutations = PyrexTypes.get_all_specialized_permutations(fused_compound_types)
if self.node.entry in env.pyfunc_entries: if self.node.entry in env.pyfunc_entries:
env.pyfunc_entries.remove(self.node.entry) env.pyfunc_entries.remove(self.node.entry)
...@@ -2311,7 +2317,7 @@ class FusedCFuncDefNode(StatListNode): ...@@ -2311,7 +2317,7 @@ class FusedCFuncDefNode(StatListNode):
copied_node.analyse_declarations(env) copied_node.analyse_declarations(env)
self.create_new_local_scope(copied_node, env, fused_to_specific) self.create_new_local_scope(copied_node, env, fused_to_specific)
self.specialize_copied_def(copied_node, cname, self.node.entry, self.specialize_copied_def(copied_node, cname, self.node.entry,
fused_to_specific, fused_types) fused_to_specific, fused_compound_types)
PyrexTypes.specialize_entry(copied_node.entry, cname) PyrexTypes.specialize_entry(copied_node.entry, cname)
copied_node.entry.used = True copied_node.entry.used = True
...@@ -2420,11 +2426,9 @@ class FusedCFuncDefNode(StatListNode): ...@@ -2420,11 +2426,9 @@ class FusedCFuncDefNode(StatListNode):
"""Specialize the copy of a DefNode given the copied node, """Specialize the copy of a DefNode given the copied node,
the specialization cname and the original DefNode entry""" the specialization cname and the original DefNode entry"""
type_strings = [ type_strings = [
fused_type.specialize(f2s).typeof_name() PyrexTypes.specialization_signature_string(fused_type, f2s)
for fused_type in fused_types for fused_type in fused_types
] ]
#type_strings = [f2s[fused_type].typeof_name()
# for fused_type in fused_types]
node.specialized_signature_string = ', '.join(type_strings) node.specialized_signature_string = ', '.join(type_strings)
...@@ -2574,7 +2578,7 @@ def __pyx_fused_cpdef(signatures, args, kwargs): ...@@ -2574,7 +2578,7 @@ def __pyx_fused_cpdef(signatures, args, kwargs):
candidates = [] candidates = []
for sig in signatures: for sig in signatures:
match_found = True match_found = [x for x in dest_sig if x]
for src_type, dst_type in zip(sig.strip('()').split(', '), dest_sig): for src_type, dst_type in zip(sig.strip('()').split(', '), dest_sig):
if dst_type is not None and match_found: if dst_type is not None and match_found:
match_found = src_type == dst_type match_found = src_type == dst_type
......
...@@ -2,20 +2,31 @@ ...@@ -2,20 +2,31 @@
# the NumPy ABI changed so that the shape, ndim, strides, etc. fields were # the NumPy ABI changed so that the shape, ndim, strides, etc. fields were
# no longer available, however the use of these were so entrenched in # no longer available, however the use of these were so entrenched in
# Cython codes # Cython codes
import os
import PyrexTypes
import ExprNodes
from StringEncoding import EncodedString from StringEncoding import EncodedString
def should_apply_numpy_hack(obj_type):
if not obj_type.is_extension_type or obj_type.objstruct_cname != 'PyArrayObject':
return False
from Scanning import FileSourceDescriptor
from Main import standard_include_path
type_source = obj_type.pos[0]
if isinstance(type_source, FileSourceDescriptor):
type_source_path = os.path.abspath(os.path.normpath(type_source.filename))
return type_source_path == os.path.join(standard_include_path, 'numpy.pxd')
else:
return False
def numpy_transform_attribute_node(node): def numpy_transform_attribute_node(node):
import PyrexTypes
import ExprNodes
assert isinstance(node, ExprNodes.AttributeNode) assert isinstance(node, ExprNodes.AttributeNode)
if node.obj.type.objstruct_cname != 'PyArrayObject': if node.obj.type.objstruct_cname != 'PyArrayObject':
return node return node
pos = node.pos pos = node.pos
numpy_pxd_scope = node.obj.entry.type.scope.parent_scope numpy_pxd_scope = node.obj.type.scope.parent_scope
def macro_call_node(numpy_macro_name): def macro_call_node(numpy_macro_name):
array_node = node.obj array_node = node.obj
......
...@@ -18,7 +18,8 @@ from Cython.Compiler.TreeFragment import TreeFragment ...@@ -18,7 +18,8 @@ from Cython.Compiler.TreeFragment import TreeFragment
from Cython.Compiler.StringEncoding import EncodedString from Cython.Compiler.StringEncoding import EncodedString
from Cython.Compiler.Errors import error, warning, CompileError, InternalError from Cython.Compiler.Errors import error, warning, CompileError, InternalError
from Cython.Compiler.Code import UtilityCode from Cython.Compiler.Code import UtilityCode
from Cython.Compiler.NumpySupport import (should_apply_numpy_hack,
numpy_transform_attribute_node)
import copy import copy
...@@ -1495,12 +1496,15 @@ if VALUE is not None: ...@@ -1495,12 +1496,15 @@ if VALUE is not None:
self.fused_function = None self.fused_function = None
if node.py_func: if node.py_func:
# Create PyCFunction nodes for each specialization
node.stats.insert(0, node.py_func) node.stats.insert(0, node.py_func)
node.py_func = self.visit(node.py_func) node.py_func = self.visit(node.py_func)
pycfunc = ExprNodes.PyCFunctionNode.from_defnode(node.py_func, pycfunc = ExprNodes.PyCFunctionNode.from_defnode(node.py_func,
True) True)
pycfunc = ExprNodes.ProxyNode(pycfunc.coerce_to_temp(env)) pycfunc = ExprNodes.ProxyNode(pycfunc.coerce_to_temp(env))
node.resulting_fused_function = pycfunc node.resulting_fused_function = pycfunc
# Create assignment node for our def function
node.fused_func_assignment = self._create_assignment( node.fused_func_assignment = self._create_assignment(
node.py_func, ExprNodes.CloneNode(pycfunc), env) node.py_func, ExprNodes.CloneNode(pycfunc), env)
else: else:
...@@ -1781,7 +1785,7 @@ class AnalyseExpressionsTransform(CythonTransform): ...@@ -1781,7 +1785,7 @@ class AnalyseExpressionsTransform(CythonTransform):
""" """
self.visit_Node(node) self.visit_Node(node)
if node.is_fused_index and node.type is not PyrexTypes.error_type: if node.is_fused_index and not node.type.is_error:
node = node.base node = node.base
elif node.memslice_ellipsis_noop: elif node.memslice_ellipsis_noop:
# memoryviewslice[...] expression, drop the IndexNode # memoryviewslice[...] expression, drop the IndexNode
...@@ -1792,8 +1796,8 @@ class AnalyseExpressionsTransform(CythonTransform): ...@@ -1792,8 +1796,8 @@ class AnalyseExpressionsTransform(CythonTransform):
self.visitchildren(node) self.visitchildren(node)
type = node.obj.type type = node.obj.type
if type.is_extension_type and type.objstruct_cname == 'PyArrayObject': if (not node.type.is_error and type.is_extension_type and
from NumpySupport import numpy_transform_attribute_node should_apply_numpy_hack(type)):
node = numpy_transform_attribute_node(node) node = numpy_transform_attribute_node(node)
self.visitchildren(node) self.visitchildren(node)
...@@ -2603,8 +2607,8 @@ class ReplaceFusedTypeChecks(VisitorTransform): ...@@ -2603,8 +2607,8 @@ class ReplaceFusedTypeChecks(VisitorTransform):
else: else:
types = PyrexTypes.get_specialized_types(type2) types = PyrexTypes.get_specialized_types(type2)
for specific_type in types: for specialized_type in types:
if type1.same_as(specific_type): if type1.same_as(specialized_type):
if op == 'in': if op == 'in':
return true_node return true_node
else: else:
......
...@@ -767,12 +767,46 @@ class BufferType(BaseType): ...@@ -767,12 +767,46 @@ class BufferType(BaseType):
def as_argument_type(self): def as_argument_type(self):
return self return self
def specialize(self, values):
dtype = self.dtype.specialize(values)
if dtype is not self.dtype:
return BufferType(self.base, dtype, self.ndim, self.mode,
self.negative_indices, self.cast)
return self
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self.base, name) return getattr(self.base, name)
def __repr__(self): def __repr__(self):
return "<BufferType %r>" % self.base return "<BufferType %r>" % self.base
def __str__(self):
# avoid ', ', as fused functions split the signature string on ', '
if self.cast:
cast_str = ',cast=True'
else:
cast_str = ''
return "%s[%s,ndim=%d%s]" % (self.base, self.dtype, self.ndim,
cast_str)
def assignable_from(self, other_type):
if other_type.is_buffer:
return (self.same_as(other_type, compare_base=False) and
self.base.assignable_from(other_type.base))
return self.base.assignable_from(other_type)
def same_as(self, other_type, compare_base=True):
if not other_type.is_buffer:
return other_type.same_as(self.base)
return (self.dtype.same_as(other_type.dtype) and
self.ndim == other_type.ndim and
self.mode == other_type.mode and
self.cast == other_type.cast and
(not compare_base or self.base.same_as(other_type.base)))
class PyObjectType(PyrexType): class PyObjectType(PyrexType):
# #
...@@ -2631,6 +2665,31 @@ def _get_all_specialized_permutations(fused_types, id="", f2s=()): ...@@ -2631,6 +2665,31 @@ def _get_all_specialized_permutations(fused_types, id="", f2s=()):
return result return result
def specialization_signature_string(fused_compound_type, fused_to_specific):
"""
Return the signature for a specialization of a fused type. e.g.
floating[:] ->
'float' or 'double'
cdef fused ft:
float[:]
double[:]
ft ->
'float[:]' or 'double[:]'
integral func(floating) ->
'int (*func)(float)' or ...
"""
fused_types = fused_compound_type.get_fused_types()
if len(fused_types) == 1:
fused_type = fused_types[0]
else:
fused_type = fused_compound_type
return fused_type.specialize(fused_to_specific).typeof_name()
def get_specialized_types(type): def get_specialized_types(type):
""" """
Return a list of specialized types sorted in reverse order in accordance Return a list of specialized types sorted in reverse order in accordance
...@@ -2640,10 +2699,15 @@ def get_specialized_types(type): ...@@ -2640,10 +2699,15 @@ def get_specialized_types(type):
if isinstance(type, FusedType): if isinstance(type, FusedType):
result = type.types result = type.types
for specialized_type in result:
specialized_type.specialization_string = specialized_type.typeof_name()
else: else:
result = [] result = []
for cname, f2s in get_all_specialized_permutations(type.get_fused_types()): for cname, f2s in get_all_specialized_permutations(type.get_fused_types()):
result.append(type.specialize(f2s)) specialized_type = type.specialize(f2s)
specialized_type.specialization_string = (
specialization_signature_string(type, f2s))
result.append(specialized_type)
return sorted(result) return sorted(result)
......
...@@ -784,6 +784,23 @@ class Scope(object): ...@@ -784,6 +784,23 @@ class Scope(object):
def add_include_file(self, filename): def add_include_file(self, filename):
self.outer_scope.add_include_file(filename) self.outer_scope.add_include_file(filename)
def get_refcounted_entries(self, include_weakref=False):
py_attrs = []
py_buffers = []
memoryview_slices = []
for entry in self.var_entries:
if entry.type.is_pyobject:
if include_weakref or entry.name != "weakref":
py_attrs.append(entry)
elif entry.type == PyrexTypes.c_py_buffer_type:
py_buffers.append(entry)
elif entry.type.is_memoryviewslice:
memoryview_slices.append(entry)
have_entries = py_attrs or py_buffers or memoryview_slices
return have_entries, (py_attrs, py_buffers, memoryview_slices)
class PreImportScope(Scope): class PreImportScope(Scope):
......
__version__ = "0.16.beta0" __version__ = "0.16rc1"
# Void cython.* directives (for case insensitive operating systems). # Void cython.* directives (for case insensitive operating systems).
from Cython.Shadow import * from Cython.Shadow import *
# tag: cpp
cimport cython
from libcpp.vector cimport vector
def test_cpp_specialization(cython.floating element):
"""
>>> import cython
>>> test_cpp_specialization[cython.float](10.0)
vector<float> * float 10.0
>>> test_cpp_specialization[cython.double](10.0)
vector<double> * double 10.0
"""
cdef vector[cython.floating] *v = new vector[cython.floating]()
v.push_back(element)
print cython.typeof(v), cython.typeof(element), v.at(0)
\ No newline at end of file
# mode: run # mode: run
cimport cython cimport cython
from cython.view cimport array
from cython cimport integral from cython cimport integral
from cpython cimport Py_INCREF from cpython cimport Py_INCREF
from Cython import Shadow as pure_cython from Cython import Shadow as pure_cython
ctypedef char * string_t ctypedef char * string_t
# floating = cython.fused_type(float, double) floating # floating = cython.fused_type(float, double) floating
...@@ -249,3 +249,26 @@ def test_sizeof_fused_type(fused_type1 b): ...@@ -249,3 +249,26 @@ def test_sizeof_fused_type(fused_type1 b):
""" """
t = sizeof(b), sizeof(fused_type1), sizeof(double) t = sizeof(b), sizeof(fused_type1), sizeof(double)
assert t[0] == t[1] == t[2], t assert t[0] == t[1] == t[2], t
def get_array(itemsize, format):
result = array((10,), itemsize, format)
result[5] = 5.0
result[6] = 6.0
return result
def test_fused_memslice_dtype(cython.floating[:] array):
"""
Note: the np.ndarray dtype test is in numpy_test
>>> import cython
>>> sorted(test_fused_memslice_dtype.__signatures__)
['double', 'float']
>>> test_fused_memslice_dtype[cython.double](get_array(8, 'd'))
double[:] double[:] 5.0 6.0
>>> test_fused_memslice_dtype[cython.float](get_array(4, 'f'))
float[:] float[:] 5.0 6.0
"""
cdef cython.floating[:] otherarray = array[0:100:1]
print cython.typeof(array), cython.typeof(otherarray), \
array[5], otherarray[6]
...@@ -133,6 +133,16 @@ def test_coerce_to_temp(): ...@@ -133,6 +133,16 @@ def test_coerce_to_temp():
print print
print _coerce_to_temp()[4][4] print _coerce_to_temp()[4][4]
def test_extclass_attribute_dealloc():
"""
>>> test_extclass_attribute_dealloc()
acquired self.arr
2
released self.arr
"""
cdef ExtClassMockedAttr obj = ExtClassMockedAttr()
print obj.arr[4, 4]
cdef float[:,::1] global_mv = array((10,10), itemsize=sizeof(float), format='f') cdef float[:,::1] global_mv = array((10,10), itemsize=sizeof(float), format='f')
global_mv = array((10,10), itemsize=sizeof(float), format='f') global_mv = array((10,10), itemsize=sizeof(float), format='f')
cdef object global_obj cdef object global_obj
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import numpy as np import numpy as np
cimport numpy as np cimport numpy as np
int64_array = np.ones((3, 2), dtype=np.int64)
def f(): def f():
""" """
...@@ -14,7 +15,7 @@ def f(): ...@@ -14,7 +15,7 @@ def f():
shape[1] 2 shape[1] 2
strides 16 8 strides 16 8
""" """
cdef np.ndarray x = np.ones((3, 2), dtype=np.int64) cdef np.ndarray x = int64_array
cdef int i cdef int i
cdef Py_ssize_t j, k cdef Py_ssize_t j, k
cdef char *p cdef char *p
...@@ -43,3 +44,11 @@ def f(): ...@@ -43,3 +44,11 @@ def f():
k = x.strides[1] k = x.strides[1]
print 'strides', j, k print 'strides', j, k
def test_non_namenode_attribute_access(obj):
"""
>>> test_non_namenode_attribute_access(int64_array)
data 1
"""
# Try casting, resulting in an AttributeNode with a TypeCastNode as object
# and 'data' as attribute
print "data", (<np.int64_t *> (<np.ndarray> obj).data)[0]
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# cannot be named "numpy" in order to not clash with the numpy module! # cannot be named "numpy" in order to not clash with the numpy module!
cimport numpy as np cimport numpy as np
cimport cython
def little_endian(): def little_endian():
cdef int endian_detector = 1 cdef int endian_detector = 1
...@@ -502,4 +503,69 @@ def test_point_record(): ...@@ -502,4 +503,69 @@ def test_point_record():
test[i].y = -i test[i].y = -i
print repr(test).replace('<', '!').replace('>', '!') print repr(test).replace('<', '!').replace('>', '!')
def test_fused_ndarray_dtype(np.ndarray[cython.floating, ndim=1] a):
"""
>>> import cython
>>> sorted(test_fused_ndarray_dtype.__signatures__)
['double', 'float']
>>> test_fused_ndarray_dtype[cython.double](np.arange(10, dtype=np.float64))
ndarray[double,ndim=1] ndarray[double,ndim=1] 5.0 6.0
>>> test_fused_ndarray_dtype[cython.float](np.arange(10, dtype=np.float32))
ndarray[float,ndim=1] ndarray[float,ndim=1] 5.0 6.0
"""
cdef np.ndarray[cython.floating, ndim=1] b = a
print cython.typeof(a), cython.typeof(b), a[5], b[6]
double_array = np.linspace(0, 1, 100)
int32_array = np.arange(100, dtype=np.int32)
cdef fused fused_external:
np.int32_t
np.int64_t
np.float32_t
np.float64_t
def test_fused_external(np.ndarray[fused_external, ndim=1] a):
"""
>>> import cython
>>> sorted(test_fused_external.__signatures__)
['float32_t', 'float64_t', 'int32_t', 'int64_t']
>>> test_fused_external["float64_t"](double_array)
float64
>>> test_fused_external["int32_t"](int32_array)
int32
>>> test_fused_external(np.arange(100)) # fix in next release
Traceback (most recent call last):
...
TypeError: No matching signature found
"""
print a.dtype
cdef fused fused_buffers:
np.ndarray[np.int32_t, ndim=1]
np.int64_t[::1]
def test_fused_buffers(fused_buffers arg):
"""
>>> sorted(test_fused_buffers.__signatures__)
['int64_t[::1]', 'ndarray[int32_t,ndim=1]']
"""
cpdef _fused_cpdef_buffers(np.ndarray[fused_external] a):
print a.dtype
def test_fused_cpdef_buffers():
"""
>>> test_fused_cpdef_buffers()
int32
int32
"""
_fused_cpdef_buffers[np.int32_t](int32_array)
cdef np.ndarray[np.int32_t] typed_array = int32_array
_fused_cpdef_buffers(typed_array)
include "numpy_common.pxi" include "numpy_common.pxi"
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