Forking what is left of ZeroNet and hopefully adding an AT Proto Frontend/Proxy
at main 115 lines 3.6 kB view raw
1import sys 2import logging 3import signal 4import importlib 5 6import gevent 7import gevent.hub 8 9from Config import config 10from . import Debug 11 12last_error = None 13 14def shutdown(reason="Unknown"): 15 logging.info("Shutting down (reason: %s)..." % reason) 16 import main 17 if "file_server" in dir(main): 18 try: 19 gevent.spawn(main.file_server.stop) 20 if "ui_server" in dir(main): 21 gevent.spawn(main.ui_server.stop) 22 except Exception as err: 23 print("Proper shutdown error: %s" % err) 24 sys.exit(0) 25 else: 26 sys.exit(0) 27 28# Store last error, ignore notify, allow manual error logging 29def handleError(*args, **kwargs): 30 global last_error 31 if not args: # Manual called 32 args = sys.exc_info() 33 silent = True 34 else: 35 silent = False 36 if args[0].__name__ != "Notify": 37 last_error = args 38 39 if args[0].__name__ == "KeyboardInterrupt": 40 shutdown("Keyboard interrupt") 41 elif not silent and args[0].__name__ != "Notify": 42 logging.exception("Unhandled exception") 43 if "greenlet.py" not in args[2].tb_frame.f_code.co_filename: # Don't display error twice 44 sys.__excepthook__(*args, **kwargs) 45 46 47# Ignore notify errors 48def handleErrorNotify(*args, **kwargs): 49 err = args[0] 50 if err.__name__ == "KeyboardInterrupt": 51 shutdown("Keyboard interrupt") 52 elif err.__name__ != "Notify": 53 logging.error("Unhandled exception: %s" % Debug.formatException(args)) 54 sys.__excepthook__(*args, **kwargs) 55 56 57if config.debug: # Keep last error for /Debug 58 sys.excepthook = handleError 59else: 60 sys.excepthook = handleErrorNotify 61 62 63# Override default error handler to allow silent killing / custom logging 64if "handle_error" in dir(gevent.hub.Hub): 65 gevent.hub.Hub._original_handle_error = gevent.hub.Hub.handle_error 66else: 67 logging.debug("gevent.hub.Hub.handle_error not found using old gevent hooks") 68 OriginalGreenlet = gevent.Greenlet 69 class ErrorhookedGreenlet(OriginalGreenlet): 70 def _report_error(self, exc_info): 71 sys.excepthook(exc_info[0], exc_info[1], exc_info[2]) 72 73 gevent.Greenlet = gevent.greenlet.Greenlet = ErrorhookedGreenlet 74 importlib.reload(gevent) 75 76def handleGreenletError(context, type, value, tb): 77 if context.__class__ is tuple and context[0].__class__.__name__ == "ThreadPool": 78 # Exceptions in ThreadPool will be handled in the main Thread 79 return None 80 81 if isinstance(value, str): 82 # Cython can raise errors where the value is a plain string 83 # e.g., AttributeError, "_semaphore.Semaphore has no attr", <traceback> 84 value = type(value) 85 86 if not issubclass(type, gevent.get_hub().NOT_ERROR): 87 sys.excepthook(type, value, tb) 88 89gevent.get_hub().handle_error = handleGreenletError 90 91try: 92 signal.signal(signal.SIGTERM, lambda signum, stack_frame: shutdown("SIGTERM")) 93except Exception as err: 94 logging.debug("Error setting up SIGTERM watcher: %s" % err) 95 96 97if __name__ == "__main__": 98 import time 99 from gevent import monkey 100 monkey.patch_all(thread=False, ssl=False) 101 from . import Debug 102 103 def sleeper(num): 104 print("started", num) 105 time.sleep(3) 106 raise Exception("Error") 107 print("stopped", num) 108 thread1 = gevent.spawn(sleeper, 1) 109 thread2 = gevent.spawn(sleeper, 2) 110 time.sleep(1) 111 print("killing...") 112 thread1.kill(exception=Debug.Notify("Worker stopped")) 113 #thread2.throw(Debug.Notify("Throw")) 114 print("killed") 115 gevent.joinall([thread1,thread2])