Avaiga / taipy

Turns Data and AI algorithms into production-ready web applications in no time.
https://www.taipy.io
Apache License 2.0
15.43k stars 1.86k forks source link

[πŸ› BUG] Issue using lambda when passing state variable to a method #2212

Open arcanaxion opened 1 week ago

arcanaxion commented 1 week ago

What went wrong? πŸ€”

I'm trying to use a lambda function in tgb.part.render with a role trait filter to hide something based on a user's credentials:

import taipy.gui.builder as tgb
from taipy import Gui
import taipy as tp
from taipy import Config
from taipy.auth import Credentials
from taipy.auth import AnyOf

Config.configure_authentication(protocol="taipy", roles={"Bob": ["admin"]})

is_admin = AnyOf(["admin"], True, False)
credentials = Credentials(user_name="guest", roles=[])

def on_init(state):
    state.credentials = tp.enterprise.gui.login(state, "Bob", "Bob")

with tgb.Page() as main_page:
    tgb.text(lambda credentials: is_admin.get_traits(credentials))
    with tgb.part(render=lambda credentials: is_admin.get_traits(credentials)):
        tgb.text("You are an admin")

if __name__ == "__main__":
    gui = Gui(main_page)
    gui.run(run_browser=False, debug=True, use_reloader=True)

I get the following error:

/home/project/.venv-enterprise-4.0/lib/python3.10/site-packages/taipy/gui/utils/_evaluator.py:369: TaipyGuiWarning: Exception raised evaluating __lambda_139622943681712_1_TPMDL_0(credentials):
Traceback (most recent call last):
  File "/home/project/.venv-enterprise-4.0/lib/python3.10/site-packages/taipy/gui/utils/_evaluator.py", line 366, in re_evaluate_expr
    expr_evaluated = eval(expr_string, ctx)
  File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not callable

  _warn(f"Exception raised evaluating {expr_string}", e)
/home/project/.venv-enterprise-4.0/lib/python3.10/site-packages/taipy/gui/utils/_evaluator.py:369: TaipyGuiWarning: Exception raised evaluating __lambda_139622943681712_2_TPMDL_0(credentials):
Traceback (most recent call last):
  File "/home/project/.venv-enterprise-4.0/lib/python3.10/site-packages/taipy/gui/utils/_evaluator.py", line 366, in re_evaluate_expr
    expr_evaluated = eval(expr_string, ctx)
  File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not callable

  _warn(f"Exception raised evaluating {expr_string}", e)

There is nothing shown on the webpage -- the tgb.text before the part block is also not shown.

Alternate community example

This code also does not work -- nothing is displayed on the webpage:

import taipy.gui.builder as tgb
from taipy import Gui
import numpy as np

rng = np.random.default_rng()
size = 1

with tgb.Page() as main_page:
    tgb.text(lambda size: rng.random(size).sum() > 0)
    with tgb.part(render=lambda size: rng.random(size).sum() > 0):
        tgb.text("Random sum is greater than 0")

if __name__ == "__main__":
    gui = Gui(main_page)
    gui.run(run_browser=False, debug=True, use_reloader=True)

With 4.0, the error is not printed in the console. I guess this is related to #2144.

With develop, I get the following error:

WARNING:root:
 - Cannot evaluate expression 'lambda size: Generator(PCG64) at 0x7FBAB6160C80.random(size).sum() > 0':
Traceback (most recent call last):
  File "/home/project/.venv-git-2024-11-05/lib/python3.10/site-packages/taipy/gui/utils/_evaluator.py", line 262, in evaluate_expr
    expr_evaluated = eval(not_encoded_expr if is_edge_case else expr_string, ctx)
  File "<string>", line 1
    lambda size: Generator(PCG64) at 0x7FBAB6160C80.random(size).sum() > 0
                                  ^^
SyntaxError: invalid syntax

 - Cannot evaluate expression '<lambda>(size)':
Traceback (most recent call last):
  File "/home/project/.venv-git-2024-11-05/lib/python3.10/site-packages/taipy/gui/utils/_evaluator.py", line 262, in evaluate_expr
    expr_evaluated = eval(not_encoded_expr if is_edge_case else expr_string, ctx)
  File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not callable

Expected Behavior

The lambda expressions should evaluate to true and the block should be rendered.

Browsers

Chrome

OS

Windows, Linux

Version of Taipy

taipy-enterprise==4.0.0; develop

Additional Context

NA

Acceptance Criteria

Code of Conduct

FredLL-Avaiga commented 4 days ago

In your community example, the lambda does not work because rng is part of the state and needs to be passed as an argument. When it is not, the lambda is re-created on the fly with the value of rng at first render which does not work here

With this code, everything works

import numpy as np

import taipy.gui.builder as tgb
from taipy import Gui

rng = np.random.default_rng()
size = 1

with tgb.Page() as main_page:
    tgb.text(lambda size, rng: rng.random(size).sum() > 0)
    with tgb.part(render=lambda size, rng: rng.random(size).sum() > 0):
        tgb.text("Random sum is greater than 0")

if __name__ == "__main__":
    gui = Gui(main_page)
    gui.run(title="2212 [πŸ› BUG] Issue using lambda when passing state variable to a method")
FredLL-Avaiga commented 4 days ago

I guess it would be the same thing for your enterprise example, if you declare is_admin as a parameter of your lambda. Can you confirm @arcanaxion ?

I would agree that a better error message would be good

arcanaxion commented 4 days ago

I can confirm that adding is_admin as a parameter of my lambda works. Though it is a shame that I lose intellisense for is_admin too.

I noticed that I could have created a variable like rngrandom = rng.random and used that and it would have worked too. Are there any unexpected consequences of using this workaround to preserve intellisense?

FredLL-Avaiga commented 4 days ago

I don't get it about rngrandom, can you copy your code ? If it works and makes you happy, I'm fine with it :-D

arcanaxion commented 4 days ago

@FredLL-Avaiga

Following (ugly) workaround works:

import numpy as np

import taipy.gui.builder as tgb
from taipy import Gui

rng = np.random.default_rng()
size = 1

with tgb.Page() as main_page:
    rngrandom = rng.random
    tgb.text(lambda size: rngrandom(size).sum())

if __name__ == "__main__":
    gui = Gui(main_page)
    gui.run()

It is ugly and I don't like it, but it gives intellisense for the rngrandom method. Compared to if I add rng as a lambda parameter, I lose intellisense and also have to rename it separately if I automatically (IDE) refactor/rename rng.

You mentioned "re-created on the fly with the value of rng at first render", but I don't really know what that means. I'm wondering if there may be unexpected behavior when doing this, as opposed to adding rng as a lambda parameter like you suggested

FredLL-Avaiga commented 4 days ago

no unexpected behavior that I can see/imagine

FredLL-Avaiga commented 3 hours ago

Shall we close this @arcanaxion ?

arcanaxion commented 2 hours ago

@FredLL-Avaiga I would still like to see a better error message, if it makes sense to do so. I wouldn't have figured out what the problem was from the current error message