Skip to content
Merged
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
2 changes: 2 additions & 0 deletions doc/release/upcoming_changes/26611.expired.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* ``set_string_function`` internal function was removed and ``PyArray_SetStringFunction``
was stubbed out.
15 changes: 0 additions & 15 deletions doc/source/reference/c-api/array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4077,21 +4077,6 @@ extension with the lowest :c:data:`NPY_FEATURE_VERSION` as possible.
:c:data:`NPY_FEATURE_VERSION` changes whenever the API changes (e.g. a
function is added). A changed value does not always require a recompile.

Internal Flexibility
~~~~~~~~~~~~~~~~~~~~

.. c:function:: void PyArray_SetStringFunction(PyObject* op, int repr)

This function allows you to alter the tp_str and tp_repr methods
of the array object to any Python function. Thus you can alter
what happens for all arrays when str(arr) or repr(arr) is called
from Python. The function to be called is passed in as *op*. If
*repr* is non-zero, then this function will be called in response
to repr(arr), otherwise the function will be called in response to
str(arr). No check on whether or not *op* is callable is
performed. The callable passed in to *op* should expect an array
argument and should return a string to be printed.


Memory management
~~~~~~~~~~~~~~~~~
Expand Down
11 changes: 5 additions & 6 deletions doc/source/reference/c-api/types-and-structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,11 @@ The :c:data:`PyArray_Type` can also be sub-typed.

.. tip::

The ``tp_as_number`` methods use a generic approach to call whatever
function has been registered for handling the operation. When the
``_multiarray_umath module`` is imported, it sets the numeric operations
for all arrays to the corresponding ufuncs. This choice can be changed with
:c:func:`PyUFunc_ReplaceLoopBySignature` The ``tp_str`` and ``tp_repr``
methods can also be altered using :c:func:`PyArray_SetStringFunction`.
The :c:member:`tp_as_number <PyTypeObject.tp_as_number>` methods use
a generic approach to call whatever function has been registered for
handling the operation. When the ``_multiarray_umath`` module is imported,
it sets the numeric operations for all arrays to the corresponding ufuncs.
This choice can be changed with :c:func:`PyUFunc_ReplaceLoopBySignature`.

PyGenericArrType_Type
---------------------
Expand Down
1 change: 0 additions & 1 deletion numpy/__init__.cython-30.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,6 @@ cdef extern from "numpy/arrayobject.h":
# more than is probably needed until it can be checked further.
int PyArray_INCREF (ndarray) except * # uses PyArray_Item_INCREF...
int PyArray_XDECREF (ndarray) except * # uses PyArray_Item_DECREF...
void PyArray_SetStringFunction (object, int)
dtype PyArray_DescrFromType (int)
object PyArray_TypeObjectFromType (int)
char * PyArray_Zero (ndarray)
Expand Down
1 change: 0 additions & 1 deletion numpy/__init__.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,6 @@ cdef extern from "numpy/arrayobject.h":
# more than is probably needed until it can be checked further.
int PyArray_INCREF (ndarray) except * # uses PyArray_Item_INCREF...
int PyArray_XDECREF (ndarray) except * # uses PyArray_Item_DECREF...
void PyArray_SetStringFunction (object, int)
dtype PyArray_DescrFromType (int)
object PyArray_TypeObjectFromType (int)
char * PyArray_Zero (ndarray)
Expand Down
9 changes: 0 additions & 9 deletions numpy/_core/_add_newdocs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1846,15 +1846,6 @@

""")


add_newdoc('numpy._core.multiarray', 'set_string_function',
"""
set_string_function(f, repr=1)

Internal method to set a function to be used when pretty printing arrays.

""")

add_newdoc('numpy._core.multiarray', 'promote_types',
"""
promote_types(type1, type2)
Expand Down
98 changes: 17 additions & 81 deletions numpy/_core/arrayprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@
'formatter': None,
# Internally stored as an int to simplify comparisons; converted from/to
# str/False on the way in/out.
'legacy': sys.maxsize}
'legacy': sys.maxsize,
'override_repr': None,
}

def _make_options_dict(precision=None, threshold=None, edgeitems=None,
linewidth=None, suppress=None, nanstr=None, infstr=None,
sign=None, formatter=None, floatmode=None, legacy=None):
sign=None, formatter=None, floatmode=None, legacy=None,
override_repr=None):
"""
Make a dictionary out of the non-None arguments, plus conversion of
*legacy* and sanity checks.
Expand Down Expand Up @@ -119,7 +122,7 @@ def _make_options_dict(precision=None, threshold=None, edgeitems=None,
def set_printoptions(precision=None, threshold=None, edgeitems=None,
linewidth=None, suppress=None, nanstr=None,
infstr=None, formatter=None, sign=None, floatmode=None,
*, legacy=None):
*, legacy=None, override_repr=None):
"""
Set printing options.

