Skip to content

Commit 2bb887d

Browse files
committed
DEPR: Deprecate pandas.core.datetools
Closes gh-14094.
1 parent 2bebc18 commit 2bb887d

File tree

5 files changed

+115
-6
lines changed

5 files changed

+115
-6
lines changed

doc/source/whatsnew/v0.19.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,7 @@ Deprecations
12311231

12321232
- ``PeriodIndex.to_datetime`` has been deprecated in favour of ``PeriodIndex.to_timestamp`` (:issue:`8254`)
12331233
- ``Timestamp.to_datetime`` has been deprecated in favour of ``Timestamp.to_pydatetime`` (:issue:`8254`)
1234+
- ``pandas.core.datetools`` module has been deprecated and will be removed in a subsequent release (:issue:`14094`)
12341235
- ``Index.to_datetime`` and ``DatetimeIndex.to_datetime`` have been deprecated in favour of ``pd.to_datetime`` (:issue:`8254`)
12351236
- ``SparseList`` has been deprecated and will be removed in a future version (:issue:`13784`)
12361237
- ``DataFrame.to_html()`` and ``DataFrame.to_latex()`` have dropped the ``colSpace`` parameter in favor of ``col_space`` (:issue:`13857`)

pandas/api/tests/test_api.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class TestPDApi(Base, tm.TestCase):
4242
'json', 'lib', 'index', 'parser']
4343

4444
# these are already deprecated; awaiting removal
45-
deprecated_modules = ['ols', 'stats']
45+
deprecated_modules = ['ols', 'stats', 'datetools']
4646

4747
# misc
4848
misc = ['IndexSlice', 'NaT']
@@ -61,14 +61,14 @@ class TestPDApi(Base, tm.TestCase):
6161
'SparseTimeSeries', 'Panel4D',
6262
'SparseList']
6363

64-
# these should be deperecated in the future
64+
# these should be deprecated in the future
6565
deprecated_classes_in_future = ['Term', 'Panel']
6666

6767
# these should be removed from top-level namespace
6868
remove_classes_from_top_level_namespace = ['Expr']
6969

7070
# external modules exposed in pandas namespace
71-
modules = ['np', 'datetime', 'datetools']
71+
modules = ['np', 'datetime']
7272

7373
# top-level functions
7474
funcs = ['bdate_range', 'concat', 'crosstab', 'cut',
@@ -99,7 +99,7 @@ class TestPDApi(Base, tm.TestCase):
9999
funcs_to = ['to_datetime', 'to_msgpack',
100100
'to_numeric', 'to_pickle', 'to_timedelta']
101101

102-
# these should be deperecated in the future
102+
# these should be deprecated in the future
103103
deprecated_funcs_in_future = ['pnow', 'groupby', 'info']
104104

105105
# these are already deprecated; awaiting removal
@@ -208,6 +208,19 @@ def test_removed_from_core_common(self):
208208
'ensure_float']:
209209
self.assertRaises(AttributeError, lambda: getattr(com, t))
210210

211+
212+
class TestDatetools(tm.TestCase):
213+
214+
def test_deprecation_access_func(self):
215+
with tm.assert_produces_warning(FutureWarning,
216+
check_stacklevel=False):
217+
pd.datetools.to_datetime('2016-01-01')
218+
219+
def test_deprecation_access_obj(self):
220+
with tm.assert_produces_warning(FutureWarning,
221+
check_stacklevel=False):
222+
pd.datetools.monthEnd
223+
211224
if __name__ == '__main__':
212225
import nose
213226
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],

pandas/core/api.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,18 @@
2828
from pandas.tseries.tdi import TimedeltaIndex, Timedelta
2929
from pandas.tseries.period import Period, PeriodIndex
3030

