|
1 | 1 | import ast |
| 2 | +import importlib |
2 | 3 | import os.path |
3 | 4 | import pathlib |
| 5 | +import types |
4 | 6 | from importlib.metadata import distribution |
5 | 7 | from site import getsitepackages |
6 | 8 |
|
7 | 9 | from numba import cuda |
8 | 10 |
|
9 | 11 | from . import cache, config |
10 | | -from .aamp import aamp # noqa: F401 |
11 | | -from .aamp_mmotifs import aamp_mmotifs # noqa: F401 |
12 | | -from .aamp_motifs import aamp_match, aamp_motifs # noqa: F401 |
13 | | -from .aamp_ostinato import aamp_ostinato, aamp_ostinatoed # noqa: F401 |
14 | | -from .aamp_stimp import aamp_stimp, aamp_stimped # noqa: F401 |
15 | | -from .aampdist import aampdist, aampdisted # noqa: F401 |
16 | | -from .aampdist_snippets import aampdist_snippets # noqa: F401 |
17 | | -from .aamped import aamped # noqa: F401 |
18 | | -from .aampi import aampi # noqa: F401 |
19 | | -from .chains import allc, atsc # noqa: F401 |
20 | | -from .core import mass # noqa: F401 |
21 | | -from .floss import floss, fluss # noqa: F401 |
22 | | -from .maamp import maamp, maamp_mdl, maamp_subspace # noqa: F401 |
23 | | -from .maamped import maamped # noqa: F401 |
24 | | -from .mmotifs import mmotifs # noqa: F401 |
25 | | -from .motifs import match, motifs # noqa: F401 |
26 | | -from .mpdist import mpdist, mpdisted # noqa: F401 |
27 | | -from .mstump import mdl, mstump, subspace # noqa: F401 |
28 | | -from .mstumped import mstumped # noqa: F401 |
29 | | -from .ostinato import ostinato, ostinatoed # noqa: F401 |
30 | | -from .scraamp import prescraamp, scraamp # noqa: F401 |
31 | | -from .scrump import prescrump, scrump # noqa: F401 |
32 | | -from .snippets import snippets # noqa: F401 |
33 | | -from .stimp import stimp, stimped # noqa: F401 |
34 | | -from .stump import stump # noqa: F401 |
35 | | -from .stumped import stumped # noqa: F401 |
36 | | -from .stumpi import stumpi # noqa: F401 |
| 12 | + |
| 13 | +# Define which functions belong to which submodules |
| 14 | +# Key: function name to expose at top level |
| 15 | +# Value: name of the submodule |
| 16 | +_lazy_imports = { |
| 17 | + "aamp": "aamp", |
| 18 | + "aamp_mmotifs": "aamp_mmotifs", |
| 19 | + "aamp_match": "aamp_motifs", |
| 20 | + "aamp_motifs": "aamp_motifs", |
| 21 | + "aamp_ostinato": "aamp_ostinato", |
| 22 | + "aamp_ostinatoed": "aamp_ostinato", |
| 23 | + "aamp_stimp": "aamp_stimp", |
| 24 | + "aamp_stimped": "aamp_stimp", |
| 25 | + "aampdist": "aampdist", |
| 26 | + "aampdisted": "aampdist", |
| 27 | + "aampdist_snippets": "aampdist_snippets", |
| 28 | + "aamped": "aamped", |
| 29 | + "aampi": "aampi", |
| 30 | + "allc": "chains", |
| 31 | + "atsc": "chains", |
| 32 | + "mass": "core", |
| 33 | + "floss": "floss", |
| 34 | + "fluss": "floss", |
| 35 | + "maamp": "maamp", |
| 36 | + "maamp_mdl": "maamp", |
| 37 | + "maamp_subspace": "maamp", |
| 38 | + "maamped": "maamped", |
| 39 | + "mmotifs": "mmotifs", |
| 40 | + "match": "motifs", |
| 41 | + "motifs": "motifs", |
| 42 | + "mpdist": "mpdist", |
| 43 | + "mpdisted": "mpdist", |
| 44 | + "mdl": "mstump", |
| 45 | + "mstump": "mstump", |
| 46 | + "subspace": "mstump", |
| 47 | + "mstumped": "mstumped", |
| 48 | + "ostinato": "ostinato", |
| 49 | + "ostinatoed": "ostinato", |
| 50 | + "prescraamp": "scraamp", |
| 51 | + "scraamp": "scraamp", |
| 52 | + "prescrump": "scrump", |
| 53 | + "scrump": "scrump", |
| 54 | + "snippets": "snippets", |
| 55 | + "stimp": "stimp", |
| 56 | + "stimped": "stimp", |
| 57 | + "stump": "stump", |
| 58 | + "stumped": "stumped", |
| 59 | + "stumpi": "stumpi", |
| 60 | +} |
37 | 61 |
|
38 | 62 | # Get the default fastmath flags for all njit functions |
39 | 63 | # and update the _STUMPY_DEFAULTS dictionary |
@@ -61,14 +85,18 @@ def _get_fastmath_value(module_name, func_name): # pragma: no cover |
61 | 85 | config._STUMPY_DEFAULTS[key] = _get_fastmath_value(module_name, func_name) |
62 | 86 |
|
63 | 87 | if cuda.is_available(): |
64 | | - from .gpu_aamp import gpu_aamp # noqa: F401 |
65 | | - from .gpu_aamp_ostinato import gpu_aamp_ostinato # noqa: F401 |
66 | | - from .gpu_aamp_stimp import gpu_aamp_stimp # noqa: F401 |
67 | | - from .gpu_aampdist import gpu_aampdist # noqa: F401 |
68 | | - from .gpu_mpdist import gpu_mpdist # noqa: F401 |
69 | | - from .gpu_ostinato import gpu_ostinato # noqa: F401 |
70 | | - from .gpu_stimp import gpu_stimp # noqa: F401 |
71 | | - from .gpu_stump import gpu_stump # noqa: F401 |
| 88 | + _lazy_imports.update( |
| 89 | + { |
| 90 | + "gpu_aamp": "gpu_aamp", |
| 91 | + "gpu_aamp_ostinato": "gpu_aamp_ostinato", |
| 92 | + "gpu_aamp_stimp": "gpu_aamp_stimp", |
| 93 | + "gpu_aampdist": "gpu_aampdist", |
| 94 | + "gpu_mpdist": "gpu_mpdist", |
| 95 | + "gpu_ostinato": "gpu_ostinato", |
| 96 | + "gpu_stimp": "gpu_stimp", |
| 97 | + "gpu_stump": "gpu_stump", |
| 98 | + } |
| 99 | + ) |
72 | 100 | else: # pragma: no cover |
73 | 101 | from . import core |
74 | 102 | from .core import _gpu_aamp_driver_not_found as gpu_aamp # noqa: F401 |
@@ -220,3 +248,36 @@ def _get_fastmath_value(module_name, func_name): # pragma: no cover |
220 | 248 | __version__ = "Please install this project with setup.py" |
221 | 249 | else: # pragma: no cover |
222 | 250 | __version__ = _dist.version |
| 251 | + |
| 252 | + |
| 253 | +# PEP 562: module-level __getattr__ for lazy imports |
| 254 | +def __getattr__(name): # pragma: no cover |
| 255 | + if name in _lazy_imports: |
| 256 | + submodule_name = _lazy_imports[name] |
| 257 | + full_module_path = f"{__package__}.{submodule_name}" |
| 258 | + module = importlib.import_module(full_module_path) |
| 259 | + # Retrieve the attribute from the loaded submodule and cache it |
| 260 | + attr = getattr(module, name) |
| 261 | + globals()[name] = attr |
| 262 | + return attr |
| 263 | + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") |
| 264 | + |
| 265 | + |
| 266 | +# Ensure that if a submodule was imported during package import |
| 267 | +# (causing the package attribute to point to the submodule object), we |
| 268 | +# replace that entry with the actual attribute (e.g., function) so that |
| 269 | +# users get the expected callable at `stumpy.aamp` rather than the module. |
| 270 | +for _name, _sub in _lazy_imports.items(): # pragma: no cover |
| 271 | + val = globals().get(_name) |
| 272 | + if isinstance(val, types.ModuleType): |
| 273 | + try: |
| 274 | + replacement = getattr(val, _name) |
| 275 | + except AttributeError: |
| 276 | + # Nothing to do if the submodule doesn't define the attribute |
| 277 | + continue |
| 278 | + globals()[_name] = replacement |
| 279 | + |
| 280 | + |
| 281 | +def __dir__(): # pragma: no cover |
| 282 | + # Expose lazy names in dir() for discoverability |
| 283 | + return sorted(list(globals().keys()) + list(_lazy_imports.keys())) |
0 commit comments