Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert most of np.zeros to HPy #12

Open
wants to merge 1 commit into
base: hpy
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions numpy/core/code_generators/cversions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@
# Version 15 (NumPy 1.22) Configurable memory allocations
# Version 14 (NumPy 1.23) No change.
0x0000000f = b8783365b873681cd204be50cdfb448d

# Version 16 (NumPy ????) HPy APIs
0x00000010 = c913943818bde9154f24f851eac6f2f0
3 changes: 3 additions & 0 deletions numpy/core/code_generators/numpy_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@
'PyDataMem_SetHandler': (304,),
'PyDataMem_GetHandler': (305,),
# End 1.21 API

# HPy API:
'HPyArray_Zeros': (307,),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we shouldn't add HPy functions to the API, but rather, eventually, have a separate function table for the HPy API.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agreed. I want to play with this a bit to move forward quicker: we'd like to prototype integration with Matplotlib port too. However, new table will be the way to go longer term.

}

ufunc_types_api = {
Expand Down
3 changes: 2 additions & 1 deletion numpy/core/setup_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
# 0x0000000e - 1.21.x
# 0x0000000f - 1.22.x
# 0x0000000f - 1.23.x
C_API_VERSION = 0x0000000f
# 0x00000010 - ??????
C_API_VERSION = 0x00000010

class MismatchCAPIWarning(Warning):
pass
Expand Down
195 changes: 175 additions & 20 deletions numpy/core/src/multiarray/conversion_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "hpy.h"
#include <structmember.h>

#include "numpy/arrayobject.h"
Expand Down Expand Up @@ -149,6 +150,73 @@ PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq)
return NPY_SUCCEED;
}

// HPY TODO: make a NUMPY_API??
// HPY TODO: turn PyArray_IntpConverter into simple delegate to this,
// but first sort out METH_FASTCALL args parsing
NPY_NO_EXPORT int
HPyArray_IntpConverter(HPyContext *ctx, HPy obj, PyArray_Dims *seq)
{
HPy_ssize_t len;
int nd;

seq->ptr = NULL;
seq->len = 0;

/*
* When the deprecation below expires, remove the `if` statement, and
* update the comment for PyArray_OptionalIntpConverter.
* HPy TODO: using HPy_IsNull, since HPy_NULL is the default working well for HPyArg_ParseKeywords
*/
if (HPy_IsNull(obj) || HPy_Is(ctx, obj, ctx->h_None)) {
/* Numpy 1.20, 2020-05-31 */
if (DEPRECATE(
"Passing None into shape arguments as an alias for () is "
"deprecated.") < 0){
return NPY_FAIL;
}
return NPY_SUCCEED;
}

len = HPy_Length(ctx, obj);
if (len == -1) {
/* Check to see if it is an integer number */
if (HPyNumber_Check(ctx, obj)) {
/*
* After the deprecation the PyNumber_Check could be replaced
* by PyIndex_Check.
* FIXME 1.9 ?
*/
len = 1;
}
}
if (len < 0) {
HPyErr_SetString(ctx, ctx->h_TypeError,
"expected sequence object with len >= 0 or a single integer");
return NPY_FAIL;
}
if (len > NPY_MAXDIMS) {
// HPY TODO: HPyErr_CFormat
HPyErr_SetString(ctx, ctx->h_ValueError, "maximum supported dimension for an ndarray is %d"
", found %d"/*, NPY_MAXDIMS, len*/);
return NPY_FAIL;
}
if (len > 0) {
seq->ptr = npy_alloc_cache_dim(len);
if (seq->ptr == NULL) {
HPyErr_NoMemory(ctx);
return NPY_FAIL;
}
}
seq->len = len;
nd = HPyArray_IntpFromIndexSequence(ctx, obj, (npy_intp *)seq->ptr, len);
if (nd == -1 || nd != len) {
npy_free_cache_dim_obj(*seq);
seq->ptr = NULL;
return NPY_FAIL;
}
return NPY_SUCCEED;
}