Expand Down Expand Up @@ -224,6 +227,9 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,

.. versionadded:: 1.14.0
.. versionchanged:: 1.22.0
override_repr: callable, optional
If set a passed function will be used for generating arrays' repr.
Other options will be ignored.

See Also
--------
Expand Down Expand Up @@ -285,9 +291,10 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
"""
opt = _make_options_dict(precision, threshold, edgeitems, linewidth,
suppress, nanstr, infstr, sign, formatter,
floatmode, legacy)
# formatter is always reset
floatmode, legacy, override_repr)
# formatter and override_repr are always reset
opt['formatter'] = formatter
opt['override_repr'] = override_repr
_format_options.update(opt)

# set the C variable for legacy mode
Expand Down Expand Up @@ -333,7 +340,7 @@ def get_printoptions():
--------

>>> np.get_printoptions()
{'edgeitems': 3, 'threshold': 1000, ..., 'legacy': False}
{'edgeitems': 3, 'threshold': 1000, ..., 'override_repr': None}

>>> np.get_printoptions()['linewidth']
75
Expand Down Expand Up @@ -1552,6 +1559,10 @@ def _array_repr_implementation(
arr, max_line_width=None, precision=None, suppress_small=None,
array2string=array2string):
"""Internal version of array_repr() that allows overriding array2string."""
override_repr = _format_options["override_repr"]
if override_repr is not None:
return override_repr(arr)

if max_line_width is None:
max_line_width = _format_options['linewidth']

Expand Down Expand Up @@ -1727,78 +1738,3 @@ def array_str(a, max_line_width=None, precision=None, suppress_small=None):
array2string=_array2string_impl)
_default_array_repr = functools.partial(_array_repr_implementation,
array2string=_array2string_impl)


def set_string_function(f, repr=True):
"""
Set a Python function to be used when pretty printing arrays.

.. deprecated:: 2.0
Use `np.set_printoptions` instead with a formatter for custom
printing of NumPy objects.

Parameters
----------
f : function or None
Function to be used to pretty print arrays. The function should expect
a single array argument and return a string of the representation of
the array. If None, the function is reset to the default NumPy function
to print arrays.
repr : bool, optional
If True (default), the function for pretty printing (``__repr__``)
is set, if False the function that returns the default string
representation (``__str__``) is set.

See Also
--------
set_printoptions, get_printoptions

Examples
--------
>>> from numpy._core.arrayprint import set_string_function
>>> def pprint(arr):
... return 'HA! - What are you going to do now?'
...
>>> set_string_function(pprint)
>>> a = np.arange(10)
>>> a
HA! - What are you going to do now?
>>> _ = a
>>> # [0 1 2 3 4 5 6 7 8 9]

We can reset the function to the default:

>>> set_string_function(None)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

`repr` affects either pretty printing or normal string representation.
Note that ``__repr__`` is still affected by setting ``__str__``
because the width of each array element in the returned string becomes
equal to the length of the result of ``__str__()``.

>>> x = np.arange(4)
>>> set_string_function(lambda x:'random', repr=False)
>>> x.__str__()
'random'
>>> x.__repr__()
'array([0, 1, 2, 3])'

