|
@@ -2,6 +2,11 @@
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
+import inspect
|
|
|
+import shutil
|
|
|
+from pathlib import Path
|
|
|
+from types import FrameType
|
|
|
+
|
|
|
from rich.console import Console
|
|
|
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
|
|
from rich.prompt import Prompt
|
|
@@ -188,6 +193,33 @@ def warn(msg: str, dedupe: bool = False, **kwargs):
|
|
|
print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
|
|
|
|
|
|
|
|
|
+def _get_first_non_framework_frame() -> FrameType | None:
|
|
|
+ import click
|
|
|
+ import typer
|
|
|
+ import typing_extensions
|
|
|
+
|
|
|
+ import reflex as rx
|
|
|
+
|
|
|
+ # Exclude utility modules that should never be the source of deprecated reflex usage.
|
|
|
+ exclude_modules = [click, rx, typer, typing_extensions]
|
|
|
+ exclude_roots = [
|
|
|
+ p.parent.resolve()
|
|
|
+ if (p := Path(m.__file__)).name == "__init__.py"
|
|
|
+ else p.resolve()
|
|
|
+ for m in exclude_modules
|
|
|
+ ]
|
|
|
+ # Specifically exclude the reflex cli module.
|
|
|
+ if reflex_bin := shutil.which(b"reflex"):
|
|
|
+ exclude_roots.append(Path(reflex_bin.decode()))
|
|
|
+
|
|
|
+ frame = inspect.currentframe()
|
|
|
+ while frame := frame and frame.f_back:
|
|
|
+ frame_path = Path(inspect.getfile(frame)).resolve()
|
|
|
+ if not any(frame_path.is_relative_to(root) for root in exclude_roots):
|
|
|
+ break
|
|
|
+ return frame
|
|
|
+
|
|
|
+
|
|
|
def deprecate(
|
|
|
feature_name: str,
|
|
|
reason: str,
|
|
@@ -206,15 +238,27 @@ def deprecate(
|
|
|
dedupe: If True, suppress multiple console logs of deprecation message.
|
|
|
kwargs: Keyword arguments to pass to the print function.
|
|
|
"""
|
|
|
- if feature_name not in _EMITTED_DEPRECATION_WARNINGS:
|
|
|
+ dedupe_key = feature_name
|
|
|
+ loc = ""
|
|
|
+
|
|
|
+ # See if we can find where the deprecation exists in "user code"
|
|
|
+ origin_frame = _get_first_non_framework_frame()
|
|
|
+ if origin_frame is not None:
|
|
|
+ filename = Path(origin_frame.f_code.co_filename)
|
|
|
+ if filename.is_relative_to(Path.cwd()):
|
|
|
+ filename = filename.relative_to(Path.cwd())
|
|
|
+ loc = f"{filename}:{origin_frame.f_lineno}"
|
|
|
+ dedupe_key = f"{dedupe_key} {loc}"
|
|
|
+
|
|
|
+ if dedupe_key not in _EMITTED_DEPRECATION_WARNINGS:
|
|
|
msg = (
|
|
|
f"{feature_name} has been deprecated in version {deprecation_version} {reason.rstrip('.')}. It will be completely "
|
|
|
- f"removed in {removal_version}"
|
|
|
+ f"removed in {removal_version}. ({loc})"
|
|
|
)
|
|
|
if _LOG_LEVEL <= LogLevel.WARNING:
|
|
|
print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
|
|
|
if dedupe:
|
|
|
- _EMITTED_DEPRECATION_WARNINGS.add(feature_name)
|
|
|
+ _EMITTED_DEPRECATION_WARNINGS.add(dedupe_key)
|
|
|
|
|
|
|
|
|
def error(msg: str, dedupe: bool = False, **kwargs):
|