-
Notifications
You must be signed in to change notification settings - Fork 41
Expand file tree
/
Copy pathtest_lazy_imports.py
More file actions
76 lines (61 loc) · 2.54 KB
/
Copy pathtest_lazy_imports.py
File metadata and controls
76 lines (61 loc) · 2.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""Tests for the lazy-import machinery added to keep `import python_utils`
light (PEP 562 `__getattr__`/`__dir__` in the package).
"""
import os
import subprocess
import sys
import pytest
import python_utils
from python_utils import types
def test_package_lazy_attribute_access() -> None:
# Submodule access and exported-name access both resolve via __getattr__.
assert python_utils.aio is python_utils.aio
assert callable(python_utils.acount)
missing = 'definitely_not_a_real_attribute'
with pytest.raises(AttributeError):
getattr(python_utils, missing)
def test_bare_import_stays_light() -> None:
# Importing the package must not eagerly pull in heavy/optional deps. This
# runs in a clean interpreter because the test session itself has long
# since imported asyncio/typing_extensions.
code = (
'import sys, python_utils\n'
"assert 'asyncio' not in sys.modules, "
"sorted(m for m in sys.modules if m.startswith('asyncio'))\n"
"assert 'typing_extensions' not in sys.modules\n"
)
env = {**os.environ, 'PYTHONPATH': os.pathsep.join(sys.path)}
result = subprocess.run(
[sys.executable, '-c', code],
capture_output=True,
text=True,
env=env,
)
assert result.returncode == 0, result.stderr
def test_first_access_caches_into_module_dict() -> None:
# PEP 562 __getattr__ runs once: the resolved object is cached in the
# module namespace so subsequent lookups skip __getattr__ entirely.
module = python_utils.time
assert python_utils.__dict__['time'] is module
func = python_utils.format_time
assert python_utils.__dict__['format_time'] is func
def test_dir_lists_lazy_submodules() -> None:
# Lazy submodules that are not in __all__ (e.g. ``containers`` and
# ``exceptions``) must still be discoverable via ``dir``; tools such as
# ``import_global`` intersect requested names with ``dir(module)``.
names = set(dir(python_utils))
assert {'containers', 'exceptions'} <= names
assert set(python_utils.__all__) <= names
@pytest.mark.asyncio
async def test_aio_timeout_generator_default_iterable() -> None:
# With no iterable the generator defaults to ``aio.acount`` -- exercising
# the lazy ``aio``/``asyncio`` import and the None-resolution branch.
count = 0
generator: types.AsyncGenerator[object, None] = (
python_utils.aio_timeout_generator(timeout=0.05, interval=0.0)
)
async for _ in generator:
count += 1
if count >= 2:
break
assert count == 2