From 1652bd1173d60fc0d37309b73cea3e587e14a31f Mon Sep 17 00:00:00 2001 From: dinhlongnguyen Date: Thu, 12 Oct 2023 03:16:34 +0700 Subject: [PATCH] Fix state access within notebook (#968) (#969) --- src/taipy/gui/state.py | 20 ++++++- tests/taipy/gui/notebook/simple_gui.ipynb | 59 ++++++++++++++----- .../gui/notebook/test_notebook_simple_gui.py | 10 ++++ 3 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src/taipy/gui/state.py b/src/taipy/gui/state.py index 65f78d442..b83d35797 100644 --- a/src/taipy/gui/state.py +++ b/src/taipy/gui/state.py @@ -14,7 +14,9 @@ from operator import attrgetter from types import FrameType -from .utils import _get_module_name_from_frame +from flask import has_app_context + +from .utils import _get_module_name_from_frame, _is_in_notebook from .utils._attributes import _attrsetter if t.TYPE_CHECKING: @@ -125,6 +127,14 @@ def __getattribute__(self, name: str) -> t.Any: raise AttributeError(f"Variable '{name}' is not available to be accessed in shared callback.") if name not in super().__getattribute__(State.__attrs[1]): raise AttributeError(f"Variable '{name}' is not defined.") + if not has_app_context() and _is_in_notebook(): + with gui.get_flask_app().app_context(): + # Code duplication is ugly but necessary due to frame resolution + set_context = self._set_context(gui) + encoded_name = gui._bind_var(name) + attr = getattr(gui._bindings(), encoded_name) + self._reset_context(gui, set_context) + return attr set_context = self._set_context(gui) encoded_name = gui._bind_var(name) attr = getattr(gui._bindings(), encoded_name) @@ -139,6 +149,14 @@ def __setattr__(self, name: str, value: t.Any) -> None: raise AttributeError(f"Variable '{name}' is not available to be accessed in shared callback.") if name not in super().__getattribute__(State.__attrs[1]): raise AttributeError(f"Variable '{name}' is not accessible.") + if not has_app_context() and _is_in_notebook(): + with gui.get_flask_app().app_context(): + # Code duplication is ugly but necessary due to frame resolution + set_context = self._set_context(gui) + encoded_name = gui._bind_var(name) + setattr(gui._bindings(), encoded_name, value) + self._reset_context(gui, set_context) + return set_context = self._set_context(gui) encoded_name = gui._bind_var(name) setattr(gui._bindings(), encoded_name, value) diff --git a/tests/taipy/gui/notebook/simple_gui.ipynb b/tests/taipy/gui/notebook/simple_gui.ipynb index dac004a92..ea72862cf 100644 --- a/tests/taipy/gui/notebook/simple_gui.ipynb +++ b/tests/taipy/gui/notebook/simple_gui.ipynb @@ -9,19 +9,7 @@ "import" ] }, - "outputs": [ - { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'taipy_proxy.gui'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtaipy_proxy\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mgui\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Gui, Markdown\n", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'taipy_proxy.gui'" - ] - } - ], + "outputs": [], "source": [ "from importlib.util import find_spec\n", "\n", @@ -42,7 +30,8 @@ }, "outputs": [], "source": [ - "page = Markdown(\"# Hello\")" + "a = 10\n", + "page = Markdown(\"# Hello\\n<|{a}|>\")" ] }, { @@ -74,6 +63,48 @@ "gui.run(run_browser=False)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b722848", + "metadata": { + "tags": [ + "get_variable" + ] + }, + "outputs": [], + "source": [ + "gui.state.a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12e0df89", + "metadata": { + "tags": [ + "set_variable" + ] + }, + "outputs": [], + "source": [ + "gui.state.a = 20" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8332b3b5", + "metadata": { + "tags": [ + "re_get_variable" + ] + }, + "outputs": [], + "source": [ + "gui.state.a" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/tests/taipy/gui/notebook/test_notebook_simple_gui.py b/tests/taipy/gui/notebook/test_notebook_simple_gui.py index 9b2554621..948eb3010 100644 --- a/tests/taipy/gui/notebook/test_notebook_simple_gui.py +++ b/tests/taipy/gui/notebook/test_notebook_simple_gui.py @@ -27,6 +27,16 @@ def test_notebook_simple_gui(tb, helpers): while not helpers.port_check(): time.sleep(0.1) assert ">Hello" in urlopen("http://127.0.0.1:5000/taipy-jsx/page1").read().decode("utf-8") + assert 'defaultValue=\\"10\\"' in urlopen("http://127.0.0.1:5000/taipy-jsx/page1").read().decode("utf-8") + # Test state manipulation within notebook + tb.execute_cell("get_variable") + assert "10" in tb.cell_output_text("get_variable") + assert 'defaultValue=\\"10\\"' in urlopen("http://127.0.0.1:5000/taipy-jsx/page1").read().decode("utf-8") + tb.execute_cell("set_variable") + assert 'defaultValue=\\"20\\"' in urlopen("http://127.0.0.1:5000/taipy-jsx/page1").read().decode("utf-8") + tb.execute_cell("re_get_variable") + assert "20" in tb.cell_output_text("re_get_variable") + # Test page reload tb.execute_cell("gui_stop") with pytest.raises(Exception) as exc_info: urlopen("http://127.0.0.1:5000/taipy-jsx/page1")