diff options
Diffstat (limited to 'cloudinit/util.py')
-rw-r--r-- | cloudinit/util.py | 83 |
1 files changed, 82 insertions, 1 deletions
diff --git a/cloudinit/util.py b/cloudinit/util.py index 8ba3e2b6..aed332ec 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -11,6 +11,7 @@ import contextlib import copy as obj_copy import email +import functools import glob import grp import gzip @@ -36,7 +37,7 @@ from collections import deque, namedtuple from errno import EACCES, ENOENT from functools import lru_cache, total_ordering from pathlib import Path -from typing import Callable, Deque, Dict, List, TypeVar +from typing import Callable, Deque, Dict, List, Optional, TypeVar from urllib import parse from cloudinit import features, importer @@ -3097,3 +3098,83 @@ class Version(namedtuple("Version", ["major", "minor", "patch", "rev"])): if self.rev > other.rev: return 1 return -1 + + +def deprecate( + *, + deprecated: str, + deprecated_version: str, + extra_message: Optional[str] = None, + schedule: int = 5, +): + """Mark a "thing" as deprecated. Deduplicated deprecations are + logged. + + @param deprecated: Noun to be deprecated. Write this as the start + of a sentence, with no period. Version and extra message will + be appended. + @param deprecated_version: The version in which the thing was + deprecated + @param extra_message: A remedy for the user's problem. A good + message will be actionable and specific (i.e., don't use a + generic "Use updated key." if the user used a deprecated key). + End the string with a period. + @param schedule: Manually set the deprecation schedule. Defaults to + 5 years. Leave a comment explaining your reason for deviation if + setting this value. + + Note: uses keyword-only arguments to improve legibility + """ + if not hasattr(deprecate, "_log"): + deprecate._log = set() # type: ignore + message = extra_message or "" + dedup = hash(deprecated + message + deprecated_version + str(schedule)) + version = Version.from_str(deprecated_version) + version_removed = Version(version.major + schedule, version.minor) + if dedup not in deprecate._log: # type: ignore + deprecate._log.add(dedup) # type: ignore + deprecate_msg = ( + f"{deprecated} is deprecated in " + f"{deprecated_version} and scheduled to be removed in " + f"{version_removed}. {message}" + ).rstrip() + if hasattr(LOG, "deprecated"): + LOG.deprecated(deprecate_msg) + else: + LOG.warning(deprecate_msg) + + +def deprecate_call( + *, deprecated_version: str, extra_message: str, schedule: int = 5 +): + """Mark a "thing" as deprecated. Deduplicated deprecations are + logged. + + @param deprecated_version: The version in which the thing was + deprecated + @param extra_message: A remedy for the user's problem. A good + message will be actionable and specific (i.e., don't use a + generic "Use updated key." if the user used a deprecated key). + End the string with a period. + @param schedule: Manually set the deprecation schedule. Defaults to + 5 years. Leave a comment explaining your reason for deviation if + setting this value. + + Note: uses keyword-only arguments to improve legibility + """ + + def wrapper(func): + @functools.wraps(func) + def decorator(*args, **kwargs): + # don't log message multiple times + out = func(*args, **kwargs) + deprecate( + deprecated_version=deprecated_version, + deprecated=func.__name__, + extra_message=extra_message, + ) + return out + + return decorator + + return wrapper |