Skip to content
This repository has been archived by the owner on Feb 15, 2024. It is now read-only.

Commit

Permalink
allow optional lov_value property (for taipy) (#970)
Browse files Browse the repository at this point in the history
* allow optional lov_value property (for taipy)

* black

* increase coverage

* black

* warning

---------

Co-authored-by: Fred Lefévère-Laoide <[email protected]>
  • Loading branch information
FredLL-Avaiga and Fred Lefévère-Laoide authored Oct 12, 2023
1 parent 6f94aa9 commit 442001a
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 5 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
omit =
proxy.py
_gui_cli.py
*tests*
[report]
exclude_lines =
pragma: no cover
Expand Down
11 changes: 8 additions & 3 deletions src/taipy/gui/_renderers/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -835,10 +835,13 @@ def __set_dynamic_bool_attribute(self, name: str, def_val: t.Any, with_update: b
else:
self.__update_vars.append(f"{_to_camel_case(name)}={hash_name}")

def __set_dynamic_property_without_default(self, name: str, property_type: PropertyType):
def __set_dynamic_property_without_default(
self, name: str, property_type: PropertyType, optional: t.Optional[bool] = False
):
hash_name = self.__hashes.get(name)
if hash_name is None:
_warn(f"{self.__element_name}.{name} should be bound.")
if not optional:
_warn(f"{self.__element_name}.{name} should be bound.")
else:
hash_name = self.__get_typed_hash_name(hash_name, property_type)
self.__update_vars.append(f"{_to_camel_case(name)}={hash_name}")
Expand Down Expand Up @@ -916,7 +919,9 @@ def set_attributes(self, attributes: t.List[tuple]): # noqa: C901
self._get_adapter(attr[0]) # need to be called before set_lov
self._set_lov(attr[0])
elif var_type == PropertyType.lov_value:
self.__set_dynamic_property_without_default(attr[0], var_type)
self.__set_dynamic_property_without_default(
attr[0], var_type, _get_tuple_val(attr, 2, None) == "optional"
)
elif isclass(var_type) and issubclass(var_type, _TaipyBase):
if hash_name := self.__hashes.get(attr[0]):
prop_name = _to_camel_case(attr[0])
Expand Down
4 changes: 2 additions & 2 deletions src/taipy/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
from ._renderers.utils import _get_columns_dict
from ._warnings import TaipyGuiWarning, _warn
from .builder import _ElementApiGenerator
from .config import Config, ConfigParameter, ServerConfig, Stylekit, _Config
from .config import Config, ConfigParameter, _Config
from .data.content_accessor import _ContentAccessor
from .data.data_accessor import _DataAccessor, _DataAccessors
from .data.data_format import _DataFormat
Expand Down Expand Up @@ -1158,7 +1158,7 @@ def _call_broadcast_callback(
callback_result = self._call_function_with_state(user_callback, args)
setattr(g, Gui.__BRDCST_CALLBACK_G_ID, False)
return callback_result
except Exception as e: # pragma: no cover
except Exception as e:
if not self._call_on_exception(user_callback.__name__, e):
_warn(f"invoke_callback(): Exception raised in '{user_callback.__name__}()':\n{e}")
return None
Expand Down
30 changes: 30 additions & 0 deletions tests/taipy/gui/extension/test_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# specific language governing permissions and limitations under the License.
import inspect
import typing as t
import pytest
from pathlib import Path

from taipy.gui import Gui
Expand Down Expand Up @@ -73,6 +74,22 @@ def get_resource(self, name: str) -> Path:
return Path(name)


class MyBadLibrary(ElementLibrary):
def get_name(self) -> str:
return "bad name"

def get_elements(self) -> t.Dict[str, Element]:
return {}


class MyGoodLibrary(ElementLibrary):
def get_name(self) -> str:
return "test_lib"

def get_elements(self) -> t.Dict[str, Element]:
return {}


Gui.add_library(MyLibrary())


Expand Down Expand Up @@ -152,3 +169,16 @@ def test_lib_inner_no_value_md(gui: Gui, test_client, helpers):
md_string = "<|test_lib.inner|>"
expected = ["<TestLib_Inner", "withProperty={tpec_TpExPr_None_TPMDL_0}"]
helpers.test_control_md(gui, md_string, expected)


def test_lib_bad_name():
with pytest.raises(NameError):
Gui.add_library(MyBadLibrary())


def test_lib_good_name():
Gui.add_library(MyGoodLibrary())


def test_add_lib():
Gui(libraries=[MyGoodLibrary()])
92 changes: 92 additions & 0 deletions tests/taipy/gui/gui_specific/test_gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Copyright 2023 Avaiga Private Limited
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

import pytest
from taipy.gui import Gui
from taipy.gui.utils import _TaipyContent
import pandas as pd
import json


def test__get_real_var_name(gui: Gui):
res = gui._get_real_var_name("")
assert isinstance(res, tuple)
assert res[0] == ""
assert res[1] == ""

gui.run(run_server=False)
with gui.get_flask_app().app_context():
with pytest.raises(NameError):
res = gui._get_real_var_name(f"{_TaipyContent.get_hash()}_var")


def test__get_user_instance(gui: Gui):
gui.run(run_server=False)
with gui.get_flask_app().app_context():
with pytest.warns(UserWarning):
gui._get_user_instance("", type(None))


def test__call_broadcast_callback(gui: Gui):
gui.run(run_server=False)
with gui.get_flask_app().app_context():
res = gui._call_broadcast_callback(lambda s, t: t, ["Hello World"], "mine")
assert res == "Hello World"

with gui.get_flask_app().app_context():
with pytest.warns(UserWarning):
res = gui._call_broadcast_callback(print, ["Hello World"], "mine")
assert res is None


def test__refresh_expr(gui: Gui):
gui.run(run_server=False)
with gui.get_flask_app().app_context():
res = gui._refresh_expr("var", None)
assert res is None


def test__tbl_cols(gui: Gui):
data = pd.DataFrame({"col1": [0, 1, 2], "col2": [True, True, False]})
gui.run(run_server=False)
with gui.get_flask_app().app_context():
res = gui._tbl_cols(True, None, json.dumps({}), json.dumps({"data": "data"}), data=data)
d = json.loads(res)
assert isinstance(d, dict)
assert d["col1"]["type"] == "int"

res = gui._tbl_cols(False, None, "", "")
assert repr(res) == "Taipy: Do not update"


def test__chart_conf(gui: Gui):
data = pd.DataFrame({"col1": [0, 1, 2], "col2": [True, True, False]})
gui.run(run_server=False)
with gui.get_flask_app().app_context():
res = gui._chart_conf(True, None, json.dumps({}), json.dumps({"data": "data"}), data=data)
d = json.loads(res)
assert isinstance(d, dict)
assert d["columns"]["col1"]["type"] == "int"

res = gui._chart_conf(False, None, "", "")
assert repr(res) == "Taipy: Do not update"

with pytest.warns(UserWarning):
res = gui._chart_conf(True, None, "", "")
assert repr(res) == "Taipy: Do not update"


def test__get_valid_adapter_result(gui: Gui):
gui.run(run_server=False)
with gui.get_flask_app().app_context():
res = gui._get_valid_adapter_result(("id", "label"))
assert isinstance(res, tuple)
assert res[0] == "id"
22 changes: 22 additions & 0 deletions tests/taipy/gui/gui_specific/test_shared.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2023 Avaiga Private Limited
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

import pytest
from taipy.gui import Gui


def test_add_shared_variables(gui: Gui):
Gui.add_shared_variable("var1", "var2")
assert isinstance(gui._Gui__shared_variables, list)
assert len(gui._Gui__shared_variables) == 2

Gui.add_shared_variables("var1", "var2")
assert len(gui._Gui__shared_variables) == 2

0 comments on commit 442001a

Please sign in to comment.