"""

# Deprecated in NumPy 2.0, 2023-07-11
warnings.warn(
"`set_string_function` is deprecated. Use `np.set_printoptions` "
"with a formatter for custom printing NumPy objects. "
"(deprecated in NumPy 2.0)",
DeprecationWarning,
stacklevel=2
)

if f is None:
if repr:
return multiarray.set_string_function(_default_array_repr, 1)
else:
return multiarray.set_string_function(_default_array_str, 0)
else:
return multiarray.set_string_function(f, repr)
3 changes: 2 additions & 1 deletion numpy/_core/arrayprint.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def set_printoptions(
sign: Literal[None, "-", "+", " "] = ...,
floatmode: None | _FloatMode = ...,
*,
legacy: Literal[None, False, "1.13", "1.21"] = ...
legacy: Literal[None, False, "1.13", "1.21"] = ...,
override_repr: None | Callable[[NDArray[Any]], str] = ...,
) -> None: ...
def get_printoptions() -> _FormatOptions: ...
def array2string(
Expand Down
2 changes: 2 additions & 0 deletions numpy/_core/code_generators/numpy_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ def get_annotations():
# Unused slot 41, was `PyArray_GetNumericOps`,
'PyArray_INCREF': (42,),
'PyArray_XDECREF': (43,),
# `PyArray_SetStringFunction` was stubbed out
# and should be removed in the future.
'PyArray_SetStringFunction': (44,),
'PyArray_DescrFromType': (45,),
'PyArray_TypeObjectFromType': (46,),
Expand Down
28 changes: 0 additions & 28 deletions numpy/_core/src/multiarray/multiarraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3176,31 +3176,6 @@ array__reconstruct(PyObject *NPY_UNUSED(dummy), PyObject *args)
return NULL;
}

static PyObject *
array_set_string_function(PyObject *NPY_UNUSED(self), PyObject *args,
PyObject *kwds)
{
PyObject *op = NULL;
int repr = 1;
static char *kwlist[] = {"f", "repr", NULL};

if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:set_string_function", kwlist, &op, &repr)) {
return NULL;
}
/* reset the array_repr function to built-in */
if (op == Py_None) {
op = NULL;
}
if (op != NULL && !PyCallable_Check(op)) {
PyErr_SetString(PyExc_TypeError,
"Argument must be callable.");
return NULL;
}
PyArray_SetStringFunction(op, repr);
Py_RETURN_NONE;
}


static PyObject *
array_set_datetimeparse_function(PyObject *NPY_UNUSED(self),
PyObject *NPY_UNUSED(args), PyObject *NPY_UNUSED(kwds))
Expand Down Expand Up @@ -4417,9 +4392,6 @@ static struct PyMethodDef array_module_methods[] = {
{"_reconstruct",
(PyCFunction)array__reconstruct,
METH_VARARGS, NULL},
{"set_string_function",
(PyCFunction)array_set_string_function,
METH_VARARGS|METH_KEYWORDS, NULL},
{"set_datetimeparse_function",
(PyCFunction)array_set_datetimeparse_function,
METH_VARARGS|METH_KEYWORDS, NULL},
Expand Down
31 changes: 1 addition & 30 deletions numpy/_core/src/multiarray/strfuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@
#include "npy_import.h"
#include "strfuncs.h"

static PyObject *PyArray_StrFunction = NULL;
static PyObject *PyArray_ReprFunction = NULL;


static void
npy_PyErr_SetStringChained(PyObject *type, const char *message)
{
Expand All @@ -30,34 +26,14 @@ npy_PyErr_SetStringChained(PyObject *type, const char *message)
NPY_NO_EXPORT void
PyArray_SetStringFunction(PyObject *op, int repr)
{
if (repr) {
/* Dispose of previous callback */
Py_XDECREF(PyArray_ReprFunction);
/* Add a reference to new callback */
Py_XINCREF(op);
/* Remember new callback */
PyArray_ReprFunction = op;
}
else {
/* Dispose of previous callback */
Py_XDECREF(PyArray_StrFunction);
/* Add a reference to new callback */
Py_XINCREF(op);
/* Remember new callback */
PyArray_StrFunction = op;
}
PyErr_SetString(PyExc_ValueError, "PyArray_SetStringFunction was removed");
}


NPY_NO_EXPORT PyObject *
array_repr(PyArrayObject *self)
{
static PyObject *repr = NULL;

if (PyArray_ReprFunction != NULL) {
return PyObject_CallFunctionObjArgs(PyArray_ReprFunction, self, NULL);
}

/*
* We need to do a delayed import here as initialization on module load
* leads to circular import problems.
Expand All @@ -76,11 +52,6 @@ NPY_NO_EXPORT PyObject *
array_str(PyArrayObject *self)
{
static PyObject *str = NULL;

if (PyArray_StrFunction != NULL) {
return PyObject_CallFunctionObjArgs(PyArray_StrFunction, self, NULL);
}

/*
* We need to do a delayed import here as initialization on module load leads
* to circular import problems.
Expand Down
11 changes: 11 additions & 0 deletions numpy/_core/tests/test_arrayprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,17 @@ def test_formatter_reset(self):
np.set_printoptions(formatter={'float_kind':None})
assert_equal(repr(x), "array([0., 1., 2.])")

def test_override_repr(self):
x = np.arange(3)
np.set_printoptions(override_repr=lambda x: "FOO")
assert_equal(repr(x), "FOO")
np.set_printoptions(override_repr=None)
assert_equal(repr(x), "array([0, 1, 2])")

with np.printoptions(override_repr=lambda x: "BAR"):
assert_equal(repr(x), "BAR")
assert_equal(repr(x), "array([0, 1, 2])")

def test_0d_arrays(self):
assert_equal(str(np.array('café', '<U4')), 'café')

Expand Down
6 changes: 3 additions & 3 deletions numpy/_core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,16 +594,16 @@ def assign(v):
)
def test_unicode_assignment(self):
# gh-5049
from numpy._core.arrayprint import set_string_function
from numpy._core.arrayprint import set_printoptions

@contextmanager
def inject_str(s):
""" replace ndarray.__str__ temporarily """
set_string_function(lambda x: s, repr=False)
set_printoptions(formatter={"all": lambda x: s})
try:
yield
finally:
set_string_function(None, repr=False)
set_printoptions()

a1d = np.array(['test'])
a0d = np.array('done')
Expand Down
Loading