Skip to content

Commit 412c0af

Browse files
committed
Defer optional array runtimes at package import
1 parent b9975ba commit 412c0af

3 files changed

Lines changed: 91 additions & 2 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "polystore"
7-
version = "0.1.28"
7+
version = "0.1.29"
88
description = "Framework-agnostic multi-backend storage abstraction for ML and scientific computing"
99
readme = "README.md"
1010
requires-python = ">=3.11"

src/polystore/__init__.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from importlib.metadata import version as _distribution_version
6+
from typing import TYPE_CHECKING
67

78
__version__ = _distribution_version("polystore")
89

@@ -35,7 +36,6 @@
3536
storage_registry,
3637
)
3738
from .constants import Backend, MemoryType
38-
from .disk import DiskBackend, DiskStorageBackend
3939
from .filemanager import FileManager
4040
from .formats import DEFAULT_IMAGE_EXTENSIONS, FileFormat
4141
from .memory import MemoryBackend, MemoryStorageBackend
@@ -68,6 +68,25 @@
6868
from .streaming_constants import NapariShapeType, StreamingDataType
6969
from .virtual_workspace import SourcePixelRef
7070

71+
if TYPE_CHECKING:
72+
from .disk import DiskBackend, DiskStorageBackend
73+
74+
75+
def __getattr__(name: str):
76+
"""Load disk implementations only when their public exports are requested."""
77+
78+
if name not in {"DiskBackend", "DiskStorageBackend"}:
79+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
80+
81+
from .disk import DiskBackend, DiskStorageBackend
82+
83+
globals().update(
84+
DiskBackend=DiskBackend,
85+
DiskStorageBackend=DiskStorageBackend,
86+
)
87+
return globals()[name]
88+
89+
7190
__all__ = [
7291
"Backend",
7392
"MemoryType",

tests/test_lazy_package_exports.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""Package exports must not initialize optional array runtimes eagerly."""
2+
3+
import json
4+
import subprocess
5+
import sys
6+
from pathlib import Path
7+
8+
9+
def _fresh_python(source: str) -> dict:
10+
result = subprocess.run(
11+
[sys.executable, "-c", source],
12+
cwd=Path(__file__).resolve().parents[1],
13+
check=True,
14+
capture_output=True,
15+
text=True,
16+
)
17+
return json.loads(result.stdout)
18+
19+
20+
def test_constants_import_does_not_load_disk_or_array_frameworks():
21+
loaded = _fresh_python(
22+
"\n".join(
23+
(
24+
"import json",
25+
"import sys",
26+
"from polystore.constants import Backend",
27+
"assert Backend.DISK.value == 'disk'",
28+
"names = ('polystore.disk', 'tensorflow', 'torch', 'cupy', 'jax')",
29+
"print(json.dumps({name: name in sys.modules for name in names}))",
30+
)
31+
)
32+
)
33+
34+
assert loaded == {
35+
"polystore.disk": False,
36+
"tensorflow": False,
37+
"torch": False,
38+
"cupy": False,
39+
"jax": False,
40+
}
41+
42+
43+
def test_root_disk_exports_load_and_cache_the_declared_classes():
44+
loaded = _fresh_python(
45+
"\n".join(
46+
(
47+
"import json",
48+
"import polystore",
49+
"disk_loaded_before = 'polystore.disk' in __import__('sys').modules",
50+
"from polystore import DiskBackend, DiskStorageBackend",
51+
"from polystore.disk import DiskBackend as DeclaredDiskBackend",
52+
"from polystore.disk import DiskStorageBackend as DeclaredDiskStorageBackend",
53+
"print(json.dumps({",
54+
" 'disk_loaded_before': disk_loaded_before,",
55+
" 'backend_identity': DiskBackend is DeclaredDiskBackend,",
56+
" 'storage_identity': DiskStorageBackend is DeclaredDiskStorageBackend,",
57+
" 'backend_cached': polystore.DiskBackend is DiskBackend,",
58+
" 'storage_cached': polystore.DiskStorageBackend is DiskStorageBackend,",
59+
"}))",
60+
)
61+
)
62+
)
63+
64+
assert loaded == {
65+
"disk_loaded_before": False,
66+
"backend_identity": True,
67+
"storage_identity": True,
68+
"backend_cached": True,
69+
"storage_cached": True,
70+
}

0 commit comments

Comments
 (0)