def copy_def(self, env): """ Create a copy of the original def or lambda function for specialized versions. """ fused_compound_types = PyrexTypes.unique( [arg.type for arg in self.node.args if arg.type.is_fused]) permutations = PyrexTypes.get_all_specialized_permutations(fused_compound_types) self.fused_compound_types = fused_compound_types if self.node.entry in env.pyfunc_entries: env.pyfunc_entries.remove(self.node.entry) for cname, fused_to_specific in permutations: copied_node = copy.deepcopy(self.node) self._specialize_function_args(copied_node.args, fused_to_specific) copied_node.return_type = self.node.return_type.specialize( fused_to_specific) copied_node.analyse_declarations(env) self.create_new_local_scope(copied_node, env, fused_to_specific) self.specialize_copied_def(copied_node, cname, self.node.entry, fused_to_specific, fused_compound_types) PyrexTypes.specialize_entry(copied_node.entry, cname) copied_node.entry.used = True env.entries[copied_node.entry.name] = copied_node.entry if not self.replace_fused_typechecks(copied_node): break self.orig_py_func = self.node self.py_func = self.make_fused_cpdef(self.node, env, is_def=True)
def specialize_copied_def(self, node, cname, py_entry, f2s, fused_types): """Specialize the copy of a DefNode given the copied node, the specialization cname and the original DefNode entry""" type_strings = [PyrexTypes.specialization_signature_string(fused_type, f2s) for fused_type in fused_types] node.specialized_signature_string = ", ".join(type_strings) node.entry.pymethdef_cname = PyrexTypes.get_fused_cname(cname, node.entry.pymethdef_cname) node.entry.doc = py_entry.doc node.entry.doc_cname = py_entry.doc_cname
def test_cpp_reference_cpp_class(self): classes = [cppclasstype("Test%d" % i, []) for i in range(2)] function_types = [ cfunctype(pt.CReferenceType(classes[0])), cfunctype(pt.CReferenceType(classes[1])), ] functions = [NameNode(None, type=t) for t in function_types] self.assertMatches(function_types[0], [classes[0]], functions) self.assertMatches(function_types[1], [classes[1]], functions)
def test_cpp_reference_single_arg(self): function_types = [ cfunctype(pt.CReferenceType(pt.c_int_type)), cfunctype(pt.CReferenceType(pt.c_long_type)), cfunctype(pt.CReferenceType(pt.c_double_type)), ] functions = [NameNode(None, type=t) for t in function_types] self.assertMatches(function_types[0], [pt.c_int_type], functions) self.assertMatches(function_types[1], [pt.c_long_type], functions) self.assertMatches(function_types[2], [pt.c_double_type], functions)
def test_cpp_reference_cpp_class_and_int(self): classes = [cppclasstype("Test%d" % i, []) for i in range(2)] function_types = [ cfunctype(pt.CReferenceType(classes[0]), pt.c_int_type), cfunctype(pt.CReferenceType(classes[0]), pt.c_long_type), cfunctype(pt.CReferenceType(classes[1]), pt.c_int_type), cfunctype(pt.CReferenceType(classes[1]), pt.c_long_type), ] functions = [NameNode(None, type=t) for t in function_types] self.assertMatches(function_types[0], [classes[0], pt.c_int_type], functions) self.assertMatches(function_types[1], [classes[0], pt.c_long_type], functions) self.assertMatches(function_types[2], [classes[1], pt.c_int_type], functions) self.assertMatches(function_types[3], [classes[1], pt.c_long_type], functions)
def specialize_copied_def(self, node, cname, py_entry, f2s, fused_types): """Specialize the copy of a DefNode given the copied node, the specialization cname and the original DefNode entry""" type_strings = [ PyrexTypes.specialization_signature_string(fused_type, f2s) for fused_type in fused_types ] node.specialized_signature_string = '|'.join(type_strings) node.entry.pymethdef_cname = PyrexTypes.get_fused_cname( cname, node.entry.pymethdef_cname) node.entry.doc = py_entry.doc node.entry.doc_cname = py_entry.doc_cname
def test_widest_numeric_type(self): def assert_widest(type1, type2, widest): self.assertEqual(widest, PT.widest_numeric_type(type1, type2)) assert_widest(PT.c_int_type, PT.c_long_type, PT.c_long_type) assert_widest(PT.c_double_type, PT.c_long_type, PT.c_double_type) assert_widest(PT.c_longdouble_type, PT.c_long_type, PT.c_longdouble_type) cenum = PT.CEnumType("E", "cenum", typedef_flag=False) assert_widest(PT.c_int_type, cenum, PT.c_int_type)
def copy_def(self, env): """ Create a copy of the original def or lambda function for specialized versions. """ fused_compound_types = PyrexTypes.unique( [arg.type for arg in self.node.args if arg.type.is_fused]) permutations = PyrexTypes.get_all_specialized_permutations( fused_compound_types) self.fused_compound_types = fused_compound_types if self.node.entry in env.pyfunc_entries: env.pyfunc_entries.remove(self.node.entry) for cname, fused_to_specific in permutations: copied_node = copy.deepcopy(self.node) self._specialize_function_args(copied_node.args, fused_to_specific) copied_node.return_type = self.node.return_type.specialize( fused_to_specific) copied_node.analyse_declarations(env) # copied_node.is_staticmethod = self.node.is_staticmethod # copied_node.is_classmethod = self.node.is_classmethod self.create_new_local_scope(copied_node, env, fused_to_specific) self.specialize_copied_def(copied_node, cname, self.node.entry, fused_to_specific, fused_compound_types) PyrexTypes.specialize_entry(copied_node.entry, cname) copied_node.entry.used = True env.entries[copied_node.entry.name] = copied_node.entry if not self.replace_fused_typechecks(copied_node): break self.orig_py_func = self.node self.py_func = self.make_fused_cpdef(self.node, env, is_def=True)
def _split_fused_types(self, arg): """ Specialize fused types and split into normal types and buffer types. """ specialized_types = PyrexTypes.get_specialized_types(arg.type) # Prefer long over int, etc # specialized_types.sort() seen_py_type_names = set() normal_types, buffer_types = [], [] for specialized_type in specialized_types: py_type_name = specialized_type.py_type_name() if py_type_name: if py_type_name in seen_py_type_names: continue seen_py_type_names.add(py_type_name) normal_types.append(specialized_type) elif specialized_type.is_buffer or specialized_type.is_memoryviewslice: buffer_types.append(specialized_type) return normal_types, buffer_types
def _buffer_parse_format_string_check(self, pyx_code, decl_code, specialized_type, env): """ For each specialized type, try to coerce the object to a memoryview slice of that type. This means obtaining a buffer and parsing the format string. TODO: separate buffer acquisition from format parsing """ dtype = specialized_type.dtype if specialized_type.is_buffer: axes = [('direct', 'strided')] * specialized_type.ndim else: axes = specialized_type.axes memslice_type = PyrexTypes.MemoryViewSliceType(dtype, axes) memslice_type.create_from_py_utility_code(env) pyx_code.context.update( coerce_from_py_func=memslice_type.from_py_function, dtype=dtype) decl_code.putln( "{{memviewslice_cname}} {{coerce_from_py_func}}(object)") pyx_code.context.update( specialized_type_name=specialized_type.specialization_string, sizeof_dtype=self._sizeof_dtype(dtype)) pyx_code.put_chunk(u""" # try {{dtype}} if itemsize == -1 or itemsize == {{sizeof_dtype}}: memslice = {{coerce_from_py_func}}(arg) if memslice.memview: __PYX_XDEC_MEMVIEW(&memslice, 1) # print 'found a match for the buffer through format parsing' %s break else: __pyx_PyErr_Clear() """ % self.match)
def put_assign_to_buffer(lhs_cname, rhs_cname, buf_entry, is_initialized, pos, code): """ Generate code for reassigning a buffer variables. This only deals with getting the buffer auxiliary structure and variables set up correctly, the assignment itself and refcounting is the responsibility of the caller. However, the assignment operation may throw an exception so that the reassignment never happens. Depending on the circumstances there are two possible outcomes: - Old buffer released, new acquired, rhs assigned to lhs - Old buffer released, new acquired which fails, reaqcuire old lhs buffer (which may or may not succeed). """ buffer_aux, buffer_type = buf_entry.buffer_aux, buf_entry.type code.globalstate.use_utility_code(acquire_utility_code) pybuffernd_struct = buffer_aux.buflocal_nd_var.cname flags = get_flags(buffer_aux, buffer_type) code.putln("{") # Set up necesarry stack for getbuffer code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % buffer_type.dtype.struct_nesting_depth()) getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below if is_initialized: # Release any existing buffer code.putln('__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);' % pybuffernd_struct) # Acquire retcode_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False) code.putln("%s = %s;" % (retcode_cname, getbuffer % rhs_cname)) code.putln('if (%s) {' % (code.unlikely("%s < 0" % retcode_cname))) # If acquisition failed, attempt to reacquire the old buffer # before raising the exception. A failure of reacquisition # will cause the reacquisition exception to be reported, one # can consider working around this later. type, value, tb = [code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False) for i in range(3)] code.putln('PyErr_Fetch(&%s, &%s, &%s);' % (type, value, tb)) code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % lhs_cname))) code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % (type, value, tb)) # Do not refnanny these! code.globalstate.use_utility_code(raise_buffer_fallback_code) code.putln('__Pyx_RaiseBufferFallbackError();') code.putln('} else {') code.putln('PyErr_Restore(%s, %s, %s);' % (type, value, tb)) for t in (type, value, tb): code.funcstate.release_temp(t) code.putln('}') code.putln('}') # Unpack indices put_unpack_buffer_aux_into_scope(buf_entry, code) code.putln(code.error_goto_if_neg(retcode_cname, pos)) code.funcstate.release_temp(retcode_cname) else: # Our entry had no previous value, so set to None when acquisition fails. # In this case, auxiliary vars should be set up right in initialization to a zero-buffer, # so it suffices to set the buf field to NULL. code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % rhs_cname))) code.putln('%s = %s; __Pyx_INCREF(Py_None); %s.rcbuffer->pybuffer.buf = NULL;' % (lhs_cname, PyrexTypes.typecast(buffer_type, PyrexTypes.py_object_type, "Py_None"), pybuffernd_struct)) code.putln(code.error_goto(pos)) code.put('} else {') # Unpack indices put_unpack_buffer_aux_into_scope(buf_entry, code) code.putln('}') code.putln("}") # Release stack
def put_assign_to_buffer(lhs_cname, rhs_cname, buf_entry, is_initialized, pos, code): """ Generate code for reassigning a buffer variables. This only deals with getting the buffer auxiliary structure and variables set up correctly, the assignment itself and refcounting is the responsibility of the caller. However, the assignment operation may throw an exception so that the reassignment never happens. Depending on the circumstances there are two possible outcomes: - Old buffer released, new acquired, rhs assigned to lhs - Old buffer released, new acquired which fails, reaqcuire old lhs buffer (which may or may not succeed). """ buffer_aux, buffer_type = buf_entry.buffer_aux, buf_entry.type code.globalstate.use_utility_code(acquire_utility_code) pybuffernd_struct = buffer_aux.buflocal_nd_var.cname flags = get_flags(buffer_aux, buffer_type) code.putln("{") # Set up necesarry stack for getbuffer code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % buffer_type.dtype.struct_nesting_depth()) getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below if is_initialized: # Release any existing buffer code.putln('__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);' % pybuffernd_struct) # Acquire retcode_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False) code.putln("%s = %s;" % (retcode_cname, getbuffer % rhs_cname)) code.putln('if (%s) {' % (code.unlikely("%s < 0" % retcode_cname))) # If acquisition failed, attempt to reacquire the old buffer # before raising the exception. A failure of reacquisition # will cause the reacquisition exception to be reported, one # can consider working around this later. type, value, tb = [ code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False) for i in range(3) ] code.putln('PyErr_Fetch(&%s, &%s, &%s);' % (type, value, tb)) code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % lhs_cname))) code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % (type, value, tb)) # Do not refnanny these! code.globalstate.use_utility_code(raise_buffer_fallback_code) code.putln('__Pyx_RaiseBufferFallbackError();') code.putln('} else {') code.putln('PyErr_Restore(%s, %s, %s);' % (type, value, tb)) for t in (type, value, tb): code.funcstate.release_temp(t) code.putln('}') code.putln('}') # Unpack indices put_unpack_buffer_aux_into_scope(buf_entry, code) code.putln(code.error_goto_if_neg(retcode_cname, pos)) code.funcstate.release_temp(retcode_cname) else: # Our entry had no previous value, so set to None when acquisition fails. # In this case, auxiliary vars should be set up right in initialization to a zero-buffer, # so it suffices to set the buf field to NULL. code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % rhs_cname))) code.putln( '%s = %s; __Pyx_INCREF(Py_None); %s.rcbuffer->pybuffer.buf = NULL;' % (lhs_cname, PyrexTypes.typecast(buffer_type, PyrexTypes.py_object_type, "Py_None"), pybuffernd_struct)) code.putln(code.error_goto(pos)) code.put('} else {') # Unpack indices put_unpack_buffer_aux_into_scope(buf_entry, code) code.putln('}') code.putln("}") # Release stack
def cfunctype(*arg_types): return pt.CFuncType(pt.c_int_type, [CFuncTypeArg("name", arg_type, None) for arg_type in arg_types])
def assertMatches(self, expected_type, arg_types, functions): match = pt.best_match(arg_types, functions) if expected_type is not None: self.assertNotEqual(None, match) self.assertEqual(expected_type, match.type)
def assertMatches(self, expected_type, arg_types, functions): args = [ NameNode(None, type=arg_type) for arg_type in arg_types ] match = pt.best_match(args, functions) if expected_type is not None: self.assertNotEqual(None, match) self.assertEqual(expected_type, match.type)
def assert_widest(type1, type2, widest): self.assertEqual(widest, PT.widest_numeric_type(type1, type2))
def cppclasstype(name, base_classes): return pt.CppClassType(name, None, 'CPP_' + name, base_classes)