From 847846774442f20ac4ffc68f7f1f53707f53b209 Mon Sep 17 00:00:00 2001 From: Chris Chang Date: Mon, 9 Sep 2024 23:41:51 -0500 Subject: [PATCH] chore: add Ruff and use it for lint checks (#175) --- .github/workflows/ci.yml | 8 ++--- Makefile | 4 +++ django_object_actions/__init__.py | 8 +++++ django_object_actions/tests/test_admin.py | 10 +++---- django_object_actions/tests/test_utils.py | 2 +- django_object_actions/tests/tests.py | 2 +- django_object_actions/utils.py | 20 +++++-------- example_project/polls/admin.py | 8 ++--- example_project/polls/factories.py | 7 +---- .../polls/migrations/0001_initial.py | 2 -- example_project/urls.py | 3 +- poetry.lock | 29 ++++++++++++++++++- pyproject.toml | 9 +++++- 13 files changed, 72 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd9fb4a..96a9a29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,13 +50,13 @@ jobs: - run: make test lint: - name: "Black" runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.11" - - run: pip install black - - run: black --check . + python-version: "3.12" + - run: pip install poetry + - run: poetry install --with=dev + - run: make lint diff --git a/Makefile b/Makefile index 8fe9fa3..4663050 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,10 @@ install: ## Install development dependencies poetry install pip install Django +lint: ## Check the project for lint errors + poetry run ruff check . + poetry run ruff format --diff . + tdd: ## Run tests with a file watcher PYTHONPATH=. nodemon --ext py -x sh -c "poetry run python -W ignore::RuntimeWarning $(MANAGE) test --failfast django_object_actions || true" diff --git a/django_object_actions/__init__.py b/django_object_actions/__init__.py index 51ca754..3508dd0 100644 --- a/django_object_actions/__init__.py +++ b/django_object_actions/__init__.py @@ -10,3 +10,11 @@ takes_instance_or_queryset, action, ) + + +__all__ = [ + "BaseDjangoObjectActions", + "DjangoObjectActions", + "takes_instance_or_queryset", + "action", +] diff --git a/django_object_actions/tests/test_admin.py b/django_object_actions/tests/test_admin.py index 70e268f..698b401 100644 --- a/django_object_actions/tests/test_admin.py +++ b/django_object_actions/tests/test_admin.py @@ -19,7 +19,7 @@ class CommentTests(LoggedInTestCase): def test_action_on_a_model_with_uuid_pk_works(self): comment = CommentFactory() comment_url = reverse("admin:polls_comment_change", args=(comment.pk,)) - action_url = "/admin/polls/comment/{0}/actions/hodor/".format(comment.pk) + action_url = f"/admin/polls/comment/{comment.pk}/actions/hodor/" # sanity check that url has a uuid self.assertIn("-", action_url) response = self.client.get(action_url) @@ -28,7 +28,7 @@ def test_action_on_a_model_with_uuid_pk_works(self): @patch("django_object_actions.utils.ChangeActionView.dispatch") def test_action_on_a_model_with_arbitrary_pk_works(self, mock_view): mock_view.return_value = HttpResponse() - action_url = "/admin/polls/comment/{0}/actions/hodor/".format(" i am a pk ") + action_url = "/admin/polls/comment/{}/actions/hodor/".format(" i am a pk ") self.client.get(action_url) @@ -38,7 +38,7 @@ def test_action_on_a_model_with_arbitrary_pk_works(self, mock_view): @patch("django_object_actions.utils.ChangeActionView.dispatch") def test_action_on_a_model_with_slash_in_pk_works(self, mock_view): mock_view.return_value = HttpResponse() - action_url = "/admin/polls/comment/{0}/actions/hodor/".format("pk/slash") + action_url = "/admin/polls/comment/{}/actions/hodor/".format("pk/slash") self.client.get(action_url) @@ -52,8 +52,8 @@ def test_action_on_a_model_with_complex_id(self): related_data_url = reverse( "admin:polls_relateddata_change", args=(related_data.pk,) ) - action_url = "/admin/polls/relateddata/{}/actions/fill_up/".format( - quote(related_data.pk) + action_url = ( + f"/admin/polls/relateddata/{quote(related_data.pk)}/actions/fill_up/" ) response = self.client.get(action_url) diff --git a/django_object_actions/tests/test_utils.py b/django_object_actions/tests/test_utils.py index 70cf799..e8834f7 100644 --- a/django_object_actions/tests/test_utils.py +++ b/django_object_actions/tests/test_utils.py @@ -81,7 +81,7 @@ def test_get_button_attrs_custom_attrs_get_partitioned(self): class BaseActionViewTests(TestCase): def setUp(self): - super(BaseActionViewTests, self).setUp() + super().setUp() self.view = BaseActionView() @mock.patch("django_object_actions.utils.messages") diff --git a/django_object_actions/tests/tests.py b/django_object_actions/tests/tests.py index bee08b3..ac457f9 100644 --- a/django_object_actions/tests/tests.py +++ b/django_object_actions/tests/tests.py @@ -7,7 +7,7 @@ class LoggedInTestCase(TestCase): def setUp(self): - super(LoggedInTestCase, self).setUp() + super().setUp() UserFactory.create( is_staff=True, is_superuser=True, username="admin", password="admin" ) diff --git a/django_object_actions/utils.py b/django_object_actions/utils.py index 2635d07..765b704 100644 --- a/django_object_actions/utils.py +++ b/django_object_actions/utils.py @@ -15,7 +15,7 @@ DEFAULT_BUTTON_TYPE = "a" -class BaseDjangoObjectActions(object): +class BaseDjangoObjectActions: """ ModelAdmin mixin to add new actions just like adding admin actions. @@ -43,7 +43,7 @@ class BaseDjangoObjectActions(object): def get_urls(self): """Prepend `get_urls` with our own patterns.""" - urls = super(BaseDjangoObjectActions, self).get_urls() + urls = super().get_urls() return self._get_action_urls() + urls def change_view(self, request, object_id, form_url="", extra_context=None): @@ -57,9 +57,7 @@ def change_view(self, request, object_id, form_url="", extra_context=None): "tools_view_name": self.tools_view_name, } ) - return super(BaseDjangoObjectActions, self).change_view( - request, object_id, form_url, extra_context - ) + return super().change_view(request, object_id, form_url, extra_context) def changelist_view(self, request, extra_context=None): extra_context = extra_context or {} @@ -72,9 +70,7 @@ def changelist_view(self, request, extra_context=None): "tools_view_name": self.tools_view_name, } ) - return super(BaseDjangoObjectActions, self).changelist_view( - request, extra_context - ) + return super().changelist_view(request, extra_context) # USER OVERRIDABLE ################## @@ -112,9 +108,9 @@ def _get_action_urls(self): model_name = self.model._meta.model_name # e.g.: polls_poll - base_url_name = "%s_%s" % (self.model._meta.app_label, model_name) + base_url_name = f"{self.model._meta.app_label}_{model_name}" # e.g.: polls_poll_actions - model_actions_url_name = "%s_actions" % base_url_name + model_actions_url_name = f"{base_url_name}_actions" self.tools_view_name = "admin:" + model_actions_url_name @@ -131,7 +127,7 @@ def _get_action_urls(self): ChangeActionView.as_view( model=self.model, actions=actions, - back="admin:%s_change" % base_url_name, + back=f"admin:{base_url_name}_change", current_app=self.admin_site.name, ) ), @@ -144,7 +140,7 @@ def _get_action_urls(self): ChangeListActionView.as_view( model=self.model, actions=actions, - back="admin:%s_changelist" % base_url_name, + back=f"admin:{base_url_name}_changelist", current_app=self.admin_site.name, ) ), diff --git a/example_project/polls/admin.py b/example_project/polls/admin.py index 3c2ffdb..3d43a61 100644 --- a/example_project/polls/admin.py +++ b/example_project/polls/admin.py @@ -86,7 +86,7 @@ class PollAdmin(DjangoObjectActions, admin.ModelAdmin): def changelist_view(self, request, extra_context=None): extra_context = {"foo": "changelist_view"} - return super(PollAdmin, self).changelist_view(request, extra_context) + return super().changelist_view(request, extra_context) # Detail ######## @@ -99,7 +99,7 @@ def changelist_view(self, request, extra_context=None): def change_view(self, request, object_id, form_url="", extra_context=None): extra = {"foo": "change_view"} - return super(PollAdmin, self).change_view(request, object_id, form_url, extra) + return super().change_view(request, object_id, form_url, extra) # Object actions ################ @@ -123,9 +123,7 @@ def question_mark(self, request, obj): change_actions = ("delete_all_choices", "question_mark") def get_change_actions(self, request, object_id, form_url): - actions = super(PollAdmin, self).get_change_actions( - request, object_id, form_url - ) + actions = super().get_change_actions(request, object_id, form_url) actions = list(actions) if not request.user.is_superuser: return [] diff --git a/example_project/polls/factories.py b/example_project/polls/factories.py index 45e4afa..05f24a4 100644 --- a/example_project/polls/factories.py +++ b/example_project/polls/factories.py @@ -49,12 +49,7 @@ def get_random_string(length): class RelatedDataFactory(factory.django.DjangoModelFactory): id = factory.lazy_attribute( - lambda __: "{}:{}-{}!{}".format( - get_random_string(2), - get_random_string(2), - get_random_string(2), - get_random_string(2), - ) + lambda __: f"{get_random_string(2)}:{get_random_string(2)}-{get_random_string(2)}!{get_random_string(2)}" ) class Meta: diff --git a/example_project/polls/migrations/0001_initial.py b/example_project/polls/migrations/0001_initial.py index a96df65..5d53eca 100644 --- a/example_project/polls/migrations/0001_initial.py +++ b/example_project/polls/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.9.2 on 2016-02-25 17:25 -from __future__ import unicode_literals from django.db import migrations, models import django.db.models.deletion diff --git a/example_project/urls.py b/example_project/urls.py index 38379ec..47b4296 100644 --- a/example_project/urls.py +++ b/example_project/urls.py @@ -1,5 +1,4 @@ -from django import VERSION -from django.urls import include, path +from django.urls import path from django.contrib import admin from example_project.polls.admin import support_admin diff --git a/poetry.lock b/poetry.lock index ee631af..0396768 100644 --- a/poetry.lock +++ b/poetry.lock @@ -217,6 +217,33 @@ files = [ {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] +[[package]] +name = "ruff" +version = "0.6.4" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.6.4-py3-none-linux_armv6l.whl", hash = "sha256:c4b153fc152af51855458e79e835fb6b933032921756cec9af7d0ba2aa01a258"}, + {file = "ruff-0.6.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:bedff9e4f004dad5f7f76a9d39c4ca98af526c9b1695068198b3bda8c085ef60"}, + {file = "ruff-0.6.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d02a4127a86de23002e694d7ff19f905c51e338c72d8e09b56bfb60e1681724f"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7862f42fc1a4aca1ea3ffe8a11f67819d183a5693b228f0bb3a531f5e40336fc"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eebe4ff1967c838a1a9618a5a59a3b0a00406f8d7eefee97c70411fefc353617"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:932063a03bac394866683e15710c25b8690ccdca1cf192b9a98260332ca93408"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:50e30b437cebef547bd5c3edf9ce81343e5dd7c737cb36ccb4fe83573f3d392e"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c44536df7b93a587de690e124b89bd47306fddd59398a0fb12afd6133c7b3818"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ea086601b22dc5e7693a78f3fcfc460cceabfdf3bdc36dc898792aba48fbad6"}, + {file = "ruff-0.6.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b52387d3289ccd227b62102c24714ed75fbba0b16ecc69a923a37e3b5e0aaaa"}, + {file = "ruff-0.6.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0308610470fcc82969082fc83c76c0d362f562e2f0cdab0586516f03a4e06ec6"}, + {file = "ruff-0.6.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:803b96dea21795a6c9d5bfa9e96127cc9c31a1987802ca68f35e5c95aed3fc0d"}, + {file = "ruff-0.6.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:66dbfea86b663baab8fcae56c59f190caba9398df1488164e2df53e216248baa"}, + {file = "ruff-0.6.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:34d5efad480193c046c86608dbba2bccdc1c5fd11950fb271f8086e0c763a5d1"}, + {file = "ruff-0.6.4-py3-none-win32.whl", hash = "sha256:f0f8968feea5ce3777c0d8365653d5e91c40c31a81d95824ba61d871a11b8523"}, + {file = "ruff-0.6.4-py3-none-win_amd64.whl", hash = "sha256:549daccee5227282289390b0222d0fbee0275d1db6d514550d65420053021a58"}, + {file = "ruff-0.6.4-py3-none-win_arm64.whl", hash = "sha256:ac4b75e898ed189b3708c9ab3fc70b79a433219e1e87193b4f2b77251d058d14"}, + {file = "ruff-0.6.4.tar.gz", hash = "sha256:ac3b5bfbee99973f80aa1b7cbd1c9cbce200883bdd067300c22a6cc1c7fba212"}, +] + [[package]] name = "six" version = "1.16.0" @@ -273,4 +300,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "24d1ba21080f62fdd172392b30b6b6168f9e410e5707c14f96390c1dfa047796" +content-hash = "a5e9e1663c3d8d3e89a83ff416cd67e4db1e6b3936cca0c0bf98810de0f05fda" diff --git a/pyproject.toml b/pyproject.toml index 5083070..1abfa86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,11 +31,12 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.7" -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] coverage = "7.*" django-extensions = "3.*" factory-boy = "3.*" dj-database-url = "2.*" +ruff = "*" [tool.semantic_release] version_toml = ["pyproject.toml:tool.poetry.version"] @@ -54,3 +55,9 @@ exclude_lines = [ "__unicode__", "raise NotImplementedError", ] + +[tool.ruff] +target-version = "py37" + +[tool.ruff.lint] +extend-select = ["UP"]