Skip to content

Commit

Permalink
improve exception reporting in expressions (#2156)
Browse files Browse the repository at this point in the history
* improve exception reporting in expressions
resolves #2144

* linter

* fix test

* better fix test

---------

Co-authored-by: Fred Lefévère-Laoide <[email protected]>
  • Loading branch information
FredLL-Avaiga and Fred Lefévère-Laoide authored Oct 25, 2024
1 parent 9c0119f commit 5e58fd1
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 10 deletions.
12 changes: 11 additions & 1 deletion taipy/gui/_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import typing as t
import warnings

from ._warnings import TaipyGuiAlwaysWarning

if t.TYPE_CHECKING:
from ._renderers import Page
from .gui import Gui
Expand All @@ -40,7 +42,15 @@ def render(self, gui: Gui, silent: t.Optional[bool] = False):
warnings.resetwarnings()
with gui._set_locals_context(self._renderer._get_module_name()):
self._rendered_jsx = self._renderer.render(gui)
if not silent:
if silent:
s = ""
for wm in w:
if wm.category is TaipyGuiAlwaysWarning:
s += f" - {wm.message}\n"
if s:
logging.warning("\033[1;31m\n" + s)

else:
if (
self._rendered_jsx
and isinstance(self._rendered_jsx, str)
Expand Down
17 changes: 13 additions & 4 deletions taipy/gui/_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,24 @@ def set_debug_mode(debug_mode: bool):
)


def _warn(message: str, e: t.Optional[BaseException] = None):
class TaipyGuiAlwaysWarning(TaipyGuiWarning):
pass


def _warn(
message: str,
e: t.Optional[BaseException] = None,
always_show: t.Optional[bool] = False,
):
warnings.warn(
(
f"{message}:\n{''.join(traceback.format_exception(type(e), e, e.__traceback__))}"
f"{message}:\n{''.join(traceback.format_exception(e))}"
if e and TaipyGuiWarning._tp_debug_mode
else f"{message}:\n{e}"
else f"{message}:\n"
+ "".join(traceback.format_exception(None, e, e.__traceback__.tb_next if e.__traceback__ else None))
if e
else message
),
TaipyGuiWarning,
TaipyGuiWarning if not always_show else TaipyGuiAlwaysWarning,
stacklevel=2,
)
16 changes: 13 additions & 3 deletions taipy/gui/utils/_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class _Evaluator:
__EXPR_EDGE_CASE_F_STRING = re.compile(r"[\{]*[a-zA-Z_][a-zA-Z0-9_]*:.+")
__IS_TAIPY_EXPR_RE = re.compile(r"TpExPr_(.*)")
__IS_ARRAY_EXPR_RE = re.compile(r"[^[]*\[(\d+)][^]]*")
__CLEAN_LAMBDA_RE = re.compile(r"^__lambda_[\d_]+(TPMDL_\d+)?(.*)$")

def __init__(self, default_bindings: t.Dict[str, t.Any], shared_variable: t.List[str]) -> None:
# key = expression, value = hashed value of the expression
Expand Down Expand Up @@ -260,7 +261,12 @@ def evaluate_expr(
with gui._get_authorization():
expr_evaluated = eval(not_encoded_expr if is_edge_case else expr_string, ctx)
except Exception as e:
_warn(f"Cannot evaluate expression '{not_encoded_expr if is_edge_case else expr_string}'", e)
exception_str = not_encoded_expr if is_edge_case else expr_string
_warn(
f"Cannot evaluate expression '{_Evaluator._clean_exception_expr(exception_str)}'",
e,
always_show=True,
)
expr_evaluated = None
if lambda_expr and callable(expr_evaluated):
expr_hash = _get_lambda_id(expr_evaluated, module=module_name) # type: ignore[reportArgumentType]
Expand Down Expand Up @@ -291,7 +297,7 @@ def refresh_expr(self, gui: Gui, var_name: str, holder: t.Optional[_TaipyBase]):
if holder is not None:
holder.set(expr_evaluated)
except Exception as e:
_warn(f"Exception raised evaluating {expr_string}", e)
_warn(f"Exception raised evaluating {_Evaluator._clean_exception_expr(expr_string)}", e)

def re_evaluate_expr(self, gui: Gui, var_name: str) -> t.Set[str]: # noqa C901
"""
Expand Down Expand Up @@ -366,7 +372,7 @@ def re_evaluate_expr(self, gui: Gui, var_name: str) -> t.Set[str]: # noqa C901
expr_evaluated = eval(expr_string, ctx)
_setscopeattr(gui, hash_expr, expr_evaluated)
except Exception as e:
_warn(f"Exception raised evaluating {expr_string}", e)
_warn(f"Exception raised evaluating {_Evaluator._clean_exception_expr(expr_string)}", e)
# refresh holders if any
for h in self.__expr_to_holders.get(expr, []):
holder_hash = self.__get_holder_hash(h, self.get_hash_from_expr(expr))
Expand All @@ -378,3 +384,7 @@ def re_evaluate_expr(self, gui: Gui, var_name: str) -> t.Set[str]: # noqa C901

def _get_instance_in_context(self, name: str):
return self.__global_ctx.get(name)

@staticmethod
def _clean_exception_expr(expr: str):
return _Evaluator.__CLEAN_LAMBDA_RE.sub(r"<lambda>\2", expr)
2 changes: 1 addition & 1 deletion tests/gui/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,4 @@ def run_e2e_multi_client(gui: Gui):

@staticmethod
def get_taipy_warnings(warns: t.List[warnings.WarningMessage]) -> t.List[warnings.WarningMessage]:
return [w for w in warns if w.category is TaipyGuiWarning]
return [w for w in warns if issubclass(w.category, TaipyGuiWarning)]
2 changes: 1 addition & 1 deletion tools/release/bump_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,5 @@ def bump_ext_version(version: Version, _base_path: str) -> None:
for _path in paths:
_version = extract_version(_path)
bump_ext_version(_version, _path)
print(f"NEW_VERSION={_version.dev_name}")
print(f"NEW_VERSION={_version.dev_name}") # noqa T201 # type: ignore[reportPossiblyUnboundVariable]

0 comments on commit 5e58fd1

Please sign in to comment.