SaltyCrane Blog — Notes on Javascript and web development

Notes on tracing code execution in Django and Python

The trace module causes Python to print lines of code as they are executed. I learned about trace via @brandon_rhodes's tweet.

Trace a Python program

python -m trace -t myprogram.py myargs 

Trace with a Django development server

From my experience, trace doesn't work with Django's auto-reloader. Use --noreload option

python -m trace -t manage.py runserver --noreload 

Tracing with more control

This article shows how to write custom functions that are passed to sys.settrace.

Django trace tool, django-trace

I wrote a Django management command that uses sys.settrace with other Django management commands. https://github.com/saltycrane/django-trace.

Install

  • $ pip install -e git://github.com/saltycrane/django-trace.git#egg=django_trace 
  • Add 'django_trace' to INSTALLED_APPS in settings.py

Usage

$ python manage.py trace --help 
Usage: manage.py trace [options] [command]

Use sys.settrace to trace code

Options:
  -v VERBOSITY, --verbosity=VERBOSITY
                        Verbosity level; 0=minimal output, 1=normal output,
                        2=verbose output, 3=very verbose output
  --settings=SETTINGS   The Python path to a settings module, e.g.
                        "myproject.settings.main". If this isn't provided, the
                        DJANGO_SETTINGS_MODULE environment variable will be
                        used.
  --pythonpath=PYTHONPATH
                        A directory to add to the Python path, e.g.
                        "/home/djangoprojects/myproject".
  --traceback           Print traceback on exception
  --include-builtins    Include builtin functions (default=False)
  --include-stdlib      Include standard library modules (default=False)
  --module-only         Display module names only (not lines of code)
  --calls-only          Display function calls only (not lines of code)
  --good=GOOD           Comma separated list of exact module names to match
  --bad=BAD             Comma separated list of exact module names to exclude
                        (takes precedence over --good and --good-regex)
  --good-regex=GOOD_REGEX
                        Regular expression of module to match
  --bad-regex=BAD_REGEX
                        Regular expression of module to exclude (takes
                        precedence over --good and --good-regex)
  --good-preset=GOOD_PRESET
                        A key in the GOOD_PRESETS setting
  --bad-preset=BAD_PRESET
                        A key in the BAD_PRESETS setting
  --version             show program's version number and exit
  -h, --help            show this help message and exit

or

$ python manage.py trace runserver 
01->django.core.management:128:     try:
01->django.core.management:129:         app_name = get_commands()[name]
02-->django.core.management:95:     if _commands is None:
02-->django.core.management:114:     return _commands
01->django.core.management:130:         if isinstance(app_name, BaseCommand):
01->django.core.management:134:             klass = load_command_class(app_name, name)
02-->django.core.management:69:     module = import_module('%s.management.commands.%s' % (app_name, name))
03--->django.utils.importlib:26:     if name.startswith('.'):
03--->django.utils.importlib:35:     __import__(name)
04---->django.contrib.staticfiles.management.commands.runserver:1: from optparse import make_option
04---->django.contrib.staticfiles.management.commands.runserver:3: from django.conf import settings
04---->django.contrib.staticfiles.management.commands.runserver:4: from django.core.management.commands.runserver import BaseRunserverCommand
05----->django.core.management.commands.runserver:1: from optparse import make_option
05----->django.core.management.commands.runserver:2: import os
05----->django.core.management.commands.runserver:3: import re
05----->django.core.management.commands.runserver:4: import sys
05----->django.core.management.commands.runserver:5: import socket
05----->django.core.management.commands.runserver:7: from django.core.management.base import BaseCommand, CommandError
05----->django.core.management.commands.runserver:8: from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException, get_internal_wsgi_application
06------>django.core.servers.basehttp:8: """
06------>django.core.servers.basehttp:10: import os
06------>django.core.servers.basehttp:11: import socket
06------>django.core.servers.basehttp:12: import sys
06------>django.core.servers.basehttp:13: import traceback
...

or

$ python manage.py trace --bad=django,SocketServer --calls-only runserver 
01->wsgiref:23: """
01->wsgiref.simple_server:11: """
02-->BaseHTTPServer:18: """
03--->BaseHTTPServer:102: class HTTPServer(SocketServer.TCPServer):
03--->BaseHTTPServer:114: class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
02-->wsgiref.handlers:1: """Base classes for server/gateway implementations"""
03--->wsgiref.util:1: """Miscellaneous WSGI-related Utilities"""
04---->wsgiref.util:11: class FileWrapper:
03--->wsgiref.headers:6: """
04---->wsgiref.headers:42: class Headers:
03--->wsgiref.handlers:43: class BaseHandler:
03--->wsgiref.handlers:371: class SimpleHandler(BaseHandler):
03--->wsgiref.handlers:412: class BaseCGIHandler(SimpleHandler):
03--->wsgiref.handlers:453: class CGIHandler(BaseCGIHandler):
02-->wsgiref.simple_server:26: class ServerHandler(SimpleHandler):
02-->wsgiref.simple_server:42: class WSGIServer(HTTPServer):
02-->wsgiref.simple_server:83: class WSGIRequestHandler(BaseHTTPRequestHandler):
01->contextlib:53: def contextmanager(func):
01->contextlib:53: def contextmanager(func):
Validating models...

0 errors found
Django version 1.4, using settings 'myproj.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
01->myproj.wsgi:15: """
01->wsgiref.simple_server:48:     def server_bind(self):
02-->BaseHTTPServer:106:     def server_bind(self):
02-->wsgiref.simple_server:53:     def setup_environ(self):
01->wsgiref.simple_server:53:     def setup_environ(self):
01->wsgiref.simple_server:66:     def set_app(self,application)

Trace decorator

I also wrote a decorator that traces code execution of the function it is decorating: https://github.com/saltycrane/trace-tools

Install

pip install -e git://github.com/saltycrane/trace-tools.git#egg=trace_tools 

Usage

from trace_tools.decorators import trace

@trace()
def some_function_to_trace(arg):
    do_something()

@trace(max_level=2)
def some_function_to_trace(arg):
    do_something()

@another_decorator
@trace(
    max_level=4,
    ignore=(
        'httplib', 'logging', 'ssl', 'email', 'encodings', 'gzip', 'urllib',
        'multiprocessing', 'django', 'cgi', 'requests', 'cookielib', 'base64',
        'slumber', 'zipfile', 'redis'))
def some_other_function():
    do_something_else()

@trace(max_level=10, calls_only=False, ignore=('debugtools', 'blessings', 'ipdb', 'IPython',), ignore_builtins=True, ignore_stdlib=True)
def process(self, content):
    do_stuff()

@trace(max_level=115, calls_only=True, ignore=(
        'suds.resolver',
        'suds.sudsobject',
        'suds.xsd',
        'debugtools', 'blessings', 'ipdb', 'IPython',),
       ignore_builtins=True, ignore_stdlib=True)
def process(self, content):
    do_stuff()

Comments