From 5ce273bbce1c76c390cb12d4b9f51fe144490fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Wed, 9 Apr 2025 10:14:21 +0300 Subject: [PATCH] Add django-upgrade to pre-commit hooks --- .pre-commit-config.yaml | 10 ++++++++++ rest_framework/authentication.py | 2 +- rest_framework/authtoken/admin.py | 4 +--- rest_framework/negotiation.py | 2 +- rest_framework/throttling.py | 2 +- tests/test_api_client.py | 4 ++-- tests/test_middleware.py | 2 +- tests/test_relations.py | 4 ++-- tests/test_renderers.py | 16 ++++++++-------- tests/test_requests_client.py | 2 +- tests/test_response.py | 8 ++++---- tests/test_testing.py | 4 ++-- tests/test_urlpatterns.py | 2 +- tests/test_versioning.py | 2 +- 14 files changed, 36 insertions(+), 28 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 27bbcb7634..a9c053fcb9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,3 +37,13 @@ repos: hooks: - id: pyupgrade args: ["--py39-plus", "--keep-percent-format"] + +- repo: https://github.com/adamchainz/django-upgrade + rev: 1.24.0 + hooks: + - id: django-upgrade + args: [ + --target-version, "4.2", + # test_urlpatterns.py has re_path related tests + --skip, "django_urls", "tests/test_urlpatterns.py", + ] diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 3f3bd2227c..58ef9d2e10 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -17,7 +17,7 @@ def get_authorization_header(request): Hide some test client ickyness where the header can be unicode. """ - auth = request.META.get('HTTP_AUTHORIZATION', b'') + auth = request.headers.get('authorization', b'') if isinstance(auth, str): # Work around django test client oddness auth = auth.encode(HTTP_HEADER_ENCODING) diff --git a/rest_framework/authtoken/admin.py b/rest_framework/authtoken/admin.py index eabb8fca8b..0c98c6a0d2 100644 --- a/rest_framework/authtoken/admin.py +++ b/rest_framework/authtoken/admin.py @@ -21,6 +21,7 @@ def url_for_result(self, result): current_app=self.model_admin.admin_site.name) +@admin.register(TokenProxy) class TokenAdmin(admin.ModelAdmin): list_display = ('key', 'user', 'created') fields = ('user',) @@ -49,6 +50,3 @@ def delete_model(self, request, obj): # Map back to actual Token, since delete() uses pk. token = Token.objects.get(key=obj.key) return super().delete_model(request, token) - - -admin.site.register(TokenProxy, TokenAdmin) diff --git a/rest_framework/negotiation.py b/rest_framework/negotiation.py index 23012f71fd..d0b43ccd85 100644 --- a/rest_framework/negotiation.py +++ b/rest_framework/negotiation.py @@ -93,5 +93,5 @@ def get_accept_list(self, request): Given the incoming request, return a tokenized list of media type strings. """ - header = request.META.get('HTTP_ACCEPT', '*/*') + header = request.headers.get('accept', '*/*') return [token.strip() for token in header.split(',')] diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py index c0d6cf42fe..90ce01c428 100644 --- a/rest_framework/throttling.py +++ b/rest_framework/throttling.py @@ -26,7 +26,7 @@ def get_ident(self, request): if present and number of proxies is > 0. If not use all of HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR. """ - xff = request.META.get('HTTP_X_FORWARDED_FOR') + xff = request.headers.get('x-forwarded-for') remote_addr = request.META.get('REMOTE_ADDR') num_proxies = api_settings.NUM_PROXIES diff --git a/tests/test_api_client.py b/tests/test_api_client.py index 976f10ed1d..b2d322de28 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -4,7 +4,7 @@ from django.http import HttpResponse from django.test import override_settings -from django.urls import path, re_path +from django.urls import path from rest_framework.compat import coreapi, coreschema from rest_framework.parsers import FileUploadParser @@ -180,7 +180,7 @@ def get(self, request): urlpatterns = [ path('', SchemaView.as_view()), path('example/', ListView.as_view()), - re_path(r'^example/(?P[0-9]+)/$', DetailView.as_view()), + path('example//', DetailView.as_view()), path('upload/', UploadView.as_view()), path('download/', DownloadView.as_view()), path('text/', TextView.as_view()), diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 11d4bc01eb..bcd076cde7 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -102,7 +102,7 @@ def test_middleware_can_access_user_when_processing_response(self): key = 'abcd1234' Token.objects.create(key=key, user=user) - self.client.get('/auth', HTTP_AUTHORIZATION='Token %s' % key) + self.client.get('/auth', headers={"authorization": 'Token %s' % key}) @override_settings(MIDDLEWARE=('tests.test_middleware.RequestPOSTMiddleware',)) def test_middleware_can_access_request_post_when_processing_response(self): diff --git a/tests/test_relations.py b/tests/test_relations.py index b9ab157896..28f8e9d668 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -4,7 +4,7 @@ from _pytest.monkeypatch import MonkeyPatch from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.test import override_settings -from django.urls import re_path +from django.urls import path from django.utils.datastructures import MultiValueDict from rest_framework import relations, serializers @@ -152,7 +152,7 @@ def test_pk_representation(self): urlpatterns = [ - re_path(r'^example/(?P.+)/$', lambda: None, name='example'), + path('example//', lambda: None, name='example'), ] diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 1b396575d4..b5b28c17f3 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -174,7 +174,7 @@ def test_head_method_serializes_no_content(self): def test_default_renderer_serializes_content_on_accept_any(self): """If the Accept header is set to */* the default renderer should serialize the response.""" - resp = self.client.get('/', HTTP_ACCEPT='*/*') + resp = self.client.get('/', headers={"accept": '*/*'}) self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -182,7 +182,7 @@ def test_default_renderer_serializes_content_on_accept_any(self): def test_specified_renderer_serializes_content_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for the default renderer)""" - resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type) + resp = self.client.get('/', headers={"accept": RendererA.media_type}) self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -190,14 +190,14 @@ def test_specified_renderer_serializes_content_default_case(self): def test_specified_renderer_serializes_content_non_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for a non-default renderer)""" - resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type) + resp = self.client.get('/', headers={"accept": RendererB.media_type}) self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) def test_unsatisfiable_accept_header_on_request_returns_406_status(self): """If the Accept header is unsatisfiable we should return a 406 Not Acceptable response.""" - resp = self.client.get('/', HTTP_ACCEPT='foo/bar') + resp = self.client.get('/', headers={"accept": 'foo/bar'}) self.assertEqual(resp.status_code, status.HTTP_406_NOT_ACCEPTABLE) def test_specified_renderer_serializes_content_on_format_query(self): @@ -228,14 +228,14 @@ def test_specified_renderer_is_used_on_format_query_with_matching_accept(self): RendererB.format ) resp = self.client.get('/' + param, - HTTP_ACCEPT=RendererB.media_type) + headers={"accept": RendererB.media_type}) self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) def test_parse_error_renderers_browsable_api(self): """Invalid data should still render the browsable API correctly.""" - resp = self.client.post('/parseerror', data='foobar', content_type='application/json', HTTP_ACCEPT='text/html') + resp = self.client.post('/parseerror', data='foobar', content_type='application/json', headers={"accept": 'text/html'}) self.assertEqual(resp['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST) @@ -714,13 +714,13 @@ class DummyView: assert result is None def test_extra_actions_dropdown(self): - resp = self.client.get('/api/examples/', HTTP_ACCEPT='text/html') + resp = self.client.get('/api/examples/', headers={"accept": 'text/html'}) assert 'id="extra-actions-menu"' in resp.content.decode() assert '/api/examples/list_action/' in resp.content.decode() assert '>Extra list action<' in resp.content.decode() def test_extra_actions_dropdown_not_authed(self): - resp = self.client.get('/api/unauth-examples/', HTTP_ACCEPT='text/html') + resp = self.client.get('/api/unauth-examples/', headers={"accept": 'text/html'}) assert 'id="extra-actions-menu"' not in resp.content.decode() assert '/api/examples/list_action/' not in resp.content.decode() assert '>Extra list action<' not in resp.content.decode() diff --git a/tests/test_requests_client.py b/tests/test_requests_client.py index c8e7be6ee3..9b4f4795fb 100644 --- a/tests/test_requests_client.py +++ b/tests/test_requests_client.py @@ -28,7 +28,7 @@ def post(self, request): } post = request.POST json = None - if request.META.get('CONTENT_TYPE') == 'application/json': + if request.headers.get('content-type') == 'application/json': json = request.data return Response({ diff --git a/tests/test_response.py b/tests/test_response.py index 83f8a67172..781478b7a3 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -151,7 +151,7 @@ def test_head_method_serializes_no_content(self): def test_default_renderer_serializes_content_on_accept_any(self): """If the Accept header is set to */* the default renderer should serialize the response.""" - resp = self.client.get('/', HTTP_ACCEPT='*/*') + resp = self.client.get('/', headers={"accept": '*/*'}) self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -159,7 +159,7 @@ def test_default_renderer_serializes_content_on_accept_any(self): def test_specified_renderer_serializes_content_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for the default renderer)""" - resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type) + resp = self.client.get('/', headers={"accept": RendererA.media_type}) self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -167,7 +167,7 @@ def test_specified_renderer_serializes_content_default_case(self): def test_specified_renderer_serializes_content_non_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for a non-default renderer)""" - resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type) + resp = self.client.get('/', headers={"accept": RendererB.media_type}) self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -192,7 +192,7 @@ def test_specified_renderer_is_used_on_format_query_with_matching_accept(self): """If both a 'format' query and a matching Accept header specified, the renderer with the matching format attribute should serialize the response.""" resp = self.client.get('/?format=%s' % RendererB.format, - HTTP_ACCEPT=RendererB.media_type) + headers={"accept": RendererB.media_type}) self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) diff --git a/tests/test_testing.py b/tests/test_testing.py index 26a6e8ffb9..db886d20a9 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -21,7 +21,7 @@ @api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']) def view(request): - data = {'auth': request.META.get('HTTP_AUTHORIZATION', b'')} + data = {'auth': request.headers.get('authorization', b'')} if request.user: data['user'] = request.user.username if request.auth: @@ -347,7 +347,7 @@ def test_empty_request_content_type(self): data=None, content_type='application/json', ) - assert request.META['CONTENT_TYPE'] == 'application/json' + assert request.headers['content-type'] == 'application/json' class TestUrlPatternTestCase(URLPatternsTestCase): diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py index adcd0a7427..3da548aeeb 100644 --- a/tests/test_urlpatterns.py +++ b/tests/test_urlpatterns.py @@ -93,7 +93,7 @@ def test_format_suffix_django2(self): def test_format_suffix_django2_args(self): urlpatterns = [ path('convtest/', dummy_view), - re_path(r'^retest/(?P[0-9]+)$', dummy_view), + path('retest/', dummy_view), ] test_paths = [ URLTestPath('/convtest/42', (), {'pk': 42}), diff --git a/tests/test_versioning.py b/tests/test_versioning.py index b216461840..2c3535c3a4 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -152,7 +152,7 @@ class TestURLReversing(URLPatternsTestCase, APITestCase): path('v1/', include((included, 'v1'), namespace='v1')), path('another/', dummy_view, name='another'), re_path(r'^(?P[v1|v2]+)/another/$', dummy_view, name='another'), - re_path(r'^(?P.+)/unversioned/$', dummy_view, name='unversioned'), + path('/unversioned/', dummy_view, name='unversioned'), ]