8000 ENH: Implement ndarray.__format__ for 0d arrays by eric-wieser · Pull Request #9883 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: Implement ndarray.__format__ for 0d arrays #9883

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

Merged
merged 2 commits into from
Oct 18, 2017

Conversation

eric-wieser
Copy link
Member
@eric-wieser eric-wieser commented Oct 18, 2017

This fixes gh-7978, by making the array behavior match the scalar behaviour

The behavior for other sized arrays is left unchanged, pending discussion in gh-5543

Similar to #9201 if you squint, but hopefully not controversial.

This fixes numpygh-7978

The behavior for other sized arrays is left unchanged, pending discussion in numpygh-5543
if (PyArray_NDIM(self) == 0) {
PyObject *item = PyArray_ToScalar(PyArray_DATA(self), self);
PyObject *res;
if (item == NULL) {
Copy link
Member

Choose a reason for hiding this comment

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

Blank line here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Above this line, I assume you mean?

Copy link
Member

Choose a reason for hiding this comment

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

Yep.


def test_0d(self):
a = np.array(np.pi)
assert_equal('{:0.3g}'.format(a), '3.14')
Copy link
Member

Choose a reason for hiding this comment

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

Is assert_equal the best way to compare strings? Maybe just assert_(str1 == str2) would be a bit cleaner.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, because then it shows the two values if they differ.

Copy link
Member

Choose a reason for hiding this comment

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

OK.

Copy link
Contributor

Choose a reason for hiding this comment

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

In pytest you can just assert str1 == str2 and you will get values if there is a failure.


def test_failure():
    a = 'apple'
    b = 'banana'
    assert a == b

Output is

================================== FAILURES ===================================
________________________________ test_failure _________________________________

    def test_failure():
        a = 'apple'
        b = 'banana'
>       assert a == b
E       AssertionError: assert 'apple' == 'banana'
E         - apple
E         + banana

test_util.py:20: AssertionError
========================== 1 failed in 0.24 seconds ===========================

Copy link
Member Author

Choose a reason for hiding this comment

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

For consistency, we should probably use the same form we use in most of the test suite, which gives:

>>> np.testing.assert_equal('a', 'b')
AssertionError: 
Items are not equal:
 ACTUAL: 'a'
 DESIRED: 'b'

AFAIK, the switch to pytest was recent, and we were previously using nose

Copy link
Member

Choose a reason for hiding this comment

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

We are still using nose, the pytest conversion is still in progress and I don't think I will get back to it until 1.15.

Py_DECREF(item);
return res;
}
/* Everything else - use the builtin */
Copy link
Member

Choose a reason for hiding this comment

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

What does the builtin do here?

Copy link
Member Author
@eric-wieser eric-wieser Oct 18, 2017

Choose a reason for hiding this comment

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

It keeps the arbitrary and python-dependent behavior that we had before this patch.

Most importantly it handles '{}'.format in the typical way. On python 3, it throws an exception if a format string is given, while on python 2 it evaluates format(str(arr), 'theformatspec').

@charris
Copy link
Member
charris commented Oct 18, 2017

Seems reasonable, just a couple of nits and some questions.

@charris
Copy link
Member
charris commented Oct 18, 2017

Added the blank line. If @ahaldane signs off I'll merge this (or he can).

Copy link
Member
@ahaldane ahaldane left a comment

Choose a reason for hiding this comment

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

yep, LGTM.

I don't think there is any controversy over what 0ds should print here, since we already decided that the str should print like a scalar.

@ahaldane
Copy link
Member

I will push the button.

Thanks for a nice fix @eric-wieser!

# Could switch on python version here, but all we care about is
# that the behaviour hasn't changed
assert_equal(
ret_and_exc(object.__format__, a, '30'),
Copy link
Member Author

Choose a reason for hiding this comment

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

I suppose we should silence warnings in ret_and_exc, or return a third tuple item which is the warning

Copy link
Member

Choose a reason for hiding this comment

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

Maybe just figure out what the result should be and use that? Eventually object.__format__ is going to fail with non-empty argument.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, and I think that ndarray.__format__ should start failing at the same time. Maybe we should just make ndarray.__format__ fail with an empty argument right now...

Copy link
Member

Choose a reason for hiding this comment

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

Looks like it went away in Python 3.4.

* :meth:`object.__format__` no longer accepts non-empty format strings, it now
  raises a :exc:`TypeError` instead.  Using a non-empty string has been
  deprecated since Python 3.2.  This change has been made to prevent a
  situation where previously working (but incorrect) code would start failing
  if an object gained a __format__ method, which means that your code may now
  raise a :exc:`TypeError` if you are using an ``'s'`` format code with objects
  that do not have a __format__ method that handles it.  See :issue:`7994` for
  background.

Should ours work if an empty string is passed?

Copy link
Member

Choose a reason for hiding this comment

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

I fixed this by making the test Python version dependent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

.format does not play well with a 0-D NumPy array
4 participants
0