1
0
forked from 0ad/0ad
0ad/source/tools/webservices/profilemiddleware.py
2011-05-02 15:47:12 +00:00

84 lines
3.5 KiB
Python

# http://www.no-ack.org/2010/12/yet-another-profiling-middleware-for.html
import os
import re
import tempfile
from cStringIO import StringIO
from django.conf import settings
import hotshot
import hotshot.stats
COMMENT_SYNTAX = ((re.compile(r'^application/(.*\+)?xml|text/html$', re.I), '<!--', '-->'),
(re.compile(r'^application/j(avascript|son)$', re.I), '/*', '*/' ))
class ProfileMiddleware(object):
def process_view(self, request, callback, args, kwargs):
# Create a profile, writing into a temporary file.
filename = tempfile.mktemp()
profile = hotshot.Profile(filename)
try:
try:
# Profile the call of the view function.
response = profile.runcall(callback, request, *args, **kwargs)
# If we have got a 3xx status code, further
# action needs to be taken by the user agent
# in order to fulfill the request. So don't
# attach any stats to the content, because of
# the content is supposed to be empty and is
# ignored by the user agent.
if response.status_code // 100 == 3:
return response
# Detect the appropriate syntax based on the
# Content-Type header.
for regex, begin_comment, end_comment in COMMENT_SYNTAX:
if regex.match(response['Content-Type'].split(';')[0].strip()):
break
else:
# If the given Content-Type is not
# supported, don't attach any stats to
# the content and return the unchanged
# response.
return response
# The response can hold an iterator, that
# is executed when the content property
# is accessed. So we also have to profile
# the call of the content property.
content = profile.runcall(response.__class__.content.fget, response)
finally:
profile.close()
# Load the stats from the temporary file and
# write them in a human readable format,
# respecting some optional settings into a
# StringIO object.
stats = hotshot.stats.load(filename)
if getattr(settings, 'PROFILE_MIDDLEWARE_STRIP_DIRS', False):
stats.strip_dirs()
if getattr(settings, 'PROFILE_MIDDLEWARE_SORT', None):
stats.sort_stats(*settings.PROFILE_MIDDLEWARE_SORT)
stats.stream = StringIO()
stats.print_stats(*getattr(settings, 'PROFILE_MIDDLEWARE_RESTRICTIONS', []))
finally:
os.unlink(filename)
# Construct an HTML/XML or Javascript comment, with
# the formatted stats, written to the StringIO object
# and attach it to the content of the response.
comment = '\n%s\n\n%s\n\n%s\n' % (begin_comment, stats.stream.getvalue().strip(), end_comment)
response.content = content + comment
# If the Content-Length header is given, add the
# number of bytes we have added to it. If the
# Content-Length header is ommited or incorrect,
# it remains so in order to don't change the
# behaviour of the web server or user agent.
if response.has_header('Content-Length'):
response['Content-Length'] = int(response['Content-Length']) + len(comment)
return response