From 518315cb0a9a133abb8c22c5019bf0b159760e82 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Tue, 29 Jan 2019 09:08:15 -0600 Subject: [PATCH] seperate file --- pandas/tests/extension/numpy_/conftest.py | 89 +++--- pandas/tests/extension/numpy_/test_numpy.py | 64 +--- .../extension/numpy_/test_numpy_nested.py | 285 ++++++++++++++++++ 3 files changed, 325 insertions(+), 113 deletions(-) create mode 100644 pandas/tests/extension/numpy_/test_numpy_nested.py diff --git a/pandas/tests/extension/numpy_/conftest.py b/pandas/tests/extension/numpy_/conftest.py index 23a1774ddaaf2..af1ebd05800ff 100644 --- a/pandas/tests/extension/numpy_/conftest.py +++ b/pandas/tests/extension/numpy_/conftest.py @@ -1,55 +1,38 @@ +import numpy as np import pytest -# A set of Base EA tests that are know to not work for -# the object-dtype PandasArray holding nested data. -skips = { - 'TestCasting.test_astype_str', - 'TestConstructors.test_array_from_scalars', - # tuple isn't instance of np.object - 'TestGetitem.test_getitem_scalar', - # Can't pass tuples to _from_sequence - 'TestGetitem.test_take_series', - # np.array shape inference - 'TestInterface.test_array_interface', - # Can't construct expected. - 'TestMethods.test_unique', - 'TestMethods.test_combine_add', - 'TestMethods.test_shift_fill_value', - 'TestMethods.test_where_series', - 'TestMethods.test_repeat', - # Can't hash ndarray[tuple] - 'TestMethods.test_hash_pandas_object_works', - # Can't construct expected. - 'TestReshaping.test_merge', - 'TestReshaping.test_merge_on_extension_array', - 'TestReshaping.test_merge_on_extension_array_duplicates', - - # ndarray setting - 'TestSetitem.test_setitem_scalar_series', - 'TestSetitem.test_setitem_sequence', - 'TestSetitem.test_setitem_sequence_mismatched_length_raises', - 'TestSetitem.test_setitem_sequence_broadcasts', - 'TestSetitem.test_setitem_sequence_broadcasts', - 'TestSetitem.test_setitem_loc_scalar_mixed', - 'TestSetitem.test_setitem_iloc_scalar_mixed', - 'TestSetitem.test_setitem_loc_scalar_multiple_homogoneous', - 'TestSetitem.test_setitem_iloc_scalar_multiple_homogoneous', - 'TestSetitem.test_setitem_mask_broadcast', - 'TestSetitem.test_setitem_scalar_key_sequence_raise', - - # parsing differs. - 'TestParsing.test_EA_types', -} - - -def pytest_collection_modifyitems(config, items): - skip = pytest.mark.skip(reason="Skipping for nested data.") - for item in items: - # TODO: See if pytest has a better way to resolve the *value* - # supplied to a fixture. Right now .keywords gets things - # like 'object' or 'data-object'. - parts = item.name.split("[") - qualname = item.parent.obj.__class__.__name__ + '.' + item.obj.__name__ - if (len(parts) > 1 and 'object' in item.name.split('[')[1] - and qualname in skips): - item.add_marker(skip) +from pandas.core.arrays.numpy_ import PandasArray + + +@pytest.fixture +def allow_in_pandas(monkeypatch): + """ + A monkeypatch to tells pandas to let us in. + + By default, passing a PandasArray to an index / series / frame + constructor will unbox that PandasArray to an ndarray, and treat + it as a non-EA column. We don't want people using EAs without + reason. + + The mechanism for this is a check against ABCPandasArray + in each constructor. + + But, for testing, we need to allow them in pandas. So we patch + the _typ of PandasArray, so that we evade the ABCPandasArray + check. + """ + with monkeypatch.context() as m: + m.setattr(PandasArray, '_typ', 'extension') + yield + + +@pytest.fixture +def na_value(): + return np.nan + + +@pytest.fixture +def na_cmp(): + def cmp(a, b): + return np.isnan(a) and np.isnan(b) + return cmp diff --git a/pandas/tests/extension/numpy_/test_numpy.py b/pandas/tests/extension/numpy_/test_numpy.py index f2c6b956b0bbd..4c93d5ee0b9d7 100644 --- a/pandas/tests/extension/numpy_/test_numpy.py +++ b/pandas/tests/extension/numpy_/test_numpy.py @@ -9,37 +9,13 @@ from .. import base -@pytest.fixture(params=['float', 'object']) -def dtype(request): - return PandasDtype(np.dtype(request.param)) - - @pytest.fixture -def allow_in_pandas(monkeypatch): - """ - A monkeypatch to tells pandas to let us in. - - By default, passing a PandasArray to an index / series / frame - constructor will unbox that PandasArray to an ndarray, and treat - it as a non-EA column. We don't want people using EAs without - reason. - - The mechanism for this is a check against ABCPandasArray - in each constructor. - - But, for testing, we need to allow them in pandas. So we patch - the _typ of PandasArray, so that we evade the ABCPandasArray - check. - """ - with monkeypatch.context() as m: - m.setattr(PandasArray, '_typ', 'extension') - yield +def dtype(): + return PandasDtype(np.dtype('float')) @pytest.fixture def data(allow_in_pandas, dtype): - if dtype.numpy_dtype == 'object': - return pd.Series([(i,) for i in range(100)]).array return PandasArray(np.arange(1, 101, dtype=dtype._dtype)) @@ -48,18 +24,6 @@ def data_missing(allow_in_pandas): return PandasArray(np.array([np.nan, 1.0])) -@pytest.fixture -def na_value(): - return np.nan - - -@pytest.fixture -def na_cmp(): - def cmp(a, b): - return np.isnan(a) and np.isnan(b) - return cmp - - @pytest.fixture def data_for_sorting(allow_in_pandas): """Length-3 array with a known sort order. @@ -152,19 +116,6 @@ class TestArithmetics(BaseNumPyTests, base.BaseArithmeticOpsTests): frame_scalar_exc = None series_array_exc = None - def _check_op(self, s, op, other, op_name, exc=NotImplementedError): - if s.dtype == 'object': - raise pytest.skip("Skipping for object dtype.") - super(TestArithmetics, self)._check_op(s, op, other, op_name, exc) - - def _check_divmod_op(self, s, op, other, exc=Exception): - if isinstance(s, pd.Series) and s.dtype == 'object': - raise pytest.skip("Skipping for object dtype.") - elif isinstance(other, pd.Series) and other.dtype == 'object': - raise pytest.skip("Skipping for object dtype.") - - super(TestArithmetics, self)._check_divmod_op(s, op, other, exc) - def test_divmod_series_array(self, data): s = pd.Series(data) self._check_divmod_op(s, divmod, data, exc=None) @@ -201,8 +152,6 @@ class TestPrinting(BaseNumPyTests, base.BasePrintingTests): class TestNumericReduce(BaseNumPyTests, base.BaseNumericReduceTests): def check_reduce(self, s, op_name, skipna): - if s.dtype == 'object': - raise pytest.skip("Skipping for object dtype.") result = getattr(s, op_name)(skipna=skipna) # avoid coercing int -> float. Just cast to the actual numpy type. expected = getattr(s.astype(s.dtype._dtype), op_name)(skipna=skipna) @@ -210,15 +159,10 @@ def check_reduce(self, s, op_name, skipna): class TestBooleanReduce(BaseNumPyTests, base.BaseBooleanReduceTests): - - def check_reduce(self, s, op_name, skipna): - if s.dtype == 'object': - raise pytest.skip("Skipping for object dtype.") - - super(TestBooleanReduce, self).check_reduce(s, op_name, skipna) + pass -class TestMissing(BaseNumPyTests, base.BaseMissingTests): +class TestMising(BaseNumPyTests, base.BaseMissingTests): pass diff --git a/pandas/tests/extension/numpy_/test_numpy_nested.py b/pandas/tests/extension/numpy_/test_numpy_nested.py new file mode 100644 index 0000000000000..186431638f7fd --- /dev/null +++ b/pandas/tests/extension/numpy_/test_numpy_nested.py @@ -0,0 +1,285 @@ +import numpy as np +import pytest + +import pandas as pd +from pandas.core.arrays.numpy_ import PandasArray, PandasDtype + +from .. import base + + +@pytest.fixture +def dtype(): + return PandasDtype(np.dtype('object')) + + +@pytest.fixture +def data(allow_in_pandas, dtype): + return pd.Series([(i,) for i in range(100)]).array + + +@pytest.fixture +def data_missing(allow_in_pandas): + return PandasArray(np.array([np.nan, (1,)])) + + +@pytest.fixture +def na_value(): + return np.nan + + +@pytest.fixture +def na_cmp(): + def cmp(a, b): + return np.isnan(a) and np.isnan(b) + return cmp + + +@pytest.fixture +def data_for_sorting(allow_in_pandas): + """Length-3 array with a known sort order. + + This should be three items [B, C, A] with + A < B < C + """ + # Use an empty tuple for first element, then remove, + # to disable np.array's shape inference. + return PandasArray( + np.array([(), (2,), (3,), (1,)])[1:] + ) + + +@pytest.fixture +def data_missing_for_sorting(allow_in_pandas): + """Length-3 array with a known sort order. + + This should be three items [B, NA, A] with + A < B and NA missing. + """ + return PandasArray( + np.array([(1,), np.nan, (0,)]) + ) + + +@pytest.fixture +def data_for_grouping(allow_in_pandas): + """Data for factorization, grouping, and unique tests. + + Expected to be like [B, B, NA, NA, A, A, B, C] + + Where A < B < C and NA is missing + """ + a, b, c = (1,), (2,), (3,) + return PandasArray(np.array( + [b, b, np.nan, np.nan, a, a, b, c] + )) + + +skip_nested = pytest.mark.skip(reason="Skipping for nested PandasArray") + + +class BaseNumPyTests(object): + pass + + +class TestCasting(BaseNumPyTests, base.BaseCastingTests): + + @skip_nested + def test_astype_str(self, data): + pass + + +class TestConstructors(BaseNumPyTests, base.BaseConstructorsTests): + @pytest.mark.skip(reason="We don't register our dtype") + # We don't want to register. This test should probably be split in two. + def test_from_dtype(self, data): + pass + + @skip_nested + def test_array_from_scalars(self, data): + pass + + +class TestDtype(BaseNumPyTests, base.BaseDtypeTests): + + @pytest.mark.skip(reason="Incorrect expected.") + # we unsurprisingly clash with a NumPy name. + def test_check_dtype(self, data): + pass + + +class TestGetitem(BaseNumPyTests, base.BaseGetitemTests): + + @skip_nested + def test_getitem_scalar(self, data): + pass + + @skip_nested + def test_take_series(self, data): + pass + + +class TestGroupby(BaseNumPyTests, base.BaseGroupbyTests): + @skip_nested + def test_groupby_extension_apply(self, data_for_grouping, op): + pass + + +class TestInterface(BaseNumPyTests, base.BaseInterfaceTests): + @skip_nested + def test_array_interface(self, data): + # NumPy array shape inference + pass + + +class TestMethods(BaseNumPyTests, base.BaseMethodsTests): + + @pytest.mark.skip(reason="TODO: remove?") + def test_value_counts(self, all_data, dropna): + pass + + @pytest.mark.skip(reason="Incorrect expected") + # We have a bool dtype, so the result is an ExtensionArray + # but expected is not + def test_combine_le(self, data_repeated): + super(TestMethods, self).test_combine_le(data_repeated) + + @skip_nested + def test_combine_add(self, data_repeated): + # Not numeric + pass + + @skip_nested + def test_shift_fill_value(self, data): + # np.array shape inference. Shift implementation fails. + super().test_shift_fill_value(data) + + @skip_nested + def test_unique(self, data, box, method): + # Fails creating expected + pass + + @skip_nested + def test_fillna_copy_frame(self, data_missing): + # The "scalar" for this array isn't a scalar. + pass + + @skip_nested + def test_fillna_copy_series(self, data_missing): + # The "scalar" for this array isn't a scalar. + pass + + @skip_nested + def test_hash_pandas_object_works(self, data, as_frame): + # ndarray of tuples not hashable + pass + + @skip_nested + def test_searchsorted(self, data_for_sorting, as_series): + # Test setup fails. + pass + + @skip_nested + def test_where_series(self, data, na_value, as_frame): + # Test setup fails. + pass + + @skip_nested + def test_repeat(self, data, repeats, as_series, use_numpy): + # Fails creating expected + pass + + +class TestPrinting(BaseNumPyTests, base.BasePrintingTests): + pass + + +class TestMissing(BaseNumPyTests, base.BaseMissingTests): + + @skip_nested + def test_fillna_scalar(self, data_missing): + # Non-scalar "scalar" values. + pass + + @skip_nested + def test_fillna_series_method(self, data_missing, method): + # Non-scalar "scalar" values. + pass + + @skip_nested + def test_fillna_series(self, data_missing): + # Non-scalar "scalar" values. + pass + + @skip_nested + def test_fillna_frame(self, data_missing): + # Non-scalar "scalar" values. + pass + + +class TestReshaping(BaseNumPyTests, base.BaseReshapingTests): + + @pytest.mark.skip("Incorrect parent test") + # not actually a mixed concat, since we concat int and int. + def test_concat_mixed_dtypes(self, data): + super(TestReshaping, self).test_concat_mixed_dtypes(data) + + @skip_nested + def test_merge(self, data, na_value): + # Fails creating expected + pass + + @skip_nested + def test_merge_on_extension_array(self, data): + # Fails creating expected + pass + + @skip_nested + def test_merge_on_extension_array_duplicates(self, data): + # Fails creating expected + pass + + +class TestSetitem(BaseNumPyTests, base.BaseSetitemTests): + + @skip_nested + def test_setitem_scalar_series(self, data, box_in_series): + pass + + @skip_nested + def test_setitem_sequence(self, data, box_in_series): + pass + + @skip_nested + def test_setitem_sequence_mismatched_length_raises(self, data, as_array): + pass + + @skip_nested + def test_setitem_sequence_broadcasts(self, data, box_in_series): + pass + + @skip_nested + def test_setitem_loc_scalar_mixed(self, data): + pass + + @skip_nested + def test_setitem_loc_scalar_multiple_homogoneous(self, data): + pass + + @skip_nested + def test_setitem_iloc_scalar_mixed(self, data): + pass + + @skip_nested + def test_setitem_iloc_scalar_multiple_homogoneous(self, data): + pass + + @skip_nested + def test_setitem_mask_broadcast(self, data, setter): + pass + + @skip_nested + def test_setitem_scalar_key_sequence_raise(self, data): + pass + + +# Skip Arithmetics, NumericReduce, BooleanReduce, Parsing