Skip to content

Commit

Permalink
STY: use pytest.raises context manager (indexes/multi) (pandas-dev#25175
Browse files Browse the repository at this point in the history
)
  • Loading branch information
simonjayhawkins authored and Pingviinituutti committed Feb 28, 2019
1 parent 5458e22 commit a4efb67
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 142 deletions.
4 changes: 3 additions & 1 deletion pandas/compat/numpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
_np_version_under1p13 = _nlv < LooseVersion('1.13')
_np_version_under1p14 = _nlv < LooseVersion('1.14')
_np_version_under1p15 = _nlv < LooseVersion('1.15')
_np_version_under1p16 = _nlv < LooseVersion('1.16')


if _nlv < '1.12':
Expand Down Expand Up @@ -64,5 +65,6 @@ def np_array_datetime64_compat(arr, *args, **kwargs):
__all__ = ['np',
'_np_version_under1p13',
'_np_version_under1p14',
'_np_version_under1p15'
'_np_version_under1p15',
'_np_version_under1p16'
]
99 changes: 43 additions & 56 deletions pandas/tests/indexes/multi/test_analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest

from pandas.compat import lrange
from pandas.compat.numpy import _np_version_under1p16

import pandas as pd
from pandas import Index, MultiIndex, date_range, period_range
Expand All @@ -13,8 +14,11 @@
def test_shift(idx):

# GH8083 test the base class for shift
pytest.raises(NotImplementedError, idx.shift, 1)
pytest.raises(NotImplementedError, idx.shift, 1, 2)
msg = "Not supported for type MultiIndex"
with pytest.raises(NotImplementedError, match=msg):
idx.shift(1)
with pytest.raises(NotImplementedError, match=msg):
idx.shift(1, 2)


def test_groupby(idx):
Expand Down Expand Up @@ -50,25 +54,26 @@ def test_truncate():
result = index.truncate(before=1, after=2)
assert len(result.levels[0]) == 2

# after < before
pytest.raises(ValueError, index.truncate, 3, 1)
msg = "after < before"
with pytest.raises(ValueError, match=msg):
index.truncate(3, 1)


def test_where():
i = MultiIndex.from_tuples([('A', 1), ('A', 2)])

with pytest.raises(NotImplementedError):
msg = r"\.where is not supported for MultiIndex operations"
with pytest.raises(NotImplementedError, match=msg):
i.where(True)


def test_where_array_like():
@pytest.mark.parametrize('klass', [list, tuple, np.array, pd.Series])
def test_where_array_like(klass):
i = MultiIndex.from_tuples([('A', 1), ('A', 2)])
klasses = [list, tuple, np.array, pd.Series]
cond = [False, True]

for klass in klasses:
with pytest.raises(NotImplementedError):
i.where(klass(cond))
msg = r"\.where is not supported for MultiIndex operations"
with pytest.raises(NotImplementedError, match=msg):
i.where(klass(cond))


# TODO: reshape
Expand Down Expand Up @@ -141,7 +146,8 @@ def test_take(idx):
# if not isinstance(idx,
# (DatetimeIndex, PeriodIndex, TimedeltaIndex)):
# GH 10791
with pytest.raises(AttributeError):
msg = "'MultiIndex' object has no attribute 'freq'"
with pytest.raises(AttributeError, match=msg):
idx.freq


Expand Down Expand Up @@ -199,7 +205,8 @@ def test_take_fill_value():
with pytest.raises(ValueError, match=msg):
idx.take(np.array([1, 0, -5]), fill_value=True)

with pytest.raises(IndexError):
msg = "index -5 is out of bounds for size 4"
with pytest.raises(IndexError, match=msg):
idx.take(np.array([1, -5]))


Expand All @@ -215,13 +222,15 @@ def test_sub(idx):
first = idx

# - now raises (previously was set op difference)
with pytest.raises(TypeError):
msg = "cannot perform __sub__ with this index type: MultiIndex"
with pytest.raises(TypeError, match=msg):
first - idx[-3:]
with pytest.raises(TypeError):
with pytest.raises(TypeError, match=msg):
idx[-3:] - first
with pytest.raises(TypeError):
with pytest.raises(TypeError, match=msg):
idx[-3:] - first.tolist()
with pytest.raises(TypeError):
msg = "cannot perform __rsub__ with this index type: MultiIndex"
with pytest.raises(TypeError, match=msg):
first.tolist() - idx[-3:]


Expand Down Expand Up @@ -272,50 +281,28 @@ def test_map_dictlike(idx, mapper):
np.arccos, np.arctan, np.sinh, np.cosh, np.tanh,
np.arcsinh, np.arccosh, np.arctanh, np.deg2rad,
np.rad2deg
])
def test_numpy_ufuncs(func):
], ids=lambda func: func.__name__)
def test_numpy_ufuncs(idx, func):
# test ufuncs of numpy. see:
# http://docs.scipy.org/doc/numpy/reference/ufuncs.html

