From 904f2eb17d637b7b147bb5d9fb2f153f948c4302 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 5 Jun 2023 14:16:13 +0200 Subject: [PATCH 1/4] Activate more ruff rules --- debug_toolbar/forms.py | 4 +- debug_toolbar/panels/headers.py | 2 +- debug_toolbar/panels/history/panel.py | 2 +- debug_toolbar/panels/profiling.py | 4 +- debug_toolbar/panels/sql/forms.py | 4 +- debug_toolbar/panels/sql/panel.py | 2 +- debug_toolbar/panels/templates/panel.py | 4 +- debug_toolbar/settings.py | 1 + debug_toolbar/toolbar.py | 2 +- pyproject.toml | 75 +++++++++++++++++++------ tests/context_processors.py | 2 +- tests/models.py | 9 +++ 12 files changed, 80 insertions(+), 31 deletions(-) diff --git a/debug_toolbar/forms.py b/debug_toolbar/forms.py index 1263c3aff..61444b43c 100644 --- a/debug_toolbar/forms.py +++ b/debug_toolbar/forms.py @@ -38,8 +38,8 @@ def clean_signed(self): signing.Signer(salt=self.salt).unsign(self.cleaned_data["signed"]) ) return verified - except signing.BadSignature: - raise ValidationError("Bad signature") + except signing.BadSignature as exc: + raise ValidationError("Bad signature") from exc def verified_data(self): return self.is_valid() and self.cleaned_data["signed"] diff --git a/debug_toolbar/panels/headers.py b/debug_toolbar/panels/headers.py index ed20d6178..e1ea6da1e 100644 --- a/debug_toolbar/panels/headers.py +++ b/debug_toolbar/panels/headers.py @@ -33,7 +33,7 @@ class HeadersPanel(Panel): template = "debug_toolbar/panels/headers.html" def process_request(self, request): - wsgi_env = list(sorted(request.META.items())) + wsgi_env = sorted(request.META.items()) self.request_headers = { unmangle(k): v for (k, v) in wsgi_env if is_http_header(k) } diff --git a/debug_toolbar/panels/history/panel.py b/debug_toolbar/panels/history/panel.py index 8bd0e8f65..503cade31 100644 --- a/debug_toolbar/panels/history/panel.py +++ b/debug_toolbar/panels/history/panel.py @@ -22,7 +22,7 @@ class HistoryPanel(Panel): def get_headers(self, request): headers = super().get_headers(request) observe_request = self.toolbar.get_observe_request() - store_id = getattr(self.toolbar, "store_id") + store_id = self.toolbar.store_id if store_id and observe_request(request): headers["djdt-store-id"] = store_id return headers diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index ca32b98c2..9947b17dc 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -13,7 +13,7 @@ class FunctionCall: def __init__( - self, statobj, func, depth=0, stats=None, id=0, parent_ids=[], hsv=(0, 0.5, 1) + self, statobj, func, depth=0, stats=None, id=0, parent_ids=None, hsv=(0, 0.5, 1) ): self.statobj = statobj self.func = func @@ -23,7 +23,7 @@ def __init__( self.stats = statobj.stats[func][:4] self.depth = depth self.id = id - self.parent_ids = parent_ids + self.parent_ids = parent_ids or [] self.hsv = hsv def parent_classes(self): diff --git a/debug_toolbar/panels/sql/forms.py b/debug_toolbar/panels/sql/forms.py index 8d2709777..0515c5c8e 100644 --- a/debug_toolbar/panels/sql/forms.py +++ b/debug_toolbar/panels/sql/forms.py @@ -37,8 +37,8 @@ def clean_params(self): try: return json.loads(value) - except ValueError: - raise ValidationError("Is not valid JSON") + except ValueError as exc: + raise ValidationError("Is not valid JSON") from exc def clean_alias(self): value = self.cleaned_data["alias"] diff --git a/debug_toolbar/panels/sql/panel.py b/debug_toolbar/panels/sql/panel.py index 984a2074a..58c1c2738 100644 --- a/debug_toolbar/panels/sql/panel.py +++ b/debug_toolbar/panels/sql/panel.py @@ -90,7 +90,7 @@ def _duplicate_query_key(query): def _process_query_groups(query_groups, databases, colors, name): counts = defaultdict(int) - for (alias, key), query_group in query_groups.items(): + for (alias, _key), query_group in query_groups.items(): count = len(query_group) # Queries are similar / duplicates only if there are at least 2 of them. if count > 1: diff --git a/debug_toolbar/panels/templates/panel.py b/debug_toolbar/panels/templates/panel.py index 35d5b5191..a1e45e83f 100644 --- a/debug_toolbar/panels/templates/panel.py +++ b/debug_toolbar/panels/templates/panel.py @@ -192,9 +192,7 @@ def generate_stats(self, request, response): template = self.templates[0]["template"] # django templates have the 'engine' attribute, while jinja # templates use 'backend' - engine_backend = getattr(template, "engine", None) or getattr( - template, "backend" - ) + engine_backend = getattr(template, "engine", None) or template.backend template_dirs = engine_backend.dirs else: context_processors = None diff --git a/debug_toolbar/settings.py b/debug_toolbar/settings.py index bf534a7da..eb6b59209 100644 --- a/debug_toolbar/settings.py +++ b/debug_toolbar/settings.py @@ -83,6 +83,7 @@ def get_panels(): warnings.warn( f"Please remove {logging_panel} from your DEBUG_TOOLBAR_PANELS setting.", DeprecationWarning, + stacklevel=1, ) return PANELS diff --git a/debug_toolbar/toolbar.py b/debug_toolbar/toolbar.py index 31010f47f..11f8a1daa 100644 --- a/debug_toolbar/toolbar.py +++ b/debug_toolbar/toolbar.py @@ -86,7 +86,7 @@ def render_toolbar(self): "The debug toolbar requires the staticfiles contrib app. " "Add 'django.contrib.staticfiles' to INSTALLED_APPS and " "define STATIC_URL in your settings." - ) + ) from None else: raise diff --git a/pyproject.toml b/pyproject.toml index ff8c22874..e04e3a42d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,24 +66,65 @@ source = ["src", ".tox/*/site-packages"] fail_under = 94 show_missing = true -[tool.ruff.isort] -combine-as-imports = true - [tool.ruff] -select = [ - # flake8/Pyflakes - "F", - # flake8/pycodestyle - "E", - "W", - # isort - "I", - # pyupgrade - "UP", - # pygrep-hooks - "PGH", +extend-select = [ + # pyflakes, pycodestyle + "F", "E", "W", + # mmcabe + # "C90", + # isort + "I", + # pep8-naming + # "N", + # pyupgrade + "UP", + # flake8-2020 + # "YTT", + # flake8-boolean-trap + # "FBT", + # flake8-bugbear + "B", + # flake8-builtins + # "A", + # flake8-comprehensions + "C4", + # flake8-django + "DJ", + # flake8-logging-format + "G", + # flake8-pie + # "PIE", + # flake8-simplify + # "SIM", + # flake8-gettext + "INT", + # pygrep-hooks + "PGH", + # pylint + # "PL", + # unused noqa + "RUF100", ] -ignore = [ - "E501", +extend-ignore = [ + # Allow zip() without strict= + "B905", + # No line length errors + "E501", ] +fix = true +show-fixes = true target-version = "py38" + +[tool.ruff.isort] +combine-as-imports = true + +[tool.ruff.mccabe] +max-complexity = 15 + +[tool.ruff.per-file-ignores] +"*/migrat*/*" = [ + # Allow using PascalCase model names in migrations + "N806", + # Ignore the fact that migration files are invalid module names + "N999", +] diff --git a/tests/context_processors.py b/tests/context_processors.py index 6fe220dba..69e112a39 100644 --- a/tests/context_processors.py +++ b/tests/context_processors.py @@ -1,2 +1,2 @@ def broken(request): - request.non_existing_attribute + _read = request.non_existing_attribute diff --git a/tests/models.py b/tests/models.py index 95696020a..e19bfe59d 100644 --- a/tests/models.py +++ b/tests/models.py @@ -11,13 +11,22 @@ def __repr__(self): class Binary(models.Model): field = models.BinaryField() + def __str__(self): + return "" + class PostgresJSON(models.Model): field = JSONField() + def __str__(self): + return "" + if settings.USE_GIS: from django.contrib.gis.db import models as gismodels class Location(gismodels.Model): point = gismodels.PointField() + + def __str__(self): + return "" From 52dedc4d87d90872ee859e88cb918f82f54f8a5f Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 5 Jun 2023 14:17:58 +0200 Subject: [PATCH 2/4] Use flake8-boolean-trap --- debug_toolbar/panels/sql/utils.py | 2 +- debug_toolbar/panels/templates/panel.py | 2 +- pyproject.toml | 2 +- tests/panels/test_sql.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/debug_toolbar/panels/sql/utils.py b/debug_toolbar/panels/sql/utils.py index 120674c96..cb4eda348 100644 --- a/debug_toolbar/panels/sql/utils.py +++ b/debug_toolbar/panels/sql/utils.py @@ -86,7 +86,7 @@ def process(stmt): return "".join(escaped_value(token) for token in stmt.flatten()) -def reformat_sql(sql, with_toggle=False): +def reformat_sql(sql, *, with_toggle=False): formatted = parse_sql(sql) if not with_toggle: return formatted diff --git a/debug_toolbar/panels/templates/panel.py b/debug_toolbar/panels/templates/panel.py index a1e45e83f..72565f016 100644 --- a/debug_toolbar/panels/templates/panel.py +++ b/debug_toolbar/panels/templates/panel.py @@ -117,7 +117,7 @@ def _store_template_info(self, sender, **kwargs): value.model._meta.label, ) else: - token = allow_sql.set(False) + token = allow_sql.set(False) # noqa: FBT003 try: saferepr(value) # this MAY trigger a db query except SQLQueryTriggered: diff --git a/pyproject.toml b/pyproject.toml index e04e3a42d..307574a5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,7 @@ extend-select = [ # flake8-2020 # "YTT", # flake8-boolean-trap - # "FBT", + "FBT", # flake8-bugbear "B", # flake8-builtins diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py index f4dcfaa2a..fd248663f 100644 --- a/tests/panels/test_sql.py +++ b/tests/panels/test_sql.py @@ -24,7 +24,7 @@ from ..models import Binary, PostgresJSON -def sql_call(use_iterator=False): +def sql_call(*, use_iterator=False): qs = User.objects.all() if use_iterator: qs = qs.iterator() @@ -105,7 +105,7 @@ async def test_cursor_wrapper_asyncio_ctx(self, mock_wrapper): await sync_to_async(sql_call)() async def task(): - sql_tracking.allow_sql.set(False) + sql_tracking.allow_sql.set(False) # noqa: FBT003 # By disabling sql_tracking.allow_sql, we are indicating that any # future SQL queries should be stopped. If SQL query occurs, # it raises an exception. From fcfdc7c6f4c2252a46f2da1a785eb479d1b790cb Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 5 Jun 2023 14:18:30 +0200 Subject: [PATCH 3/4] Activate flake8-pie --- debug_toolbar/panels/__init__.py | 1 - pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/debug_toolbar/panels/__init__.py b/debug_toolbar/panels/__init__.py index 6d9491b1f..57f385a5e 100644 --- a/debug_toolbar/panels/__init__.py +++ b/debug_toolbar/panels/__init__.py @@ -124,7 +124,6 @@ def ready(cls): be done unconditionally for the panel regardless of whether it is enabled for a particular request. It should be idempotent. """ - pass # URLs for panel-specific views diff --git a/pyproject.toml b/pyproject.toml index 307574a5c..ce6bcf740 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -93,7 +93,7 @@ extend-select = [ # flake8-logging-format "G", # flake8-pie - # "PIE", + "PIE", # flake8-simplify # "SIM", # flake8-gettext From ebd6ea3bc2adcdbf1190dfbc3540b12793ba28d9 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 5 Jun 2023 14:19:58 +0200 Subject: [PATCH 4/4] Activate flake8-simplify --- debug_toolbar/panels/cache.py | 5 +---- debug_toolbar/panels/history/panel.py | 6 +++--- debug_toolbar/panels/profiling.py | 5 +---- debug_toolbar/panels/request.py | 4 +--- debug_toolbar/panels/sql/tracking.py | 11 ++++------- debug_toolbar/utils.py | 5 +---- pyproject.toml | 2 +- tests/panels/test_profiling.py | 5 ++--- 8 files changed, 14 insertions(+), 29 deletions(-) diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index 31ce70988..4c7bf5af7 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -117,10 +117,7 @@ def _store_call_info( else: self.hits += 1 elif name == "get_many": - if "keys" in kwargs: - keys = kwargs["keys"] - else: - keys = args[0] + keys = kwargs["keys"] if "keys" in kwargs else args[0] self.hits += len(return_value) self.misses += len(keys) - len(return_value) time_taken *= 1000 diff --git a/debug_toolbar/panels/history/panel.py b/debug_toolbar/panels/history/panel.py index 503cade31..508a60577 100644 --- a/debug_toolbar/panels/history/panel.py +++ b/debug_toolbar/panels/history/panel.py @@ -1,3 +1,4 @@ +import contextlib import json from django.http.request import RawPostDataException @@ -62,10 +63,9 @@ def generate_stats(self, request, response): and request.body and request.headers.get("content-type") == "application/json" ): - try: + with contextlib.suppress(ValueError): data = json.loads(request.body) - except ValueError: - pass + except RawPostDataException: # It is not guaranteed that we may read the request data (again). data = None diff --git a/debug_toolbar/panels/profiling.py b/debug_toolbar/panels/profiling.py index 9947b17dc..9d10229ad 100644 --- a/debug_toolbar/panels/profiling.py +++ b/debug_toolbar/panels/profiling.py @@ -88,10 +88,7 @@ def subfuncs(self): for func, stats in self.statobj.all_callees[self.func].items(): i += 1 h1 = h + (i / count) / (self.depth + 1) - if stats[3] == 0: - s1 = 0 - else: - s1 = s * (stats[3] / self.stats[3]) + s1 = 0 if stats[3] == 0 else s * (stats[3] / self.stats[3]) yield FunctionCall( self.statobj, func, diff --git a/debug_toolbar/panels/request.py b/debug_toolbar/panels/request.py index bfb485ae7..a936eba6b 100644 --- a/debug_toolbar/panels/request.py +++ b/debug_toolbar/panels/request.py @@ -64,7 +64,5 @@ def generate_stats(self, request, response): (k, request.session.get(k)) for k in sorted(request.session.keys()) ] except TypeError: - session_list = [ - (k, request.session.get(k)) for k in request.session.keys() - ] + session_list = [(k, request.session.get(k)) for k in request.session] self.record_stats({"session": {"list": session_list}}) diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index ee75f8e06..9fab89b8b 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -1,3 +1,4 @@ +import contextlib import contextvars import datetime import json @@ -59,10 +60,7 @@ def cursor(*args, **kwargs): cursor = connection._djdt_cursor(*args, **kwargs) if logger is None: return cursor - if allow_sql.get(): - wrapper = NormalCursorWrapper - else: - wrapper = ExceptionCursorWrapper + wrapper = NormalCursorWrapper if allow_sql.get() else ExceptionCursorWrapper return wrapper(cursor.cursor, connection, logger) def chunked_cursor(*args, **kwargs): @@ -174,10 +172,9 @@ def _record(self, method, sql, params): stop_time = perf_counter() duration = (stop_time - start_time) * 1000 _params = "" - try: + with contextlib.suppress(TypeError): + # object JSON serializable? _params = json.dumps(self._decode(params)) - except TypeError: - pass # object not JSON serializable template_info = get_template_info() # Sql might be an object (such as psycopg Composed). diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py index 4a7e9b2c3..968160078 100644 --- a/debug_toolbar/utils.py +++ b/debug_toolbar/utils.py @@ -168,10 +168,7 @@ def get_template_source_from_exception_info( def get_name_from_obj(obj: Any) -> str: - if hasattr(obj, "__name__"): - name = obj.__name__ - else: - name = obj.__class__.__name__ + name = obj.__name__ if hasattr(obj, "__name__") else obj.__class__.__name__ if hasattr(obj, "__module__"): module = obj.__module__ diff --git a/pyproject.toml b/pyproject.toml index ce6bcf740..1ce4f3787 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,7 +95,7 @@ extend-select = [ # flake8-pie "PIE", # flake8-simplify - # "SIM", + "SIM", # flake8-gettext "INT", # pygrep-hooks diff --git a/tests/panels/test_profiling.py b/tests/panels/test_profiling.py index 2169932b2..ff613dfe1 100644 --- a/tests/panels/test_profiling.py +++ b/tests/panels/test_profiling.py @@ -88,7 +88,6 @@ def test_view_executed_once(self): self.assertContains(response, "Profiling") self.assertEqual(User.objects.count(), 1) - with self.assertRaises(IntegrityError): - with transaction.atomic(): - response = self.client.get("/new_user/") + with self.assertRaises(IntegrityError), transaction.atomic(): + response = self.client.get("/new_user/") self.assertEqual(User.objects.count(), 1)