@@ -162,9 +162,160 @@ def _assert_match(result_fill_value, expected_fill_value):
162162 assert match_value or match_missing
163163
164164
165- def test_maybe_promote_int_with_int ():
166- # placeholder due to too many xfails; see GH 23982 / 25425
167- pass
165+ @pytest .mark .parametrize (
166+ "dtype, fill_value, expected_dtype" ,
167+ [
168+ # size 8
169+ ("int8" , 1 , "int8" ),
170+ ("int8" , np .iinfo ("int8" ).max + 1 , "int16" ),
171+ ("int8" , np .iinfo ("int16" ).max + 1 , "int32" ),
172+ ("int8" , np .iinfo ("int32" ).max + 1 , "int64" ),
173+ ("int8" , np .iinfo ("int64" ).max + 1 , "object" ),
174+ ("int8" , - 1 , "int8" ),
175+ ("int8" , np .iinfo ("int8" ).min - 1 , "int16" ),
176+ ("int8" , np .iinfo ("int16" ).min - 1 , "int32" ),
177+ ("int8" , np .iinfo ("int32" ).min - 1 , "int64" ),
178+ ("int8" , np .iinfo ("int64" ).min - 1 , "object" ),
179+ # keep signed-ness as long as possible
180+ ("uint8" , 1 , "uint8" ),
181+ ("uint8" , np .iinfo ("int8" ).max + 1 , "uint8" ),
182+ ("uint8" , np .iinfo ("uint8" ).max + 1 , "uint16" ),
183+ ("uint8" , np .iinfo ("int16" ).max + 1 , "uint16" ),
184+ ("uint8" , np .iinfo ("uint16" ).max + 1 , "uint32" ),
185+ ("uint8" , np .iinfo ("int32" ).max + 1 , "uint32" ),
186+ ("uint8" , np .iinfo ("uint32" ).max + 1 , "uint64" ),
187+ ("uint8" , np .iinfo ("int64" ).max + 1 , "uint64" ),
188+ ("uint8" , np .iinfo ("uint64" ).max + 1 , "object" ),
189+ # max of uint8 cannot be contained in int8
190+ ("uint8" , - 1 , "int16" ),
191+ ("uint8" , np .iinfo ("int8" ).min - 1 , "int16" ),
192+ ("uint8" , np .iinfo ("int16" ).min - 1 , "int32" ),
193+ ("uint8" , np .iinfo ("int32" ).min - 1 , "int64" ),
194+ ("uint8" , np .iinfo ("int64" ).min - 1 , "object" ),
195+ # size 16
196+ ("int16" , 1 , "int16" ),
197+ ("int16" , np .iinfo ("int8" ).max + 1 , "int16" ),
198+ ("int16" , np .iinfo ("int16" ).max + 1 , "int32" ),
199+ ("int16" , np .iinfo ("int32" ).max + 1 , "int64" ),
200+ ("int16" , np .iinfo ("int64" ).max + 1 , "object" ),
201+ ("int16" , - 1 , "int16" ),
202+ ("int16" , np .iinfo ("int8" ).min - 1 , "int16" ),
203+ ("int16" , np .iinfo ("int16" ).min - 1 , "int32" ),
204+ ("int16" , np .iinfo ("int32" ).min - 1 , "int64" ),
205+ ("int16" , np .iinfo ("int64" ).min - 1 , "object" ),
206+ ("uint16" , 1 , "uint16" ),
207+ ("uint16" , np .iinfo ("int8" ).max + 1 , "uint16" ),
208+ ("uint16" , np .iinfo ("uint8" ).max + 1 , "uint16" ),
209+ ("uint16" , np .iinfo ("int16" ).max + 1 , "uint16" ),
210+ ("uint16" , np .iinfo ("uint16" ).max + 1 , "uint32" ),
211+ ("uint16" , np .iinfo ("int32" ).max + 1 , "uint32" ),
212+ ("uint16" , np .iinfo ("uint32" ).max + 1 , "uint64" ),
213+ ("uint16" , np .iinfo ("int64" ).max + 1 , "uint64" ),
214+ ("uint16" , np .iinfo ("uint64" ).max + 1 , "object" ),
215+ ("uint16" , - 1 , "int32" ),
216+ ("uint16" , np .iinfo ("int8" ).min - 1 , "int32" ),
217+ ("uint16" , np .iinfo ("int16" ).min - 1 , "int32" ),
218+ ("uint16" , np .iinfo ("int32" ).min - 1 , "int64" ),
219+ ("uint16" , np .iinfo ("int64" ).min - 1 , "object" ),
220+ # size 32
221+ ("int32" , 1 , "int32" ),
222+ ("int32" , np .iinfo ("int8" ).max + 1 , "int32" ),
223+ ("int32" , np .iinfo ("int16" ).max + 1 , "int32" ),
224+ ("int32" , np .iinfo ("int32" ).max + 1 , "int64" ),
225+ ("int32" , np .iinfo ("int64" ).max + 1 , "object" ),
226+ ("int32" , - 1 , "int32" ),
227+ ("int32" , np .iinfo ("int8" ).min - 1 , "int32" ),
228+ ("int32" , np .iinfo ("int16" ).min - 1 , "int32" ),
229+ ("int32" , np .iinfo ("int32" ).min - 1 , "int64" ),
230+ ("int32" , np .iinfo ("int64" ).min - 1 , "object" ),
231+ ("uint32" , 1 , "uint32" ),
232+ ("uint32" , np .iinfo ("int8" ).max + 1 , "uint32" ),
233+ ("uint32" , np .iinfo ("uint8" ).max + 1 , "uint32" ),
234+ ("uint32" , np .iinfo ("int16" ).max + 1 , "uint32" ),
235+ ("uint32" , np .iinfo ("uint16" ).max + 1 , "uint32" ),
236+ ("uint32" , np .iinfo ("int32" ).max + 1 , "uint32" ),
237+ ("uint32" , np .iinfo ("uint32" ).max + 1 , "uint64" ),
238+ ("uint32" , np .iinfo ("int64" ).max + 1 , "uint64" ),
239+ ("uint32" , np .iinfo ("uint64" ).max + 1 , "object" ),
240+ ("uint32" , - 1 , "int64" ),
241+ ("uint32" , np .iinfo ("int8" ).min - 1 , "int64" ),
242+ ("uint32" , np .iinfo ("int16" ).min - 1 , "int64" ),
243+ ("uint32" , np .iinfo ("int32" ).min - 1 , "int64" ),
244+ ("uint32" , np .iinfo ("int64" ).min - 1 , "object" ),
245+ # size 64
246+ ("int64" , 1 , "int64" ),
247+ ("int64" , np .iinfo ("int8" ).max + 1 , "int64" ),
248+ ("int64" , np .iinfo ("int16" ).max + 1 , "int64" ),
249+ ("int64" , np .iinfo ("int32" ).max + 1 , "int64" ),
250+ ("int64" , np .iinfo ("int64" ).max + 1 , "object" ),
251+ ("int64" , - 1 , "int64" ),
252+ ("int64" , np .iinfo ("int8" ).min - 1 , "int64" ),
253+ ("int64" , np .iinfo ("int16" ).min - 1 , "int64" ),
254+ ("int64" , np .iinfo ("int32" ).min - 1 , "int64" ),
255+ ("int64" , np .iinfo ("int64" ).min - 1 , "object" ),
256+ ("uint64" , 1 , "uint64" ),
257+ ("uint64" , np .iinfo ("int8" ).max + 1 , "uint64" ),
258+ ("uint64" , np .iinfo ("uint8" ).max + 1 , "uint64" ),
259+ ("uint64" , np .iinfo ("int16" ).max + 1 , "uint64" ),
260+ ("uint64" , np .iinfo ("uint16" ).max + 1 , "uint64" ),
261+ ("uint64" , np .iinfo ("int32" ).max + 1 , "uint64" ),
262+ ("uint64" , np .iinfo ("uint32" ).max + 1 , "uint64" ),
263+ ("uint64" , np .iinfo ("int64" ).max + 1 , "uint64" ),
264+ ("uint64" , np .iinfo ("uint64" ).max + 1 , "object" ),
265+ ("uint64" , - 1 , "object" ),
266+ ("uint64" , np .iinfo ("int8" ).min - 1 , "object" ),
267+ ("uint64" , np .iinfo ("int16" ).min - 1 , "object" ),
268+ ("uint64" , np .iinfo ("int32" ).min - 1 , "object" ),
269+ ("uint64" , np .iinfo ("int64" ).min - 1 , "object" ),
270+ ],
271+ )
272+ def test_maybe_promote_int_with_int (dtype , fill_value , expected_dtype , box ):
273+ dtype = np .dtype (dtype )
274+ expected_dtype = np .dtype (expected_dtype )
275+ boxed , box_dtype = box # read from parametrized fixture
276+
277+ if not boxed :
278+ if expected_dtype == object :
279+ pytest .xfail ("overflow error" )
280+ if expected_dtype == "int32" :
281+ pytest .xfail ("always upcasts to platform int" )
282+ if dtype == "int8" and expected_dtype == "int16" :
283+ pytest .xfail ("casts to int32 instead of int16" )
284+ if (
285+ issubclass (dtype .type , np .unsignedinteger )
286+ and np .iinfo (dtype ).max < fill_value <= np .iinfo ("int64" ).max
287+ ):
288+ pytest .xfail ("falsely casts to signed" )
289+ if (dtype , expected_dtype ) in [
290+ ("uint8" , "int16" ),
291+ ("uint32" , "int64" ),
292+ ] and fill_value != np .iinfo ("int32" ).min - 1 :
293+ pytest .xfail ("casts to int32 instead of int8/int16" )
294+ # this following xfail is "only" a consequence of the - now strictly
295+ # enforced - principle that maybe_promote_with_scalar always casts
296+ pytest .xfail ("wrong return type of fill_value" )
297+ if boxed :
298+ if expected_dtype != object :
299+ pytest .xfail ("falsely casts to object" )
300+ if box_dtype is None and (
301+ fill_value > np .iinfo ("int64" ).max or np .iinfo ("int64" ).min < fill_value < 0
302+ ):
303+ pytest .xfail ("falsely casts to float instead of object" )
304+
305+ # output is not a generic int, but corresponds to expected_dtype
306+ exp_val_for_scalar = np .array ([fill_value ], dtype = expected_dtype )[0 ]
307+ # no missing value marker for integers
308+ exp_val_for_array = None if expected_dtype != "object" else np .nan
309+
310+ _check_promote (
311+ dtype ,
312+ fill_value ,
313+ boxed ,
314+ box_dtype ,
315+ expected_dtype ,
316+ exp_val_for_scalar ,
317+ exp_val_for_array ,
318+ )
168319
169320
170321# override parametrization due to to many xfails; see GH 23982 / 25425
0 commit comments