Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorboard/lazy.py: 55%

42 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-03 07:57 +0000

1# Copyright 2017 The TensorFlow Authors. All Rights Reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================== 

15"""TensorBoard is a webapp for understanding TensorFlow runs and graphs.""" 

16 

17 

18import functools 

19import threading 

20import types 

21 

22 

23def lazy_load(name): 

24 """Decorator to define a function that lazily loads the module 'name'. 

25 

26 This can be used to defer importing troublesome dependencies - e.g. ones that 

27 are large and infrequently used, or that cause a dependency cycle - 

28 until they are actually used. 

29 

30 Args: 

31 name: the fully-qualified name of the module; typically the last segment 

32 of 'name' matches the name of the decorated function 

33 

34 Returns: 

35 Decorator function that produces a lazy-loading module 'name' backed by the 

36 underlying decorated function. 

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 

82 

83def _memoize(f): 

84 """Memoizing decorator for f, which must have exactly 1 hashable 

85 argument.""" 

86 nothing = object() # Unique "no value" sentinel object. 

87 cache = {} 

88 # Use a reentrant lock so that if f references the resulting wrapper we die 

89 # with recursion depth exceeded instead of deadlocking. 

90 lock = threading.RLock() 

91 

92 @functools.wraps(f) 

93 def wrapper(arg): 

94 if cache.get(arg, nothing) is nothing: 

95 with lock: 

96 if cache.get(arg, nothing) is nothing: 

97 cache[arg] = f(arg) 

98 return cache[arg] 

99 

100 return wrapper