# Copyright 2017 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """TensorBoard is a webapp for understanding TensorFlow runs and graphs.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function import functools import threading import types def lazy_load(name): """Decorator to define a function that lazily loads the module 'name'. This can be used to defer importing troublesome dependencies - e.g. ones that are large and infrequently used, or that cause a dependency cycle - until they are actually used. Args: name: the fully-qualified name of the module; typically the last segment of 'name' matches the name of the decorated function Returns: Decorator function that produces a lazy-loading module 'name' backed by the underlying decorated function. """ def wrapper(load_fn): # Wrap load_fn to call it exactly once and update __dict__ afterwards to # make future lookups efficient (only failed lookups call __getattr__). @_memoize def load_once(self): if load_once.loading: raise ImportError( "Circular import when resolving LazyModule %r" % name ) load_once.loading = True try: module = load_fn() finally: load_once.loading = False self.__dict__.update(module.__dict__) load_once.loaded = True return module load_once.loading = False load_once.loaded = False # Define a module that proxies getattr() and dir() to the result of calling # load_once() the first time it's needed. The class is nested so we can close # over load_once() and avoid polluting the module's attrs with our own state. class LazyModule(types.ModuleType): def __getattr__(self, attr_name): return getattr(load_once(self), attr_name) def __dir__(self): return dir(load_once(self)) def __repr__(self): if load_once.loaded: return "<%r via LazyModule (loaded)>" % load_once(self) return ( "" % self.__name__ ) return LazyModule(name) return wrapper def _memoize(f): """Memoizing decorator for f, which must have exactly 1 hashable argument.""" nothing = object() # Unique "no value" sentinel object. cache = {} # Use a reentrant lock so that if f references the resulting wrapper we die # with recursion depth exceeded instead of deadlocking. lock = threading.RLock() @functools.wraps(f) def wrapper(arg): if cache.get(arg, nothing) is nothing: with lock: if cache.get(arg, nothing) is nothing: cache[arg] = f(arg) return cache[arg] return wrapper