diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 667846cdc..050d5b11d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,6 +1,11 @@
name: Test
-on: [push, pull_request]
+on:
+ push:
+ pull_request:
+ schedule:
+ # Run weekly on Saturday
+ - cron: '37 3 * * SAT'
jobs:
mysql:
@@ -9,11 +14,11 @@ jobs:
fail-fast: false
max-parallel: 5
matrix:
- python-version: ['3.6', '3.7', '3.8', '3.9']
+ python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
services:
mariadb:
- image: mariadb:10.3
+ image: mariadb
env:
MYSQL_ROOT_PASSWORD: debug_toolbar
options: >-
@@ -76,11 +81,11 @@ jobs:
fail-fast: false
max-parallel: 5
matrix:
- python-version: ['3.6', '3.7', '3.8', '3.9']
+ python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
services:
postgres:
- image: 'postgres:9.5'
+ image: postgres
env:
POSTGRES_DB: debug_toolbar
POSTGRES_USER: debug_toolbar
@@ -143,7 +148,7 @@ jobs:
fail-fast: false
max-parallel: 5
matrix:
- python-version: ['3.6', '3.7', '3.8', '3.9']
+ python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
steps:
- uses: actions/checkout@v2
@@ -216,4 +221,4 @@ jobs:
python -m pip install --upgrade tox
- name: Test with tox
- run: tox -e docs,style,packaging
+ run: tox -e docs,packaging
diff --git a/.gitignore b/.gitignore
index df5a2d10c..6caa61357 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,5 @@ docs/_build
example/db.sqlite3
htmlcov
.tox
-node_modules
-package-lock.json
geckodriver.log
coverage.xml
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 000000000..30f6b7bb0
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,48 @@
+repos:
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.0.1
+ hooks:
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
+ - id: mixed-line-ending
+- repo: https://github.com/pycqa/flake8
+ rev: 4.0.1
+ hooks:
+ - id: flake8
+- repo: https://github.com/pycqa/doc8
+ rev: 0.10.1
+ hooks:
+ - id: doc8
+- repo: https://github.com/pycqa/isort
+ rev: 5.10.1
+ hooks:
+ - id: isort
+- repo: https://github.com/pre-commit/pygrep-hooks
+ rev: v1.9.0
+ hooks:
+ - id: python-check-blanket-noqa
+ - id: python-check-mock-methods
+ - id: python-no-eval
+ - id: python-no-log-warn
+ - id: rst-backticks
+ - id: rst-directive-colons
+- repo: https://github.com/pre-commit/mirrors-prettier
+ rev: v2.5.1
+ hooks:
+ - id: prettier
+ types_or: [javascript, css]
+- repo: https://github.com/pre-commit/mirrors-eslint
+ rev: v8.4.0
+ hooks:
+ - id: eslint
+ files: \.js?$
+ types: [file]
+ args:
+ - --fix
+- repo: https://github.com/psf/black
+ rev: 21.12b0
+ hooks:
+ - id: black
+ language_version: python3
+ entry: black --target-version=py36
diff --git a/.tx/config b/.tx/config
index bdbb9bf43..5c9ecc129 100644
--- a/.tx/config
+++ b/.tx/config
@@ -6,4 +6,3 @@ lang_map = sr@latin:sr_Latn
file_filter = debug_toolbar/locale/
") def test_cache_page(self): + # Clear the cache before testing the views. Other tests that use cached_view + # may run earlier and cause fewer cache calls. + cache.clear() self.client.get("/cached_view/") self.assertEqual(len(self.toolbar.get_panel_by_id("CachePanel").calls), 3) self.client.get("/cached_view/") self.assertEqual(len(self.toolbar.get_panel_by_id("CachePanel").calls), 5) + @override_settings(ROOT_URLCONF="tests.urls_use_package_urls") + def test_include_package_urls(self): + """Test urlsconf that uses the debug_toolbar.urls in the include call""" + # Clear the cache before testing the views. Other tests that use cached_view + # may run earlier and cause fewer cache calls. + cache.clear() + self.client.get("/cached_view/") + self.assertEqual(len(self.toolbar.get_panel_by_id("CachePanel").calls), 3) + self.client.get("/cached_view/") + self.assertEqual(len(self.toolbar.get_panel_by_id("CachePanel").calls), 5) + + def test_low_level_cache_view(self): + """Test cases when low level caching API is used within a request.""" + self.client.get("/cached_low_level_view/") + self.assertEqual(len(self.toolbar.get_panel_by_id("CachePanel").calls), 2) + self.client.get("/cached_low_level_view/") + self.assertEqual(len(self.toolbar.get_panel_by_id("CachePanel").calls), 3) + + def test_cache_disable_instrumentation(self): + """ + Verify that middleware cache usages before and after + DebugToolbarMiddleware are not counted. + """ + self.assertIsNone(cache.set("UseCacheAfterToolbar.before", None)) + self.assertIsNone(cache.set("UseCacheAfterToolbar.after", None)) + self.client.get("/execute_sql/") + self.assertEqual(cache.get("UseCacheAfterToolbar.before"), 1) + self.assertEqual(cache.get("UseCacheAfterToolbar.after"), 1) + self.assertEqual(len(self.toolbar.get_panel_by_id("CachePanel").calls), 0) + def test_is_toolbar_request(self): self.request.path = "/__debug__/render_panel/" self.assertTrue(self.toolbar.is_toolbar_request(self.request)) @@ -273,6 +310,28 @@ def test_sql_explain_checks_show_toolbar(self): ) self.assertEqual(response.status_code, 404) + @unittest.skipUnless(settings.USE_GIS, "Test only valid with gis support") + def test_sql_explain_gis(self): + from django.contrib.gis.geos import GEOSGeometry + + from .models import Location + + db_table = Location._meta.db_table + + url = "/__debug__/sql_explain/" + geom = GEOSGeometry("POLYGON((0 0, 0 1, 1 1, 0 0))") + data = { + "sql": f'SELECT "{db_table}"."point" FROM "{db_table}" WHERE "{db_table}"."point" @ {geom.hex} LIMIT 1', + "raw_sql": f'SELECT "{db_table}"."point" FROM "{db_table}" WHERE "{db_table}"."point" @ %s LIMIT 1', + "params": json.dumps([geom.hex]), + "alias": "default", + "duration": "0", + } + data["hash"] = SQLSelectForm().make_hash(data) + + response = self.client.post(url, data=data) + self.assertEqual(response.status_code, 200) + @unittest.skipUnless( connection.vendor == "postgresql", "Test valid only on PostgreSQL" ) @@ -376,7 +435,7 @@ def test_view_returns_template_response(self): self.assertEqual(response.status_code, 200) @override_settings(DEBUG_TOOLBAR_CONFIG={"DISABLE_PANELS": set()}) - def test_incercept_redirects(self): + def test_intcercept_redirects(self): response = self.client.get("/redirect/") self.assertEqual(response.status_code, 200) # Link to LOCATION header. diff --git a/tests/urls.py b/tests/urls.py index cef00e3e2..c12fc744a 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -2,8 +2,6 @@ from django.contrib.auth.views import LoginView from django.urls import include, path, re_path -import debug_toolbar - from . import views from .models import NonAsciiRepr @@ -20,9 +18,10 @@ path("new_user/", views.new_user), path("execute_sql/", views.execute_sql), path("cached_view/", views.cached_view), + path("cached_low_level_view/", views.cached_low_level_view), path("json_view/", views.json_view), path("redirect/", views.redirect_view), path("login_without_redirect/", LoginView.as_view(redirect_field_name=None)), path("admin/", admin.site.urls), - path("__debug__/", include(debug_toolbar.urls)), + path("__debug__/", include("debug_toolbar.urls")), ] diff --git a/tests/urls_use_package_urls.py b/tests/urls_use_package_urls.py new file mode 100644 index 000000000..50f7dfd69 --- /dev/null +++ b/tests/urls_use_package_urls.py @@ -0,0 +1,11 @@ +"""urls.py to test using debug_toolbar.urls in include""" +from django.urls import include, path + +import debug_toolbar + +from . import views + +urlpatterns = [ + path("cached_view/", views.cached_view), + path("__debug__/", include(debug_toolbar.urls)), +] diff --git a/tests/views.py b/tests/views.py index 15c0c18ec..b2fd21c54 100644 --- a/tests/views.py +++ b/tests/views.py @@ -1,4 +1,5 @@ from django.contrib.auth.models import User +from django.core.cache import cache from django.http import HttpResponseRedirect, JsonResponse from django.shortcuts import render from django.template.response import TemplateResponse @@ -33,6 +34,15 @@ def cached_view(request): return render(request, "base.html") +def cached_low_level_view(request): + key = "spam" + value = cache.get(key) + if not value: + value = "eggs" + cache.set(key, value, 60) + return render(request, "base.html") + + def json_view(request): return JsonResponse({"foo": "bar"}) diff --git a/tox.ini b/tox.ini index c3bb0bac2..3abd404bc 100644 --- a/tox.ini +++ b/tox.ini @@ -1,19 +1,20 @@ [tox] envlist = docs - style packaging - py{36,37}-dj{22,31,32}-sqlite - py{38,39}-dj{22,31,32,main}-sqlite - py{36,37,38,39}-dj{22,31,32}-{postgresql,mysql} + py{36,37}-dj{22,31,32}-{sqlite,postgresql,postgis,mysql} + py{38,39}-dj{22,31,32,40,main}-{sqlite,postgresql,postgis,mysql} + py{310}-dj{32,40,main}-{sqlite,postgresql,postgis,mysql} [testenv] deps = dj22: Django==2.2.* dj31: Django==3.1.* - dj32: Django>=3.2a1,<4.0 + dj32: Django>=3.2,<4.0 + dj40: Django>=4.0rc1,<4.1 sqlite: mock postgresql: psycopg2-binary + postgis: psycopg2-binary mysql: mysqlclient djmain: https://github.com/django/django/archive/main.tar.gz coverage @@ -33,7 +34,7 @@ passenv= setenv = PYTHONPATH = {toxinidir} PYTHONWARNINGS = d - py38-dj31-postgresql: DJANGO_SELENIUM_TESTS = true + py39-dj32-postgresql: DJANGO_SELENIUM_TESTS = true DB_NAME = {env:DB_NAME:debug_toolbar} DB_USER = {env:DB_USER:debug_toolbar} DB_HOST = {env:DB_HOST:localhost} @@ -42,19 +43,19 @@ whitelist_externals = make pip_pre = True commands = make coverage TEST_ARGS='{posargs:tests}' -[testenv:py{36,37,38,39}-dj{22,31,32}-postgresql] +[testenv:py{36,37,38,39,310}-dj{22,31,32}-postgresql] setenv = {[testenv]setenv} DB_BACKEND = postgresql DB_PORT = {env:DB_PORT:5432} -[testenv:py{36,37,38,39}-dj{22,31,32}-mysql] +[testenv:py{36,37,38,39,310}-dj{22,31,32}-mysql] setenv = {[testenv]setenv} DB_BACKEND = mysql DB_PORT = {env:DB_PORT:3306} -[testenv:py{36,37,38,39}-dj{22,31,32,main}-sqlite] +[testenv:py{36,37,38,39,310}-dj{22,31,32,main}-sqlite] setenv = {[testenv]setenv} DB_BACKEND = sqlite3 @@ -66,14 +67,6 @@ deps = Sphinx sphinxcontrib-spelling -[testenv:style] -commands = make style_check -deps = - black>=19.10b0 - flake8 - isort>=5.0.2 -skip_install = true - [testenv:packaging] commands = python setup.py sdist bdist_wheel @@ -90,9 +83,11 @@ python = 3.7: py37 3.8: py38 3.9: py39 + 3.10: py310 [gh-actions:env] DB_BACKEND = mysql: mysql postgresql: postgresql + postgis: postgresql sqlite3: sqlite