Skip to content

Managing Deprecation using decorators #1585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Feb 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions ignite/contrib/engines/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from ignite.handlers import Checkpoint, DiskSaver, EarlyStopping, TerminateOnNan
from ignite.handlers.checkpoint import BaseSaveHandler
from ignite.metrics import RunningAverage
from ignite.utils import deprecated


def setup_common_training_handlers(
Expand Down Expand Up @@ -274,11 +275,21 @@ def empty_cuda_cache(_: Engine) -> None:
gc.collect()


def setup_any_logging(logger, logger_module, trainer, optimizers, evaluators, log_every_iters) -> None: # type: ignore
raise DeprecationWarning(
"ignite.contrib.engines.common.setup_any_logging is deprecated since 0.4.0. and will be remove in 0.6.0. "
"Please use instead: setup_tb_logging, setup_visdom_logging or setup_mlflow_logging etc."
)
@deprecated(
"0.4.0",
"0.6.0",
("Please use instead: setup_tb_logging, setup_visdom_logging or setup_mlflow_logging etc.",),
raise_exception=True,
)
def setup_any_logging(
logger: BaseLogger,
logger_module: Any,
trainer: Engine,
optimizers: Optional[Union[Optimizer, Dict[str, Optimizer], Dict[None, Optimizer]]],
evaluators: Optional[Union[Engine, Dict[str, Engine]]],
log_every_iters: int,
) -> None:
pass


def _setup_logging(
Expand Down
35 changes: 34 additions & 1 deletion ignite/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import collections.abc as collections
import functools
import logging
import random
import sys
from typing import Any, Callable, Optional, TextIO, Tuple, Type, Union, cast
import warnings
from typing import Any, Callable, Dict, Optional, TextIO, Tuple, Type, TypeVar, Union, cast

import torch

Expand Down Expand Up @@ -171,3 +173,34 @@ def manual_seed(seed: int) -> None:
np.random.seed(seed)
except ImportError:
pass


def deprecated(
deprecated_in: str, removed_in: str = "", reasons: Tuple[str, ...] = (), raise_exception: bool = False
) -> Callable:

F = TypeVar("F", bound=Callable[..., Any])

def decorator(func: F) -> F:
func_doc = func.__doc__ if func.__doc__ else ""
deprecation_warning = (
f"This function has been deprecated since version {deprecated_in}"
+ (f" and will be removed in version {removed_in}" if removed_in else "")
+ ".\n Please refer to the documentation for more details."
)

@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Dict[str, Any]) -> Callable:
if raise_exception:
raise DeprecationWarning(deprecation_warning)
warnings.warn(deprecation_warning, DeprecationWarning, stacklevel=2)
return func(*args, **kwargs)

appended_doc = f".. deprecated:: {deprecated_in}" + ("\n\n\t" if len(reasons) else "")

for reason in reasons:
appended_doc += "\n\t- " + reason
wrapper.__doc__ = f"**Deprecated function**.\n\n {func_doc}{appended_doc}"
return cast(F, wrapper)

return decorator
2 changes: 1 addition & 1 deletion tests/ignite/contrib/engines/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ def set_eval_metric(engine):

def test_deprecated_setup_any_logging():

with pytest.raises(DeprecationWarning, match=r"is deprecated since 0\.4\.0\."):
with pytest.raises(DeprecationWarning, match=r"deprecated since version 0.4.0"):
setup_any_logging(None, None, None, None, None, None)


Expand Down
72 changes: 71 additions & 1 deletion tests/ignite/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import logging
import os
import sys
import warnings
from collections import namedtuple

import pytest
import torch

from ignite.engine import Engine, Events
from ignite.utils import convert_tensor, setup_logger, to_onehot
from ignite.utils import convert_tensor, deprecated, setup_logger, to_onehot


def test_convert_tensor():
Expand Down Expand Up @@ -150,3 +151,72 @@ def _(_):

# Needed by windows to release FileHandler in the loggers
logging.shutdown()


def test_deprecated():

# Test on function without docs, @deprecated without reasons
@deprecated("0.4.2", "0.6.0")
def func_no_docs():
return 24

assert func_no_docs.__doc__ == "**Deprecated function**.\n\n .. deprecated:: 0.4.2"

# Test on function with docs, @deprecated without reasons
@deprecated("0.4.2", "0.6.0")
def func_no_reasons():
"""Docs are cool
"""
return 24

assert func_no_reasons.__doc__ == "**Deprecated function**.\n\n Docs are cool\n .. deprecated:: 0.4.2"

# Test on function with docs, @deprecated with reasons
@deprecated("0.4.2", "0.6.0", reasons=("r1", "r2"))
def func_no_warnings():
"""Docs are very cool
"""
return 24

assert (
func_no_warnings.__doc__
== "**Deprecated function**.\n\n Docs are very cool\n .. deprecated:: 0.4.2\n\n\t\n\t- r1\n\t- r2"
)

# Tests that the function emits DeprecationWarning
@deprecated("0.4.2", "0.6.0", reasons=("r1", "r2"))
def func_check_warning():
"""Docs are very ...
"""
return 24

with pytest.deprecated_call():
func_check_warning()
assert func_check_warning() == 24
with warnings.catch_warnings(record=True) as w:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
# Trigger a warning.
func_check_warning()
# Verify some things
assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning)
assert (
"This function has been deprecated since version 0.4.2 and will be removed in version 0.6.0."
+ "\n Please refer to the documentation for more details."
in str(w[-1].message)
)

# Test that the function raises Exception
@deprecated("0.4.2", "0.6.0", reasons=("reason1", "reason2"), raise_exception=True)
def func_with_everything():
return 1

with pytest.raises(Exception) as exec_info:
func_with_everything()

assert (
str(exec_info.value)
== "This function has been deprecated since version 0.4.2 and will be removed in version 0.6.0."
+ "\n Please refer to the documentation for more details."
)