/*
* Like PyArray_IntpConverter, but leaves `seq` untouched if `None` is passed
* rather than treating `None` as `()`.
Expand Down Expand Up @@ -370,53 +438,68 @@ PyArray_BoolConverter(PyObject *object, npy_bool *val)
}

static int
string_converter_helper(
PyObject *object,
hpy_string_converter_helper(
HPyContext *ctx,
HPy object,
void *out,
int (*str_func)(char const*, Py_ssize_t, void*),
char const *name,
char const *message)
{
/* allow bytes for compatibility */
PyObject *str_object = NULL;
if (PyBytes_Check(object)) {
str_object = PyUnicode_FromEncodedObject(object, NULL, NULL);
if (str_object == NULL) {
PyErr_Format(PyExc_ValueError,
"%s %s (got %R)", name, message, object);
HPy str_object = HPy_NULL;
if (HPyBytes_Check(ctx, object)) {
str_object = HPyUnicode_FromEncodedObject(ctx, object, NULL, NULL);
if (HPy_IsNull(str_object)) {
// HPY TODO: HPyErr_Format
HPyErr_SetString(ctx, ctx->h_ValueError,
"%s %s (got %R)"/*, name, message, object*/);
return NPY_FAIL;
}
}
else if (PyUnicode_Check(object)) {
str_object = object;
Py_INCREF(str_object);
else if (HPyUnicode_Check(ctx, object)) {
str_object = HPy_Dup(ctx, object);
}
else {
PyErr_Format(PyExc_TypeError,
"%s must be str, not %s", name, Py_TYPE(object)->tp_name);
// HPY TODO: HPyErr_Format
HPyErr_SetString(ctx, ctx->h_TypeError,
"%s must be str, not %s"/*, name, Py_TYPE(object)->tp_name*/);
return NPY_FAIL;
}

Py_ssize_t length;
char const *str = PyUnicode_AsUTF8AndSize(str_object, &length);
HPy_ssize_t length;
char const *str = HPyUnicode_AsUTF8AndSize(ctx, str_object, &length);
if (str == NULL) {
Py_DECREF(str_object);
HPy_Close(ctx, str_object);
return NPY_FAIL;
}

int ret = str_func(str, length, out);
Py_DECREF(str_object);
HPy_Close(ctx, str_object);
if (ret < 0) {
/* str_func returns -1 without an exception if the value is wrong */
if (!PyErr_Occurred()) {
PyErr_Format(PyExc_ValueError,
"%s %s (got %R)", name, message, object);
if (!HPyErr_Occurred(ctx)) {
// HPY TODO: HPyErr_Format
HPyErr_SetString(ctx, ctx->h_ValueError,
"%s %s (got %R)"/*, name, message, object*/);
}
return NPY_FAIL;
}
return NPY_SUCCEED;
}

static int
string_converter_helper(
PyObject *object,
void *out,
int (*str_func)(char const*, Py_ssize_t, void*),
char const *name,
char const *message)
{
HPyContext *ctx = npy_get_context();
return hpy_string_converter_helper(ctx, HPy_FromPyObject(ctx, object), out, str_func, name, message);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HPy_FromPyObject(ctx, object) handle needs to be closed

}

static int byteorder_parser(char const *str, Py_ssize_t length, void *data)
{
char *endian = (char *)data;
Expand Down Expand Up @@ -632,6 +715,20 @@ PyArray_OrderConverter(PyObject *object, NPY_ORDER *val)
"must be one of 'C', 'F', 'A', or 'K'");
}

// HPY TODO: NUMPY_API?
NPY_NO_EXPORT int
HPyArray_OrderConverter(HPyContext *ctx, HPy object, NPY_ORDER *val)
{
/* Leave the desired default from the caller for None or NULL */
// HPY TODO: once METH_FASTCALL args parsing is sorted out, reconsider what is the default
if (HPy_IsNull(object) || HPy_Is(ctx, object, ctx->h_None)) {
return NPY_SUCCEED;
}
return hpy_string_converter_helper(
ctx, object, (void *)val, order_parser, "order",
"must be one of 'C', 'F', 'A', or 'K'");
}

static int clipmode_parser(char const *str, Py_ssize_t length, void *data)
{
NPY_CLIPMODE *val = (NPY_CLIPMODE *)data;
Expand Down Expand Up @@ -1067,6 +1164,64 @@ PyArray_IntpFromIndexSequence(PyObject *seq, npy_intp *vals, npy_intp maxvals)
return nd;
}

NPY_NO_EXPORT npy_intp
HPyArray_IntpFromIndexSequence(HPyContext *ctx, HPy seq, npy_intp *vals, npy_intp maxvals)
{
HPy_ssize_t nd;
npy_intp i;
HPy op, err;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err is unused


/*
* Check to see if sequence is a single integer first.
* or, can be made into one
*/
nd = HPy_Length(ctx, seq);
if (nd == -1) {
if (HPyErr_Occurred(ctx)) {
HPyErr_Clear(ctx);
}

// HPY TODO: vals[0] = PyArray_PyIntAsIntp(seq);
vals[0] = HPyLong_AsUnsignedLong(ctx, seq);
if(vals[0] == -1) {
int was_error = HPyErr_Occurred(ctx);
if (was_error &&
HPyErr_ExceptionMatches(ctx, ctx->h_OverflowError)) {
HPyErr_SetString(ctx, ctx->h_ValueError,
"Maximum allowed dimension exceeded");
}
if(was_error) {
return -1;
}
}
nd = 1;
}
else {
for (i = 0; i < PyArray_MIN(nd,maxvals); i++) {
op = HPy_GetItem_i(ctx, seq, i);
if (HPy_IsNull(op)) {
return -1;
}

// HPY TODO: vals[i] = PyArray_PyIntAsIntp(op);
vals[i] = HPyLong_AsUnsignedLong(ctx, op);
HPy_Close(ctx, op);
if(vals[i] == -1) {
int was_error = HPyErr_Occurred(ctx);
if (was_error && HPyErr_ExceptionMatches(ctx, ctx->h_OverflowError)) {
HPyErr_SetString(ctx, ctx->h_ValueError,
"Maximum allowed dimension exceeded");
}
if(was_error) {
return -1;
}
}
}
}
return nd;
}


/*NUMPY_API
* PyArray_IntpFromSequence
* Returns the number of integers converted or -1 if an error occurred.
Expand Down
9 changes: 9 additions & 0 deletions numpy/core/src/multiarray/conversion_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,13 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags);
*/
extern NPY_NO_EXPORT int evil_global_disable_warn_O4O8_flag;

NPY_NO_EXPORT int
HPyArray_IntpConverter(HPyContext *ctx, HPy obj, PyArray_Dims *seq);

NPY_NO_EXPORT npy_intp
HPyArray_IntpFromIndexSequence(HPyContext *ctx, HPy seq, npy_intp *vals, npy_intp maxvals);

NPY_NO_EXPORT int
HPyArray_OrderConverter(HPyContext *ctx, HPy object, NPY_ORDER *val);

#endif /* NUMPY_CORE_SRC_MULTIARRAY_CONVERSION_UTILS_H_ */
38 changes: 27 additions & 11 deletions numpy/core/src/multiarray/ctors.c
Original file line number Diff line number Diff line change
Expand Up @@ -2988,32 +2988,48 @@ PyArray_CheckAxis(PyArrayObject *arr, int *axis, int flags)
NPY_NO_EXPORT PyObject *
PyArray_Zeros(int nd, npy_intp const *dims, PyArray_Descr *type, int is_f_order)
{
PyArrayObject *ret;
HPyContext *ctx = npy_get_context();
HPy_AsPyObject(ctx, HPyArray_Zeros(ctx, nd, dims, type, is_f_order));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this handle needs to be closed

}

/*NUMPY_API
* Zeros
*
* For now, type should be a PyObject.
* accepts NULL type
*/
NPY_NO_EXPORT HPy
HPyArray_Zeros(HPyContext *ctx, int nd, npy_intp const *dims, PyArray_Descr *type, int is_f_order)
{
HPy ret;

if (!type) {
type = PyArray_DescrFromType(NPY_DEFAULT_TYPE);
}

ret = (PyArrayObject *)PyArray_NewFromDescr_int(
&PyArray_Type, type,
ret = HPyArray_NewFromDescr_int(
ctx, HPy_FromPyObject(ctx, (PyObject *)&PyArray_Type), type,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle leak

nd, dims, NULL, NULL,
is_f_order, NULL, NULL,
is_f_order, HPy_NULL, HPy_NULL,
1, 0);

if (ret == NULL) {
return NULL;
if (HPy_IsNull(ret)) {
return HPy_NULL;
}

/* handle objects */
if (PyDataType_REFCHK(PyArray_DESCR(ret))) {
if (_zerofill(ret) < 0) {
Py_DECREF(ret);
return NULL;
if (PyDataType_REFCHK(type)) {
PyObject *py_ret = HPy_AsPyObject(ctx, ret);
int result = _zerofill((PyArrayObject*) py_ret);
Py_DECREF(py_ret);
if (result < 0) {
HPy_Close(ctx, ret);
return HPy_NULL;
}
}


return (PyObject *)ret;
return ret;

}

Expand Down
Loading