@@ -253,23 +253,129 @@ def test_set_index_raise_keys(self, frame_of_index_cols, drop, append):
253
253
df .set_index (['A' , df ['A' ], tuple (df ['A' ])],
254
254
drop = drop , append = append )
255
255
256
+ @pytest .mark .xfail (reason = 'broken due to revert, see GH 25085' )
256
257
@pytest .mark .parametrize ('append' , [True , False ])
257
258
@pytest .mark .parametrize ('drop' , [True , False ])
258
- @pytest .mark .parametrize ('box' , [set , iter ])
259
+ @pytest .mark .parametrize ('box' , [set , iter , lambda x : (y for y in x )],
260
+ ids = ['set' , 'iter' , 'generator' ])
259
261
def test_set_index_raise_on_type (self , frame_of_index_cols , box ,
260
262
drop , append ):
261
263
df = frame_of_index_cols
262
264
263
265
msg = 'The parameter "keys" may be a column key, .*'
264
- # forbidden type, e.g. set/tuple/ iter
265
- with pytest .raises (ValueError , match = msg ):
266
+ # forbidden type, e.g. set/iter/generator
267
+ with pytest .raises (TypeError , match = msg ):
266
268
df .set_index (box (df ['A' ]), drop = drop , append = append )
267
269
268
- # forbidden type in list, e.g. set/tuple/ iter
269
- with pytest .raises (ValueError , match = msg ):
270
+ # forbidden type in list, e.g. set/iter/generator
271
+ with pytest .raises (TypeError , match = msg ):
270
272
df .set_index (['A' , df ['A' ], box (df ['A' ])],
271
273
drop = drop , append = append )
272
274
275
+ def test_set_index_custom_label_type (self ):
276
+ # GH 24969
277
+
278
+ class Thing (object ):
279
+ def __init__ (self , name , color ):
280
+ self .name = name
281
+ self .color = color
282
+
283
+ def __str__ (self ):
284
+ return "<Thing %r>" % (self .name ,)
285
+
286
+ # necessary for pretty KeyError
287
+ __repr__ = __str__
288
+
289
+ thing1 = Thing ('One' , 'red' )
290
+ thing2 = Thing ('Two' , 'blue' )
291
+ df = DataFrame ({thing1 : [0 , 1 ], thing2 : [2 , 3 ]})
292
+ expected = DataFrame ({thing1 : [0 , 1 ]},
293
+ index = Index ([2 , 3 ], name = thing2 ))
294
+
295
+ # use custom label directly
296
+ result = df .set_index (thing2 )
297
+ tm .assert_frame_equal (result , expected )
298
+
299
+ # custom label wrapped in list
300
+ result = df .set_index ([thing2 ])
301
+ tm .assert_frame_equal (result , expected )
302
+
303
+ # missing key
304
+ thing3 = Thing ('Three' , 'pink' )
305
+ msg = "<Thing 'Three'>"
306
+ with pytest .raises (KeyError , match = msg ):
307
+ # missing label directly
308
+ df .set_index (thing3 )
309
+
310
+ with pytest .raises (KeyError , match = msg ):
311
+ # missing label in list
312
+ df .set_index ([thing3 ])
313
+
314
+ def test_set_index_custom_label_hashable_iterable (self ):
315
+ # GH 24969
316
+
317
+ # actual example discussed in GH 24984 was e.g. for shapely.geometry
318
+ # objects (e.g. a collection of Points) that can be both hashable and
319
+ # iterable; using frozenset as a stand-in for testing here
320
+
321
+ class Thing (frozenset ):
322
+ # need to stabilize repr for KeyError (due to random order in sets)
323
+ def __repr__ (self ):
324
+ tmp = sorted (list (self ))
325
+ # double curly brace prints one brace in format string
326
+ return "frozenset({{{}}})" .format (', ' .join (map (repr , tmp )))
327
+
328
+ thing1 = Thing (['One' , 'red' ])
329
+ thing2 = Thing (['Two' , 'blue' ])
330
+ df = DataFrame ({thing1 : [0 , 1 ], thing2 : [2 , 3 ]})
331
+ expected = DataFrame ({thing1 : [0 , 1 ]},
332
+ index = Index ([2 , 3 ], name = thing2 ))
333
+
334
+ # use custom label directly
335
+ result = df .set_index (thing2 )
336
+ tm .assert_frame_equal (result , expected )
337
+
338
+ # custom label wrapped in list
339
+ result = df .set_index ([thing2 ])
340
+ tm .assert_frame_equal (result , expected )
341
+
342
+ # missing key
343
+ thing3 = Thing (['Three' , 'pink' ])
344
+ msg = '.*' # due to revert, see GH 25085
345
+ with pytest .raises (KeyError , match = msg ):
346
+ # missing label directly
347
+ df .set_index (thing3 )
348
+
349
+ with pytest .raises (KeyError , match = msg ):
350
+ # missing label in list
351
+ df .set_index ([thing3 ])
352
+
353
+ def test_set_index_custom_label_type_raises (self ):
354
+ # GH 24969
355
+
356
+ # purposefully inherit from something unhashable
357
+ class Thing (set ):
358
+ def __init__ (self , name , color ):
359
+ self .name = name
360
+ self .color = color
361
+
362
+ def __str__ (self ):
363
+ return "<Thing %r>" % (self .name ,)
364
+
365
+ thing1 = Thing ('One' , 'red' )
366
+ thing2 = Thing ('Two' , 'blue' )
367
+ df = DataFrame ([[0 , 2 ], [1 , 3 ]], columns = [thing1 , thing2 ])
368
+
369
+ msg = 'unhashable type.*'
370
+
371
+ with pytest .raises (TypeError , match = msg ):
372
+ # use custom label directly
373
+ df .set_index (thing2 )
374
+
375
+ with pytest .raises (TypeError , match = msg ):
376
+ # custom label wrapped in list
377
+ df .set_index ([thing2 ])
378
+
273
379
def test_construction_with_categorical_index (self ):
274
380
ci = tm .makeCategoricalIndex (10 )
275
381
ci .name = 'B'
0 commit comments