Skip to content

BUG: number.__int__ and int(number) are not the same #9972

Closed

Description

@eric-wieser

As worked around in #9181, we have two implementation of every number.__int__, one in scalarmath (int), and one in scalartypes (__int__).

This happens because after calling PyType_Ready, we reassign tp_number, but the __int__ slot wrapper has already been created.

It's super misleading that this is the case, and exposes cpython implementation details.

It seems to me it would be safer to not set __int__ at all until we fix this, rather than having two possibly different implementation to trap the end user

Activity

  1. mattip commented on Nov 15, 2017

    @mattip
    Member

    Not only because of reassignment after PyType_Ready, also because of CPython quirks. For example, this code has different paths due to CPython not inheriting nb_int but inheriting __int__ via mro lookup.

    It successfully calls int(a) via int_new which eventually calls int_from_string in cpython2.7 abstract.c since PyStringArrType_Type.tp_from_number.nb_int is NULL.

    It fails with a.__int__() since type(a).mro() hits numpy.generic which has gentype_int as its nb_int

    >>> a = np.array(['100'])[0]
    >>> int(a)
    100
    >>> a.__int__()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: don't know how to convert scalar number to int
    
  2. mattip commented on Nov 15, 2017

    @mattip
    Member

    Just to clarify - PyStringArrType_Type.tp_as_number is not overwritten after PyType_Ready in addscalar_math, only the numeric types are overwritten, so this is purely a CPython method lookup quirk

  3. eric-wieser commented on Nov 17, 2017

    @eric-wieser
    MemberAuthor

    I'm wondering if the culprit is ndarray.__int__, which for some reason doesn't fall back on PyNumber_Int, instead trying to call the members directly.

  4. mattip commented on Nov 17, 2017

    @mattip
    Member

    That would add another layer of abstraction, slowing down the conversion for the 99% of the cases when a nb_int exists

  5. eric-wieser commented on Nov 17, 2017

    @eric-wieser
    MemberAuthor

    Right, but it also means that our implementation right now is incorrect. int(x) is also supposed to try __trunc__ if __int__ doesn't exist. #10042 fixes that.

  6. eric-wieser commented on Nov 17, 2017

    @eric-wieser
    MemberAuthor

    Another example:

    >>> a = np.array(None)
    >>> a[()] = bytearray('10')
    >>> int(a)
    TypeError: cannot convert to an int; scalar object is not a number
    >>> int(bytearray('10'))
    10
  7. mattip commented on Feb 20, 2019

    @mattip
    Member

    #10042 fixed these. These now work

    >>> a = np.array([b'100'])[0]
    >>> int(a)
    100
    >>> a.__int__()
    100
    

    and

    >>> a = np.array(None)
    >>> a[()] = bytearray(b'10')
    >>> int(a)
    10
    
  8. bsipocz commented on Oct 15, 2019

    @bsipocz
    Member

    It looks like everything works here now. Can this be closed @mattip?

  9. mattip commented on Dec 8, 2019

    @mattip
    Member

    Closing. Please reopen if more discrepancies appear

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions