Anton Pererva
Anton Pererva

Reputation: 329

Remote hotplug Python debugger

How I can setup a hotpluggable remote debugger with Python IDE integration? For example using PyCharm. By hotpluggable I mean: can connect and disconnect from dev server on fly.

I have dev server in the cloud (django, nginx, uwsgi, postgres, debian), and use PyCharm as the main IDE (but if you have solution for any other IDE, please provide it).

Sometimes, I need to connect and debug scripts without stopping/restarting the dev server. Using PyCharm's debugger (pydevd), dev server cannot start without working debugger server (Connection refused), and if I stop the remote debugger while the dev server is running, it crashes e.g.

An existing connection was forcibly closed by the remote host

I found pdg/epdg but they have no integration with PyCharm. Also PyCharm has a nice feature : "Attach to process", but it works only with local processes.

Upvotes: 1

Views: 273

Answers (1)

rocky
rocky

Reputation: 7098

The basic approach that would probably work is to set up a signal handler to the program you want to debug to load in a debugger. Then sending the process a signal to debug (via the kill command) you could then attach remotely.

Here is how you'd do this via the python trepan debuggers

import signal

def signal_handler(num, f):
    from trepan.interfaces import server as Mserver
    from trepan.api import debug
    connection_opts={'IO': 'TCP', 'PORT': 1955}
    intf = Mserver.ServerInterface(connection_opts=connection_opts)
    dbg_opts = {'interface': intf}
    print('Starting TCP server listening on port 1955.')
    debug(dbg_opts=dbg_opts)
    return

signal.signal(signal.SIGUSR1, signal_handler)
# Go about your business...

import time
import os
print(os.getpid())
for i in range(10000):
    time.sleep(0.2)

Now run that:

$ python /tmp/foo.py
8530

From above output we helpfully listed the pid of the Python process we want to debug.

Now in a shell we send a signal to tell the process to go into the debugger that is set up in the signal handler. You will have to adjust the process id.

$ kill -USR1 8530   # Adjust the pid to what you see above

And in the shell where we ran /tmp/foo.py you should now see the new output:

$ python /tmp/foo.py
8530
Starting TCP server listening on port 1955. # This is new

Back to the shell where we issued the kill -USR1, we now attach the the process now stopped inside a debugger:

$ trepan2 --client --port 1955
Connected.
(/tmp/foo.py:11 @101): signal_handler
-- 11     return
(trepan2*) list
  6         connection_opts={'IO': 'TCP', 'PORT': 1955}
  7         intf = Mserver.ServerInterface(connection_opts=connection_opts)
  8         dbg_opts = {'interface': intf}
  9         print('Starting TCP server listening on port 1955.')
 10         debug(dbg_opts=dbg_opts)
 11  ->     return
 12
 13     signal.signal(signal.SIGUSR1, signal_handler)
 14     # Go about your business...
 (trepan2*) backtrace
 ->   0 signal_handler(num=10, f=<frame object at 0x7f9036796050>)
      called from file '/tmp/foo.py' at line 11
 ##   1 <module> file '/tmp/foo.py' at line 20

Upvotes: 1

Related Questions