Skip to content

Conversation

@eendebakpt
Copy link
Contributor

Addresses part of #28800. Also see the discussion in #29523.

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.

Nice, I much prefer this, I think! I don't care if we even consider _set_shape() semi-public. It's the users problem to know that they are mutating a fresh array if they insist, although in general, I hope there isn't really a reason to ever do so.

(__array_finalize__ is hopefully one of the very few ones.)

PyErr_SetString(PyExc_AttributeError,
"Cannot delete array shape");
return -1;
}
Copy link
Member

Choose a reason for hiding this comment

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

I guess we can delete the "cannot delete" path here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. This is supposes to be called from the python side or via array_shape_set (which checks on val), so val cannot be null. I will add an assert just in case.

Setting the shape attribute is now deprecated since mutating
an array is unsafe if an array is shared, especially by multiple
threads. As an alternative, you can create a new view via
`np.reshape` or `array.reshape`. For example: `x = np.arange(15); x = np.reshape(x, (3, 5))`.
Copy link
Member

Choose a reason for hiding this comment

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

We should point out the option to use reshape(..., copy=False) to ensure an error (which is one reason why some places used the shape attribute setting).

We could actually roll that out in the places where things were changed here, although overall, I am not really worried about accidental copies here.

I feel we should mention _set_shape() somewhere maybe. Probably just here? I.e. say that if there is no alternative and you know that you are the only owner (for example within __array_finalize__) setting the shape is still possible via _set_shape()). This is mostly used by matrices although we discourage the behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a mention to _set_shape without further explanation (to discourage usage).

/* Deprecated NumPy 2.4, 2025-07-29 */
if (DEPRECATE("Setting the shape on a NumPy array has been deprecated"
" in NumPy 2.4.\nAs an alternative, you can create a new"
" view using np.reshape."
Copy link
Member

Choose a reason for hiding this comment

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

Could also mention copy=False here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Specifically, maybe add " using np.reshape (with copy=False if needed)."

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.

FWIW, I am happy to try this, if others agree mildly let's give it a shot. Might apply the suggestiong or do a minor thing, but I think this should be ready rally.

@eendebakpt
Copy link
Contributor Author

Scipy and astropy are using the shape setting. I creates issues to keep track scipy/scipy#23522, astropy/astropy#18561.

Copy link
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

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

I think this all looks good! I really have only very minor comments -- to be honest hardly worth running CI again, but maybe just...

# switch first and second axis
output[0].shape = (1, -1) + s0[2:]
output[1].shape = (-1, 1) + s0[2:]
output[0] = output[0].reshape((1, -1) + s0[2:])
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd tend to instead generate output separately for this case, but obviously no big deal.

Copy link
Contributor

Choose a reason for hiding this comment

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

Still think if makes more sense to do,

if indexing == 'xy' and ndim > 1:
    output = [np.asanyarray(x).reshape((1, -1) + s0[2:]
              for x in xi]
else:
    output = [np.asanyarray(x).reshape(s0[:i] + (-1,) + s0[i + 1:])
              for i, x in enumerate(xi)]

But I can also see that you want to minimize changes...

`np.reshape` or `np.ndarray.reshape`. For example: ``x = np.arange(15); x = np.reshape(x, (3, 5))``.
To ensure no copy is made from the data, one can use ``np.reshape(..., copy = False)``.

Directly setting the shape on an array is possible with the private method `np.ndarray._set_shape`.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd add ", though is discouraged" (on a new line).

/* Deprecated NumPy 2.4, 2025-07-29 */
if (DEPRECATE("Setting the shape on a NumPy array has been deprecated"
" in NumPy 2.4.\nAs an alternative, you can create a new"
" view using np.reshape."
Copy link
Contributor

Choose a reason for hiding this comment

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

Specifically, maybe add " using np.reshape (with copy=False if needed)."

@mhvk
Copy link
Contributor

mhvk commented Aug 28, 2025

p.s. I just saw that #29523 is still alive. Is that still being considered, or are we just going for it here? (I'm fine with that.)

@eendebakpt
Copy link
Contributor Author

p.s. I just saw that #29523 is still alive. Is that still being considered, or are we just going for it here? (I'm fine with that.)

No decision has been made on which version to use iirc, but I am fine with this one.

Copy link
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

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

Basically ready! My only real comment is about the changelog snippet. The rest is mostly attempts to make usage more uniform.

b.shape = (10,)

assert_raises(AttributeError, rs)
with warnings.catch_warnings(): # gh-28901
Copy link
Contributor

Choose a reason for hiding this comment

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

Since python 3.11, one can just do with warnings.catch_warnings(action="ignore", category=DeprecationWarning).

Though I think just using pytest.warns(DeprecationWarning), as you do below, is cleaner, and would suggest you use that everywhere.

# switch first and second axis
output[0].shape = (1, -1) + s0[2:]
output[1].shape = (-1, 1) + s0[2:]
output[0] = output[0].reshape((1, -1) + s0[2:])
Copy link
Contributor

Choose a reason for hiding this comment

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

Still think if makes more sense to do,

if indexing == 'xy' and ndim > 1:
    output = [np.asanyarray(x).reshape((1, -1) + s0[2:]
              for x in xi]
else:
    output = [np.asanyarray(x).reshape(s0[:i] + (-1,) + s0[i + 1:])
              for i, x in enumerate(xi)]

But I can also see that you want to minimize changes...

if np.asarray(b).ndim == 1:
expect_resids.shape = (1,)
expect_resids = expect_resids.reshape((1,))
assert_equal(residuals.shape, expect_resids.shape)
Copy link
Contributor

Choose a reason for hiding this comment

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

Surely, this line should be outside the if...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I agree, changed.

@eendebakpt eendebakpt requested a review from mhvk October 29, 2025 22:12
Copy link
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

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

Looks all good to me, thanks!

@seberg
Copy link
Member

seberg commented Nov 4, 2025

I think we even checked and fixed larger downstream packages for this, right? Because in that case I think we can try to do this (can always revert if there is larger fallout).
If not, maybe let's wait until after branching since it may be a bit noisier (although I have to say that a deprecation doesn't break an end-user workflow, worst case it may slow it a bit).

@eendebakpt
Copy link
Contributor Author

If I recall correctly astropy, h5py and scipy have been addressed and there are PRs open for yt-project. scikit-learn still has a couple of deprecated operations I think.

@seberg
Copy link
Member

seberg commented Nov 8, 2025

Thanks, I am tempted to wait for 2.5 then. If we are worried about fix, I think there is far more disruption to worry about here.

@eendebakpt
Copy link
Contributor Author

Thanks, I am tempted to wait for 2.5 then. If we are worried about fix, I think there is far more disruption to worry about here.

@seberg Is this ready to go in?

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.

4 participants