# copy and paste from idx fixture as pytest doesn't support
# parameters and fixtures at the same time.
major_axis = Index(['foo', 'bar', 'baz', 'qux'])
minor_axis = Index(['one', 'two'])
major_codes = np.array([0, 0, 1, 2, 3, 3])
minor_codes = np.array([0, 1, 0, 1, 0, 1])
index_names = ['first', 'second']

idx = MultiIndex(
levels=[major_axis, minor_axis],
codes=[major_codes, minor_codes],
names=index_names,
verify_integrity=False
)

with pytest.raises(Exception):
with np.errstate(all='ignore'):
func(idx)
if _np_version_under1p16:
expected_exception = AttributeError
msg = "'tuple' object has no attribute '{}'".format(func.__name__)
else:
expected_exception = TypeError
msg = ("loop of ufunc does not support argument 0 of type tuple which"
" has no callable {} method").format(func.__name__)
with pytest.raises(expected_exception, match=msg):
func(idx)


@pytest.mark.parametrize('func', [
np.isfinite, np.isinf, np.isnan, np.signbit
])
def test_numpy_type_funcs(func):
# for func in [np.isfinite, np.isinf, np.isnan, np.signbit]:
# copy and paste from idx fixture as pytest doesn't support
# parameters and fixtures at the same time.
major_axis = Index(['foo', 'bar', 'baz', 'qux'])
minor_axis = Index(['one', 'two'])
major_codes = np.array([0, 0, 1, 2, 3, 3])
minor_codes = np.array([0, 1, 0, 1, 0, 1])
index_names = ['first', 'second']

idx = MultiIndex(
levels=[major_axis, minor_axis],
codes=[major_codes, minor_codes],
names=index_names,
verify_integrity=False
)

with pytest.raises(Exception):
], ids=lambda func: func.__name__)
def test_numpy_type_funcs(idx, func):
msg = ("ufunc '{}' not supported for the input types, and the inputs"
" could not be safely coerced to any supported types according to"
" the casting rule ''safe''").format(func.__name__)
with pytest.raises(TypeError, match=msg):
func(idx)
6 changes: 2 additions & 4 deletions pandas/tests/indexes/multi/test_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ def test_compat(indices):

def test_pickle_compat_construction(holder):
# this is testing for pickle compat
if holder is None:
return

# need an object to create with
pytest.raises(TypeError, holder)
with pytest.raises(TypeError, match="Must pass both levels and codes"):
holder()
67 changes: 32 additions & 35 deletions pandas/tests/indexes/multi/test_constructor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-

from collections import OrderedDict
import re

import numpy as np
import pytest
Expand Down Expand Up @@ -30,10 +29,10 @@ def test_constructor_no_levels():
with pytest.raises(ValueError, match=msg):
MultiIndex(levels=[], codes=[])

both_re = re.compile('Must pass both levels and codes')
with pytest.raises(TypeError, match=both_re):
msg = "Must pass both levels and codes"
with pytest.raises(TypeError, match=msg):
MultiIndex(levels=[])
with pytest.raises(TypeError, match=both_re):
with pytest.raises(TypeError, match=msg):
MultiIndex(codes=[])


Expand All @@ -42,20 +41,20 @@ def test_constructor_nonhashable_names():
levels = [[1, 2], [u'one', u'two']]
codes = [[0, 0, 1, 1], [0, 1, 0, 1]]
names = (['foo'], ['bar'])
message = "MultiIndex.name must be a hashable type"
with pytest.raises(TypeError, match=message):
msg = r"MultiIndex\.name must be a hashable type"
with pytest.raises(TypeError, match=msg):
MultiIndex(levels=levels, codes=codes, names=names)

# With .rename()
mi = MultiIndex(levels=[[1, 2], [u'one', u'two']],
codes=[[0, 0, 1, 1], [0, 1, 0, 1]],
names=('foo', 'bar'))
renamed = [['foor'], ['barr']]
with pytest.raises(TypeError, match=message):
with pytest.raises(TypeError, match=msg):
mi.rename(names=renamed)

# With .set_names()
with pytest.raises(TypeError, match=message):
with pytest.raises(TypeError, match=msg):
mi.set_names(names=renamed)


Expand All @@ -67,8 +66,9 @@ def test_constructor_mismatched_codes_levels(idx):
with pytest.raises(ValueError, match=msg):
MultiIndex(levels=levels, codes=codes)

length_error = re.compile('>= length of level')
label_error = re.compile(r'Unequal code lengths: \[4, 2\]')
length_error = (r"On level 0, code max \(3\) >= length of level \(1\)\."
" NOTE: this index is in an inconsistent state")
label_error = r"Unequal code lengths: \[4, 2\]"

# important to check that it's looking at the right thing.
with pytest.raises(ValueError, match=length_error):
Expand Down Expand Up @@ -253,21 +253,14 @@ def test_from_arrays_empty():
tm.assert_index_equal(result, expected)


