1414import copy
1515import socket
1616import multiprocessing
17-
17+ import time
1818
1919import signal
2020from urllib .parse import unquote
2121
2222from .rate_control import rate
2323
24+ makeDaemonic = (platform .system () == "Windows" )
2425
2526# Redefine `Thread.run` to not show a traceback for Spyder when stopping
2627# the server by raising a KeyboardInterrupt or SystemExit.
@@ -38,7 +39,8 @@ def run(*args, **kwargs):
3839 try :
3940 run_old (* args , ** kwargs )
4041 except (KeyboardInterrupt , SystemExit ):
41- print ("VPython server stopped." )
42+ pass
43+ # ("VPython server stopped.")
4244 except :
4345 raise
4446 threading .Thread .run = run
@@ -49,9 +51,9 @@ def run(*args, **kwargs):
4951
5052# Check for Ctrl+C. SIGINT will also be sent by our code if WServer is closed.
5153def signal_handler (signal , frame ):
54+ #print("in signal handler, calling stop server")
5255 stop_server ()
5356
54-
5557signal .signal (signal .SIGINT , signal_handler )
5658
5759# Requests from client to http server can be the following:
@@ -211,8 +213,18 @@ async def onMessage(self, data, isBinary):
211213 # message format used by notebook
212214 msg = {'content' : {'data' : [m ]}}
213215 loop = asyncio .get_event_loop ()
214- await loop .run_in_executor (None , GW .handle_msg , msg )
215-
216+ try :
217+ await loop .run_in_executor (None , GW .handle_msg , msg )
218+ except :
219+ #
220+ # this will throw a runtime exception after the main Thread
221+ # has stopped, but we don't really case since the main thread
222+ # is no longer there to do anything anyway.
223+ if threading .main_thread ().is_alive ():
224+ raise
225+ else :
226+ pass
227+
216228 def onClose (self , wasClean , code , reason ):
217229 """Called when browser tab is closed."""
218230 global websocketserving
@@ -293,7 +305,7 @@ def start_Qapp(port):
293305 __m = multiprocessing .Process (target = start_Qapp , args = (__HTTP_PORT ,))
294306 __m .start ()
295307
296- __w = threading .Thread (target = __server .serve_forever )
308+ __w = threading .Thread (target = __server .serve_forever , daemon = makeDaemonic )
297309__w .start ()
298310
299311
@@ -326,14 +338,16 @@ def start_websocket_server():
326338# Put the websocket server in a separate thread running its own event loop.
327339# That works even if some other program (e.g. spyder) already running an
328340# async event loop.
329- __t = threading .Thread (target = start_websocket_server )
341+ __t = threading .Thread (target = start_websocket_server , daemon = makeDaemonic )
330342__t .start ()
331343
332344
333345def stop_server ():
334346 """Shuts down all threads and exits cleanly."""
347+ #print("in stop server")
335348 global __server
336349 __server .shutdown ()
350+
337351 event_loop = txaio .config .loop
338352 event_loop .stop ()
339353 # We've told the event loop to stop, but it won't shut down until we poke
@@ -352,19 +366,36 @@ def stop_server():
352366 raise KeyboardInterrupt
353367
354368 if threading .main_thread ().is_alive ():
369+ #print("main is alive...")
355370 sys .exit (0 )
356371 else :
357- pass
372+ #
373+ # check to see if the event loop is still going, if so join it.
374+ #
375+ #print("main is dead..")
376+ if __t .is_alive ():
377+ #print("__t is alive still")
378+ if threading .get_ident () != __t .ident :
379+ #print("but it's not my thread, so I'll join...")
380+ __t .join ()
381+ else :
382+ #print("__t is alive, but that's my thread! So skip it.")
383+ pass
384+ else :
385+ if makeDaemonic :
386+ sys .exit (0 )
387+
358388 # If the main thread has already stopped, the python interpreter
359389 # is likely just running .join on the two remaining threads (in
360390 # python/threading.py:_shutdown). Since we just stopped those threads,
361391 # we'll now exit.
362-
363-
392+
364393GW = GlowWidget ()
365394
366395while not (httpserving and websocketserving ): # try to make sure setup is complete
367- rate (60 )
396+ time .sleep (0.1 )
397+
368398
369399# Dummy variable to import
370400_ = None
401+
0 commit comments