29
29
import dataclasses
30
30
import functools
31
31
import importlib
32
+ import inspect
32
33
import sys
33
34
import threading
34
35
import types
40
41
41
42
_ErrorCallback = Callable [[Exception ], None ]
42
43
_SuccessCallback = Callable [[str ], None ]
44
+ # Eagerly resolve this import since it's needed in setattr.
45
+ _getattr_static = inspect .getattr_static
43
46
44
47
# Store a lock per module to avoid problems with multiple threads trying to
45
48
# import the same module at the same time.
@@ -55,6 +58,7 @@ class LazyModule:
55
58
error_callback : str | _ErrorCallback | None
56
59
success_callback : _SuccessCallback | None
57
60
_submodules : dict [str , LazyModule ] = dataclasses .field (default_factory = dict )
61
+ _initialized : bool = dataclasses .field (default = False , init = False )
58
62
59
63
def __post_init__ (self ):
60
64
if self .adhoc_kwargs is not None :
@@ -66,6 +70,7 @@ def __post_init__(self):
66
70
self .adhoc_kwargs .pop ("reload_workspace" , None )
67
71
self .adhoc_kwargs .pop ("cell_autoreload" , None )
68
72
self .adhoc_kwargs .pop ("restrict_reload" , None )
73
+ self ._initialized = True
69
74
70
75
@functools .cached_property
71
76
def _module (self ) -> types .ModuleType :
@@ -117,7 +122,20 @@ def __getattr__(self, name: str) -> Any:
117
122
else :
118
123
return getattr (self ._module , name )
119
124
120
- # TODO(epot): Also support __setattr__
125
+ def __setattr__ (self , name : str , value : Any ) -> None :
126
+ # Avoid regular `__getattr__` path during initialization.
127
+ if _getattr_static (self , "_initialized" ):
128
+ # Trigger import first to overwrite the old attribute if it exists.
129
+ setattr (self ._module , name , value )
130
+ else :
131
+ super ().__setattr__ (name , value )
132
+
133
+ def __delattr__ (self , name : str ):
134
+ if name in self ._submodules :
135
+ del self ._submodules [name ]
136
+ # Always delete from underlying module so that lazy import doesn't replace
137
+ # a deleted attribute.
138
+ delattr (self ._module , name )
121
139
122
140
123
141
def _register_submodule (module : LazyModule , name : str ) -> LazyModule :
0 commit comments