Slightly nerf gallic naked fanatic rush.
[0ad.git] / source / tools / webservices / profilemiddleware.py
blob39bc8cca2cba3dcfe6ac476733d6f0f73602ac7d
1 # http://www.no-ack.org/2010/12/yet-another-profiling-middleware-for.html
3 import os
4 import re
5 import tempfile
6 from cStringIO import StringIO
8 from django.conf import settings
9 import hotshot
10 import hotshot.stats
12 COMMENT_SYNTAX = ((re.compile(r'^application/(.*\+)?xml|text/html$', re.I), '<!--', '-->'),
13 (re.compile(r'^application/j(avascript|son)$', re.I), '/*', '*/' ))
15 class ProfileMiddleware(object):
16 def process_view(self, request, callback, args, kwargs):
17 # Create a profile, writing into a temporary file.
18 filename = tempfile.mktemp()
19 profile = hotshot.Profile(filename)
21 try:
22 try:
23 # Profile the call of the view function.
24 response = profile.runcall(callback, request, *args, **kwargs)
26 # If we have got a 3xx status code, further
27 # action needs to be taken by the user agent
28 # in order to fulfill the request. So don't
29 # attach any stats to the content, because of
30 # the content is supposed to be empty and is
31 # ignored by the user agent.
32 if response.status_code // 100 == 3:
33 return response
35 # Detect the appropriate syntax based on the
36 # Content-Type header.
37 for regex, begin_comment, end_comment in COMMENT_SYNTAX:
38 if regex.match(response['Content-Type'].split(';')[0].strip()):
39 break
40 else:
41 # If the given Content-Type is not
42 # supported, don't attach any stats to
43 # the content and return the unchanged
44 # response.
45 return response
47 # The response can hold an iterator, that
48 # is executed when the content property
49 # is accessed. So we also have to profile
50 # the call of the content property.
51 content = profile.runcall(response.__class__.content.fget, response)
52 finally:
53 profile.close()
55 # Load the stats from the temporary file and
56 # write them in a human readable format,
57 # respecting some optional settings into a
58 # StringIO object.
59 stats = hotshot.stats.load(filename)
60 if getattr(settings, 'PROFILE_MIDDLEWARE_STRIP_DIRS', False):
61 stats.strip_dirs()
62 if getattr(settings, 'PROFILE_MIDDLEWARE_SORT', None):
63 stats.sort_stats(*settings.PROFILE_MIDDLEWARE_SORT)
64 stats.stream = StringIO()
65 stats.print_stats(*getattr(settings, 'PROFILE_MIDDLEWARE_RESTRICTIONS', []))
66 finally:
67 os.unlink(filename)
69 # Construct an HTML/XML or Javascript comment, with
70 # the formatted stats, written to the StringIO object
71 # and attach it to the content of the response.
72 comment = '\n%s\n\n%s\n\n%s\n' % (begin_comment, stats.stream.getvalue().strip(), end_comment)
73 response.content = content + comment
75 # If the Content-Length header is given, add the
76 # number of bytes we have added to it. If the
77 # Content-Length header is ommited or incorrect,
78 # it remains so in order to don't change the
79 # behaviour of the web server or user agent.
80 if response.has_header('Content-Length'):
81 response['Content-Length'] = int(response['Content-Length']) + len(comment)
83 return response