title: sys.exit() not working, no other threads and no try: blocks capturing SystemExit?
body: <p>I can't get my Python app to exit. After a call to sys.exit(), python.exe stays running and I have to kill it with task manager.</p>
  
  <p>I've spent the past 4 hours looking into this, and I'm stumped.</p>
  
  <p>This is Python 3.4.4 on Windows 10 x86.</p>
  
  <p>First, I do have a multithreaded application. However I have verified that all threads are exiting with only the main thread running before I call sys.exit(). (I did this by calling threading.enumerate() in a while loop and waiting until there's only the main thread remaining, printing the list of running threads and watching it get smaller on each loop until only the main thread remains.)</p>
  
  <p>Also, I've confirmed that I don't have anything wrapped in a try: block that would be swallowing the SystemExit exception. If I print sys.exc_info() I get (None, None, None), and if I call raise then it also confirms there are no exceptions pending.</p>
  
  <p>What's interesting is that I've narrowed this down to the offending thread by commenting out different parts of my app to disable each thread one-by-one. (I have 4 threads total, each doing different things.)</p>
  
  <p>If I comment out the thread in question, I can quit my app no problem. But again, even when I have that thread running, that thread does successfully exit, there's just something in there that's preventing the main Python exe from exiting.</p>
  
  <p>I've tried setting the daemon flag, but that doesn't do anything either way. The offending thread's purpose is to wait at a PriorityQueue() with a 1 second timeout, and then when that times out it checks a threading.Event() flag to exit itself gracefully. Again, that works fine. I can see in my while() loop while the program is exiting that that thread is running, then stops.</p>
  
  <p>The only other information is this application is launched via a console_scripts entry. I've looked at the script file that setuptools creates and see that just wraps the call to my entry point in a sys.exit(), but even hacking that file, I just cannot get this thing to exit.</p>
  
  <p>I've tried calling sys.exit, raising SystemExit, and simply returning to let the console_script call sys.exit. None of those work.</p>
  
  <p>I've also tried more brute force efforts, like os._exit(), but that also doesn't work.</p>
  
  <p>What's really weird is that if I create a recursive loop (a simple one-line method that just calls itself), and I put that in my stop method before I set my threading Event which stops the threads, then Python will exit as it should. (I did that by mistake and first and was dumbfounded that that works. But if I move that loop call down a few lines to just before I call sys.exit, then the recursive loop doesn't kill python.exe. So even though my problem thread exits properly, something about it trying to exit is causing Python.exe to hang.</p>
  
  <p>So, my question, does anyone have any other ideas or things to try about why Python won't exit? Specifically why my problem thread stops and only the main thread remains, yet sys.exit or os._exit() do nothing? I'm completely stumped.</p>
  
  <p>My app consumes about 90MB of memory, and in task manager, I can see the GC doing its job as when my app is "hung" after the sys.exit() call, I see the memory usage drop from 90MB to 0.1MB over the course of about 30 seconds. But even after leaving it, python.exe doesn't stop.</p>
  
  <p>Update: Here's some code that demonstrates what things look like:</p>
  
  <p>From the module and function that's registered as the console_script:</p>
  
  <pre><code>  def run_from_command_line(args=None):
          path = os.path.abspath(os.path.curdir)
          CommandLineUtility(path).execute()
  </code></pre>
  
  <p>From the CommandLineUtility() which starts my app. This is the last line:</p>
  
  <pre><code>  def __init__(...):
        ... skipping a bunch of setup stuff
        MpfMc(options=vars(args), config=mpf_config,
              machine_path=machine_path).run()  # this is not a threading run, just the name of the method for my app
  </code></pre>
  
  <p>From MpfMc():</p>
  
  <pre><code>def __init__(...):
      ...
      self.thread_stopper = threading.Event()
      ...
      self.asset_manager = AssetManager(self)
  </code></pre>
  
  <p>From AssetManager():</p>
  
  <pre><code>    self.loader_thread = AssetLoader(loader_queue=self.loader_queue,
                                       loaded_queue=self.loaded_queue,
                                       exception_queue=self.machine.crash_queue,
                                       thread_stopper=self.machine.thread_stopper)
      self.loader_thread.daemon = True
      self.loader_thread.start()
  </code></pre>
  
  <p>From AssetLoader:</p>
  
  <pre><code>def run(self):
      """Run loop for the loader thread."""
      while True:
          try:
              asset = self.loader_queue.get(block=True, timeout=1)
          except Empty:
              asset = None
  
          if self.thread_stopper.is_set():
              return
  
          if asset:
              if not asset.loaded:
                  with asset.lock:
                      asset.do_load()
              self.loaded_queue.put(asset)
  </code></pre>
  
  <p>From the MpfMc.stop() method that stops the app:</p>
  
  <pre><code>def stop(self):
      self.log.info("Stopping ...")
  
      self.thread_stopper.set()
  
      while [x for x in self.threads if x.is_alive()]:
          # self.threads is a list of threads I created, not the main thread.
          print("Waiting for threads to stop")
          print([x for x in self.threads if x.is_alive()])
          print(threading.enumerate())
          time.sleep(0.5)
  
      for thread in self.threads:
          # verify none of the sub threads are alive
          print("THREAD", thread, thread.is_alive())
  
      sys.exit()  # here's where I also tried raise SystemExit, os._exit(), etc
  </code></pre>
  
  <p>Thanks!</p>