diff --git a/src/dateutil/__init__.py b/src/dateutil/__init__.py index a2c19c06..c510f0d5 100644 --- a/src/dateutil/__init__.py +++ b/src/dateutil/__init__.py @@ -6,13 +6,13 @@ except ImportError: __version__ = 'unknown' -__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz', - 'utils', 'zoneinfo'] +__all__ = ["easter", "parser", "relativedelta", "rrule", "tz", "utils"] + def __getattr__(name): import importlib - if name in __all__: + if name in __all__ or name == "zoneinfo": return importlib.import_module("." + name, __name__) raise AttributeError( "module {!r} has not attribute {!r}".format(__name__, name) @@ -21,4 +21,6 @@ def __getattr__(name): def __dir__(): # __dir__ should include all the lazy-importable modules as well. - return [x for x in globals() if x not in sys.modules] + __all__ + return ( + [x for x in globals() if x not in sys.modules] + __all__ + ["zoneinfo,"] + ) diff --git a/src/dateutil/zoneinfo/__init__.py b/src/dateutil/zoneinfo/__init__.py index 40f808b8..6944280e 100644 --- a/src/dateutil/zoneinfo/__init__.py +++ b/src/dateutil/zoneinfo/__init__.py @@ -1,4 +1,15 @@ # -*- coding: utf-8 -*- +""" +The ``dateutil.zoneinfo`` module was originally designed to work with a tarball +shipped with ``dateutil``. Since ``dateutil`` now uses data from the same +sources as :py:mod:`zoneinfo`, this module is no longer necessary. + +.. caution:: + This module is deprecated, and its functionality has been dramatically + curtailed. It is recommended that you adopt the standard library module + :py:mod:`zoneinfo` or :func:`dateutil.tz.gettz` instead. + +""" import json import sys import warnings @@ -11,9 +22,18 @@ __all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] -ZONEFILENAME = "dateutil-zoneinfo.tar.gz" +ZONEFILENAME = None METADATA_FN = 'METADATA' +warnings.warn( + "The `dateutil.zoneinfo` module has been replaced with a wrapper around " + "the tzdata package, and its use is deprecated, to be removed in a future " + "version. Use the standard library module `zoneinfo` or `dateutil.tz`" + "instead.", + DeprecationWarning, + stacklevel=2, +) + class tzfile(_tzfile): def __reduce__(self): @@ -31,7 +51,16 @@ def __init__(self, zonefile_stream=None): self._eager_load_tzdata() def _eager_load_tzdata(self): - import tzdata + try: + import tzdata + except ImportError as e: + six.raise_from( + ImportError( + "The tzdata module is required to use ZoneInfoFile; either add " + "a dependency on tzdata or migrate away from dateutil.zoneinfo." + ), + e, + ) zone_dict = {} diff --git a/tests/test_import_star.py b/tests/test_import_star.py index 2fb70981..29e96900 100644 --- a/tests/test_import_star.py +++ b/tests/test_import_star.py @@ -20,7 +20,6 @@ def test_imported_modules(): import dateutil.rrule import dateutil.tz import dateutil.utils - import dateutil.zoneinfo assert dateutil.easter == new_locals.pop("easter") assert dateutil.parser == new_locals.pop("parser") @@ -28,6 +27,5 @@ def test_imported_modules(): assert dateutil.rrule == new_locals.pop("rrule") assert dateutil.tz == new_locals.pop("tz") assert dateutil.utils == new_locals.pop("utils") - assert dateutil.zoneinfo == new_locals.pop("zoneinfo") assert not new_locals diff --git a/tests/test_imports.py b/tests/test_imports.py index 7d0749ec..e22de49d 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -1,5 +1,6 @@ import sys import unittest + import pytest import six @@ -49,12 +50,14 @@ def clean_import(): @filter_import_warning @pytest.mark.parametrize( "module", - ["easter", "parser", "relativedelta", "rrule", "tz", "utils", "zoneinfo"], + ["easter", "parser", "relativedelta", "rrule", "tz", "utils"], ) def test_lazy_import(clean_import, module): """Test that dateutil.[submodule] works for py version > 3.7""" - import dateutil, importlib + import importlib + + import dateutil if sys.version_info < (3, 7): pytest.xfail("Lazy loading does not work for Python < 3.7") @@ -66,6 +69,14 @@ def test_lazy_import(clean_import, module): assert mod_obj is mod_imported +def test_lazy_import_zoneinfo(clean_import): + """Test that zoneinfo raises a DeprecationWarning when imported lazily.""" + import dateutil + + with pytest.warns(DeprecationWarning): + assert dateutil.zoneinfo is not None + + HOST_IS_WINDOWS = sys.platform.startswith('win') @@ -103,11 +114,8 @@ def test_import_parser_from(): def test_import_parser_all(): # All interface - from dateutil.parser import parse - from dateutil.parser import parserinfo - # Other public classes - from dateutil.parser import parser + from dateutil.parser import parse, parser, parserinfo for var in (parse, parserinfo, parser): assert var is not None @@ -122,8 +130,7 @@ def test_import_relative_delta_from(): from dateutil import relativedelta def test_import_relative_delta_all(): - from dateutil.relativedelta import relativedelta - from dateutil.relativedelta import MO, TU, WE, TH, FR, SA, SU + from dateutil.relativedelta import FR, MO, SA, SU, TH, TU, WE, relativedelta for var in (relativedelta, MO, TU, WE, TH, FR, SA, SU): assert var is not None @@ -143,12 +150,25 @@ def test_import_rrule_from(): def test_import_rrule_all(): - from dateutil.rrule import rrule - from dateutil.rrule import rruleset - from dateutil.rrule import rrulestr - from dateutil.rrule import YEARLY, MONTHLY, WEEKLY, DAILY - from dateutil.rrule import HOURLY, MINUTELY, SECONDLY - from dateutil.rrule import MO, TU, WE, TH, FR, SA, SU + from dateutil.rrule import ( + DAILY, + FR, + HOURLY, + MINUTELY, + MO, + MONTHLY, + SA, + SECONDLY, + SU, + TH, + TU, + WE, + WEEKLY, + YEARLY, + rrule, + rruleset, + rrulestr, + ) rr_all = (rrule, rruleset, rrulestr, YEARLY, MONTHLY, WEEKLY, DAILY, @@ -173,20 +193,22 @@ def test_import_tz_from(): def test_import_tz_all(): - from dateutil.tz import tzutc - from dateutil.tz import tzoffset - from dateutil.tz import tzlocal - from dateutil.tz import tzfile - from dateutil.tz import tzrange - from dateutil.tz import tzstr - from dateutil.tz import tzical - from dateutil.tz import gettz - from dateutil.tz import tzwin - from dateutil.tz import tzwinlocal - from dateutil.tz import UTC - from dateutil.tz import datetime_ambiguous - from dateutil.tz import datetime_exists - from dateutil.tz import resolve_imaginary + from dateutil.tz import ( + UTC, + datetime_ambiguous, + datetime_exists, + gettz, + resolve_imaginary, + tzfile, + tzical, + tzlocal, + tzoffset, + tzrange, + tzstr, + tzutc, + tzwin, + tzwinlocal, + ) tz_all = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", "tzstr", "tzical", "gettz", "datetime_ambiguous", @@ -211,8 +233,7 @@ def test_import_tz_windows_from(): @pytest.mark.skipif(not HOST_IS_WINDOWS, reason="Requires Windows") def test_import_tz_windows_star(): - from dateutil.tzwin import tzwin - from dateutil.tzwin import tzwinlocal + from dateutil.tzwin import tzwin, tzwinlocal tzwin_all = [tzwin, tzwinlocal] @@ -221,18 +242,19 @@ def test_import_tz_windows_star(): # Test imports of Zone Info -def test_import_zone_info_direct(): - import dateutil.zoneinfo +def test_import_zone_info_direct(clean_import): + with pytest.warns(DeprecationWarning): + import dateutil.zoneinfo -def test_import_zone_info_from(): - from dateutil import zoneinfo +def test_import_zone_info_from(clean_import): + with pytest.warns(DeprecationWarning): + from dateutil import zoneinfo -def test_import_zone_info_star(): - from dateutil.zoneinfo import gettz - from dateutil.zoneinfo import gettz_db_metadata - from dateutil.zoneinfo import rebuild +def test_import_zone_info_star(clean_import): + with pytest.warns(DeprecationWarning): + from dateutil.zoneinfo import gettz, gettz_db_metadata, rebuild zi_all = (gettz, gettz_db_metadata, rebuild) diff --git a/tests/test_tz.py b/tests/test_tz.py index bf49b8b3..e0f7d56b 100644 --- a/tests/test_tz.py +++ b/tests/test_tz.py @@ -7,6 +7,7 @@ import sys import threading import unittest +import warnings import weakref from datetime import datetime from datetime import time as dt_time @@ -23,9 +24,12 @@ import pytest from dateutil import tz as tz -from dateutil import zoneinfo from dateutil.parser import parse +with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + from dateutil import zoneinfo + # dateutil imports from dateutil.relativedelta import SU, TH, relativedelta