Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-django-silk for 
openSUSE:Factory checked in at 2026-03-29 20:00:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-silk (Old)
 and      /work/SRC/openSUSE:Factory/.python-django-silk.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-django-silk"

Sun Mar 29 20:00:57 2026 rev:18 rq:1343411 version:5.5.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-django-silk/python-django-silk.changes    
2026-03-23 17:11:59.766249816 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-django-silk.new.8177/python-django-silk.changes
  2026-03-29 20:01:20.771048437 +0200
@@ -1,0 +2,141 @@
+Sun Mar 29 10:32:45 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 5.5.0:
+  * Fix context manager for `_process_response` (#827)
+  * Fix mouse event for sql navigation (#847)
+  * Add support for Django 6.0 (#836)
+  * Add support for Python 3.14 (#834)
+  * Get paginator limit from URL params (#646)
+  * Hide pagination when there's only one page (#844)
+  * Remove official support for Python 3.9 (#834)
+  * Fix double EXPLAIN when calling explain on queryset (#654)
+  * Fix serialization issues for binary and json fields (#821)
+  * Reverts #798 which causes issues when serializing JSONFields
+  * Also reverts #798 which has a race condition when modifying
+    `execute_sql`
+  * Catch and ignore sql encoding errors (#810) @albertyw
+  * Document that context_processors.request is required (#815)
+  * Fix documentation formatting (#810) @albertyw
+  * Test refactors (#814) @albertyw
+  * Fixes curl/client values rendering in request_detail (#797)
+  * Fix serialization of non-unicode binary data, add cleanup in
+    middleware (#798) @glennmatthews
+  * Make transactions target the DB alias selected by the router
+    (#801) @OscarVanL
+  * Add support for Django 5.2 (#784) @albertyw
+  * Support opening SQL details in a new window (#788)
+  * Avoid timeouts when deserializing large jsons (#768)
+  * Make autopep8 optional (#782) @albertyw
+  * Fix masking sensitive data when an empty
+    `SILKY_SENSITIVE_KEYS` is provided (#777) @ahsanshafiq742
+  * Remove support for Django 5.0 (#783) @albertyw
+  * Fix logger deprecations (#766) @rjdebastiani
+  * Update dependencies and various autoupdate cleanups
+
+-------------------------------------------------------------------
+Sun Mar 29 10:32:14 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 5.5.0:
+  * ## 5.5.0 (2026-03-06)
+  * :release-by: Albert Wang (@albertyw)
+  * Full Changelog
+
+  * **Fixes:**
+  * Fix context manager for `_process_response` (#827)
+    @izabala033
+  * Fix mouse event for sql navigation (#847) @albertyw
+
+  * **Features/Enhancements:**
+
+  * Add support for Django 6.0 (#836) @albertyw
+  * Add support for Python 3.14 (#834) @albertyw
+  * Get paginator limit from URL params (#646) @strig
+  * Hide pagination when there's only one page (#844) @ShlomoCode
+
+  * **Maintenance and Cleanup:**
+  * Remove official support for Python 3.9 (#834) @albertyw
+  * Dependency updates
+
+
+  * ## 5.4.3 (2025-09-08)
+  * :release-by: Albert Wang (@albertyw)
+  * Full Changelog
+
+  * **Fixes:**
+
+  * Fix double EXPLAIN when calling explain on queryset (#654)
+    @stereodamage
+  * Fix serialization issues for binary and json fields (#821)
+    @albertyw
+
+
+  * ## 5.4.2 (2025-08-17)
+  * :release-by: Albert Wang (@albertyw)
+  * Full Changelog
+
+  * **Fixes:**
+
+  * Reverts #798 which causes issues when serializing JSONFields
+    (#807) @albertyw
+  * Also reverts #798 which has a race condition when modifying
+    `execute_sql` (#816) @albertyw
+  * Catch and ignore sql encoding errors (#810) @albertyw
+    @bpascard
+
+  * **Maintenance and Cleanup:**
+
+  * Document that context_processors.request is required (#815)
+    @albertyw
+  * Fix documentation formatting (#810) @albertyw
+  * Test refactors (#814) @albertyw
+
+
+  * ## 5.4.1 (2025-08-10)
+  * :release-by: Albert Wang (@albertyw)
+  * Full Changelog
+
+  * **Fixes:**
+
+  * Fixes curl/client values rendering in request_detail (#797)
+    @bcmyguest
+  * Fix serialization of non-unicode binary data, add cleanup in
+    middleware (#798) @glennmatthews
+  * Make transactions target the DB alias selected by the router
+    (#801) @OscarVanL
+
+  * **Maintenance and Cleanup:**
+
+  * Dependency updates
+  * Documentation updates
+
+
+  * ## 5.4.0 (2025-05-03)
+  * :release-by: Albert Wang (@albertyw)
+  * Full Changelog
+
+  * **Note: this release removes support for Django 5.0**
+  * **Note: this release removes autoformatting of python
+    snippets; continue formatting by pip installing `django-
+    silk[formatting]`**
+
+  * **Features/Enhancements:**
+
+  * Add support for Django 5.2 (#784) @albertyw
+  * Support opening SQL details in a new window (#788)
+    @joaopedroalbq
+  * Avoid timeouts when deserializing large jsons (#768)
+    @quertenmont
+  * Make autopep8 optional (#782) @albertyw
+
+  * **Fixes:**
+
+  * Fix masking sensitive data when an empty
+    `SILKY_SENSITIVE_KEYS` is provided (#777) @ahsanshafiq742
+
+  * **Maintenance and Cleanup:**
+
+  * Remove support for Django 5.0 (#783) @albertyw
+  * Fix logger deprecations (#766) @rjdebastiani
+  * Update dependencies and various autoupdate cleanups
+
+-------------------------------------------------------------------

Old:
----
  django_silk-5.3.2.tar.gz

New:
----
  django_silk-5.5.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-django-silk.spec ++++++
--- /var/tmp/diff_new_pack.Wm2tpN/_old  2026-03-29 20:01:21.659085024 +0200
+++ /var/tmp/diff_new_pack.Wm2tpN/_new  2026-03-29 20:01:21.663085188 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python-django-silk
-Version:        5.3.2
+Version:        5.5.0
 Release:        0
 Summary:        Profiling for the Django Framework
 License:        MIT

++++++ django_silk-5.3.2.tar.gz -> django_silk-5.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/.github/workflows/release.yml 
new/django_silk-5.5.0/.github/workflows/release.yml
--- old/django_silk-5.3.2/.github/workflows/release.yml 2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/.github/workflows/release.yml 2026-03-07 
09:15:38.000000000 +0100
@@ -18,7 +18,7 @@
       - name: Set up Python
         uses: actions/setup-python@v5
         with:
-          python-version: 3.13
+          python-version: 3.14
 
       - name: Install dependencies
         run: |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/.github/workflows/test.yml 
new/django_silk-5.5.0/.github/workflows/test.yml
--- old/django_silk-5.3.2/.github/workflows/test.yml    2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/.github/workflows/test.yml    2026-03-07 
09:15:38.000000000 +0100
@@ -9,22 +9,30 @@
     strategy:
       fail-fast: false
       matrix:
-        python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
-        django-version: ['4.2', '5.0', '5.1', 'main']
-        postgres-version: ['13', '17']
-        mariadb-version: ['10.6', '10.11', '11.4']
+        python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
+        django-version: ['4.2', '5.1', '5.2', '6.0', 'main']
+        postgres-version: ['14', '18']
+        mariadb-version: ['10.6', '10.11', '11.4', '11.8']
         exclude:
-          # Django 5.0 doesn't support python <=3.9 
(https://docs.djangoproject.com/en/5.0/faq/install/)
-          - python-version: '3.9'
-            django-version: '5.0'
+          # Django 4.2 doesn't support Python >= 3.13
+          - django-version: '4.2'
+            python-version: '3.13'
+          - django-version: '4.2'
+            python-version: '3.14'
 
-          # Django 5.1 doesn't support python <=3.9 
(https://docs.djangoproject.com/en/5.1/faq/install/)
-          - python-version: '3.9'
-            django-version: '5.1'
+          # Django 5.1 doesn't support Python >= 3.14
+          - django-version: '5.1'
+            python-version: '3.14'
 
-          # Django main doesn't support python <=3.9 
(https://docs.djangoproject.com/en/5.1/faq/install/)
-          - python-version: '3.9'
-            django-version: 'main'
+          # Django 6.0 doesn't support Python <3.12 
(https://docs.djangoproject.com/en/dev/releases/6.0/#python-compatibility)
+          - django-version: '6.0'
+            python-version: '3.10'
+          - django-version: '6.0'
+            python-version: '3.11'
+          - django-version: 'main'
+            python-version: '3.10'
+          - django-version: 'main'
+            python-version: '3.11'
 
     services:
       postgres:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/.pre-commit-config.yaml 
new/django_silk-5.5.0/.pre-commit-config.yaml
--- old/django_silk-5.3.2/.pre-commit-config.yaml       2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/.pre-commit-config.yaml       2026-03-07 
09:15:38.000000000 +0100
@@ -1,10 +1,10 @@
 repos:
 - repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: 'v5.0.0'
+  rev: 'v6.0.0'
   hooks:
   - id: check-merge-conflict
 - repo: https://github.com/hadialqattan/pycln
-  rev: v2.4.0
+  rev: v2.6.0
   hooks:
   - id: pycln
     args: ['--all']
@@ -13,12 +13,12 @@
   hooks:
   - id: yesqa
 - repo: https://github.com/pycqa/isort
-  rev: '5.13.2'
+  rev: '8.0.1'
   hooks:
   - id: isort
     args: ['--profile', 'black']
 - repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: 'v5.0.0'
+  rev: 'v6.0.0'
   hooks:
   - id: end-of-file-fixer
     exclude: >-
@@ -47,21 +47,21 @@
   - id: detect-private-key
     exclude: ^examples|(?:tests/ssl)/
 - repo: https://github.com/asottile/pyupgrade
-  rev: 'v3.19.0'
+  rev: 'v3.21.2'
   hooks:
   - id: pyupgrade
     args: ['--keep-mock']
 - repo: https://github.com/adamchainz/django-upgrade
-  rev: '1.22.2'
+  rev: '1.30.0'
   hooks:
   - id: django-upgrade
     args: [--target-version, '4.2']
 - repo: https://github.com/hhatto/autopep8
-  rev: 'v2.3.1'
+  rev: 'v2.3.2'
   hooks:
   - id: autopep8
 - repo: https://github.com/PyCQA/flake8
-  rev: '7.1.1'
+  rev: '7.3.0'
   hooks:
   - id: flake8
     exclude: '^docs/'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/CHANGELOG.md 
new/django_silk-5.5.0/CHANGELOG.md
--- old/django_silk-5.3.2/CHANGELOG.md  2024-12-14 06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/CHANGELOG.md  2026-03-07 09:15:38.000000000 +0100
@@ -1,6 +1,94 @@
 # Change Log
 ## Unreleased
 
+## [5.5.0](https://github.com/jazzband/django-silk/tree/5.5.0) (2026-03-06)
+:release-by: Albert Wang (@albertyw)
+[Full Changelog](https://github.com/jazzband/django-silk/compare/5.4.3..5.5.0)
+
+**Fixes:**
+ - Fix context manager for `_process_response` (#827) @izabala033
+ - Fix mouse event for sql navigation (#847) @albertyw
+
+**Features/Enhancements:**
+
+ - Add support for Django 6.0 (#836) @albertyw
+ - Add support for Python 3.14 (#834) @albertyw
+ - Get paginator limit from URL params (#646) @strig
+ - Hide pagination when there's only one page (#844) @ShlomoCode
+
+**Maintenance and Cleanup:**
+ - Remove official support for Python 3.9 (#834) @albertyw
+ - Dependency updates
+
+
+## [5.4.3](https://github.com/jazzband/django-silk/tree/5.4.3) (2025-09-08)
+:release-by: Albert Wang (@albertyw)
+[Full Changelog](https://github.com/jazzband/django-silk/compare/5.4.2..5.4.3)
+
+**Fixes:**
+
+ - Fix double EXPLAIN when calling explain on queryset (#654) @stereodamage
+ - Fix serialization issues for binary and json fields (#821) @albertyw
+
+
+## [5.4.2](https://github.com/jazzband/django-silk/tree/5.4.2) (2025-08-17)
+:release-by: Albert Wang (@albertyw)
+[Full Changelog](https://github.com/jazzband/django-silk/compare/5.4.1..5.4.2)
+
+**Fixes:**
+
+ - Reverts #798 which causes issues when serializing JSONFields (#807) 
@albertyw
+ - Also reverts #798 which has a race condition when modifying `execute_sql` 
(#816) @albertyw
+ - Catch and ignore sql encoding errors (#810) @albertyw @bpascard
+
+**Maintenance and Cleanup:**
+
+ - Document that context_processors.request is required (#815) @albertyw
+ - Fix documentation formatting (#810) @albertyw
+ - Test refactors (#814) @albertyw
+
+
+## [5.4.1](https://github.com/jazzband/django-silk/tree/5.4.1) (2025-08-10)
+:release-by: Albert Wang (@albertyw)
+[Full Changelog](https://github.com/jazzband/django-silk/compare/5.4.0..5.4.1)
+
+**Fixes:**
+
+ - Fixes curl/client values rendering in request_detail (#797) @bcmyguest
+ - Fix serialization of non-unicode binary data, add cleanup in middleware 
(#798) @glennmatthews
+ - Make transactions target the DB alias selected by the router (#801) 
@OscarVanL
+
+**Maintenance and Cleanup:**
+
+ - Dependency updates
+ - Documentation updates
+
+
+## [5.4.0](https://github.com/jazzband/django-silk/tree/5.4.0) (2025-05-03)
+:release-by: Albert Wang (@albertyw)
+[Full Changelog](https://github.com/jazzband/django-silk/compare/5.3.2..5.4.0)
+
+**Note: this release removes support for Django 5.0**
+**Note: this release removes autoformatting of python snippets; continue 
formatting by pip installing `django-silk[formatting]`**
+
+**Features/Enhancements:**
+
+ - Add support for Django 5.2 (#784) @albertyw
+ - Support opening SQL details in a new window (#788) @joaopedroalbq
+ - Avoid timeouts when deserializing large jsons (#768) @quertenmont
+ - Make autopep8 optional (#782) @albertyw
+
+**Fixes:**
+
+ - Fix masking sensitive data when an empty `SILKY_SENSITIVE_KEYS` is provided 
(#777) @ahsanshafiq742
+
+**Maintenance and Cleanup:**
+
+ - Remove support for Django 5.0 (#783) @albertyw
+ - Fix logger deprecations (#766) @rjdebastiani
+ - Update dependencies and various autoupdate cleanups
+
+
 ## [5.3.2](https://github.com/jazzband/django-silk/tree/5.3.2) (2024-12-05)
 :release-by: Albert Wang (@albertyw)
 [Full Changelog](https://github.com/jazzband/django-silk/compare/5.3.1..5.3.2)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/PKG-INFO 
new/django_silk-5.5.0/PKG-INFO
--- old/django_silk-5.3.2/PKG-INFO      2024-12-14 06:54:07.738027600 +0100
+++ new/django_silk-5.5.0/PKG-INFO      2026-03-07 09:15:50.052632300 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: django-silk
-Version: 5.3.2
+Version: 5.5.0
 Summary: Silky smooth profiling for the Django Framework
 Home-page: https://github.com/jazzband/django-silk
 Author: Michael Ford
@@ -10,25 +10,39 @@
 Classifier: Environment :: Web Environment
 Classifier: Framework :: Django
 Classifier: Framework :: Django :: 4.2
-Classifier: Framework :: Django :: 5.0
 Classifier: Framework :: Django :: 5.1
+Classifier: Framework :: Django :: 5.2
+Classifier: Framework :: Django :: 6.0
 Classifier: Intended Audience :: Developers
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
 Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
-Requires-Python: >=3.9
+Requires-Python: >=3.10
 Description-Content-Type: text/markdown
 License-File: LICENSE
 Requires-Dist: Django>=4.2
 Requires-Dist: sqlparse
-Requires-Dist: autopep8
 Requires-Dist: gprof2dot>=2017.09.19
+Provides-Extra: formatting
+Requires-Dist: autopep8; extra == "formatting"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
 
 # Silk
 
@@ -62,8 +76,8 @@
 
 Silk has been tested with:
 
-* Django: 4.2, 5.0, 5.1
-* Python: 3.9, 3.10, 3.11, 3.12, 3.13
+* Django: 4.2, 5.1, 5.2, 6.0
+* Python: 3.10, 3.11, 3.12, 3.13, 3.14
 
 ## Installation
 
@@ -73,6 +87,12 @@
 pip install django-silk
 ```
 
+To including optional formatting of python snippets:
+
+```bash
+pip install django-silk[formatting]
+```
+
 In `settings.py` add the following:
 
 ```python
@@ -82,6 +102,17 @@
     ...
 ]
 
+TEMPLATES = [{
+    ...
+    'OPTIONS': {
+        'context_processors': [
+            ...
+            'django.template.context_processors.request',
+        ],
+    },
+}]
+
+
 INSTALLED_APPS = (
     ...
     'silk'
@@ -129,14 +160,14 @@
 Via [github tags](https://github.com/jazzband/django-silk/releases):
 
 ```bash
-pip install https://github.com/jazzband/silk/archive/<version>.tar.gz
+pip install 
git+https://github.com/jazzband/django-silk.git@<version>#egg=django_silk
 ```
 
 You can install from master using the following, but please be aware that the 
version in master
 may not be working for all versions specified in [requirements](#requirements)
 
 ```bash
-pip install -e git+https://github.com/jazzband/django-silk.git#egg=django-silk
+pip install -e git+https://github.com/jazzband/django-silk.git#egg=django_silk
 ```
 
 ## Features
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/README.md 
new/django_silk-5.5.0/README.md
--- old/django_silk-5.3.2/README.md     2024-12-14 06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/README.md     2026-03-07 09:15:38.000000000 +0100
@@ -30,8 +30,8 @@
 
 Silk has been tested with:
 
-* Django: 4.2, 5.0, 5.1
-* Python: 3.9, 3.10, 3.11, 3.12, 3.13
+* Django: 4.2, 5.1, 5.2, 6.0
+* Python: 3.10, 3.11, 3.12, 3.13, 3.14
 
 ## Installation
 
@@ -41,6 +41,12 @@
 pip install django-silk
 ```
 
+To including optional formatting of python snippets:
+
+```bash
+pip install django-silk[formatting]
+```
+
 In `settings.py` add the following:
 
 ```python
@@ -50,6 +56,17 @@
     ...
 ]
 
+TEMPLATES = [{
+    ...
+    'OPTIONS': {
+        'context_processors': [
+            ...
+            'django.template.context_processors.request',
+        ],
+    },
+}]
+
+
 INSTALLED_APPS = (
     ...
     'silk'
@@ -97,14 +114,14 @@
 Via [github tags](https://github.com/jazzband/django-silk/releases):
 
 ```bash
-pip install https://github.com/jazzband/silk/archive/<version>.tar.gz
+pip install 
git+https://github.com/jazzband/django-silk.git@<version>#egg=django_silk
 ```
 
 You can install from master using the following, but please be aware that the 
version in master
 may not be working for all versions specified in [requirements](#requirements)
 
 ```bash
-pip install -e git+https://github.com/jazzband/django-silk.git#egg=django-silk
+pip install -e git+https://github.com/jazzband/django-silk.git#egg=django_silk
 ```
 
 ## Features
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/django_silk.egg-info/PKG-INFO 
new/django_silk-5.5.0/django_silk.egg-info/PKG-INFO
--- old/django_silk-5.3.2/django_silk.egg-info/PKG-INFO 2024-12-14 
06:54:07.000000000 +0100
+++ new/django_silk-5.5.0/django_silk.egg-info/PKG-INFO 2026-03-07 
09:15:49.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: django-silk
-Version: 5.3.2
+Version: 5.5.0
 Summary: Silky smooth profiling for the Django Framework
 Home-page: https://github.com/jazzband/django-silk
 Author: Michael Ford
@@ -10,25 +10,39 @@
 Classifier: Environment :: Web Environment
 Classifier: Framework :: Django
 Classifier: Framework :: Django :: 4.2
-Classifier: Framework :: Django :: 5.0
 Classifier: Framework :: Django :: 5.1
+Classifier: Framework :: Django :: 5.2
+Classifier: Framework :: Django :: 6.0
 Classifier: Intended Audience :: Developers
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
 Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
-Requires-Python: >=3.9
+Requires-Python: >=3.10
 Description-Content-Type: text/markdown
 License-File: LICENSE
 Requires-Dist: Django>=4.2
 Requires-Dist: sqlparse
-Requires-Dist: autopep8
 Requires-Dist: gprof2dot>=2017.09.19
+Provides-Extra: formatting
+Requires-Dist: autopep8; extra == "formatting"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
 
 # Silk
 
@@ -62,8 +76,8 @@
 
 Silk has been tested with:
 
-* Django: 4.2, 5.0, 5.1
-* Python: 3.9, 3.10, 3.11, 3.12, 3.13
+* Django: 4.2, 5.1, 5.2, 6.0
+* Python: 3.10, 3.11, 3.12, 3.13, 3.14
 
 ## Installation
 
@@ -73,6 +87,12 @@
 pip install django-silk
 ```
 
+To including optional formatting of python snippets:
+
+```bash
+pip install django-silk[formatting]
+```
+
 In `settings.py` add the following:
 
 ```python
@@ -82,6 +102,17 @@
     ...
 ]
 
+TEMPLATES = [{
+    ...
+    'OPTIONS': {
+        'context_processors': [
+            ...
+            'django.template.context_processors.request',
+        ],
+    },
+}]
+
+
 INSTALLED_APPS = (
     ...
     'silk'
@@ -129,14 +160,14 @@
 Via [github tags](https://github.com/jazzband/django-silk/releases):
 
 ```bash
-pip install https://github.com/jazzband/silk/archive/<version>.tar.gz
+pip install 
git+https://github.com/jazzband/django-silk.git@<version>#egg=django_silk
 ```
 
 You can install from master using the following, but please be aware that the 
version in master
 may not be working for all versions specified in [requirements](#requirements)
 
 ```bash
-pip install -e git+https://github.com/jazzband/django-silk.git#egg=django-silk
+pip install -e git+https://github.com/jazzband/django-silk.git#egg=django_silk
 ```
 
 ## Features
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/django_silk.egg-info/SOURCES.txt 
new/django_silk-5.5.0/django_silk.egg-info/SOURCES.txt
--- old/django_silk-5.3.2/django_silk.egg-info/SOURCES.txt      2024-12-14 
06:54:07.000000000 +0100
+++ new/django_silk-5.5.0/django_silk.egg-info/SOURCES.txt      2026-03-07 
09:15:49.000000000 +0100
@@ -213,6 +213,7 @@
 silk/static/silk/js/pages/request.js
 silk/static/silk/js/pages/requests.js
 silk/static/silk/js/pages/root_base.js
+silk/static/silk/js/pages/sql.js
 silk/static/silk/js/pages/sql_detail.js
 silk/static/silk/js/pages/summary.js
 silk/static/silk/lib/bootstrap-datetimepicker.min.css
@@ -286,6 +287,7 @@
 silk/templatetags/silk_filters.py
 silk/templatetags/silk_inclusion.py
 silk/templatetags/silk_nav.py
+silk/templatetags/silk_urls.py
 silk/utils/__init__.py
 silk/utils/data_deletion.py
 silk/utils/pagination.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/django_silk.egg-info/requires.txt 
new/django_silk-5.5.0/django_silk.egg-info/requires.txt
--- old/django_silk-5.3.2/django_silk.egg-info/requires.txt     2024-12-14 
06:54:07.000000000 +0100
+++ new/django_silk-5.5.0/django_silk.egg-info/requires.txt     2026-03-07 
09:15:49.000000000 +0100
@@ -1,4 +1,6 @@
 Django>=4.2
 sqlparse
-autopep8
 gprof2dot>=2017.09.19
+
+[formatting]
+autopep8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/docs/index.rst 
new/django_silk-5.5.0/docs/index.rst
--- old/django_silk-5.3.2/docs/index.rst        2024-12-14 06:53:50.000000000 
+0100
+++ new/django_silk-5.5.0/docs/index.rst        2026-03-07 09:15:38.000000000 
+0100
@@ -57,5 +57,5 @@
 Requirements
 ------------
 
-* Django: 4.2, 5.0, 5.1
-* Python: 3.9, 3.10, 3.11, 3.12, 3.13
+* Django: 4.2, 5.1, 5.2, 6.0
+* Python: 3.10, 3.11, 3.12, 3.13, 3.14
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/docs/quickstart.rst 
new/django_silk-5.5.0/docs/quickstart.rst
--- old/django_silk-5.3.2/docs/quickstart.rst   2024-12-14 06:53:50.000000000 
+0100
+++ new/django_silk-5.5.0/docs/quickstart.rst   2026-03-07 09:15:38.000000000 
+0100
@@ -12,13 +12,24 @@
 Add the following to your ``settings.py``:
 
 .. code-block:: python
-       
+
        MIDDLEWARE = [
            ...
            'silk.middleware.SilkyMiddleware',
            ...
        ]
 
+       TEMPLATES = [{
+           ...
+           'OPTIONS': {
+               'context_processors': [
+                   ...
+                   'django.template.context_processors.request',
+               ],
+           },
+       }]
+
+
        INSTALLED_APPS = [
            ...
            'silk.apps.SilkAppConfig'
@@ -27,7 +38,7 @@
 Add the following to your ``urls.py``:
 
 .. code-block:: python
-       
+
        urlpatterns += [path('silk', include('silk.urls', namespace='silk'))]
 
 Run ``migrate`` to create Silk's database tables:
@@ -38,6 +49,16 @@
 
 And voila! Silk will begin intercepting requests and queries which you can 
inspect by visiting ``/silk/``
 
+Python Snippet Formatting
+-------------------------
+
+Silk supports generating Python snippets to reproduce requests.
+To enable autopep8 formatting of these snippets, install Silk with the 
`formatting` extras:
+
+.. code-block:: bash
+
+    pip install django-silk[formatting]
+
 Other Installation Options
 --------------------------
 
@@ -51,4 +72,4 @@
 
 .. code-block:: bash
 
-       pip install -e git+https://github.com/jazzband/django-silk.git#egg=silk
+       pip install -e 
git+https://github.com/jazzband/django-silk.git#egg=django_silk
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/docs/troubleshooting.rst 
new/django_silk-5.5.0/docs/troubleshooting.rst
--- old/django_silk-5.3.2/docs/troubleshooting.rst      2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/docs/troubleshooting.rst      2026-03-07 
09:15:38.000000000 +0100
@@ -18,6 +18,22 @@
 
 See this `github issue <https://github.com/jazzband/django-silk/issues/21>`_ 
for more details and workarounds.
 
+Context Processor
+-----------------
+
+Silk requires the template context to include a ``request`` object in order to 
save and analyze it.
+
+If you see errors like:
+
+.. code-block:: text
+
+    File 
"/service/venv/lib/python3.12/site-packages/silk/templatetags/silk_nav.py", 
line 9, in navactive
+      path = request.path
+             ^^^^^^^^^^^^
+    AttributeError: 'str' object has no attribute 'path'
+
+Include ``django.template.context_processors.request`` in your Django 
settings' ``TEMPLATES`` context processors as `recommended 
<https://github.com/jazzband/django-silk/issues/805>`_.
+
 Middleware
 ----------
 
@@ -29,4 +45,5 @@
 To `avoid <https://github.com/jazzband/django-silk/issues/265>`_ `deadlock 
<https://github.com/jazzband/django-silk/issues/294>`_ `issues 
<https://github.com/jazzband/django-silk/issues/371>`_, you might want to 
decouple silk's garbage collection from your webserver's request processing, 
set ``SILKY_MAX_RECORDED_REQUESTS_CHECK_PERCENT=0`` and trigger it manually, 
e.g. in a cron job:
 
 .. code-block:: bash
+
     python manage.py silk_request_garbage_collect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/package.json 
new/django_silk-5.5.0/package.json
--- old/django_silk-5.3.2/package.json  2024-12-14 06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/package.json  2026-03-07 09:15:38.000000000 +0100
@@ -1,6 +1,6 @@
 {
   "name": "silk",
-  "version": "5.3.2",
+  "version": "5.5.0",
   "description": "https://github.com/jazzband/django-silk";,
   "main": "index.js",
   "directories": {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/project/project/settings.py 
new/django_silk-5.5.0/project/project/settings.py
--- old/django_silk-5.3.2/project/project/settings.py   2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/project/project/settings.py   2026-03-07 
09:15:38.000000000 +0100
@@ -106,10 +106,7 @@
         'APP_DIRS': True,
         'OPTIONS': {
             'context_processors': [
-                'django.template.context_processors.debug',
                 'django.template.context_processors.request',
-                'django.contrib.auth.context_processors.auth',
-                'django.contrib.messages.context_processors.messages',
             ],
         },
     },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/project/tests/test_end_points.py 
new/django_silk-5.5.0/project/tests/test_end_points.py
--- old/django_silk-5.3.2/project/tests/test_end_points.py      2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/project/tests/test_end_points.py      2026-03-07 
09:15:38.000000000 +0100
@@ -61,7 +61,16 @@
             .filter(request_id__isnull=False)
         )
         request_id = request_query_data['request_id']
-        response = self.client.get(silky_reverse('request_sql', 
kwargs={'request_id': request_id}))
+        base_url = silky_reverse('request_sql', kwargs={'request_id': 
request_id})
+        response = self.client.get(base_url)
+        self.assertTrue(response.status_code == 200)
+
+        # Test with valid page size
+        response = self.client.get(base_url + "?per_page=100")
+        self.assertTrue(response.status_code == 200)
+
+        # Test with invalid page size
+        response = self.client.get(base_url + "?per_page=notanumber")
         self.assertTrue(response.status_code == 200)
 
     def test_request_sql_detail(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/project/tests/test_execute_sql.py 
new/django_silk-5.5.0/project/tests/test_execute_sql.py
--- old/django_silk-5.3.2/project/tests/test_execute_sql.py     2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/project/tests/test_execute_sql.py     2026-03-07 
09:15:38.000000000 +0100
@@ -1,6 +1,7 @@
 from unittest.mock import Mock, NonCallableMagicMock, NonCallableMock, patch
 
 from django.test import TestCase
+from django.utils.encoding import force_str
 
 from silk.collector import DataCollector
 from silk.models import Request, SQLQuery
@@ -8,14 +9,18 @@
 
 from .util import delete_all_models
 
+_simple_mock_query_sql = 'SELECT * FROM table_name WHERE column1 = %s'
+_simple_mock_query_params = ('asdf',)
+_non_unicode_binary_mock_query_params = (b'\x0a\x00\x00\xff',)
+_unicode_binary_mock_query_params = ('🫠'.encode(),)
 
-def mock_sql():
+
+def mock_sql(mock_query_params):
     mock_sql_query = Mock(spec_set=['_execute_sql', 'query', 'as_sql', 
'connection'])
     mock_sql_query._execute_sql = Mock()
     mock_sql_query.query = NonCallableMock(spec_set=['model'])
     mock_sql_query.query.model = Mock()
-    query_string = 'SELECT * from table_name'
-    mock_sql_query.as_sql = Mock(return_value=(query_string, ()))
+    mock_sql_query.as_sql = Mock(return_value=(_simple_mock_query_sql, 
mock_query_params))
 
     mock_sql_query.connection = NonCallableMock(
         spec_set=['cursor', 'features', 'ops'],
@@ -27,34 +32,35 @@
             spec_set=['supports_explaining_query_execution'],
             supports_explaining_query_execution=True
         ),
-        ops=NonCallableMock(spec_set=['explain_query_prefix']),
+        ops=NonCallableMock(spec_set=['explain_query_prefix'],
+                            explain_query_prefix=Mock(return_value='')),
     )
 
-    return mock_sql_query, query_string
-
+    return mock_sql_query, mock_query_params
 
-def call_execute_sql(cls, request):
-    DataCollector().configure(request=request)
-    delete_all_models(SQLQuery)
-    cls.mock_sql, cls.query_string = mock_sql()
-    kwargs = {
-        'one': 1,
-        'two': 2
-    }
-    cls.args = [1, 2]
-    cls.kwargs = kwargs
-    execute_sql(cls.mock_sql, *cls.args, **cls.kwargs)
-
-
-class TestCallNoRequest(TestCase):
-    @classmethod
-    def setUpClass(cls):
-        super().setUpClass()
-        call_execute_sql(cls, None)
 
+class BaseTestCase(TestCase):
     def tearDown(self):
         DataCollector().stop_python_profiler()
 
+    def call_execute_sql(self, request, mock_query_params):
+        DataCollector().configure(request=request)
+        delete_all_models(SQLQuery)
+        self.query_string = _simple_mock_query_sql
+        self.mock_sql, self.query_params = mock_sql(mock_query_params)
+        self.kwargs = {
+            'one': 1,
+            'two': 2
+        }
+        self.args = [1, 2]
+        execute_sql(self.mock_sql, *self.args, **self.kwargs)
+
+
+class TestCallNoRequest(BaseTestCase):
+    def setUp(self):
+        super().setUp()
+        self.call_execute_sql(None, _simple_mock_query_params)
+
     def test_called(self):
         self.mock_sql._execute_sql.assert_called_once_with(*self.args, 
**self.kwargs)
 
@@ -62,33 +68,33 @@
         self.assertEqual(0, len(DataCollector().queries))
 
 
-class TestCallRequest(TestCase):
-    @classmethod
-    def setUpClass(cls):
-        super().setUpClass()
-        call_execute_sql(cls, Request())
-
-    def tearDown(self):
-        DataCollector().stop_python_profiler()
-
-    def test_called(self):
+class TestCallRequest(BaseTestCase):
+    def test_query_simple(self):
+        self.call_execute_sql(Request(), _simple_mock_query_params)
         self.mock_sql._execute_sql.assert_called_once_with(*self.args, 
**self.kwargs)
-
-    def test_count(self):
         self.assertEqual(1, len(DataCollector().queries))
+        query = list(DataCollector().queries.values())[0]
+        expected = self.query_string % tuple(force_str(param) for param in 
self.query_params)
+        self.assertEqual(query['query'], expected)
 
-    def test_query(self):
+    def test_query_unicode(self):
+        self.call_execute_sql(Request(), _unicode_binary_mock_query_params)
+        self.mock_sql._execute_sql.assert_called_once_with(*self.args, 
**self.kwargs)
+        self.assertEqual(1, len(DataCollector().queries))
         query = list(DataCollector().queries.values())[0]
-        self.assertEqual(query['query'], self.query_string)
+        expected = self.query_string % tuple(force_str(param) for param in 
self.query_params)
+        self.assertEqual(query['query'], expected)
 
+    def test_query_non_unicode(self):
+        self.call_execute_sql(Request(), _non_unicode_binary_mock_query_params)
+        self.mock_sql._execute_sql.assert_called_once_with(*self.args, 
**self.kwargs)
+        self.assertEqual(0, len(DataCollector().queries))
 
-class TestCallSilky(TestCase):
-    def tearDown(self):
-        DataCollector().stop_python_profiler()
 
+class TestCallSilky(BaseTestCase):
     def test_no_effect(self):
         DataCollector().configure()
-        sql, _ = mock_sql()
+        sql, _ = mock_sql(_simple_mock_query_params)
         sql.query.model = NonCallableMagicMock(spec_set=['__module__'])
         sql.query.model.__module__ = 'silk.models'
         # No SQLQuery models should be created for silk requests for obvious 
reasons
@@ -97,10 +103,7 @@
             self.assertFalse(mock_DataCollector().register_query.call_count)
 
 
-class TestCollectorInteraction(TestCase):
-    def tearDown(self):
-        DataCollector().stop_python_profiler()
-
+class TestCollectorInteraction(BaseTestCase):
     def _query(self):
         try:
             query = list(DataCollector().queries.values())[0]
@@ -110,23 +113,44 @@
 
     def test_request(self):
         
DataCollector().configure(request=Request.objects.create(path='/path/to/somewhere'))
-        sql, _ = mock_sql()
+        sql, _ = mock_sql(_simple_mock_query_params)
         execute_sql(sql)
         query = self._query()
         self.assertEqual(query['request'], DataCollector().request)
 
     def test_registration(self):
         
DataCollector().configure(request=Request.objects.create(path='/path/to/somewhere'))
-        sql, _ = mock_sql()
+        sql, _ = mock_sql(_simple_mock_query_params)
         execute_sql(sql)
         query = self._query()
         self.assertIn(query, DataCollector().queries.values())
 
-    def test_explain(self):
+    def test_explain_simple(self):
+        
DataCollector().configure(request=Request.objects.create(path='/path/to/somewhere'))
+        sql, params = mock_sql(_simple_mock_query_params)
+        prefix = "EXPLAIN"
+        mock_cursor = sql.connection.cursor.return_value.__enter__.return_value
+        sql.connection.ops.explain_query_prefix.return_value = prefix
+        execute_sql(sql)
+        self.assertNotIn(prefix, params)
+        mock_cursor.execute.assert_called_once_with(f"{prefix} 
{_simple_mock_query_sql}", params)
+
+    def test_explain_unicode(self):
+        
DataCollector().configure(request=Request.objects.create(path='/path/to/somewhere'))
+        sql, params = mock_sql(_unicode_binary_mock_query_params)
+        prefix = "EXPLAIN"
+        mock_cursor = sql.connection.cursor.return_value.__enter__.return_value
+        sql.connection.ops.explain_query_prefix.return_value = prefix
+        execute_sql(sql)
+        self.assertNotIn(prefix, params)
+        mock_cursor.execute.assert_called_once_with(f"{prefix} 
{_simple_mock_query_sql}", params)
+
+    def test_explain_non_unicode(self):
         
DataCollector().configure(request=Request.objects.create(path='/path/to/somewhere'))
-        sql, qs = mock_sql()
+        sql, params = mock_sql(_non_unicode_binary_mock_query_params)
         prefix = "EXPLAIN"
         mock_cursor = sql.connection.cursor.return_value.__enter__.return_value
         sql.connection.ops.explain_query_prefix.return_value = prefix
         execute_sql(sql)
-        mock_cursor.execute.assert_called_once_with(f"{prefix} {qs}", ())
+        self.assertNotIn(prefix, params)
+        self.assertFalse(mock_cursor.execute.called)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_silk-5.3.2/project/tests/test_sensitive_data_in_request.py 
new/django_silk-5.5.0/project/tests/test_sensitive_data_in_request.py
--- old/django_silk-5.3.2/project/tests/test_sensitive_data_in_request.py       
2024-12-14 06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/project/tests/test_sensitive_data_in_request.py       
2026-03-07 09:15:38.000000000 +0100
@@ -75,6 +75,12 @@
         expected = f"foo={CLEANSED}"
         self.assertEqual(expected, self._mask(body))
 
+    def test_sensitive_values_remain_unmasked_with_empty_settings(self):
+        SilkyConfig().SILKY_SENSITIVE_KEYS = {}
+        body = "foo=hidethis"
+        expected = "foo=hidethis"
+        self.assertEqual(expected, self._mask(body))
+
 
 class MaskCredentialsInJsonTest(TestCase):
     def tearDown(self):
@@ -122,6 +128,10 @@
         SilkyConfig().SILKY_SENSITIVE_KEYS = {"foo"}
         self.assertNotIn("hidethis", self._mask({"foo": "hidethis"}))
 
+    def test_sensitive_values_remain_unmasked_with_empty_settings(self):
+        SilkyConfig().SILKY_SENSITIVE_KEYS = {}
+        self.assertIn("hidethis", self._mask({"foo": "hidethis"}))
+
 
 class TestEncodingForRequests(TestCase):
     """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/requirements.txt 
new/django_silk-5.5.0/requirements.txt
--- old/django_silk-5.3.2/requirements.txt      2024-12-14 06:53:50.000000000 
+0100
+++ new/django_silk-5.5.0/requirements.txt      2026-03-07 09:15:38.000000000 
+0100
@@ -1,9 +1,9 @@
-coverage==7.6.8
-factory-boy==3.3.1
-freezegun==1.5.1
-networkx==3.2.1
-pillow==11.0.0
-pydot==3.0.3
-pygments==2.18.0
-pytest-cov==6.0.0
-pytest-django==4.9.0
+coverage==7.13.0
+factory-boy==3.3.3
+freezegun==1.5.5
+networkx==3.4.2
+pillow==12.1.1
+pydot==3.0.4
+pygments==2.19.2
+pytest-cov==7.0.0
+pytest-django==4.11.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/setup.py 
new/django_silk-5.5.0/setup.py
--- old/django_silk-5.3.2/setup.py      2024-12-14 06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/setup.py      2026-03-07 09:15:38.000000000 +0100
@@ -22,25 +22,28 @@
         'Environment :: Web Environment',
         'Framework :: Django',
         'Framework :: Django :: 4.2',
-        'Framework :: Django :: 5.0',
         'Framework :: Django :: 5.1',
+        'Framework :: Django :: 5.2',
+        'Framework :: Django :: 6.0',
         'Intended Audience :: Developers',
         'Operating System :: OS Independent',
         'Programming Language :: Python',
-        'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
         'Programming Language :: Python :: 3.11',
         'Programming Language :: Python :: 3.12',
         'Programming Language :: Python :: 3.13',
+        'Programming Language :: Python :: 3.14',
         'Topic :: Internet :: WWW/HTTP',
         'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
     ],
     install_requires=[
         'Django>=4.2',
         'sqlparse',
-        'autopep8',
         'gprof2dot>=2017.09.19',
     ],
-    python_requires='>=3.9',
+    extras_require={
+        'formatting': ['autopep8'],
+    },
+    python_requires='>=3.10',
     setup_requires=['setuptools_scm'],
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_silk-5.3.2/silk/code_generation/django_test_client.py 
new/django_silk-5.5.0/silk/code_generation/django_test_client.py
--- old/django_silk-5.3.2/silk/code_generation/django_test_client.py    
2024-12-14 06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/silk/code_generation/django_test_client.py    
2026-03-07 09:15:38.000000000 +0100
@@ -1,6 +1,9 @@
 from urllib.parse import urlencode
 
-import autopep8
+try:
+    import autopep8
+except ImportError:
+    autopep8 = None
 from django.template import Context, Template
 
 from silk.profiling.dynamic import is_str_typ
@@ -42,7 +45,13 @@
             data = "'%s'" % data
         context['data'] = data
         context['query_params'] = query_params
-    return autopep8.fix_code(
-        t.render(Context(context, autoescape=False)),
-        options=autopep8.parse_args(['--aggressive', '']),
-    )
+    code = t.render(Context(context, autoescape=False))
+    if autopep8:
+        # autopep8 is not a hard requirement, so we check if it's available
+        # if autopep8 is available, we use it to format the code and do things
+        # like remove long lines and improve readability
+        code = autopep8.fix_code(
+            code,
+            options=autopep8.parse_args(['--aggressive', '']),
+        )
+    return code
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/middleware.py 
new/django_silk-5.5.0/silk/middleware.py
--- old/django_silk-5.3.2/silk/middleware.py    2024-12-14 06:53:50.000000000 
+0100
+++ new/django_silk-5.5.0/silk/middleware.py    2026-03-07 09:15:38.000000000 
+0100
@@ -2,12 +2,13 @@
 import random
 
 from django.conf import settings
-from django.db import DatabaseError, transaction
+from django.db import DatabaseError, router, transaction
 from django.db.models.sql.compiler import SQLCompiler
 from django.urls import NoReverseMatch, reverse
 from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
 
+from silk import models
 from silk.collector import DataCollector
 from silk.config import SilkyConfig
 from silk.errors import SilkNotConfigured
@@ -142,29 +143,31 @@
         request_model = RequestModelFactory(request).construct_request_model()
         DataCollector().configure(request_model, should_profile=should_profile)
 
-    @transaction.atomic()
     def _process_response(self, request, response):
-        Logger.debug('Process response')
-        with silk_meta_profiler():
-            collector = DataCollector()
-            collector.stop_python_profiler()
-            silk_request = collector.request
+        # Use a context manager instead of a decorator so db_for_write is 
evaluated at runtime,
+        # which is important for dynamic database configurations (e.g., 
multitenancy).
+        with transaction.atomic(using=router.db_for_write(models.SQLQuery)):
+            Logger.debug('Process response')
+            with silk_meta_profiler():
+                collector = DataCollector()
+                collector.stop_python_profiler()
+                silk_request = collector.request
+                if silk_request:
+                    ResponseModelFactory(response).construct_response_model()
+                    silk_request.end_time = timezone.now()
+                    collector.finalise()
+                else:
+                    Logger.error(
+                        'No request model was available when processing 
response. '
+                        'Did something go wrong in 
process_request/process_view?'
+                        '\n' + str(request) + '\n\n' + str(response)
+                    )
+            # Need to save the data outside the silk_meta_profiler
+            # Otherwise the  meta time collected in the context manager
+            # is not taken in account
             if silk_request:
-                ResponseModelFactory(response).construct_response_model()
-                silk_request.end_time = timezone.now()
-                collector.finalise()
-            else:
-                Logger.error(
-                    'No request model was available when processing response. '
-                    'Did something go wrong in process_request/process_view?'
-                    '\n' + str(request) + '\n\n' + str(response)
-                )
-        # Need to save the data outside the silk_meta_profiler
-        # Otherwise the  meta time collected in the context manager
-        # is not taken in account
-        if silk_request:
-            silk_request.save()
-        Logger.debug('Process response done.')
+                silk_request.save()
+            Logger.debug('Process response done.')
 
     def process_response(self, request, response):
         max_attempts = 2
@@ -178,7 +181,7 @@
                     break
                 except (AttributeError, DatabaseError):
                     if attempts >= max_attempts:
-                        Logger.warn('Exhausted _process_response attempts; not 
processing request')
+                        Logger.warning('Exhausted _process_response attempts; 
not processing request')
                         break
                 attempts += 1
         return response
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/model_factory.py 
new/django_silk-5.5.0/silk/model_factory.py
--- old/django_silk-5.3.2/silk/model_factory.py 2024-12-14 06:53:50.000000000 
+0100
+++ new/django_silk-5.5.0/silk/model_factory.py 2026-03-07 09:15:38.000000000 
+0100
@@ -91,7 +91,7 @@
             pattern = re.compile(key_string, re.I)
             if isinstance(obj, dict):
                 for key in obj.keys():
-                    if pattern.search(key):
+                    if key_string and pattern.search(key):
                         obj[key] = RequestModelFactory.CLEANSED_SUBSTITUTE
                     else:
                         obj[key] = replace_pattern_values(obj[key])
@@ -99,18 +99,19 @@
                 for index, item in enumerate(obj):
                     obj[index] = replace_pattern_values(item)
             else:
-                if pattern.search(str(obj)):
+                if key_string and pattern.search(str(obj)):
                     return RequestModelFactory.CLEANSED_SUBSTITUTE
             return obj
 
         try:
             json_body = json.loads(body)
         except Exception as e:
-            pattern = re.compile(fr'(({key_string})[^=]*)=(.*?)(&|$)', re.M | 
re.I)
-            try:
-                body = re.sub(pattern, 
f'\\1={RequestModelFactory.CLEANSED_SUBSTITUTE}\\4', body)
-            except Exception:
-                Logger.debug(f'{str(e)}')
+            if key_string:
+                pattern = re.compile(fr'(({key_string})[^=]*)=(.*?)(&|$)', 
re.M | re.I)
+                try:
+                    body = re.sub(pattern, 
f'\\1={RequestModelFactory.CLEANSED_SUBSTITUTE}\\4', body)
+                except Exception:
+                    Logger.debug(f'{str(e)}')
         else:
             body = json.dumps(replace_pattern_values(json_body), 
ensure_ascii=SilkyConfig().SILKY_JSON_ENSURE_ASCII)
 
@@ -278,15 +279,11 @@
                         )
             if content and content_type in content_types_json:
                 # TODO: Perhaps theres a way to format the JSON without 
parsing it?
-                if not isinstance(content, str):
-                    # byte string is not compatible with json.loads(...)
-                    # and json.dumps(...) in python3
-                    content = content.decode()
                 try:
                     body = json.dumps(json.loads(content), sort_keys=True, 
indent=4
                                       , 
ensure_ascii=SilkyConfig().SILKY_JSON_ENSURE_ASCII)
                 except (TypeError, ValueError):
-                    Logger.warn(
+                    Logger.warning(
                         'Response to request with pk %s has content type %s 
but was unable to parse it'
                         % (self.request.pk, content_type)
                     )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/models.py 
new/django_silk-5.5.0/silk/models.py
--- old/django_silk-5.3.2/silk/models.py        2024-12-14 06:53:50.000000000 
+0100
+++ new/django_silk-5.5.0/silk/models.py        2026-03-07 09:15:38.000000000 
+0100
@@ -8,7 +8,7 @@
 from django.conf import settings
 from django.core.files.storage import storages
 from django.core.files.storage.handler import InvalidStorageError
-from django.db import models, transaction
+from django.db import models, router, transaction
 from django.db.models import (
     BooleanField,
     CharField,
@@ -226,18 +226,18 @@
 
 # TODO rewrite docstring
 class SQLQueryManager(models.Manager):
-    @transaction.atomic
     def bulk_create(self, *args, **kwargs):
         """ensure that num_sql_queries remains consistent. Bulk create does 
not call
         the model save() method and hence we must add this logic here too"""
-        if len(args):
-            objs = args[0]
-        else:
-            objs = kwargs.get('objs')
-        for obj in objs:
-            obj.prepare_save()
+        with transaction.atomic(using=router.db_for_write(SQLQuery)):
+            if len(args):
+                objs = args[0]
+            else:
+                objs = kwargs.get('objs')
+            for obj in objs:
+                obj.prepare_save()
 
-        return super().bulk_create(*args, **kwargs)
+            return super().bulk_create(*args, **kwargs)
 
 
 class SQLQuery(models.Model):
@@ -322,16 +322,16 @@
                 self.request.num_sql_queries += 1
                 self.request.save(update_fields=['num_sql_queries'])
 
-    @transaction.atomic()
     def save(self, *args, **kwargs):
-        self.prepare_save()
-        super().save(*args, **kwargs)
+        with transaction.atomic(using=router.db_for_write(self)):
+            self.prepare_save()
+            super().save(*args, **kwargs)
 
-    @transaction.atomic()
     def delete(self, *args, **kwargs):
-        self.request.num_sql_queries -= 1
-        self.request.save()
-        super().delete(*args, **kwargs)
+        with transaction.atomic(using=router.db_for_write(self)):
+            self.request.num_sql_queries -= 1
+            self.request.save()
+            super().delete(*args, **kwargs)
 
 
 class BaseProfile(models.Model):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/sql.py 
new/django_silk-5.5.0/silk/sql.py
--- old/django_silk-5.3.2/silk/sql.py   2024-12-14 06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/silk/sql.py   2026-03-07 09:15:38.000000000 +0100
@@ -53,7 +53,11 @@
 
         # currently we cannot use explain() method
         # for queries other than `select`
-        prefixed_query = f"{prefix} {q}"
+        if q.upper().startswith(prefix.upper()):
+            # to avoid "EXPLAIN EXPLAIN", do not add prefix
+            prefixed_query = q
+        else:
+            prefixed_query = f"{prefix} {q}"
         with connection.cursor() as cur:
             cur.execute(prefixed_query, params)
             result = _unpack_explanation(cur.fetchall())
@@ -77,7 +81,13 @@
             return iter([])
         else:
             return
-    sql_query = q % tuple(force_str(param) for param in params)
+    try:
+        sql_query = q % tuple(force_str(param) for param in params)
+    except UnicodeDecodeError:
+        # Sometimes `force_str` can still raise a UnicodeDecodeError
+        # Reference: https://github.com/jazzband/django-silk/issues?q=encoding
+        # This could log a warning but given this is run in the hot path, 
logging could be too expensive.
+        return self._execute_sql(*args, **kwargs)
     if _should_wrap(sql_query):
         tb = ''.join(reversed(traceback.format_stack()))
         query_dict = {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/static/silk/js/pages/sql.js 
new/django_silk-5.5.0/silk/static/silk/js/pages/sql.js
--- old/django_silk-5.3.2/silk/static/silk/js/pages/sql.js      1970-01-01 
01:00:00.000000000 +0100
+++ new/django_silk-5.5.0/silk/static/silk/js/pages/sql.js      2026-03-07 
09:15:38.000000000 +0100
@@ -0,0 +1,17 @@
+$(document).ready(function () {
+  document.querySelectorAll(".data-row").forEach((rowElement) => {
+    let sqlDetailUrl = rowElement.dataset.sqlDetailUrl;
+    rowElement.addEventListener("mouseup", (e) => {
+      switch (e.button) {
+        case 0:
+          window.location = sqlDetailUrl;
+          break;
+        case 1:
+          window.open(sqlDetailUrl);
+          break;
+        default:
+          break;
+      }
+    });
+  });
+});
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/templates/silk/sql.html 
new/django_silk-5.5.0/silk/templates/silk/sql.html
--- old/django_silk-5.3.2/silk/templates/silk/sql.html  2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/silk/templates/silk/sql.html  2026-03-07 
09:15:38.000000000 +0100
@@ -4,12 +4,14 @@
 {% load silk_filters %}
 {% load static %}
 {% load silk_inclusion %}
+{% load silk_urls %}
 
 {% block pagetitle %}Silky - SQL - {{ silk_request.path }}{% endblock %}
 
 {% block js %}
   <script type="text/javascript" src="{% static 'silk/lib/sortable.js' 
%}"></script>
   {{ block.super }}
+  <script src="{% static 'silk/js/pages/sql.js' %}"></script>
 {% endblock %}
 
 {% block style %}
@@ -20,6 +22,26 @@
     <link rel="stylesheet" href="{% static 'silk/css/components/cell.css' %}">
 {% endblock %}
 
+{% block filter %}
+    <form id="filter-form" action="." method="get"></form>
+
+    <div class="menu-item">
+        <div class="menu-item-outer">
+            <div class="menu-item-inner">
+                <label>Show:
+                    <select name="per_page" form="filter-form" 
onchange="this.form.submit();">
+                        {% for option in options_page_size %}
+                            <option value="{{ option }}"
+                                    {% if option == per_page %}selected{% 
endif %}>{{ option }}</option>
+                        {% endfor %}
+                    </select>
+                </label>
+            </div>
+        </div>
+    </div>
+    {{ block.super }}
+{% endblock %}
+
 {% block menu %}
     {% if profile %}
         {% profile_menu request profile silk_request %}
@@ -57,16 +79,8 @@
                     <th class="right-aligned">Execution Time (ms)</th>
                 </tr>
                 {% for sql_query in items %}
-                    <!-- TODO: Pretty grimy... -->
-                    <tr class="data-row" onclick="window.location=' \
-                       {% if profile and silk_request %}\
-                           {% url "silk:request_and_profile_sql_detail" 
silk_request.id profile.id sql_query.id %}\
-                       {% elif profile %}\
-                           {% url "silk:profile_sql_detail" profile.id 
sql_query.id %}\
-                       {% elif silk_request %}\
-                           {% url "silk:request_sql_detail" silk_request.id 
sql_query.id %}\
-                       {% endif %}\
-                        ';">
+                    {% sql_detail_url silk_request profile sql_query as 
detail_url %}
+                    <tr class="data-row" data-sql-detail-url="{{ detail_url 
}}">
                         <td class="left-aligned">+{{ 
sql_query.start_time_relative }}</td>
                         <td class="left-aligned">{{ sql_query.first_keywords 
}}</td>
                         <td class="left-aligned">{{ 
sql_query.tables_involved|join:", " }}</td>
@@ -77,6 +91,7 @@
 
             </table>
 
+            {% if items.paginator.num_pages > 1 %}
             <div id="table-pagination" class="pagination">
                 <div class="current">
                     Page {{ items.number }} of {{ items.paginator.num_pages }}.
@@ -84,18 +99,19 @@
 
                 <div class="step-links">
                     {% if items.has_previous %}
-                        <a href="?page={{ items.previous_page_number 
}}">previous</a>
+                        <a href="?page={{ items.previous_page_number 
}}&per_page={{ per_page }}">previous</a>
                     {% else %}
                         previous
                     {% endif %}
                     |
                     {% if items.has_next %}
-                        <a href="?page={{ items.next_page_number }}">next</a>
+                        <a href="?page={{ items.next_page_number 
}}&per_page={{ per_page }}">next</a>
                     {% else %}
                         next
                     {% endif %}
                 </div>
             </div>
+            {% endif %}
         </div>
     </div>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/templatetags/silk_urls.py 
new/django_silk-5.5.0/silk/templatetags/silk_urls.py
--- old/django_silk-5.3.2/silk/templatetags/silk_urls.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/django_silk-5.5.0/silk/templatetags/silk_urls.py        2026-03-07 
09:15:38.000000000 +0100
@@ -0,0 +1,17 @@
+from django.template import Library
+from django.urls import reverse
+
+register = Library()
+
+
[email protected]_tag
+def sql_detail_url(silk_request, profile, sql_query):
+    if profile and silk_request:
+        return reverse(
+            "silk:request_and_profile_sql_detail",
+            args=[silk_request.id, profile.id, sql_query.id],
+        )
+    elif profile:
+        return reverse("silk:profile_sql_detail", args=[profile.id, 
sql_query.id])
+    elif silk_request:
+        return reverse("silk:request_sql_detail", args=[silk_request.id, 
sql_query.id])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/utils/pagination.py 
new/django_silk-5.5.0/silk/utils/pagination.py
--- old/django_silk-5.3.2/silk/utils/pagination.py      2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/silk/utils/pagination.py      2026-03-07 
09:15:38.000000000 +0100
@@ -3,8 +3,8 @@
 __author__ = 'mtford'
 
 
-def _page(request, query_set):
-    paginator = Paginator(query_set, 200)
+def _page(request, query_set, per_page=200):
+    paginator = Paginator(query_set, per_page)
     page_number = request.GET.get('page')
     try:
         page = paginator.page(page_number)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/views/request_detail.py 
new/django_silk-5.5.0/silk/views/request_detail.py
--- old/django_silk-5.3.2/silk/views/request_detail.py  2024-12-14 
06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/silk/views/request_detail.py  2026-03-07 
09:15:38.000000000 +0100
@@ -19,24 +19,28 @@
         query_params = None
         if silk_request.query_params:
             query_params = json.loads(silk_request.query_params)
-        body = silk_request.raw_body
-        try:
-            body = json.loads(body)  # Incase encoded as JSON
-        except (ValueError, TypeError):
-            pass
+
         context = {
             'silk_request': silk_request,
-            'curl': curl_cmd(url=request.build_absolute_uri(silk_request.path),
-                             method=silk_request.method,
-                             query_params=query_params,
-                             body=body,
-                             content_type=silk_request.content_type),
             'query_params': json.dumps(query_params, sort_keys=True, indent=4) 
if query_params else None,
-            'client': gen(path=silk_request.path,
-                          method=silk_request.method,
-                          query_params=query_params,
-                          data=body,
-                          content_type=silk_request.content_type),
             'request': request
         }
+
+        if len(silk_request.raw_body) < 20000:  # Don't do this for large 
request
+            body = silk_request.raw_body
+            try:
+                body = json.loads(body)  # Incase encoded as JSON
+            except (ValueError, TypeError):
+                pass
+            context['curl'] = 
curl_cmd(url=request.build_absolute_uri(silk_request.path),
+                                       method=silk_request.method,
+                                       query_params=query_params,
+                                       body=body,
+                                       content_type=silk_request.content_type)
+            context['client'] = gen(path=silk_request.path,
+                                    method=silk_request.method,
+                                    query_params=query_params,
+                                    data=body,
+                                    content_type=silk_request.content_type)
+
         return render(request, 'silk/request.html', context)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/silk/views/sql.py 
new/django_silk-5.5.0/silk/views/sql.py
--- old/django_silk-5.3.2/silk/views/sql.py     2024-12-14 06:53:50.000000000 
+0100
+++ new/django_silk-5.5.0/silk/views/sql.py     2026-03-07 09:15:38.000000000 
+0100
@@ -10,25 +10,33 @@
 
 
 class SQLView(View):
+    page_sizes = [5, 10, 25, 100, 200, 500, 1000]
+    default_page_size = 200
 
     @method_decorator(login_possibly_required)
     @method_decorator(permissions_possibly_required)
     def get(self, request, *_, **kwargs):
         request_id = kwargs.get('request_id')
         profile_id = kwargs.get('profile_id')
+        try:
+            per_page = int(request.GET.get('per_page', self.default_page_size))
+        except (TypeError, ValueError):
+            per_page = self.default_page_size
         context = {
             'request': request,
+            'options_page_size': self.page_sizes,
+            'per_page': per_page,
         }
         if request_id:
             silk_request = Request.objects.get(id=request_id)
             query_set = 
SQLQuery.objects.filter(request=silk_request).order_by('-start_time')
             for q in query_set:
                 q.start_time_relative = q.start_time - silk_request.start_time
-            page = _page(request, query_set)
+            page = _page(request, query_set, per_page)
             context['silk_request'] = silk_request
         if profile_id:
             p = Profile.objects.get(id=profile_id)
-            page = _page(request, p.queries.order_by('-start_time').all())
+            page = _page(request, p.queries.order_by('-start_time').all(), 
per_page)
             context['profile'] = p
         if not (request_id or profile_id):
             raise KeyError('no profile_id or request_id')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_silk-5.3.2/tox.ini 
new/django_silk-5.5.0/tox.ini
--- old/django_silk-5.3.2/tox.ini       2024-12-14 06:53:50.000000000 +0100
+++ new/django_silk-5.5.0/tox.ini       2026-03-07 09:15:38.000000000 +0100
@@ -1,22 +1,23 @@
 [gh-actions]
 python =
-    3.9: py39
     3.10: py310
     3.11: py311
     3.12: py312
     3.13: py313
+    3.14: py314
 
 [gh-actions:env]
 DJANGO =
     4.2: dj42
-    5.0: dj50
     5.1: dj51
+    5.2: dj52
+    6.0: dj60
     main: djmain
 
 [tox]
 envlist =
-    py{38,39,310,311,312,313}-dj42-{sqlite3,mysql,postgresql}
-    py{310,311,312,313}-dj{50,51,main}-{sqlite3,mysql,postgresql}
+    py{310,311,312,313,314}-dj{42,50,51,52}-{sqlite3,mysql,postgresql}
+    py{312,313,314}-dj{60,main}-{sqlite3,mysql,postgresql}
 
 [testenv]
 usedevelop = True
@@ -28,11 +29,14 @@
     mysql: mysqlclient
     postgresql: psycopg2-binary
     dj42: django>=4.2,<4.3
-    dj50: django>=5.0,<5.1
     dj51: django>=5.1,<5.2
+    dj52: django>=5.2,<5.3
+    dj60: django>=6.0,<6.1
     djmain: https://github.com/django/django/archive/main.tar.gz
     py312: setuptools
     py313: setuptools
+    py314: setuptools
+extras = formatting
 setenv =
     PYTHONPATH={toxinidir}:{toxinidir}
     PYTHONDONTWRITEBYTECODE=1

Reply via email to