void pydynd::array_no_dim_broadcast_assign_from_py( const dynd::ndt::type &dst_tp, const char *dst_arrmeta, char *dst_data, PyObject *value, const dynd::eval::eval_context *ectx) { // TODO: This is a hack, need a proper way to pass this dst param dynd::nd::array tmp_dst( dynd::make_array_memory_block(dst_tp.get_arrmeta_size())); tmp_dst.get()->tp = dst_tp; tmp_dst.get()->flags = dynd::nd::read_access_flag | dynd::nd::write_access_flag; if (dst_tp.get_arrmeta_size() > 0) { dst_tp.extended()->arrmeta_copy_construct( tmp_dst.get()->metadata(), dst_arrmeta, dynd::intrusive_ptr<dynd::memory_block_data>()); } tmp_dst.get()->data = dst_data; dynd::ndt::type src_tp = dynd::ndt::type::make<void>(); const char *src_arrmeta = NULL; char *src_data = reinterpret_cast<char *>(&value); const char *kwd_names[1] = {"broadcast"}; dynd::nd::array kwd_values[1] = {false}; (*pydynd::nd::copy_from_pyobject.get().get())( tmp_dst.get_type(), tmp_dst.get()->metadata(), tmp_dst.data(), 1, &src_tp, &src_arrmeta, &src_data, 1, kwd_values, std::map<std::string, dynd::ndt::type>()); tmp_dst.get()->tp = dynd::ndt::type(); }
std::string pydynd::ndt_type_repr(const dynd::ndt::type& d) { std::stringstream ss; if (d.is_builtin() && d.get_type_id() != dynd::complex_float32_type_id && d.get_type_id() != dynd::complex_float64_type_id) { ss << "ndt." << d; } else { switch (d.get_type_id()) { case complex_float32_type_id: ss << "ndt.complex_float32"; break; case complex_float64_type_id: ss << "ndt.complex_float64"; break; case date_type_id: ss << "ndt.date"; break; case time_type_id: if (d.tcast<time_type>()->get_timezone() == tz_abstract) { ss << "ndt.time"; } else { print_generic_type_repr(ss, d); } break; case datetime_type_id: if (d.tcast<datetime_type>()->get_timezone() == tz_abstract) { ss << "ndt.datetime"; } else if (d.tcast<datetime_type>()->get_timezone() == tz_utc) { ss << "ndt.datetimeutc"; } else { print_generic_type_repr(ss, d); } break; case json_type_id: ss << "ndt.json"; break; case bytes_type_id: if (d.tcast<bytes_type>()->get_target_alignment() == 1) { ss << "ndt.bytes"; } else { print_generic_type_repr(ss, d); } break; case string_type_id: if (d.tcast<string_type>()->get_encoding() == string_encoding_utf_8) { ss << "ndt.string"; } else { print_generic_type_repr(ss, d); } break; default: print_generic_type_repr(ss, d); break; } } return ss.str(); }
bool pydynd::broadcast_as_scalar(const dynd::ndt::type &tp, PyObject *obj) { intptr_t obj_ndim = 0; // Estimate the number of dimensions in ``obj`` by repeatedly indexing // along zero pyobject_ownref v(obj); Py_INCREF(v); for (;;) { // Don't treat these types as sequences if (PyDict_Check(v)) { if (tp.get_dtype().get_kind() == struct_kind) { // If the object to assign to a dynd struct ends in a dict, apply // the dict as the struct's value return (tp.get_ndim() > obj_ndim); } break; } else if (PyUnicode_Check(v) || PyBytes_Check(v)) { break; } PyObject *iter = PyObject_GetIter(v); if (iter != NULL) { ++obj_ndim; if (iter == v.get()) { // This was already an iterator, don't do any broadcasting, // because we have no visibility into it. Py_DECREF(iter); return false; } else { pyobject_ownref iter_owner(iter); PyObject *item = PyIter_Next(iter); if (item == NULL) { if (PyErr_ExceptionMatches(PyExc_StopIteration)) { PyErr_Clear(); break; } else { throw exception(); } } else { v.reset(item); } } } else { PyErr_Clear(); break; } } return (get_leading_dim_count(tp) > obj_ndim); }
void pydynd::nd::array_copy_from_numpy(const dynd::ndt::type &dst_tp, const char *dst_arrmeta, char *dst_data, PyArrayObject *src_arr, const dynd::eval::eval_context *ectx) { intptr_t src_ndim = PyArray_NDIM(src_arr); strided_of_numpy_arrmeta src_am_holder; const char *src_am = reinterpret_cast<const char *>( &src_am_holder.sdt[NPY_MAXDIMS - src_ndim]); // Fill in metadata for a multi-dim strided array, corresponding // to the numpy array, with a void type at the end for the numpy // specific data. uintptr_t src_alignment = reinterpret_cast<uintptr_t>(PyArray_DATA(src_arr)); for (intptr_t i = 0; i < src_ndim; ++i) { dynd::fixed_dim_type_arrmeta &am = src_am_holder.sdt[NPY_MAXDIMS - src_ndim + i]; am.dim_size = PyArray_DIM(src_arr, (int)i); am.stride = am.dim_size != 1 ? PyArray_STRIDE(src_arr, (int)i) : 0; src_alignment |= static_cast<uintptr_t>(am.stride); } dynd::ndt::type src_tp = dynd::ndt::make_type( src_ndim, PyArray_SHAPE(src_arr), dynd::ndt::type::make<void>()); src_am_holder.am.src_dtype = PyArray_DTYPE(src_arr); src_am_holder.am.src_alignment = src_alignment; // TODO: This is a hack, need a proper way to pass this dst param dynd::nd::array tmp_dst( dynd::make_array_memory_block(dst_tp.get_arrmeta_size())); tmp_dst.get()->tp = dst_tp; tmp_dst.get()->flags = dynd::nd::read_access_flag | dynd::nd::write_access_flag; if (dst_tp.get_arrmeta_size() > 0) { dst_tp.extended()->arrmeta_copy_construct( tmp_dst.get()->metadata(), dst_arrmeta, dynd::intrusive_ptr<dynd::memory_block_data>()); } tmp_dst.get()->data = dst_data; char *src_data = reinterpret_cast<char *>(PyArray_DATA(src_arr)); const char *kwd_names[1] = {"broadcast"}; dynd::nd::array kwd_values[1] = {true}; (*pydynd::nd::copy_from_numpy::get().get())( tmp_dst.get_type(), tmp_dst.get()->metadata(), tmp_dst.data(), 1, &src_tp, &src_am, &src_data, 1, kwd_values, std::map<std::string, dynd::ndt::type>()); tmp_dst.get()->tp = dynd::ndt::type(); }
inline ::testing::AssertionResult MatchNdtTypes(const char *expr1, const char *expr2, const dynd::ndt::type &pattern, const dynd::ndt::type &candidate) { if (pattern.match(candidate)) { return ::testing::AssertionSuccess(); } else { return ::testing::AssertionFailure() << "The type of candidate " << expr2 << " does not match pattern " << expr1 << "\n" << expr1 << " has value " << pattern << ",\n" << expr2 << " has value " << candidate << "."; } }
void pydynd::array_copy_to_numpy(PyArrayObject *dst_arr, const dynd::ndt::type &src_tp, const char *src_arrmeta, const char *src_data) { intptr_t dst_ndim = PyArray_NDIM(dst_arr); intptr_t src_ndim = src_tp.get_ndim(); uintptr_t dst_alignment = reinterpret_cast<uintptr_t>(PyArray_DATA(dst_arr)); strided_of_numpy_arrmeta dst_am_holder; const char *dst_am = reinterpret_cast<const char *>( &dst_am_holder.sdt[NPY_MAXDIMS - dst_ndim]); // Fill in metadata for a multi-dim strided array, corresponding // to the numpy array, with a void type at the end for the numpy // specific data. for (intptr_t i = 0; i < dst_ndim; ++i) { dynd::fixed_dim_type_arrmeta &am = dst_am_holder.sdt[NPY_MAXDIMS - dst_ndim + i]; am.stride = PyArray_STRIDE(dst_arr, (int)i); dst_alignment |= static_cast<uintptr_t>(am.stride); am.dim_size = PyArray_DIM(dst_arr, (int)i); } dynd::ndt::type dst_tp = dynd::ndt::make_type( dst_ndim, PyArray_SHAPE(dst_arr), dynd::ndt::make_type<void>()); dst_am_holder.am.dst_dtype = PyArray_DTYPE(dst_arr); dst_am_holder.am.dst_alignment = dst_alignment; // TODO: This is a hack, need a proper way to pass this dst param intptr_t tmp_dst_arrmeta_size = dst_ndim * sizeof(dynd::fixed_dim_type_arrmeta) + sizeof(copy_to_numpy_arrmeta); dynd::nd::array tmp_dst( reinterpret_cast<dynd::array_preamble *>( dynd::make_array_memory_block(tmp_dst_arrmeta_size).get()), true); tmp_dst.get()->tp = dst_tp; tmp_dst.get()->flags = dynd::nd::read_access_flag | dynd::nd::write_access_flag; if (dst_tp.get_arrmeta_size() > 0) { memcpy(tmp_dst.get()->metadata(), dst_am, tmp_dst_arrmeta_size); } tmp_dst.get()->data = (char *)PyArray_DATA(dst_arr); char *src_data_nonconst = const_cast<char *>(src_data); copy_to_numpy::get()->call(tmp_dst.get_type(), tmp_dst.get()->metadata(), tmp_dst.data(), 1, &src_tp, &src_arrmeta, &src_data_nonconst, 1, NULL, std::map<std::string, dynd::ndt::type>()); }
dynd::ndt::type pydynd::ndt_type_getitem(const dynd::ndt::type& d, PyObject *subscript) { // Convert the pyobject into an array of iranges intptr_t size; shortvector<irange> indices; if (!PyTuple_Check(subscript)) { // A single subscript size = 1; indices.init(1); indices[0] = pyobject_as_irange(subscript); } else { size = PyTuple_GET_SIZE(subscript); // Tuple of subscripts indices.init(size); for (Py_ssize_t i = 0; i < size; ++i) { indices[i] = pyobject_as_irange(PyTuple_GET_ITEM(subscript, i)); } } // Do an indexing operation return d.at_array((int)size, indices.get()); }
/** * Gets the number of dimensions at index 0, including tuple * and struct as dimensions. */ static intptr_t get_leading_dim_count(const dynd::ndt::type &tp) { intptr_t ndim = tp.get_ndim(); if (ndim) { return ndim + get_leading_dim_count(tp.get_dtype()); } else if (tp.get_kind() == expr_kind) { return get_leading_dim_count(tp.value_type()); } else if (tp.get_kind() == tuple_kind || tp.get_kind() == struct_kind) { if (tp.extended<ndt::tuple_type>()->get_field_count() == 0) { return 1; } else { return 1 + get_leading_dim_count( tp.extended<ndt::tuple_type>()->get_field_type(0)); } } else { return 0; } }
/** * This sets up a ckernel to copy from a dynd array * to a numpy array. The destination numpy array is * represented by dst_tp being ``void`` and the dst_arrmeta * being a pointer to the ``PyArray_Descr *`` of the type for the destination. */ intptr_t pydynd::copy_to_numpy_ck::instantiate( char *DYND_UNUSED(static_data), char *DYND_UNUSED(data), void *ckb, intptr_t ckb_offset, const dynd::ndt::type &dst_tp, const char *dst_arrmeta, intptr_t nsrc, const dynd::ndt::type *src_tp, const char *const *src_arrmeta, dynd::kernel_request_t kernreq, intptr_t nkwd, const dynd::nd::array *kwds, const std::map<std::string, dynd::ndt::type> &tp_vars) { if (dst_tp.get_type_id() != dynd::void_type_id) { stringstream ss; ss << "Cannot instantiate dynd::nd::callable with signature ("; ss << src_tp[0] << ") -> " << dst_tp; throw dynd::type_error(ss.str()); } PyObject *dst_obj = *reinterpret_cast<PyObject *const *>(dst_arrmeta); uintptr_t dst_alignment = reinterpret_cast<const uintptr_t *>(dst_arrmeta)[1]; PyArray_Descr *dtype = reinterpret_cast<PyArray_Descr *>(dst_obj); if (!PyDataType_FLAGCHK(dtype, NPY_ITEM_HASOBJECT)) { // If there is no object type in the numpy type, get the dynd equivalent // type and use it to do the copying dynd::ndt::type dst_view_tp = _type_from_numpy_dtype(dtype, dst_alignment); return dynd::make_assignment_kernel(ckb, ckb_offset, dst_view_tp, NULL, src_tp[0], src_arrmeta[0], kernreq, &dynd::eval::default_eval_context); } else if (PyDataType_ISOBJECT(dtype)) { dynd::nd::base_callable *af = const_cast<dynd::nd::base_callable *>( nd::copy_to_pyobject::get().get()); return af->instantiate(af->static_data(), NULL, ckb, ckb_offset, dynd::ndt::make_type<void>(), NULL, nsrc, src_tp, src_arrmeta, kernreq, 0, NULL, tp_vars); } else if (PyDataType_HASFIELDS(dtype)) { if (src_tp[0].get_kind() != dynd::struct_kind && src_tp[0].get_kind() != dynd::tuple_kind) { stringstream ss; pyobject_ownref dtype_str(PyObject_Str((PyObject *)dtype)); ss << "Cannot assign from source dynd type " << src_tp[0] << " to numpy type " << pydynd::pystring_as_string(dtype_str.get()); throw invalid_argument(ss.str()); } // Get the fields out of the numpy dtype vector<PyArray_Descr *> field_dtypes_orig; vector<string> field_names_orig; vector<size_t> field_offsets_orig; pydynd::extract_fields_from_numpy_struct( dtype, field_dtypes_orig, field_names_orig, field_offsets_orig); intptr_t field_count = field_dtypes_orig.size(); if (field_count != src_tp[0].extended<dynd::ndt::tuple_type>()->get_field_count()) { stringstream ss; pyobject_ownref dtype_str(PyObject_Str((PyObject *)dtype)); ss << "Cannot assign from source dynd type " << src_tp[0] << " to numpy type " << pydynd::pystring_as_string(dtype_str.get()); throw invalid_argument(ss.str()); } // Permute the numpy fields to match with the dynd fields vector<PyArray_Descr *> field_dtypes; vector<size_t> field_offsets; if (src_tp[0].get_kind() == dynd::struct_kind) { field_dtypes.resize(field_count); field_offsets.resize(field_count); for (intptr_t i = 0; i < field_count; ++i) { intptr_t src_i = src_tp[0].extended<dynd::ndt::struct_type>()->get_field_index( field_names_orig[i]); if (src_i >= 0) { field_dtypes[src_i] = field_dtypes_orig[i]; field_offsets[src_i] = field_offsets_orig[i]; } else { stringstream ss; pyobject_ownref dtype_str(PyObject_Str((PyObject *)dtype)); ss << "Cannot assign from source dynd type " << src_tp[0] << " to numpy type " << pydynd::pystring_as_string(dtype_str.get()); throw invalid_argument(ss.str()); } } } else { // In the tuple case, use position instead of name field_dtypes.swap(field_dtypes_orig); field_offsets.swap(field_offsets_orig); } vector<dynd::ndt::type> dst_fields_tp(field_count, dynd::ndt::make_type<void>()); vector<copy_to_numpy_arrmeta> dst_arrmeta_values(field_count); vector<const char *> dst_fields_arrmeta(field_count); for (intptr_t i = 0; i < field_count; ++i) { dst_arrmeta_values[i].dst_dtype = field_dtypes[i]; dst_arrmeta_values[i].dst_alignment = dst_alignment | field_offsets[i]; dst_fields_arrmeta[i] = reinterpret_cast<const char *>(&dst_arrmeta_values[i]); } const uintptr_t *src_arrmeta_offsets = src_tp[0].extended<dynd::ndt::tuple_type>()->get_arrmeta_offsets_raw(); dynd::shortvector<const char *> src_fields_arrmeta(field_count); for (intptr_t i = 0; i != field_count; ++i) { src_fields_arrmeta[i] = src_arrmeta[0] + src_arrmeta_offsets[i]; } // Todo: Remove this dynd::nd::callable af = dynd::nd::callable::make<copy_to_numpy_ck>( dynd::ndt::type("(Any) -> void"), 0); return make_tuple_unary_op_ckernel( af.get(), af.get_type(), ckb, ckb_offset, field_count, &field_offsets[0], &dst_fields_tp[0], &dst_fields_arrmeta[0], src_tp[0].extended<dynd::ndt::tuple_type>()->get_data_offsets( src_arrmeta[0]), src_tp[0].extended<dynd::ndt::tuple_type>()->get_field_types_raw(), src_fields_arrmeta.get(), kernreq); } else { stringstream ss; ss << "TODO: implement assign from source dynd type " << src_tp[0] << " to numpy type " << pyobject_repr((PyObject *)dtype); throw invalid_argument(ss.str()); } }
void pydynd::nd::copy_from_numpy_callable::instantiate(char *DYND_UNUSED(data), dynd::nd::kernel_builder *ckb, const dynd::ndt::type &dst_tp, const char *dst_arrmeta, intptr_t DYND_UNUSED(nsrc), const dynd::ndt::type *src_tp, const char *const *src_arrmeta, dynd::kernel_request_t kernreq, intptr_t nkwd, const dynd::nd::array *kwds, const std::map<std::string, dynd::ndt::type> &tp_vars) { if (src_tp[0].get_id() != dynd::void_id) { stringstream ss; ss << "Cannot instantiate dynd::nd::callable copy_from_numpy with " "signature ("; ss << src_tp[0] << ") -> " << dst_tp; throw dynd::type_error(ss.str()); } PyArray_Descr *dtype = *reinterpret_cast<PyArray_Descr *const *>(src_arrmeta[0]); uintptr_t src_alignment = reinterpret_cast<const uintptr_t *>(src_arrmeta[0])[1]; if (!PyDataType_FLAGCHK(dtype, NPY_ITEM_HASOBJECT)) { // If there is no object type in the numpy type, get the dynd equivalent // type and use it to do the copying dynd::ndt::type src_view_tp = _type_from_numpy_dtype(dtype, src_alignment); dynd::nd::array error_mode = dynd::assign_error_fractional; dynd::nd::assign->instantiate(NULL, ckb, dst_tp, dst_arrmeta, 1, &src_view_tp, NULL, kernreq, 1, &error_mode, std::map<std::string, dynd::ndt::type>()); return; } else if (PyDataType_ISOBJECT(dtype)) { dynd::nd::base_callable *af = dynd::nd::assign.get(); dynd::ndt::type child_src_tp = dynd::ndt::make_type<pyobject_type>(); af->instantiate(NULL, ckb, dst_tp, dst_arrmeta, 1, &child_src_tp, NULL, kernreq, nkwd, kwds, tp_vars); return; } else if (PyDataType_HASFIELDS(dtype)) { if (dst_tp.get_id() != dynd::struct_id && dst_tp.get_id() != dynd::tuple_id) { stringstream ss; ss << "Cannot assign from numpy type " << pyobject_repr((PyObject *)dtype) << " to dynd type " << dst_tp; throw invalid_argument(ss.str()); } // Get the fields out of the numpy dtype vector<PyArray_Descr *> field_dtypes_orig; vector<std::string> field_names_orig; vector<size_t> field_offsets_orig; pydynd::extract_fields_from_numpy_struct(dtype, field_dtypes_orig, field_names_orig, field_offsets_orig); intptr_t field_count = field_dtypes_orig.size(); if (field_count != dst_tp.extended<dynd::ndt::tuple_type>()->get_field_count()) { stringstream ss; ss << "Cannot assign from numpy type " << pyobject_repr((PyObject *)dtype) << " to dynd type " << dst_tp; throw invalid_argument(ss.str()); } // Permute the numpy fields to match with the dynd fields vector<PyArray_Descr *> field_dtypes; vector<size_t> field_offsets; if (dst_tp.get_id() == dynd::struct_id) { field_dtypes.resize(field_count); field_offsets.resize(field_count); for (intptr_t i = 0; i < field_count; ++i) { intptr_t src_i = dst_tp.extended<dynd::ndt::struct_type>()->get_field_index(field_names_orig[i]); if (src_i >= 0) { field_dtypes[src_i] = field_dtypes_orig[i]; field_offsets[src_i] = field_offsets_orig[i]; } else { stringstream ss; ss << "Cannot assign from numpy type " << pyobject_repr((PyObject *)dtype) << " to dynd type " << dst_tp; throw invalid_argument(ss.str()); } } } else { // In the tuple case, use position instead of name field_dtypes.swap(field_dtypes_orig); field_offsets.swap(field_offsets_orig); } vector<dynd::ndt::type> src_fields_tp(field_count, dynd::ndt::make_type<void>()); vector<copy_from_numpy_arrmeta> src_arrmeta_values(field_count); vector<const char *> src_fields_arrmeta(field_count); for (intptr_t i = 0; i < field_count; ++i) { src_arrmeta_values[i].src_dtype = field_dtypes[i]; src_arrmeta_values[i].src_alignment = src_alignment | field_offsets[i]; src_fields_arrmeta[i] = reinterpret_cast<const char *>(&src_arrmeta_values[i]); } const uintptr_t *dst_arrmeta_offsets = dst_tp.extended<dynd::ndt::tuple_type>()->get_arrmeta_offsets_raw(); dynd::shortvector<const char *> dst_fields_arrmeta(field_count); for (intptr_t i = 0; i != field_count; ++i) { dst_fields_arrmeta[i] = dst_arrmeta + dst_arrmeta_offsets[i]; } // Todo: Remove this line dynd::nd::callable af = dynd::nd::make_callable<copy_from_numpy_callable>(); make_tuple_unary_op_ckernel(af.get(), af.get_type(), ckb, field_count, dst_tp.extended<dynd::ndt::tuple_type>()->get_data_offsets(dst_arrmeta), dst_tp.extended<dynd::ndt::tuple_type>()->get_field_types_raw(), dst_fields_arrmeta.get(), &field_offsets[0], &src_fields_tp[0], &src_fields_arrmeta[0], kernreq); return; } else { stringstream ss; ss << "TODO: implement assign from numpy type " << pyobject_repr((PyObject *)dtype) << " to dynd type " << dst_tp; throw invalid_argument(ss.str()); } }
void pydynd::fill_arrmeta_from_numpy_dtype(const dynd::ndt::type &dt, PyArray_Descr *d, char *arrmeta) { switch (dt.get_id()) { case dynd::struct_id: { // In DyND, the struct offsets are part of the arrmeta instead of the dtype. // That's why we have to populate them here. PyObject *d_names = d->names; const dynd::ndt::struct_type *sdt = dt.extended<dynd::ndt::struct_type>(); const uintptr_t *arrmeta_offsets = sdt->get_arrmeta_offsets_raw(); size_t field_count = sdt->get_field_count(); uintptr_t *offsets = reinterpret_cast<size_t *>(arrmeta); for (size_t i = 0; i < field_count; ++i) { PyObject *tup = PyDict_GetItem(d->fields, PyTuple_GET_ITEM(d_names, i)); PyArray_Descr *fld_dtype; PyObject *title; int offset = 0; if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, &offset, &title)) { throw dynd::type_error("Numpy struct dtype has corrupt data"); } // Set the field offset in the output arrmeta offsets[i] = offset; // Fill the arrmeta for the field, if necessary const dynd::ndt::type &ft = sdt->get_field_type(i); if (!ft.is_builtin()) { fill_arrmeta_from_numpy_dtype(ft, fld_dtype, arrmeta + arrmeta_offsets[i]); } } break; } case dynd::fixed_dim_id: { // The Numpy subarray becomes a series of fixed_dim types, so we // need to copy the strides into the arrmeta. dynd::ndt::type el; PyArray_ArrayDescr *adescr = d->subarray; if (adescr == NULL) { stringstream ss; ss << "Internal error building dynd arrmeta: Numpy dtype has " "NULL subarray corresponding to strided_dim type"; throw dynd::type_error(ss.str()); } if (PyTuple_Check(adescr->shape)) { int ndim = (int)PyTuple_GET_SIZE(adescr->shape); dynd::fixed_dim_type_arrmeta *md = reinterpret_cast<dynd::fixed_dim_type_arrmeta *>(arrmeta); intptr_t stride = adescr->base->elsize; el = dt; for (int i = ndim - 1; i >= 0; --i) { md[i].dim_size = pydynd::pyobject_as_index(PyTuple_GET_ITEM(adescr->shape, i)); md[i].stride = stride; stride *= md[i].dim_size; el = el.extended<dynd::ndt::fixed_dim_type>()->get_element_type(); } arrmeta += ndim * sizeof(dynd::fixed_dim_type_arrmeta); } else { dynd::fixed_dim_type_arrmeta *md = reinterpret_cast<dynd::fixed_dim_type_arrmeta *>(arrmeta); arrmeta += sizeof(dynd::fixed_dim_type_arrmeta); md->dim_size = pydynd::pyobject_as_index(adescr->shape); md->stride = adescr->base->elsize; el = dt.extended<dynd::ndt::fixed_dim_type>()->get_element_type(); } // Fill the arrmeta for the array element, if necessary if (!el.is_builtin()) { fill_arrmeta_from_numpy_dtype(el, adescr->base, arrmeta); } break; } default: break; } }