diff --git a/CHANGELOG.md b/CHANGELOG.md index b59e09fb..ecea0d38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### zeronet-conservancy 0.7.10+ +- ipython-based repl via --repl for debug/interactive development (@caryoscelus) ### zeronet-conservancy 0.7.10 (2023-07-26) (18d35d3bed4f0683e99) prepared by @caryoscelus diff --git a/requirements.txt b/requirements.txt index 1059795c..b9eb9fc9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,3 +15,4 @@ rich defusedxml>=0.7 pyaes requests +ipython>=8 diff --git a/src/Config.py b/src/Config.py index 6c25af00..1dba0007 100644 --- a/src/Config.py +++ b/src/Config.py @@ -15,7 +15,7 @@ class Config(object): self.version = "0.7.10+" self.user_agent = "conservancy" # DEPRECATED ; replace with git-generated commit - self.rev = 5121 + self.rev = 5130 self.user_agent_rev = 8192 self.argv = argv self.action = None @@ -304,6 +304,7 @@ class Config(object): self.parser.add_argument('--tor_hs_limit', help='Maximum number of hidden services in Tor always mode', metavar='limit', type=int, default=10) self.parser.add_argument('--tor_hs_port', help='Hidden service port in Tor always mode', metavar='limit', type=int, default=15441) + self.parser.add_argument('--repl', help='Instead of printing logs in console, drop into REPL after initialization', action='store_true') self.parser.add_argument('--version', action='version', version=f'zeronet-conservancy {self.version} r{self.rev}') self.parser.add_argument('--end', help='Stop multi value argument parsing', action='store_true') @@ -583,7 +584,7 @@ class Config(object): format = '%(name)s %(message)s' if self.console_log_level == "default": - if self.silent: + if self.silent or self.repl: level = logging.ERROR elif self.debug: level = logging.DEBUG diff --git a/src/main.py b/src/main.py index 67480aaf..b15f46ec 100644 --- a/src/main.py +++ b/src/main.py @@ -157,6 +157,7 @@ if config.msgpack_purepython: os.environ["MSGPACK_PUREPYTHON"] = "True" # Fix console encoding on Windows +# TODO: check if this is still required if sys.platform.startswith("win"): import subprocess try: @@ -193,7 +194,7 @@ elif config.bind: @PluginManager.acceptPlugins -class Actions(object): +class Actions: def call(self, function_name, kwargs): logging.info("Version: %s r%s, Python %s, Gevent: %s" % (config.version, config.rev, sys.version, gevent.__version__)) @@ -202,6 +203,11 @@ class Actions(object): if back: print(back) + def ipythonThread(self): + import IPython + IPython.embed() + self.gevent_quit.set() + # Default action: Start serving UiServer and FileServer def main(self): global ui_server, file_server @@ -221,7 +227,25 @@ class Actions(object): CryptConnection.manager.removeCerts() logging.info("Starting servers....") - gevent.joinall([gevent.spawn(ui_server.start), gevent.spawn(file_server.start)]) + + import threading + self.gevent_quit = threading.Event() + launched_greenlets = [gevent.spawn(ui_server.start), gevent.spawn(file_server.start)] + + # if --repl, start ipython thread + # FIXME: Unfortunately this leads to exceptions on exit so use with care + if config.repl: + threading.Thread(target=self.ipythonThread).start() + + stopped = 0 + # Process all greenlets in main thread + while not self.gevent_quit.is_set() and stopped < len(launched_greenlets): + stopped += len(gevent.joinall(launched_greenlets, timeout=1)) + + # Exited due to repl, so must kill greenlets + if stopped < len(launched_greenlets): + gevent.killall(launched_greenlets, exception=KeyboardInterrupt) + logging.info("All server stopped") # Site commands