From ca8f96b036fdff7e5955b3341b78b07052c69be9 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Sun, 24 Feb 2019 12:02:20 +0100 Subject: [PATCH 01/18] TST: tests for maybe_promote (precursor to #23982) --- pandas/conftest.py | 65 ++- pandas/tests/dtypes/cast/test_promote.py | 624 +++++++++++++++++++++++ 2 files changed, 682 insertions(+), 7 deletions(-) create mode 100644 pandas/tests/dtypes/cast/test_promote.py diff --git a/pandas/conftest.py b/pandas/conftest.py index 35a6b5df35ddc..debc9734730f3 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -367,10 +367,15 @@ def unique_nulls_fixture(request): TIMEZONES = [None, 'UTC', 'US/Eastern', 'Asia/Tokyo', 'dateutil/US/Pacific', 'dateutil/Asia/Singapore', tzutc(), tzlocal(), FixedOffset(300), FixedOffset(0), FixedOffset(-300)] +TIMEZONE_IDS = ['None', 'UTC', 'US/Eastern', 'Asia/Tokyp', + 'dateutil/US/Pacific', 'dateutil/Asia/Singapore', + 'dateutil.tz.tzutz()', 'dateutil.tz.tzlocal()', + 'pytz.FixedOffset(300)', 'pytz.FixedOffset(0)', + 'pytz.FixedOffset(-300)'] -@td.parametrize_fixture_doc(str(TIMEZONES)) -@pytest.fixture(params=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} @@ -378,8 +383,8 @@ def tz_naive_fixture(request): return request.param -@td.parametrize_fixture_doc(str(TIMEZONES[1:])) -@pytest.fixture(params=TIMEZONES[1:]) +@td.parametrize_fixture_doc(str(TIMEZONE_IDS[1:])) +@pytest.fixture(params=TIMEZONES[1:], ids=TIMEZONE_IDS[1:]) def tz_aware_fixture(request): """ Fixture for trying explicit timezones: {0} @@ -387,8 +392,14 @@ def tz_aware_fixture(request): return request.param +# Generate cartesian product of tz_aware_fixture: +tz_aware_fixture2 = tz_aware_fixture + + # ---------------------------------------------------------------- # Dtypes + + UNSIGNED_INT_DTYPES = ["uint8", "uint16", "uint32", "uint64"] UNSIGNED_EA_INT_DTYPES = ["UInt8", "UInt16", "UInt32", "UInt64"] SIGNED_INT_DTYPES = [int, "int8", "int16", "int32", "int64"] @@ -400,8 +411,8 @@ def tz_aware_fixture(request): COMPLEX_DTYPES = [complex, "complex64", "complex128"] STRING_DTYPES = [str, 'str', 'U'] -DATETIME_DTYPES = ['datetime64[ns]', 'M8[ns]'] -TIMEDELTA_DTYPES = ['timedelta64[ns]', 'm8[ns]'] +DATETIME64_DTYPES = ['datetime64[ns]', 'M8[ns]'] +TIMEDELTA64_DTYPES = ['timedelta64[ns]', 'm8[ns]'] BOOL_DTYPES = [bool, 'bool'] BYTES_DTYPES = [bytes, 'bytes'] @@ -409,7 +420,7 @@ def tz_aware_fixture(request): ALL_REAL_DTYPES = FLOAT_DTYPES + ALL_INT_DTYPES ALL_NUMPY_DTYPES = (ALL_REAL_DTYPES + COMPLEX_DTYPES + STRING_DTYPES - + DATETIME_DTYPES + TIMEDELTA_DTYPES + BOOL_DTYPES + + DATETIME64_DTYPES + TIMEDELTA64_DTYPES + BOOL_DTYPES + OBJECT_DTYPES + BYTES_DTYPES * PY3) # bytes only for PY3 @@ -424,6 +435,46 @@ def string_dtype(request): return request.param +@pytest.fixture(params=BYTES_DTYPES) +def bytes_dtype(request): + """Parametrized fixture for bytes dtypes. + + * bytes + * 'bytes' + """ + return request.param + + +@pytest.fixture(params=OBJECT_DTYPES) +def object_dtype(request): + """Parametrized fixture for object dtypes. + + * object + * 'object' + """ + return request.param + + +@pytest.fixture(params=DATETIME64_DTYPES) +def datetime64_dtype(request): + """Parametrized fixture for datetime/timedelta dtypes. + + * 'datetime64[ns]' + * 'M8[ns]' + """ + return request.param + + +@pytest.fixture(params=TIMEDELTA64_DTYPES) +def timedelta64_dtype(request): + """Parametrized fixture for datetime/timedelta dtypes. + + * 'timedelta64[ns]' + * 'm8[ns]' + """ + return request.param + + @pytest.fixture(params=FLOAT_DTYPES) def float_dtype(request): """ diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py new file mode 100644 index 0000000000000..9af05775df898 --- /dev/null +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -0,0 +1,624 @@ +# -*- coding: utf-8 -*- + +""" +These test the method maybe_promote from core/dtypes/cast.py +""" + +import datetime + +import numpy as np +import pytest + +from pandas._libs.tslibs import NaT, iNaT +from pandas.compat import is_platform_windows + +from pandas.core.dtypes.cast import maybe_promote +from pandas.core.dtypes.common import ( + is_complex_dtype, is_datetime64_dtype, is_datetime_or_timedelta_dtype, + is_float_dtype, is_integer_dtype, is_object_dtype, is_scalar, + is_string_dtype, is_timedelta64_dtype) +from pandas.core.dtypes.dtypes import DatetimeTZDtype + +import pandas as pd + + +def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar=None, exp_val_for_array=None): + assert is_scalar(fill_value) + + if boxed: + fill_array = np.array([fill_value], dtype=box_dtype) + result_dtype, result_fill_value = maybe_promote(dtype, fill_array) + expected_fill_value = exp_val_for_array + else: + result_dtype, result_fill_value = maybe_promote(dtype, fill_value) + expected_fill_value = exp_val_for_scalar + + # try/except as numpy dtypes (i.e. if result_dtype is np.object_) do not + # know some expected dtypes like DatetimeTZDtype, and hence raise TypeError + try: + assert result_dtype == expected_dtype + except TypeError: + assert expected_dtype == result_dtype + + # for equal values, also check type (relevant e.g. for int vs float, resp. + # for different datetimes and timedeltas) + # for missing values, None == None and iNaT == iNaT, but np.nan != np.nan + assert ((result_fill_value == expected_fill_value + # disabled type check due to too many xfails; see GH 23982 / 25425 + # and type(result_fill_value) == type(expected_fill_value) + ) + or (result_fill_value is np.nan and expected_fill_value is np.nan) + or (result_fill_value is NaT and expected_fill_value is NaT)) + + +def test_maybe_promote_int_with_int(): + # placeholder due to too many xfails; see GH 23982 / 25425 + pass + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value wrapped in array with auto-dtype + # disabled due to too many xfails; see GH 23982 / 25425 + # (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_int_with_float(any_int_dtype, float_dtype, + boxed, box_dtype): + dtype = np.dtype(any_int_dtype) + fill_dtype = np.dtype(float_dtype) + + if float_dtype == 'float32' and not boxed: + pytest.xfail('falsely upcasts to float64') + + # create array of given dtype; casts "1" to correct dtype + fill_value = np.array([1], dtype=fill_dtype)[0] + + # filling int with float always upcasts to float64 + expected_dtype = np.float64 + # fill_value can be different float type + exp_val_for_scalar = np.float64(fill_value) + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value wrapped in array with auto-dtype + # disabled due to too many xfails; see GH 23982 / 25425 + # (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_float_with_int(float_dtype, any_int_dtype, + boxed, box_dtype): + + dtype = np.dtype(float_dtype) + fill_dtype = np.dtype(any_int_dtype) + + # create array of given dtype; casts "1" to correct dtype + fill_value = np.array([1], dtype=fill_dtype)[0] + + # filling float with int always keeps float dtype + # because: np.finfo('float32').max > np.iinfo('uint64').max + expected_dtype = dtype + # output is not a generic float, but corresponds to expected_dtype + exp_val_for_scalar = np.array([fill_value], dtype=expected_dtype)[0] + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +def test_maybe_promote_float_with_float(): + # placeholder due to too many xfails; see GH 23982 / 25425 + pass + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value wrapped in array with auto-dtype + (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_bool_with_any(any_numpy_dtype, boxed, box_dtype): + dtype = np.dtype(bool) + fill_dtype = np.dtype(any_numpy_dtype) + + if boxed and fill_dtype == bool: + pytest.xfail('falsely upcasts to object') + if (boxed and box_dtype is None + and is_datetime_or_timedelta_dtype(fill_dtype)): + pytest.xfail('wrongly casts fill_value') + + # create array of given dtype; casts "1" to correct dtype + fill_value = np.array([1], dtype=fill_dtype)[0] + + # filling bool with anything but bool casts to object + expected_dtype = np.dtype(object) if fill_dtype != bool else fill_dtype + exp_val_for_scalar = fill_value + exp_val_for_array = np.nan if fill_dtype != bool else None + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value wrapped in array with auto-dtype + (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_any_with_bool(any_numpy_dtype, boxed, box_dtype): + dtype = np.dtype(any_numpy_dtype) + fill_value = True + + if boxed and dtype == bool: + pytest.xfail('falsely upcasts to object') + if boxed and dtype not in (str, object) and box_dtype is None: + pytest.xfail('falsely upcasts to object') + if not boxed and is_datetime_or_timedelta_dtype(dtype): + pytest.xfail('raises error') + + # filling anything but bool with bool casts to object + expected_dtype = np.dtype(object) if dtype != bool else dtype + # output is not a generic bool, but corresponds to expected_dtype + exp_val_for_scalar = np.array([fill_value], dtype=expected_dtype)[0] + exp_val_for_array = np.nan if dtype != bool else None + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +def test_maybe_promote_bytes_with_any(): + # placeholder due to too many xfails; see GH 23982 / 25425 + pass + + +def test_maybe_promote_any_with_bytes(): + # placeholder due to too many xfails; see GH 23982 / 25425 + pass + + +def test_maybe_promote_datetime64_with_any(): + # placeholder due to too many xfails; see GH 23982 / 25425 + pass + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value array with auto-dtype + # disabled due to too many xfails; see GH 23982 / 25425 + # (True, 'dt_dtype'), # fill_value array with explicit datetime dtype + # (True, object), # fill_value array with object dtype + (False, None) # fill_value directly +]) +@pytest.mark.parametrize('fill_value', [ + pd.Timestamp('now'), np.datetime64('now'), + datetime.datetime.now(), datetime.date.today() +], ids=['pd.Timestamp', 'np.datetime64', 'datetime.datetime', 'datetime.date']) +def test_maybe_promote_any_with_datetime64(any_numpy_dtype, datetime64_dtype, + fill_value, boxed, box_dtype): + dtype = np.dtype(any_numpy_dtype) + + if is_datetime64_dtype(dtype): + if (boxed and (box_dtype == object + or (box_dtype is None + and not is_datetime64_dtype(type(fill_value))))): + pytest.xfail('falsely upcasts to object') + else: + if (boxed and (box_dtype == 'dt_dtype' + or (box_dtype is None + and is_datetime64_dtype(type(fill_value))))): + pytest.xfail('mix of lack of upcasting, resp. wrong missing value') + if not boxed and is_timedelta64_dtype(dtype): + pytest.xfail('raises error') + + # special case for box_dtype + box_dtype = (np.dtype(datetime64_dtype) if box_dtype == 'dt_dtype' + else box_dtype) + + # filling datetime with anything but datetime casts to object + if is_datetime64_dtype(dtype): + expected_dtype = dtype + # for datetime dtypes, scalar values get cast to pd.Timestamp.value + exp_val_for_scalar = pd.Timestamp(fill_value).value + exp_val_for_array = iNaT + else: + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, object), # fill_value wrapped in array with object dtype + # disabled due to too many xfails; see GH 23982 / 25425 + # (True, None), # fill_value wrapped in array with auto-dtype + # (False, None) # fill_value directly +]) +def test_maybe_promote_datetimetz_with_any_numpy_dtype( + tz_aware_fixture, any_numpy_dtype, boxed, box_dtype): + dtype = DatetimeTZDtype(tz=tz_aware_fixture) + fill_dtype = np.dtype(any_numpy_dtype) + + if box_dtype != object: + pytest.xfail('does not upcast correctly') + + # create array of given dtype; casts "1" to correct dtype + fill_value = np.array([1], dtype=fill_dtype)[0] + + # filling datetimetz with any numpy dtype casts to object + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value wrapped in array with auto-dtype + (True, object), # fill_value wrapped in array with object dtype + # disabled due to too many xfails; see GH 23982 / 25425 + # (False, None) # fill_value directly +]) +def test_maybe_promote_datetimetz_with_datetimetz(tz_aware_fixture, + tz_aware_fixture2, + boxed, box_dtype): + dtype = DatetimeTZDtype(tz=tz_aware_fixture) + fill_dtype = DatetimeTZDtype(tz=tz_aware_fixture2) + + from dateutil.tz import tzlocal + if is_platform_windows() and tz_aware_fixture2 == tzlocal(): + pytest.xfail('Cannot process fill_value with this dtype, see GH 24310') + if dtype.tz == fill_dtype.tz and boxed: + pytest.xfail('falsely upcasts') + if dtype.tz != fill_dtype.tz and not boxed: + pytest.xfail('falsely upcasts') + + # create array of given dtype; casts "1" to correct dtype + fill_value = pd.Series([10 ** 9], dtype=fill_dtype)[0] + + # filling datetimetz with datetimetz casts to object, unless tz matches + exp_val_for_scalar = fill_value + if dtype.tz == fill_dtype.tz: + expected_dtype = dtype + exp_val_for_array = NaT + else: + expected_dtype = np.dtype(object) + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('fill_value', [None, np.nan, NaT, iNaT], + ids=['None', 'np.nan', 'pd.NaT', 'iNaT']) +@pytest.mark.parametrize('boxed, box_dtype', [ + # disabled due to too many xfails; see GH 23982 / 25425 + # (True, None), # fill_value wrapped in array with auto-dtype + # (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_datetimetz_with_na(tz_aware_fixture, fill_value, + boxed, box_dtype): + + dtype = DatetimeTZDtype(tz=tz_aware_fixture) + + if (boxed and (box_dtype == object + or (box_dtype is None + and (fill_value is None or fill_value is NaT)))): + pytest.xfail('false upcasts to object') + # takes the opinion that DatetimeTZ should have single na-marker + # using iNaT would lead to errors elsewhere -> NaT + if not boxed and fill_value == iNaT: + pytest.xfail('wrong missing value marker') + + expected_dtype = dtype + # DatetimeTZDtype does not use iNaT as missing value marker + exp_val_for_scalar = NaT + exp_val_for_array = NaT + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('fill_value', [ + pd.Timestamp('now'), np.datetime64('now'), + datetime.datetime.now(), datetime.date.today() +], ids=['pd.Timestamp', 'np.datetime64', 'datetime.datetime', 'datetime.date']) +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value wrapped in array with auto-dtype + (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_any_numpy_dtype_with_datetimetz( + any_numpy_dtype, tz_aware_fixture, fill_value, boxed, box_dtype): + dtype = np.dtype(any_numpy_dtype) + fill_dtype = DatetimeTZDtype(tz=tz_aware_fixture) + + if is_datetime_or_timedelta_dtype(dtype) and not boxed: + pytest.xfail('raises error') + + fill_value = pd.Series([fill_value], dtype=fill_dtype)[0] + + # filling any numpy dtype with datetimetz casts to object + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +def test_maybe_promote_timedelta64_with_any(): + # placeholder due to too many xfails; see GH 23982 / 25425 + pass + + +@pytest.mark.parametrize('fill_value', [ + pd.Timedelta(days=1), np.timedelta64(24, 'h'), datetime.timedelta(1) +], ids=['pd.Timedelta', 'np.timedelta64', 'datetime.timedelta']) +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value array with auto-dtype + # disabled due to too many xfails; see GH 23982 / 25425 + # (True, 'td_dtype'), # fill_value array with explicit timedelta dtype + (True, object), # fill_value array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_any_with_timedelta64(any_numpy_dtype, timedelta64_dtype, + fill_value, boxed, box_dtype): + dtype = np.dtype(any_numpy_dtype) + + if is_timedelta64_dtype(dtype): + if (boxed and (box_dtype == object + or (box_dtype is None + and not is_timedelta64_dtype(type(fill_value))))): + pytest.xfail('falsely upcasts to object') + else: + if (boxed and box_dtype is None + and is_timedelta64_dtype(type(fill_value))): + pytest.xfail('does not upcast correctly') + if (not boxed and is_timedelta64_dtype(type(fill_value)) and ( + is_integer_dtype(dtype) or is_float_dtype(dtype) + or is_complex_dtype(dtype) + or issubclass(dtype.type, np.bytes_))): + pytest.xfail('does not upcast correctly') + if box_dtype == 'td_dtype': + pytest.xfail('falsely upcasts') + if not boxed and is_datetime64_dtype(dtype): + pytest.xfail('raises error') + + # special case for box_dtype + box_dtype = (np.dtype(timedelta64_dtype) if box_dtype == 'td_dtype' + else box_dtype) + + # filling anything but timedelta with timedelta casts to object + if is_timedelta64_dtype(dtype): + expected_dtype = dtype + # for timedelta dtypes, scalar values get cast to pd.Timedelta.value + exp_val_for_scalar = pd.Timedelta(fill_value).value + exp_val_for_array = iNaT + else: + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value wrapped in array with auto-dtype + (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_string_with_any(string_dtype, any_numpy_dtype, + boxed, box_dtype): + dtype = np.dtype(string_dtype) + fill_dtype = np.dtype(any_numpy_dtype) + + if (boxed and box_dtype is None + and is_datetime_or_timedelta_dtype(fill_dtype)): + pytest.xfail('wrong missing value marker') + + # create array of given dtype; casts "1" to correct dtype + fill_value = np.array([1], dtype=fill_dtype)[0] + + # filling string with anything casts to object + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('boxed, box_dtype', [ + # disabled due to too many xfails; see GH 23982 / 25425 + # (True, None), # fill_value wrapped in array with auto-dtype + # (True, 'str'), # fill_value wrapped in array with generic string-dtype + (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_any_with_string(any_numpy_dtype, string_dtype, + boxed, box_dtype): + dtype = np.dtype(any_numpy_dtype) + fill_dtype = np.dtype(string_dtype) + + if is_datetime_or_timedelta_dtype(dtype) and box_dtype != object: + pytest.xfail('does not upcast or raises') + if (boxed and box_dtype in (None, 'str') and ( + is_integer_dtype(dtype) or is_float_dtype(dtype) + or is_complex_dtype(dtype) + or issubclass(dtype.type, np.bytes_))): + pytest.xfail('does not upcast correctly') + + # create array of given dtype + fill_value = 'abc' + + # special case for box_dtype (cannot use fixture in parametrization) + box_dtype = fill_dtype if box_dtype == 'str' else box_dtype + + # filling string with anything casts to object + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value wrapped in array with auto-dtype + (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_object_with_any(object_dtype, any_numpy_dtype, + boxed, box_dtype): + dtype = np.dtype(object_dtype) + fill_dtype = np.dtype(any_numpy_dtype) + + if (boxed and box_dtype is None + and is_datetime_or_timedelta_dtype(fill_dtype)): + pytest.xfail('wrong missing value marker') + + # create array of given dtype; casts "1" to correct dtype + fill_value = np.array([1], dtype=fill_dtype)[0] + + # filling object with anything stays object + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('boxed, box_dtype', [ + (True, None), # fill_value wrapped in array with auto-dtype + (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_any_with_object(any_numpy_dtype, object_dtype, + boxed, box_dtype): + dtype = np.dtype(any_numpy_dtype) + + if not boxed and is_datetime_or_timedelta_dtype(dtype): + pytest.xfail('raises error') + + # create array of object dtype from a scalar value (i.e. passing + # dtypes.common.is_scalar), which can however not be cast to int/float etc. + fill_value = pd.DateOffset(1) + + # filling object with anything stays object + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('fill_value', [None, np.nan, NaT, iNaT], + ids=['None', 'np.nan', 'pd.NaT', 'iNaT']) +@pytest.mark.parametrize('boxed, box_dtype', [ + # disabled due to too many xfails; see GH 23982 / 25425 + # (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value directly +]) +def test_maybe_promote_any_numpy_dtype_with_na(any_numpy_dtype, fill_value, + boxed, box_dtype): + dtype = np.dtype(any_numpy_dtype) + + if (dtype == bytes and not boxed + and fill_value is not None and fill_value is not NaT): + pytest.xfail('does not upcast to object') + elif dtype == 'uint64' and not boxed and fill_value == iNaT: + pytest.xfail('does not upcast correctly') + elif is_datetime_or_timedelta_dtype(dtype) and boxed: + pytest.xfail('falsely upcasts to object') + elif (boxed and (is_integer_dtype(dtype) or is_float_dtype(dtype) + or is_complex_dtype(dtype)) + and fill_value is not NaT and dtype != 'uint64'): + pytest.xfail('falsely upcasts to object') + elif (boxed and dtype == 'uint64' + and (fill_value is np.nan or fill_value is None)): + pytest.xfail('falsely upcasts to object') + # below: opinionated that iNaT should be interpreted as missing value + elif (not boxed and (is_float_dtype(dtype) or is_complex_dtype(dtype)) + and fill_value == iNaT): + pytest.xfail('does not cast to missing value marker correctly') + elif ((is_string_dtype(dtype) or dtype == bool) + and not boxed and fill_value == iNaT): + pytest.xfail('does not cast to missing value marker correctly') + + if is_integer_dtype(dtype) and dtype == 'uint64' and fill_value == iNaT: + # uint64 + negative int casts to object; iNaT is considered as missing + expected_dtype = np.dtype(object) + exp_val_for_scalar = np.nan + elif is_integer_dtype(dtype) and fill_value == iNaT: + # other integer + iNaT casts to int64 + expected_dtype = np.int64 + exp_val_for_scalar = iNaT + elif is_integer_dtype(dtype) and fill_value is not NaT: + # integer + other missing value (np.nan / None) casts to float + expected_dtype = np.float64 + exp_val_for_scalar = np.nan + elif is_object_dtype(dtype) and (fill_value == iNaT or fill_value is NaT): + # inserting into object does not cast the value + # but *does* cast None to np.nan + expected_dtype = np.dtype(object) + exp_val_for_scalar = fill_value + elif is_datetime_or_timedelta_dtype(dtype): + # datetime / timedelta cast all missing values to iNaT + expected_dtype = dtype + exp_val_for_scalar = iNaT + elif fill_value is NaT: + # NaT upcasts everything that's not datetime/timedelta to object + expected_dtype = np.dtype(object) + exp_val_for_scalar = NaT + elif is_float_dtype(dtype) or is_complex_dtype(dtype): + # float / complex + missing value (!= NaT) stays the same + expected_dtype = dtype + exp_val_for_scalar = np.nan + else: + # all other cases cast to object, and use np.nan as missing value + expected_dtype = np.dtype(object) + exp_val_for_scalar = np.nan + + # array case has same expected_dtype; but returns corresponding na-marker + if is_integer_dtype(expected_dtype): + # integers cannot hold NaNs; maybe_promote_with_array returns None + exp_val_for_array = None + elif is_datetime_or_timedelta_dtype(expected_dtype): + exp_val_for_array = iNaT + else: # expected_dtype = float / complex / object + exp_val_for_array = np.nan + + _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, + exp_val_for_scalar, exp_val_for_array) + + +@pytest.mark.parametrize('dim', [0, 2, 3]) +def test_maybe_promote_dimensions(any_numpy_dtype, dim): + dtype = np.dtype(any_numpy_dtype) + + # create 0-dim array of given dtype; casts "1" to correct dtype + fill_array = np.array(1, dtype=dtype) + + # expand to desired dimension: + for _ in range(dim): + fill_array = np.expand_dims(fill_array, 0) + + # test against 1-dimensional case + expected_dtype, expected_missing_value = maybe_promote( + dtype, np.array([1], dtype=dtype)) + + result_dtype, result_missing_value = maybe_promote(dtype, fill_array) + + assert result_dtype == expected_dtype + # None == None, iNaT == iNaT, but np.nan != np.nan + assert ((result_missing_value == expected_missing_value) + or (result_missing_value is np.nan + and expected_missing_value is np.nan)) From e14d8a2d50c0ac213de168d2393792f51245e1fe Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Mon, 13 May 2019 19:16:05 +0200 Subject: [PATCH 02/18] Fix tabs vs. spaces --- pandas/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index eb80e081f842c..8c5a1de08b348 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -381,7 +381,7 @@ def unique_nulls_fixture(request): 'dateutil.tz.tzutz()', 'dateutil.tz.tzlocal()', 'pytz.FixedOffset(300)', 'pytz.FixedOffset(0)', 'pytz.FixedOffset(-300)', 'datetime.timezone.+1', - 'datetime.timezone.-1.named'] + 'datetime.timezone.-1.named'] @td.parametrize_fixture_doc(str(TIMEZONE_IDS)) From ab328c03bcba806b08c6185f241bd7908b46a1ba Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Tue, 14 May 2019 07:54:30 +0200 Subject: [PATCH 03/18] fix conftest --- pandas/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index 8c5a1de08b348..c71ce3489e90d 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -430,7 +430,7 @@ def tz_aware_fixture(request): ALL_REAL_DTYPES = FLOAT_DTYPES + ALL_INT_DTYPES ALL_NUMPY_DTYPES = (ALL_REAL_DTYPES + COMPLEX_DTYPES + STRING_DTYPES + - DATETIME_DTYPES + TIMEDELTA_DTYPES + BOOL_DTYPES + + DATETIME64_DTYPES + TIMEDELTA64_DTYPES + BOOL_DTYPES + OBJECT_DTYPES + BYTES_DTYPES) From 80d408164a8d062ed9f28ccc0e2e13b8de77414e Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Tue, 14 May 2019 08:20:57 +0200 Subject: [PATCH 04/18] more conftest merge conflict artefact fixes --- pandas/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index c71ce3489e90d..6b63190e86447 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -380,8 +380,8 @@ def unique_nulls_fixture(request): 'dateutil/US/Pacific', 'dateutil/Asia/Singapore', 'dateutil.tz.tzutz()', 'dateutil.tz.tzlocal()', 'pytz.FixedOffset(300)', 'pytz.FixedOffset(0)', - 'pytz.FixedOffset(-300)', 'datetime.timezone.+1', - 'datetime.timezone.-1.named'] + 'pytz.FixedOffset(-300)', 'datetime.timezone.utc', + 'datetime.timezone.+1', 'datetime.timezone.-1.named'] @td.parametrize_fixture_doc(str(TIMEZONE_IDS)) From 4a2327c16857768338f0e555be4e71204b88a18c Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Tue, 14 May 2019 09:01:23 +0200 Subject: [PATCH 05/18] lint --- pandas/tests/dtypes/cast/test_promote.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index 9af05775df898..1cdc9f2d337e7 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ These test the method maybe_promote from core/dtypes/cast.py """ From 79299aff6934be386a182424e2c568a31b68fe7b Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Thu, 30 May 2019 16:30:07 +0200 Subject: [PATCH 06/18] Review (jreback) --- pandas/tests/dtypes/cast/test_promote.py | 32 ++++++++++++++---------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index 1cdc9f2d337e7..a2cfe81b8120d 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -15,7 +15,7 @@ is_complex_dtype, is_datetime64_dtype, is_datetime_or_timedelta_dtype, is_float_dtype, is_integer_dtype, is_object_dtype, is_scalar, is_string_dtype, is_timedelta64_dtype) -from pandas.core.dtypes.dtypes import DatetimeTZDtype +from pandas.core.dtypes.dtypes import DatetimeTZDtype, PandasExtensionDtype import pandas as pd @@ -32,22 +32,28 @@ def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, result_dtype, result_fill_value = maybe_promote(dtype, fill_value) expected_fill_value = exp_val_for_scalar - # try/except as numpy dtypes (i.e. if result_dtype is np.object_) do not - # know some expected dtypes like DatetimeTZDtype, and hence raise TypeError - try: - assert result_dtype == expected_dtype - except TypeError: + if isinstance(expected_dtype, PandasExtensionDtype): + # numpy dtypes (e.g. if result_dtype is np.object_) do not know some + # expected dtypes and would raise TypeError in their __eq__-method. assert expected_dtype == result_dtype + else: + assert result_dtype == expected_dtype # for equal values, also check type (relevant e.g. for int vs float, resp. # for different datetimes and timedeltas) - # for missing values, None == None and iNaT == iNaT, but np.nan != np.nan - assert ((result_fill_value == expected_fill_value - # disabled type check due to too many xfails; see GH 23982 / 25425 - # and type(result_fill_value) == type(expected_fill_value) - ) - or (result_fill_value is np.nan and expected_fill_value is np.nan) - or (result_fill_value is NaT and expected_fill_value is NaT)) + match_value = (result_fill_value == expected_fill_value + # disabled type check due to too many xfails; GH 23982/25425 + # and type(result_fill_value) == type(expected_fill_value) + ) + + # for missing values, None == None and iNaT == iNaT (are checked through + # match_value), but np.nan != np.nan and pd.NaT != pd.NaT + match_missing = ((result_fill_value is np.nan + and expected_fill_value is np.nan) + or (result_fill_value is NaT + and expected_fill_value is NaT)) + + assert match_value or match_missing def test_maybe_promote_int_with_int(): From 1b18fde8caf6056698129651cca430e3b6252157 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Thu, 30 May 2019 16:40:17 +0200 Subject: [PATCH 07/18] Add docstring to _check_promote --- pandas/tests/dtypes/cast/test_promote.py | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index a2cfe81b8120d..50f1a5f6c44d9 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -22,13 +22,44 @@ def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, exp_val_for_scalar=None, exp_val_for_array=None): + """ + Auxiliary function to unify handling of scalar/array promotion. + + Parameters + ---------- + dtype : dtype + The value to pass on as an argument to maybe_promote. + fill_value : scalar + The value to pass on as an argument to maybe_promote, *either* as a + scalar, or boxed into an array (depending on the parameter `boxed`). + boxed : Boolean + Parameter whether fill_value should be passed to maybe_promote + directly, or wrapped in an array (of dtype box_dtype). + box_dtype : dtype + The dtype to enforce when wrapping fill_value into an array. + expected_dtype : dtype + The expected return of maybe_promote (should be the same, regardless of + whether the value was passed as scalar or in an array!). + exp_val_for_scalar : scalar + The expected value for the returned value (that has potentially been + upcast by maybe_promote). + exp_val_for_array : scalar + The expected missing value marker for the expected_dtype (which is + returned by maybe_promote when fill_value is an array). + """ assert is_scalar(fill_value) if boxed: + # here, we pass on fill_value as an array of specified box_dtype; + # the expected value returned from maybe_promote is the missing value + # marker for the returned dtype. fill_array = np.array([fill_value], dtype=box_dtype) result_dtype, result_fill_value = maybe_promote(dtype, fill_array) expected_fill_value = exp_val_for_array else: + # here, we pass on fill_value as a scalar directly; the expected value + # returned from maybe_promote is fill_value, potentially upcast to the + # returned dtype. result_dtype, result_fill_value = maybe_promote(dtype, fill_value) expected_fill_value = exp_val_for_scalar From 9fec7f92137f5c2bf5a4d990d048194b51b3526d Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 31 May 2019 12:36:54 +0200 Subject: [PATCH 08/18] doc improvements --- pandas/tests/dtypes/cast/test_promote.py | 33 ++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index 50f1a5f6c44d9..e24f6ee9ed040 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -23,36 +23,36 @@ def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, exp_val_for_scalar=None, exp_val_for_array=None): """ - Auxiliary function to unify handling of scalar/array promotion. + Auxiliary function to unify testing of scalar/array promotion. Parameters ---------- dtype : dtype - The value to pass on as an argument to maybe_promote. + The value to pass on as the first argument to maybe_promote. fill_value : scalar - The value to pass on as an argument to maybe_promote, *either* as a - scalar, or boxed into an array (depending on the parameter `boxed`). + The value to pass on as the second argument to maybe_promote, either as + a scalar, or boxed into an array (depending on the parameter `boxed`). boxed : Boolean Parameter whether fill_value should be passed to maybe_promote directly, or wrapped in an array (of dtype box_dtype). box_dtype : dtype The dtype to enforce when wrapping fill_value into an array. expected_dtype : dtype - The expected return of maybe_promote (should be the same, regardless of - whether the value was passed as scalar or in an array!). + The expected dtype returned by maybe_promote (must be the same, + regardless of whether fill_value was passed as scalar or in an array!). exp_val_for_scalar : scalar - The expected value for the returned value (that has potentially been - upcast by maybe_promote). + The expected value for the (potentially upcast) fill_value returned by + maybe_promote. exp_val_for_array : scalar The expected missing value marker for the expected_dtype (which is - returned by maybe_promote when fill_value is an array). + returned by maybe_promote when it receives an array). """ assert is_scalar(fill_value) if boxed: - # here, we pass on fill_value as an array of specified box_dtype; - # the expected value returned from maybe_promote is the missing value - # marker for the returned dtype. + # in this case, we pass on fill_value wrapped in an array of specified + # box_dtype; the expected value returned from maybe_promote is the + # missing value marker for the returned dtype. fill_array = np.array([fill_value], dtype=box_dtype) result_dtype, result_fill_value = maybe_promote(dtype, fill_array) expected_fill_value = exp_val_for_array @@ -64,8 +64,9 @@ def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, expected_fill_value = exp_val_for_scalar if isinstance(expected_dtype, PandasExtensionDtype): - # numpy dtypes (e.g. if result_dtype is np.object_) do not know some - # expected dtypes and would raise TypeError in their __eq__-method. + # switch order of equality check because numpy dtypes (e.g. if + # result_dtype is np.object_) do not know some expected dtypes (e.g. + # DatetimeTZDtype) and would raise a TypeError in their __eq__-method. assert expected_dtype == result_dtype else: assert result_dtype == expected_dtype @@ -77,8 +78,8 @@ def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, # and type(result_fill_value) == type(expected_fill_value) ) - # for missing values, None == None and iNaT == iNaT (are checked through - # match_value), but np.nan != np.nan and pd.NaT != pd.NaT + # for missing values, None == None and iNaT == iNaT (which is checked + # through match_value above), but np.nan != np.nan and pd.NaT != pd.NaT match_missing = ((result_fill_value is np.nan and expected_fill_value is np.nan) or (result_fill_value is NaT From bf8c02d2f9c46da7dade67767b2c3916ad59a781 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 31 May 2019 13:38:21 +0200 Subject: [PATCH 09/18] fix c&p artefact --- pandas/tests/dtypes/cast/test_promote.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index e24f6ee9ed040..daf56f5d29afa 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -496,7 +496,7 @@ def test_maybe_promote_any_with_string(any_numpy_dtype, string_dtype, # special case for box_dtype (cannot use fixture in parametrization) box_dtype = fill_dtype if box_dtype == 'str' else box_dtype - # filling string with anything casts to object + # filling anything with a string casts to object expected_dtype = np.dtype(object) exp_val_for_scalar = fill_value exp_val_for_array = np.nan From 595012c6d1279375b27c2162fc17688cc5856b50 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 31 May 2019 13:40:09 +0200 Subject: [PATCH 10/18] Introduce parametrized fixture for box-variants; override where required --- pandas/tests/dtypes/cast/test_promote.py | 176 ++++++++++------------- 1 file changed, 77 insertions(+), 99 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index daf56f5d29afa..5e2adb332440b 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -20,6 +20,27 @@ import pandas as pd +@pytest.fixture(params=[(True, None), (True, object), (False, None)]) +def box(request): + """ + Parametrized fixture determining whether/how to transform fill_value + + Returns + ------- + boxed : Boolean + Whether fill_value should be wrapped in an np.array + box_dtype : dtype + The dtype to pass to to np.array(fill_value, dtype=box_dtype). If None, + then this is passed on unmodified, and corresponds to the numpy default + dtype for the given fill_value. + + * (True, None), # fill_value wrapped in array with default dtype + * (True, object), # fill_value wrapped in array with object dtype + * (False, None) # fill_value passed on as scalar + """ + return request.param + + def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, exp_val_for_scalar=None, exp_val_for_array=None): """ @@ -93,16 +114,12 @@ def test_maybe_promote_int_with_int(): pass -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value wrapped in array with auto-dtype - # disabled due to too many xfails; see GH 23982 / 25425 - # (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) -def test_maybe_promote_int_with_float(any_int_dtype, float_dtype, - boxed, box_dtype): +# override parametrization due to to many xfails; see GH 23982 / 25425 +@pytest.mark.parametrize('box', [(True, None), (False, None)]) +def test_maybe_promote_int_with_float(any_int_dtype, float_dtype, box): dtype = np.dtype(any_int_dtype) fill_dtype = np.dtype(float_dtype) + boxed, box_dtype = box # read from parametrized fixture if float_dtype == 'float32' and not boxed: pytest.xfail('falsely upcasts to float64') @@ -120,17 +137,13 @@ def test_maybe_promote_int_with_float(any_int_dtype, float_dtype, exp_val_for_scalar, exp_val_for_array) -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value wrapped in array with auto-dtype - # disabled due to too many xfails; see GH 23982 / 25425 - # (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) -def test_maybe_promote_float_with_int(float_dtype, any_int_dtype, - boxed, box_dtype): +# override parametrization due to to many xfails; see GH 23982 / 25425 +@pytest.mark.parametrize('box', [(True, None), (False, None)]) +def test_maybe_promote_float_with_int(float_dtype, any_int_dtype, box): dtype = np.dtype(float_dtype) fill_dtype = np.dtype(any_int_dtype) + boxed, box_dtype = box # read from parametrized fixture # create array of given dtype; casts "1" to correct dtype fill_value = np.array([1], dtype=fill_dtype)[0] @@ -151,14 +164,10 @@ def test_maybe_promote_float_with_float(): pass -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value wrapped in array with auto-dtype - (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) -def test_maybe_promote_bool_with_any(any_numpy_dtype, boxed, box_dtype): +def test_maybe_promote_bool_with_any(any_numpy_dtype, box): dtype = np.dtype(bool) fill_dtype = np.dtype(any_numpy_dtype) + boxed, box_dtype = box # read from parametrized fixture if boxed and fill_dtype == bool: pytest.xfail('falsely upcasts to object') @@ -178,14 +187,10 @@ def test_maybe_promote_bool_with_any(any_numpy_dtype, boxed, box_dtype): exp_val_for_scalar, exp_val_for_array) -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value wrapped in array with auto-dtype - (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) -def test_maybe_promote_any_with_bool(any_numpy_dtype, boxed, box_dtype): +def test_maybe_promote_any_with_bool(any_numpy_dtype, box): dtype = np.dtype(any_numpy_dtype) fill_value = True + boxed, box_dtype = box # read from parametrized fixture if boxed and dtype == bool: pytest.xfail('falsely upcasts to object') @@ -219,20 +224,22 @@ def test_maybe_promote_datetime64_with_any(): pass -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value array with auto-dtype +# override parametrization of box to add special case for dt_dtype +@pytest.mark.parametrize('box', [ + (True, None), # fill_value array with default dtype # disabled due to too many xfails; see GH 23982 / 25425 # (True, 'dt_dtype'), # fill_value array with explicit datetime dtype # (True, object), # fill_value array with object dtype - (False, None) # fill_value directly + (False, None) # fill_value passed as scalar ]) @pytest.mark.parametrize('fill_value', [ pd.Timestamp('now'), np.datetime64('now'), datetime.datetime.now(), datetime.date.today() ], ids=['pd.Timestamp', 'np.datetime64', 'datetime.datetime', 'datetime.date']) def test_maybe_promote_any_with_datetime64(any_numpy_dtype, datetime64_dtype, - fill_value, boxed, box_dtype): + fill_value, box): dtype = np.dtype(any_numpy_dtype) + boxed, box_dtype = box # read from parametrized fixture if is_datetime64_dtype(dtype): if (boxed and (box_dtype == object @@ -266,16 +273,13 @@ def test_maybe_promote_any_with_datetime64(any_numpy_dtype, datetime64_dtype, exp_val_for_scalar, exp_val_for_array) -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, object), # fill_value wrapped in array with object dtype - # disabled due to too many xfails; see GH 23982 / 25425 - # (True, None), # fill_value wrapped in array with auto-dtype - # (False, None) # fill_value directly -]) -def test_maybe_promote_datetimetz_with_any_numpy_dtype( - tz_aware_fixture, any_numpy_dtype, boxed, box_dtype): +# override parametrization due to to many xfails; see GH 23982 / 25425 +@pytest.mark.parametrize('box', [(True, object)]) +def test_maybe_promote_datetimetz_with_any_numpy_dtype(tz_aware_fixture, + any_numpy_dtype, box): dtype = DatetimeTZDtype(tz=tz_aware_fixture) fill_dtype = np.dtype(any_numpy_dtype) + boxed, box_dtype = box # read from parametrized fixture if box_dtype != object: pytest.xfail('does not upcast correctly') @@ -292,17 +296,13 @@ def test_maybe_promote_datetimetz_with_any_numpy_dtype( exp_val_for_scalar, exp_val_for_array) -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value wrapped in array with auto-dtype - (True, object), # fill_value wrapped in array with object dtype - # disabled due to too many xfails; see GH 23982 / 25425 - # (False, None) # fill_value directly -]) +# override parametrization due to to many xfails; see GH 23982 / 25425 +@pytest.mark.parametrize('box', [(True, None), (True, object)]) def test_maybe_promote_datetimetz_with_datetimetz(tz_aware_fixture, - tz_aware_fixture2, - boxed, box_dtype): + tz_aware_fixture2, box): dtype = DatetimeTZDtype(tz=tz_aware_fixture) fill_dtype = DatetimeTZDtype(tz=tz_aware_fixture2) + boxed, box_dtype = box # read from parametrized fixture from dateutil.tz import tzlocal if is_platform_windows() and tz_aware_fixture2 == tzlocal(): @@ -330,16 +330,12 @@ def test_maybe_promote_datetimetz_with_datetimetz(tz_aware_fixture, @pytest.mark.parametrize('fill_value', [None, np.nan, NaT, iNaT], ids=['None', 'np.nan', 'pd.NaT', 'iNaT']) -@pytest.mark.parametrize('boxed, box_dtype', [ - # disabled due to too many xfails; see GH 23982 / 25425 - # (True, None), # fill_value wrapped in array with auto-dtype - # (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) -def test_maybe_promote_datetimetz_with_na(tz_aware_fixture, fill_value, - boxed, box_dtype): +# override parametrization due to to many xfails; see GH 23982 / 25425 +@pytest.mark.parametrize('box', [(False, None)]) +def test_maybe_promote_datetimetz_with_na(tz_aware_fixture, fill_value, box): dtype = DatetimeTZDtype(tz=tz_aware_fixture) + boxed, box_dtype = box # read from parametrized fixture if (boxed and (box_dtype == object or (box_dtype is None @@ -363,15 +359,11 @@ def test_maybe_promote_datetimetz_with_na(tz_aware_fixture, fill_value, pd.Timestamp('now'), np.datetime64('now'), datetime.datetime.now(), datetime.date.today() ], ids=['pd.Timestamp', 'np.datetime64', 'datetime.datetime', 'datetime.date']) -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value wrapped in array with auto-dtype - (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) def test_maybe_promote_any_numpy_dtype_with_datetimetz( - any_numpy_dtype, tz_aware_fixture, fill_value, boxed, box_dtype): + any_numpy_dtype, tz_aware_fixture, fill_value, box): dtype = np.dtype(any_numpy_dtype) fill_dtype = DatetimeTZDtype(tz=tz_aware_fixture) + boxed, box_dtype = box # read from parametrized fixture if is_datetime_or_timedelta_dtype(dtype) and not boxed: pytest.xfail('raises error') @@ -395,16 +387,18 @@ def test_maybe_promote_timedelta64_with_any(): @pytest.mark.parametrize('fill_value', [ pd.Timedelta(days=1), np.timedelta64(24, 'h'), datetime.timedelta(1) ], ids=['pd.Timedelta', 'np.timedelta64', 'datetime.timedelta']) -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value array with auto-dtype +# override parametrization of box to add special case for td_dtype +@pytest.mark.parametrize('box', [ + (True, None), # fill_value array with default dtype # disabled due to too many xfails; see GH 23982 / 25425 # (True, 'td_dtype'), # fill_value array with explicit timedelta dtype (True, object), # fill_value array with object dtype - (False, None) # fill_value directly + (False, None) # fill_value passed as scalar ]) def test_maybe_promote_any_with_timedelta64(any_numpy_dtype, timedelta64_dtype, - fill_value, boxed, box_dtype): + fill_value, box): dtype = np.dtype(any_numpy_dtype) + boxed, box_dtype = box # read from parametrized fixture if is_timedelta64_dtype(dtype): if (boxed and (box_dtype == object @@ -444,15 +438,10 @@ def test_maybe_promote_any_with_timedelta64(any_numpy_dtype, timedelta64_dtype, exp_val_for_scalar, exp_val_for_array) -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value wrapped in array with auto-dtype - (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) -def test_maybe_promote_string_with_any(string_dtype, any_numpy_dtype, - boxed, box_dtype): +def test_maybe_promote_string_with_any(string_dtype, any_numpy_dtype, box): dtype = np.dtype(string_dtype) fill_dtype = np.dtype(any_numpy_dtype) + boxed, box_dtype = box # read from parametrized fixture if (boxed and box_dtype is None and is_datetime_or_timedelta_dtype(fill_dtype)): @@ -470,17 +459,18 @@ def test_maybe_promote_string_with_any(string_dtype, any_numpy_dtype, exp_val_for_scalar, exp_val_for_array) -@pytest.mark.parametrize('boxed, box_dtype', [ +# override parametrization of box to add special case for str +@pytest.mark.parametrize('box', [ # disabled due to too many xfails; see GH 23982 / 25425 - # (True, None), # fill_value wrapped in array with auto-dtype + # (True, None), # fill_value wrapped in array with default dtype # (True, 'str'), # fill_value wrapped in array with generic string-dtype (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly + (False, None) # fill_value passed as scalar ]) -def test_maybe_promote_any_with_string(any_numpy_dtype, string_dtype, - boxed, box_dtype): +def test_maybe_promote_any_with_string(any_numpy_dtype, string_dtype, box): dtype = np.dtype(any_numpy_dtype) fill_dtype = np.dtype(string_dtype) + boxed, box_dtype = box # read from parametrized fixture if is_datetime_or_timedelta_dtype(dtype) and box_dtype != object: pytest.xfail('does not upcast or raises') @@ -505,15 +495,10 @@ def test_maybe_promote_any_with_string(any_numpy_dtype, string_dtype, exp_val_for_scalar, exp_val_for_array) -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value wrapped in array with auto-dtype - (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) -def test_maybe_promote_object_with_any(object_dtype, any_numpy_dtype, - boxed, box_dtype): +def test_maybe_promote_object_with_any(object_dtype, any_numpy_dtype, box): dtype = np.dtype(object_dtype) fill_dtype = np.dtype(any_numpy_dtype) + boxed, box_dtype = box # read from parametrized fixture if (boxed and box_dtype is None and is_datetime_or_timedelta_dtype(fill_dtype)): @@ -531,14 +516,9 @@ def test_maybe_promote_object_with_any(object_dtype, any_numpy_dtype, exp_val_for_scalar, exp_val_for_array) -@pytest.mark.parametrize('boxed, box_dtype', [ - (True, None), # fill_value wrapped in array with auto-dtype - (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) -def test_maybe_promote_any_with_object(any_numpy_dtype, object_dtype, - boxed, box_dtype): +def test_maybe_promote_any_with_object(any_numpy_dtype, object_dtype, box): dtype = np.dtype(any_numpy_dtype) + boxed, box_dtype = box # read from parametrized fixture if not boxed and is_datetime_or_timedelta_dtype(dtype): pytest.xfail('raises error') @@ -558,14 +538,12 @@ def test_maybe_promote_any_with_object(any_numpy_dtype, object_dtype, @pytest.mark.parametrize('fill_value', [None, np.nan, NaT, iNaT], ids=['None', 'np.nan', 'pd.NaT', 'iNaT']) -@pytest.mark.parametrize('boxed, box_dtype', [ - # disabled due to too many xfails; see GH 23982 / 25425 - # (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value directly -]) -def test_maybe_promote_any_numpy_dtype_with_na(any_numpy_dtype, fill_value, - boxed, box_dtype): +# override parametrization due to to many xfails; see GH 23982 / 25425 +@pytest.mark.parametrize('box', [(False, None)]) +def test_maybe_promote_any_numpy_dtype_with_na(any_numpy_dtype, + fill_value, box): dtype = np.dtype(any_numpy_dtype) + boxed, box_dtype = box # read from parametrized fixture if (dtype == bytes and not boxed and fill_value is not None and fill_value is not NaT): From f89e8b4dbada249910d343e1c0d2f962352270af Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 31 May 2019 13:57:13 +0200 Subject: [PATCH 11/18] improve docstring for box-fixture --- pandas/tests/dtypes/cast/test_promote.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index 5e2adb332440b..ce9e8b630c261 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -23,20 +23,23 @@ @pytest.fixture(params=[(True, None), (True, object), (False, None)]) def box(request): """ - Parametrized fixture determining whether/how to transform fill_value + Parametrized fixture determining whether/how to transform fill_value. + + Since fill_value is defined on a per-test basis, the actual transformation + (based on this fixture) is executed in _check_promote. Returns ------- boxed : Boolean - Whether fill_value should be wrapped in an np.array + Whether fill_value should be wrapped in an np.array. box_dtype : dtype - The dtype to pass to to np.array(fill_value, dtype=box_dtype). If None, + The dtype to pass to np.array(fill_value, dtype=box_dtype). If None, then this is passed on unmodified, and corresponds to the numpy default dtype for the given fill_value. - * (True, None), # fill_value wrapped in array with default dtype - * (True, object), # fill_value wrapped in array with object dtype - * (False, None) # fill_value passed on as scalar + * (True, None) # fill_value wrapped in array with default dtype + * (True, object) # fill_value wrapped in array with object dtype + * (False, None) # fill_value passed on as scalar """ return request.param From 4317f77ac6d30bc389eee1c0cfb5acc3e5e462d0 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 31 May 2019 15:03:01 +0200 Subject: [PATCH 12/18] fix comments --- pandas/tests/dtypes/cast/test_promote.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index ce9e8b630c261..59a5ad7766d33 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -229,11 +229,11 @@ def test_maybe_promote_datetime64_with_any(): # override parametrization of box to add special case for dt_dtype @pytest.mark.parametrize('box', [ - (True, None), # fill_value array with default dtype + (True, None), # fill_value wrapped in array with default dtype # disabled due to too many xfails; see GH 23982 / 25425 - # (True, 'dt_dtype'), # fill_value array with explicit datetime dtype - # (True, object), # fill_value array with object dtype - (False, None) # fill_value passed as scalar + # (True, 'dt_dtype'), # fill_value in array with explicit datetime dtype + # (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value passed on as scalar ]) @pytest.mark.parametrize('fill_value', [ pd.Timestamp('now'), np.datetime64('now'), @@ -392,11 +392,11 @@ def test_maybe_promote_timedelta64_with_any(): ], ids=['pd.Timedelta', 'np.timedelta64', 'datetime.timedelta']) # override parametrization of box to add special case for td_dtype @pytest.mark.parametrize('box', [ - (True, None), # fill_value array with default dtype + (True, None), # fill_value wrapped in array with default dtype # disabled due to too many xfails; see GH 23982 / 25425 - # (True, 'td_dtype'), # fill_value array with explicit timedelta dtype - (True, object), # fill_value array with object dtype - (False, None) # fill_value passed as scalar + # (True, 'td_dtype'), # fill_value in array with explicit timedelta dtype + (True, object), # fill_value wrapped in array with object dtype + (False, None) # fill_value passed on as scalar ]) def test_maybe_promote_any_with_timedelta64(any_numpy_dtype, timedelta64_dtype, fill_value, box): @@ -468,7 +468,7 @@ def test_maybe_promote_string_with_any(string_dtype, any_numpy_dtype, box): # (True, None), # fill_value wrapped in array with default dtype # (True, 'str'), # fill_value wrapped in array with generic string-dtype (True, object), # fill_value wrapped in array with object dtype - (False, None) # fill_value passed as scalar + (False, None) # fill_value passed on as scalar ]) def test_maybe_promote_any_with_string(any_numpy_dtype, string_dtype, box): dtype = np.dtype(any_numpy_dtype) From c0b4a3f04c20daf18d70621e8671822909737ef9 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 31 May 2019 18:10:58 +0200 Subject: [PATCH 13/18] fix fixture docstrings --- pandas/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index 6b63190e86447..42614d48bb9f9 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -408,7 +408,7 @@ def tz_aware_fixture(request): # ---------------------------------------------------------------- # Dtypes - +# ---------------------------------------------------------------- UNSIGNED_INT_DTYPES = ["uint8", "uint16", "uint32", "uint64"] UNSIGNED_EA_INT_DTYPES = ["UInt8", "UInt16", "UInt32", "UInt64"] @@ -467,7 +467,7 @@ def object_dtype(request): @pytest.fixture(params=DATETIME64_DTYPES) def datetime64_dtype(request): - """Parametrized fixture for datetime/timedelta dtypes. + """Parametrized fixture for datetime64 dtypes. * 'datetime64[ns]' * 'M8[ns]' @@ -477,7 +477,7 @@ def datetime64_dtype(request): @pytest.fixture(params=TIMEDELTA64_DTYPES) def timedelta64_dtype(request): - """Parametrized fixture for datetime/timedelta dtypes. + """Parametrized fixture for timedelta64 dtypes. * 'timedelta64[ns]' * 'm8[ns]' From d6001a7de89950a562fd75b20baa47c04b8509b5 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 31 May 2019 18:23:31 +0200 Subject: [PATCH 14/18] docstring updates --- pandas/tests/dtypes/cast/test_promote.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index 59a5ad7766d33..261c6923881cb 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -33,7 +33,7 @@ def box(request): boxed : Boolean Whether fill_value should be wrapped in an np.array. box_dtype : dtype - The dtype to pass to np.array(fill_value, dtype=box_dtype). If None, + The dtype to pass to np.array([fill_value], dtype=box_dtype). If None, then this is passed on unmodified, and corresponds to the numpy default dtype for the given fill_value. @@ -60,9 +60,9 @@ def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, Parameter whether fill_value should be passed to maybe_promote directly, or wrapped in an array (of dtype box_dtype). box_dtype : dtype - The dtype to enforce when wrapping fill_value into an array. + The dtype to enforce when wrapping fill_value into an np.array. expected_dtype : dtype - The expected dtype returned by maybe_promote (must be the same, + The expected dtype returned by maybe_promote (by design this is the same regardless of whether fill_value was passed as scalar or in an array!). exp_val_for_scalar : scalar The expected value for the (potentially upcast) fill_value returned by From f73740b7e96318eb8f8d3d88e0c2f76b851da06e Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Sat, 1 Jun 2019 11:14:26 +0200 Subject: [PATCH 15/18] add ids for box-fixture --- pandas/tests/dtypes/cast/test_promote.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index 261c6923881cb..47608ed7f2867 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -20,7 +20,8 @@ import pandas as pd -@pytest.fixture(params=[(True, None), (True, object), (False, None)]) +@pytest.fixture(params=[(True, None), (True, object), (False, None)], + ids=['True-None', 'True-object', 'False-None']) def box(request): """ Parametrized fixture determining whether/how to transform fill_value. From 200365ef06f787a05d5d09e51ebfbe9fd34fb66c Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Sat, 1 Jun 2019 12:00:11 +0200 Subject: [PATCH 16/18] lint --- pandas/tests/dtypes/cast/test_promote.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index 47608ed7f2867..aaf32a9da7a11 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -63,8 +63,9 @@ def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, box_dtype : dtype The dtype to enforce when wrapping fill_value into an np.array. expected_dtype : dtype - The expected dtype returned by maybe_promote (by design this is the same - regardless of whether fill_value was passed as scalar or in an array!). + The expected dtype returned by maybe_promote (by design this is the + same regardless of whether fill_value was passed as a scalar or in an + array!). exp_val_for_scalar : scalar The expected value for the (potentially upcast) fill_value returned by maybe_promote. From a97d29b96c335c8cb3b051ade54b6dafbd1dbfbb Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Thu, 6 Jun 2019 00:26:12 +0200 Subject: [PATCH 17/18] break out function to make _check_promote even more branchless --- pandas/tests/dtypes/cast/test_promote.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index aaf32a9da7a11..82726b726eef5 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -45,6 +45,19 @@ def box(request): return request.param +def _safe_dtype_assert(left_dtype, right_dtype): + """ + Compare two dtypes without raising TypeError. + """ + if isinstance(right_dtype, PandasExtensionDtype): + # switch order of equality check because numpy dtypes (e.g. if + # left_dtype is np.object_) do not know some expected dtypes (e.g. + # DatetimeTZDtype) and would raise a TypeError in their __eq__-method. + assert right_dtype == left_dtype + else: + assert left_dtype == right_dtype + + def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, exp_val_for_scalar=None, exp_val_for_array=None): """ @@ -89,13 +102,7 @@ def _check_promote(dtype, fill_value, boxed, box_dtype, expected_dtype, result_dtype, result_fill_value = maybe_promote(dtype, fill_value) expected_fill_value = exp_val_for_scalar - if isinstance(expected_dtype, PandasExtensionDtype): - # switch order of equality check because numpy dtypes (e.g. if - # result_dtype is np.object_) do not know some expected dtypes (e.g. - # DatetimeTZDtype) and would raise a TypeError in their __eq__-method. - assert expected_dtype == result_dtype - else: - assert result_dtype == expected_dtype + _safe_dtype_assert(result_dtype, expected_dtype) # for equal values, also check type (relevant e.g. for int vs float, resp. # for different datetimes and timedeltas) From a1add658bc775083b2ef1bfea001410db31d05dc Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Mon, 10 Jun 2019 14:52:13 +0200 Subject: [PATCH 18/18] Refactor with reduced any_numpy_dtype fixture (review jreback) --- pandas/tests/dtypes/cast/test_promote.py | 81 ++++++++++++++++-------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/pandas/tests/dtypes/cast/test_promote.py b/pandas/tests/dtypes/cast/test_promote.py index 82726b726eef5..5a5b5d47b3ccc 100644 --- a/pandas/tests/dtypes/cast/test_promote.py +++ b/pandas/tests/dtypes/cast/test_promote.py @@ -20,6 +20,29 @@ import pandas as pd +@pytest.fixture(params=[bool, 'uint8', 'int32', 'uint64', 'float32', 'float64', + 'complex64', 'complex128', 'M8[ns]', 'm8[ns]', str, + bytes, object]) +def any_numpy_dtype_reduced(request): + """ + Parameterized fixture for numpy dtypes, reduced from any_numpy_dtype. + + * bool + * 'int32' + * 'uint64' + * 'float32' + * 'float64' + * 'complex64' + * 'complex128' + * 'M8[ns]' + * 'M8[ns]' + * str + * bytes + * object + """ + return request.param + + @pytest.fixture(params=[(True, None), (True, object), (False, None)], ids=['True-None', 'True-object', 'False-None']) def box(request): @@ -176,9 +199,9 @@ def test_maybe_promote_float_with_float(): pass -def test_maybe_promote_bool_with_any(any_numpy_dtype, box): +def test_maybe_promote_bool_with_any(any_numpy_dtype_reduced, box): dtype = np.dtype(bool) - fill_dtype = np.dtype(any_numpy_dtype) + fill_dtype = np.dtype(any_numpy_dtype_reduced) boxed, box_dtype = box # read from parametrized fixture if boxed and fill_dtype == bool: @@ -199,8 +222,8 @@ def test_maybe_promote_bool_with_any(any_numpy_dtype, box): exp_val_for_scalar, exp_val_for_array) -def test_maybe_promote_any_with_bool(any_numpy_dtype, box): - dtype = np.dtype(any_numpy_dtype) +def test_maybe_promote_any_with_bool(any_numpy_dtype_reduced, box): + dtype = np.dtype(any_numpy_dtype_reduced) fill_value = True boxed, box_dtype = box # read from parametrized fixture @@ -248,9 +271,9 @@ def test_maybe_promote_datetime64_with_any(): pd.Timestamp('now'), np.datetime64('now'), datetime.datetime.now(), datetime.date.today() ], ids=['pd.Timestamp', 'np.datetime64', 'datetime.datetime', 'datetime.date']) -def test_maybe_promote_any_with_datetime64(any_numpy_dtype, datetime64_dtype, - fill_value, box): - dtype = np.dtype(any_numpy_dtype) +def test_maybe_promote_any_with_datetime64(any_numpy_dtype_reduced, + datetime64_dtype, fill_value, box): + dtype = np.dtype(any_numpy_dtype_reduced) boxed, box_dtype = box # read from parametrized fixture if is_datetime64_dtype(dtype): @@ -287,10 +310,10 @@ def test_maybe_promote_any_with_datetime64(any_numpy_dtype, datetime64_dtype, # override parametrization due to to many xfails; see GH 23982 / 25425 @pytest.mark.parametrize('box', [(True, object)]) -def test_maybe_promote_datetimetz_with_any_numpy_dtype(tz_aware_fixture, - any_numpy_dtype, box): +def test_maybe_promote_datetimetz_with_any_numpy_dtype( + tz_aware_fixture, any_numpy_dtype_reduced, box): dtype = DatetimeTZDtype(tz=tz_aware_fixture) - fill_dtype = np.dtype(any_numpy_dtype) + fill_dtype = np.dtype(any_numpy_dtype_reduced) boxed, box_dtype = box # read from parametrized fixture if box_dtype != object: @@ -372,8 +395,8 @@ def test_maybe_promote_datetimetz_with_na(tz_aware_fixture, fill_value, box): datetime.datetime.now(), datetime.date.today() ], ids=['pd.Timestamp', 'np.datetime64', 'datetime.datetime', 'datetime.date']) def test_maybe_promote_any_numpy_dtype_with_datetimetz( - any_numpy_dtype, tz_aware_fixture, fill_value, box): - dtype = np.dtype(any_numpy_dtype) + any_numpy_dtype_reduced, tz_aware_fixture, fill_value, box): + dtype = np.dtype(any_numpy_dtype_reduced) fill_dtype = DatetimeTZDtype(tz=tz_aware_fixture) boxed, box_dtype = box # read from parametrized fixture @@ -407,9 +430,9 @@ def test_maybe_promote_timedelta64_with_any(): (True, object), # fill_value wrapped in array with object dtype (False, None) # fill_value passed on as scalar ]) -def test_maybe_promote_any_with_timedelta64(any_numpy_dtype, timedelta64_dtype, - fill_value, box): - dtype = np.dtype(any_numpy_dtype) +def test_maybe_promote_any_with_timedelta64( + any_numpy_dtype_reduced, timedelta64_dtype, fill_value, box): + dtype = np.dtype(any_numpy_dtype_reduced) boxed, box_dtype = box # read from parametrized fixture if is_timedelta64_dtype(dtype): @@ -450,9 +473,10 @@ def test_maybe_promote_any_with_timedelta64(any_numpy_dtype, timedelta64_dtype, exp_val_for_scalar, exp_val_for_array) -def test_maybe_promote_string_with_any(string_dtype, any_numpy_dtype, box): +def test_maybe_promote_string_with_any(string_dtype, + any_numpy_dtype_reduced, box): dtype = np.dtype(string_dtype) - fill_dtype = np.dtype(any_numpy_dtype) + fill_dtype = np.dtype(any_numpy_dtype_reduced) boxed, box_dtype = box # read from parametrized fixture if (boxed and box_dtype is None @@ -479,8 +503,9 @@ def test_maybe_promote_string_with_any(string_dtype, any_numpy_dtype, box): (True, object), # fill_value wrapped in array with object dtype (False, None) # fill_value passed on as scalar ]) -def test_maybe_promote_any_with_string(any_numpy_dtype, string_dtype, box): - dtype = np.dtype(any_numpy_dtype) +def test_maybe_promote_any_with_string(any_numpy_dtype_reduced, + string_dtype, box): + dtype = np.dtype(any_numpy_dtype_reduced) fill_dtype = np.dtype(string_dtype) boxed, box_dtype = box # read from parametrized fixture @@ -507,9 +532,10 @@ def test_maybe_promote_any_with_string(any_numpy_dtype, string_dtype, box): exp_val_for_scalar, exp_val_for_array) -def test_maybe_promote_object_with_any(object_dtype, any_numpy_dtype, box): +def test_maybe_promote_object_with_any(object_dtype, + any_numpy_dtype_reduced, box): dtype = np.dtype(object_dtype) - fill_dtype = np.dtype(any_numpy_dtype) + fill_dtype = np.dtype(any_numpy_dtype_reduced) boxed, box_dtype = box # read from parametrized fixture if (boxed and box_dtype is None @@ -528,8 +554,9 @@ def test_maybe_promote_object_with_any(object_dtype, any_numpy_dtype, box): exp_val_for_scalar, exp_val_for_array) -def test_maybe_promote_any_with_object(any_numpy_dtype, object_dtype, box): - dtype = np.dtype(any_numpy_dtype) +def test_maybe_promote_any_with_object(any_numpy_dtype_reduced, + object_dtype, box): + dtype = np.dtype(any_numpy_dtype_reduced) boxed, box_dtype = box # read from parametrized fixture if not boxed and is_datetime_or_timedelta_dtype(dtype): @@ -552,9 +579,9 @@ def test_maybe_promote_any_with_object(any_numpy_dtype, object_dtype, box): ids=['None', 'np.nan', 'pd.NaT', 'iNaT']) # override parametrization due to to many xfails; see GH 23982 / 25425 @pytest.mark.parametrize('box', [(False, None)]) -def test_maybe_promote_any_numpy_dtype_with_na(any_numpy_dtype, +def test_maybe_promote_any_numpy_dtype_with_na(any_numpy_dtype_reduced, fill_value, box): - dtype = np.dtype(any_numpy_dtype) + dtype = np.dtype(any_numpy_dtype_reduced) boxed, box_dtype = box # read from parametrized fixture if (dtype == bytes and not boxed @@ -627,8 +654,8 @@ def test_maybe_promote_any_numpy_dtype_with_na(any_numpy_dtype, @pytest.mark.parametrize('dim', [0, 2, 3]) -def test_maybe_promote_dimensions(any_numpy_dtype, dim): - dtype = np.dtype(any_numpy_dtype) +def test_maybe_promote_dimensions(any_numpy_dtype_reduced, dim): + dtype = np.dtype(any_numpy_dtype_reduced) # create 0-dim array of given dtype; casts "1" to correct dtype fill_array = np.array(1, dtype=dtype)