diff options
author | Allan Haldane <ealloc@gmail.com> | 2017-09-28 17:51:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-28 17:51:35 +0200 |
commit | cea77c21cd0907afc0093b7301d7824d73f7c740 (patch) | |
tree | aff42687b117d5bf0fa89a32c79675987ea8d048 | |
parent | eb5dd4128e8323f5c4af089d2fea85e6159dd739 (diff) | |
parent | 0f63efa619a1f6dc0a2673be026c042807e31807 (diff) | |
download | numpy-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.rst | 17 | ||||
-rw-r--r-- | numpy/core/arrayprint.py | 31 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.c.src | 60 | ||||
-rw-r--r-- | numpy/core/tests/test_arrayprint.py | 4 |
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): |