summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Haldane <ealloc@gmail.com>2017-09-28 17:51:35 +0200
committerGitHub <noreply@github.com>2017-09-28 17:51:35 +0200
commitcea77c21cd0907afc0093b7301d7824d73f7c740 (patch)
treeaff42687b117d5bf0fa89a32c79675987ea8d048
parenteb5dd4128e8323f5c4af089d2fea85e6159dd739 (diff)
parent0f63efa619a1f6dc0a2673be026c042807e31807 (diff)
downloadnumpy-cea77c21cd0907afc0093b7301d7824d73f7c740.tar.gz
Merge pull request #9784 from eric-wieser/fix-void-recursion
BUG: remove voidtype-repr recursion in scalartypes.c/arrayprint.py
-rw-r--r--doc/release/1.14.0-notes.rst17
-rw-r--r--numpy/core/arrayprint.py31
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src60
-rw-r--r--numpy/core/tests/test_arrayprint.py4
4 files changed, 71 insertions, 41 deletions
diff --git a/doc/release/1.14.0-notes.rst b/doc/release/1.14.0-notes.rst
index c9f1cec76..3224b80fd 100644
--- a/doc/release/1.14.0-notes.rst
+++ b/doc/release/1.14.0-notes.rst
@@ -256,17 +256,24 @@ Changes
0d arrays now use the array2string formatters to print their elements, like
other arrays. The ``style`` argument of ``array2string`` is now non-functional.
+User-defined types now need to implement ``__str__`` and ``__repr__``
+---------------------------------------------------------------------
+Previously, user-defined types could fall back to a default implementation of
+``__str__`` and ``__repr__`` implemented in numpy, but this has now been
+removed. Now user-defined types will fall back to the python default
+``object.__str__`` and ``object.__repr__``.
+
``np.linalg.matrix_rank`` is more efficient for hermitian matrices
------------------------------------------------------------------
The keyword argument ``hermitian`` was added to toggle between standard
SVD-based matrix rank calculation and the more efficient eigenvalue-based
method for symmetric/hermitian matrices.
-Integer scalars are now unaffected by ``np.set_string_function``
-----------------------------------------------------------------
-Previously the str/repr of integer scalars could be controlled by
-``np.set_string_function``, unlike most other numpy scalars. This is no longer
-the case.
+Integer and Void scalars are now unaffected by ``np.set_string_function``
+-------------------------------------------------------------------------
+Previously the ``str`` and ``repr`` of integer and void scalars could be
+controlled by ``np.set_string_function``, unlike most other numpy scalars. This
+is no longer the case.
Multiple-field indexing/assignment of structured arrays
-------------------------------------------------------
diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py
index e0da9f81e..e1df556ef 100644
--- a/numpy/core/arrayprint.py
+++ b/numpy/core/arrayprint.py
@@ -309,13 +309,7 @@ def _get_format_function(data, **options):
"""
dtype_ = data.dtype
if dtype_.fields is not None:
- format_functions = []
- for field_name in dtype_.names:
- format_function = _get_format_function(data[field_name], **options)
- if dtype_[field_name].shape != ():
- format_function = SubArrayFormat(format_function)
- format_functions.append(format_function)
- return StructureFormat(format_functions)
+ return StructureFormat.from_data(data, **options)
dtypeobj = dtype_.type
formatdict = _get_formatdict(data, **options)
@@ -873,6 +867,20 @@ class StructureFormat(object):
self.format_functions = format_functions
self.num_fields = len(format_functions)
+ @classmethod
+ def from_data(cls, data, **options):
+ """
+ This is a second way to initialize StructureFormat, using the raw data
+ as input. Added to avoid changing the signature of __init__.
+ """
+ format_functions = []
+ for field_name in data.dtype.names:
+ format_function = _get_format_function(data[field_name], **options)
+ if data.dtype[field_name].shape != ():
+ format_function = SubArrayFormat(format_function)
+ format_functions.append(format_function)
+ return cls(format_functions)
+
def __call__(self, x):
s = "("
for field, format_function in zip(x, self.format_functions):
@@ -880,6 +888,15 @@ class StructureFormat(object):
return (s[:-2] if 1 < self.num_fields else s[:-1]) + ")"
+def _void_scalar_repr(x):
+ """
+ Implements the repr for structured-void scalars. It is called from the
+ scalartypes.c.src code, and is placed here because it uses the elementwise
+ formatters defined above.
+ """
+ return StructureFormat.from_data(array(x), **_format_options)(x)
+
+
_typelessdata = [int_, float_, complex_]
if issubclass(intc, int):
_typelessdata.append(intc)
diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src
index 7a6ed6a86..c92d835ed 100644
--- a/numpy/core/src/multiarray/scalartypes.c.src
+++ b/numpy/core/src/multiarray/scalartypes.c.src
@@ -25,6 +25,7 @@
#include "_datetime.h"
#include "datetime_strings.h"
#include "alloc.h"
+#include "npy_import.h"
#include <stdlib.h>
@@ -339,33 +340,6 @@ gentype_nonzero_number(PyObject *m1)
}
static PyObject *
-gentype_str(PyObject *self)
-{
- PyObject *arr, *ret = NULL;
-
- arr = PyArray_FromScalar(self, NULL);
- if (arr != NULL) {
- ret = PyObject_Str((PyObject *)arr);
- Py_DECREF(arr);
- }
- return ret;
-}
-
-static PyObject *
-gentype_repr(PyObject *self)
-{
- PyObject *arr, *ret = NULL;
-
- arr = PyArray_FromScalar(self, NULL);
- if (arr != NULL) {
- /* XXX: Why are we using str here? */
- ret = PyObject_Str((PyObject *)arr);
- Py_DECREF(arr);
- }
- return ret;
-}
-
-static PyObject *
genint_type_str(PyObject *self)
{
PyObject *item, *item_str;
@@ -634,6 +608,34 @@ static PyObject *
/**end repeat**/
static PyObject *
+voidtype_str(PyObject *self)
+{
+ if (PyDataType_HASFIELDS(((PyVoidScalarObject*)self)->descr)) {
+ static PyObject *reprfunc = NULL;
+
+ npy_cache_import("numpy.core.arrayprint",
+ "_void_scalar_repr", &reprfunc);
+ if (reprfunc == NULL) {
+ return NULL;
+ }
+
+ return PyObject_CallFunction(reprfunc, "O", self);
+ }
+ else {
+ PyObject *item, *item_str;
+
+ item = gentype_generic_method(self, NULL, NULL, "item");
+ if (item == NULL) {
+ return NULL;
+ }
+
+ item_str = PyObject_Str(item);
+ Py_DECREF(item);
+ return item_str;
+ }
+}
+
+static PyObject *
datetimetype_repr(PyObject *self)
{
PyDatetimeScalarObject *scal;
@@ -4073,8 +4075,6 @@ initialize_numeric_types(void)
PyGenericArrType_Type.tp_new = NULL;
PyGenericArrType_Type.tp_alloc = gentype_alloc;
PyGenericArrType_Type.tp_free = (freefunc)gentype_free;
- PyGenericArrType_Type.tp_repr = gentype_repr;
- PyGenericArrType_Type.tp_str = gentype_str;
PyGenericArrType_Type.tp_richcompare = gentype_richcompare;
PyBoolArrType_Type.tp_as_number = &bool_arrtype_as_number;
@@ -4122,6 +4122,8 @@ initialize_numeric_types(void)
PyVoidArrType_Type.tp_getset = voidtype_getsets;
PyVoidArrType_Type.tp_as_mapping = &voidtype_as_mapping;
PyVoidArrType_Type.tp_as_sequence = &voidtype_as_sequence;
+ PyVoidArrType_Type.tp_repr = voidtype_str;
+ PyVoidArrType_Type.tp_str = voidtype_str;
PyIntegerArrType_Type.tp_getset = inttype_getsets;
diff --git a/numpy/core/tests/test_arrayprint.py b/numpy/core/tests/test_arrayprint.py
index b03229447..26faabfb8 100644
--- a/numpy/core/tests/test_arrayprint.py
+++ b/numpy/core/tests/test_arrayprint.py
@@ -60,6 +60,10 @@ class TestArrayRepr(object):
assert_equal(repr(arr1d),
'array([list([1, 2]), list([3])], dtype=object)')
+ def test_void_scalar_recursion(self):
+ # gh-9345
+ repr(np.void(b'test')) # RecursionError ?
+
class TestComplexArray(object):
def test_str(self):