app/app_profiler/app_profiler.py
author Mario Ferraro <fadinlight@gmail.com>
Sun, 15 Nov 2009 22:12:20 +0100
changeset 3093 d1be59b6b627
parent 2857 bc793800116e
permissions -rw-r--r--
GMaps related JS changed to use new google namespace. Google is going to change permanently in the future the way to load its services, so better stay safe. Also this commit shows uses of the new melange.js module. Fixes Issue 634.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
     1
import cProfile
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
     2
import ppstats
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
     3
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
     4
from google.appengine.ext import webapp
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
     5
from google.appengine.api import memcache
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
     6
import google.appengine.ext.webapp.util
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
     7
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
     8
from email.MIMEMultipart import MIMEMultipart
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
     9
from email.Message import Message
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
    10
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
    11
import httplib
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    12
import logging
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
    13
import os.path
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
    14
import random
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    15
import re
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    16
import string
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    17
import zlib
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    18
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    19
mc_client = memcache.Client()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    20
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    21
alphanumeric = string.letters + string.digits
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    22
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    23
global_profiler = None
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    24
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    25
class GAEProfiler(object):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    26
    _save_every = 10
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    27
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    28
    def __init__(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    29
        self.is_profiling = False
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    30
        self._profiler = None
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    31
        self.num_requests = 0
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    32
        self.requests_profiled = 0
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    33
        self.request_regex = None
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    34
        self.profile_key = ''.join([random.choice(alphanumeric) for x in range(4)])
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    35
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    36
    def start_profiling(self, request_regex=None, num_requests=0):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    37
        "start profiling with this object, setting # of requests and filter"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    38
        if self.is_profiling:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    39
            return
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    40
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    41
        self.is_profiling = True
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    42
        if self._profiler is None:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    43
            self._profiler = cProfile.Profile()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    44
        self.num_requests = num_requests
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    45
        if request_regex:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    46
            self.request_regex = re.compile(request_regex)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    47
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    48
    def stop_profiling(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    49
        self.is_profiling = False
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    50
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    51
    def resume_profiling(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    52
        self.is_profiling = True
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    53
    
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    54
    def has_profiler(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    55
        return self._profiler is not None
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    56
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    57
    def get_pstats(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    58
        "return a ppstats object from current profile data"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    59
        gae_base_dir = '/'.join(webapp.__file__.split('/')[:-5])
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    60
        sys_base_dir = '/'.join(logging.__file__.split('/')[:-2])
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    61
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    62
        stats = ppstats.Stats(self._profiler)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    63
        stats.hide_directory(gae_base_dir, 'GAEHome')
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    64
        stats.hide_directory(sys_base_dir, 'SysHome')
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    65
        stats.strip_dirs()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    66
        return stats
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    67
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    68
    def runcall(self, func, *args, **kwargs):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    69
        "profile one call, incrementing requests_profiled and maybe saving stats"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    70
        self.requests_profiled += 1
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    71
        if self._profiler:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    72
            ret = self._profiler.runcall(func, *args, **kwargs)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    73
        else:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    74
            ret = func(*args, **kwargs)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    75
        
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    76
#        if (self.requests_profiled % self._save_every) == 0 or \
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    77
#                self.requests_profiled == self.num_requests:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    78
#            self.save_pstats_to_memcache()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    79
        self.save_pstats_to_memcache()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    80
        return ret
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    81
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    82
    def should_profile_request(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    83
        "check for # of requests profiled and that SCRIPT_NAME matches regex"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    84
        env = dict(os.environ)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    85
        script_name = env.get('SCRIPT_NAME', '')
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    86
        logging.info(script_name)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    87
        
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    88
        if self.num_requests and self.requests_profiled >= self.num_requests:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    89
            return False
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    90
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    91
        if self.request_regex and not self.request_regex.search(script_name):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    92
            return False
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    93
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    94
        return True
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    95
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    96
    def save_pstats_to_memcache(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    97
        "save stats from profiler object to memcache"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    98
        ps = self.get_pstats()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
    99
        output = ps.dump_stats_pickle()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   100
        compressed_data = zlib.compress(output, 3)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   101
        cache_key = cache_key_for_profile(self.profile_key)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   102
        mc_client.set(cache_key, compressed_data)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   103
        logging.info("Saved pstats to memcache with key %s" % cache_key)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   104
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   105
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   106
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   107
def get_global_profiler():
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   108
    global global_profiler
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   109
    if not global_profiler:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   110
        global_profiler = GAEProfiler()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   111
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   112
    return global_profiler
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   113
    
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   114
def new_global_profiler():
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   115
    global global_profiler
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   116
    global_profiler = GAEProfiler()
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   117
    return global_profiler
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   118
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   119
def cache_key_for_profile(profile_key):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   120
    "generate a memcache key"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   121
    return "ProfileData.%s" % profile_key
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   122
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   123
def load_pstats_from_memcache(profile_key):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   124
    "retrieve ppstats object"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   125
    mc_data = mc_client.get(cache_key_for_profile(profile_key))
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   126
    if not mc_data:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   127
        return None
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   128
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   129
    return ppstats.from_gz(mc_data)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   130
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   131
def get_stats_from_global_or_request(request_obj):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   132
    "get pstats for a key, or the global pstats"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   133
    key = request_obj.get('key', '')
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   134
    if key:
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   135
        gp = GAEProfiler()
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   136
        gp.profile_obj = load_pstats_from_memcache(key)
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   137
        gp.profile_key = key
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   138
        return gp
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   139
    else:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   140
        gp = get_global_profiler()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   141
        if not gp.has_profiler():
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   142
            return None
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   143
        return gp
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   144
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   145
def mime_upload_data_as_file(field_name, filename, body):
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   146
    part = Message()
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   147
    part['Content-Disposition'] = 'form-data; name="%s"; filename="%s"' % (field_name, filename)
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   148
    part['Content-Transfer-Encoding'] = 'binary'
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   149
    part['Content-Type'] = 'application/octet-stream'
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   150
    part['Content-Length'] = str(len(body))
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   151
    part.set_payload(body)
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   152
    return part
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   153
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   154
def mime_form_value(name, value):
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   155
    part = Message()
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   156
    part['Content-Disposition'] = 'form-data; name="%s"' % name
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   157
    part.set_payload(value)
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   158
    return part
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   159
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   160
class show_profile(webapp.RequestHandler):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   161
    def get(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   162
        ps = get_stats_from_global_or_request(self.request)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   163
        if not ps:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   164
            self.response.out.write("<body><html><h3>No profiler.</h3><html></body>")
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   165
            return
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   166
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   167
        ps.profile_obj.set_output(self.response.out)
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   168
        sort = self.request.get('sort', 'time')
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   169
        ps.profile_obj.sort_stats(sort)
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   170
        self.response.out.write("<body><html><pre>\n")
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   171
        ps.profile_obj.print_stats(30)
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   172
        self.response.out.write("</pre></html></body>")
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   173
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   174
class download_profile_data(webapp.RequestHandler):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   175
    def get(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   176
        ps = get_stats_from_global_or_request(self.request)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   177
        if not ps:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   178
            self.response.out.write("<body><html><h3>No profiler.</h3><html></body>")
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   179
            return            
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   180
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   181
        output = ps.profile_obj.dump_stats_pickle()
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   182
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   183
        self.response.headers['Content-Type'] = 'application/octet-stream'
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   184
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   185
        self.response.out.write(output)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   186
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   187
class send_profile_data(webapp.RequestHandler):
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   188
    def get(self):
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   189
        ps = get_stats_from_global_or_request(self.request)
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   190
        if not ps:
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   191
            self.response.out.write("<body><html><h3>No profiler.</h3><html></body>")
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   192
            return            
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   193
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   194
        dest = self.request.get('dest', '')
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   195
        if not dest:
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   196
            self.response.out.write("<body><html>No destination</html></body>")
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   197
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   198
        upload_form = MIMEMultipart('form-data')
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   199
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   200
        upload_filename =  'profile.%s.pstats' % ps.profile_key
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   201
        upload_field_name = 'profile_file'
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   202
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   203
        upload_form.attach(mime_upload_data_as_file('profile_file', upload_field_name, zlib.compress(ps.profile_obj.dump_stats_pickle())))
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   204
        upload_form.attach(mime_form_value('key_only', '1'))
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   205
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   206
        http_conn = httplib.HTTPConnection(dest)
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   207
        http_conn.connect()
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   208
        http_conn.request('POST', '/upload_profile', upload_form.as_string(), 
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   209
                          {'Content-Type': 'multipart/form-data; boundary=%s' % upload_form.get_boundary()})
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   210
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   211
        http_resp = http_conn.getresponse()
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   212
        remote_data = http_resp.read()
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   213
        if http_resp.status == 200:
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   214
            remote_url = "http://%s/view_profile?key=%s" % (dest, remote_data)
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   215
            self.response.out.write("<html><body>Success! <a href='%s'>%s</a></body></html>" % (remote_url, remote_url))
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   216
        else:
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   217
            self.response.out.write("Failure!\n%s: %s\n%s" % (http_resp.status, http_resp.reason, remote_data))
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   218
            
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   219
class show_profiler_status(webapp.RequestHandler):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   220
    def get(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   221
        gp = get_global_profiler()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   222
        if not gp.has_profiler:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   223
            self.response.out.write("<body><html><h3>No profiler.</h3><html></body>")
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   224
            return
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   225
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   226
        self.response.out.write("<html><body>")
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   227
        self.response.out.write("<b>Currently profiling:</b> %s<br>" % gp.is_profiling)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   228
        self.response.out.write("<b>Profile Key</b>: %s<br>" % gp.profile_key)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   229
        self.response.out.write("<b>Requests profiled so far:</b> %s<br>" % gp.requests_profiled)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   230
        self.response.out.write("<b>Requests to profile:</b> %s<br>" % gp.num_requests)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   231
        self.response.out.write("<b>Request regex:</b> %s<br>" % gp.request_regex)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   232
        self.response.out.write("</body></html>")
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   233
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   234
class start_profiler(webapp.RequestHandler):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   235
    def get(self):
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   236
        gp = new_global_profiler()
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   237
        gp.start_profiling()
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   238
        self.response.headers['Content-Type'] = "text/plain"
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   239
        self.response.out.write("Started profiling (key: %s).\n" % gp.profile_key)
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   240
        self.response.out.write("Retrieve saved results at <a href='/profiler/show?key=%(key)s'>/profiler/show?key=%(key)s).\n" % {'key':gp.profile_key})
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   241
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   242
class stop_profiler(webapp.RequestHandler):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   243
    def get(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   244
        gp = get_global_profiler()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   245
        gp.stop_profiling()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   246
        self.request.out.write("Content-Type: text/plain\n\n")
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   247
        self.request.out.write("done.")
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   248
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   249
class save_profile_data(webapp.RequestHandler):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   250
    def get(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   251
        gp = get_global_profiler()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   252
        
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   253
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   254
def _add_our_endpoints(application):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   255
    "insert our URLs into the application map"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   256
    url_mapping = [(regex.pattern, handler) for (regex, handler) in application._url_mapping]
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   257
    return webapp.WSGIApplication(url_mapping, debug=True)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   258
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   259
#
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   260
#  wrapper to for webapp applications
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   261
#
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   262
def run_wsgi_app(application):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   263
    "proxy webapp.util's call to profile when needed"
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   264
    gp = get_global_profiler()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   265
    if gp.is_profiling and gp.should_profile_request():
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   266
        return gp.runcall(google.appengine.ext.webapp.util.run_wsgi_app, *(application,))
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   267
    else:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   268
        return google.appengine.ext.webapp.util.run_wsgi_app(application)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   269
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   270
#
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   271
# middleware for django applications
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   272
#
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   273
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   274
class ProfileMiddleware(object):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   275
    def __init__(self):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   276
        self.profiler = None
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   277
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   278
    def process_request(self, request):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   279
        self.profiler = get_global_profiler()
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   280
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   281
    def process_view(self, request, callback, callback_args, callback_kwargs):
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   282
        if self.profiler.is_profiling:
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   283
            return self.profiler.runcall(callback, request, *callback_args, **callback_kwargs)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   284
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   285
application = webapp.WSGIApplication(
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   286
    [('/profiler/start', start_profiler),
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   287
     ('/profiler/stop', stop_profiler),
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   288
     ('/profiler/show', show_profile),
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   289
     ('/profiler/download', download_profile_data),
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   290
     ('/profiler/status', show_profiler_status),
2857
bc793800116e Updated to r11 of app_profiler
Sverre Rabbelier <srabbelier@gmail.com>
parents: 2848
diff changeset
   291
     ('/profiler/send', send_profile_data),
2832
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   292
     ],
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   293
    debug=True)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   294
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   295
    
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   296
def main():
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   297
    google.appengine.ext.webapp.util.run_wsgi_app(application)
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   298
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   299
if __name__ == '__main__':
2a0a7e081caf Profiling patch
Sverre Rabbelier <srabbelier@gmail.com>
parents:
diff changeset
   300
    main()