# -*- coding: utf-8 -*- # File: develop.py # Author: tensorpack contributors """ Utilities for developers only. These are not visible to users (not automatically imported). And should not appeared in docs.""" import functools import importlib import os import types from collections import defaultdict from datetime import datetime import six from . import logger __all__ = [] def create_dummy_class(klass, dependency): """ When a dependency of a class is not available, create a dummy class which throws ImportError when used. Args: klass (str): name of the class. dependency (str): name of the dependency. Returns: class: a class object """ assert not building_rtfd() class _DummyMetaClass(type): # throw error on class attribute access def __getattr__(_, __): raise AttributeError("Cannot import '{}', therefore '{}' is not available".format(dependency, klass)) @six.add_metaclass(_DummyMetaClass) class _Dummy(object): # throw error on constructor def __init__(self, *args, **kwargs): raise ImportError("Cannot import '{}', therefore '{}' is not available".format(dependency, klass)) return _Dummy def create_dummy_func(func, dependency): """ When a dependency of a function is not available, create a dummy function which throws ImportError when used. Args: func (str): name of the function. dependency (str or list[str]): name(s) of the dependency. Returns: function: a function object """ assert not building_rtfd() if isinstance(dependency, (list, tuple)): dependency = ','.join(dependency) def _dummy(*args, **kwargs): raise ImportError("Cannot import '{}', therefore '{}' is not available".format(dependency, func)) return _dummy def building_rtfd(): """ Returns: bool: if the library is being imported to generate docs now. """ return os.environ.get('READTHEDOCS') == 'True' \ or os.environ.get('DOC_BUILDING') _DEPRECATED_LOG_NUM = defaultdict(int) def log_deprecated(name="", text="", eos="", max_num_warnings=None): """ Log deprecation warning. Args: name (str): name of the deprecated item. text (str, optional): information about the deprecation. eos (str, optional): end of service date such as "YYYY-MM-DD". max_num_warnings (int, optional): the maximum number of times to print this warning """ assert name or text if eos: eos = "after " + datetime(*map(int, eos.split("-"))).strftime("%d %b") if name: if eos: warn_msg = "%s will be deprecated %s. %s" % (name, eos, text) else: warn_msg = "%s was deprecated. %s" % (name, text) else: warn_msg = text if eos: warn_msg += " Legacy period ends %s" % eos if max_num_warnings is not None: if _DEPRECATED_LOG_NUM[warn_msg] >= max_num_warnings: return _DEPRECATED_LOG_NUM[warn_msg] += 1 logger.warn("[Deprecated] " + warn_msg) def deprecated(text="", eos="", max_num_warnings=None): """ Args: text, eos, max_num_warnings: same as :func:`log_deprecated`. Returns: a decorator which deprecates the function. Example: .. code-block:: python @deprecated("Explanation of what to do instead.", "2017-11-4") def foo(...): pass """ def get_location(): import inspect frame = inspect.currentframe() if frame: callstack = inspect.getouterframes(frame)[-1] return '%s:%i' % (callstack[1], callstack[2]) else: stack = inspect.stack(0) entry = stack[2] return '%s:%i' % (entry[1], entry[2]) def deprecated_inner(func): @functools.wraps(func) def new_func(*args, **kwargs): name = "{} [{}]".format(func.__name__, get_location()) log_deprecated(name, text, eos, max_num_warnings=max_num_warnings) return func(*args, **kwargs) return new_func return deprecated_inner def HIDE_DOC(func): func.__HIDE_SPHINX_DOC__ = True return func # Copied from https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/util/lazy_loader.py class LazyLoader(types.ModuleType): def __init__(self, local_name, parent_module_globals, name): self._local_name = local_name self._parent_module_globals = parent_module_globals super(LazyLoader, self).__init__(name) def _load(self): # Import the target module and insert it into the parent's namespace module = importlib.import_module(self.__name__) self._parent_module_globals[self._local_name] = module # Update this object's dict so that if someone keeps a reference to the # LazyLoader, lookups are efficient (__getattr__ is only called on lookups # that fail). self.__dict__.update(module.__dict__) return module def __getattr__(self, item): module = self._load() return getattr(module, item) def __dir__(self): module = self._load() return dir(module)