np.append, np.pad (constant mode), np.isin, and np.select are
pure-Python wrappers that convert scalar/sequence arguments with
np.asarray/np.ravel/np.broadcast_arrays before the target array's
dtype is known. When the argument is a bare Python str (or np.str_,
or a sequence of these) and the other operand is a StringDType array,
this premature conversion infers fixed-width unicode (U dtype), which
silently strips trailing NUL characters before the value ever reaches
StringDType-aware code.
Fix each function by detecting when the result would be StringDType
and, in that case, converting the raw (non-ndarray) operand directly
against that dtype instead of through the lossy default inference,
following the same pattern already used for np.strings.replace/
partition/rpartition (numpygh-32040).
isin() is fixed symmetrically (element vs. test_elements), which also
transparently fixes setdiff1d for the same reason. select() keeps its
existing NEP-50 result_type detection unchanged, since passing a raw
str directly into np.result_type raises rather than promoting it like
int/float/complex do; the fix re-derives raw choices/default after the
StringDType result is detected.
Closes numpy#32431
np.append, np.pad (constant mode), np.isin, and np.select are pure-Python wrappers that convert scalar/sequence arguments with np.asarray/np.ravel/np.broadcast_arrays before the target array's dtype is known. When the argument is a bare Python str (or np.str_, or a sequence of these) and the other operand is a StringDType array, this premature conversion infers fixed-width unicode (U dtype), which silently strips trailing NUL characters before the value ever reaches StringDType-aware code.
Fix each function by detecting when the result would be StringDType and, in that case, converting the raw (non-ndarray) operand directly against that dtype instead of through the lossy default inference, following the same pattern already used for np.strings.replace/ partition/rpartition (numpygh-32040).
isin() is fixed symmetrically (element vs. test_elements), which also transparently fixes setdiff1d for the same reason. select() keeps its existing NEP-50 result_type detection unchanged, since passing a raw str directly into np.result_type raises rather than promoting it like int/float/complex do; the fix re-derives raw choices/default after the StringDType result is detected.
Closes numpy#32431
PR summary
First time committer introduction
AI Disclosure