Forking what is left of ZeroNet and hopefully adding an AT Proto Frontend/Proxy
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])