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.
This commit is contained in:
Masen Furer 2025-01-15 14:19:11 -08:00 committed by GitHub
parent caf29c3680
commit fbf9524a6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 5 deletions

View File

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

View File

@ -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",