Skip to content

BUG: Allow int to be called on nested object arrays, fix np.str_.__int__ - #10042

Merged
ahaldane merged 1 commit into
numpy:masterfrom
eric-wieser:better-ndarray.__int__
Nov 22, 2017
Merged

ahaldane merged 1 commit into
numpy:masterfrom
eric-wieser:better-ndarray.__int__

Conversation

@eric-wieser

@eric-wieser eric-wieser commented Nov 17, 2017 •

Copy link
Copy Markdown
Member

Rather than assuming that any object array is self-referencing, we can just use PyEnter_RecursiveCall, in:

  • __int__
  • __float__
  • __long__
  • __hex__
  • __oct__

This works towards (but does not fix) #9972, by not trying to touch the nb_* slots ourselves (the same way as #10040).

Substantial code deduplication here. Error message is different, but perhaps also better:

>>> int(np.array([None]))
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType' # now
TypeError: cannot convert to an int; scalar object is not a number #before

@eric-wieser eric-wieser changed the title BUG: Allow int to be called on nested object arrays BUG: Allow int to be called on nested object arrays, fix np.str_.__int__ Nov 17, 2017
Comment thread numpy/core/src/multiarray/number.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"module" is not defined, you probably meant "mod"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch, only tested on python 3 locally

Comment thread numpy/core/src/multiarray/number.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"module" -> "mod" ?

Comment thread numpy/core/tests/test_regression.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

RecursionError is a python3-ism, is there a trick for python 2?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

test_multiarray does:

        try:
            Error = RecursionError
        except NameError:
            Error = RuntimeError  # python < 3.5
        assert_raises(Error, bool, self_containing)  # previously stack overflow

@eric-wieser eric-wieser Nov 17, 2017 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Turns out it was me that wrote the code in multiarray... Again, good catch!

@eric-wieser
eric-wieser force-pushed the better-ndarray.__int__ branch from 93a0254 to 413c38a Compare November 17, 2017 07:55
@eric-wieser
eric-wieser force-pushed the better-ndarray.__int__ branch 2 times, most recently from b353de3 to a4c30ee Compare November 17, 2017 07:57
@eric-wieser

eric-wieser commented Nov 17, 2017 •

Copy link
Copy Markdown
Member Author

Something fishy going on here on py 2... EDIT: PyCallMethodObjArgs didn't have the signature I thought it did. Solved.

@eric-wieser
eric-wieser force-pushed the better-ndarray.__int__ branch from a4c30ee to a4c44aa Compare November 17, 2017 08:10
@mattip

mattip commented Nov 17, 2017

Copy link
Copy Markdown
Member

document in release notes?

@eric-wieser

Copy link
Copy Markdown
Member Author

Not sure it's note-worthy, since this is a bugfix for a bunch of contrived corner-cases. I don't think it has any compatibility ramifications either.

Comment thread numpy/core/src/multiarray/number.c Outdated

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In principle we could use templating instead of function pointers, but we don't really care about speed here.

@eric-wieser
eric-wieser force-pushed the better-ndarray.__int__ branch 5 times, most recently from f6b7eda to 6c6da73 Compare November 17, 2017 08:49
@eric-wieser

eric-wieser commented Nov 17, 2017 •

Copy link
Copy Markdown
Member Author

Travis fails with:

