Hello community, here is the log from the commit of package python-django-silk for openSUSE:Factory checked in at 2020-02-20 14:59:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-django-silk (Old) and /work/SRC/openSUSE:Factory/.python-django-silk.new.26092 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-silk" Thu Feb 20 14:59:39 2020 rev:5 rq:777606 version:4.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-django-silk/python-django-silk.changes 2019-09-17 13:37:31.401841766 +0200 +++ /work/SRC/openSUSE:Factory/.python-django-silk.new.26092/python-django-silk.changes 2020-02-20 14:59:43.218799002 +0100 @@ -1,0 +2,12 @@ +Thu Feb 20 09:08:46 UTC 2020 - Tomáš Chvátal <[email protected]> + +- Update to 4.0.0: + * Ability to clean up all requests/queries #368 (nasirhjafri) + * Used bulk_create to save number of queries #370 (nasirhjafri) + * Dropped Python 2 and 3.4 support #380 (munza) + * Added Python 3.8 support #380 (nasirhjafri) + * Removed django<2.2 support and added django 3.0 support #385 (nasirhjafri) + * Add function support for enabling profiling #391 (tredzko) + * Mask authorization header #376 (StefanMich) + +------------------------------------------------------------------- Old: ---- django-silk-3.0.4.tar.gz New: ---- django-silk-4.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-django-silk.spec ++++++ --- /var/tmp/diff_new_pack.B02yLg/_old 2020-02-20 14:59:44.342801207 +0100 +++ /var/tmp/diff_new_pack.B02yLg/_new 2020-02-20 14:59:44.342801207 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-django-silk # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,16 +16,15 @@ # -%{?!python_module:%define python_module() python-%{**} python3-%{**}} +%define skip_python2 1 Name: python-django-silk -Version: 3.0.4 +Version: 4.0.0 Release: 0 Summary: Profiling for the Django Framework License: MIT -Group: Development/Languages/Python -Url: https://github.com/jazzband/django-silk +URL: https://github.com/jazzband/django-silk Source: https://github.com/jazzband/django-silk/archive/%{version}.tar.gz#/django-silk-%{version}.tar.gz -BuildRequires: %{python_module Django >= 1.11} +BuildRequires: %{python_module Django >= 2.2} BuildRequires: %{python_module Jinja2 >= 2.8} BuildRequires: %{python_module Pillow >= 3.2} BuildRequires: %{python_module Pygments >= 2.0} @@ -36,7 +35,19 @@ BuildRequires: %{python_module requests >= 2.10} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module sqlparse >= 0.1.19} +BuildRequires: fdupes BuildRequires: python-rpm-macros +Requires: python-Django >= 2.2 +Requires: python-Jinja2 >= 2.8 +Requires: python-Pillow >= 3.2 +Requires: python-Pygments >= 2.0 +Requires: python-autopep8 >= 1.2.1 +Requires: python-gprof2dot >= 2017.09.19 +Requires: python-python-dateutil >= 2.4 +Requires: python-pytz > 2014.2 +Requires: python-requests >= 2.10 +Requires: python-sqlparse >= 0.1.19 +BuildArch: noarch # SECTION test requirements BuildRequires: %{python_module contextlib2 >= 0.5.5} BuildRequires: %{python_module factory_boy >= 2.8.1} @@ -48,19 +59,6 @@ BuildRequires: %{python_module simplejson >= 3.13.2} BuildRequires: %{python_module six >= 1.11.0} # /SECTION -BuildRequires: fdupes -Requires: python-Django >= 1.11 -Requires: python-Jinja2 >= 2.8 -Requires: python-Pillow >= 3.2 -Requires: python-Pygments >= 2.0 -Requires: python-autopep8 >= 1.2.1 -Requires: python-gprof2dot >= 2017.09.19 -Requires: python-python-dateutil >= 2.4 -Requires: python-pytz > 2014.2 -Requires: python-requests >= 2.10 -Requires: python-sqlparse >= 0.1.19 -BuildArch: noarch - %python_subpackages %description ++++++ django-silk-3.0.4.tar.gz -> django-silk-4.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/.travis.yml new/django-silk-4.0.0/.travis.yml --- old/django-silk-3.0.4/.travis.yml 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/.travis.yml 2020-01-21 09:09:29.000000000 +0100 @@ -2,60 +2,25 @@ dist: xenial sudo: false python: -- '2.7' -- '3.4' - '3.5' - '3.6' - '3.7' +- '3.8' env: -- DJANGO="Django>=1.11.0,<2.0" DB=postgresql DB_NAME=travis_ci_test -- DJANGO="Django>=2.0,<2.1.0" DB=postgresql DB_NAME=travis_ci_test -- DJANGO="Django>=2.1,<2.2.0" DB=postgresql DB_NAME=travis_ci_test - DJANGO="Django>=2.2,<2.3.0" DB=postgresql DB_NAME=travis_ci_test -- DJANGO="Django>=1.11.0,<2.0" DB=sqlite3 DB_NAME=db.sqlite3 -- DJANGO="Django>=2.0,<2.1.0" DB=sqlite3 DB_NAME=db.sqlite3 -- DJANGO="Django>=2.1,<2.2.0" DB=sqlite3 DB_NAME=db.sqlite3 +- DJANGO="Django>=3.0" DB=postgresql DB_NAME=travis_ci_test - DJANGO="Django>=2.2,<2.3.0" DB=sqlite3 DB_NAME=db.sqlite3 -- DJANGO="Django>=1.11.0,<2.0" DB=mysql DB_NAME=mysql_db -- DJANGO="Django>=2.0,<2.1.0" DB=mysql DB_NAME=mysql_db -- DJANGO="Django>=2.1,<2.2.0" DB=mysql DB_NAME=mysql_db +- DJANGO="Django>=3.0" DB=sqlite3 DB_NAME=db.sqlite3 - DJANGO="Django>=2.2,<2.3.0" DB=mysql DB_NAME=mysql_db +- DJANGO="Django>=3.0" DB=mysql DB_NAME=mysql_db matrix: exclude: - - python: '2.7' - env: DJANGO="Django>=2.0,<2.1.0" DB=postgresql DB_NAME=travis_ci_test - - python: '2.7' - env: DJANGO="Django>=2.1,<2.2.0" DB=postgresql DB_NAME=travis_ci_test - - python: '2.7' - env: DJANGO="Django>=2.2,<2.3.0" DB=postgresql DB_NAME=travis_ci_test - - python: '2.7' - env: DJANGO="Django>=2.0,<2.1.0" DB=sqlite3 DB_NAME=db.sqlite3 - - python: '2.7' - env: DJANGO="Django>=2.1,<2.2.0" DB=sqlite3 DB_NAME=db.sqlite3 - - python: '2.7' - env: DJANGO="Django>=2.2,<2.3.0" DB=sqlite3 DB_NAME=db.sqlite3 - - python: '2.7' - env: DJANGO="Django>=2.0,<2.1.0" DB=mysql DB_NAME=mysql_db - - python: '2.7' - env: DJANGO="Django>=2.1,<2.2.0" DB=mysql DB_NAME=mysql_db - - python: '2.7' - env: DJANGO="Django>=2.2,<2.3.0" DB=mysql DB_NAME=mysql_db - - python: '3.4' - env: DJANGO="Django>=2.1,<2.2.0" DB=postgresql DB_NAME=travis_ci_test - - python: '3.4' - env: DJANGO="Django>=2.2,<2.3.0" DB=postgresql DB_NAME=travis_ci_test - - python: '3.4' - env: DJANGO="Django>=2.1,<2.2.0" DB=sqlite3 DB_NAME=db.sqlite3 - - python: '3.4' - env: DJANGO="Django>=2.2,<2.3.0" DB=sqlite3 DB_NAME=db.sqlite3 - - python: '3.4' - env: DJANGO="Django>=1.11.0,<2.0" DB=mysql DB_NAME=mysql_db - - python: '3.4' - env: DJANGO="Django>=2.0,<2.1.0" DB=mysql DB_NAME=mysql_db - - python: '3.4' - env: DJANGO="Django>=2.1,<2.2.0" DB=mysql DB_NAME=mysql_db - - python: '3.4' - env: DJANGO="Django>=2.2,<2.3.0" DB=mysql DB_NAME=mysql_db + - python: '3.5' + env: DJANGO="Django>=3.0" DB=postgresql DB_NAME=travis_ci_test + - python: '3.5' + env: DJANGO="Django>=3.0" DB=sqlite3 DB_NAME=db.sqlite3 + - python: '3.5' + env: DJANGO="Django>=3.0" DB=mysql DB_NAME=mysql_db fast_finish: true services: - mysql diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/CHANGELOG.md new/django-silk-4.0.0/CHANGELOG.md --- old/django-silk-3.0.4/CHANGELOG.md 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/CHANGELOG.md 2020-01-21 09:09:29.000000000 +0100 @@ -1,5 +1,43 @@ # Change Log + +## [4.0.0](https://github.com/jazzband/django-silk/tree/4.0.0) (2020-01-09) + +[Full Changelog](https://github.com/jazzband/django-silk/compare/3.0.4...4.0.0) + +**New features/Implemented enhancements:** + +- Ability to clean up all requests/queries [\#368](https://github.com/jazzband/django-silk/pull/368) ([nasirhjafri](https://github.com/nasirhjafri)) +- Used bulk_create to save number of queries [\#370](https://github.com/jazzband/django-silk/pull/370) ([nasirhjafri](https://github.com/nasirhjafri)) +- Dropped Python 2 and 3.4 support [\#380](https://github.com/jazzband/django-silk/pull/380) ([munza](https://github.com/munza)) +- Added Python 3.8 support [\#380](https://github.com/jazzband/django-silk/pull/380) ([nasirhjafri](https://github.com/nasirhjafri)) +- Removed django<2.2 support and added django 3.0 support [\#385](https://github.com/jazzband/django-silk/pull/385) ([nasirhjafri](https://github.com/nasirhjafri)) +- Add function support for enabling profiling [\#391](https://github.com/jazzband/django-silk/pull/391) ([tredzko](https://github.com/tredzko)) + +**Fixed bugs:** + +- Mask authorization header [\#376](https://github.com/jazzband/django-silk/pull/376) ([StefanMich](https://github.com/StefanMich)) + +**Closed issues:** + +- Ability to clean up all requests/queries [\#365](https://github.com/jazzband/django-silk/issues/365) +- Use bulk_create to save number of queries [\#369](https://github.com/jazzband/django-silk/issues/369) +- Headers are not sanitized [\#375](https://github.com/jazzband/django-silk/issues/375) +- Django 3 support [\#382](https://github.com/jazzband/django-silk/issues/382) +- Support functional cProfile enable [\#390](https://github.com/jazzband/django-silk/issues/390) + + +**Merged pull requests:** + +- Mask authorization header [\#376](https://github.com/jazzband/django-silk/pull/376) ([StefanMich](https://github.com/StefanMich)) +- Ability to clean up all requests/queries [\#368](https://github.com/jazzband/django-silk/pull/368) ([nasirhjafri](https://github.com/nasirhjafri)) +- Used bulk_create to save number of queries [\#370](https://github.com/jazzband/django-silk/pull/370) ([nasirhjafri](https://github.com/nasirhjafri)) +- Dropped Python 2 and 3.4 support [\#380](https://github.com/jazzband/django-silk/pull/380) ([munza](https://github.com/munza)) +- Added Python 3.8 support [\#380](https://github.com/jazzband/django-silk/pull/380) ([nasirhjafri](https://github.com/nasirhjafri)) +- Removed django<2.2 support and added django 3.0 support [\#385](https://github.com/jazzband/django-silk/pull/385) ([nasirhjafri](https://github.com/nasirhjafri)) +- Add function support for enabling profiling [\#391](https://github.com/jazzband/django-silk/pull/391) ([tredzko](https://github.com/tredzko)) + + ## [3.0.4](https://github.com/jazzband/django-silk/tree/3.0.4) (2019-08-12) [Full Changelog](https://github.com/jazzband/django-silk/compare/3.0.2...3.0.4) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/README.md new/django-silk-4.0.0/README.md --- old/django-silk-3.0.4/README.md 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/README.md 2020-01-21 09:09:29.000000000 +0100 @@ -1,9 +1,5 @@ # Silk -==== - -*Silk has now moved to the [Jazzband](https://jazzband.co/) organization and is looking for contributors - if you think you can help out, please get in touch!* - [](https://travis-ci.org/jazzband/django-silk) [](https://pypi.python.org/pypi/django-silk) [](https://pypi.python.org/pypi/django-silk) @@ -34,8 +30,8 @@ Silk has been tested with: -* Django: 1.11, 2.0, 2.1, 2.2 -* Python: 2.7, 3.4, 3.5, 3.6, 3.7 +* Django: 2.2, 3.0 +* Python: 3.5, 3.6, 3.7, 3.8 ## Installation @@ -356,6 +352,26 @@ it has already been imported, no profiling would be triggered. +#### Custom Logic for Profiling + +Sometimes you may want to dynamically control when the profiler runs. You can write your own logic for when to enable the profiler. To do this add the following to your `settings.py`: + +This setting is mutually exclusive with SILKY_PYTHON_PROFILER and will be used over it if present. It will work with SILKY_DYNAMIC_PROFILING. + +```python +def my_custom_logic(request): + return 'profile_requests' in request.session + +SILKY_PYTHON_PROFILER_FUNC = my_custom_logic # profile only session has recording enabled. +``` + +You can also use a `lambda`. + +```python +# profile only session has recording enabled. +SILKY_PYTHON_PROFILER_FUNC = lambda request: 'profile_requests' in request.session +``` + ### Code Generation Silk currently generates two bits of code per request: @@ -422,7 +438,7 @@ ### Recording a Fraction of Requests -On high-load sites it may be helpful to only record a fraction of the requests that are made.To do this add the following to your `settings.py`: +On high-load sites it may be helpful to only record a fraction of the requests that are made. To do this add the following to your `settings.py`: Note: This setting is mutually exclusive with SILKY_INTERCEPT_FUNC. @@ -432,7 +448,7 @@ #### Custom Logic for Recording Requests -On high-load sites it may also be helpful to write your own logic for when to intercept requests.To do this add the following to your `settings.py`: +On high-load sites it may also be helpful to write your own logic for when to intercept requests. To do this add the following to your `settings.py`: Note: This setting is mutually exclusive with SILKY_INTERCEPT_PERCENT. @@ -508,6 +524,12 @@ For other combinations, check [`.travis.yml`](./.travis.yml). +Now from the root of the sample `project` apply the migrations + +```bash +python manage.py migrate +``` + Now from the root of the sample `project` directory start the django server ```bash diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/docs/index.rst new/django-silk-4.0.0/docs/index.rst --- old/django-silk-3.0.4/docs/index.rst 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/docs/index.rst 2020-01-21 09:09:29.000000000 +0100 @@ -57,5 +57,5 @@ Requirements ------------ -* Django: 1.11, 2.0, 2.1, 2.2 -* Python: 2.7, 3.4, 3.5, 3.6, 3.7 +* Django: 2.2, 3.0 +* Python: 3.5, 3.6, 3.7, 3.8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/package.json new/django-silk-4.0.0/package.json --- old/django-silk-3.0.4/package.json 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/package.json 2020-01-21 09:09:29.000000000 +0100 @@ -1,6 +1,6 @@ { "name": "silk", - "version": "3.0.4", + "version": "4.0.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-3.0.4/project/test-requirements.txt new/django-silk-4.0.0/project/test-requirements.txt --- old/django-silk-3.0.4/project/test-requirements.txt 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/project/test-requirements.txt 2020-01-21 09:09:29.000000000 +0100 @@ -1,5 +1,4 @@ Pygments>=2.0,<2.1 -six==1.11.0 simplejson==3.13.2 python-dateutil>=2.4,<2.5 requests==2.21.0 @@ -10,7 +9,7 @@ mock==2.0.0 Pillow==5.4.1 factory-boy==2.9.2 -freezegun==0.3.11 +freezegun==0.3.12 networkx==1.11 pydotplus==2.0.2 contextlib2==0.5.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/project/tests/test_dynamic_profiling.py new/django-silk-4.0.0/project/tests/test_dynamic_profiling.py --- old/django-silk-3.0.4/project/tests/test_dynamic_profiling.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/project/tests/test_dynamic_profiling.py 2020-01-21 09:09:29.000000000 +0100 @@ -1,6 +1,5 @@ from django.test import TestCase from mock import patch -import six import silk from silk.profiling.dynamic import _get_module, _get_parent_module, profile_function_or_method @@ -73,12 +72,12 @@ 'dynamic': True, 'file_path': source_file_name(), 'name': 'test', - 'line_num': six.get_function_code(foo).co_firstlineno + 'line_num': foo.__code__.co_firstlineno }, call_args) def test_func_as_str(self): name = foo.__name__ - line_num = six.get_function_code(foo).co_firstlineno + line_num = foo.__code__.co_firstlineno profile_function_or_method('tests.test_dynamic_profiling', 'foo', 'test') dc = mock_data_collector() with patch('silk.profiling.profiler.DataCollector', return_value=dc) as mock_DataCollector: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/project/tests/test_profile_dot.py new/django-silk-4.0.0/project/tests/test_profile_dot.py --- old/django-silk-3.0.4/project/tests/test_profile_dot.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/project/tests/test_profile_dot.py 2020-01-21 09:09:29.000000000 +0100 @@ -3,11 +3,8 @@ import cProfile import tempfile from contextlib import contextmanager -from six import PY3 -if PY3: - from unittest.mock import MagicMock -else: - from mock import MagicMock +from unittest.mock import MagicMock + # 3rd party from django.test import TestCase from networkx.drawing.nx_pydot import read_dot @@ -80,8 +77,7 @@ # create dot with tempfile.NamedTemporaryFile(delete=False) as dotfile: dot = _create_dot(self._profile(), 5) - dot = dot.encode('utf-8') if PY3 else dot - dotfile.write(dot) + dotfile.write(dot.encode('utf-8')) # verify generated dot is valid G = read_dot(dotfile.name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/project/tests/test_profile_parser.py new/django-silk-4.0.0/project/tests/test_profile_parser.py --- old/django-silk-3.0.4/project/tests/test_profile_parser.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/project/tests/test_profile_parser.py 2020-01-21 09:09:29.000000000 +0100 @@ -2,10 +2,9 @@ from __future__ import print_function # std import cProfile -import sys +from io import StringIO # 3rd party import contextlib2 as contextlib -from six import StringIO, PY3 from django.test import TestCase # silk from silk.utils.profile_parser import parse_profile @@ -22,33 +21,11 @@ cProfile.run('print()') stream.seek(0) actual = list(parse_profile(stream)) - if PY3: - if sys.version_info < (3,5): - expected = [ - ['ncalls', 'tottime', 'percall', 'cumtime', 'percall', 'filename:lineno(function)'], - ['1', '0.000', '0.000', '0.000', '0.000', '<string>:1(<module>)'], - ['1', '0.000', '0.000', '0.000', '0.000', '{built-in method exec}'], - ['1', '0.000', '0.000', '0.000', '0.000', '{built-in method print}'], - ['1', '0.000', '0.000', '0.000', '0.000', "{method 'disable' of '_lsprof.Profiler' objects}"], - ] - else: - expected = [ - ['ncalls', 'tottime', 'percall', 'cumtime', 'percall', 'filename:lineno(function)'], - ['1', '0.000', '0.000', '0.000', '0.000', '<string>:1(<module>)'], - ['1', '0.000', '0.000', '0.000', '0.000', '{built-in method builtins.exec}'], - ['1', '0.000', '0.000', '0.000', '0.000', '{built-in method builtins.print}'], - ['1', '0.000', '0.000', '0.000', '0.000', "{method 'disable' of '_lsprof.Profiler' objects}"], - ] - else: - expected = [ - ['ncalls', 'tottime', 'percall', 'cumtime', 'percall', 'filename:lineno(function)'], - ['1', '0.000', '0.000', '0.000', '0.000', '<string>:1(<module>)'], - ['2', '0.000', '0.000', '0.000', '0.000', 'StringIO.py:208(write)'], - ['2', '0.000', '0.000', '0.000', '0.000', 'StringIO.py:38(_complain_ifclosed)'], - ['2', '0.000', '0.000', '0.000', '0.000', '{isinstance}'], - ['2', '0.000', '0.000', '0.000', '0.000', '{len}'], - ['2', '0.000', '0.000', '0.000', '0.000', "{method 'append' of 'list' objects}"], - ['1', '0.000', '0.000', '0.000', '0.000', "{method 'disable' of '_lsprof.Profiler' objects}"] - ] - - self.assertListEqual(actual, expected) \ No newline at end of file + expected = [ + ['ncalls', 'tottime', 'percall', 'cumtime', 'percall', 'filename:lineno(function)'], + ['1', '0.000', '0.000', '0.000', '0.000', '<string>:1(<module>)'], + ['1', '0.000', '0.000', '0.000', '0.000', '{built-in method builtins.exec}'], + ['1', '0.000', '0.000', '0.000', '0.000', '{built-in method builtins.print}'], + ['1', '0.000', '0.000', '0.000', '0.000', "{method 'disable' of '_lsprof.Profiler' objects}"] + ] + self.assertListEqual(actual, expected) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/project/tests/test_sensitive_data_in_request.py new/django-silk-4.0.0/project/tests/test_sensitive_data_in_request.py --- old/django-silk-3.0.4/project/tests/test_sensitive_data_in_request.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/project/tests/test_sensitive_data_in_request.py 2020-01-21 09:09:29.000000000 +0100 @@ -11,6 +11,76 @@ HTTP_CONTENT_TYPE = 'Content-Type' +class MaskCredentialsInFormsTest(TestCase): + def _mask(self, value): + return RequestModelFactory(None)._mask_credentials(value) + + def test_mask_credentials_preserves_single_insensitive_values(self): + self.assertIn("public", self._mask("foo=public")) + + def test_mask_credentials_preserves_insensitive_values_between_sensitive_values(self): + self.assertIn("public", self._mask("password=1&foo=public&secret=2")) + + def test_mask_credentials_masks_sensitive_values(self): + self.assertNotIn("secret", self._mask("password=secret")) + + def test_mask_credentials_masks_sensitive_values_between_insensitive_values(self): + self.assertNotIn("secret", self._mask("public1=foo&password=secret&public2=bar")) + + def test_mask_credentials_is_case_insensitive(self): + self.assertNotIn("secret", self._mask("UsErNaMe=secret")) + + def test_mask_credentials_handles_prefixes(self): + self.assertNotIn("secret", self._mask("prefixed-username=secret")) + + def test_mask_credentials_handles_suffixes(self): + self.assertNotIn("secret", self._mask("username-with-suffix=secret")) + + def test_mask_credentials_handles_complex_cases(self): + self.assertNotIn("secret", self._mask("foo=public&prefixed-uSeRname-with-suffix=secret&bar=public")) + + +class MaskCredentialsInJsonTest(TestCase): + def _mask(self, value): + return RequestModelFactory(None)._mask_credentials(json.dumps(value)) + + def test_mask_credentials_preserves_single_insensitive_values(self): + self.assertIn("public", self._mask({"foo": "public"})) + + def test_mask_credentials_preserves_insensitive_values_in_presence_of_sensitive(self): + self.assertIn("public", self._mask({"password": "secret", "foo": "public"})) + + def test_mask_credentials_masks_sensitive_values(self): + self.assertNotIn("secret", self._mask({"password": "secret"})) + + def test_mask_credentials_masks_sensitive_values_in_presence_of_regular(self): + self.assertNotIn("secret", self._mask({"foo": "public", "password": "secret"})) + + def test_mask_credentials_is_case_insensitive(self): + self.assertNotIn("secret", self._mask({"UsErNaMe": "secret"})) + + def test_mask_credentials_handles_prefixes(self): + self.assertNotIn("secret", self._mask({"prefixed-username": "secret"})) + + def test_mask_credentials_handles_suffixes(self): + self.assertNotIn("secret", self._mask({"username-with-suffix": "secret"})) + + def test_mask_credentials_handles_complex_cases(self): + self.assertNotIn("secret", self._mask({ + "foo": "public", + "prefixed-uSeRname-with-suffix": "secret" + })) + + def test_mask_credentials_in_nested_data_structures(self): + self.assertNotIn("secret", self._mask({ + "foo": "public", + "nested": { + "prefixed-uSeRname-with-suffix": "secret", + }, + })) + + + class TestEncodingForRequests(TestCase): """ Check that the RequestModelFactory masks sensitive data @@ -33,7 +103,8 @@ def test_password_in_json(self): mock_request = Mock() mock_request.META = {DJANGO_META_CONTENT_TYPE: 'application/json; charset=UTF-8'} - d = {'x': 'testunmasked', 'username': 'test_username', 'password': 'testpassword'} + d = {'x': 'testunmasked', 'username': 'test_username', 'password': 'testpassword', + 'prefixed-secret': 'testsecret'} mock_request.body = json.dumps(d) mock_request.get = mock_request.META.get factory = RequestModelFactory(mock_request) @@ -41,12 +112,15 @@ self.assertIn('testunmasked', raw_body) self.assertNotIn('test_username', raw_body) self.assertNotIn('testpassword', raw_body) + self.assertNotIn('testsecret', raw_body) self.assertNotIn('test_username', body) self.assertNotIn('testpassword', body) + self.assertNotIn('testsecret', body) for datum in [json.loads(body), json.loads(raw_body)]: self.assertEqual(datum['username'], RequestModelFactory.CLEANSED_SUBSTITUTE) self.assertEqual(datum['password'], RequestModelFactory.CLEANSED_SUBSTITUTE) + self.assertEqual(datum['prefixed-secret'], RequestModelFactory.CLEANSED_SUBSTITUTE) self.assertEqual(datum['x'], 'testunmasked') def test_password_in_batched_json(self): @@ -73,3 +147,15 @@ self.assertEqual(datum['username'], RequestModelFactory.CLEANSED_SUBSTITUTE) self.assertEqual(datum['password'], RequestModelFactory.CLEANSED_SUBSTITUTE) self.assertEqual(datum['x'], 'testunmasked') + + def test_authorization_header(self): + mock_request = Mock() + mock_request.META = {'HTTP_AUTHORIZATION': 'secret'} + mock_request.body = '' + mock_request.get = mock_request.META.get + factory = RequestModelFactory(mock_request) + headers = factory.encoded_headers() + json_headers = json.loads(headers) + + self.assertIn('AUTHORIZATION', json_headers) + self.assertEqual(json_headers['AUTHORIZATION'], RequestModelFactory.CLEANSED_SUBSTITUTE) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/requirements.txt new/django-silk-4.0.0/requirements.txt --- old/django-silk-3.0.4/requirements.txt 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/requirements.txt 2020-01-21 09:09:29.000000000 +0100 @@ -7,7 +7,7 @@ pytz>2014.2 mock>=1.0.1 Pillow>=3.2 -Django>=1.11 +Django>=2.2 freezegun>=0.3 factory-boy>=2.8.1 gprof2dot>=2017.09.19 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/setup.py new/django-silk-4.0.0/setup.py --- old/django-silk-3.0.4/setup.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/setup.py 2020-01-21 09:09:29.000000000 +0100 @@ -3,25 +3,14 @@ import os from setuptools import setup -try: - from pypandoc import convert - - def read_md(f): - return convert(f, 'rst') -except ImportError: - print("warning: pypandoc module not found, could not convert Markdown to RST") - - def read_md(f): - return open(f, 'r').read() - -README = read_md('README.md') - # allow setup.py to be run from any path os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) +README = open('README.md', 'rb').read().decode("UTF-8") + setup( name='django-silk', - version='3.0.4', + version='4.0.0', packages=['silk'], include_package_data=True, license='MIT License', @@ -35,25 +24,20 @@ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', - 'Framework :: Django :: 1.11', - 'Framework :: Django :: 2.0', - 'Framework :: Django :: 2.1', 'Framework :: Django :: 2.2', + 'Framework :: Django :: 3.0', 'Intended Audience :: Developers', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', ], install_requires=[ - 'Django>=1.11', + 'Django>=2.2', 'Pygments', 'python-dateutil', 'requests', @@ -62,5 +46,6 @@ 'autopep8', 'pytz', 'gprof2dot>=2017.09.19', - ] + ], + python_requires='>=3.5' ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/auth.py new/django-silk-4.0.0/silk/auth.py --- old/django-silk-3.0.4/silk/auth.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/auth.py 2020-01-21 09:09:29.000000000 +0100 @@ -1,8 +1,7 @@ -from functools import wraps +from functools import wraps, WRAPPER_ASSIGNMENTS from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied -from django.utils.decorators import available_attrs from silk.config import SilkyConfig @@ -26,7 +25,7 @@ def user_passes_test(test_func): def decorator(view_func): - @wraps(view_func, assigned=available_attrs(view_func)) + @wraps(view_func, assigned=WRAPPER_ASSIGNMENTS) def _wrapped_view(request, *args, **kwargs): if test_func(request.user): return view_func(request, *args, **kwargs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/code_generation/curl.py new/django-silk-4.0.0/silk/code_generation/curl.py --- old/django-silk-3.0.4/silk/code_generation/curl.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/code_generation/curl.py 2020-01-21 09:09:29.000000000 +0100 @@ -1,7 +1,7 @@ import json # noinspection PyUnresolvedReferences -from django.utils.six.moves.urllib.parse import urlencode +from urllib.parse import urlencode import jinja2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/code_generation/django_test_client.py new/django-silk-4.0.0/silk/code_generation/django_test_client.py --- old/django-silk-3.0.4/silk/code_generation/django_test_client.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/code_generation/django_test_client.py 2020-01-21 09:09:29.000000000 +0100 @@ -2,7 +2,7 @@ import jinja2 # noinspection PyUnresolvedReferences -from django.utils.six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from silk.profiling.dynamic import is_str_typ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/collector.py new/django-silk-4.0.0/silk/collector.py --- old/django-silk-3.0.4/silk/collector.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/collector.py 2020-01-21 09:09:29.000000000 +0100 @@ -4,7 +4,7 @@ import pstats import logging -from django.utils.six import StringIO, with_metaclass +from io import StringIO from silk import models from silk.config import SilkyConfig @@ -26,7 +26,7 @@ 'these methods, Silk will not have the chance to inspect the request/response objects.') -class DataCollector(with_metaclass(Singleton, object)): +class DataCollector(metaclass=Singleton): """ Provides the ability to save all models at the end of the request. We cannot save during the request due to the possibility of atomic blocks @@ -92,7 +92,12 @@ self.request = request self._configure() - if silky_config.SILKY_PYTHON_PROFILER: + if silky_config.SILKY_PYTHON_PROFILER_FUNC: + should_profile = silky_config.SILKY_PYTHON_PROFILER_FUNC(request) + else: + should_profile = silky_config.SILKY_PYTHON_PROFILER + + if should_profile: self.local.pythonprofiler = cProfile.Profile() self.local.pythonprofiler.enable() @@ -154,10 +159,20 @@ self.request.prof_file = f.name self.request.save() - for _, query in self.queries.items(): - query_model = models.SQLQuery.objects.create(**query) - query['model'] = query_model - for _, profile in self.profiles.items(): + sql_queries = [] + for identifier, query in self.queries.items(): + query['identifier'] = identifier + sql_query = models.SQLQuery(**query) + sql_queries += [sql_query] + + models.SQLQuery.objects.bulk_create(sql_queries) + sql_queries = models.SQLQuery.objects.filter(request=self.request) + for sql_query in sql_queries.all(): + query = self.queries.get(sql_query.identifier) + if query: + query['model'] = sql_query + + for profile in self.profiles.values(): profile_query_models = [] if TYP_QUERIES in profile: profile_queries = profile[TYP_QUERIES] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/config.py new/django-silk-4.0.0/silk/config.py --- old/django-silk-3.0.4/silk/config.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/config.py 2020-01-21 09:09:29.000000000 +0100 @@ -1,7 +1,5 @@ from copy import copy -from django.utils import six - from silk.singleton import Singleton @@ -11,7 +9,7 @@ return False -class SilkyConfig(six.with_metaclass(Singleton, object)): +class SilkyConfig(metaclass=Singleton): defaults = { 'SILKY_DYNAMIC_PROFILING': [], 'SILKY_IGNORE_PATHS': [], @@ -28,6 +26,7 @@ 'SILKY_INTERCEPT_PERCENT': 100, 'SILKY_INTERCEPT_FUNC': None, 'SILKY_PYTHON_PROFILER': False, + 'SILKY_PYTHON_PROFILER_FUNC': None, 'SILKY_STORAGE_CLASS': 'silk.storage.ProfilerResultStorage', 'SILKY_MIDDLEWARE_CLASS': 'silk.middleware.SilkyMiddleware' } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/migrations/0007_sqlquery_identifier.py new/django-silk-4.0.0/silk/migrations/0007_sqlquery_identifier.py --- old/django-silk-3.0.4/silk/migrations/0007_sqlquery_identifier.py 1970-01-01 01:00:00.000000000 +0100 +++ new/django-silk-4.0.0/silk/migrations/0007_sqlquery_identifier.py 2020-01-21 09:09:29.000000000 +0100 @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2019-10-26 12:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('silk', '0006_fix_request_prof_file_blank'), + ] + + operations = [ + migrations.AddField( + model_name='sqlquery', + name='identifier', + field=models.IntegerField(default=-1), + ), + ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/model_factory.py new/django-silk-4.0.0/silk/model_factory.py --- old/django-silk-3.0.4/silk/model_factory.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/model_factory.py 2020-01-21 09:09:29.000000000 +0100 @@ -74,12 +74,18 @@ to the name. So, for example, a header called X-Bender would be mapped to the META key HTTP_X_BENDER." """ headers = {} + sensitive_headers = {'AUTHORIZATION'} + for k, v in self.request.META.items(): if k.startswith('HTTP') or k in ('CONTENT_TYPE', 'CONTENT_LENGTH'): splt = k.split('_') if splt[0] == 'HTTP': splt = splt[1:] k = '-'.join(splt) + + if k in sensitive_headers: + v = RequestModelFactory.CLEANSED_SUBSTITUTE + headers[k] = v if SilkyConfig().SILKY_HIDE_COOKIES: try: @@ -97,14 +103,17 @@ key_string = '|'.join(sensitive_keys) def replace_pattern_values(obj): + pattern = re.compile(key_string, re.I) if isinstance(obj, dict): - for key in set(obj.keys()) & sensitive_keys: - obj[key] = RequestModelFactory.CLEANSED_SUBSTITUTE + for key in obj.keys(): + if pattern.search(key): + obj[key] = RequestModelFactory.CLEANSED_SUBSTITUTE + else: + obj[key] = replace_pattern_values(obj[key]) elif isinstance(obj, list): for index, item in enumerate(obj): obj[index] = replace_pattern_values(item) else: - pattern = re.compile(r'{}'.format(key_string), re.I) if pattern.search(str(obj)): return RequestModelFactory.CLEANSED_SUBSTITUTE return obj @@ -112,7 +121,7 @@ try: json_body = json.loads(body) except Exception as e: - pattern = re.compile(r'({})=(.*?)(&|$)'.format(key_string), re.M) + pattern = re.compile(r'({})[^=]*=(.*?)(&|$)'.format(key_string), re.M | re.I) try: results = re.findall(pattern, body) except Exception: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/models.py new/django-silk-4.0.0/silk/models.py --- old/django-silk-3.0.4/silk/models.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/models.py 2020-01-21 09:09:29.000000000 +0100 @@ -216,6 +216,7 @@ # 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""" @@ -223,18 +224,10 @@ objs = args[0] else: objs = kwargs.get('objs') + for obj in objs: + obj.prepare_save() - with transaction.atomic(): - request_counter = Counter([x.request_id for x in objs]) - requests = Request.objects.filter(pk__in=request_counter.keys()) - # TODO: Not that there is ever more than one request (but there could be eventually) - # but perhaps there is a cleaner way of apply the increment from the counter without iterating - # and saving individually? e.g. bulk update but with diff. increments. Couldn't come up with this - # off hand. - for r in requests: - r.num_sql_queries = F('num_sql_queries') + request_counter[r.pk] - r.save() - return super(SQLQueryManager, self).bulk_create(*args, **kwargs) + return super(SQLQueryManager, self).bulk_create(*args, **kwargs) class SQLQuery(models.Model): @@ -242,6 +235,7 @@ start_time = DateTimeField(null=True, blank=True, default=timezone.now) end_time = DateTimeField(null=True, blank=True) time_taken = FloatField(blank=True, null=True) + identifier = IntegerField(default=-1) request = ForeignKey( Request, related_name='queries', null=True, blank=True, db_index=True, on_delete=models.CASCADE, @@ -289,9 +283,7 @@ pass return tables - @transaction.atomic() - def save(self, *args, **kwargs): - + def prepare_save(self): if self.end_time and self.start_time: interval = self.end_time - self.start_time self.time_taken = interval.total_seconds() * 1000 @@ -301,6 +293,9 @@ 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(SQLQuery, self).save(*args, **kwargs) @transaction.atomic() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/profiling/dynamic.py new/django-silk-4.0.0/silk/profiling/dynamic.py --- old/django-silk-3.0.4/silk/profiling/dynamic.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/profiling/dynamic.py 2020-01-21 09:09:29.000000000 +0100 @@ -4,8 +4,6 @@ import sys import re -from django.utils import six - from silk.profiling.profiler import silk_profile Logger = logging.getLogger('silk.profiling.dynamic') @@ -53,7 +51,7 @@ @param module: module object or module name in form 'path.to.module' @param func: function object or function name in form 'foo' or 'Class.method' """ - if isinstance(module, six.string_types) or isinstance(module, six.text_type): + if isinstance(module, str): module = _get_module(module) decorator = silk_profile(name, _dynamic=True) func_name = func @@ -144,12 +142,12 @@ # could be. # # relevant: http://stackoverflow.com/questions/2749655/why-are-closures-broken-within-exec - globals = six.get_function_globals(func) + globals = func.__globals__ locals = calling_frame.f_locals combined = globals.copy() combined.update(locals) Logger.debug('New src_str:\n %s' % src_str) - six.exec_(src_str, combined, context) + exec(src_str, combined, context) return context[func.__name__] @@ -199,8 +197,7 @@ def is_str_typ(o): - return any(map(partial(isinstance, o), six.string_types)) \ - or isinstance(o, six.text_type) + return isinstance(o, str) def inject_context_manager_func(module, func, start_line, end_line, name): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/profiling/profiler.py new/django-silk-4.0.0/silk/profiling/profiler.py --- old/django-silk-3.0.4/silk/profiling/profiler.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/profiling/profiler.py 2020-01-21 09:09:29.000000000 +0100 @@ -6,8 +6,6 @@ from django.conf import settings from django.utils import timezone -from django.utils import six - from silk.collector import DataCollector from silk.config import SilkyConfig from silk.models import _time_taken @@ -138,7 +136,7 @@ def wrapped_target(*args, **kwargs): with silk_meta_profiler(): try: - func_code = six.get_function_code(target) + func_code = target.__code__ except AttributeError: raise NotImplementedError('Profile not implemented to decorate type %s' % target.__class__.__name__) line_num = func_code.co_firstlineno diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/singleton.py new/django-silk-4.0.0/silk/singleton.py --- old/django-silk-3.0.4/silk/singleton.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/singleton.py 2020-01-21 09:09:29.000000000 +0100 @@ -1,7 +1,7 @@ __author__ = 'mtford' -class Singleton(type): +class Singleton(type, metaclass=object): def __init__(cls, name, bases, d): super(Singleton, cls).__init__(name, bases, d) cls.instance = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/templates/silk/clear_db.html new/django-silk-4.0.0/silk/templates/silk/clear_db.html --- old/django-silk-3.0.4/silk/templates/silk/clear_db.html 1970-01-01 01:00:00.000000000 +0100 +++ new/django-silk-4.0.0/silk/templates/silk/clear_db.html 2020-01-21 09:09:29.000000000 +0100 @@ -0,0 +1,96 @@ +{% extends 'silk/base/root_base.html' %} +{% load static %} +{% load silk_inclusion %} + +{% block menu %} + {% if silk_request %} + {% request_menu request silk_request %} + {% else %} + {% root_menu request %} + {% endif %} +{% endblock %} + +{% block style %} + {{ block.super }} + <style> + .container { + padding: 0 1em; + } + + h2 { + margin-bottom: 10px; + } + + .cleardb-form .cleardb-form-wrapper{ + margin-bottom: 20px; + } + + .cleardb-form label { + display: block; + margin-bottom: 8px; + } + + .cleardb-form .btn { + background: #333344; + color: #fff; + padding: 10px 20px; + border-radius: 2px; + cursor: pointer; + box-shadow: none; + font-size: 16px; + line-height: 20px; + border: 0; + min-width: 150px; + text-align: center; + } + .cleardb-form label :last-child { + margin-bottom: 0; + } + .msg { + margin-top: 20px; + color: #bac54b; + } + </style> + +{% endblock %} + +{% block js %} + {{ block.super }} + <script> + + $(document).ready(function () { + initFilters(); + initFilterButton(); + }); + </script> +{% endblock %} + + +{% block data %} + <div class="container"> + <h2>Silk Clear DB</h2> + <form class="cleardb-form" action="." method="post"> + {% csrf_token %} + <div class="cleardb-form-wrapper"> + <label> + <input type="checkbox" name="clear_requests" disabled="disabled"/> + Requests + </label> + <label> + <input type="checkbox" name="clear_profiling" disabled="disabled"/> + Profiling + </label> + <label> + <input type="checkbox" name="clear_all"/> + All + </label> + </div> + <button class="btn">Clear</button> + </form> + <div class="msg">{{ msg }}</div> + </div> +{% endblock %} + + +{% block filter %}{% endblock %} +{% block filters %}{% endblock %} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/templates/silk/inclusion/root_menu.html new/django-silk-4.0.0/silk/templates/silk/inclusion/root_menu.html --- old/django-silk-3.0.4/silk/templates/silk/inclusion/root_menu.html 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/templates/silk/inclusion/root_menu.html 2020-01-21 09:09:29.000000000 +0100 @@ -20,4 +20,11 @@ </div> </a> </div> +<div class="menu-item selectable-menu-item {% navactive request 'silk:cleardb' %}"> + <a href="{% url "silk:cleardb" %}"> + <div class="menu-item-outer"> + <div class="menu-item-inner">Clear DB</div> + </div> + </a> +</div> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/urls.py new/django-silk-4.0.0/silk/urls.py --- old/django-silk-3.0.4/silk/urls.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/urls.py 2020-01-21 09:09:29.000000000 +0100 @@ -1,5 +1,6 @@ from django.conf.urls import url +from silk.views.clear_db import ClearDBView from silk.views.profile_detail import ProfilingDetailView from silk.views.profile_download import ProfileDownloadView from silk.views.profile_dot import ProfileDotView @@ -82,6 +83,7 @@ name='profile_sql_detail' ), url(r'^profiling/$', ProfilingView.as_view(), name='profiling'), + url(r'^cleardb/$', ClearDBView.as_view(), name='cleardb'), url( r'^request/(?P<request_id>[a-zA-Z0-9\-]+)/cprofile/$', CProfileView.as_view(), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/utils/profile_parser.py new/django-silk-4.0.0/silk/utils/profile_parser.py --- old/django-silk-3.0.4/silk/utils/profile_parser.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/utils/profile_parser.py 2020-01-21 09:09:29.000000000 +0100 @@ -1,4 +1,3 @@ -from six import text_type import re @@ -9,7 +8,7 @@ """ Parse the output of cProfile to a list of tuples. """ - if isinstance(output, text_type): + if isinstance(output, str): output = output.split('\n') for i, line in enumerate(output): # ignore n function calls, total time and ordered by and empty lines diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/views/clear_db.py new/django-silk-4.0.0/silk/views/clear_db.py --- old/django-silk-3.0.4/silk/views/clear_db.py 1970-01-01 01:00:00.000000000 +0100 +++ new/django-silk-4.0.0/silk/views/clear_db.py 2020-01-21 09:09:29.000000000 +0100 @@ -0,0 +1,33 @@ +from django.db import connection +from django.shortcuts import render +from django.utils.decorators import method_decorator +from django.views.generic import View + +from silk.auth import login_possibly_required, permissions_possibly_required +from silk.models import Request, Response, SQLQuery, Profile + + +class ClearDBView(View): + + def _truncate_tables(self, models): + raw_query = 'TRUNCATE TABLE {0};' + truncate_query = [raw_query.format(m._meta.db_table) for m in models] + truncate_query = ' '.join(truncate_query) + query = 'SET FOREIGN_KEY_CHECKS = 0; {0} SET FOREIGN_KEY_CHECKS = 1;'.format(truncate_query) + cursor = connection.cursor() + cursor.execute(query) + + @method_decorator(login_possibly_required) + @method_decorator(permissions_possibly_required) + def get(self, request, *_, **kwargs): + return render(request, 'silk/clear_db.html') + + @method_decorator(login_possibly_required) + @method_decorator(permissions_possibly_required) + def post(self, request, *_, **kwargs): + context = {} + if 'clear_all' in request.POST: + self._truncate_tables([Response, SQLQuery, Profile, Request]) + tables = ['Response', 'SQLQuery', 'Profile', 'Request'] + context['msg'] = 'Cleared data for following silk tables: {0}'.format(', '.join(tables)) + return render(request, 'silk/clear_db.html', context=context) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-silk-3.0.4/silk/views/profile_dot.py new/django-silk-4.0.0/silk/views/profile_dot.py --- old/django-silk-3.0.4/silk/views/profile_dot.py 2019-09-05 18:47:21.000000000 +0200 +++ new/django-silk-4.0.0/silk/views/profile_dot.py 2020-01-21 09:09:29.000000000 +0100 @@ -5,7 +5,7 @@ import shutil from contextlib import closing, contextmanager # 3rd party -from six import StringIO +from io import StringIO from django.shortcuts import get_object_or_404 from django.utils.decorators import method_decorator from django.views.generic import View
