Debugging Python CGI Scripts with Wing
Wing is a Python IDE that can be used to develop, test, and debug CGI scripts written in Python. Debugging takes place in the context of the web server, as scripts are invoked during a browser page load. Wing also provides auto-completion, call tips, find uses, and many other features that help you write, navigate, and understand Python code.
Two versions of Wing are appropriate for use with this document: Wing Pro is the full-featured Python IDE for professional programmers, and Wing Personal is a free alternative with reduced feature set.
If you do not already have Wing installed, download it now.
To get started using Wing, refer to the tutorial in the Help menu in Wing or the Quickstart Guide.
To set up your CGIs for debugging with Wing, refer to the Debugging Externally Launched Code section of the manual. Pay careful attention to the permissions on files, especially if your web server is running as a different user than the process that is running Wing. You will also need to make sure that the wingdebugpw file is referenced correctly as described in the instructions.
Tips and Tricks
The rest of this guide provides some tips specific to the task of debugging CGIs:
(1) If Wing is failing to stop on breakpoints, check whether you are loading a web page that loads multiple parts with separate http requests -- in that case, Wing may still be busy processing an earlier CGI request when a new one comes in and will fail to stop on breakpoints because only one debug process is serviced at a time. This is a limitation in Wing. The work-around is to load specific parts of the page in the browser by entering the URL you wish to debug.
(2) Any content from your CGI script that isn't understood by the web server will be written to the server's error log. Since this can be annoying to search through, it is much easier to ensure that all output, including output made in error, is displayed in your web browser.
To do this, insert the following at the very start of your code, before importing wingdbstub or calling the debugger API:
print "Content-type: text/html\n\n\n<html>\n"
(In Python 3.x, use print() instead of print)
This will cause all subsequent data to be included in the browser window, even if your normal Content-type specifier code is not being reached.
(3) Place a catch-all exception handler at the top level of your CGI code and print exception information to the browser. The following function is useful for inspecting the state of the CGI environment when an exception occurs (in Python 3.x replace print with print()):
import sys import cgi import traceback import string #----------------------------------------------------------------------- def DisplayError(): """ Output an error page with traceback, etc """ print "<H2>An Internal Error Occurred!</H2>" print "<I>Runtime Failure Details:</I><P>" t, val, tb = sys.exc_info() print "<P>Exception = ", t, "<br>" print "Value = ", val, "\n", "<p>" print "<I>Traceback:</I><P>" tbf = traceback.format_tb(tb) print "<pre>" for item in tbf: outstr = string.replace(item, '<', '<') outstr = string.replace(outstr, '>', '>') print string.replace(outstr, '\n', '\n'), "<BR>" print "</pre>" print "<P>" cgi.print_environ() print "<BR><BR>"
(4) If you are using wingdbstub.py, you can set kLogFile to receive extra information from the debug server, in order to debug problems connecting back to Wing.
(5) If you are unable to see script output that may be relevant to trouble-shooting, try invoking your CGI script from the command line. The script may fail but you will be able to see messages from the debug server, when those are enabled.
(6) If all else fails, read your web browser documentation to locate and read its error log file. On Linux with Apache, this is often in /var/log/httpd/error_log. Any errors not seen on the browser are appended there.
(7) Once you have the debugger working for one CGI script, you will have to set up the wingdbstub import in each and every other top-level CGI in the same way. Because this can be somewhat tedious, and because the import needs to happen at the start of each file (in the __main__ scope), it makes sense to develop your code so that all page loads for a site are through a single entry point CGI and page-specific behavior is obtained via dispatch within that CGI to other modules. With Python's flexible import and invocation features, this is relatively easy to do.