FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

ENH: add NEP-50-style string promotion outside ufuncs by ngoldbaum · Pull Request #32356 · numpy/numpy · GitHub

/ numpy Public

ENH: add NEP-50-style string promotion outside ufuncs - #32356

Open
ngoldbaum wants to merge 3 commits into
numpy:mainfrom
ngoldbaum:fix-promotion-leftovers
Open

ENH: add NEP-50-style string promotion outside ufuncs#32356
ngoldbaum wants to merge 3 commits into
numpy:mainfrom
ngoldbaum:fix-promotion-leftovers

Conversation

ngoldbaum commented Aug 19, 2026
edited
Loading

Copy link
Copy Markdown
Member

PR summary

Followup for #32040

This adds NEP-50 style promotion for strings to copyto and where, which have explicit handling for numeric scalars. It also adds new promotion handling for concatenate and choose. These weren't needed before in concatenate and choose because numeric scalars don't need to do anything the the scalar value, only type metadata. Concatenate and choose correctly propagate that metadata but don't correctly propagate trailing NULLs.

The net effect is that trailing NULLs are now preserved for StringDType in all four operations, see tests.

AI Disclosure

I iterated on this with an AI model.

ngoldbaum force-pushed the fix-promotion-leftovers branch from 70812c6 to 53e0462 Compare August 19, 2026 21:46

mhvk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Looks good! Most comments just nits about organization.

p.s. There's part of me that still wonders whether str shouldn't be treated more just like the numerical scalars, but I guess we can always change that if needed.

Py_DECREF(item);
}

/* Explicit axes reject the 0-d str before resolving a descriptor */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This can be inside if (axis == NPY_RAVEL_AXIS) just below

}

if (axis == NPY_RAVEL_AXIS) {
ret = PyArray_ConcatenateFlattenedArrays(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I guess for numerical scalers, the check is done inside ConcatenateFlattenedArrays -- it would seem more logical to do the same for str as well (if for some reason that's just unhandy, maybe add a comment here that the same checks for numerical scalars are done in ConcatenateFlattenedArrays).

/* 'narrays' was set to how far we got in the conversion */
for (iarrays = 0; iarrays < narrays; ++iarrays) {
Py_DECREF(arrays[iarrays]);
Py_XDECREF(arrays[iarrays]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Why is this change necessary?

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I rewrote the PR so changing this isn't necessary anymore

Comment thread numpy/_core/tests/test_stringdtype.py Outdated
dst = np.empty(2, dtype=dtype)
np.copyto(dst, scalar)
assert dst[0] == scalar
np.copyto(dst, scalar, casting="unsafe")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I think you need to recreate the array to be sure the tests checks that copyto actually worked (since there was a copy already). Or is the casting="unsafe" needed if the array is already filled? If so, do a quick pytest.raises before with regular casting (and add a comment!).

Comment thread numpy/_core/tests/test_stringdtype.py Outdated

res = np.concatenate((arr, scalar), axis=None)
assert res.dtype == dtype
assert_array_equal(res, np.array(["y", scalar], dtype=dtype))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

If you add strict=True, you do not really need to assert the dtype above (though perhaps you prefer it for clarity).

ngoldbaum force-pushed the fix-promotion-leftovers branch from 53e0462 to b7dda5a Compare August 26, 2026 20:19

Copy link
Copy Markdown
Member Author

@mhvk thanks for the suggestions! I think I've addressed your comments, mostly to reorganize and delete code that you were commenting on. This is a lot simpler now. I'd appreciate it if you could give it another look.

mhvk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@ngoldbaum - it has indeed become a nice surgical addition! One remaining nitpick, but also a question about casting that struck me in the tests. I may just be missing something obvious, though!


for (iarrays = 0; iarrays < narrays; ++iarrays) {
Py_DECREF(arrays[iarrays]);
Py_XDECREF(arrays[iarrays]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I thought I saw come by that this change was no longer needed. Did you forget to push the change? Or was that in another piece of code. Anyway, no big deal if it is needed, but if not, maybe nice to undo.


# A Python str adopts a StringDType's semantics in the same way
np.copyto(np.empty(3, dtype=np.dtypes.StringDType()), "x", casting="no")
with pytest.raises(TypeError):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I realize I'm actually confused: how can "no" casting be OK, but "equiv" not? I understand the ones above, where one goes up in stringency, from "safe" to "equiv", but here you go down!

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL