Enable registering of legacy attributes without exact substitutes (#1438)

* Enable registering of legacy attributes without...

... exact substitutes. (See diff for an example.)

* Take new callable instead of old name ...

... so we can ensure existence

* Require old names to be passed as key words

This is a lot simpler, less error prone, and works for all kinds of old
names, not only those which are proper camelcase.
This commit is contained in:
RumovZ 2021-10-20 10:13:55 +02:00 committed by GitHub
parent 4678b12570
commit 54df350cda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -44,15 +44,21 @@ class DeprecatedNamesMixin:
# attributes on the consuming class # attributes on the consuming class
_deprecated_aliases: dict[str, str] = {} _deprecated_aliases: dict[str, str] = {}
_deprecated_attributes: dict[str, tuple[str, str]] = {}
@no_type_check @no_type_check
def __getattr__(self, name: str) -> Any: def __getattr__(self, name: str) -> Any:
remapped = self._deprecated_aliases.get(name) or stringcase.snakecase(name) if some_tuple := self._deprecated_attributes.get(name):
remapped, replacement = some_tuple
else:
replacement = remapped = self._deprecated_aliases.get(
name
) or stringcase.snakecase(name)
if remapped == name: if remapped == name:
raise AttributeError raise AttributeError
out = getattr(self, remapped) out = getattr(self, remapped)
_print_warning(f"'{name}'", f"please use '{remapped}'") _print_warning(f"'{name}'", f"please use '{replacement}'")
return out return out
@ -67,6 +73,28 @@ class DeprecatedNamesMixin:
""" """
cls._deprecated_aliases = {k: _target_to_string(v) for k, v in kwargs.items()} cls._deprecated_aliases = {k: _target_to_string(v) for k, v in kwargs.items()}
@no_type_check
@classmethod
def register_deprecated_attributes(
cls,
**kwargs: tuple[DeprecatedAliasTarget, DeprecatedAliasTarget],
) -> None:
"""Manually add deprecated attributes without exact substitutes.
Pass a tuple of (alias, replacement), where alias is the attribute's new
name (by convention: snakecase, prepended with '_legacy_'), and
replacement is any callable to be used instead in new code.
Also note the docstring of `register_deprecated_aliases`.
E.g. given `def oldFunc(args): return new_func(additionalLogic(args))`,
rename `oldFunc` to `_legacy_old_func` and call
`register_deprecated_attributes(oldFunc=(_legacy_old_func, new_func))`.
"""
cls._deprecated_attributes = {
k: (_target_to_string(v[0]), _target_to_string(v[1]))
for k, v in kwargs.items()
}
def deprecated(replaced_by: Callable | None = None, info: str = "") -> Callable: def deprecated(replaced_by: Callable | None = None, info: str = "") -> Callable:
"""Print a deprecation warning, telling users to use `replaced_by`, or show `doc`.""" """Print a deprecation warning, telling users to use `replaced_by`, or show `doc`."""