diff --git a/pandas/__init__.py b/pandas/__init__.py index 10d65e41d3030..491bcb21f245d 100644 --- a/pandas/__init__.py +++ b/pandas/__init__.py @@ -297,13 +297,25 @@ def __getattr__(self, item): np = __numpy() - class __Datetime: - def __init__(self): - from datetime import datetime as dt + class __Datetime(type): - self.datetime = dt + from datetime import datetime as dt - def __getattr__(self, item): + datetime = dt + + def __getattr__(cls, item): + cls.emit_warning() + + try: + return getattr(cls.datetime, item) + except AttributeError: + raise AttributeError(f"module datetime has no attribute {item}") + + def __instancecheck__(cls, other): + return isinstance(other, cls.datetime) + + class __DatetimeSub(metaclass=__Datetime): + def emit_warning(dummy=0): import warnings warnings.warn( @@ -311,18 +323,45 @@ def __getattr__(self, item): "and will be removed from pandas in a future version. " "Import from datetime instead.", FutureWarning, - stacklevel=2, + stacklevel=3, ) - try: - return getattr(self.datetime, item) - except AttributeError: - raise AttributeError(f"module datetime has no attribute {item}") + def __new__(cls, *args, **kwargs): + cls.emit_warning() + from datetime import datetime as dt - datetime = __Datetime().datetime + return dt(*args, **kwargs) - class SparseArray: - pass + datetime = __DatetimeSub + + class __SparseArray(type): + + from pandas.core.arrays.sparse import SparseArray as sa + + SparseArray = sa + + def __instancecheck__(cls, other): + return isinstance(other, cls.SparseArray) + + class __SparseArraySub(metaclass=__SparseArray): + def emit_warning(dummy=0): + import warnings + + warnings.warn( + "The pandas.SparseArray class is deprecated " + "and will be removed from pandas in a future version. " + "Use pandas.arrays.SparseArray instead.", + FutureWarning, + stacklevel=3, + ) + + def __new__(cls, *args, **kwargs): + cls.emit_warning() + from pandas.core.arrays.sparse import SparseArray as sa + + return sa(*args, **kwargs) + + SparseArray = __SparseArraySub # module level doc-string diff --git a/pandas/tests/api/test_api.py b/pandas/tests/api/test_api.py index fb2615a7b34f3..8b897524cb053 100644 --- a/pandas/tests/api/test_api.py +++ b/pandas/tests/api/test_api.py @@ -43,7 +43,7 @@ class TestPDApi(Base): ] # these are already deprecated; awaiting removal - deprecated_modules: List[str] = [] + deprecated_modules: List[str] = ["np", "datetime"] # misc misc = ["IndexSlice", "NaT", "NA"] @@ -90,15 +90,17 @@ class TestPDApi(Base): "UInt64Dtype", "NamedAgg", ] - if not compat.PY37: - classes.extend(["Panel", "SparseSeries", "SparseDataFrame", "SparseArray"]) - deprecated_modules.extend(["np", "datetime"]) # these are already deprecated; awaiting removal deprecated_classes: List[str] = [] # these should be deprecated in the future - deprecated_classes_in_future: List[str] = [] + deprecated_classes_in_future: List[str] = ["SparseArray"] + + if not compat.PY37: + classes.extend(["Panel", "SparseSeries", "SparseDataFrame"]) + # deprecated_modules.extend(["np", "datetime"]) + # deprecated_classes_in_future.extend(["SparseArray"]) # external modules exposed in pandas namespace modules: List[str] = [] @@ -201,44 +203,46 @@ class TestPDApi(Base): def test_api(self): - self.check( - pd, + checkthese = ( self.lib + self.misc + self.modules - + self.deprecated_modules + self.classes - + self.deprecated_classes - + self.deprecated_classes_in_future + self.funcs + self.funcs_option + self.funcs_read + self.funcs_json + self.funcs_to - + self.deprecated_funcs_in_future - + self.deprecated_funcs - + self.private_modules, - self.ignored, + + self.private_modules ) + if not compat.PY37: + checkthese.extend( + self.deprecated_modules + + self.deprecated_classes + + self.deprecated_classes_in_future + + self.deprecated_funcs_in_future + + self.deprecated_funcs + ) + self.check(pd, checkthese, self.ignored) def test_depr(self): - deprecated = ( + deprecated_list = ( self.deprecated_modules + self.deprecated_classes + self.deprecated_classes_in_future + self.deprecated_funcs + self.deprecated_funcs_in_future ) - for depr in deprecated: + for depr in deprecated_list: with tm.assert_produces_warning(FutureWarning): - if compat.PY37: - getattr(pd, depr) - elif depr == "datetime": - deprecated = getattr(pd, "__Datetime") - deprecated().__getattr__(dir(pd.datetime)[-1]) - else: - deprecated = getattr(pd, depr) - deprecated.__getattr__(dir(deprecated)[-1]) + deprecated = getattr(pd, depr) + if not compat.PY37: + if depr == "datetime": + deprecated.__getattr__(dir(pd.datetime.datetime)[-1]) + elif depr == "SparseArray": + deprecated([]) + else: + deprecated.__getattr__(dir(deprecated)[-1]) def test_datetime(): @@ -249,6 +253,16 @@ def test_datetime(): warnings.simplefilter("ignore", FutureWarning) assert datetime(2015, 1, 2, 0, 0) == pd.datetime(2015, 1, 2, 0, 0) + assert isinstance(pd.datetime(2015, 1, 2, 0, 0), pd.datetime) + + +def test_sparsearray(): + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + assert isinstance(pd.array([1, 2, 3], dtype="Sparse"), pd.SparseArray) + def test_np(): import numpy as np