Setup
- macOS Big Sur
- Python 3.9.5
- main dependencies
grpcio = "^1.41.0"
grpcio-tools = "^1.41.0"
grpcio-reflection = "^1.41.0"
Problem
I have a simple Python script using TkInter that works under commandline mode, but crashes when the module gets called using gRPC.
Code
The TkInter related code:
import tkinter as tk
import tkinter.filedialog as tkfiledialog
def main():
root = tk.Tk()
root.withdraw()
root.after_idle(prompt, root)
root.mainloop()
def prompt(root):
return tkfiledialog.askopenfilename(
parent=root,
title="Select Files",
filety[es=[("All Files", "*")],
defaultextension=("All Files", "*"),
multiple=True,
)
The gRPC architecture is quite standard that I don't think is worth posting. It has little to do with my client implementation as well, because I can easily reproduce the crash using grpcurl
grpcurl -d '{"dryrun": false, "verbose": false, "startdir": ".", "dironly": false, "filepatterns": "*" }' -plaintext localhost:57728 selectfilesfolders.SelectFilesFolders.SelectFilesFolders
Crash log
2021-10-10 16:06:50.147 Python[2855:90390] WARNING: NSWindow drag regions should only be invalidated on the Main Thread! This will throw an exception in the future. Called from (
0 AppKit 0x00007fff22e55ed1 -[NSWindow(NSWindow_Theme) _postWindowNeedsToResetDragMarginsUnlessPostingDisabled] 352
1 AppKit 0x00007fff22e40aa2 -[NSWindow _initContent:styleMask:backing:defer:contentView:] 1296
2 AppKit 0x00007fff23002c2f -[NSPanel _initContent:styleMask:backing:defer:contentView:] 50
3 AppKit 0x00007fff22e4058b -[NSWindow initWithContentRect:styleMask:backing:defer:] 42
4 AppKit 0x00007fff23002be4 -[NSPanel initWithContentRect:styleMask:backing:defer:] 64
5 AppKit 0x00007fff2384e274 -[NSSavePanel initWithContentRect:styleMask:backing:defer:] 97
6 AppKit 0x00007fff2385636d -[NSOpenPanel initWithContentRect:styleMask:backing:defer:] 151
7 AppKit 0x00007fff2314f0e5 -[NSPanel init] 75
8 AppKit 0x00007fff2384e1f0 -[NSSavePanel init] 197
9 AppKit 0x00007fff23522c3a [NSSavePanel(Instantiation) _crunchyRawUnbonedPanel] 58
10 libtk8.6.dylib 0x0000000102b97a11 Tk_GetOpenFileObjCmd 67
11 libtcl8.6.dylib 0x00000001029886dc TclNRRunCallbacks 80
12 _tkinter.cpython-39-darwin.so 0x0000000102961029 Tkapp_Call 585
13 Python 0x000000010123245d cfunction_call 125
14 Python 0x00000001011f3a3c _PyObject_Call 140
15 Python 0x00000001012c7a73 _PyEval_EvalFrameDefault 27027
16 Python 0x00000001012cab33 _PyEval_EvalCode 2611
17 Python 0x00000001011f3c41 _PyFunction_Vectorcall 289
18 Python 0x00000001012c9e3c call_function 732
19 Python 0x00000001012c7342 _PyEval_EvalFrameDefault 25186
20 Python 0x00000001012cab33 _PyEval_EvalCode 2611
21 Python 0x00000001011f3c41 _PyFunction_Vectorcall 289
22 Python 0x00000001012c9e3c call_function 732
23 Python 0x00000001012c7491 _PyEval_EvalFrameDefault 25521
24 Python 0x00000001011f3cb8 function_code_fastcall 104
25 Python 0x00000001011f5de9 method_vectorcall 441
26 Python 0x00000001012c7a73 _PyEval_EvalFrameDefault 27027
27 Python 0x00000001012cab33 _PyEval_EvalCode 2611
28 Python 0x00000001011f3c41 _PyFunction_Vectorcall 289
29 Python 0x00000001012c7a73 _PyEval_EvalFrameDefault 27027
30 Python 0x00000001012cab33 _PyEval_EvalCode 2611
31 Python 0x00000001011f3c41 _PyFunction_Vectorcall 289
32 Python 0x00000001011f5d42 method_vectorcall 274
33 _tkinter.cpython-39-darwin.so 0x0000000102963bb1 PythonCmd 209
34 libtcl8.6.dylib 0x00000001029886dc TclNRRunCallbacks 80
35 libtcl8.6.dylib 0x0000000102a54ed3 AfterProc 83
36 libtcl8.6.dylib 0x0000000102a54570 TclServiceIdle 87
37 libtcl8.6.dylib 0x0000000102a37d27 Tcl_DoOneEvent 349
38 libtk8.6.dylib 0x0000000102b1aa02 MapFrame 40
39 libtcl8.6.dylib 0x0000000102a54570 TclServiceIdle 87
40 libtcl8.6.dylib 0x0000000102a37d27 Tcl_DoOneEvent 349
41 _tkinter.cpython-39-darwin.so 0x00000001029633de _tkinter_tkapp_mainloop 382
42 Python 0x00000001011fc3df method_vectorcall_FASTCALL 335
43 Python 0x00000001012c9e3c call_function 732
44 Python 0x00000001012c7342 _PyEval_EvalFrameDefault 25186
45 Python 0x00000001012cab33 _PyEval_EvalCode 2611
46 Python 0x00000001011f3c41 _PyFunction_Vectorcall 289
47 Python 0x00000001011f5cfa method_vectorcall 202
48 Python 0x00000001012c9e3c call_function 732
49 Python 0x00000001012c7363 _PyEval_EvalFrameDefault 25219
50 Python 0x00000001011f3cb8 function_code_fastcall 104
51 Python 0x00000001012c9e3c call_function 732
52 Python 0x00000001012c7342 _PyEval_EvalFrameDefault 25186
53 Python 0x00000001011f3cb8 function_code_fastcall 104
54 Python 0x00000001012c7a73 _PyEval_EvalFrameDefault 27027
55 Python 0x00000001012cab33 _PyEval_EvalCode 2611
56 Python 0x00000001011f3c41 _PyFunction_Vectorcall 289
57 Python 0x00000001012c9e3c call_function 732
58 Python 0x00000001012c7363 _PyEval_EvalFrameDefault 25219
59 Python 0x00000001011f3cb8 function_code_fastcall 104
60 Python 0x00000001011f5cfa method_vectorcall 202
61 Python 0x00000001012c9e3c call_function 732
62 Python 0x00000001012c73fb _PyEval_EvalFrameDefault 25371
63 Python 0x00000001012cab33 _PyEval_EvalCode 2611
64 Python 0x00000001011f3c41 _PyFunction_Vectorcall 289
65 Python 0x00000001012c9e3c call_function 732
66 Python 0x00000001012c73fb _PyEval_EvalFrameDefault 25371
67 Python 0x00000001011f3cb8 function_code_fastcall 104
68 Python 0x00000001012c7a73 _PyEval_EvalFrameDefault 27027
69 Python 0x00000001011f3cb8 function_code_fastcall 104
70 Python 0x00000001012c9e3c call_function 732
71 Python 0x00000001012c7342 _PyEval_EvalFrameDefault 25186
72 Python 0x00000001011f3cb8 function_code_fastcall 104
73 Python 0x00000001012c7a73 _PyEval_EvalFrameDefault 27027
74 Python 0x00000001011f3cb8 function_code_fastcall 104
75 Python 0x00000001012c9e3c call_function 732
76 Python 0x00000001012c7342 _PyEval_EvalFrameDefault 25186
77 Python 0x00000001011f3cb8 function_code_fastcall 104
78 Python 0x00000001012c9e3c call_function 732
79 Python 0x00000001012c7342 _PyEval_EvalFrameDefault 25186
80 Python 0x00000001011f3cb8 function_code_fastcall 104
81 Python 0x00000001011f5d42 method_vectorcall 274
82 Python 0x000000010136ec56 t_bootstrap 70
83 Python 0x0000000101320169 pythread_wrapper 25
84 libsystem_pthread.dylib 0x00007fff2052c8fc _pthread_start 224
85 libsystem_pthread.dylib 0x00007fff20528443 thread_start 15
)
Workaround
- If I replace the TkInter part with ANY code, everything works.
- Call this with IPC through child process works as well
Question
How should I go about debugging or should this be a sign that TkInter is not proper for my task?
CodePudding user response:
According to this GitHub anwser
It is likely a macOS-specific threading issue
On the other hand, according to tkinter's doc. The library may have special event handling logic and threading restrictions, and gRPC Python uses a thread pool to handle incoming requests with gRPC Core handling the IO.
By digging into that doc, we find
A Python interpreter may have many threads associated with it. In Tcl, multiple threads can be created, but each thread has a separate Tcl interpreter instance associated with it. Threads can also create more than one interpreter instance, though each interpreter instance can be used only by the one thread that created it.
Each Tk object created by tkinter contains a Tcl interpreter. It also keeps track of which thread created that interpreter. Calls to tkinter can be made from any Python thread. Internally, if a call comes from a thread other than the one that created the Tk object, an event is posted to the interpreter’s event queue, and when executed, the result is returned to the calling Python thread.
Although I'm not an expert on platform-specific threading models, I do know that macOS is using a "Grand Central Dispatch" task-queue system that hides away a lot of thread-pool details, which is different from Windows.
Thus, without digging into gRPC's thread-pool model, I can't say it's safe to call Tkinter widgets, who keep track of thread that create them, from the volatile gRPC's I/O threads.