Open torablien opened 3 months ago
Completion handlers in general only get called when there's an active Cocoa event loop (the exception being when you can specify a dispatch queue, those often send work to a background thread and call the completion handler on a background thread as well).
That's not the case here though. The completion handler gets called in a background thread and there appears to be a race condition between cleaning up on the background thread (which is a daemon thread as far as the CPython runtime is concerned) and the main thread exiting.
A quick workaround for this is to add a short sleep after waiting for the completion_event. On my system time.sleep(0.1)
worked reliably, but YMMV. With some luck the sleep isn't necessary when you integrate this in a larger code base.
I'm keeping the issue open because I want to do further research to see if I can avoid the crash (or have to file an issue with the cpython project).
Thanks @ronaldoussoren - are there any example implementations or recommendations on how to work with completion handlers?
I tried some simple implementations with AppHelper.runConsoleEventLoop()
, Foundation.CFRunLoopRun()
, but couldn't get them to work well/robustly (e.g. terminating program, hanging if there is an error). Python's threading.Event
somewhat works but is also running into issues (e.g. sometimes some completion handlers seem to never get called and the Event times out, but something lingers that slowly bring my machine to a halt). I get the sense that this is not the best way to do this - if you have any recommendations, that would be greatly appreciated!
Thanks @ronaldoussoren - are there any example implementations or recommendations on how to work with completion handlers?
I tried some simple implementations with
AppHelper.runConsoleEventLoop()
,Foundation.CFRunLoopRun()
, but couldn't get them to work well/robustly (e.g. terminating program, hanging if there is an error). Python'sthreading.Event
somewhat works but is also running into issues (e.g. sometimes some completion handlers seem to never get called and the Event times out, but something lingers that slowly bring my machine to a halt). I get the sense that this is not the best way to do this - if you have any recommendations, that would be greatly appreciated!
I don't have a good solution at this moment, and is something I want to research over the summer anyway because issues like this will crop up more given Apple's switch to Swift and the concurrency model of Swift (which is spelled as "async/await" but uses pervasive threading)
I hope to have some time over the summer to finish some work on asyncio integration for PyObjC, including adding async methods for all Objective-C selectors with a completion handler argument. This should help hide most of the complexity in shared code, and should also result in cleaner code in scripts.
That should end up with code like this for your problem:
import asyncio
from ScreenCaptureKit import SCShareableContent
async def get_shareable_applications():
return await SCShareableContent.getShareableContent()
asyncio.run(get_shareable_applications())
I still have to work out a good design though, and this likely requires using a PyObjC implementation of an asyncio runloop in general (but not necessarily in this use case).
Thanks, appreciate it. If you come across a good workaround, I'd be interested in trying it. An API for these kinds of situations (e.g. async methods) would be great.
I'm new to pyobjc so this definitely could be a beginner mistake.
I'm trying to figure out the best way to wait for completion handlers to finish and encapsulate that into a simpler synchronous-like function. I've been testing with
threading.Event
and although it generally works, I've noticed that if the program exits immediately after, I see a SIGTRAP (zsh: trace trap ...
). Is there a proper/better way to do this?Running Python 3.12 on M1 Pro 14.1.2
Thanks!