Просмотр исходного кода

Show file and line number in deprecation warnings (#4631)

* Show file and line number in deprecation warnings

* Exclude modules/packages by import

Less bespoke method of considering some packages to be part of the framework
and passed over when finding user code.
Masen Furer 4 месяцев назад
Родитель
Сommit
fbf9524a6c
2 измененных файлов с 49 добавлено и 5 удалено
  1. 47 3
      reflex/utils/console.py
  2. 2 2
      reflex/vars/base.py

+ 47 - 3
reflex/utils/console.py

@@ -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):

+ 2 - 2
reflex/vars/base.py

@@ -561,7 +561,7 @@ class Var(Generic[VAR_TYPE]):
         if _var_is_local is not None:
             console.deprecate(
                 feature_name="_var_is_local",
-                reason="The _var_is_local argument is not supported for Var."
+                reason="The _var_is_local argument is not supported for Var. "
                 "If you want to create a Var from a raw Javascript expression, use the constructor directly",
                 deprecation_version="0.6.0",
                 removal_version="0.7.0",
@@ -569,7 +569,7 @@ class Var(Generic[VAR_TYPE]):
         if _var_is_string is not None:
             console.deprecate(
                 feature_name="_var_is_string",
-                reason="The _var_is_string argument is not supported for Var."
+                reason="The _var_is_string argument is not supported for Var. "
                 "If you want to create a Var from a raw Javascript expression, use the constructor directly",
                 deprecation_version="0.6.0",
                 removal_version="0.7.0",