Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In on_connect hook: RuntimeError: app.storage.user needs a storage_secret passed in ui.run() #2520

Open
dr-yd opened this issue Feb 6, 2024 · 12 comments
Labels
bug Something isn't working
Milestone

Comments

@dr-yd
Copy link

dr-yd commented Feb 6, 2024

Description

Code example:

from nicegui import ui, app


class Ui:

    def __init__(self, callback):
        self.callback = callback
        app.on_connect(self.callback)


class Obj:

    def __init__(self):
        self.ui = Ui(self.on_connect)

    def on_connect(self):
        print(app.storage.user.get('test'))


@ui.page('/')
async def root(code: str = '', state: str = ''):
    obj = Obj()


ui.run(storage_secret='foobar', )

Result:

Traceback (most recent call last):
  File "/Users/me/Library/Python/3.11/lib/python/site-packages/nicegui/client.py", line 284, in safe_invoke
    result = func(self) if len(inspect.signature(func).parameters) == 1 else func()
                                                                             ^^^^^^
  File "/Users/me/demo.py", line 17, in on_connect
    print(app.storage.user.get('test'))
          ^^^^^^^^^^^^^^^^
  File "/Users/me/Library/Python/3.11/lib/python/site-packages/nicegui/storage.py", line 134, in user
    raise RuntimeError('app.storage.user needs a storage_secret passed in ui.run()')

Intent in the actual application is to initialize a session to a remote server if the user has an active OAuth session with that server. And that session would be stored in the user storage. At the very least, the message is misleading, but I'm not sure how to do this properly.

@falkoschindler
Copy link
Contributor

Hi @dr-yd, thanks for reporting this problem!

Here is a minimal reproduction:

from nicegui import ui, app

@ui.page('/')
def root():
    app.on_connect(lambda: print(app.storage.user.get('test')))

ui.run(storage_secret='foobar')

But I haven't found the reason why we see this specific error message. The app.on_connect handler seems to be called although the client isn't fully connected yet?

Even though we should fix this misleading RuntimeError, the following code works as expected:

from nicegui import Client, app, ui

@ui.page('/')
async def root(client: Client):
    await client.connected()
    app.storage.user.get('test')

ui.run(storage_secret='foobar')

@falkoschindler falkoschindler added the bug Something isn't working label Feb 8, 2024
@falkoschindler falkoschindler added this to the 2.0.0 milestone Feb 8, 2024
@falkoschindler
Copy link
Contributor

Since we'll change how requests are stored in the client object in 2.0, we'll look into this issue when working on this branch.

@kwmartin
Copy link

I'm seeing a similar issue. I'm trying to get Oauth2 working (mostly have succeeded) and am now trying to add password authentication. I'm running uvicorn from fastapi and then using:

    ui.run_with(
        fastapi_app,
        mount_path='/gui',  # NOTE this can be omitted if you want the paths passed to @ui.page to be at the root
        storage_secret='pick your private secret here',  # NOTE setting a secret is optional but allows for persistent storage per user
    )

With breakpoints I traced that storage_secret seems to be getting added (with sessionMiddleware), but later when I try check app.storage.user the key is not there

-> if not app.storage.user.get('authenticated', False):
(Pdb) n
RuntimeError: app.storage.user needs a storage_secret passed in ui.run()
> /home/Dropbox/programming/Python/FastApi/Sandbox/authenticate2/frontend.py(15)dispatch()
-> if not app.storage.user.get('authenticated', False):

p.s. I'm also trying to use app.add_middleware(AuthMiddleware)
I can't find a complete Outh2 flow for NiceGui anywhere, if someone knows one, please send link

@falkoschindler
Copy link
Contributor

In #2662 we noticed another example for the misleading "RuntimeError: app.storage.user needs a storage_secret passed in ui.run()":

@ui.page('/')
def page():
    ui.label('Hello, world!')

app.on_disconnect(lambda: print(app.storage.user))

@Bacoknight
Copy link

Bacoknight commented Mar 20, 2024

Hi all,
Not sure if this is helpful but I've also noticed this error being raised when calling app.user.storage in a function called using run.io_bound

@ui.page("/")
async def main(client: Client):
    await client.connected()
    async def on_click():
        result = await run.io_bound(run_analysis) # Run in async.
    ui.button("Run", on_click=lambda: on_click())

def run_analysis():
    return app.storage.user["test"]

# Main guard to be used for testing. 
if __name__ in {"__main__", "__mp_main__"}:
    ui.run(storage_secret="test_secret!")

The above results in RuntimeError: app.storage.user needs a storage_secret passed in ui.run()
However, running the function synchronously seems to work fine i.e.,:

@ui.page("/")
async def main(client: Client):
    await client.connected()
    async def on_click():
        result = run_analysis() # Not run in async.
    ui.button("Run", on_click=lambda: on_click())

def run_analysis():
    return app.storage.user["test"]

# Main guard to be used for testing. 
if __name__ in {"__main__", "__mp_main__"}:
    ui.run(storage_secret="test_secret!")

@Dan1001
Copy link

Dan1001 commented Apr 25, 2024

Hi - I also have this problem... don't suppose anyone has a workaround?

@Dan1001
Copy link

Dan1001 commented Apr 27, 2024

okay, this seems to be fixed in the latest version!

@falkoschindler
Copy link
Contributor

@Dan1001 As far as I can tell, my reproduction from #2520 (comment) still causes the RuntimeError.

@Dan1001
Copy link

Dan1001 commented Apr 27, 2024

that's funny - I'm now getting it in some circumstances but not others (sorry, I know that's very unhelpful; complex code).

I'll try to work out why...

@erdincka
Copy link

erdincka commented May 2, 2024

This happens to me as well, when using run.io_bound. I somehow figured if my function within "io_bound" has an issue (key not found for dict for example), logger.debug is not showing anything but giving this error, along with 22:14:31:ERROR:handle_exception: There is no current event loop in thread 'ThreadPoolExecutor-2_0'..
I wish I could provide more details, but code is a bit mess for now. I will eventually put my code on public repo here, but I hope I would figure this out by then.
PS: Thanks for this amazing framework.

@erdincka
Copy link

erdincka commented May 2, 2024

Update (and possibly a workaround) on my code. It seems like a race condition when you try to update app.storage very fast. I have a process checking the status of a REST call every few seconds, and based on the fields of the returned json, I update the status on the persistent storage.

In this case, I am using app.storage.general, not even app.storage.user, but I get the error regarding the user.

With the following piece of code, if I have this sleep(0.2) then it all works fine, otherwise, it fires one or the other error.

I don't know exactly why it works for the first two consecutive updates, but fails on the third.

Hope this helps figuring out best way to handle this type of situation (assuming this is the reason).

elif result['status'] == "OK":
    app.storage.general["stream_replication"] = "OK"
    if result["data"][0].get('replicaState', "") == "REPLICA_STATE_REPLICATING":
        app.storage.general["stream_replication"] = "REPLICATING"
        if result["data"][0].get("isUptodate", False) == True:
            sleep(0.2)
            app.storage.general["stream_replication"] = "IN SYNC"

@j4nSolo
Copy link

j4nSolo commented May 18, 2024

Hi all, Not sure if this is helpful but I've also noticed this error being raised when calling app.user.storage in a function called using run.io_bound

I'm experiencing the exact same issue and I got to the same conclusion: whenever app.storage.user is accessed from within a function spawned by run.io_bound it seems the storage_secret is "lost".

It happens the same with app.storage.tab.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

7 participants