From c44bdcea3d0c6d7aeb8c106abd301c821efd1dce Mon Sep 17 00:00:00 2001 From: Evgeniy Krysanov Date: Sun, 16 Jun 2019 17:18:11 +0300 Subject: [PATCH 1/7] Update tests --- tests/tests.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index f8d3e3e..2f65fe0 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -2,8 +2,6 @@ import django -from time import sleep - from django.contrib.auth.models import User from django.core.cache import cache from django.test import TestCase, override_settings @@ -16,6 +14,11 @@ except ImportError: from django.core.urlresolvers import reverse +if django.VERSION < (1, 10): + MIDDLEWARE_SETTINGS_NAME = 'MIDDLEWARE_CLASSES' +else: + MIDDLEWARE_SETTINGS_NAME = 'MIDDLEWARE' + @override_settings(SPEEDINFO_EXCLUDE_URLS=[], SPEEDINFO_TESTS=True) class ProfilerTest(TestCase): @@ -115,19 +118,15 @@ def test_per_view_cache(self): data.refresh_from_db() self.assertEqual(data.cache_hits, 2) - # Wait for the cache timeout - sleep(3) + # Clear cache + cache.clear() + self.client.get(self.cached_func_view_url) data.refresh_from_db() self.assertEqual(data.cache_hits, 2) def test_per_site_cache(self): - if django.VERSION < (1, 10): - middleware_settings_name = 'MIDDLEWARE_CLASSES' - else: - middleware_settings_name = 'MIDDLEWARE' - - with self.modify_settings(**{middleware_settings_name: { + with self.modify_settings(**{MIDDLEWARE_SETTINGS_NAME: { 'append': 'django.middleware.cache.FetchFromCacheMiddleware', 'prepend': 'django.middleware.cache.UpdateCacheMiddleware', }}): From d604ee5bddf823138ded30cca346309a22d7e65b Mon Sep 17 00:00:00 2001 From: Evgeniy Krysanov Date: Sun, 16 Jun 2019 17:21:06 +0300 Subject: [PATCH 2/7] Add system checks --- speedinfo/__init__.py | 1 + speedinfo/apps.py | 78 +++++++++++++++++++++++++++++++++++++++++++ tests/tests.py | 41 +++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 speedinfo/apps.py diff --git a/speedinfo/__init__.py b/speedinfo/__init__.py index 593f5f5..7e34c39 100644 --- a/speedinfo/__init__.py +++ b/speedinfo/__init__.py @@ -2,4 +2,5 @@ from speedinfo.profiler import Profiler +default_app_config = 'speedinfo.apps.SpeedinfoConfig' profiler = Profiler() diff --git a/speedinfo/apps.py b/speedinfo/apps.py new file mode 100644 index 0000000..0e72b70 --- /dev/null +++ b/speedinfo/apps.py @@ -0,0 +1,78 @@ +# coding: utf-8 + +import django +from django.conf import settings +from django.core.checks import Error, Warning, register +from django.apps import AppConfig + +if django.VERSION < (1, 10): + MIDDLEWARE_SETTINGS_NAME = 'MIDDLEWARE_CLASSES' +else: + MIDDLEWARE_SETTINGS_NAME = 'MIDDLEWARE' + + +def check_middleware(app_configs, **kwargs): + profiler_middleware_index = None + profiler_middleware_multiple = False + cache_middleware_index = None + errors = [] + + for i, middleware in enumerate(getattr(settings, MIDDLEWARE_SETTINGS_NAME)): + if middleware == 'speedinfo.middleware.ProfilerMiddleware': + profiler_middleware_multiple = profiler_middleware_index is not None + profiler_middleware_index = i + elif middleware == 'django.middleware.cache.FetchFromCacheMiddleware': + cache_middleware_index = i + + if profiler_middleware_index is None: + errors.append( + Warning( + 'speedinfo.middleware.ProfilerMiddleware is missing from %s' % MIDDLEWARE_SETTINGS_NAME, + hint='Add speedinfo.middleware.ProfilerMiddleware to %s' % MIDDLEWARE_SETTINGS_NAME, + id='speedinfo.W001', + ) + ) + + elif (cache_middleware_index is not None) and (profiler_middleware_index > cache_middleware_index): + errors.append( + Error( + 'speedinfo.middleware.ProfilerMiddleware occurs after ' + 'django.middleware.cache.FetchFromCacheMiddleware in %s' % MIDDLEWARE_SETTINGS_NAME, + hint='Move speedinfo.middleware.ProfilerMiddleware to before ' + 'django.middleware.cache.FetchFromCacheMiddleware in %s' % MIDDLEWARE_SETTINGS_NAME, + id='speedinfo.E001', + ) + ) + + elif profiler_middleware_multiple: + errors.append( + Error( + 'speedinfo.middleware.ProfilerMiddleware occurs multiple times in %s' % MIDDLEWARE_SETTINGS_NAME, + hint='Add speedinfo.middleware.ProfilerMiddleware only once in %s' % MIDDLEWARE_SETTINGS_NAME, + id='speedinfo.E002', + ) + ) + + return errors + + +def check_cache_backend(app_configs, **kwargs): + for params in settings.CACHES.values(): + if params.get('BACKEND') == 'speedinfo.backends.proxy_cache': + return [] + + return [ + Error( + 'speedinfo.backends.proxy_cache is missing from CACHES', + hint='Add speedinfo.backends.proxy_cache to CACHES', + id='speedinfo.E003', + ) + ] + + +class SpeedinfoConfig(AppConfig): + name = 'speedinfo' + + def ready(self): + register()(check_middleware) + register()(check_cache_backend) diff --git a/tests/tests.py b/tests/tests.py index 2f65fe0..79073d2 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -4,6 +4,7 @@ from django.contrib.auth.models import User from django.core.cache import cache +from django.core.checks import run_checks from django.test import TestCase, override_settings from speedinfo import profiler @@ -20,6 +21,46 @@ MIDDLEWARE_SETTINGS_NAME = 'MIDDLEWARE' +class SystemChecksTestCase(TestCase): + def test_valid_middleware_config(self): + messages = run_checks() + self.assertEqual(messages, []) + + def test_missing_middleware(self): + with self.settings(**{MIDDLEWARE_SETTINGS_NAME: []}): + messages = run_checks() + self.assertTrue(len(messages) > 0) + self.assertEqual(messages[0].id, 'speedinfo.W001') + + def test_cache_middleware_position(self): + with self.settings(**{MIDDLEWARE_SETTINGS_NAME: [ + 'django.middleware.cache.FetchFromCacheMiddleware', + 'speedinfo.middleware.ProfilerMiddleware', + ]}): + messages = run_checks() + self.assertTrue(len(messages) > 0) + self.assertEqual(messages[0].id, 'speedinfo.E001') + + def test_multiple_middlewares(self): + with self.settings(**{MIDDLEWARE_SETTINGS_NAME: [ + 'speedinfo.middleware.ProfilerMiddleware', + 'speedinfo.middleware.ProfilerMiddleware', + ]}): + messages = run_checks() + self.assertTrue(len(messages) > 0) + self.assertEqual(messages[0].id, 'speedinfo.E002') + + def test_missing_cache_backend(self): + with self.settings(CACHES={ + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + } + }): + messages = run_checks() + self.assertTrue(len(messages) > 0) + self.assertEqual(messages[0].id, 'speedinfo.E003') + + @override_settings(SPEEDINFO_EXCLUDE_URLS=[], SPEEDINFO_TESTS=True) class ProfilerTest(TestCase): def setUp(self): From fc0ce9af89eee16d4ef985c7fd694a7eea8d1338 Mon Sep 17 00:00:00 2001 From: Evgeniy Krysanov Date: Sun, 16 Jun 2019 17:32:51 +0300 Subject: [PATCH 3/7] Update tests --- tests/tests.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/tests.py b/tests/tests.py index 79073d2..799a389 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -27,13 +27,22 @@ def test_valid_middleware_config(self): self.assertEqual(messages, []) def test_missing_middleware(self): - with self.settings(**{MIDDLEWARE_SETTINGS_NAME: []}): + with self.settings(**{MIDDLEWARE_SETTINGS_NAME: [ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + ]}): messages = run_checks() self.assertTrue(len(messages) > 0) self.assertEqual(messages[0].id, 'speedinfo.W001') def test_cache_middleware_position(self): with self.settings(**{MIDDLEWARE_SETTINGS_NAME: [ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', 'speedinfo.middleware.ProfilerMiddleware', ]}): @@ -43,6 +52,10 @@ def test_cache_middleware_position(self): def test_multiple_middlewares(self): with self.settings(**{MIDDLEWARE_SETTINGS_NAME: [ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', 'speedinfo.middleware.ProfilerMiddleware', 'speedinfo.middleware.ProfilerMiddleware', ]}): From 40e1572156cbe741ad38c87bdba249de487cac03 Mon Sep 17 00:00:00 2001 From: Evgeniy Krysanov Date: Sun, 16 Jun 2019 17:40:08 +0300 Subject: [PATCH 4/7] Replace `mysql` with `sqlite` in test project --- .travis.yml | 7 ------- tests/test_settings.py | 7 ++----- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 59378d9..763e78c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,6 @@ python: - "3.6" - "3.7" -services: - - mysql - -before_install: - - mysql -e 'CREATE DATABASE test_db;' - env: - DJANGO_VERSION=1.8 - DJANGO_VERSION=1.10 @@ -34,7 +28,6 @@ matrix: install: - pip install -q Django==$DJANGO_VERSION - - pip install mysqlclient script: - python manage.py test diff --git a/tests/test_settings.py b/tests/test_settings.py index cde0a6a..4dbd47a 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -54,11 +54,8 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'test_db', - 'USER': 'root', - 'PASSWORD': '', - 'HOST': '127.0.0.1', + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'db.sqlite', } } From 2801381cdcc78caa5ad4a3f2b0362bd836491697 Mon Sep 17 00:00:00 2001 From: Evgeniy Krysanov Date: Sun, 16 Jun 2019 17:47:00 +0300 Subject: [PATCH 5/7] Update test settings --- tests/test_settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_settings.py b/tests/test_settings.py index 4dbd47a..9cd2849 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -6,8 +6,9 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -SECRET_KEY = 'secret-key' +SECRET_KEY = 'dummy' DEBUG = False +ALLOWED_HOSTS = ['*'] INSTALLED_APPS = [ 'django.contrib.admin', From 5a9c9ecf8524637294300a8e91a3919c600f699d Mon Sep 17 00:00:00 2001 From: Evgeniy Krysanov Date: Sun, 16 Jun 2019 17:49:42 +0300 Subject: [PATCH 6/7] Rename `order_field` to `expression` in `ReportColumnFormat` namedtuple --- speedinfo/admin.py | 4 ++-- speedinfo/settings.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/speedinfo/admin.py b/speedinfo/admin.py index 42d79db..6068f9a 100644 --- a/speedinfo/admin.py +++ b/speedinfo/admin.py @@ -66,9 +66,9 @@ def get_queryset(self, request): qs = super(ViewProfilerAdmin, self).get_queryset(request) for rc in speedinfo_settings.SPEEDINFO_REPORT_COLUMNS_FORMAT: - if (rc.attr_name in speedinfo_settings.SPEEDINFO_REPORT_COLUMNS) and not isinstance(rc.order_field, str): + if (rc.attr_name in speedinfo_settings.SPEEDINFO_REPORT_COLUMNS) and not isinstance(rc.expression, str): qs = qs.annotate(**{ - rc.attr_name: rc.order_field + rc.attr_name: rc.expression }) return qs diff --git a/speedinfo/settings.py b/speedinfo/settings.py index 3258038..d8d6a78 100644 --- a/speedinfo/settings.py +++ b/speedinfo/settings.py @@ -4,7 +4,7 @@ from django.conf import settings from django.db.models import ExpressionWrapper, F, FloatField, IntegerField -ReportColumnFormat = namedtuple('ReportColumnFormat', ['name', 'format', 'attr_name', 'order_field']) +ReportColumnFormat = namedtuple('ReportColumnFormat', ['name', 'format', 'attr_name', 'expression']) DEFAULTS = { 'SPEEDINFO_TESTS': False, From 104484bd7200ca67af68261e9030613d8a3001a3 Mon Sep 17 00:00:00 2001 From: Evgeniy Krysanov Date: Sun, 16 Jun 2019 18:00:43 +0300 Subject: [PATCH 7/7] Bump version to 1.4.1 --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5a7bd45..3516c19 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='django-speedinfo', - version='1.4.0', + version='1.4.1', packages=['speedinfo', 'speedinfo.migrations', 'speedinfo.conditions'], include_package_data=True, license='MIT', @@ -28,6 +28,7 @@ 'Framework :: Django :: 1.11', 'Framework :: Django :: 2.0', 'Framework :: Django :: 2.1', + 'Framework :: Django :: 2.2', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python',