Package: manhole
FeaturesUses unix domain sockets, only root or same effective user can connect.Can run the connection in a thread or in a signal handler (seeoneshot_onoption).Can start the thread listening for connections from a signal handler (seeactivate_onoption)Compatible with apps that fork, reinstalls the Manhole thread after fork - had to monkeypatch os.fork/os.forkpty for
this.Compatible with gevent and eventlet with some limitations - you need to either:Useoneshot_on,orDisable thread monkeypatching (eg:gevent.monkey.patch_all(thread=False),eventlet.monkey_patch(thread=False)Note: on eventletyou mightneed to setup the hub first to prevent
circular import problems:importeventleteventlet.hubs.get_hub()# do this firsteventlet.monkey_patch(thread=False)The thread is compatible with apps that use signalfd (will mask all signals for the Manhole threads).Optionsmanhole.install(verbose=True,verbose_destination=2,patch_fork=True,activate_on=None,oneshot_on=None,sigmask=manhole.ALL_SIGNALS,socket_path=None,reinstall_delay=0.5,locals=None,strict=True,)verbose- Set it toFalseto squelch the logging.verbose_destination- Destination for verbose messages. Set it to a file descriptor or handle. Default is
unbuffered stderr (stderr2file descriptor).patch_fork- Set it toFalseif you don’t want youros.forkandos.forkpymonkeypatchedactivate_on- Set to"USR1","USR2"or some other signal name, or a number if you want the Manhole thread
to start when this signal is sent. This is desirable in case you don’t want the thread active all the time.thread- Set toTrueto start the always-on ManholeThread. Default:True.
Automatically switched toFalseifoneshot_onoractivate_onare used.oneshot_on- Set to"USR1","USR2"or some other signal name, or a number if you want the Manhole to
listen for connection in the signal handler. This is desireable in case you don’t want threads at all.sigmask- Will set the signal mask to the given list (usingsignalfd.sigprocmask). No action is done ifsignalfdis not importable.NOTE: This is done so that the Manhole thread doesn’tstealany signals;
Normally that is fine because Python will force all the signal handling to be run in the main thread but signalfd
doesn’t.socket_path- Use a specific path for the unix domain socket (instead of/tmp/manhole-<pid>). This disablespatch_forkas children cannot reuse the same path.reinstall_delay- Delay the unix domain socket creationreinstall_delayseconds. This alleviates
cleanup failures when using fork+exec patterns.locals- Names to add to manhole interactive shell locals.daemon_connection- The connection thread is daemonic (dies on app exit). Default:False.redirect_stderr- Redirect output from stderr to manhole console. Default:True.strict- IfTruethenAlreadyInstalledwill be raised when attempting to install manhole twice.
Default:True.Environment variable installationManhole can be installed via thePYTHONMANHOLEenvironment variable.This:PYTHONMANHOLE='' python yourapp.pyIs equivalent to having this inyourapp.py:import manhole
manhole.install()Any extra text in the environment variable is passed tomanhole.install(). Example:PYTHONMANHOLE='onshot_on="USR2"' python yourapp.pyWhat happens when you actually connect to the socketCredentials are checked (if it’s same user or root)sys.__std*__/sys.std*are redirected to the UDSStacktraces for each thread are written to the UDSREPL is started so you can fiddle with the processKnown issuesUsing threads and file handle (not raw file descriptor)verbose_destinationcan cause deadlocks. See bug reports:PyPyandPython 3.4.SIGTERM and socket cleanupBy default Python doesn’t call theatexitcallbacks with the default SIGTERM handling. This makes manhole leave
stray socket files around. If this is undesirable you should install a custom SIGTERM handler soatexitis
properly invoked.Example:importsignalimportsysdefhandle_sigterm(signo,frame):sys.exit(128+signo)# this will raise SystemExit and cause atexit to be calledsignal.signal(signal.SIGTERM,handle_sigterm)Using Manhole with uWSGIBecause uWSGI overrides signal handling Manhole is a bit more tricky to setup. One way is to use “uWSGI signals” (not
the POSIX signals) and have the workers check a file for the pid you want to open the Manhole in.Stick something this in your WSGI application file:from__future__importprint_functionimportsysimportosimportmanholestack_dump_file='/tmp/manhole-pid'uwsgi_signal_number=17try:importuwsgiifnotos.path.exists(stack_dump_file):open(stack_dump_file,'w')defopen_manhole(dummy_signum):withopen(stack_dump_file,'r')asfh:pid=fh.read().strip()ifpid==str(os.getpid()):inst=manhole.install(strict=False,thread=False)inst.handle_oneshot(dummy_signum,dummy_signum)uwsgi.register_signal(uwsgi_signal_number,'workers',open_manhole)uwsgi.add_file_monitor(uwsgi_signal_number,stack_dump_file)print("Listening for stack mahole requests via%r"%(stack_dump_file,),file=sys.stderr)exceptImportError:print("Not running under uwsgi; unable to configure manhole trigger",file=sys.stderr)exceptIOError:print("IOError creating manhole trigger%r"%(stack_dump_file,),file=sys.stderr)# somewhere bellow you'd have something likefromdjango.core.wsgiimportget_wsgi_applicationapplication=get_wsgi_application()# ordefapplication(environ,start_response):start_response('200 OK',[('Content-Type','text/plain'),('Content-Length','2')])yieldb'OK'To open the Manhole just runecho 1234 > /tmp/manhole-pidand thenmanhole-cli 1234.RequirementsOS:Linux, OS XRuntime:Python 2.7, 3.4, 3.5, 3.6 or PyPySimilar projectsTwisted’smanhole- it has colors and
server-side history.wsgi-shell- spawns a thread.pyrasite- uses gdb to inject code.pydbattach- uses gdb to inject code.pystuck- very similar, usesrpycfor
communication.pyringe- uses gdb to inject code, more reliable, but relies ondbgpython
builds unfortunatelly.pdb-clone- uses gdb to inject code, with adifferent strategy.Changelog1.8.0 (2021-04-08)Simplified connection closing code.
Contributed by Anton Ryzhov in#62.Made connection shutdown inmanhole-climore graceful.
Contributed by Anton Ryzhov in#63.1.7.0 (2021-03-22)Fixed memory leak viasys.last_type,sys.last_value,sys.last_traceback.
Contributed by Anton Ryzhov in#59.Fixed a bunch of double-close bugs and simplified stream handler code.
Contributed by Anton Ryzhov in#58.Loosen uppidargument parsing inmanhole-clito allow using paths with any prefix
(not just/tmp).1.6.0 (2019-01-19)Testing improvements (changed some skips to xfail, added osx in Travis).Fixed long standing Python 2.7 bug wheresys.getfilesystemencoding()would be broken after installing a threaded
manhole. See#51.Dropped support for Python 2.6, 3.3 and 3.4.Fixed handling whensocket.setdefaulttimeout()is used.
Contributed by “honnix” in#53.Fixed some typos. Contributed by Jesús Cea in#43.Fixed handling inmanhole-cliso that timeout is actually seconds and not milliseconds.
Contributed by Nir Soffer in#45.Cleaned up useless polling options inmanhole-cli.
Contributed by Nir Soffer in#46.Documented and implemented a solution for using Manhole with Eventlet.
See#49.1.5.0 (2017-08-31)Added two string aliases forconnection_handleroption. Now you can conveniently useconnection_handler="exec".Improvedhandle_connection_exec. It now has a clean way to exit (exit()) and properly closes the socket.1.4.0 (2017-08-29)Added theconnection_handlerinstall option. Default value ismanhole.handle_connection_repl, and alternatemanhole.handle_connection_execis provided (very simple: no output redirection, no stacktrace dumping).Dropped Python 3.2 from the test grid. It may work but it’s a huge pain to support (pip/pytest don’t support it anymore).Added Python 3.5 and 3.6 in the test grid.Fixed issues with piping tomanhole-cli. Nowecho foobar |manhole-cliwill wait 1 second for output from manhole
(you can customize this with the--timeoutoption).Fixed issues with newer PyPy (caused by gevent/eventlet socket unwrapping).1.3.0 (2015-09-03)Allowed Manhole to be configured without any thread or activation (in case you want to manually activate).Added an example and tests for using Manhole with uWSGi.Fixed error handling inmanhole-clion Python 3 (exc vars don’t leak anymore).Fixed support for running in gevent/eventlet-using apps on Python 3 (now that they support Python 3).Allowed reinstalling the manhole (in non-strictmode). Previous install is undone.1.2.0 (2015-07-06)Changedmanhole-cli:Won’t spam the terminal with errors if socket file doesn’t exist.Allowed sending any signal (new--signalargument).Fixed some validation issues for thePIDargument.1.1.0 (2015-06-06)Added support for installing the manhole via thePYTHONMANHOLEenvironment variable.Added astrictinstall option. Set it to false to avoid getting theAlreadyInstalledexception.Added amanhole-cliscript that emulatessocat readlineunix-connect:/tmp/manhole-1234.1.0.0 (2014-10-13)Addedsocket_pathinstall option (contributed byNir Soffer).Addedreinstall_delayinstall option.Addedlocalsinstall option (contributed byNir Soffer).Addedredirect_stderrinstall option (contributed byNir Soffer).Lots of internals cleanup (contributed byNir Soffer).0.6.2 (2014-04-28)Fix OS X regression.0.6.1 (2014-04-28)Support for OS X (contributed bySaulius Menkevičius).