Skip to content

MAINT/BUG: Fixups to scalar base classes #15393

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
Jan 23, 2020

Conversation

eric-wieser
Copy link
Member

This seems to be left from when np.int_ subclassed int, which it no longer does.

Change the if to an assert so that we can catch this type of change in future.

} \
/* now do default conversion */ \
}
assert (type->tp_bases && (PyTuple_GET_SIZE(type->tp_bases) == 2)); \
Copy link
Member Author

Choose a reason for hiding this comment

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

This assertion is failing for subclasses of np.float64. The fact that it is suggests that this code isn't doing what it's supposed to be doing anyway though...

Copy link
Member

Choose a reason for hiding this comment

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

This is patently scary... Is there a reason it is not hardcoding sup directly?

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed, very scary - fixed in the follow-up commit.

Copy link
Member

@seberg seberg Jan 23, 2020

Choose a reason for hiding this comment

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

Can +we remove the num completely, as far as I can tell, it basically maps to a known instance type?

Copy link
Member Author

Choose a reason for hiding this comment

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

It does indeed map to a known type, but getting hold of that type through the template mechanism is harder than grabbing it at runtime. Essentially you're choosing between:

class A(B, C):
    def __new__(cls, ...):
        C.__new__(...)

and

class A(B, C):
    def __new__(cls, ...):
        A.__bases__[1].__new__(...)

.

Both are completely correct, unlike what was there before which was outright stupid:

class A(B, C):
    def __new__(cls, ...):
        cls.__bases__[1].__new__(...)

Copy link
Member

Choose a reason for hiding this comment

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

Yes, the first seemed nicer to me? although not sure if we have the PyFloatType name available easily. Could also think about __bases__[-1]. in any case, not sure it would make things better or worse, so OK with just leaving it and not doing the last thing is a nice fix.

Copy link
Member Author

@eric-wieser eric-wieser Jan 23, 2020

Choose a reason for hiding this comment

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

We can't use -1 because for unicode_ and bytes_ it's __bases__[0], but for float_ it's __bases__[1] - for bizarre reasons the order is deliberately swapped.

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 suspect I will need to make further cleanups here to address #15385 anyway, in future PRs.

@eric-wieser eric-wieser changed the title MAINT: Remove leftover code which handled integer base classes MAINT/BUG: Fixups to scalar base classes Jan 23, 2020
Copy link
Member

@seberg seberg left a comment

Choose a reason for hiding this comment

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

OK, forget about the small stuff if you like)and just put it in yourself once the test pass. This code is really confusing, but the changes look all correct, and like no actual change in behaviour.

This seems to be left from when `np.int_` subclassed `int`, which it no longer does.

Change the `if` to an `assert` so that we can catch this type of change in future.
@eric-wieser
Copy link
Member Author

Rebased locally with the above changes

The previously code was equivalent to something like
```python
class SomeScalar(base1, base2):
    def __new__(cls, *args):
        return cls.__bases__[1].__new__(*args)
```
This is nonsense and can easily cause recursion, for instance if `cls` is a superclass of SomeScalar.
The correct way to spell this is to pin the base class call statically.
```python
class SomeScalar(base1, base2):
    def __new__(cls, *args):
        return SomeScalar.__bases__[1].__new__(*args)
```

Fixes numpy#15395
@mattip mattip merged commit 04ac2a1 into numpy:master Jan 23, 2020
@mattip
Copy link
Member

mattip commented Jan 23, 2020

Thanks @eric-wieser, @seberg

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