31-
# legacy
32-
import pandas.core.datetools as datetools
31+
# see gh-14094.
32+
from pandas.util.depr_module import _DeprecatedModule
33+
34+
_alts = ['pandas.tseries.tools', 'pandas.tseries.offsets',
35+
'pandas.tseries.frequencies']
36+
_removals = ['day', 'bday', 'businessDay', 'cday', 'customBusinessDay',
37+
'customBusinessMonthEnd', 'customBusinessMonthBegin',
38+
'monthEnd', 'yearEnd', 'yearBegin', 'bmonthEnd', 'bmonthBegin',
39+
'cbmonthEnd', 'cbmonthBegin', 'bquarterEnd', 'quarterEnd',
40+
'byearEnd', 'week']
41+
datetools = _DeprecatedModule(deprmod='pandas.core.datetools', alts=_alts,
42+
removals=_removals)
3343

3444
from pandas.core.config import (get_option, set_option, reset_option,
3545
describe_option, option_context, options)

pandas/core/datetools.py

+6
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22

33
# flake8: noqa
44

5+
import warnings
6+
57
from pandas.tseries.tools import *
68
from pandas.tseries.offsets import *
79
from pandas.tseries.frequencies import *
810

11+
warnings.warn("The pandas.core.datetools module is deprecated and will be "
12+
"removed in a future version. Please use the pandas.tseries "
13+
"module instead.", FutureWarning, stacklevel=2)
14+
915
day = DateOffset()
1016
bday = BDay()
1117
businessDay = bday

pandas/util/depr_module.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""
2+
This module houses a utility class for mocking deprecated modules.
3+
It is for internal use only and should not be used beyond this purpose.
4+
"""
5+
6+
import warnings
7+
import importlib
8+
9+
10+
class _DeprecatedModule(object):
11+
""" Class for mocking deprecated modules.
12+
13+
Parameters
14+
----------
15+
deprmod : name of module to be deprecated.
16+
alts : alternative modules to be used to access objects or methods
17+
available in module.
18+
removals : objects or methods in module that will no longer be
19+
accessible once module is removed.
20+
"""
21+
def __init__(self, deprmod, alts=None, removals=None):
22+
self.deprmod = deprmod
23+
24+
self.alts = alts
25+
if self.alts is not None:
26+
self.alts = frozenset(self.alts)
27+
28+
self.removals = removals
29+
if self.removals is not None:
30+
self.removals = frozenset(self.removals)
31+
32+
# For introspection purposes.
33+
self.self_dir = frozenset(dir(self.__class__))
34+
35+
def __dir__(self):
36+
_dir = object.__dir__(self)
37+
38+
if self.removals is not None:
39+
_dir.extend(list(self.removals))
40+
41+
if self.alts is not None:
42+
for modname in self.alts:
43+
module = importlib.import_module(modname)
44+
_dir.extend(dir(module))
45+
46+
return _dir
47+
48+
def __getattr__(self, name):
49+
if name in self.self_dir:
50+
return object.__getattribute__(self, name)
51+
52+
if self.removals is not None and name in self.removals:
53+
with warnings.catch_warnings():
54+
warnings.filterwarnings('ignore', category=FutureWarning)
55+
module = importlib.import_module(self.deprmod)
56+
57+
warnings.warn(
58+
"{deprmod}.{name} is deprecated and will be removed in "
59+
"a future version.".format(deprmod=self.deprmod, name=name),
60+
FutureWarning, stacklevel=2)
61+
62+
return object.__getattribute__(module, name)
63+
64+
if self.alts is not None:
65+
for modname in self.alts:
66+
module = importlib.import_module(modname)
67+
68+
if hasattr(module, name):
69+
warnings.warn(
70+
"{deprmod}.{name} is deprecated. Please use "
71+
"{modname}.{name} instead.".format(
72+
deprmod=self.deprmod, modname=modname, name=name),
73+
FutureWarning, stacklevel=2)
74+
75+
return getattr(module, name)
76+
77+
raise AttributeError("module '{deprmod}' has no attribute "
78+
"'{name}'".format(deprmod=self.deprmod,
79+
name=name))

0 commit comments

Comments
 (0)