web/lib/django_extensions/management/commands/runprofileserver.py
changeset 3 526ebd3988b0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/lib/django_extensions/management/commands/runprofileserver.py	Wed Jan 20 12:37:40 2010 +0100
@@ -0,0 +1,213 @@
+"""
+runprofileserver.py
+
+    Starts a lightweight Web server with profiling enabled.
+
+Credits for kcachegrind support taken from lsprofcalltree.py go to:
+ David Allouche
+ Jp Calderone & Itamar Shtull-Trauring
+ Johan Dahlin
+"""
+
+from django.core.management.base import BaseCommand, CommandError
+from optparse import make_option
+import os
+import sys
+
+def label(code):
+    if isinstance(code, str):
+        return ('~', 0, code)    # built-in functions ('~' sorts at the end)
+    else:
+        return '%s %s:%d' % (code.co_name,
+                             code.co_filename,
+                             code.co_firstlineno)
+
+class KCacheGrind(object):
+    def __init__(self, profiler):
+        self.data = profiler.getstats()
+        self.out_file = None
+
+    def output(self, out_file):
+        self.out_file = out_file
+        print >> out_file, 'events: Ticks'
+        self._print_summary()
+        for entry in self.data:
+            self._entry(entry)
+
+    def _print_summary(self):
+        max_cost = 0
+        for entry in self.data:
+            totaltime = int(entry.totaltime * 1000)
+            max_cost = max(max_cost, totaltime)
+        print >> self.out_file, 'summary: %d' % (max_cost,)
+
+    def _entry(self, entry):
+        out_file = self.out_file
+
+        code = entry.code
+        #print >> out_file, 'ob=%s' % (code.co_filename,)
+        if isinstance(code, str):
+            print >> out_file, 'fi=~'
+        else:
+            print >> out_file, 'fi=%s' % (code.co_filename,)
+        print >> out_file, 'fn=%s' % (label(code),)
+
+        inlinetime = int(entry.inlinetime * 1000)
+        if isinstance(code, str):
+            print >> out_file, '0 ', inlinetime
+        else:
+            print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
+
+        # recursive calls are counted in entry.calls
+        if entry.calls:
+            calls = entry.calls
+        else:
+            calls = []
+
+        if isinstance(code, str):
+            lineno = 0
+        else:
+            lineno = code.co_firstlineno
+
+        for subentry in calls:
+            self._subentry(lineno, subentry)
+        print >> out_file
+
+    def _subentry(self, lineno, subentry):
+        out_file = self.out_file
+        code = subentry.code
+        #print >> out_file, 'cob=%s' % (code.co_filename,)
+        print >> out_file, 'cfn=%s' % (label(code),)
+        if isinstance(code, str):
+            print >> out_file, 'cfi=~'
+            print >> out_file, 'calls=%d 0' % (subentry.callcount,)
+        else:
+            print >> out_file, 'cfi=%s' % (code.co_filename,)
+            print >> out_file, 'calls=%d %d' % (
+                subentry.callcount, code.co_firstlineno)
+
+        totaltime = int(subentry.totaltime * 1000)
+        print >> out_file, '%d %d' % (lineno, totaltime)
+
+class Command(BaseCommand):
+    option_list = BaseCommand.option_list + (
+        make_option('--noreload', action='store_false', dest='use_reloader', default=True,
+            help='Tells Django to NOT use the auto-reloader.'),
+        make_option('--adminmedia', dest='admin_media_path', default='',
+            help='Specifies the directory from which to serve admin media.'),
+        make_option('--prof-path', dest='prof_path', default='/tmp',
+            help='Specifies the directory which to save profile information in.'),
+        make_option('--nomedia', action='store_true', dest='no_media', default=False,
+            help='Do not profile MEDIA_URL and ADMIN_MEDIA_URL'),
+        make_option('--use-cprofile', action='store_true', dest='use_cprofile', default=False,
+            help='Use cProfile if available, this is disabled per default because of incompatibilities.'),
+        make_option('--kcachegrind', action='store_true', dest='use_lsprof', default=False,
+            help='Create kcachegrind compatible lsprof files, this requires and automatically enables cProfile.'),
+    )
+    help = "Starts a lightweight Web server with profiling enabled."
+    args = '[optional port number, or ipaddr:port]'
+
+    # Validation is called explicitly each time the server is reloaded.
+    requires_model_validation = False
+
+    def handle(self, addrport='', *args, **options):
+        import django
+        from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
+        from django.core.handlers.wsgi import WSGIHandler
+        if args:
+            raise CommandError('Usage is runserver %s' % self.args)
+        if not addrport:
+            addr = ''
+            port = '8000'
+        else:
+            try:
+                addr, port = addrport.split(':')
+            except ValueError:
+                addr, port = '', addrport
+        if not addr:
+            addr = '127.0.0.1'
+
+        if not port.isdigit():
+            raise CommandError("%r is not a valid port number." % port)
+
+        use_reloader = options.get('use_reloader', True)
+        admin_media_path = options.get('admin_media_path', '')
+        shutdown_message = options.get('shutdown_message', '')
+        no_media = options.get('no_media', False)
+        quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
+
+        def inner_run():
+            from django.conf import settings
+
+            import hotshot, time, os
+            USE_CPROFILE = options.get('use_cprofile', False)
+            USE_LSPROF = options.get('use_lsprof', False)
+            if USE_LSPROF:
+               USE_CPROFILE = True
+            if USE_CPROFILE:
+                try:
+                    import cProfile
+                    USE_CPROFILE = True
+                except ImportError:
+                    print "cProfile disabled, module cannot be imported!"
+                    USE_CPROFILE = False
+            if USE_LSPROF and not USE_CPROFILE:
+                raise SystemExit("Kcachegrind compatible output format required cProfile from Python 2.5")
+            prof_path = options.get('prof_path', '/tmp')
+            def make_profiler_handler(inner_handler):
+                def handler(environ, start_response):
+                    path_info = environ['PATH_INFO']
+                    # normally /media/ is MEDIA_URL, but in case still check it in case it's differently
+                    # should be hardly a penalty since it's an OR expression.
+                    # TODO: fix this to check the configuration settings and not make assumpsions about where media are on the url
+                    if no_media and (path_info.startswith('/media') or path_info.startswith(settings.MEDIA_URL)):
+                        return inner_handler(environ, start_response)
+                    path_name = path_info.strip("/").replace('/', '.') or "root"
+                    profname = "%s.%.3f.prof" % (path_name, time.time())
+                    profname = os.path.join(prof_path, profname)
+                    if USE_CPROFILE:
+                        prof = cProfile.Profile()
+                    else:
+                        prof = hotshot.Profile(profname)
+                    try:
+                        return prof.runcall(inner_handler, environ, start_response)
+                    finally:
+                        if USE_LSPROF:
+                            kg = KCacheGrind(prof)
+                            kg.output(file(profname, 'w'))
+                        elif USE_CPROFILE:
+                            prof.dump_stats(profname)
+                return handler
+
+            print "Validating models..."
+            self.validate(display_num_errors=True)
+            print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE)
+            print "Development server is running at http://%s:%s/" % (addr, port)
+            print "Quit the server with %s." % quit_command
+            try:
+                path = admin_media_path or django.__path__[0] + '/contrib/admin/media'
+                handler = make_profiler_handler(AdminMediaHandler(WSGIHandler(), path))
+                run(addr, int(port), handler)
+            except WSGIServerException, e:
+                # Use helpful error messages instead of ugly tracebacks.
+                ERRORS = {
+                    13: "You don't have permission to access that port.",
+                    98: "That port is already in use.",
+                    99: "That IP address can't be assigned-to.",
+                }
+                try:
+                    error_text = ERRORS[e.args[0].args[0]]
+                except (AttributeError, KeyError):
+                    error_text = str(e)
+                sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n')
+                # Need to use an OS exit because sys.exit doesn't work in a thread
+                os._exit(1)
+            except KeyboardInterrupt:
+                if shutdown_message:
+                    print shutdown_message
+                sys.exit(0)
+        if use_reloader:
+            from django.utils import autoreload
+            autoreload.main(inner_run)
+        else:
+            inner_run()