numpy/core/src/multiarray/number.c: In function ‘array_scalar_forward’:
numpy/core/src/multiarray/number.c:842:9: warning: passing argument 1 of ‘_Py_CheckRecursiveCall’ discards ‘const’ qualifier from pointer target type [enabled by default]
         if (Py_EnterRecursiveCall(where) != 0) {
         ^
In file included from /usr/include/python3.4dm/Python.h:115:0,
                 from numpy/core/src/multiarray/number.c:2:
/usr/include/python3.4dm/ceval.h:80:17: note: expected ‘char *’ but argument is of type ‘const char *’
 PyAPI_FUNC(int) _Py_CheckRecursiveCall(char *where);
                 ^

However, this is a python bug, not a numpy bug - it's fixed in python/cpython@5fa22fc (3.4.4) and python/cpython@1670af6 (2.7.11), which are greater than or equal to the versions the python docs are generated from.

@mattip

mattip commented Nov 17, 2017

Copy link
Copy Markdown
Member

could you either skip the test or do a cast (for specifically that version)?

@mattip

mattip commented Nov 17, 2017

Copy link
Copy Markdown
Member

@eric-wieser This pull request is required for PyPy to pass the test suite. There is a regression somewhere from 1.13 wrt handling of string scalars, I am not sure exactly where. Could we mark the pull request for inclusion in 1.14?

@eric-wieser

eric-wieser commented Nov 17, 2017 •

Copy link
Copy Markdown
Member Author

If this truly is a regression, it should make 1.13.4 - but I'm surprised, as I don't remember changes that would cause this - indeed, the tests of string_.__int__ fail on 1.12 for me.

@eric-wieser
eric-wieser force-pushed the better-ndarray.__int__ branch from 6c6da73 to 1578dda Compare November 17, 2017 16:51
@eric-wieser eric-wieser modified the milestones: 1.13.4 release, 1.14.0 release Nov 17, 2017
@eric-wieser

Copy link
Copy Markdown
Member Author

Milestone added, along with workaround to squash the warning for old python

Rather than assuming that any object array is self-referencing, we can just use PyEnter_RecursiveCall in:

* `__int__`
* `__float__`
* `__long__`
* `__hex__`
* `__oct__`

This works towards (but does _not_ fix) numpy#9972, by not directly touching the `nb_*` slots ourselves.

Substantial code deduplication here. Error message is different, but perhaps also better:

```python
>>> int(np.array([None]))
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType' # now
TypeError: cannot convert to an int; scalar object is not a number #before
```
@eric-wieser
eric-wieser force-pushed the better-ndarray.__int__ branch from 1578dda to bcec12b Compare November 17, 2017 17:00
@ahaldane

Copy link
Copy Markdown
Member

Seems like a good cleanup, code LGTM. So it now it allows object arrays (that don't recurse), and uses the python-builtin conversion funcs instead of (badly) trying to do the conversion ourselves.

I don't totally see why this supersedes #10040 - that PR did adjustments of the tp_ slots. I presume we want to come back to the tp_ slot issues later. But on it's own this PR looks good.

@eric-wieser

eric-wieser commented Nov 18, 2017 •

Copy link
Copy Markdown
Member Author

#10040 was needed specifically because we did a bad job of doing the conversion ourselves - our approach didn't special-case string types like the builtin PyNumber_Long does. Now that we use PyNumber_Long directly, everything is fine.

It does seem a little weird that str.__int__ does not exist in CPython...

@mattip

mattip commented Nov 18, 2017 •

Copy link
Copy Markdown
Member

@eric-wieser wrt regression comment above, for PyPy tests started failing after commit 931758e (#9856), "BUG: Make bool(void_scalar) and void_scalar.astype(bool) consistent", which added extra calls to PyArray_Scalar in @from@_to_@to@ conversions. After this pull request those tests again pass

And yes, it is strange that str.__int__ does not exists but int(str) works

@eric-wieser

eric-wieser commented Nov 18, 2017 •

Copy link
Copy Markdown
Member Author

Thanks for the info - looks like I'm to blame for the accidental breakage, so it's fitting that I should accidentally fix it too.
Any idea why that change caused PyPy to fail? Can you point me to the currently failing test on 1.13?

@mattip

mattip commented Nov 18, 2017

Copy link
Copy Markdown
Member

That commit AFAICT is not part of a 1.13 release, as far as git tag --contains 931758e2 and git branch --contains 931758e2 can tell me

These tests newly fail after that commit (since in PyPy the logic in PyInt_FromString uses type.__int__ which reaches gentype_int which fails):

  • numpy.ma.tests.test_mrecords.TestMRecordsImport.test_fromarrays, raises TypeError: cannot convert to a float; scalar object is not a number
  • `numpy.ma.tests.test_core.TestFillingValues.test_fillvalue_conversionraisesTypeError: cannot convert to an int; scalar object is not a number``
  • numpy.core.tests.test_item_selection.TestTake.test_simple raises ``TypeError: cannot convert to an int; scalar object is not a number''
  • numpy.core.tests.test_indexing.TestFancyIndexingEquivalence.test_cast_equivalence raises TypeError: cannot convert to an int; scalar object is not a number

After this pull request those tests now pass

@eric-wieser

eric-wieser commented Nov 18, 2017 •

Copy link
Copy Markdown
Member Author

Got it - the problem is that before in conversions we were doing int(arr.item()), and after #9856 we're doing int(arr[()]). The incentive was I suppose to avoid calling void.item(), which is broken until we work out how to safely merge #8157.

You're right, it's not in 1.13, so this only needs to make it into 1.14 for there to be no regression.

@eric-wieser eric-wieser mentioned this pull request Nov 19, 2017
@ahaldane

Copy link
Copy Markdown
Member

I think this is ready to go. I'll merge in a little while if there are no further comments.

@ahaldane

Copy link
Copy Markdown
Member

Merging. Thanks Eric!

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.

3 participants