@pytest.mark.parametrize('invalid_array', [
(1),
([1]),
([1, 2]),
([[1], 2]),
('a'),
(['a']),
(['a', 'b']),
([['a'], 'b']),
])
def test_from_arrays_invalid_input(invalid_array):
invalid_inputs = [1, [1], [1, 2], [[1], 2],
'a', ['a'], ['a', 'b'], [['a'], 'b']]
for i in invalid_inputs:
pytest.raises(TypeError, MultiIndex.from_arrays, arrays=i)
@pytest.mark.parametrize('invalid_sequence_of_arrays', [
1, [1], [1, 2], [[1], 2], 'a', ['a'], ['a', 'b'], [['a'], 'b']])
def test_from_arrays_invalid_input(invalid_sequence_of_arrays):
msg = (r"Input must be a list / sequence of array-likes|"
r"Input must be list-like|"
r"object of type 'int' has no len\(\)")
with pytest.raises(TypeError, match=msg):
MultiIndex.from_arrays(arrays=invalid_sequence_of_arrays)


@pytest.mark.parametrize('idx1, idx2', [
Expand Down Expand Up @@ -332,9 +325,10 @@ def test_tuples_with_name_string():
# GH 15110 and GH 14848

li = [(0, 0, 1), (0, 1, 0), (1, 0, 0)]
with pytest.raises(ValueError):
msg = "Names should be list-like for a MultiIndex"
with pytest.raises(ValueError, match=msg):
pd.Index(li, name='abc')
with pytest.raises(ValueError):
with pytest.raises(ValueError, match=msg):
pd.Index(li, name='a')


Expand Down Expand Up @@ -398,7 +392,10 @@ def test_from_product_empty_three_levels(N):
[['a'], 'b'],
])
def test_from_product_invalid_input(invalid_input):
pytest.raises(TypeError, MultiIndex.from_product, iterables=invalid_input)
msg = (r"Input must be a list / sequence of iterables|"
"Input must be list-like")
with pytest.raises(TypeError, match=msg):
MultiIndex.from_product(iterables=invalid_input)


def test_from_product_datetimeindex():
Expand Down Expand Up @@ -563,15 +560,15 @@ def test_from_frame_valid_names(names_in, names_out):
assert mi.names == names_out


@pytest.mark.parametrize('names_in,names_out', [
('bad_input', ValueError("Names should be list-like for a MultiIndex")),
(['a', 'b', 'c'], ValueError("Length of names must match number of "
"levels in MultiIndex."))
@pytest.mark.parametrize('names,expected_error_msg', [
('bad_input', "Names should be list-like for a MultiIndex"),
(['a', 'b', 'c'],
"Length of names must match number of levels in MultiIndex")
])
def test_from_frame_invalid_names(names_in, names_out):
def test_from_frame_invalid_names(names, expected_error_msg):
# GH 22420
df = pd.DataFrame([['a', 'a'], ['a', 'b'], ['b', 'a'], ['b', 'b']],
columns=pd.MultiIndex.from_tuples([('L1', 'x'),
('L2', 'y')]))
with pytest.raises(type(names_out), match=names_out.args[0]):
pd.MultiIndex.from_frame(df, names=names_in)
with pytest.raises(ValueError, match=expected_error_msg):
pd.MultiIndex.from_frame(df, names=names)
23 changes: 16 additions & 7 deletions pandas/tests/indexes/multi/test_contains.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,24 @@ def test_isin_level_kwarg():
tm.assert_numpy_array_equal(expected, idx.isin(vals_1, level=1))
tm.assert_numpy_array_equal(expected, idx.isin(vals_1, level=-1))

pytest.raises(IndexError, idx.isin, vals_0, level=5)
pytest.raises(IndexError, idx.isin, vals_0, level=-5)

pytest.raises(KeyError, idx.isin, vals_0, level=1.0)
pytest.raises(KeyError, idx.isin, vals_1, level=-1.0)
pytest.raises(KeyError, idx.isin, vals_1, level='A')
msg = "Too many levels: Index has only 2 levels, not 6"
with pytest.raises(IndexError, match=msg):
idx.isin(vals_0, level=5)
msg = ("Too many levels: Index has only 2 levels, -5 is not a valid level"
" number")
with pytest.raises(IndexError, match=msg):
idx.isin(vals_0, level=-5)

with pytest.raises(KeyError, match=r"'Level 1\.0 not found'"):
idx.isin(vals_0, level=1.0)
with pytest.raises(KeyError, match=r"'Level -1\.0 not found'"):
idx.isin(vals_1, level=-1.0)
with pytest.raises(KeyError, match="'Level A not found'"):
idx.isin(vals_1, level='A')

idx.names = ['A', 'B']
tm.assert_numpy_array_equal(expected, idx.isin(vals_0, level='A'))
tm.assert_numpy_array_equal(expected, idx.isin(vals_1, level='B'))

pytest.raises(KeyError, idx.isin, vals_1, level='C')
with pytest.raises(KeyError, match="'Level C not found'"):
idx.isin(vals_1, level='C')
Loading

0 comments on commit a4efb67

Please sign in to comment.