Skip to content

Commit bdf270c

Browse files
committed
ENH: Make core/index exceptions more descriptive
* `assert_copy`: You can use `assert_copy` to check that two iterables produce copies that are not the same object. (uses `assert_almost_equal` under the hood). * Fix assert_almost_equal to handle non-ndarrays (previously failed after iterable check) * Fix test_mixed_panel to reflect true name behavior
1 parent 7fd6b20 commit bdf270c

File tree

6 files changed

+130
-59
lines changed

6 files changed

+130
-59
lines changed

Diff for: doc/source/indexing.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -1229,7 +1229,9 @@ However:
12291229
::
12301230

12311231
>>> s.ix[('a', 'b'):('b', 'a')]
1232-
Exception: MultiIndex lexsort depth 1, key was length 2
1232+
Traceback (most recent call last)
1233+
...
1234+
KeyError: Key length (3) was greater than MultiIndex lexsort depth (2)
12331235

12341236
Swapping levels with ``swaplevel``
12351237
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Diff for: pandas/core/index.py

+27-25
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def __new__(cls, data, dtype=None, copy=False, name=None, **kwargs):
109109

110110
subarr = com._asarray_tuplesafe(data, dtype=object)
111111
elif np.isscalar(data):
112-
raise ValueError('Index(...) must be called with a collection '
112+
raise TypeError('Index(...) must be called with a collection '
113113
'of some kind, %s was passed' % repr(data))
114114
else:
115115
# other iterable of some kind
@@ -201,7 +201,7 @@ def _get_names(self):
201201

202202
def _set_names(self, values):
203203
if len(values) != 1:
204-
raise AssertionError('Length of new names must be 1, got %d'
204+
raise ValueError('Length of new names must be 1, got %d'
205205
% len(values))
206206
self.name = values[0]
207207

@@ -327,7 +327,7 @@ def __hash__(self):
327327
return hash(self.view(np.ndarray))
328328

329329
def __setitem__(self, key, value):
330-
raise Exception(str(self.__class__) + ' object is immutable')
330+
raise TypeError(str(self.__class__) + ' does not support item assignment')
331331

332332
def __getitem__(self, key):
333333
"""Override numpy.ndarray's __getitem__ method to work as desired"""
@@ -513,7 +513,7 @@ def order(self, return_indexer=False, ascending=True):
513513
return sorted_index
514514

515515
def sort(self, *args, **kwargs):
516-
raise Exception('Cannot sort an Index object')
516+
raise TypeError('Cannot sort an %r object' % self.__class__.__name__)
517517

518518
def shift(self, periods=1, freq=None):
519519
"""
@@ -572,7 +572,7 @@ def union(self, other):
572572
union : Index
573573
"""
574574
if not hasattr(other, '__iter__'):
575-
raise Exception('Input must be iterable!')
575+
raise TypeError('Input must be iterable.')
576576

577577
if len(other) == 0 or self.equals(other):
578578
return self
@@ -637,7 +637,7 @@ def intersection(self, other):
637637
intersection : Index
638638
"""
639639
if not hasattr(other, '__iter__'):
640-
raise Exception('Input must be iterable!')
640+
raise TypeError('Input must be iterable!')
641641

642642
self._assert_can_do_setop(other)
643643

@@ -679,7 +679,7 @@ def diff(self, other):
679679
"""
680680

681681
if not hasattr(other, '__iter__'):
682-
raise Exception('Input must be iterable!')
682+
raise TypeError('Input must be iterable!')
683683

684684
if self.equals(other):
685685
return Index([], name=self.name)
@@ -807,7 +807,7 @@ def get_indexer(self, target, method=None, limit=None):
807807
return this.get_indexer(target, method=method, limit=limit)
808808

809809
if not self.is_unique:
810-
raise Exception('Reindexing only valid with uniquely valued Index '
810+
raise InvalidIndexError('Reindexing only valid with uniquely valued Index '
811811
'objects')
812812

813813
if method == 'pad':
@@ -900,7 +900,7 @@ def reindex(self, target, method=None, level=None, limit=None,
900900
target = _ensure_index(target)
901901
if level is not None:
902902
if method is not None:
903-
raise ValueError('Fill method not supported if level passed')
903+
raise TypeError('Fill method not supported if level passed')
904904
_, indexer, _ = self._join_level(target, level, how='right',
905905
return_indexers=True)
906906
else:
@@ -1055,7 +1055,7 @@ def _join_level(self, other, level, how='left', return_indexers=False):
10551055
the MultiIndex will not be changed (currently)
10561056
"""
10571057
if isinstance(self, MultiIndex) and isinstance(other, MultiIndex):
1058-
raise Exception('Join on level between two MultiIndex objects '
1058+
raise TypeError('Join on level between two MultiIndex objects '
10591059
'is ambiguous')
10601060

10611061
left, right = self, other
@@ -1429,11 +1429,10 @@ class MultiIndex(Index):
14291429

14301430
def __new__(cls, levels=None, labels=None, sortorder=None, names=None):
14311431
if len(levels) != len(labels):
1432-
raise AssertionError(
1432+
raise ValueError(
14331433
'Length of levels and labels must be the same')
14341434
if len(levels) == 0:
1435-
raise Exception('Must pass non-zero number of levels/labels')
1436-
1435+
raise TypeError('Must pass non-zero number of levels/labels')
14371436
if len(levels) == 1:
14381437
if names:
14391438
name = names[0]
@@ -1534,17 +1533,17 @@ def _get_level_number(self, level):
15341533
try:
15351534
count = self.names.count(level)
15361535
if count > 1:
1537-
raise Exception('The name %s occurs multiple times, use a '
1536+
raise ValueError('The name %s occurs multiple times, use a '
15381537
'level number' % level)
15391538
level = self.names.index(level)
15401539
except ValueError:
15411540
if not isinstance(level, int):
1542-
raise Exception('Level %s not found' % str(level))
1541+
raise KeyError('Level %s not found' % str(level))
15431542
elif level < 0:
15441543
level += self.nlevels
15451544
# Note: levels are zero-based
15461545
elif level >= self.nlevels:
1547-
raise ValueError('Index has only %d levels, not %d'
1546+
raise IndexError('Too many levels: Index has only %d levels, not %d'
15481547
% (self.nlevels, level + 1))
15491548
return level
15501549

@@ -1790,7 +1789,8 @@ def from_tuples(cls, tuples, sortorder=None, names=None):
17901789
index : MultiIndex
17911790
"""
17921791
if len(tuples) == 0:
1793-
raise Exception('Cannot infer number of levels from empty list')
1792+
# I think this is right? Not quite sure...
1793+
raise TypeError('Cannot infer number of levels from empty list')
17941794

17951795
if isinstance(tuples, np.ndarray):
17961796
if isinstance(tuples, Index):
@@ -2158,7 +2158,8 @@ def reindex(self, target, method=None, level=None, limit=None,
21582158
"""
21592159
if level is not None:
21602160
if method is not None:
2161-
raise ValueError('Fill method not supported if level passed')
2161+
# FIXME: Should this actually be a TypeError [given that it's a signature issue] or ValueError
2162+
raise TypeError('Fill method not supported if level passed')
21622163
target, indexer, _ = self._join_level(target, level, how='right',
21632164
return_indexers=True)
21642165
else:
@@ -2202,7 +2203,7 @@ def _tuple_index(self):
22022203
def slice_locs(self, start=None, end=None, strict=False):
22032204
"""
22042205
For an ordered MultiIndex, compute the slice locations for input
2205-
labels. They can tuples representing partial levels, e.g. for a
2206+
labels. They can be tuples representing partial levels, e.g. for a
22062207
MultiIndex with 3 levels, you can pass a single value (corresponding to
22072208
the first level), or a 1-, 2-, or 3-tuple.
22082209
@@ -2240,8 +2241,9 @@ def slice_locs(self, start=None, end=None, strict=False):
22402241

22412242
def _partial_tup_index(self, tup, side='left'):
22422243
if len(tup) > self.lexsort_depth:
2243-
raise KeyError('MultiIndex lexsort depth %d, key was length %d' %
2244-
(self.lexsort_depth, len(tup)))
2244+
raise KeyError('Key length (%d) was greater than MultiIndex'
2245+
' lexsort depth (%d)' %
2246+
(len(tup), self.lexsort_depth))
22452247

22462248
n = len(tup)
22472249
start, end = 0, len(self)
@@ -2251,7 +2253,7 @@ def _partial_tup_index(self, tup, side='left'):
22512253

22522254
if lab not in lev:
22532255
if not lev.is_type_compatible(lib.infer_dtype([lab])):
2254-
raise Exception('Level type mismatch: %s' % lab)
2256+
raise TypeError('Level type mismatch: %s' % lab)
22552257

22562258
# short circuit
22572259
loc = lev.searchsorted(lab, side=side)
@@ -2546,7 +2548,7 @@ def diff(self, other):
25462548
try:
25472549
other = MultiIndex.from_tuples(other)
25482550
except:
2549-
raise TypeError("other should be a MultiIndex or a list of tuples")
2551+
raise TypeError("other must be a MultiIndex or a list of tuples")
25502552
result_names = self.names
25512553
else:
25522554
result_names = self.names if self.names == other.names else None
@@ -2588,7 +2590,7 @@ def insert(self, loc, item):
25882590
if not isinstance(item, tuple):
25892591
item = (item,) + ('',) * (self.nlevels - 1)
25902592
elif len(item) != self.nlevels:
2591-
raise ValueError('Passed item incompatible tuple length')
2593+
raise ValueError('Item must have length equal to number of levels.')
25922594

25932595
new_levels = []
25942596
new_labels = []
@@ -2691,7 +2693,7 @@ def _ensure_index(index_like):
26912693

26922694
def _validate_join_method(method):
26932695
if method not in ['left', 'right', 'inner', 'outer']:
2694-
raise Exception('do not recognize join method %s' % method)
2696+
raise ValueError('do not recognize join method %s' % method)
26952697

26962698

26972699
# TODO: handle index names!

Diff for: pandas/tests/test_frame.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -5920,14 +5920,15 @@ def test_corrwith_series(self):
59205920
assert_series_equal(result, expected)
59215921

59225922
def test_drop_names(self):
5923-
df = DataFrame([[1, 2, 3],[3, 4, 5],[5, 6, 7]], index=['a', 'b', 'c'], columns=['d', 'e', 'f'])
5923+
df = DataFrame([[1, 2, 3],[3, 4, 5],[5, 6, 7]], index=['a', 'b', 'c'],
5924+
columns=['d', 'e', 'f'])
59245925
df.index.name, df.columns.name = 'first', 'second'
59255926
df_dropped_b = df.drop('b')
59265927
df_dropped_e = df.drop('e', axis=1)
5927-
self.assert_(df_dropped_b.index.name == 'first')
5928-
self.assert_(df_dropped_e.index.name == 'first')
5929-
self.assert_(df_dropped_b.columns.name == 'second')
5930-
self.assert_(df_dropped_e.columns.name == 'second')
5928+
self.assertEqual(df_dropped_b.index.name, 'first')
5929+
self.assertEqual(df_dropped_e.index.name, 'first')
5930+
self.assertEqual(df_dropped_b.columns.name, 'second')
5931+
self.assertEqual(df_dropped_e.columns.name, 'second')
59315932

59325933
def test_dropEmptyRows(self):
59335934
N = len(self.frame.index)

0 commit comments

Comments
 (0)