Cross-platform remote Python debugging

Overview

Python Tools already provides a number of facilities to debug apps launched from the IDE itself, as well as attaching to existing Python processes on both local and remote machines. However, these all only work if the process being debugged is running on a Windows PC, and attaching remotely also requires the Visual Studio Remote Debugging Monitor (MSVSMon) to be running on the remote PC. For situations when your app is running on a different operating system, such as Linux or OS X, or when you do not have permissions to install or run MSVSMon on a Windows machine, Python Tools 2.0 adds an alternative remote debugging option that does not require any separate processes, and runs on any OS capable of running Python itself.

In this mode, the Python script being debugged also hosts the debug server to which the IDE then attaches over TCP. Consequently, it requires the source code of your script to be modified to import the debug server package and launch the server.

Preparing the script for debugging

The following Python script will be used for demonstration purposes (taken without modifications from the Python wiki):
import random

guesses_made = 0
name = raw_input('Hello! What is your name?\n')
number = random.randint(1, 20)
print 'Well, {0}, I am thinking of a number between 1 and 20.'.format(name)

while guesses_made < 6:
    guess = int(raw_input('Take a guess: '))
    guesses_made += 1
    if guess < number:
        print 'Your guess is too low.'
    if guess > number:
        print 'Your guess is too high.'
    if guess == number:
        break
if guess == number:
    print 'Good job, {0}! You guessed my number in {1} guesses!'.format(name, guesses_made)
else:
    print 'Nope. The number I was thinking of was {0}'.format(number)

Python Tools debug server is contained in the ptvsd package that comes with Python Tools. In alpha release, it has to be located on the machine where Python Tools is installed, and manually copied over to the remote machine such that it can be imported from the script to be debugged. The easiest and least intrusive way to do so is to copy it to the same directory as the script being debugged; other options are to copy it to site-packages, or to some other directory that is in PYTHONPATH.

Location of the ptvsd package on the machine with Python Tools depends on whether the installation was global or per-user. For global installation, it is located in the folder where Visual Studio is installed (typically C:\Program Files (x86)\Microsoft Visual Studio 11.0), in Common7\IDE\Extensions\Microsoft\Python Tools for Visual Studio\2.0 subfolder. For per-user install, it is located in your user folder (typically C:\Users\your-name in AppData\Local\Microsoft\VisualStudio\11.0\Extensions\Microsoft\Python Tools for Visual Studio\2.0 subfolder. In future versions, there will be a menu item provided to automatically open Explorer in the right location.

An SFTP client, such as WinSCP, can be used to copy the directory containing the package over to the remote machine if it’s running Linux. After the package is made available for import on the remote machine, the script needs to have the following two lines of code added to enable remote debugging:
import ptvsd
ptvsd.enable_attach(secret = 'joshua')

Preferably, these lines should be inserted at the first line of the script, so that they are executed before any other code in it. It is not a strict requirement, but it will be impossible to debug any background threads spawned before enable_attach function is invoked.

The secret parameter passed to enable_attach is used to prevent random people from connecting to the running script – it will have to be specified in Visual Studio when connecting, and connections will only be permitted when the specified secret matches the one used by the script. If you wish to disable this, and allow anyone to connect, you should use enable_attach(secret=None).

After making the necessary edits, start the script. Note that enable_attach call does not block – the debug server is launched on the background thread and awaits incoming connections, while your program continues to run. A separate function wait_for_attach is provided by ptvsd package if it is more desirable to wait until the debugger is attached before running.

In addition to the two functions mentioned above, ptvsd also provides a helper function break_into_debugging, which serves as a programmatic breakpoint. This can be useful when you need a breakpoint to be triggered only when some complicated condition is true.

Attaching remotely from Python Tools

The most common scenario for remote debugging is to set a breakpoint in the source and run the script until that breakpoint is hit. Note that you will need a local copy of the source file being debugged on the machine running Python Tools. It does not matter where the file is located, but its name should match the name of the actual script on the remote machine that will be attached to:

RemoteDebuggingScreenshot1.png

Attaching is done using Debug → Attach to Process menu command, which opens the Attach to Process dialog. The first thing to do in it is to open the “Transport” combo box, and change selection to “Python remote debugging (unsecured)”:

RemoteDebuggingScreenshot2.png

After that, the “Qualifier” textbox and the “Available Processes” list will be blank, and “Transport information” will provide a brief reminder of the steps necessary to set up remote debugging. Now, type the address of the remote machine, prefixed with the secret that was defined in the script, into the “Qualifier” textbox, and press Enter:

RemoteDebuggingScreenshot3.png

At this point, Python Tools will try to connect to the debug server on the remote machine and obtain various process information from it to display in the “Available Processes” list. If some error occurs (e.g. a connection could not be established, or the secret did not match), an error message will be shown. One of the typical causes of error is that the remote machine is behind a firewall that does not have the debug server port (default is 5678) open. The workaround is to open the port, or, if it is not possible, to use a different port; the latter can be done by explicitly specifying it in the call to enable_attach in the address parameter, e.g.:
ptvsd.enable_attach(secret = 'joshua', address = ('0.0.0.0', 8080)) 

Address format is the same as the one used by the standard Python module socket for sockets of type AF_INET; see its documentation for details.

Once the process appears in the list, it can be double-clicked to attach to it. Visual Studio changes into the debugging perspective, while the script continues to run. Enter some number to hit the breakpoint:

RemoteDebuggingScreenshot4.png

From there on, you can use all the usual debugging features offered by Python Tools.

If you stop debugging, Visual Studio detaches from your script, but it continues running. The debug server continues running on a background thread, as well, so you can re-attach to the script later using the same procedure.

Last edited Mar 7 at 10:10 PM by pminaev, version 8

Comments

No comments yet.