summaryrefslogtreecommitdiff
path: root/numpy/core
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/core')
-rw-r--r--numpy/core/setup.py6
-rw-r--r--numpy/core/src/common/npy_argparse.c421
-rw-r--r--numpy/core/src/common/npy_argparse.h96
-rw-r--r--numpy/core/src/multiarray/_multiarray_tests.c.src23
-rw-r--r--numpy/core/src/multiarray/methods.c254
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c167
-rw-r--r--numpy/core/tests/test_argparse.py62
7 files changed, 845 insertions, 184 deletions
diff --git a/numpy/core/setup.py b/numpy/core/setup.py
index 822f9f580..8c34a3286 100644
--- a/numpy/core/setup.py
+++ b/numpy/core/setup.py
@@ -716,8 +716,10 @@ def configuration(parent_package='',top_path=None):
config.add_extension('_multiarray_tests',
sources=[join('src', 'multiarray', '_multiarray_tests.c.src'),
- join('src', 'common', 'mem_overlap.c')],
+ join('src', 'common', 'mem_overlap.c'),
+ join('src', 'common', 'npy_argparse.c')],
depends=[join('src', 'common', 'mem_overlap.h'),
+ join('src', 'common', 'npy_argparse.h'),
join('src', 'common', 'npy_extint128.h')],
libraries=['npymath'])
@@ -731,6 +733,7 @@ def configuration(parent_package='',top_path=None):
join('src', 'common', 'cblasfuncs.h'),
join('src', 'common', 'lowlevel_strided_loops.h'),
join('src', 'common', 'mem_overlap.h'),
+ join('src', 'common', 'npy_argparse.h'),
join('src', 'common', 'npy_cblas.h'),
join('src', 'common', 'npy_config.h'),
join('src', 'common', 'npy_ctypes.h'),
@@ -749,6 +752,7 @@ def configuration(parent_package='',top_path=None):
common_src = [
join('src', 'common', 'array_assign.c'),
join('src', 'common', 'mem_overlap.c'),
+ join('src', 'common', 'npy_argparse.c'),
join('src', 'common', 'npy_longdouble.c'),
join('src', 'common', 'templ_common.h.src'),
join('src', 'common', 'ucsnarrow.c'),
diff --git a/numpy/core/src/common/npy_argparse.c b/numpy/core/src/common/npy_argparse.c
new file mode 100644
index 000000000..3df780990
--- /dev/null
+++ b/numpy/core/src/common/npy_argparse.c
@@ -0,0 +1,421 @@
+#include "Python.h"
+
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+#define _MULTIARRAYMODULE
+
+#include "numpy/ndarraytypes.h"
+#include "npy_argparse.h"
+#include "npy_pycompat.h"
+#include "npy_import.h"
+
+#include "arrayfunction_override.h"
+
+
+/**
+ * Small wrapper converting to array just like CPython does.
+ *
+ * We could use our own PyArray_PyIntAsInt function, but it handles floats
+ * differently.
+ * A disadvantage of this function compared to ``PyArg_*("i")`` code is that
+ * it will not say which parameter is wrong.
+ *
+ * @param obj The python object to convert
+ * @param value The output value
+ *
+ * @returns 0 on failure and 1 on success (`NPY_FAIL`, `NPY_SUCCEED`)
+ */
+NPY_NO_EXPORT int
+PyArray_PythonPyIntFromInt(PyObject *obj, int *value)
+{
+ /* Pythons behaviour is to check only for float explicitly... */
+ if (NPY_UNLIKELY(PyFloat_Check(obj))) {
+ PyErr_SetString(PyExc_TypeError,
+ "integer argument expected, got float");
+ return NPY_FAIL;
+ }
+
+ long result = PyLong_AsLong(obj);
+ if (NPY_UNLIKELY((result == -1) && PyErr_Occurred())) {
+ return NPY_FAIL;
+ }
+ if (NPY_UNLIKELY((result > INT_MAX) || (result < INT_MIN))) {
+ PyErr_SetString(PyExc_OverflowError,
+ "Python int too large to convert to C int");
+ return NPY_FAIL;
+ }
+ else {
+ *value = (int)result;
+ return NPY_SUCCEED;
+ }
+}
+
+
+typedef int convert(PyObject *, void *);
+
+/**
+ * Internal function to initialize keyword argument parsing.
+ *
+ * This does a few simple jobs:
+ *
+ * * Check the input for consistency to find coding errors, for example
+ * a parameter not marked with | after one marked with | (optional).
+ * 2. Find the number of positional-only arguments, the number of
+ * total, required, and keyword arguments.
+ * 3. Intern all keyword arguments strings to allow fast, identity based
+ * parsing and avoid string creation overhead on each call.
+ *
+ * @param funcname Name of the function, mainly used for errors.
+ * @param cache A cache object stored statically in the parsing function
+ * @param va_orig Argument list to npy_parse_arguments
+ * @return 0 on success, -1 on failure
+ */
+static int
+initialize_keywords(const char *funcname,
+ _NpyArgParserCache *cache, va_list va_orig) {
+ va_list va;
+ int nargs = 0;
+ int nkwargs = 0;
+ int npositional_only = 0;
+ int nrequired = 0;
+ int npositional = 0;
+ char state = '\0';
+
+ va_copy(va, va_orig);
+ while (1) {
+ /* Count length first: */
+ char *name = va_arg(va, char *);
+ convert *converter = va_arg(va, convert *);
+ void *data = va_arg(va, void *);
+
+ /* Check if this is the sentinel, only converter may be NULL */
+ if ((name == NULL) && (converter == NULL) && (data == NULL)) {
+ break;
+ }
+
+ if (name == NULL) {
+ PyErr_Format(PyExc_SystemError,
+ "NumPy internal error: name is NULL in %s() at "
+ "argument %d.", funcname, nargs);
+ va_end(va);
+ return -1;
+ }
+ if (data == NULL) {
+ PyErr_Format(PyExc_SystemError,
+ "NumPy internal error: data is NULL in %s() at "
+ "argument %d.", funcname, nargs);
+ va_end(va);
+ return -1;
+ }
+
+ nargs += 1;
+ if (*name == '|') {
+ if (state == '$') {
+ PyErr_Format(PyExc_SystemError,
+ "NumPy internal error: positional argument `|` "
+ "after keyword only `$` one to %s() at argument %d.",
+ funcname, nargs);
+ va_end(va);
+ return -1;
+ }
+ state = '|';
+ name++; /* advance to actual name. */
+ npositional += 1;
+ }
+ else if (*name == '$') {
+ state = '$';
+ name++; /* advance to actual name. */
+ }
+ else {
+ if (state != '\0') {
+ PyErr_Format(PyExc_SystemError,
+ "NumPy internal error: non-required argument after "
+ "required | or $ one to %s() at argument %d.",
+ funcname, nargs);
+ va_end(va);
+ return -1;
+ }
+
+ nrequired += 1;
+ npositional += 1;
+ }
+
+ if (*name == '\0') {
+ /* Empty string signals positional only */
+ if (state != '\0') {
+ PyErr_Format(PyExc_SystemError,
+ "NumPy internal error: non-kwarg marked with | or $ "
+ "to %s() at argument %d.", funcname, nargs);
+ va_end(va);
+ return -1;
+ }
+ npositional_only += 1;
+ }
+ else {
+ nkwargs += 1;
+ }
+ }
+ va_end(va);
+
+ if (npositional == -1) {
+ npositional = nargs;
+ }
+
+ if (nargs > _NPY_MAX_KWARGS) {
+ PyErr_Format(PyExc_SystemError,
+ "NumPy internal error: function %s() has %d arguments, but "
+ "the maximum is currently limited to %d for easier parsing; "
+ "it can be increased by modifying `_NPY_MAX_KWARGS`.",
+ funcname, nargs, _NPY_MAX_KWARGS);
+ return -1;
+ }
+
+ /*
+ * Do any necessary string allocation and interning,
+ * creating a caching object.
+ */
+ cache->nargs = nargs;
+ cache->npositional_only = npositional_only;
+ cache->npositional = npositional;
+ cache->nrequired = nrequired;
+
+ /* NULL kw_strings for easier cleanup (and NULL termination) */
+ memset(cache->kw_strings, 0, sizeof(PyObject *) * (nkwargs + 1));
+
+ va_copy(va, va_orig);
+ for (int i = 0; i < nargs; i++) {
+ /* Advance through non-kwargs, which do not require setup. */
+ char *name = va_arg(va, char *);
+ va_arg(va, convert *);
+ va_arg(va, void *);
+
+ if (*name == '|' || *name == '$') {
+ name++; /* ignore | and $ */
+ }
+ if (i >= npositional_only) {
+ int i_kwarg = i - npositional_only;
+ cache->kw_strings[i_kwarg] = PyUString_InternFromString(name);
+ if (cache->kw_strings[i_kwarg] == NULL) {
+ va_end(va);
+ goto error;
+ }
+ }
+ }
+
+ va_end(va);
+ return 0;
+
+error:
+ for (int i = 0; i < nkwargs; i++) {
+ Py_XDECREF(cache->kw_strings[i]);
+ }
+ cache->npositional = -1; /* not initialized */
+ return -1;
+}
+
+
+static int
+raise_incorrect_number_of_positional_args(const char *funcname,
+ const _NpyArgParserCache *cache, Py_ssize_t len_args)
+{
+ if (cache->npositional == cache->nrequired) {
+ PyErr_Format(PyExc_TypeError,
+ "%s() takes %d positional arguments but %zd were given",
+ funcname, cache->npositional, len_args);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "%s() takes from %d to %d positional arguments but "
+ "%zd were given",
+ funcname, cache->nrequired, cache->npositional, len_args);
+ }
+ return -1;
+}
+
+static void
+raise_missing_argument(const char *funcname,
+ const _NpyArgParserCache *cache, int i)
+{
+ if (i < cache->npositional_only) {
+ PyErr_Format(PyExc_TypeError,
+ "%s() missing required positional argument %d",
+ funcname, i);
+ }
+ else {
+ PyObject *kw = cache->kw_strings[i - cache->npositional_only];
+ PyErr_Format(PyExc_TypeError,
+ "%s() missing required argument '%S' (pos %d)",
+ funcname, kw, i);
+ }
+}
+
+
+/**
+ * Generic helper for argument parsing
+ *
+ * See macro version for an example pattern of how to use this function.
+ *
+ * @param funcname
+ * @param cache
+ * @param args Python passed args (METH_FASTCALL)
+ * @param len_args
+ * @param kwnames
+ * @param ... List of arguments (see macro version).
+ *
+ * @return Returns 0 on success and -1 on failure.
+ */
+NPY_NO_EXPORT int
+_npy_parse_arguments(const char *funcname,
+ /* cache_ptr is a NULL initialized persistent storage for data */
+ _NpyArgParserCache *cache,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames,
+ /* ... is NULL, NULL, NULL terminated: name, converter, value */
+ ...)
+{
+ if (NPY_UNLIKELY(cache->npositional == -1)) {
+ va_list va;
+ va_start(va, kwnames);
+
+ int res = initialize_keywords(funcname, cache, va);
+ va_end(va);
+ if (res < 0) {
+ return -1;
+ }
+ }
+
+ if (NPY_UNLIKELY(len_args > cache->npositional)) {
+ return raise_incorrect_number_of_positional_args(
+ funcname, cache, len_args);
+ }
+
+ /* NOTE: Could remove the limit but too many kwargs are slow anyway. */
+ PyObject *all_arguments[NPY_MAXARGS];
+
+ for (Py_ssize_t i = 0; i < len_args; i++) {
+ all_arguments[i] = args[i];
+ }
+
+ /* Without kwargs, do not iterate all converters. */
+ int max_nargs = (int)len_args;
+ Py_ssize_t len_kwargs = 0;
+
+ /* If there are any kwargs, first handle them */
+ if (NPY_LIKELY(kwnames != NULL)) {
+ len_kwargs = PyTuple_GET_SIZE(kwnames);
+ max_nargs = cache->nargs;
+
+ for (int i = len_args; i < cache->nargs; i++) {
+ all_arguments[i] = NULL;
+ }
+
+ for (Py_ssize_t i = 0; i < len_kwargs; i++) {
+ PyObject *key = PyTuple_GET_ITEM(kwnames, i);
+ PyObject *value = args[i + len_args];
+ PyObject *const *name;
+
+ /* Super-fast path, check identity: */
+ for (name = cache->kw_strings; *name != NULL; name++) {
+ if (*name == key) {
+ break;
+ }
+ }
+ if (NPY_UNLIKELY(*name == NULL)) {
+ /* Slow fallback, if identity checks failed for some reason */
+ for (name = cache->kw_strings; *name != NULL; name++) {
+ int eq = PyObject_RichCompareBool(*name, key, Py_EQ);
+ if (eq == -1) {
+ return -1;
+ }
+ else if (eq) {
+ break;
+ }
+ }
+ if (NPY_UNLIKELY(*name == NULL)) {
+ /* Invalid keyword argument. */
+ PyErr_Format(PyExc_TypeError,
+ "%s() got an unexpected keyword argument '%S'",
+ funcname, key);
+ return -1;
+ }
+ }
+
+ ssize_t param_pos = (
+ (name - cache->kw_strings) + cache->npositional_only);
+
+ /* There could be an identical positional argument */
+ if (NPY_UNLIKELY(all_arguments[param_pos] != NULL)) {
+ PyErr_Format(PyExc_TypeError,
+ "argument for %s() given by name ('%S') and position "
+ "(position %zd)", funcname, key, param_pos);
+ return -1;
+ }
+
+ all_arguments[param_pos] = value;
+ }
+ }
+
+ /*
+ * There cannot be too many args, too many kwargs would find an
+ * incorrect one above.
+ */
+ assert(len_args + len_kwargs <= cache->nargs);
+
+ /* At this time `all_arguments` holds either NULLs or the objects */
+ va_list va;
+ va_start(va, kwnames);
+
+ for (int i = 0; i < max_nargs; i++) {
+ va_arg(va, char *);
+ convert *converter = va_arg(va, convert *);
+ void *data = va_arg(va, void *);
+
+ if (all_arguments[i] == NULL) {
+ continue;
+ }
+
+ int res;
+ if (converter == NULL) {
+ *((PyObject **) data) = all_arguments[i];
+ continue;
+ }
+ res = converter(all_arguments[i], data);
+
+ if (NPY_UNLIKELY(res == NPY_SUCCEED)) {
+ continue;
+ }
+ else if (NPY_UNLIKELY(res == NPY_FAIL)) {
+ /* It is usually the users responsibility to clean up. */
+ goto converting_failed;
+ }
+ else if (NPY_UNLIKELY(res == Py_CLEANUP_SUPPORTED)) {
+ /* TODO: Implementing cleanup if/when needed should not be hard */
+ PyErr_Format(PyExc_SystemError,
+ "converter cleanup of parameter %d to %s() not supported.",
+ i, funcname);
+ goto converting_failed;
+ }
+ assert(0);
+ }
+
+ /* Required arguments are typically not passed as keyword arguments */
+ if (NPY_UNLIKELY(len_args < cache->nrequired)) {
+ /* (PyArg_* also does this after the actual parsing is finished) */
+ if (NPY_UNLIKELY(max_nargs < cache->nrequired)) {
+ raise_missing_argument(funcname, cache, max_nargs);
+ goto converting_failed;
+ }
+ for (int i = 0; i < cache->nrequired; i++) {
+ if (NPY_UNLIKELY(all_arguments[i] == NULL)) {
+ raise_missing_argument(funcname, cache, i);
+ goto converting_failed;
+ }
+ }
+ }
+
+ va_end(va);
+ return 0;
+
+converting_failed:
+ va_end(va);
+ return -1;
+
+}
diff --git a/numpy/core/src/common/npy_argparse.h b/numpy/core/src/common/npy_argparse.h
new file mode 100644
index 000000000..5da535c91
--- /dev/null
+++ b/numpy/core/src/common/npy_argparse.h
@@ -0,0 +1,96 @@
+#ifndef _NPY_ARGPARSE_H
+#define _NPY_ARGPARSE_H
+
+#include "Python.h"
+#include "numpy/ndarraytypes.h"
+
+/*
+ * This file defines macros to help with keyword argument parsing.
+ * This solves two issues as of now:
+ * 1. Pythons C-API PyArg_* keyword argument parsers are slow, due to
+ * not caching the strings they use.
+ * 2. It allows the use of METH_ARGPARSE (and `tp_vectorcall`)
+ * when available in Python, which removes a large chunk of overhead.
+ *
+ * Internally CPython achieves similar things by using a code generator
+ * argument clinic. NumPy may well decide to use argument clinic or a different
+ * solution in the future.
+ */
+
+NPY_NO_EXPORT int
+PyArray_PythonPyIntFromInt(PyObject *obj, int *value);
+
+
+#define _NPY_MAX_KWARGS 15
+
+typedef struct {
+ int npositional;
+ int nargs;
+ int npositional_only;
+ int nrequired;
+ /* Null terminated list of keyword argument name strings */
+ PyObject *kw_strings[_NPY_MAX_KWARGS+1];
+} _NpyArgParserCache;
+
+
+/*
+ * The sole purpose of this macro is to hide the argument parsing cache.
+ * Since this cache must be static, this also removes a source of error.
+ */
+#define NPY_PREPARE_ARGPARSER static _NpyArgParserCache __argparse_cache = {-1}
+
+/**
+ * Macro to help with argument parsing.
+ *
+ * The pattern for using this macro is by defining the method as:
+ *
+ * @code
+ * static PyObject *
+ * my_method(PyObject *self,
+ * PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
+ * {
+ * NPY_PREPARE_ARGPARSER;
+ *
+ * PyObject *argument1, *argument3;
+ * int argument2 = -1;
+ * if (npy_parse_arguments("method", args, len_args, kwnames),
+ * "argument1", NULL, &argument1,
+ * "|argument2", &PyArray_PythonPyIntFromInt, &argument2,
+ * "$argument3", NULL, &argument3,
+ * NULL, NULL, NULL) < 0) {
+ * return NULL;
+ * }
+ * }
+ * @endcode
+ *
+ * The `NPY_PREPARE_ARGPARSER` macro sets up a static cache variable necessary
+ * to hold data for speeding up the parsing. `npy_parse_arguments` must be
+ * used in cunjunction with the macro defined in the same scope.
+ * (No two `npy_parse_arguments` may share a single `NPY_PREPARE_ARGPARSER`.)
+ *
+ * @param funcname
+ * @param args Python passed args (METH_FASTCALL)
+ * @param len_args Number of arguments (not flagged)
+ * @param kwnames Tuple as passed by METH_FASTCALL or NULL.
+ * @param ... List of arguments must be param1_name, param1_converter,
+ * *param1_outvalue, param2_name, ..., NULL, NULL, NULL.
+ * Where name is ``char *``, ``converter`` a python converter
+ * function or NULL and ``outvalue`` is the ``void *`` passed to
+ * the converter (holding the converted data or a borrowed
+ * reference if converter is NULL).
+ *
+ * @return Returns 0 on success and -1 on failure.
+ */
+NPY_NO_EXPORT int
+_npy_parse_arguments(const char *funcname,
+ /* cache_ptr is a NULL initialized persistent storage for data */
+ _NpyArgParserCache *cache_ptr,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames,
+ /* va_list is NULL, NULL, NULL terminated: name, converter, value */
+ ...) NPY_GCC_NONNULL(1);
+
+#define npy_parse_arguments(funcname, args, len_args, kwnames, ...) \
+ _npy_parse_arguments(funcname, &__argparse_cache, \
+ args, len_args, kwnames, __VA_ARGS__)
+
+#endif /* _NPY_ARGPARSE_H */
diff --git a/numpy/core/src/multiarray/_multiarray_tests.c.src b/numpy/core/src/multiarray/_multiarray_tests.c.src
index 3c8caefce..febcc8512 100644
--- a/numpy/core/src/multiarray/_multiarray_tests.c.src
+++ b/numpy/core/src/multiarray/_multiarray_tests.c.src
@@ -7,6 +7,7 @@
#include "numpy/npy_math.h"
#include "numpy/halffloat.h"
#include "common.h"
+#include "npy_argparse.h"
#include "mem_overlap.h"
#include "npy_extint128.h"
#include "array_method.h"
@@ -19,6 +20,25 @@
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static PyObject *
+argparse_example_function(PyObject *NPY_UNUSED(mod),
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
+{
+ NPY_PREPARE_ARGPARSER;
+ int arg1;
+ PyObject *arg2, *arg3, *arg4;
+ if (npy_parse_arguments("func", args, len_args, kwnames,
+ "", &PyArray_PythonPyIntFromInt, &arg1,
+ "arg2", NULL, &arg2,
+ "|arg3", NULL, &arg3,
+ "$arg3", NULL, &arg4,
+ NULL, NULL, NULL) < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
/* test PyArray_IsPythonScalar, before including private py3 compat header */
static PyObject *
IsPythonScalar(PyObject * dummy, PyObject *args)
@@ -2255,6 +2275,9 @@ run_intp_converter(PyObject* NPY_UNUSED(self), PyObject *args)
}
static PyMethodDef Multiarray_TestsMethods[] = {
+ {"argparse_example_function",
+ (PyCFunction)argparse_example_function,
+ METH_KEYWORDS | METH_FASTCALL, NULL},
{"IsPythonScalar",
IsPythonScalar,
METH_VARARGS, NULL},
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index 04ce53ed7..ff5a5d8bc 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -10,6 +10,7 @@
#include "numpy/arrayscalars.h"
#include "arrayfunction_override.h"
+#include "npy_argparse.h"
#include "npy_config.h"
#include "npy_pycompat.h"
#include "npy_import.h"
@@ -103,20 +104,23 @@ forward_ndarray_method(PyArrayObject *self, PyObject *args, PyObject *kwds,
static PyObject *
-array_take(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_take(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
int dimension = NPY_MAXDIMS;
PyObject *indices;
PyArrayObject *out = NULL;
NPY_CLIPMODE mode = NPY_RAISE;
- static char *kwlist[] = {"indices", "axis", "out", "mode", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O&O&:take", kwlist,
- &indices,
- PyArray_AxisConverter, &dimension,
- PyArray_OutputConverter, &out,
- PyArray_ClipmodeConverter, &mode))
+ if (npy_parse_arguments("take", args, len_args, kwnames,
+ "indices", NULL, &indices,
+ "|axis", &PyArray_AxisConverter, &dimension,
+ "|out", &PyArray_OutputConverter, &out,
+ "|mode", &PyArray_ClipmodeConverter, &mode,
+ NULL, NULL, NULL) < 0) {
return NULL;
+ }
PyObject *ret = PyArray_TakeFrom(self, indices, dimension, out, mode);
@@ -199,14 +203,16 @@ array_reshape(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
static PyObject *
-array_squeeze(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_squeeze(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
PyObject *axis_in = NULL;
npy_bool axis_flags[NPY_MAXDIMS];
+ NPY_PREPARE_ARGPARSER;
- static char *kwlist[] = {"axis", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:squeeze", kwlist,
- &axis_in)) {
+ if (npy_parse_arguments("squeeze", args, len_args, kwnames,
+ "|axis", NULL, &axis_in,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
@@ -224,16 +230,18 @@ array_squeeze(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
static PyObject *
-array_view(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_view(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
PyObject *out_dtype = NULL;
PyObject *out_type = NULL;
PyArray_Descr *dtype = NULL;
+ NPY_PREPARE_ARGPARSER;
- static char *kwlist[] = {"dtype", "type", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:view", kwlist,
- &out_dtype,
- &out_type)) {
+ if (npy_parse_arguments("view", args, len_args, kwnames,
+ "|dtype", NULL, &out_dtype,
+ "|type", NULL, &out_type,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
@@ -271,16 +279,19 @@ array_view(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
static PyObject *
-array_argmax(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_argmax(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
int axis = NPY_MAXDIMS;
PyArrayObject *out = NULL;
- static char *kwlist[] = {"axis", "out", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&:argmax", kwlist,
- PyArray_AxisConverter, &axis,
- PyArray_OutputConverter, &out))
+ if (npy_parse_arguments("argmax", args, len_args, kwnames,
+ "|axis", &PyArray_AxisConverter, &axis,
+ "|out", &PyArray_OutputConverter, &out,
+ NULL, NULL, NULL) < 0) {
return NULL;
+ }
PyObject *ret = PyArray_ArgMax(self, axis, out);
@@ -294,16 +305,19 @@ array_argmax(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
static PyObject *
-array_argmin(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_argmin(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
int axis = NPY_MAXDIMS;
PyArrayObject *out = NULL;
- static char *kwlist[] = {"axis", "out", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&:argmin", kwlist,
- PyArray_AxisConverter, &axis,
- PyArray_OutputConverter, &out))
+ if (npy_parse_arguments("argmin", args, len_args, kwnames,
+ "|axis", &PyArray_AxisConverter, &axis,
+ "|out", &PyArray_OutputConverter, &out,
+ NULL, NULL, NULL) < 0) {
return NULL;
+ }
PyObject *ret = PyArray_ArgMin(self, axis, out);
@@ -804,10 +818,9 @@ array_setscalar(PyArrayObject *self, PyObject *args)
static PyObject *
-array_astype(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_astype(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
- static char *kwlist[] = {"dtype", "order", "casting",
- "subok", "copy", NULL};
PyArray_Descr *dtype = NULL;
/*
* TODO: UNSAFE default for compatibility, I think
@@ -816,13 +829,15 @@ array_astype(PyArrayObject *self, PyObject *args, PyObject *kwds)
NPY_CASTING casting = NPY_UNSAFE_CASTING;
NPY_ORDER order = NPY_KEEPORDER;
int forcecopy = 1, subok = 1;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&ii:astype", kwlist,
- PyArray_DescrConverter, &dtype,
- PyArray_OrderConverter, &order,
- PyArray_CastingConverter, &casting,
- &subok,
- &forcecopy)) {
+ NPY_PREPARE_ARGPARSER;
+
+ if (npy_parse_arguments("astype", args, len_args, kwnames,
+ "dtype", &PyArray_DescrConverter, &dtype,
+ "|order", &PyArray_OrderConverter, &order,
+ "|casting", &PyArray_CastingConverter, &casting,
+ "|subok", &PyArray_PythonPyIntFromInt, &subok,
+ "|copy", &PyArray_PythonPyIntFromInt, &forcecopy,
+ NULL, NULL, NULL) < 0) {
Py_XDECREF(dtype);
return NULL;
}
@@ -1143,13 +1158,15 @@ array_function(PyArrayObject *NPY_UNUSED(self), PyObject *c_args, PyObject *c_kw
}
static PyObject *
-array_copy(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_copy(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
NPY_ORDER order = NPY_CORDER;
- static char *kwlist[] = {"order", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:copy", kwlist,
- PyArray_OrderConverter, &order)) {
+ if (npy_parse_arguments("copy", args, len_args, kwnames,
+ "|order", PyArray_OrderConverter, &order,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
@@ -1257,7 +1274,8 @@ array_choose(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
static PyObject *
-array_sort(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_sort(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
int axis=-1;
int val;
@@ -1265,12 +1283,13 @@ array_sort(PyArrayObject *self, PyObject *args, PyObject *kwds)
PyObject *order = NULL;
PyArray_Descr *saved = NULL;
PyArray_Descr *newd;
- static char *kwlist[] = {"axis", "kind", "order", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO&O:sort", kwlist,
- &axis,
- PyArray_SortkindConverter, &sortkind,
- &order)) {
+ if (npy_parse_arguments("sort", args, len_args, kwnames,
+ "|axis", &PyArray_PythonPyIntFromInt, &axis,
+ "|kind", &PyArray_SortkindConverter, &sortkind,
+ "|order", NULL, &order,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
if (order == Py_None) {
@@ -1313,7 +1332,8 @@ array_sort(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
static PyObject *
-array_partition(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_partition(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
int axis=-1;
int val;
@@ -1321,16 +1341,16 @@ array_partition(PyArrayObject *self, PyObject *args, PyObject *kwds)
PyObject *order = NULL;
PyArray_Descr *saved = NULL;
PyArray_Descr *newd;
- static char *kwlist[] = {"kth", "axis", "kind", "order", NULL};
PyArrayObject * ktharray;
PyObject * kthobj;
+ NPY_PREPARE_ARGPARSER;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO&O:partition", kwlist,
- &kthobj,
- &axis,
- PyArray_SelectkindConverter, &sortkind,
- &order)) {
+ if (npy_parse_arguments("partition", args, len_args, kwnames,
+ "kth", NULL, &kthobj,
+ "|axis", &PyArray_PythonPyIntFromInt, &axis,
+ "|kind", &PyArray_SelectkindConverter, &sortkind,
+ "|order", NULL, &order,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
@@ -1381,18 +1401,20 @@ array_partition(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
static PyObject *
-array_argsort(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_argsort(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
int axis = -1;
NPY_SORTKIND sortkind = NPY_QUICKSORT;
PyObject *order = NULL, *res;
PyArray_Descr *newd, *saved=NULL;
- static char *kwlist[] = {"axis", "kind", "order", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&O:argsort", kwlist,
- PyArray_AxisConverter, &axis,
- PyArray_SortkindConverter, &sortkind,
- &order)) {
+ if (npy_parse_arguments("argsort", args, len_args, kwnames,
+ "|axis", &PyArray_AxisConverter, &axis,
+ "|kind", &PyArray_SortkindConverter, &sortkind,
+ "|order", NULL, &order,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
if (order == Py_None) {
@@ -1433,21 +1455,23 @@ array_argsort(PyArrayObject *self, PyObject *args, PyObject *kwds)
static PyObject *
-array_argpartition(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_argpartition(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
int axis = -1;
NPY_SELECTKIND sortkind = NPY_INTROSELECT;
PyObject *order = NULL, *res;
PyArray_Descr *newd, *saved=NULL;
- static char *kwlist[] = {"kth", "axis", "kind", "order", NULL};
PyObject * kthobj;
PyArrayObject * ktharray;
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O&O:argpartition", kwlist,
- &kthobj,
- PyArray_AxisConverter, &axis,
- PyArray_SelectkindConverter, &sortkind,
- &order)) {
+ if (npy_parse_arguments("argpartition", args, len_args, kwnames,
+ "kth", NULL, &kthobj,
+ "|axis", &PyArray_AxisConverter, &axis,
+ "|kind", &PyArray_SelectkindConverter, &sortkind,
+ "|order", NULL, &order,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
if (order == Py_None) {
@@ -1494,17 +1518,20 @@ array_argpartition(PyArrayObject *self, PyObject *args, PyObject *kwds)
}
static PyObject *
-array_searchsorted(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_searchsorted(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
- static char *kwlist[] = {"v", "side", "sorter", NULL};
PyObject *keys;
PyObject *sorter;
NPY_SEARCHSIDE side = NPY_SEARCHLEFT;
+ NPY_PREPARE_ARGPARSER;
sorter = NULL;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O:searchsorted",
- kwlist, &keys,
- PyArray_SearchsideConverter, &side, &sorter)) {
+ if (npy_parse_arguments("searchsorted", args, len_args, kwnames,
+ "v", NULL, &keys,
+ "|side", &PyArray_SearchsideConverter, &side,
+ "|sorter", NULL, &sorter,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
if (sorter == Py_None) {
@@ -2285,14 +2312,17 @@ array_cumprod(PyArrayObject *self, PyObject *args, PyObject *kwds)
static PyObject *
-array_dot(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_dot(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
PyObject *a = (PyObject *)self, *b, *o = NULL;
PyArrayObject *ret;
- static char* kwlist[] = {"b", "out", NULL};
+ NPY_PREPARE_ARGPARSER;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:dot", kwlist, &b, &o)) {
+ if (npy_parse_arguments("dot", args, len_args, kwnames,
+ "b", NULL, &b,
+ "|out", NULL, &o,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
@@ -2374,20 +2404,22 @@ array_nonzero(PyArrayObject *self, PyObject *args)
static PyObject *
-array_trace(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_trace(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
int axis1 = 0, axis2 = 1, offset = 0;
PyArray_Descr *dtype = NULL;
PyArrayObject *out = NULL;
int rtype;
- static char *kwlist[] = {"offset", "axis1", "axis2", "dtype", "out", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiO&O&:trace", kwlist,
- &offset,
- &axis1,
- &axis2,
- PyArray_DescrConverter2, &dtype,
- PyArray_OutputConverter, &out)) {
+ NPY_PREPARE_ARGPARSER;
+
+ if (npy_parse_arguments("trace", args, len_args, kwnames,
+ "|offset", &PyArray_PythonPyIntFromInt, &offset,
+ "|axis1", &PyArray_PythonPyIntFromInt, &axis1,
+ "|axis2", &PyArray_PythonPyIntFromInt, &axis2,
+ "|dtype", &PyArray_DescrConverter2, &dtype,
+ "|out", &PyArray_OutputConverter, &out,
+ NULL, NULL, NULL) < 0) {
Py_XDECREF(dtype);
return NULL;
}
@@ -2448,13 +2480,15 @@ array_diagonal(PyArrayObject *self, PyObject *args, PyObject *kwds)
static PyObject *
-array_flatten(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_flatten(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
NPY_ORDER order = NPY_CORDER;
- static char *kwlist[] = {"order", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:flatten", kwlist,
- PyArray_OrderConverter, &order)) {
+ if (npy_parse_arguments("flatten", args, len_args, kwnames,
+ "|order", PyArray_OrderConverter, &order,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
return PyArray_Flatten(self, order);
@@ -2462,13 +2496,15 @@ array_flatten(PyArrayObject *self, PyObject *args, PyObject *kwds)
static PyObject *
-array_ravel(PyArrayObject *self, PyObject *args, PyObject *kwds)
+array_ravel(PyArrayObject *self,
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
NPY_ORDER order = NPY_CORDER;
- static char *kwlist[] = {"order", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:ravel", kwlist,
- PyArray_OrderConverter, &order)) {
+ if (npy_parse_arguments("ravel", args, len_args, kwnames,
+ "|order", PyArray_OrderConverter, &order,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
return PyArray_Ravel(self, order);
@@ -2724,19 +2760,19 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"argmax",
(PyCFunction)array_argmax,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"argmin",
(PyCFunction)array_argmin,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"argpartition",
(PyCFunction)array_argpartition,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"argsort",
(PyCFunction)array_argsort,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"astype",
(PyCFunction)array_astype,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"byteswap",
(PyCFunction)array_byteswap,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -2757,7 +2793,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS, NULL},
{"copy",
(PyCFunction)array_copy,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"cumprod",
(PyCFunction)array_cumprod,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -2769,13 +2805,13 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"dot",
(PyCFunction)array_dot,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"fill",
(PyCFunction)array_fill,
METH_VARARGS, NULL},
{"flatten",
(PyCFunction)array_flatten,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"getfield",
(PyCFunction)array_getfield,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -2802,7 +2838,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS, NULL},
{"partition",
(PyCFunction)array_partition,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"prod",
(PyCFunction)array_prod,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -2814,7 +2850,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"ravel",
(PyCFunction)array_ravel,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"repeat",
(PyCFunction)array_repeat,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -2829,7 +2865,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"searchsorted",
(PyCFunction)array_searchsorted,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"setfield",
(PyCFunction)array_setfield,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -2838,10 +2874,10 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"sort",
(PyCFunction)array_sort,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"squeeze",
(PyCFunction)array_squeeze,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"std",
(PyCFunction)array_stddev,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -2853,7 +2889,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS, NULL},
{"take",
(PyCFunction)array_take,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"tobytes",
(PyCFunction)array_tobytes,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -2868,7 +2904,7 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"trace",
(PyCFunction)array_trace,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"transpose",
(PyCFunction)array_transpose,
METH_VARARGS, NULL},
@@ -2877,6 +2913,6 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"view",
(PyCFunction)array_view,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{NULL, NULL, 0, NULL} /* sentinel */
};
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index 7915c75be..a0f7afeb5 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -26,7 +26,7 @@
#include "numpy/arrayscalars.h"
#include "numpy/npy_math.h"
-
+#include "npy_argparse.h"
#include "npy_config.h"
#include "npy_pycompat.h"
#include "npy_import.h"
@@ -2829,28 +2829,36 @@ array_fastCopyAndTranspose(PyObject *NPY_UNUSED(dummy), PyObject *args)
}
static PyObject *
-array_correlate(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
+array_correlate(PyObject *NPY_UNUSED(dummy),
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
PyObject *shape, *a0;
int mode = 0;
- static char *kwlist[] = {"a", "v", "mode", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|i:correlate", kwlist,
- &a0, &shape, &mode)) {
+ if (npy_parse_arguments("correlate", args, len_args, kwnames,
+ "a", NULL, &a0,
+ "v", NULL, &shape,
+ "|mode", &PyArray_PythonPyIntFromInt, &mode,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
return PyArray_Correlate(a0, shape, mode);
}
static PyObject*
-array_correlate2(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
+array_correlate2(PyObject *NPY_UNUSED(dummy),
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
PyObject *shape, *a0;
int mode = 0;
- static char *kwlist[] = {"a", "v", "mode", NULL};
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|i:correlate2", kwlist,
- &a0, &shape, &mode)) {
+ if (npy_parse_arguments("correlate2", args, len_args, kwnames,
+ "a", NULL, &a0,
+ "v", NULL, &shape,
+ "|mode", &PyArray_PythonPyIntFromInt, &mode,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
return PyArray_Correlate2(a0, shape, mode);
@@ -3426,6 +3434,42 @@ array_datetime_data(PyObject *NPY_UNUSED(dummy), PyObject *args)
return res;
}
+
+static int
+trimmode_converter(PyObject *obj, TrimMode *trim)
+{
+ if (!PyUnicode_Check(obj) || PyUnicode_GetLength(obj) != 1) {
+ goto error;
+ }
+ const char *trimstr = PyUnicode_AsUTF8AndSize(obj, NULL);
+
+ if (trimstr != NULL) {
+ if (trimstr[0] == 'k') {
+ *trim = TrimMode_None;
+ }
+ else if (trimstr[0] == '.') {
+ *trim = TrimMode_Zeros;
+ }
+ else if (trimstr[0] == '0') {
+ *trim = TrimMode_LeaveOneZero;
+ }
+ else if (trimstr[0] == '-') {
+ *trim = TrimMode_DptZeros;
+ }
+ else {
+ goto error;
+ }
+ }
+ return NPY_SUCCEED;
+
+error:
+ PyErr_Format(PyExc_TypeError,
+ "if supplied, trim must be 'k', '.', '0' or '-' found `%100S`",
+ obj);
+ return NPY_FAIL;
+}
+
+
/*
* Prints floating-point scalars using the Dragon4 algorithm, scientific mode.
* See docstring of `np.format_float_scientific` for description of arguments.
@@ -3433,43 +3477,28 @@ array_datetime_data(PyObject *NPY_UNUSED(dummy), PyObject *args)
* precision, which is equivalent to `None`.
*/
static PyObject *
-dragon4_scientific(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
+dragon4_scientific(PyObject *NPY_UNUSED(dummy),
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
PyObject *obj;
- static char *kwlist[] = {"x", "precision", "unique", "sign", "trim",
- "pad_left", "exp_digits", NULL};
int precision=-1, pad_left=-1, exp_digits=-1;
- char *trimstr=NULL;
DigitMode digit_mode;
TrimMode trim = TrimMode_None;
int sign=0, unique=1;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iiisii:dragon4_scientific",
- kwlist, &obj, &precision, &unique, &sign, &trimstr, &pad_left,
- &exp_digits)) {
+ NPY_PREPARE_ARGPARSER;
+
+ if (npy_parse_arguments("dragon4_scientific", args, len_args, kwnames,
+ "x", NULL , &obj,
+ "|precision", &PyArray_PythonPyIntFromInt, &precision,
+ "|unique", &PyArray_PythonPyIntFromInt, &unique,
+ "|sign", &PyArray_PythonPyIntFromInt, &sign,
+ "|trim", &trimmode_converter, &trim,
+ "|pad_left", &PyArray_PythonPyIntFromInt, &pad_left,
+ "|exp_digits", &PyArray_PythonPyIntFromInt, &exp_digits,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
- if (trimstr != NULL) {
- if (strcmp(trimstr, "k") == 0) {
- trim = TrimMode_None;
- }
- else if (strcmp(trimstr, ".") == 0) {
- trim = TrimMode_Zeros;
- }
- else if (strcmp(trimstr, "0") == 0) {
- trim = TrimMode_LeaveOneZero;
- }
- else if (strcmp(trimstr, "-") == 0) {
- trim = TrimMode_DptZeros;
- }
- else {
- PyErr_SetString(PyExc_TypeError,
- "if supplied, trim must be 'k', '.', '0' or '-'");
- return NULL;
- }
- }
-
digit_mode = unique ? DigitMode_Unique : DigitMode_Exact;
if (unique == 0 && precision < 0) {
@@ -3489,44 +3518,30 @@ dragon4_scientific(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
* precision, which is equivalent to `None`.
*/
static PyObject *
-dragon4_positional(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
+dragon4_positional(PyObject *NPY_UNUSED(dummy),
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
PyObject *obj;
- static char *kwlist[] = {"x", "precision", "unique", "fractional",
- "sign", "trim", "pad_left", "pad_right", NULL};
int precision=-1, pad_left=-1, pad_right=-1;
- char *trimstr=NULL;
CutoffMode cutoff_mode;
DigitMode digit_mode;
TrimMode trim = TrimMode_None;
int sign=0, unique=1, fractional=0;
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iiiisii:dragon4_positional",
- kwlist, &obj, &precision, &unique, &fractional, &sign, &trimstr,
- &pad_left, &pad_right)) {
+ NPY_PREPARE_ARGPARSER;
+
+ if (npy_parse_arguments("dragon4_positional", args, len_args, kwnames,
+ "x", NULL , &obj,
+ "|precision", &PyArray_PythonPyIntFromInt, &precision,
+ "|unique", &PyArray_PythonPyIntFromInt, &unique,
+ "|fractional", &PyArray_PythonPyIntFromInt, &fractional,
+ "|sign", &PyArray_PythonPyIntFromInt, &sign,
+ "|trim", &trimmode_converter, &trim,
+ "|pad_left", &PyArray_PythonPyIntFromInt, &pad_left,
+ "|pad_right", &PyArray_PythonPyIntFromInt, &pad_right,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
- if (trimstr != NULL) {
- if (strcmp(trimstr, "k") == 0) {
- trim = TrimMode_None;
- }
- else if (strcmp(trimstr, ".") == 0) {
- trim = TrimMode_Zeros;
- }
- else if (strcmp(trimstr, "0") == 0) {
- trim = TrimMode_LeaveOneZero;
- }
- else if (strcmp(trimstr, "-") == 0) {
- trim = TrimMode_DptZeros;
- }
- else {
- PyErr_SetString(PyExc_TypeError,
- "if supplied, trim must be 'k', '.', '0' or '-'");
- return NULL;
- }
- }
-
digit_mode = unique ? DigitMode_Unique : DigitMode_Exact;
cutoff_mode = fractional ? CutoffMode_FractionLength :
CutoffMode_TotalLength;
@@ -4052,15 +4067,19 @@ array_may_share_memory(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *
}
static PyObject *
-normalize_axis_index(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
+normalize_axis_index(PyObject *NPY_UNUSED(self),
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
- static char *kwlist[] = {"axis", "ndim", "msg_prefix", NULL};
int axis;
int ndim;
PyObject *msg_prefix = Py_None;
+ NPY_PREPARE_ARGPARSER;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii|O:normalize_axis_index",
- kwlist, &axis, &ndim, &msg_prefix)) {
+ if (npy_parse_arguments("normalize_axis_index", args, len_args, kwnames,
+ "axis", &PyArray_PythonPyIntFromInt, &axis,
+ "ndim", &PyArray_PythonPyIntFromInt, &ndim,
+ "|msg_prefix", NULL, &msg_prefix,
+ NULL, NULL, NULL) < 0) {
return NULL;
}
if (check_and_adjust_axis_msg(&axis, ndim, msg_prefix) < 0) {
@@ -4191,10 +4210,10 @@ static struct PyMethodDef array_module_methods[] = {
METH_VARARGS, NULL},
{"correlate",
(PyCFunction)array_correlate,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"correlate2",
(PyCFunction)array_correlate2,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"frombuffer",
(PyCFunction)array_frombuffer,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -4241,10 +4260,10 @@ static struct PyMethodDef array_module_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"dragon4_positional",
(PyCFunction)dragon4_positional,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"dragon4_scientific",
(PyCFunction)dragon4_scientific,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"compare_chararrays",
(PyCFunction)compare_chararrays,
METH_VARARGS | METH_KEYWORDS, NULL},
@@ -4277,7 +4296,7 @@ static struct PyMethodDef array_module_methods[] = {
{"unpackbits", (PyCFunction)io_unpack,
METH_VARARGS | METH_KEYWORDS, NULL},
{"normalize_axis_index", (PyCFunction)normalize_axis_index,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"set_legacy_print_mode", (PyCFunction)set_legacy_print_mode,
METH_VARARGS, NULL},
{"_discover_array_parameters", (PyCFunction)_discover_array_parameters,
diff --git a/numpy/core/tests/test_argparse.py b/numpy/core/tests/test_argparse.py
new file mode 100644
index 000000000..63a01dee4
--- /dev/null
+++ b/numpy/core/tests/test_argparse.py
@@ -0,0 +1,62 @@
+"""
+Tests for the private NumPy argument parsing functionality.
+They mainly exists to ensure good test coverage without having to try the
+weirder cases on actual numpy functions but test them in one place.
+
+The test function is defined in C to be equivalent to (errors may not always
+match exactly, and could be adjusted):
+
+ def func(arg1, /, arg2, *, arg3):
+ i = integer(arg1) # reproducing the 'i' parsing in Python.
+ return None
+"""
+
+import pytest
+
+import numpy as np
+from numpy.core._multiarray_tests import argparse_example_function as func
+
+
+def test_invalid_integers():
+ with pytest.raises(TypeError,
+ match="integer argument expected, got float"):
+ func(1.)
+ with pytest.raises(OverflowError):
+ func(2**100)
+
+
+def test_missing_arguments():
+ with pytest.raises(TypeError,
+ match="missing required positional argument 0"):
+ func()
+ with pytest.raises(TypeError,
+ match="missing required positional argument 0"):
+ func(arg2=1, arg3=4)
+ with pytest.raises(TypeError,
+ match=r"missing required argument \'arg2\' \(pos 1\)"):
+ func(1, arg3=5)
+
+
+def test_too_many_positional():
+ # the second argument is positional but can be passed as keyword.
+ with pytest.raises(TypeError,
+ match="takes from 2 to 3 positional arguments but 4 were given"):
+ func(1, 2, 3, 4)
+
+
+def test_multiple_values():
+ with pytest.raises(TypeError,
+ match=r"given by name \('arg2'\) and position \(position 1\)"):
+ func(1, 2, arg2=3)
+
+
+def test_string_fallbacks():
+ # We can (currently?) use numpy strings to test the "slow" fallbacks
+ # that should normally not be taken due to string interning.
+ arg2 = np.unicode_("arg2")
+ missing_arg = np.unicode_("missing_arg")
+ func(1, **{arg2: 3})
+ with pytest.raises(TypeError,
+ match="got an unexpected keyword argument 'missing_arg'"):
+ func(2, **{missing_arg: 3})
+