Skip to content

Commit

Permalink
Show file and line number in deprecation warnings (#4631)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
masenf authored Jan 15, 2025
1 parent caf29c3 commit fbf9524
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 5 deletions.
50 changes: 47 additions & 3 deletions reflex/utils/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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):
Expand Down
4 changes: 2 additions & 2 deletions reflex/vars/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,15 +561,15 @@ def create(
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",
)
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",
Expand Down

0 comments on commit fbf9524

Please sign in to comment.