Closed FeodorFitsner closed 1 year ago
Having asyncio support "natively" would be awesome -- but it does seem like a lot of work. Sprinkling async and await all over the place does make any code look unwieldy, and maintaining the sync alternatives seems like almost doubling the maintenance effort.
Anyone wanting to call into async functions from flet
code could take advantage of the minimal unsync package, where a simple usage example would be:
import asyncio
import flet
from flet import ElevatedButton
from flet import Page
from unsync import unsync
def clicked(event):
something_async()
@unsync
async def something_async():
print("Starting an async operation")
await asyncio.sleep(1)
print("Finished")
def main(page: Page):
page.add(ElevatedButton("Click me to do something async", on_click=clicked))
page.update()
flet.app(target=main)
unsync
maintains a singleton loop for its use, so the overhead is reasonable for most use cases.
Is there a chance to support asyncio soon? When can we expect this support, is there a rough date for it?
It's not a high priority right now. What would be your use case for that?
Easy integration of projects working with asyncio.
Currently, to bring these projects together with flet, I would have to create a workaround (calling async functions). For now I use qt and set the qt main loop as my asyncio event loop, bringing the two worlds together,... which allows me to use the gui with async functions.
If I could use the event loop of flet as asynchio loop, it would be easier, something like this:
import asyncio
import sys
from asyncqt import QEventLoop
from PySide2.QtWidgets import QApplication
from pykalah.view.main_window import MainWindow
def main() -> None:
"""The main function"""
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop) # <-bring qt and asyncio together
window = MainWindow(None, initial_amount_pieces=6)
window.show()
with loop:
sys.exit(loop.run_forever())
if __name__ == "__main__":
main()```
It looks great, of course, it's just we don't have enough hands at the moment to implement that 😕
We go ahead with Async support in Flet: https://flet.dev/blog/flet-mobile-update#asyncio-support
How wonderful, thanks! :)
So, async support has been merged into main
and you can give it a try by installing pre-release version of Flet package with pip install flet --pre
.
Async version of "counter" app:
import flet as ft
async def main(page: ft.Page):
page.title = "Flet counter example"
page.vertical_alignment = ft.MainAxisAlignment.CENTER
txt_number = ft.TextField(value="0", text_align=ft.TextAlign.RIGHT, width=100)
async def minus_click(e):
txt_number.value = str(int(txt_number.value) - 1)
await page.update_async()
async def plus_click(e):
txt_number.value = str(int(txt_number.value) + 1)
await page.update_async()
await page.add_async(
ft.Row(
[
ft.IconButton(ft.icons.REMOVE, on_click=minus_click),
txt_number,
ft.IconButton(ft.icons.ADD, on_click=plus_click),
],
alignment=ft.MainAxisAlignment.CENTER,
)
)
ft.app(main)
A few async samples:
Already tried this! Awesome implementation.
I think there is a bug with pubsub @FeodorFitsner
TypeError: __main__.main.<locals>.handle_message() argument after ** must be a mapping, not list
This line should only have 1 asterisk/star: https://github.com/flet-dev/flet/blob/14c215b0194ae94ebf87aef8efe7b1aaf6e1ee7e/sdk/python/flet/pubsub.py#L177
Great, will take a look!
This is my first time using Flet, so this could be normal but possibly not. Using the example code from the appbar documentation, when I close the Flet GUI, I get:
Traceback (most recent call last):
File "C:\Users\jgogo\Desktop\fl.py", line 42, in <module>
ft.app(target=main)
File "C:\Users\jgogo\AppData\Local\Programs\Python\Python310\lib\site-packages\flet\flet.py", line 81, in app
__app_sync(
File "C:\Users\jgogo\AppData\Local\Programs\Python\Python310\lib\site-packages\flet\flet.py", line 160, in __app_sync
conn.close()
File "C:\Users\jgogo\AppData\Local\Programs\Python\Python310\lib\site-packages\flet\sync_local_socket_connection.py", line 129, in close
if self.__uds_path and os.path.exists(self.__uds_path):
AttributeError: 'SyncLocalSocketConnection' object has no attribute '_SyncLocalSocketConnection__uds_path'. Did you mean: '_SyncLocalSocketConnection__recvall'?
Also, about half the time whenever I run the example code, the UI seems to indefinitely load. No error is shown: The other half of the time it works as expected.
Edit: To be clear, I'm using the prerelease version of Flet, but using the synchronous example code provided in the documentation.
Will fix that later today, looks like a bug!
@JartanFTW it's been fixed in the latest pre-release package.
The problem I previously mentioned was indeed fixed by the latest pre-release. Thank you!
Does an asynchronous alternative to ft.app(main)
exist? I'm not sure what the exact terminology is so I'll use an example instead.
Old startup method for discord.py bots:
bot.run('token')
used in the same fashion as the flet process mentioned above.
Recently, discord.py introduced a new way to start the bots (the old way still works):
await bot.start('token')
This allows for more freedom in advanced startup conditions, an example for discord.py provided here.
@JartanFTW Yep, you can use await ft.app_async(main)
.
@JartanFTW it's been fixed in the latest pre-release package.
@FeodorFitsner is there a way to install the pre-release package that fixes this issue on Windows, I'm having no luck and having the same issue?
@JartanFTW it's been fixed in the latest pre-release package.
@FeodorFitsner is there a way to install the pre-release package that fixes this issue on Windows, I'm having no luck and having the same issue?
Simply re-running the pip install flet --pre
command again does not actually update flet. You'll first need to run pip uninstall flet
and then install it again.
@JartanFTW it's been fixed in the latest pre-release package.
@FeodorFitsner is there a way to install the pre-release package that fixes this issue on Windows, I'm having no luck and having the same issue?
Simply re-running the
pip install flet --pre
command again does not actually update flet. You'll first need to runpip uninstall flet
and then install it again.
@JartanFTW I've tried that already but still running into the same issue you were having.
@JartanFTW I've tried that already but still running into the same issue you were having.
Can you provide the code you're running?
@JartanFTW I've tried that already but still running into the same issue you were having.
Can you provide the code you're running?
Running any code with anything being async gives the error I've tried with all of the examples here:
So, async support has been merged into
main
and you can give it a try by installing pre-release version of Flet package withpip install flet --pre
.Async version of "counter" app:
import flet as ft async def main(page: ft.Page): page.title = "Flet counter example" page.vertical_alignment = ft.MainAxisAlignment.CENTER txt_number = ft.TextField(value="0", text_align=ft.TextAlign.RIGHT, width=100) async def minus_click(e): txt_number.value = str(int(txt_number.value) - 1) await page.update_async() async def plus_click(e): txt_number.value = str(int(txt_number.value) + 1) await page.update_async() await page.add_async( ft.Row( [ ft.IconButton(ft.icons.REMOVE, on_click=minus_click), txt_number, ft.IconButton(ft.icons.ADD, on_click=plus_click), ], alignment=ft.MainAxisAlignment.CENTER, ) ) ft.app(main)
A few async samples:
* [Audio player](https://github.com/flet-dev/examples/pull/58/files#diff-f4afcaaa658d4528e4553a65ba045a27ee8046515272d50be2c7a5c485e06da9) * [Simple OAuth](https://github.com/flet-dev/examples/pull/58/files#diff-c6a2af89cbfa2bacad9790976416d9ae63c1409651d79c2a01621ac7f1704c6f) * [List GitHub repos](https://github.com/flet-dev/examples/pull/58/files#diff-e20c204d3f11a4ae5cb185b81c3d51f93c45753414a7309ce11c612ca111a644)
Running any code with anything being async gives the error I've tried with all of the examples here:
Can you also please provide the error you are receiving? I know you've said it's the same as mine, but it can still be helpful to see.
Actually it appears my issue is slightly different my mistake it shows:
Traceback (most recent call last):
File "D:\Users\User\Desktop\test.py", line 28, in
ft.app(main) File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\site-packages\flet\flet.py", line 67, in app asyncio.run( File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 190, in run
return runner.run(main) ^^^^^^^^^^^^^^^^ File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\site-packages\flet\flet.py", line 238, in app_async await conn.close() File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\site-packages\flet\async_local_socket_connection.py", line 158, in close if self.__uds_path and os.path.exists(self.uds_path): ^^^^^^^^^^^^^^^ AttributeError: 'AsyncLocalSocketConnection' object has no attribute '_AsyncLocalSocketConnectionuds_path'. Did you mean: '_AsyncLocalSocketConnection__port'?
Actually it appears my issue is slightly different my mistake it shows:
This looks like a @FeodorFitsner thing. Good luck.
@890r121289rh what do you have when running pip show flet
?
@890r121289rh what do you have when running
pip show flet
?
@FeodorFitsner
C:\Users\User>pip show flet Name: flet Version: 0.4.0.dev1070 Summary: Flet for Python - easily build interactive multi-platform apps in Python Home-page: Author: Appveyor Systems Inc. Author-email: hello@flet.dev License: MIT Location: C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\site-packages Requires: flet-core, httpx, oauthlib, packaging, watchdog, websocket-client, websockets Required-by:
should be 0.4.0.dev1081
.
Do pip install flet --upgrade --pre
.
should be
0.4.0.dev1081
.Do
pip install flet --upgrade --pre
.
I'm not able to get 0.4.0.dev1081 same for the previous version 0.4.0.dev1070 is the latest I can download
On pypi there is only a macos whl file: flet-0.4.0.dev1081-py3-none-macosx_10_14_x86_64.whl
Ah, right! Sorry about that! I'm going to update it in a couple of minutes.
Ah, right! Sorry about that! I'm going to update it in a couple of minutes.
Sounds good, and no worries I appreciate your help
@890r121289rh OK, try doing pip install flet --upgrade --pre
now. It should give you 0.4.0.dev1084
. Let me know how that worked.
@890r121289rh OK, try doing
pip install flet --upgrade --pre
now. It should give you0.4.0.dev1084
. Let me know how that worked.
It's working fine now thank you for your help
The feature has been implemented and documented: https://flet.dev/docs/guides/python/async-apps
Discussed in https://github.com/flet-dev/flet/discussions/72
Problem
It is currently impossible to call
async
method from Flet event handlers. They are synchronous functions run in separate threads. This limitation requires to use bulky constructs like:where
get_weather(search_value)
is async function.Solution
As in other languages like C# a "proper" async must be implemented from ground up - it's impossible/ineffective to partially support calling
async
methods from sync ones. The entire program must be running in an event loop (in Python terms).Flet Python client must be re-implemented to use
asyncio
library.Sample code
Flet client will be providing both sync and async methods, for example async version of "Hello, world" might look like:
All async methods will have
_async
suffix.WebSockets client will be implemented with websockets library based on
asyncio
.Resources:
Flet API
Sync is going to be default style of writing Flet apps. Flet API that needs to be available in both sync and async variants:
Connection
class - have a baseConnection
class and two implementations:SyncConnection
andAsyncConnection
:def connect()
async def connect_async()
def send_command(self, session_id: str, command: Command)
async def send_command_async(self, session_id: str, command: Command)
def send_commands(self, session_id: str, commands: List[Command])
async def send_commands_async(self, session_id: str, commands: List[Command])
def close(self)
on_event
- could be eitherCallable
orCoroutine
on_session_created
- could be eitherCallable
orCoroutine
ReconnectingWebSocket
must be created insideSyncConnection
.send_commands
only in all cases?Page
class: