From 265ae230105c9c9e42b2b5e85552e3945780c3f7 Mon Sep 17 00:00:00 2001 From: Martin Winkel Date: Tue, 17 Mar 2020 08:48:00 +0100 Subject: [PATCH 1/3] [#31989] pandas/conftest.py refactoring From 849009db854e902591e1dbe02dbbf809e4c48839 Mon Sep 17 00:00:00 2001 From: Martin Winkel Date: Tue, 17 Mar 2020 08:48:30 +0100 Subject: [PATCH 2/3] _gen_mi -> _create_multiindex and added docstring --- pandas/conftest.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index d8f96021cdb15..0885bca08eb0e 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -965,8 +965,10 @@ def __len__(self): return TestNonDictMapping -def _gen_mi(): - # a MultiIndex used to test the general functionality of this object +def _create_multiindex(): + """ + MultiIndex used to test the general functionality of this object + """ # See Also: tests.multi.conftest.idx major_axis = Index(["foo", "bar", "baz", "qux"]) @@ -1000,7 +1002,7 @@ def _gen_mi(): "interval": tm.makeIntervalIndex(100), "empty": Index([]), "tuples": MultiIndex.from_tuples(zip(["foo", "bar", "baz"], [1, 2, 3])), - "multi": _gen_mi(), + "multi": _create_multiindex(), "repeats": Index([0, 0, 1, 1, 2, 2]), } From 2a903afb4aa8cc501ae3392db7a17be302de5ca5 Mon Sep 17 00:00:00 2001 From: Martin Winkel Date: Tue, 17 Mar 2020 22:40:04 +0100 Subject: [PATCH 3/3] defined and reordered base conftest.py --- pandas/conftest.py | 1498 +++++++++++++++++++++++--------------------- 1 file changed, 776 insertions(+), 722 deletions(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index 0885bca08eb0e..1e905cae72d9c 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -1,3 +1,23 @@ +""" +This file is very long and growing, but it was decided to not split it yet, as +it's still manageable (2020-03-17, ~1.1k LoC). See gh-31989 + +Instead of splitting it was decided to define sections here: +- Configuration / Settings +- Autouse fixtures +- Common arguments +- Missing values & co. +- Classes +- Indices +- Series' +- DataFrames +- Operators & Operations +- Data sets/files +- Time zones +- Dtypes +- Misc +""" + from collections import abc from datetime import date, time, timedelta, timezone from decimal import Decimal @@ -19,19 +39,11 @@ from pandas.core import ops from pandas.core.indexes.api import Index, MultiIndex -hypothesis.settings.register_profile( - "ci", - # Hypothesis timing checks are tuned for scalars by default, so we bump - # them from 200ms to 500ms per test case as the global default. If this - # is too short for a specific test, (a) try to make it faster, and (b) - # if it really is slow add `@settings(deadline=...)` with a working value, - # or `deadline=None` to entirely disable timeouts for that test. - deadline=500, - suppress_health_check=(hypothesis.HealthCheck.too_slow,), -) -hypothesis.settings.load_profile("ci") - +# ---------------------------------------------------------------- +# Configuration / Settings +# ---------------------------------------------------------------- +# pytest def pytest_addoption(parser): parser.addoption("--skip-slow", action="store_true", help="skip slow tests") parser.addoption("--skip-network", action="store_true", help="skip network tests") @@ -66,6 +78,55 @@ def pytest_runtest_setup(item): pytest.skip("skipping high memory test since --run-high-memory was not set") +# Hypothesis +hypothesis.settings.register_profile( + "ci", + # Hypothesis timing checks are tuned for scalars by default, so we bump + # them from 200ms to 500ms per test case as the global default. If this + # is too short for a specific test, (a) try to make it faster, and (b) + # if it really is slow add `@settings(deadline=...)` with a working value, + # or `deadline=None` to entirely disable timeouts for that test. + deadline=500, + suppress_health_check=(hypothesis.HealthCheck.too_slow,), +) +hypothesis.settings.load_profile("ci") + +# Registering these strategies makes them globally available via st.from_type, +# which is use for offsets in tests/tseries/offsets/test_offsets_properties.py +for name in "MonthBegin MonthEnd BMonthBegin BMonthEnd".split(): + cls = getattr(pd.tseries.offsets, name) + st.register_type_strategy( + cls, st.builds(cls, n=st.integers(-99, 99), normalize=st.booleans()) + ) + +for name in "YearBegin YearEnd BYearBegin BYearEnd".split(): + cls = getattr(pd.tseries.offsets, name) + st.register_type_strategy( + cls, + st.builds( + cls, + n=st.integers(-5, 5), + normalize=st.booleans(), + month=st.integers(min_value=1, max_value=12), + ), + ) + +for name in "QuarterBegin QuarterEnd BQuarterBegin BQuarterEnd".split(): + cls = getattr(pd.tseries.offsets, name) + st.register_type_strategy( + cls, + st.builds( + cls, + n=st.integers(-24, 24), + normalize=st.booleans(), + startingMonth=st.integers(min_value=1, max_value=12), + ), + ) + + +# ---------------------------------------------------------------- +# Autouse fixtures +# ---------------------------------------------------------------- @pytest.fixture(autouse=True) def configure_tests(): """ @@ -83,16 +144,9 @@ def add_imports(doctest_namespace): doctest_namespace["pd"] = pd -@pytest.fixture(params=["bsr", "coo", "csc", "csr", "dia", "dok", "lil"]) -def spmatrix(request): - """ - Yields scipy sparse matrix classes. - """ - from scipy import sparse - - return getattr(sparse, request.param + "_matrix") - - +# ---------------------------------------------------------------- +# Common arguments +# ---------------------------------------------------------------- @pytest.fixture(params=[0, 1, "index", "columns"], ids=lambda x: f"axis {repr(x)}") def axis(request): """ @@ -112,19 +166,6 @@ def axis_series(request): return request.param -@pytest.fixture -def ip(): - """ - Get an instance of IPython.InteractiveShell. - - Will raise a skip if IPython is not installed. - """ - pytest.importorskip("IPython", minversion="6.0.0") - from IPython.core.interactiveshell import InteractiveShell - - return InteractiveShell() - - @pytest.fixture(params=[True, False, None]) def observed(request): """ @@ -146,949 +187,962 @@ def ordered_fixture(request): return request.param -_all_arithmetic_operators = [ - "__add__", - "__radd__", - "__sub__", - "__rsub__", - "__mul__", - "__rmul__", - "__floordiv__", - "__rfloordiv__", - "__truediv__", - "__rtruediv__", - "__pow__", - "__rpow__", - "__mod__", - "__rmod__", -] - - -@pytest.fixture(params=_all_arithmetic_operators) -def all_arithmetic_operators(request): +@pytest.fixture(params=["first", "last", False]) +def keep(request): """ - Fixture for dunder names for common arithmetic operations. + Valid values for the 'keep' parameter used in + .duplicated or .drop_duplicates """ return request.param -@pytest.fixture( - params=[ - operator.add, - ops.radd, - operator.sub, - ops.rsub, - operator.mul, - ops.rmul, - operator.truediv, - ops.rtruediv, - operator.floordiv, - ops.rfloordiv, - operator.mod, - ops.rmod, - operator.pow, - ops.rpow, - ] -) -def all_arithmetic_functions(request): +@pytest.fixture(params=["left", "right", "both", "neither"]) +def closed(request): """ - Fixture for operator and roperator arithmetic functions. - - Notes - ----- - This includes divmod and rdivmod, whereas all_arithmetic_operators - does not. + Fixture for trying all interval closed parameters. """ return request.param -_all_numeric_reductions = [ - "sum", - "max", - "min", - "mean", - "prod", - "std", - "var", - "median", - "kurt", - "skew", -] - - -@pytest.fixture(params=_all_numeric_reductions) -def all_numeric_reductions(request): +@pytest.fixture(params=["left", "right", "both", "neither"]) +def other_closed(request): """ - Fixture for numeric reduction names. + Secondary closed fixture to allow parametrizing over all pairs of closed. """ return request.param -_all_boolean_reductions = ["all", "any"] - - -@pytest.fixture(params=_all_boolean_reductions) -def all_boolean_reductions(request): +@pytest.fixture(params=[None, "gzip", "bz2", "zip", "xz"]) +def compression(request): """ - Fixture for boolean reduction names. + Fixture for trying common compression types in compression tests. """ return request.param -_cython_table = pd.core.base.SelectionMixin._cython_table.items() - - -@pytest.fixture(params=list(_cython_table)) -def cython_table_items(request): +@pytest.fixture(params=["gzip", "bz2", "zip", "xz"]) +def compression_only(request): """ - Yields a tuple of a function and its corresponding name. Correspond to - the list of aggregator "Cython functions" used on selected table items. + Fixture for trying common compression types in compression tests excluding + uncompressed case. """ return request.param -def _get_cython_table_params(ndframe, func_names_and_expected): +@pytest.fixture(params=[True, False]) +def writable(request): """ - Combine frame, functions from SelectionMixin._cython_table - keys and expected result. - - Parameters - ---------- - ndframe : DataFrame or Series - func_names_and_expected : Sequence of two items - The first item is a name of a NDFrame method ('sum', 'prod') etc. - The second item is the expected return value. - - Returns - ------- - list - List of three items (DataFrame, function, expected result) + Fixture that an array is writable. """ - results = [] - for func_name, expected in func_names_and_expected: - results.append((ndframe, func_name, expected)) - results += [ - (ndframe, func, expected) - for func, name in _cython_table - if name == func_name - ] - return results + return request.param -@pytest.fixture(params=["__eq__", "__ne__", "__le__", "__lt__", "__ge__", "__gt__"]) -def all_compare_operators(request): +@pytest.fixture(params=["inner", "outer", "left", "right"]) +def join_type(request): """ - Fixture for dunder names for common compare operations - - * >= - * > - * == - * != - * < - * <= + Fixture for trying all types of join operations. """ return request.param -@pytest.fixture(params=["__le__", "__lt__", "__ge__", "__gt__"]) -def compare_operators_no_eq_ne(request): +@pytest.fixture(params=["nlargest", "nsmallest"]) +def nselect_method(request): """ - Fixture for dunder names for compare operations except == and != - - * >= - * > - * < - * <= + Fixture for trying all nselect methods. """ return request.param -@pytest.fixture( - params=["__and__", "__rand__", "__or__", "__ror__", "__xor__", "__rxor__"] -) -def all_logical_operators(request): +# ---------------------------------------------------------------- +# Missing values & co. +# ---------------------------------------------------------------- +@pytest.fixture(params=[None, np.nan, pd.NaT, float("nan"), np.float("NaN"), pd.NA]) +def nulls_fixture(request): """ - Fixture for dunder names for common logical operations - - * | - * & - * ^ + Fixture for each null type in pandas. """ return request.param -@pytest.fixture(params=[None, "gzip", "bz2", "zip", "xz"]) -def compression(request): +nulls_fixture2 = nulls_fixture # Generate cartesian product of nulls_fixture + + +@pytest.fixture(params=[None, np.nan, pd.NaT]) +def unique_nulls_fixture(request): """ - Fixture for trying common compression types in compression tests. + Fixture for each null type in pandas, each null type exactly once. """ return request.param -@pytest.fixture(params=["gzip", "bz2", "zip", "xz"]) -def compression_only(request): - """ - Fixture for trying common compression types in compression tests excluding - uncompressed case. - """ - return request.param - - -@pytest.fixture(params=[True, False]) -def writable(request): - """ - Fixture that an array is writable. - """ - return request.param +# Generate cartesian product of unique_nulls_fixture: +unique_nulls_fixture2 = unique_nulls_fixture -@pytest.fixture(scope="module") -def datetime_tz_utc(): - """ - Yields the UTC timezone object from the datetime module. +# ---------------------------------------------------------------- +# Classes +# ---------------------------------------------------------------- +@pytest.fixture(params=[pd.Index, pd.Series], ids=["index", "series"]) +def index_or_series(request): """ - return timezone.utc + Fixture to parametrize over Index and Series, made necessary by a mypy + bug, giving an error: + List item 0 has incompatible type "Type[Series]"; expected "Type[PandasObject]" -@pytest.fixture(params=["utc", "dateutil/UTC", utc, tzutc(), timezone.utc]) -def utc_fixture(request): - """ - Fixture to provide variants of UTC timezone strings and tzinfo objects. + See GH#29725 """ return request.param -@pytest.fixture(params=["inner", "outer", "left", "right"]) -def join_type(request): +@pytest.fixture +def dict_subclass(): """ - Fixture for trying all types of join operations. + Fixture for a dictionary subclass. """ - return request.param + class TestSubDict(dict): + def __init__(self, *args, **kwargs): + dict.__init__(self, *args, **kwargs) -@pytest.fixture -def strict_data_files(pytestconfig): - """ - Returns the configuration for the test setting `--strict-data-files`. - """ - return pytestconfig.getoption("--strict-data-files") + return TestSubDict @pytest.fixture -def datapath(strict_data_files): +def non_mapping_dict_subclass(): + """ + Fixture for a non-mapping dictionary subclass. """ - Get the path to a data file. - Parameters - ---------- - path : str - Path to the file, relative to ``pandas/tests/`` + class TestNonDictMapping(abc.Mapping): + def __init__(self, underlying_dict): + self._data = underlying_dict - Returns - ------- - path including ``pandas/tests``. + def __getitem__(self, key): + return self._data.__getitem__(key) - Raises - ------ - ValueError - If the path doesn't exist and the --strict-data-files option is set. - """ - BASE_PATH = os.path.join(os.path.dirname(__file__), "tests") + def __iter__(self): + return self._data.__iter__() - def deco(*args): - path = os.path.join(BASE_PATH, *args) - if not os.path.exists(path): - if strict_data_files: - raise ValueError( - f"Could not find file {path} and --strict-data-files is set." - ) - else: - pytest.skip(f"Could not find {path}.") - return path + def __len__(self): + return self._data.__len__() - return deco + return TestNonDictMapping +# ---------------------------------------------------------------- +# Indices +# ---------------------------------------------------------------- @pytest.fixture -def iris(datapath): +def multiindex_year_month_day_dataframe_random_data(): """ - The iris dataset as a DataFrame. + DataFrame with 3 level MultiIndex (year, month, day) covering + first 100 business days from 2000-01-01 with random data """ - return pd.read_csv(datapath("data", "iris.csv")) + tdf = tm.makeTimeDataFrame(100) + ymd = tdf.groupby([lambda x: x.year, lambda x: x.month, lambda x: x.day]).sum() + # use Int64Index, to make sure things work + ymd.index.set_levels([lev.astype("i8") for lev in ymd.index.levels], inplace=True) + ymd.index.set_names(["year", "month", "day"], inplace=True) + return ymd -@pytest.fixture(params=["nlargest", "nsmallest"]) -def nselect_method(request): +def _create_multiindex(): """ - Fixture for trying all nselect methods. + MultiIndex used to test the general functionality of this object """ - return request.param + # See Also: tests.multi.conftest.idx + major_axis = Index(["foo", "bar", "baz", "qux"]) + minor_axis = Index(["one", "two"]) -@pytest.fixture(params=["first", "last", False]) -def keep(request): - """ - Valid values for the 'keep' parameter used in - .duplicated or .drop_duplicates - """ - return request.param + major_codes = np.array([0, 0, 1, 2, 3, 3]) + minor_codes = np.array([0, 1, 0, 1, 0, 1]) + index_names = ["first", "second"] + mi = MultiIndex( + levels=[major_axis, minor_axis], + codes=[major_codes, minor_codes], + names=index_names, + verify_integrity=False, + ) + return mi -@pytest.fixture(params=["left", "right", "both", "neither"]) -def closed(request): - """ - Fixture for trying all interval closed parameters. - """ - return request.param +indices_dict = { + "unicode": tm.makeUnicodeIndex(100), + "string": tm.makeStringIndex(100), + "datetime": tm.makeDateIndex(100), + "datetime-tz": tm.makeDateIndex(100, tz="US/Pacific"), + "period": tm.makePeriodIndex(100), + "timedelta": tm.makeTimedeltaIndex(100), + "int": tm.makeIntIndex(100), + "uint": tm.makeUIntIndex(100), + "range": tm.makeRangeIndex(100), + "float": tm.makeFloatIndex(100), + "bool": tm.makeBoolIndex(10), + "categorical": tm.makeCategoricalIndex(100), + "interval": tm.makeIntervalIndex(100), + "empty": Index([]), + "tuples": MultiIndex.from_tuples(zip(["foo", "bar", "baz"], [1, 2, 3])), + "multi": _create_multiindex(), + "repeats": Index([0, 0, 1, 1, 2, 2]), +} -@pytest.fixture(params=["left", "right", "both", "neither"]) -def other_closed(request): - """ - Secondary closed fixture to allow parametrizing over all pairs of closed. +@pytest.fixture(params=indices_dict.keys()) +def indices(request): """ - return request.param - + Fixture for many "simple" kinds of indices. -@pytest.fixture(params=[None, np.nan, pd.NaT, float("nan"), np.float("NaN"), pd.NA]) -def nulls_fixture(request): - """ - Fixture for each null type in pandas. + These indices are unlikely to cover corner cases, e.g. + - no names + - no NaTs/NaNs + - no values near implementation bounds + - ... """ - return request.param - - -nulls_fixture2 = nulls_fixture # Generate cartesian product of nulls_fixture + # copy to avoid mutation, e.g. setting .name + return indices_dict[request.param].copy() -@pytest.fixture(params=[None, np.nan, pd.NaT]) -def unique_nulls_fixture(request): +# ---------------------------------------------------------------- +# Series' +# ---------------------------------------------------------------- +@pytest.fixture +def datetime_series(): """ - Fixture for each null type in pandas, each null type exactly once. + Fixture for Series of floats with DatetimeIndex """ - return request.param + s = tm.makeTimeSeries() + s.name = "ts" + return s -# Generate cartesian product of unique_nulls_fixture: -unique_nulls_fixture2 = unique_nulls_fixture +def _create_series(index): + """ Helper for the _series dict """ + size = len(index) + data = np.random.randn(size) + return pd.Series(data, index=index, name="a") -TIMEZONES = [ - None, - "UTC", - "US/Eastern", - "Asia/Tokyo", - "dateutil/US/Pacific", - "dateutil/Asia/Singapore", - tzutc(), - tzlocal(), - FixedOffset(300), - FixedOffset(0), - FixedOffset(-300), - timezone.utc, - timezone(timedelta(hours=1)), - timezone(timedelta(hours=-1), name="foo"), -] -TIMEZONE_IDS = [repr(i) for i in TIMEZONES] +_series = { + f"series-with-{index_id}-index": _create_series(index) + for index_id, index in indices_dict.items() +} -@td.parametrize_fixture_doc(str(TIMEZONE_IDS)) -@pytest.fixture(params=TIMEZONES, ids=TIMEZONE_IDS) -def tz_naive_fixture(request): +@pytest.fixture +def series_with_simple_index(indices): """ - Fixture for trying timezones including default (None): {0} + Fixture for tests on series with changing types of indices. """ - return request.param + return _create_series(indices) -@td.parametrize_fixture_doc(str(TIMEZONE_IDS[1:])) -@pytest.fixture(params=TIMEZONES[1:], ids=TIMEZONE_IDS[1:]) -def tz_aware_fixture(request): +_narrow_dtypes = [ + np.float16, + np.float32, + np.int8, + np.int16, + np.int32, + np.uint8, + np.uint16, + np.uint32, +] +_narrow_series = { + f"{dtype.__name__}-series": tm.makeFloatSeries(name="a").astype(dtype) + for dtype in _narrow_dtypes +} + + +@pytest.fixture(params=_narrow_series.keys()) +def narrow_series(request): """ - Fixture for trying explicit timezones: {0} + Fixture for Series with low precision data types """ - return request.param + # copy to avoid mutation, e.g. setting .name + return _narrow_series[request.param].copy() -# Generate cartesian product of tz_aware_fixture: -tz_aware_fixture2 = tz_aware_fixture +_index_or_series_objs = {**indices_dict, **_series, **_narrow_series} + + +@pytest.fixture(params=_index_or_series_objs.keys()) +def index_or_series_obj(request): + """ + Fixture for tests on indexes, series and series with a narrow dtype + copy to avoid mutation, e.g. setting .name + """ + return _index_or_series_objs[request.param].copy(deep=True) # ---------------------------------------------------------------- -# Dtypes +# DataFrames # ---------------------------------------------------------------- +@pytest.fixture +def float_frame(): + """ + Fixture for DataFrame of floats with index of unique strings -UNSIGNED_INT_DTYPES = ["uint8", "uint16", "uint32", "uint64"] -UNSIGNED_EA_INT_DTYPES = ["UInt8", "UInt16", "UInt32", "UInt64"] -SIGNED_INT_DTYPES = [int, "int8", "int16", "int32", "int64"] -SIGNED_EA_INT_DTYPES = ["Int8", "Int16", "Int32", "Int64"] -ALL_INT_DTYPES = UNSIGNED_INT_DTYPES + SIGNED_INT_DTYPES -ALL_EA_INT_DTYPES = UNSIGNED_EA_INT_DTYPES + SIGNED_EA_INT_DTYPES + Columns are ['A', 'B', 'C', 'D']. -FLOAT_DTYPES = [float, "float32", "float64"] -COMPLEX_DTYPES = [complex, "complex64", "complex128"] -STRING_DTYPES = [str, "str", "U"] + A B C D + P7GACiRnxd -0.465578 -0.361863 0.886172 -0.053465 + qZKh6afn8n -0.466693 -0.373773 0.266873 1.673901 + tkp0r6Qble 0.148691 -0.059051 0.174817 1.598433 + wP70WOCtv8 0.133045 -0.581994 -0.992240 0.261651 + M2AeYQMnCz -1.207959 -0.185775 0.588206 0.563938 + QEPzyGDYDo -0.381843 -0.758281 0.502575 -0.565053 + r78Jwns6dn -0.653707 0.883127 0.682199 0.206159 + ... ... ... ... ... + IHEGx9NO0T -0.277360 0.113021 -1.018314 0.196316 + lPMj8K27FA -1.313667 -0.604776 -1.305618 -0.863999 + qa66YMWQa5 1.110525 0.475310 -0.747865 0.032121 + yOa0ATsmcE -0.431457 0.067094 0.096567 -0.264962 + 65znX3uRNG 1.528446 0.160416 -0.109635 -0.032987 + eCOBvKqf3e 0.235281 1.622222 0.781255 0.392871 + xSucinXxuV -1.263557 0.252799 -0.552247 0.400426 -DATETIME64_DTYPES = ["datetime64[ns]", "M8[ns]"] -TIMEDELTA64_DTYPES = ["timedelta64[ns]", "m8[ns]"] + [30 rows x 4 columns] + """ + return DataFrame(tm.getSeriesData()) -BOOL_DTYPES = [bool, "bool"] -BYTES_DTYPES = [bytes, "bytes"] -OBJECT_DTYPES = [object, "object"] -ALL_REAL_DTYPES = FLOAT_DTYPES + ALL_INT_DTYPES -ALL_NUMPY_DTYPES = ( - ALL_REAL_DTYPES - + COMPLEX_DTYPES - + STRING_DTYPES - + DATETIME64_DTYPES - + TIMEDELTA64_DTYPES - + BOOL_DTYPES - + OBJECT_DTYPES - + BYTES_DTYPES -) +# ---------------------------------------------------------------- +# Operators & Operations +# ---------------------------------------------------------------- +_all_arithmetic_operators = [ + "__add__", + "__radd__", + "__sub__", + "__rsub__", + "__mul__", + "__rmul__", + "__floordiv__", + "__rfloordiv__", + "__truediv__", + "__rtruediv__", + "__pow__", + "__rpow__", + "__mod__", + "__rmod__", +] -@pytest.fixture(params=STRING_DTYPES) -def string_dtype(request): +@pytest.fixture(params=_all_arithmetic_operators) +def all_arithmetic_operators(request): """ - Parametrized fixture for string dtypes. - - * str - * 'str' - * 'U' + Fixture for dunder names for common arithmetic operations. """ return request.param -@pytest.fixture(params=BYTES_DTYPES) -def bytes_dtype(request): +@pytest.fixture( + params=[ + operator.add, + ops.radd, + operator.sub, + ops.rsub, + operator.mul, + ops.rmul, + operator.truediv, + ops.rtruediv, + operator.floordiv, + ops.rfloordiv, + operator.mod, + ops.rmod, + operator.pow, + ops.rpow, + ] +) +def all_arithmetic_functions(request): """ - Parametrized fixture for bytes dtypes. + Fixture for operator and roperator arithmetic functions. - * bytes - * 'bytes' + Notes + ----- + This includes divmod and rdivmod, whereas all_arithmetic_operators + does not. """ return request.param -@pytest.fixture(params=OBJECT_DTYPES) -def object_dtype(request): - """ - Parametrized fixture for object dtypes. - - * object - * 'object' - """ - return request.param +_all_numeric_reductions = [ + "sum", + "max", + "min", + "mean", + "prod", + "std", + "var", + "median", + "kurt", + "skew", +] -@pytest.fixture(params=DATETIME64_DTYPES) -def datetime64_dtype(request): +@pytest.fixture(params=_all_numeric_reductions) +def all_numeric_reductions(request): """ - Parametrized fixture for datetime64 dtypes. - - * 'datetime64[ns]' - * 'M8[ns]' + Fixture for numeric reduction names. """ return request.param -@pytest.fixture(params=TIMEDELTA64_DTYPES) -def timedelta64_dtype(request): - """ - Parametrized fixture for timedelta64 dtypes. - - * 'timedelta64[ns]' - * 'm8[ns]' - """ - return request.param +_all_boolean_reductions = ["all", "any"] -@pytest.fixture(params=FLOAT_DTYPES) -def float_dtype(request): +@pytest.fixture(params=_all_boolean_reductions) +def all_boolean_reductions(request): """ - Parameterized fixture for float dtypes. - - * float - * 'float32' - * 'float64' + Fixture for boolean reduction names. """ return request.param -@pytest.fixture(params=COMPLEX_DTYPES) -def complex_dtype(request): +@pytest.fixture(params=["__eq__", "__ne__", "__le__", "__lt__", "__ge__", "__gt__"]) +def all_compare_operators(request): """ - Parameterized fixture for complex dtypes. + Fixture for dunder names for common compare operations - * complex - * 'complex64' - * 'complex128' + * >= + * > + * == + * != + * < + * <= """ return request.param -@pytest.fixture(params=SIGNED_INT_DTYPES) -def sint_dtype(request): +@pytest.fixture(params=["__le__", "__lt__", "__ge__", "__gt__"]) +def compare_operators_no_eq_ne(request): """ - Parameterized fixture for signed integer dtypes. + Fixture for dunder names for compare operations except == and != - * int - * 'int8' - * 'int16' - * 'int32' - * 'int64' + * >= + * > + * < + * <= """ return request.param -@pytest.fixture(params=UNSIGNED_INT_DTYPES) -def uint_dtype(request): +@pytest.fixture( + params=["__and__", "__rand__", "__or__", "__ror__", "__xor__", "__rxor__"] +) +def all_logical_operators(request): """ - Parameterized fixture for unsigned integer dtypes. + Fixture for dunder names for common logical operations - * 'uint8' - * 'uint16' - * 'uint32' - * 'uint64' + * | + * & + * ^ """ return request.param -@pytest.fixture(params=ALL_INT_DTYPES) -def any_int_dtype(request): +# ---------------------------------------------------------------- +# Data sets/files +# ---------------------------------------------------------------- +@pytest.fixture +def strict_data_files(pytestconfig): """ - Parameterized fixture for any integer dtype. - - * int - * 'int8' - * 'uint8' - * 'int16' - * 'uint16' - * 'int32' - * 'uint32' - * 'int64' - * 'uint64' + Returns the configuration for the test setting `--strict-data-files`. """ - return request.param + return pytestconfig.getoption("--strict-data-files") -@pytest.fixture(params=ALL_EA_INT_DTYPES) -def any_nullable_int_dtype(request): +@pytest.fixture +def datapath(strict_data_files): """ - Parameterized fixture for any nullable integer dtype. + Get the path to a data file. - * 'UInt8' - * 'Int8' - * 'UInt16' - * 'Int16' - * 'UInt32' - * 'Int32' - * 'UInt64' - * 'Int64' - """ - return request.param + Parameters + ---------- + path : str + Path to the file, relative to ``pandas/tests/`` + Returns + ------- + path including ``pandas/tests``. -@pytest.fixture(params=ALL_REAL_DTYPES) -def any_real_dtype(request): + Raises + ------ + ValueError + If the path doesn't exist and the --strict-data-files option is set. """ - Parameterized fixture for any (purely) real numeric dtype. + BASE_PATH = os.path.join(os.path.dirname(__file__), "tests") - * int - * 'int8' - * 'uint8' - * 'int16' - * 'uint16' - * 'int32' - * 'uint32' - * 'int64' - * 'uint64' - * float - * 'float32' - * 'float64' + def deco(*args): + path = os.path.join(BASE_PATH, *args) + if not os.path.exists(path): + if strict_data_files: + raise ValueError( + f"Could not find file {path} and --strict-data-files is set." + ) + else: + pytest.skip(f"Could not find {path}.") + return path + + return deco + + +@pytest.fixture +def iris(datapath): + """ + The iris dataset as a DataFrame. + """ + return pd.read_csv(datapath("data", "iris.csv")) + + +# ---------------------------------------------------------------- +# Time zones +# ---------------------------------------------------------------- +TIMEZONES = [ + None, + "UTC", + "US/Eastern", + "Asia/Tokyo", + "dateutil/US/Pacific", + "dateutil/Asia/Singapore", + tzutc(), + tzlocal(), + FixedOffset(300), + FixedOffset(0), + FixedOffset(-300), + timezone.utc, + timezone(timedelta(hours=1)), + timezone(timedelta(hours=-1), name="foo"), +] +TIMEZONE_IDS = [repr(i) for i in TIMEZONES] + + +@td.parametrize_fixture_doc(str(TIMEZONE_IDS)) +@pytest.fixture(params=TIMEZONES, ids=TIMEZONE_IDS) +def tz_naive_fixture(request): + """ + Fixture for trying timezones including default (None): {0} """ return request.param -@pytest.fixture(params=ALL_NUMPY_DTYPES) -def any_numpy_dtype(request): +@td.parametrize_fixture_doc(str(TIMEZONE_IDS[1:])) +@pytest.fixture(params=TIMEZONES[1:], ids=TIMEZONE_IDS[1:]) +def tz_aware_fixture(request): """ - Parameterized fixture for all numpy dtypes. + Fixture for trying explicit timezones: {0} + """ + return request.param + + +# Generate cartesian product of tz_aware_fixture: +tz_aware_fixture2 = tz_aware_fixture + + +@pytest.fixture(scope="module") +def datetime_tz_utc(): + """ + Yields the UTC timezone object from the datetime module. + """ + return timezone.utc + + +@pytest.fixture(params=["utc", "dateutil/UTC", utc, tzutc(), timezone.utc]) +def utc_fixture(request): + """ + Fixture to provide variants of UTC timezone strings and tzinfo objects. + """ + return request.param + + +# ---------------------------------------------------------------- +# Dtypes +# ---------------------------------------------------------------- + +UNSIGNED_INT_DTYPES = ["uint8", "uint16", "uint32", "uint64"] +UNSIGNED_EA_INT_DTYPES = ["UInt8", "UInt16", "UInt32", "UInt64"] +SIGNED_INT_DTYPES = [int, "int8", "int16", "int32", "int64"] +SIGNED_EA_INT_DTYPES = ["Int8", "Int16", "Int32", "Int64"] +ALL_INT_DTYPES = UNSIGNED_INT_DTYPES + SIGNED_INT_DTYPES +ALL_EA_INT_DTYPES = UNSIGNED_EA_INT_DTYPES + SIGNED_EA_INT_DTYPES + +FLOAT_DTYPES = [float, "float32", "float64"] +COMPLEX_DTYPES = [complex, "complex64", "complex128"] +STRING_DTYPES = [str, "str", "U"] + +DATETIME64_DTYPES = ["datetime64[ns]", "M8[ns]"] +TIMEDELTA64_DTYPES = ["timedelta64[ns]", "m8[ns]"] + +BOOL_DTYPES = [bool, "bool"] +BYTES_DTYPES = [bytes, "bytes"] +OBJECT_DTYPES = [object, "object"] + +ALL_REAL_DTYPES = FLOAT_DTYPES + ALL_INT_DTYPES +ALL_NUMPY_DTYPES = ( + ALL_REAL_DTYPES + + COMPLEX_DTYPES + + STRING_DTYPES + + DATETIME64_DTYPES + + TIMEDELTA64_DTYPES + + BOOL_DTYPES + + OBJECT_DTYPES + + BYTES_DTYPES +) + + +@pytest.fixture(params=STRING_DTYPES) +def string_dtype(request): + """ + Parametrized fixture for string dtypes. - * bool - * 'bool' - * int - * 'int8' - * 'uint8' - * 'int16' - * 'uint16' - * 'int32' - * 'uint32' - * 'int64' - * 'uint64' - * float - * 'float32' - * 'float64' - * complex - * 'complex64' - * 'complex128' * str * 'str' * 'U' + """ + return request.param + + +@pytest.fixture(params=BYTES_DTYPES) +def bytes_dtype(request): + """ + Parametrized fixture for bytes dtypes. + * bytes * 'bytes' - * 'datetime64[ns]' - * 'M8[ns]' - * 'timedelta64[ns]' - * 'm8[ns]' + """ + return request.param + + +@pytest.fixture(params=OBJECT_DTYPES) +def object_dtype(request): + """ + Parametrized fixture for object dtypes. + * object * 'object' """ return request.param -# categoricals are handled separately -_any_skipna_inferred_dtype = [ - ("string", ["a", np.nan, "c"]), - ("string", ["a", pd.NA, "c"]), - ("bytes", [b"a", np.nan, b"c"]), - ("empty", [np.nan, np.nan, np.nan]), - ("empty", []), - ("mixed-integer", ["a", np.nan, 2]), - ("mixed", ["a", np.nan, 2.0]), - ("floating", [1.0, np.nan, 2.0]), - ("integer", [1, np.nan, 2]), - ("mixed-integer-float", [1, np.nan, 2.0]), - ("decimal", [Decimal(1), np.nan, Decimal(2)]), - ("boolean", [True, np.nan, False]), - ("boolean", [True, pd.NA, False]), - ("datetime64", [np.datetime64("2013-01-01"), np.nan, np.datetime64("2018-01-01")]), - ("datetime", [pd.Timestamp("20130101"), np.nan, pd.Timestamp("20180101")]), - ("date", [date(2013, 1, 1), np.nan, date(2018, 1, 1)]), - # The following two dtypes are commented out due to GH 23554 - # ('complex', [1 + 1j, np.nan, 2 + 2j]), - # ('timedelta64', [np.timedelta64(1, 'D'), - # np.nan, np.timedelta64(2, 'D')]), - ("timedelta", [timedelta(1), np.nan, timedelta(2)]), - ("time", [time(1), np.nan, time(2)]), - ("period", [pd.Period(2013), pd.NaT, pd.Period(2018)]), - ("interval", [pd.Interval(0, 1), np.nan, pd.Interval(0, 2)]), -] -ids, _ = zip(*_any_skipna_inferred_dtype) # use inferred type as fixture-id +@pytest.fixture(params=DATETIME64_DTYPES) +def datetime64_dtype(request): + """ + Parametrized fixture for datetime64 dtypes. + * 'datetime64[ns]' + * 'M8[ns]' + """ + return request.param -@pytest.fixture(params=_any_skipna_inferred_dtype, ids=ids) -def any_skipna_inferred_dtype(request): + +@pytest.fixture(params=TIMEDELTA64_DTYPES) +def timedelta64_dtype(request): """ - Fixture for all inferred dtypes from _libs.lib.infer_dtype + Parametrized fixture for timedelta64 dtypes. - The covered (inferred) types are: - * 'string' - * 'empty' - * 'bytes' - * 'mixed' - * 'mixed-integer' - * 'mixed-integer-float' - * 'floating' - * 'integer' - * 'decimal' - * 'boolean' - * 'datetime64' - * 'datetime' - * 'date' - * 'timedelta' - * 'time' - * 'period' - * 'interval' + * 'timedelta64[ns]' + * 'm8[ns]' + """ + return request.param - Returns - ------- - inferred_dtype : str - The string for the inferred dtype from _libs.lib.infer_dtype - values : np.ndarray - An array of object dtype that will be inferred to have - `inferred_dtype` - Examples - -------- - >>> import pandas._libs.lib as lib - >>> - >>> def test_something(any_skipna_inferred_dtype): - ... inferred_dtype, values = any_skipna_inferred_dtype - ... # will pass - ... assert lib.infer_dtype(values, skipna=True) == inferred_dtype +@pytest.fixture(params=FLOAT_DTYPES) +def float_dtype(request): """ - inferred_dtype, values = request.param - values = np.array(values, dtype=object) # object dtype to avoid casting + Parameterized fixture for float dtypes. - # correctness of inference tested in tests/dtypes/test_inference.py - return inferred_dtype, values + * float + * 'float32' + * 'float64' + """ + return request.param -@pytest.fixture( - params=[ - getattr(pd.offsets, o) - for o in pd.offsets.__all__ - if issubclass(getattr(pd.offsets, o), pd.offsets.Tick) - ] -) -def tick_classes(request): +@pytest.fixture(params=COMPLEX_DTYPES) +def complex_dtype(request): """ - Fixture for Tick based datetime offsets available for a time series. + Parameterized fixture for complex dtypes. + + * complex + * 'complex64' + * 'complex128' """ return request.param -# ---------------------------------------------------------------- -# Global setup for tests using Hypothesis +@pytest.fixture(params=SIGNED_INT_DTYPES) +def sint_dtype(request): + """ + Parameterized fixture for signed integer dtypes. + * int + * 'int8' + * 'int16' + * 'int32' + * 'int64' + """ + return request.param -# Registering these strategies makes them globally available via st.from_type, -# which is use for offsets in tests/tseries/offsets/test_offsets_properties.py -for name in "MonthBegin MonthEnd BMonthBegin BMonthEnd".split(): - cls = getattr(pd.tseries.offsets, name) - st.register_type_strategy( - cls, st.builds(cls, n=st.integers(-99, 99), normalize=st.booleans()) - ) -for name in "YearBegin YearEnd BYearBegin BYearEnd".split(): - cls = getattr(pd.tseries.offsets, name) - st.register_type_strategy( - cls, - st.builds( - cls, - n=st.integers(-5, 5), - normalize=st.booleans(), - month=st.integers(min_value=1, max_value=12), - ), - ) +@pytest.fixture(params=UNSIGNED_INT_DTYPES) +def uint_dtype(request): + """ + Parameterized fixture for unsigned integer dtypes. -for name in "QuarterBegin QuarterEnd BQuarterBegin BQuarterEnd".split(): - cls = getattr(pd.tseries.offsets, name) - st.register_type_strategy( - cls, - st.builds( - cls, - n=st.integers(-24, 24), - normalize=st.booleans(), - startingMonth=st.integers(min_value=1, max_value=12), - ), - ) + * 'uint8' + * 'uint16' + * 'uint32' + * 'uint64' + """ + return request.param -@pytest.fixture -def datetime_series(): +@pytest.fixture(params=ALL_INT_DTYPES) +def any_int_dtype(request): """ - Fixture for Series of floats with DatetimeIndex + Parameterized fixture for any integer dtype. + + * int + * 'int8' + * 'uint8' + * 'int16' + * 'uint16' + * 'int32' + * 'uint32' + * 'int64' + * 'uint64' """ - s = tm.makeTimeSeries() - s.name = "ts" - return s + return request.param -@pytest.fixture -def float_frame(): +@pytest.fixture(params=ALL_EA_INT_DTYPES) +def any_nullable_int_dtype(request): """ - Fixture for DataFrame of floats with index of unique strings + Parameterized fixture for any nullable integer dtype. - Columns are ['A', 'B', 'C', 'D']. + * 'UInt8' + * 'Int8' + * 'UInt16' + * 'Int16' + * 'UInt32' + * 'Int32' + * 'UInt64' + * 'Int64' + """ + return request.param - A B C D - P7GACiRnxd -0.465578 -0.361863 0.886172 -0.053465 - qZKh6afn8n -0.466693 -0.373773 0.266873 1.673901 - tkp0r6Qble 0.148691 -0.059051 0.174817 1.598433 - wP70WOCtv8 0.133045 -0.581994 -0.992240 0.261651 - M2AeYQMnCz -1.207959 -0.185775 0.588206 0.563938 - QEPzyGDYDo -0.381843 -0.758281 0.502575 -0.565053 - r78Jwns6dn -0.653707 0.883127 0.682199 0.206159 - ... ... ... ... ... - IHEGx9NO0T -0.277360 0.113021 -1.018314 0.196316 - lPMj8K27FA -1.313667 -0.604776 -1.305618 -0.863999 - qa66YMWQa5 1.110525 0.475310 -0.747865 0.032121 - yOa0ATsmcE -0.431457 0.067094 0.096567 -0.264962 - 65znX3uRNG 1.528446 0.160416 -0.109635 -0.032987 - eCOBvKqf3e 0.235281 1.622222 0.781255 0.392871 - xSucinXxuV -1.263557 0.252799 -0.552247 0.400426 - [30 rows x 4 columns] +@pytest.fixture(params=ALL_REAL_DTYPES) +def any_real_dtype(request): """ - return DataFrame(tm.getSeriesData()) - + Parameterized fixture for any (purely) real numeric dtype. -@pytest.fixture(params=[pd.Index, pd.Series], ids=["index", "series"]) -def index_or_series(request): + * int + * 'int8' + * 'uint8' + * 'int16' + * 'uint16' + * 'int32' + * 'uint32' + * 'int64' + * 'uint64' + * float + * 'float32' + * 'float64' """ - Fixture to parametrize over Index and Series, made necessary by a mypy - bug, giving an error: + return request.param - List item 0 has incompatible type "Type[Series]"; expected "Type[PandasObject]" - See GH#29725 +@pytest.fixture(params=ALL_NUMPY_DTYPES) +def any_numpy_dtype(request): + """ + Parameterized fixture for all numpy dtypes. + + * bool + * 'bool' + * int + * 'int8' + * 'uint8' + * 'int16' + * 'uint16' + * 'int32' + * 'uint32' + * 'int64' + * 'uint64' + * float + * 'float32' + * 'float64' + * complex + * 'complex64' + * 'complex128' + * str + * 'str' + * 'U' + * bytes + * 'bytes' + * 'datetime64[ns]' + * 'M8[ns]' + * 'timedelta64[ns]' + * 'm8[ns]' + * object + * 'object' """ return request.param -@pytest.fixture -def dict_subclass(): - """ - Fixture for a dictionary subclass. - """ - - class TestSubDict(dict): - def __init__(self, *args, **kwargs): - dict.__init__(self, *args, **kwargs) - - return TestSubDict +# categoricals are handled separately +_any_skipna_inferred_dtype = [ + ("string", ["a", np.nan, "c"]), + ("string", ["a", pd.NA, "c"]), + ("bytes", [b"a", np.nan, b"c"]), + ("empty", [np.nan, np.nan, np.nan]), + ("empty", []), + ("mixed-integer", ["a", np.nan, 2]), + ("mixed", ["a", np.nan, 2.0]), + ("floating", [1.0, np.nan, 2.0]), + ("integer", [1, np.nan, 2]), + ("mixed-integer-float", [1, np.nan, 2.0]), + ("decimal", [Decimal(1), np.nan, Decimal(2)]), + ("boolean", [True, np.nan, False]), + ("boolean", [True, pd.NA, False]), + ("datetime64", [np.datetime64("2013-01-01"), np.nan, np.datetime64("2018-01-01")]), + ("datetime", [pd.Timestamp("20130101"), np.nan, pd.Timestamp("20180101")]), + ("date", [date(2013, 1, 1), np.nan, date(2018, 1, 1)]), + # The following two dtypes are commented out due to GH 23554 + # ('complex', [1 + 1j, np.nan, 2 + 2j]), + # ('timedelta64', [np.timedelta64(1, 'D'), + # np.nan, np.timedelta64(2, 'D')]), + ("timedelta", [timedelta(1), np.nan, timedelta(2)]), + ("time", [time(1), np.nan, time(2)]), + ("period", [pd.Period(2013), pd.NaT, pd.Period(2018)]), + ("interval", [pd.Interval(0, 1), np.nan, pd.Interval(0, 2)]), +] +ids, _ = zip(*_any_skipna_inferred_dtype) # use inferred type as fixture-id -@pytest.fixture -def non_mapping_dict_subclass(): - """ - Fixture for a non-mapping dictionary subclass. +@pytest.fixture(params=_any_skipna_inferred_dtype, ids=ids) +def any_skipna_inferred_dtype(request): """ + Fixture for all inferred dtypes from _libs.lib.infer_dtype - class TestNonDictMapping(abc.Mapping): - def __init__(self, underlying_dict): - self._data = underlying_dict - - def __getitem__(self, key): - return self._data.__getitem__(key) - - def __iter__(self): - return self._data.__iter__() - - def __len__(self): - return self._data.__len__() - - return TestNonDictMapping + The covered (inferred) types are: + * 'string' + * 'empty' + * 'bytes' + * 'mixed' + * 'mixed-integer' + * 'mixed-integer-float' + * 'floating' + * 'integer' + * 'decimal' + * 'boolean' + * 'datetime64' + * 'datetime' + * 'date' + * 'timedelta' + * 'time' + * 'period' + * 'interval' + Returns + ------- + inferred_dtype : str + The string for the inferred dtype from _libs.lib.infer_dtype + values : np.ndarray + An array of object dtype that will be inferred to have + `inferred_dtype` -def _create_multiindex(): - """ - MultiIndex used to test the general functionality of this object + Examples + -------- + >>> import pandas._libs.lib as lib + >>> + >>> def test_something(any_skipna_inferred_dtype): + ... inferred_dtype, values = any_skipna_inferred_dtype + ... # will pass + ... assert lib.infer_dtype(values, skipna=True) == inferred_dtype """ + inferred_dtype, values = request.param + values = np.array(values, dtype=object) # object dtype to avoid casting - # See Also: tests.multi.conftest.idx - 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"] - mi = MultiIndex( - levels=[major_axis, minor_axis], - codes=[major_codes, minor_codes], - names=index_names, - verify_integrity=False, - ) - return mi - - -indices_dict = { - "unicode": tm.makeUnicodeIndex(100), - "string": tm.makeStringIndex(100), - "datetime": tm.makeDateIndex(100), - "datetime-tz": tm.makeDateIndex(100, tz="US/Pacific"), - "period": tm.makePeriodIndex(100), - "timedelta": tm.makeTimedeltaIndex(100), - "int": tm.makeIntIndex(100), - "uint": tm.makeUIntIndex(100), - "range": tm.makeRangeIndex(100), - "float": tm.makeFloatIndex(100), - "bool": tm.makeBoolIndex(10), - "categorical": tm.makeCategoricalIndex(100), - "interval": tm.makeIntervalIndex(100), - "empty": Index([]), - "tuples": MultiIndex.from_tuples(zip(["foo", "bar", "baz"], [1, 2, 3])), - "multi": _create_multiindex(), - "repeats": Index([0, 0, 1, 1, 2, 2]), -} + # correctness of inference tested in tests/dtypes/test_inference.py + return inferred_dtype, values -@pytest.fixture(params=indices_dict.keys()) -def indices(request): +# ---------------------------------------------------------------- +# Misc +# ---------------------------------------------------------------- +@pytest.fixture +def ip(): """ - Fixture for many "simple" kinds of indices. + Get an instance of IPython.InteractiveShell. - These indices are unlikely to cover corner cases, e.g. - - no names - - no NaTs/NaNs - - no values near implementation bounds - - ... + Will raise a skip if IPython is not installed. """ - # copy to avoid mutation, e.g. setting .name - return indices_dict[request.param].copy() - - -def _create_series(index): - """ Helper for the _series dict """ - size = len(index) - data = np.random.randn(size) - return pd.Series(data, index=index, name="a") - + pytest.importorskip("IPython", minversion="6.0.0") + from IPython.core.interactiveshell import InteractiveShell -_series = { - f"series-with-{index_id}-index": _create_series(index) - for index_id, index in indices_dict.items() -} + return InteractiveShell() -@pytest.fixture -def series_with_simple_index(indices): +@pytest.fixture(params=["bsr", "coo", "csc", "csr", "dia", "dok", "lil"]) +def spmatrix(request): """ - Fixture for tests on series with changing types of indices. + Yields scipy sparse matrix classes. """ - return _create_series(indices) + from scipy import sparse + return getattr(sparse, request.param + "_matrix") -_narrow_dtypes = [ - np.float16, - np.float32, - np.int8, - np.int16, - np.int32, - np.uint8, - np.uint16, - np.uint32, -] -_narrow_series = { - f"{dtype.__name__}-series": tm.makeFloatSeries(name="a").astype(dtype) - for dtype in _narrow_dtypes -} +_cython_table = pd.core.base.SelectionMixin._cython_table.items() -@pytest.fixture(params=_narrow_series.keys()) -def narrow_series(request): + +@pytest.fixture(params=list(_cython_table)) +def cython_table_items(request): """ - Fixture for Series with low precision data types + Yields a tuple of a function and its corresponding name. Correspond to + the list of aggregator "Cython functions" used on selected table items. """ - # copy to avoid mutation, e.g. setting .name - return _narrow_series[request.param].copy() + return request.param -_index_or_series_objs = {**indices_dict, **_series, **_narrow_series} +def _get_cython_table_params(ndframe, func_names_and_expected): + """ + Combine frame, functions from SelectionMixin._cython_table + keys and expected result. + Parameters + ---------- + ndframe : DataFrame or Series + func_names_and_expected : Sequence of two items + The first item is a name of a NDFrame method ('sum', 'prod') etc. + The second item is the expected return value. -@pytest.fixture(params=_index_or_series_objs.keys()) -def index_or_series_obj(request): - """ - Fixture for tests on indexes, series and series with a narrow dtype - copy to avoid mutation, e.g. setting .name + Returns + ------- + list + List of three items (DataFrame, function, expected result) """ - return _index_or_series_objs[request.param].copy(deep=True) + results = [] + for func_name, expected in func_names_and_expected: + results.append((ndframe, func_name, expected)) + results += [ + (ndframe, func, expected) + for func, name in _cython_table + if name == func_name + ] + return results -@pytest.fixture -def multiindex_year_month_day_dataframe_random_data(): +@pytest.fixture( + params=[ + getattr(pd.offsets, o) + for o in pd.offsets.__all__ + if issubclass(getattr(pd.offsets, o), pd.offsets.Tick) + ] +) +def tick_classes(request): """ - DataFrame with 3 level MultiIndex (year, month, day) covering - first 100 business days from 2000-01-01 with random data + Fixture for Tick based datetime offsets available for a time series. """ - tdf = tm.makeTimeDataFrame(100) - ymd = tdf.groupby([lambda x: x.year, lambda x: x.month, lambda x: x.day]).sum() - # use Int64Index, to make sure things work - ymd.index.set_levels([lev.astype("i8") for lev in ymd.index.levels], inplace=True) - ymd.index.set_names(["year", "month", "day"], inplace=True) - return ymd + return request.param