Skip to content

TYP: strings.* shape-typing - #32749

Open
jorenham wants to merge 30 commits into
numpy:mainfrom
jorenham:typing/strings/shape-typing
Open

jorenham wants to merge 30 commits into
numpy:mainfrom
jorenham:typing/strings/shape-typing

Conversation

@jorenham

@jorenham jorenham commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

This adds shape-typing support to all public numpy.strings.* functions when the input is an array with known shape-type, or for <=2d array-likes (nested sequences of str or bytes).

See the commit messages for the full list the individual functions (so let's not squash merge this).

(I tried using stacked PRs, but apparently that doens't work from a fork, and I didn't want to push 15 branches to the upstream numpy repo.)


Pair programmed with AI (Fable 5.1 + ChatGPT Astra 6 as adverserial reviewer).

@jorenham jorenham added this to the 2.6.0 Release milestone Sep 22, 2026
@jorenham

Copy link
Copy Markdown
Member Author

ping @ngoldbaum in case you're interested

@ngoldbaum ngoldbaum left a comment

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.

Just one comment but also I'm curious how we should handle NEP-58 and merging in bytestringdtype on top of these changes.

Comment thread numpy/_core/strings.pyi
Comment on lines +1057 to +1058
old: _CharLike_co,
new: _CharLike_co,

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.

FWIW this is a runtime error:

>>> np.strings.replace("abc", np.bytes_("a"), np.bytes_("b"))
# UFuncTypeError

This works though, although maybe this is a bug?

>>> np.strings.replace("abc", b"a", b"b")
 array('bbc', dtype='<U3')

FWIW Python thinks this shouldn't work (IMO it's right):

>>> "abc".replace(b"a", b"b")
TypeError: replace() argument 1 must be str, not bytes

By the way, how do you want to handle NEP-58 and this PR? Would you like to help me expand this to include ByteStringDType? And maybe help catch edge cases like this?

We could also just declare that all of this weirdness with the np.str_ and np.bytes_ dtypes is legacy behavior that we're not going to change. ByteStringDType/StringDType already handle this. Unfortunately for users to see that, we'd need to change the coercion rules for str and bytes which is a big change.

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.

FWIW this is a runtime error:

>>> np.strings.replace("abc", np.bytes_("a"), np.bytes_("b"))
# UFuncTypeError

This works though, although maybe this is a bug?

>>> np.strings.replace("abc", b"a", b"b")
 array('bbc', dtype='<U3')

np.bytes_ subclasses builtins.bytes, so there's no way to reject np.bytes_ when builtins.bytes is accepted, I'm afraid.


FWIW Python thinks this shouldn't work (IMO it's right):

>>> "abc".replace(b"a", b"b")
TypeError: replace() argument 1 must be str, not bytes

If this isn't intended behavior, then we can just tighten this to only allow str here?


By the way, how do you want to handle NEP-58 and this PR?

That depends on the runtime behavior I guess.

Would you like to help me expand this to include ByteStringDType? And maybe help catch edge cases like this?

Sure :)

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.

If this isn't intended behavior, then we can just tighten this to only allow str here?

I'll look at fixing this problem as well.

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.

And just to be clear, are we talking about just replace, or the others as well ([r]partition, center, {r,l}just)?

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.

For strings.center there's an issue with that I see, because the fillchar default is a hard-coded " ":

def center(a, width, fillchar=' '):

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.

I'll be opening a PR for all of np.strings shortly. Unfortunately IMO it needs a deprecation too.

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.

For strings.center there's an issue with that I see, because the fillchar default is a hard-coded " "

IMO we should change those defaults to np._NoValue. I don't think there are any compatibility issues with that.

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.

Alright I'll wait a bit then

@jorenham
jorenham force-pushed the typing/strings/shape-typing branch from fca9295 to c35bd32 Compare September 22, 2026 19:31
@jorenham

Copy link
Copy Markdown
Member Author

blocker: #32757

This branch has not been deployed

No deployments
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.

2 participants