Commit e0b1a5ce authored by Patrick Wieschollek's avatar Patrick Wieschollek Committed by Yuxin Wu

Add helper function/decorator for deprecation warning (#145)

* Add helper function/decorator for deprecation warning

This logs warnings about deprecated functions and also shows the
filename as well as the linenumber.

* enhance documentation

* try to fix python3 (blindly)

* deprecation: use one function to log and one function as decorator

* fix linting
parent 076a728f
......@@ -142,7 +142,7 @@ class ClassificationError(Inferencer):
def _datapoint(self, outputs):
vec = outputs[0]
if vec.ndim == 0:
logger.error("[DEPRECATED] use a 'wrong vector' for ClassificationError instead of nr_wrong")
logger.error("[DEPRECATED] use a 'wrong vector' for ClassificationError instead of nr_wrong. Exiting..")
sys.exit(1)
else:
# TODO put shape assertion into inferencerrunner
......
......@@ -8,9 +8,10 @@ import tensorflow as tf
import pickle
import six
from ..utils import logger, INPUTS_KEY
from ..utils import logger, INPUTS_KEY, deprecated, log_deprecated
from ..utils.argtools import memoized
from ..tfutils.modelutils import apply_slim_collections
from ..tfutils.gradproc import CheckGradient
__all__ = ['InputDesc', 'InputVar', 'ModelDesc', 'ModelFromMetaGraph']
......@@ -62,9 +63,9 @@ class ModelDesc(object):
"""
return self.build_placeholders()
@deprecated("Use get_reused_placehdrs() instead.", "2017-04-11")
def get_input_vars(self):
# this wasn't a public API anyway
logger.warn("[Deprecated] get_input_vars() was renamed to get_reused_placehdrs()!")
return self.get_reused_placehdrs()
def build_placeholders(self, prefix=''):
......@@ -97,8 +98,7 @@ class ModelDesc(object):
"""
:returns: a list of InputDesc
"""
# TODO deprecate @ Apr 11
logger.warn("[Deprecated] _get_input_vars() is renamed to _get_inputs()")
log_deprecated("", "_get_input_vars() was renamed to _get_inputs().", "2017-04-11")
return self._get_input_vars()
def _get_input_vars(self): # keep backward compatibility
......
......@@ -6,7 +6,7 @@ import six
import tensorflow as tf
import re
from ..utils import logger
from ..utils import log_deprecated
from ..utils.naming import MOVING_SUMMARY_VARS_KEY
from .tower import get_current_tower_context
from .symbolic_functions import rms
......@@ -61,7 +61,7 @@ def add_param_summary(*summary_lists):
if ctx is not None and not ctx.is_main_training_tower:
return
if len(summary_lists) == 1 and isinstance(summary_lists[0], list):
logger.warn("[Deprecated] Use positional args to call add_param_summary() instead of a list.")
log_deprecated(text="Use positional args to call add_param_summary() instead of a list.")
summary_lists = summary_lists[0]
def perform(var, action):
......
......@@ -10,7 +10,7 @@ from ..callbacks import (
MaintainStepCounter)
from ..dataflow.base import DataFlow
from ..models import ModelDesc
from ..utils import logger
from ..utils import logger, log_deprecated
from ..tfutils import (JustCurrentSession,
get_default_sess_config, SessionInit)
from .input_data import InputData
......@@ -62,7 +62,7 @@ class TrainConfig(object):
# process data
if 'dataset' in kwargs:
dataflow = kwargs.pop('dataset')
logger.warn("[Deprecated] TrainConfig.dataset has been deprecated. Use TrainConfig.dataflow instead.")
log_deprecated("TrainConfig.dataset", "Use TrainConfig.dataflow instead.")
if dataflow is not None:
assert data is None, "dataflow and data cannot be both presented in TrainConfig!"
self.dataflow = dataflow
......@@ -75,9 +75,10 @@ class TrainConfig(object):
if isinstance(callbacks, Callbacks):
# keep quiet now because I haven't determined the final API yet.
logger.warn("[Deprecated] API of TrainConfig(callbacks=) has changed!")
logger.warn("[Deprecated] Please change the argument 'callbacks=' to a *list* of "
"callbacks without StatPrinter().")
log_deprecated(
"TrainConfig(callbacks=Callbacks([...]))",
"Change the argument 'callbacks=' to a *list* of callbacks without StatPrinter().")
callbacks = callbacks.cbs[:-1] # the last one is StatPrinter()
assert_type(callbacks, list)
if extra_callbacks is None:
......@@ -102,8 +103,7 @@ class TrainConfig(object):
if steps_per_epoch is None:
steps_per_epoch = kwargs.pop('step_per_epoch', None)
if steps_per_epoch is not None:
# TODO deprecate @Mar.27
logger.warn("[Deprecated] Use steps_per_epoch instead of step_per_epoch!")
log_deprecated("step_per_epoch", "Use steps_per_epoch instead!", "2017-03-27")
if steps_per_epoch is None:
try:
if dataflow is not None:
......@@ -138,9 +138,7 @@ class TrainConfig(object):
assert len(kwargs) == 0, 'Unknown arguments: {}'.format(str(kwargs.keys()))
def set_tower(self, nr_tower=None, tower=None):
# this is a deprecated function
# TODO Deprecate @ Mar 15
logger.warn("config.set_tower is deprecated. set config.tower or config.nr_tower directly")
log_deprecated("config.set_tower", "Set config.tower or config.nr_tower directly.", "2017-03-15")
assert nr_tower is None or tower is None, "Cannot set both nr_tower and tower!"
if nr_tower:
tower = list(range(nr_tower))
......
......@@ -5,7 +5,7 @@
import tensorflow as tf
from ..utils import logger
from ..utils import log_deprecated
from ..tfutils.tower import TowerContext
from ..tfutils.gradproc import apply_grad_processors
from .input_data import QueueInput, FeedfreeInput
......@@ -118,8 +118,7 @@ def QueueInputTrainer(config, input_queue=None, predict_tower=None):
"""
config.data = QueueInput(config.dataflow, input_queue)
if predict_tower is not None:
logger.warn("[Deprecated] Argument `predict_tower` is deprecated for trainer. "
"Use TrainConfig(predict_tower=...) instead!")
log_deprecated("Argument `predict_tower` in trainer", "Use TrainConfig(predict_tower=...) instead!")
config.predict_tower = predict_tower
assert len(config.tower) == 1, \
"QueueInputTrainer doesn't support multigpu! Use Sync/AsyncMultiGPUTrainer instead."
......
......@@ -8,7 +8,7 @@ import itertools
import re
from six.moves import zip, range
from ..utils import logger
from ..utils import logger, log_deprecated
from ..utils.naming import SUMMARY_BACKUP_KEYS
from ..utils.concurrency import LoopThread
from ..tfutils.tower import TowerContext
......@@ -93,8 +93,7 @@ class SyncMultiGPUTrainer(MultiGPUTrainer,
assert isinstance(self._input_method, QueueInput)
if predict_tower is not None:
logger.warn("[Deprecated] Argument `predict_tower` is deprecated for trainer. "
"Use TrainConfig.predict_tower instead!")
log_deprecated("Argument `predict_tower` in trainer", "Use TrainConfig(predict_tower=...) instead!")
config.predict_tower = predict_tower
super(SyncMultiGPUTrainer, self).__init__(config)
......@@ -188,8 +187,7 @@ class AsyncMultiGPUTrainer(MultiGPUTrainer,
super(AsyncMultiGPUTrainer, self).__init__(config)
if predict_tower is not None:
logger.warn("[Deprecated] Argument `predict_tower` is deprecated for trainer. "
"Use TrainConfig.predict_tower instead!")
log_deprecated("Argument `predict_tower` in trainer", "Use TrainConfig.predict_tower instead!")
config.predict_tower = predict_tower
self._setup_predictor_factory()
......
......@@ -9,6 +9,8 @@ import inspect
from datetime import datetime
from tqdm import tqdm
import numpy as np
import functools
from . import logger
__all__ = ['change_env',
......@@ -17,6 +19,8 @@ __all__ = ['change_env',
'get_tqdm',
'execute_only_once',
'building_rtfd',
'log_deprecated',
'deprecated'
]
......@@ -62,8 +66,7 @@ def execute_only_once():
first time and False afterwards.
Returns:
bool: whether this is the first time this function gets called from
this line of code.
bool: whether this is the first time this function gets called from this line of code.
Example:
.. code-block:: python
......@@ -116,3 +119,64 @@ def building_rtfd():
"""
return os.environ.get('READTHEDOCS') == 'True' \
or os.environ.get('TENSORPACK_DOC_BUILDING')
def log_deprecated(name="", text="", eos=""):
"""
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".
"""
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 on %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
logger.warn("[Deprecated] " + warn_msg)
def deprecated(text="", eos=""):
"""
Args:
text, eos: 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)
return func(*args, **kwargs)
return new_func
return deprecated_inner
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment