# cython: language_level=3 from __future__ import absolute_import from .PyrexTypes import CType, CTypedefType, CStructOrUnionType import cython try: import pythran pythran_is_pre_0_9 = tuple(map(int, pythran.__version__.split('.')[0:2])) < (0, 9) except ImportError: pythran = None pythran_is_pre_0_9 = True # Pythran/Numpy specific operations def has_np_pythran(env): if env is None: return False directives = getattr(env, 'directives', None) return (directives and directives.get('np_pythran', False)) @cython.ccall def is_pythran_supported_dtype(type_): if isinstance(type_, CTypedefType): return is_pythran_supported_type(type_.typedef_base_type) return type_.is_numeric def pythran_type(Ty, ptype="ndarray"): if Ty.is_buffer: ndim,dtype = Ty.ndim, Ty.dtype if isinstance(dtype, CStructOrUnionType): ctype = dtype.cname elif isinstance(dtype, CType): ctype = dtype.sign_and_name() elif isinstance(dtype, CTypedefType): ctype = dtype.typedef_cname else: raise ValueError("unsupported type %s!" % dtype) if pythran_is_pre_0_9: return "pythonic::types::%s<%s,%d>" % (ptype,ctype, ndim) else: return "pythonic::types::%s<%s,pythonic::types::pshape<%s>>" % (ptype,ctype, ",".join(("long",)*ndim)) if Ty.is_pythran_expr: return Ty.pythran_type #if Ty.is_none: # return "decltype(pythonic::__builtin__::None)" if Ty.is_numeric: return Ty.sign_and_name() raise ValueError("unsupported pythran type %s (%s)" % (Ty, type(Ty))) @cython.cfunc def type_remove_ref(ty): return "typename std::remove_reference<%s>::type" % ty def pythran_binop_type(op, tA, tB): if op == '**': return 'decltype(pythonic::numpy::functor::power{}(std::declval<%s>(), std::declval<%s>()))' % ( pythran_type(tA), pythran_type(tB)) else: return "decltype(std::declval<%s>() %s std::declval<%s>())" % ( pythran_type(tA), op, pythran_type(tB)) def pythran_unaryop_type(op, type_): return "decltype(%sstd::declval<%s>())" % ( op, pythran_type(type_)) @cython.cfunc def _index_access(index_code, indices): indexing = ",".join([index_code(idx) for idx in indices]) return ('[%s]' if len(indices) == 1 else '(%s)') % indexing def _index_type_code(index_with_type): idx, index_type = index_with_type if idx.is_slice: n = 2 + int(not idx.step.is_none) return "pythonic::__builtin__::functor::slice{}(%s)" % (",".join(["0"]*n)) elif index_type.is_int: return "std::declval<%s>()" % index_type.sign_and_name() elif index_type.is_pythran_expr: return "std::declval<%s>()" % index_type.pythran_type raise ValueError("unsupported indexing type %s!" % index_type) def _index_code(idx): if idx.is_slice: values = idx.start, idx.stop, idx.step if idx.step.is_none: func = "contiguous_slice" values = values[:2] else: func = "slice" return "pythonic::types::%s(%s)" % ( func, ",".join((v.pythran_result() for v in values))) elif idx.type.is_int: return to_pythran(idx) elif idx.type.is_pythran_expr: return idx.pythran_result() raise ValueError("unsupported indexing type %s" % idx.type) def pythran_indexing_type(type_, indices): return type_remove_ref("decltype(std::declval<%s>()%s)" % ( pythran_type(type_), _index_access(_index_type_code, indices), )) def pythran_indexing_code(indices): return _index_access(_index_code, indices) def np_func_to_list(func): if not func.is_numpy_attribute: return [] return np_func_to_list(func.obj) + [func.attribute] if pythran is None: def pythran_is_numpy_func_supported(name): return False else: def pythran_is_numpy_func_supported(func): CurF = pythran.tables.MODULES['numpy'] FL = np_func_to_list(func) for F in FL: CurF = CurF.get(F, None) if CurF is None: return False return True def pythran_functor(func): func = np_func_to_list(func) submodules = "::".join(func[:-1] + ["functor"]) return "pythonic::numpy::%s::%s" % (submodules, func[-1]) def pythran_func_type(func, args): args = ",".join(("std::declval<%s>()" % pythran_type(a.type) for a in args)) return "decltype(%s{}(%s))" % (pythran_functor(func), args) @cython.ccall def to_pythran(op, ptype=None): op_type = op.type if op_type.is_int: # Make sure that integer literals always have exactly the type that the templates expect. return op_type.cast_code(op.result()) if is_type(op_type, ["is_pythran_expr", "is_numeric", "is_float", "is_complex"]): return op.result() if op.is_none: return "pythonic::__builtin__::None" if ptype is None: ptype = pythran_type(op_type) assert op.type.is_pyobject return "from_python<%s>(%s)" % (ptype, op.py_result()) @cython.cfunc def is_type(type_, types): for attr in types: if getattr(type_, attr, False): return True return False def is_pythran_supported_node_or_none(node): return node.is_none or is_pythran_supported_type(node.type) @cython.ccall def is_pythran_supported_type(type_): pythran_supported = ( "is_pythran_expr", "is_int", "is_numeric", "is_float", "is_none", "is_complex") return is_type(type_, pythran_supported) or is_pythran_expr(type_) def is_pythran_supported_operation_type(type_): pythran_supported = ( "is_pythran_expr", "is_int", "is_numeric", "is_float", "is_complex") return is_type(type_,pythran_supported) or is_pythran_expr(type_) @cython.ccall def is_pythran_expr(type_): return type_.is_pythran_expr def is_pythran_buffer(type_): return (type_.is_numpy_buffer and is_pythran_supported_dtype(type_.dtype) and type_.mode in ("c", "strided") and not type_.cast) def pythran_get_func_include_file(func): func = np_func_to_list(func) return "pythonic/numpy/%s.hpp" % "/".join(func) def include_pythran_generic(env): # Generic files env.add_include_file("pythonic/core.hpp") env.add_include_file("pythonic/python/core.hpp") env.add_include_file("pythonic/types/bool.hpp") env.add_include_file("pythonic/types/ndarray.hpp") env.add_include_file("pythonic/numpy/power.hpp") env.add_include_file("pythonic/__builtin__/slice.hpp") env.add_include_file("") # for placement new for i in (8, 16, 32, 64): env.add_include_file("pythonic/types/uint%d.hpp" % i) env.add_include_file("pythonic/types/int%d.hpp" % i) for t in ("float", "float32", "float64", "set", "slice", "tuple", "int", "complex", "complex64", "complex128"): env.add_include_file("pythonic/types/%s.hpp" % t)