mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-21 05:59:18 +00:00
feat: add more fine-grained deprecation warning control (#44307)
* feat: add more fine-grained deprecation warning control see: https://github.com/frappe/frappe/wiki/Deprecations * chore: ensure frappe deprecation dumpster is reused where possible; and loaded to parse PYTHONWARNINGS (!)
This commit is contained in:
@@ -5,7 +5,7 @@ This file is the final resting place (or should we say, "retirement home"?) for
|
|||||||
|
|
||||||
Each function or method that checks in here comes with its own personalized decorator, complete with:
|
Each function or method that checks in here comes with its own personalized decorator, complete with:
|
||||||
1. The date it was marked for deprecation (its "over the hill" birthday)
|
1. The date it was marked for deprecation (its "over the hill" birthday)
|
||||||
2. The ERPNext version in which it will be removed (its "graduation" to the great codebase in the sky)
|
2. The ERPNext version at the beginning of which it becomes an error and at the end of which it will be removed (its "graduation" to the great codebase in the sky)
|
||||||
3. A user-facing note on alternative solutions (its "parting wisdom")
|
3. A user-facing note on alternative solutions (its "parting wisdom")
|
||||||
|
|
||||||
Warning: The global namespace herein is more patched up than a sailor's favorite pair of jeans. Proceed with caution and a sense of humor!
|
Warning: The global namespace herein is more patched up than a sailor's favorite pair of jeans. Proceed with caution and a sense of humor!
|
||||||
@@ -15,52 +15,63 @@ Remember, deprecated doesn't mean useless - it just means these functions are en
|
|||||||
Enjoy your stay in the Deprecation Dumpster, where every function gets a second chance to shine (or at least, to not break everything).
|
Enjoy your stay in the Deprecation Dumpster, where every function gets a second chance to shine (or at least, to not break everything).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
from frappe.deprecation_dumpster import Color, _deprecated, colorize
|
||||||
def colorize(text, color_code):
|
|
||||||
if sys.stdout.isatty():
|
|
||||||
return f"\033[{color_code}m{text}\033[0m"
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
class Color:
|
# we use Warning because DeprecationWarning has python default filters which would exclude them from showing
|
||||||
RED = 91
|
# see also frappe.__init__ enabling them when a dev_server
|
||||||
YELLOW = 93
|
class ERPNextDeprecationError(Warning):
|
||||||
CYAN = 96
|
"""Deprecated feature in current version.
|
||||||
|
|
||||||
|
Raises an error by default but can be configured via PYTHONWARNINGS in an emergency.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ERPNextDeprecationWarning(Warning):
|
class ERPNextDeprecationWarning(Warning):
|
||||||
...
|
"""Deprecated feature in next version"""
|
||||||
|
|
||||||
|
|
||||||
try:
|
class PendingERPNextDeprecationWarning(ERPNextDeprecationWarning):
|
||||||
# since python 3.13, PEP 702
|
"""Deprecated feature in develop beyond next version.
|
||||||
from warnings import deprecated as _deprecated
|
|
||||||
except ImportError:
|
|
||||||
import functools
|
|
||||||
import warnings
|
|
||||||
from collections.abc import Callable
|
|
||||||
from typing import Optional, TypeVar, Union, overload
|
|
||||||
|
|
||||||
T = TypeVar("T", bound=Callable)
|
Warning ignored by default.
|
||||||
|
|
||||||
def _deprecated(message: str, category=ERPNextDeprecationWarning, stacklevel=1) -> Callable[[T], T]:
|
The deprecation decision may still be reverted or deferred at this stage.
|
||||||
def decorator(func: T) -> T:
|
Regardless, using the new variant is encouraged and stable.
|
||||||
@functools.wraps(func)
|
"""
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
if message:
|
|
||||||
warning_msg = f"{func.__name__} is deprecated.\n{message}"
|
|
||||||
else:
|
|
||||||
warning_msg = f"{func.__name__} is deprecated."
|
|
||||||
warnings.warn(warning_msg, category=category, stacklevel=stacklevel + 1)
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
wrapper.__deprecated__ = True # hint for the type checker
|
|
||||||
|
|
||||||
return decorator
|
warnings.simplefilter("error", ERPNextDeprecationError)
|
||||||
|
warnings.simplefilter("ignore", PendingERPNextDeprecationWarning)
|
||||||
|
|
||||||
|
|
||||||
|
class V15ERPNextDeprecationWarning(ERPNextDeprecationError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class V16ERPNextDeprecationWarning(ERPNextDeprecationWarning):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class V17ERPNextDeprecationWarning(PendingERPNextDeprecationWarning):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def __get_deprecation_class(graduation: str | None = None, class_name: str | None = None) -> type:
|
||||||
|
if graduation:
|
||||||
|
# Scrub the graduation string to ensure it's a valid class name
|
||||||
|
cleaned_graduation = re.sub(r"\W|^(?=\d)", "_", graduation.upper())
|
||||||
|
class_name = f"{cleaned_graduation}ERPNextDeprecationWarning"
|
||||||
|
current_module = sys.modules[__name__]
|
||||||
|
try:
|
||||||
|
return getattr(current_module, class_name)
|
||||||
|
except AttributeError:
|
||||||
|
return PendingDeprecationWarning
|
||||||
|
|
||||||
|
|
||||||
def deprecated(original: str, marked: str, graduation: str, msg: str, stacklevel: int = 1):
|
def deprecated(original: str, marked: str, graduation: str, msg: str, stacklevel: int = 1):
|
||||||
@@ -79,6 +90,7 @@ def deprecated(original: str, marked: str, graduation: str, msg: str, stacklevel
|
|||||||
wrapper = _deprecated(
|
wrapper = _deprecated(
|
||||||
colorize(f"It was marked on {marked} for removal from {graduation} with note: ", Color.RED)
|
colorize(f"It was marked on {marked} for removal from {graduation} with note: ", Color.RED)
|
||||||
+ colorize(f"{msg}", Color.YELLOW),
|
+ colorize(f"{msg}", Color.YELLOW),
|
||||||
|
category=__get_deprecation_class(graduation),
|
||||||
stacklevel=stacklevel,
|
stacklevel=stacklevel,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -103,7 +115,7 @@ def deprecation_warning(marked: str, graduation: str, msg: str):
|
|||||||
Color.RED,
|
Color.RED,
|
||||||
)
|
)
|
||||||
+ colorize(f"{msg}\n", Color.YELLOW),
|
+ colorize(f"{msg}\n", Color.YELLOW),
|
||||||
category=ERPNextDeprecationWarning,
|
category=__get_deprecation_class(graduation),
|
||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user