From 9a8fe9deea8ca47b8accc7abb84a7746c84881eb Mon Sep 17 00:00:00 2001 From: jferry Date: Mon, 17 Jun 2013 00:04:37 +0200 Subject: [PATCH 1/2] first silly unit test and tox.in file to insure cross python compatibilty --- .gitignore | 62 +++++++++++++++++++++++++++++++++--- profiler/tests.py | 16 ---------- profiler/tests/__init__.py | 4 +++ profiler/tests/settings.py | 19 +++++++++++ profiler/tests/tests.py | 9 ++++++ tox.ini | 65 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 155 insertions(+), 20 deletions(-) delete mode 100644 profiler/tests.py create mode 100644 profiler/tests/__init__.py create mode 100644 profiler/tests/settings.py create mode 100644 profiler/tests/tests.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 107b787..5d19f16 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,59 @@ -*.log -*.pot -*.pyc -local_settings.py +# python compiled source and temporary files # +############################################## + +*.py[co] *~ + +# HG, SVN # +########### + +.svn +.hg +.hgignore + +# Sqlite base # +############### + +sqlite3 + +# gettext compiled file # +######################### + +*.pot +*.mo + +# Buildout directories and files # +################################## + +*.sublime-project +*.sublime-workspace +*.egg-info +docs +local + +bin +lib +dist +eggs +parts +build +include +downloads +src_eggs +uploads +develop-eggs +cfc.egg-info + +.installed.cfg +Makefile +bootstrap.py +buildout.cfg +mydatabase.db +versions.cfg + +# Misc # +######## + +.tox +profiler_test +*.log diff --git a/profiler/tests.py b/profiler/tests.py deleted file mode 100644 index 501deb7..0000000 --- a/profiler/tests.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. -""" - -from django.test import TestCase - - -class SimpleTest(TestCase): - def test_basic_addition(self): - """ - Tests that 1 + 1 always equals 2. - """ - self.assertEqual(1 + 1, 2) diff --git a/profiler/tests/__init__.py b/profiler/tests/__init__.py new file mode 100644 index 0000000..18ef0bf --- /dev/null +++ b/profiler/tests/__init__.py @@ -0,0 +1,4 @@ +# coding=utf-8 +from __future__ import unicode_literals + +from .tests import ProfilerTests # NOQA diff --git a/profiler/tests/settings.py b/profiler/tests/settings.py new file mode 100644 index 0000000..0b8ae49 --- /dev/null +++ b/profiler/tests/settings.py @@ -0,0 +1,19 @@ +# coding=utf-8 +from __future__ import absolute_import, unicode_literals +""" +django-live-profiler.tests.settings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Contains the settings to be used when running the test suite. +""" +from os.path import dirname, join + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'mydatabase' + } +} + +LOCALE_PATHS = (join(dirname(dirname(__file__)), 'locale'),) +INSTALLED_APPS = ['profiler'] +SECRET_KEY = '*' diff --git a/profiler/tests/tests.py b/profiler/tests/tests.py new file mode 100644 index 0000000..f6c0b54 --- /dev/null +++ b/profiler/tests/tests.py @@ -0,0 +1,9 @@ +from django.test import SimpleTestCase + +from ..models import SQLCompiler + + +class ProfilerTests(SimpleTestCase): + + def test_first(self): + self.assertTrue(SQLCompiler.execute_sql) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..4256e0a --- /dev/null +++ b/tox.ini @@ -0,0 +1,65 @@ +[tox] +downloadcache = {toxworkdir}/cache/ +envlist = + py26-1.4, + py26-1.5, + py27-1.4, + py27-1.5, + #pypy-1.4, + #pypy-1.5, + #py32-1.5, + py33-1.5 + +[testenv] +setenv = + PYTHONPATH = . + DJANGO_SETTINGS_MODULE = profiler.tests.settings +commands = + django-admin.py test profiler + +[testenv:py26-1.4] +basepython = python2.6 +deps = + Django==1.5.1 + +[testenv:py26-1.5] +basepython = python2.6 +deps = + Django==1.5.1 + +[testenv:py27-1.4] +basepython = python2.7 +deps = + Django==1.4.3 + +[testenv:py27-1.5] +basepython = python2.7 +deps = + Django==1.5.1 + +[testenv:pypy-1.4] +basepython = pypy +deps = + Django==1.4.3 + +# Django 1.5 not compatible with pypy 1.9... +[testenv:pypy-1.5] +basepython = pypy +deps = + Django==1.5.1 + +[testenv:py32-1.5] +basepython = python3.2 +deps = + Django==1.5.1 + +[testenv:py33-1.5] +basepython = python3.3 +deps = + Django==1.5.1 + +# Test Django trunk : 1.6 actually +[testenv:py27-trunk] +basepython = python2.7 +deps = + https://github.com/django/django/tarball/master#egg=Django From 94ff6e1287a431ac3f85df1c9270e51dc952cbe7 Mon Sep 17 00:00:00 2001 From: jferry Date: Mon, 17 Jun 2013 01:01:52 +0200 Subject: [PATCH 2/2] pass flake8 checker --- profiler/__init__.py | 2 + profiler/instrument.py | 18 ++++++--- profiler/middleware.py | 47 ++++++++++++---------- profiler/models.py | 4 +- profiler/urls.py | 5 +-- profiler/views.py | 89 +++++++++++++++++++++++++++--------------- 6 files changed, 102 insertions(+), 63 deletions(-) diff --git a/profiler/__init__.py b/profiler/__init__.py index 33f96ff..0f11df2 100644 --- a/profiler/__init__.py +++ b/profiler/__init__.py @@ -2,8 +2,10 @@ _local = threading.local() + def _set_current_view(view): _local.current_view = view + def _get_current_view(): return getattr(_local, 'current_view', None) diff --git a/profiler/instrument.py b/profiler/instrument.py index 36a8791..3554007 100644 --- a/profiler/instrument.py +++ b/profiler/instrument.py @@ -3,13 +3,14 @@ from django.db.models.sql.compiler import SQLCompiler from django.db.models.sql.datastructures import EmptyResultSet from django.db.models.sql.constants import MULTI -from django.db import connection from aggregate.client import get_client from profiler import _get_current_view + def execute_sql(self, *args, **kwargs): + client = get_client() if client is None: return self.__execute_sql(*args, **kwargs) @@ -27,13 +28,20 @@ def execute_sql(self, *args, **kwargs): return self.__execute_sql(*args, **kwargs) finally: d = (datetime.now() - start) - client.insert({'query' : q, 'view' : _get_current_view(), 'type' : 'sql'}, - {'time' : 0.0 + d.seconds * 1000 + d.microseconds/1000, 'count' : 1}) + client.insert( + { + 'query': q, + 'view': _get_current_view(), + 'type': 'sql' + }, + { + 'time': 0.0 + d.seconds * 1000 + d.microseconds / 1000, + 'count': 1 + } + ) INSTRUMENTED = False - - if not INSTRUMENTED: SQLCompiler.__execute_sql = SQLCompiler.execute_sql SQLCompiler.execute_sql = execute_sql diff --git a/profiler/middleware.py b/profiler/middleware.py index 7e8f01a..76fb98b 100644 --- a/profiler/middleware.py +++ b/profiler/middleware.py @@ -1,10 +1,7 @@ -from datetime import datetime import inspect import statprof -from django.db import connection -from django.core.cache import cache from django.conf import settings @@ -12,17 +9,21 @@ from profiler import _set_current_view + class ProfilerMiddleware(object): def process_view(self, request, view_func, view_args, view_kwargs): if inspect.ismethod(view_func): - view_name = view_func.im_class.__module__+ '.' + view_func.im_class.__name__ + view_func.__name__ + view_name = ( + view_func.im_class.__module__ + + '.' + view_func.im_class.__name__ + + view_func.__name__ + ) else: view_name = view_func.__module__ + '.' + view_func.__name__ - + _set_current_view(view_name) - def process_response(self, request, response): _set_current_view(None) return response @@ -31,9 +32,11 @@ def process_response(self, request, response): class StatProfMiddleware(object): def process_request(self, request): - statprof.reset(getattr(settings, 'LIVEPROFILER_STATPROF_FREQUENCY', 100)) + statprof.reset(getattr( + settings, 'LIVEPROFILER_STATPROF_FREQUENCY', 100 + )) statprof.start() - + def process_response(self, request, response): statprof.stop() client = get_client() @@ -43,18 +46,20 @@ def process_response(self, request, response): secs_per_sample = statprof.state.accumulated_time / total_samples client.insert_all([( - {'file' : c.key.filename, - 'lineno' : c.key.lineno, - 'function' : c.key.name, - 'type' : 'python'}, - {'self_nsamples' : c.self_sample_count, - 'cum_nsamples' : c.cum_sample_count, - 'tot_nsamples' : total_samples, - 'cum_time' : c.cum_sample_count * secs_per_sample, - 'self_time' : c.self_sample_count * secs_per_sample - }) - for c in statprof.CallData.all_calls.itervalues()]) - - + { + 'file': c.key.filename, + 'lineno': c.key.lineno, + 'function': c.key.name, + 'type': 'python' + }, + { + 'self_nsamples': c.self_sample_count, + 'cum_nsamples': c.cum_sample_count, + 'tot_nsamples': total_samples, + 'cum_time': c.cum_sample_count * secs_per_sample, + 'self_time': c.self_sample_count * secs_per_sample + }) + for c in statprof.CallData.all_calls.itervalues() + ]) return response diff --git a/profiler/models.py b/profiler/models.py index b44dc4e..f61223b 100644 --- a/profiler/models.py +++ b/profiler/models.py @@ -1,5 +1,5 @@ -from django.db import models +from django.db import models # NOQA # Create your models here. -from profiler.instrument import * +from profiler.instrument import * # NOQA diff --git a/profiler/urls.py b/profiler/urls.py index 07f3b7f..51164ac 100644 --- a/profiler/urls.py +++ b/profiler/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import * +from django.conf.urls.defaults import url, patterns urlpatterns = patterns( 'profiler.views', @@ -6,5 +6,4 @@ url(r'^by_view/$', 'stats_by_view', name='profiler_stats_by_view'), url(r'^code/$', 'python_stats', name='profiler_python_stats'), url(r'^reset/$', 'reset', name='profiler_reset'), - ) - +) diff --git a/profiler/views.py b/profiler/views.py index 150f3ba..6a9c6f9 100644 --- a/profiler/views.py +++ b/profiler/views.py @@ -1,65 +1,90 @@ -from django.http import HttpResponse, HttpResponseRedirect +from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.template.context import RequestContext -from django.core.cache import cache from django.contrib.auth.decorators import user_passes_test from django.core.urlresolvers import reverse from django.utils import simplejson from aggregate.client import get_client -@user_passes_test(lambda u:u.is_superuser) + +@user_passes_test(lambda u: u.is_superuser) def global_stats(request): - stats = get_client().select(group_by=['query'], where={'type':'sql'}) + stats = get_client().select( + group_by=['query'], + where={'type': 'sql'} + ) for s in stats: s['average_time'] = s['time'] / s['count'] - return render_to_response('profiler/index.html', - {'queries' : stats}, - context_instance=RequestContext(request)) + return render_to_response( + 'profiler/index.html', + {'queries': stats}, + context_instance=RequestContext(request) + ) + -@user_passes_test(lambda u:u.is_superuser) +@user_passes_test(lambda u: u.is_superuser) def stats_by_view(request): - stats = get_client().select(group_by=['view','query'], where={'type':'sql'}) + stats = get_client().select( + group_by=['view', 'query'], + where={'type': 'sql'} + ) grouped = {} for r in stats: if r['view'] not in grouped: - grouped[r['view']] = {'queries' : [], - 'count' : 0, - 'time' : 0, - 'average_time' : 0} + grouped[r['view']] = { + 'queries': [], + 'count': 0, + 'time': 0, + 'average_time': 0 + } grouped[r['view']]['queries'].append(r) grouped[r['view']]['count'] += r['count'] grouped[r['view']]['time'] += r['time'] - r['average_time'] = r['time'] / r['count'] + r['average_time'] = r['time'] / r['count'] grouped[r['view']]['average_time'] += r['average_time'] - + maxtime = 0 for r in stats: if r['average_time'] > maxtime: maxtime = r['average_time'] for r in stats: - r['normtime'] = (0.0+r['average_time'])/maxtime - - return render_to_response('profiler/by_view.html', - {'queries' : grouped, - 'stats' :simplejson.dumps(stats)}, - context_instance=RequestContext(request)) + r['normtime'] = (0.0 + r['average_time']) / maxtime -@user_passes_test(lambda u:u.is_superuser) + return render_to_response( + 'profiler/by_view.html', + {'queries': grouped, + 'stats': simplejson.dumps(stats)}, + context_instance=RequestContext(request) + ) + + +@user_passes_test(lambda u: u.is_superuser) def reset(request): - next = request.GET.get('next') or request.POST.get('next') or request.META.get('HTTP_REFERER') or reverse('profiler_global_stats') + next = ( + request.GET.get('next') + or request.POST.get('next') + or request.META.get('HTTP_REFERER') + or reverse('profiler_global_stats') + ) if request.method == 'POST': get_client().clear() return HttpResponseRedirect(next) - return render_to_response('profiler/reset.html', - {'next' : next}, - context_instance=RequestContext(request)) - + return render_to_response( + 'profiler/reset.html', + {'next': next}, + context_instance=RequestContext(request) + ) -@user_passes_test(lambda u:u.is_superuser) +@user_passes_test(lambda u: u.is_superuser) def python_stats(request): - stats = get_client().select(group_by=['file','lineno'], where={'type':'python'}) - return render_to_response('profiler/code.html', - {'stats' : stats}, - context_instance=RequestContext(request)) + stats = get_client().select( + group_by=['file', 'lineno'], + where={'type': 'python'} + ) + return render_to_response( + 'profiler/code.html', + {'stats': stats}, + context_instance=RequestContext(request) + )