(load_fn)
| 37 | """ |
| 38 | |
| 39 | def wrapper(load_fn): |
| 40 | # Wrap load_fn to call it exactly once and update __dict__ afterwards to |
| 41 | # make future lookups efficient (only failed lookups call __getattr__). |
| 42 | @_memoize |
| 43 | def load_once(self): |
| 44 | if load_once.loading: |
| 45 | raise ImportError( |
| 46 | "Circular import when resolving LazyModule %r" % name |
| 47 | ) |
| 48 | load_once.loading = True |
| 49 | try: |
| 50 | module = load_fn() |
| 51 | finally: |
| 52 | load_once.loading = False |
| 53 | self.__dict__.update(module.__dict__) |
| 54 | load_once.loaded = True |
| 55 | return module |
| 56 | |
| 57 | load_once.loading = False |
| 58 | load_once.loaded = False |
| 59 | |
| 60 | # Define a module that proxies getattr() and dir() to the result of calling |
| 61 | # load_once() the first time it's needed. The class is nested so we can close |
| 62 | # over load_once() and avoid polluting the module's attrs with our own state. |
| 63 | class LazyModule(types.ModuleType): |
| 64 | def __getattr__(self, attr_name): |
| 65 | return getattr(load_once(self), attr_name) |
| 66 | |
| 67 | def __dir__(self): |
| 68 | return dir(load_once(self)) |
| 69 | |
| 70 | def __repr__(self): |
| 71 | if load_once.loaded: |
| 72 | return "<%r via LazyModule (loaded)>" % load_once(self) |
| 73 | return ( |
| 74 | "<module %r via LazyModule (not yet loaded)>" |
| 75 | % self.__name__ |
| 76 | ) |
| 77 | |
| 78 | return LazyModule(name) |
| 79 | |
| 80 | return wrapper |
| 81 |
nothing calls this directly
no test coverage detected
searching dependent graphs…