Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 28da62d

Browse files
authored
Merge pull request #1007 from orsonadams/lazyload-py37
Lazyload submodules python37+
2 parents 6b03551 + e78c3c7 commit 28da62d

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

changelog.d/1007.feature.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Made all ``dateutil`` submodules lazily imported using `PEP 562
2+
<https://www.python.org/dev/peps/pep-0562/>`_. On Python 3.7+, things like
3+
``import dateutil; dateutil.tz.gettz("America/New_York")`` will now work
4+
without explicitly importing ``dateutil.tz``, with the import occurring behind
5+
the scenes on first use. The old behavior remains on Python 3.6 and earlier.
6+
Fixed by Orson Adams. (gh issue #771, gh pr #1007)

dateutil/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
11
# -*- coding: utf-8 -*-
2+
import sys
3+
24
try:
35
from ._version import version as __version__
46
except ImportError:
57
__version__ = 'unknown'
68

79
__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz',
810
'utils', 'zoneinfo']
11+
12+
def __getattr__(name):
13+
import importlib
14+
15+
if name in __all__:
16+
return importlib.import_module("." + name, __name__)
17+
raise AttributeError(
18+
"module {!r} has not attribute {!r}".format(__name__, name)
19+
)
20+
21+
22+
def __dir__():
23+
# __dir__ should include all the lazy-importable modules as well.
24+
return [x for x in globals() if x not in sys.modules] + __all__

dateutil/test/test_imports.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,69 @@
11
import sys
2+
import unittest
23
import pytest
4+
import six
5+
6+
MODULE_TYPE = type(sys)
7+
8+
9+
# Tests live in datetutil/test which cause a RuntimeWarning for Python2 builds.
10+
# But since we expect lazy imports tests to fail for Python < 3.7 we'll ignore those
11+
# warnings with this filter.
12+
13+
if six.PY2:
14+
filter_import_warning = pytest.mark.filterwarnings("ignore::RuntimeWarning")
15+
else:
16+
17+
def filter_import_warning(f):
18+
return f
19+
20+
21+
@pytest.fixture(scope="function")
22+
def clean_import():
23+
"""Create a somewhat clean import base for lazy import tests"""
24+
du_modules = {
25+
mod_name: mod
26+
for mod_name, mod in sys.modules.items()
27+
if mod_name.startswith("dateutil")
28+
}
29+
30+
other_modules = {
31+
mod_name for mod_name in sys.modules if mod_name not in du_modules
32+
}
33+
34+
for mod_name in du_modules:
35+
del sys.modules[mod_name]
36+
37+
yield
38+
39+
# Delete anything that wasn't in the origin sys.modules list
40+
for mod_name in list(sys.modules):
41+
if mod_name not in other_modules:
42+
del sys.modules[mod_name]
43+
44+
# Restore original modules
45+
for mod_name, mod in du_modules.items():
46+
sys.modules[mod_name] = mod
47+
48+
49+
@filter_import_warning
50+
@pytest.mark.parametrize(
51+
"module",
52+
["easter", "parser", "relativedelta", "rrule", "tz", "utils", "zoneinfo"],
53+
)
54+
def test_lazy_import(clean_import, module):
55+
"""Test that dateutil.[submodule] works for py version > 3.7"""
56+
57+
import dateutil, importlib
58+
59+
if sys.version_info < (3, 7):
60+
pytest.xfail("Lazy loading does not work for Python < 3.7")
61+
62+
mod_obj = getattr(dateutil, module, None)
63+
assert isinstance(mod_obj, MODULE_TYPE)
64+
65+
mod_imported = importlib.import_module("dateutil.%s" % module)
66+
assert mod_obj is mod_imported
367

468

569
HOST_IS_WINDOWS = sys.platform.startswith('win')

0 commit comments

Comments
 (0)