Skip to content

Commit 97980c3

Browse files
authored
support multi-level module attribute access in LazyImport (#120)
1 parent a9e46af commit 97980c3

2 files changed

Lines changed: 62 additions & 14 deletions

File tree

angelslim/compressor/speculative/train/trainer/eagle3_trainer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
from abc import ABC, abstractmethod
1717
from typing import Any, Dict, List, Optional, Tuple
1818

19-
import deepspeed
2019
import torch
2120
from torch import nn
2221
from transformers import Trainer
2322

23+
from angelslim.utils.lazy_imports import deepspeed
24+
2425
from ...utils import padding
2526

2627

angelslim/utils/lazy_imports.py

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class LazyModule:
3434
_module_name (str): The full name of the module to import
3535
_extra_group (str): The extra dependency group required for this module
3636
_module (ModuleType): The actual imported module (None until first access)
37+
_submodules (dict): Cache for LazyModule instances of submodules
3738
3839
Example:
3940
>>> ray = LazyModule('ray', 'speculative')
@@ -52,36 +53,81 @@ def __init__(self, module_name: str, extra_group: str = None):
5253
self._module_name = module_name
5354
self._extra_group = extra_group
5455
self._module = None
56+
self._submodules = {}
57+
58+
def _import_module(self):
59+
"""
60+
Import the target module if not already imported.
61+
62+
Raises:
63+
ImportError: If the module cannot be imported
64+
"""
65+
if self._module is None:
66+
try:
67+
self._module = importlib.import_module(self._module_name)
68+
except ImportError as e:
69+
if self._extra_group:
70+
raise ImportError(
71+
f"Module '{self._module_name}' requires "
72+
f"additional dependencies. Please install: "
73+
f"pip install 'angelslim[{self._extra_group}]'"
74+
) from e
75+
raise
5576

5677
def __getattr__(self, name: str) -> Any:
5778
"""
5879
Delegate attribute access to the actual module.
5980
6081
On first access, this method imports the target module and then
61-
delegates the attribute lookup to the actual module.
82+
delegates the attribute lookup to the actual module. For submodules,
83+
it returns a LazyModule instance to support multi-level access.
6284
6385
Args:
6486
name: Name of the attribute to access
6587
6688
Returns:
67-
The requested attribute from the target module
89+
The requested attribute from the target module, or a LazyModule
90+
instance for submodules
6891
6992
Raises:
7093
ImportError: If the module cannot be imported and an
7194
extra_group is specified, provides installation instructions
7295
"""
73-
if self._module is None:
96+
# Return cached submodule if exists
97+
if name in self._submodules:
98+
return self._submodules[name]
99+
100+
self._import_module()
101+
102+
# Try to get the attribute from the imported module
103+
try:
104+
attr = getattr(self._module, name)
105+
# If it's a module, wrap it in LazyModule for consistent behavior
106+
if isinstance(attr, type(self._module)):
107+
submodule_name = f"{self._module_name}.{name}"
108+
lazy_submodule = LazyModule(submodule_name, self._extra_group)
109+
lazy_submodule._module = attr # Cache the already imported module
110+
self._submodules[name] = lazy_submodule
111+
return lazy_submodule
112+
return attr
113+
except AttributeError:
114+
# If attribute not found, try importing as a submodule
115+
submodule_name = f"{self._module_name}.{name}"
74116
try:
75-
self._module = importlib.import_module(self._module_name)
76-
except ImportError as e:
77-
if self._extra_group:
78-
raise ImportError(
79-
f"Module '{self._module_name}' requires "
80-
f"additional dependencies. Please install: "
81-
f"pip install 'angelslim[{self._extra_group}]'"
82-
) from e
83-
raise
84-
return getattr(self._module, name)
117+
# Create a LazyModule for the submodule
118+
lazy_submodule = LazyModule(submodule_name, self._extra_group)
119+
# Trigger import to verify it exists
120+
lazy_submodule._import_module()
121+
# Cache it
122+
self._submodules[name] = lazy_submodule
123+
# Also cache in parent module for consistency
124+
setattr(self._module, name, lazy_submodule._module)
125+
return lazy_submodule
126+
except ImportError:
127+
# If submodule import fails, re-raise the original AttributeError
128+
raise AttributeError(
129+
f"module '{self._module_name}' has no attribute '{name}'"
130+
)
85131

86132

87133
class LazyAttribute:
@@ -154,6 +200,7 @@ def __getattr__(self, name: str) -> Any:
154200
anthropic = LazyModule("anthropic", "speculative")
155201
jsonschema_specifications = LazyModule("jsonschema_specifications", "speculative")
156202
referencing = LazyModule("referencing", "speculative")
203+
deepspeed = LazyModule("deepspeed", "speculative")
157204

158205

159206
# --- VLM related lazy imports ---

0 commit comments

Comments
 (0)