From de665311e388a40050d94f9819e7148bd86607c5 Mon Sep 17 00:00:00 2001 From: Roman Karpovich Date: Thu, 16 Jul 2020 11:57:37 +0300 Subject: [PATCH] dropped python 2; django 1.11/2.0/2.1 support. added django 3.0 to tox --- .flake8 | 19 ++++++++++++++++++ .isort.cfg | 10 ++++++++++ .travis.yml | 3 ++- drf_batch_example/urls.py | 2 +- drf_batch_example/views.py | 1 + drf_batch_requests/backends/sync.py | 1 + drf_batch_requests/graph.py | 6 ++++-- drf_batch_requests/request.py | 19 +++++++++--------- drf_batch_requests/serializers.py | 5 ++--- drf_batch_requests/urls.py | 2 +- drf_batch_requests/utils.py | 2 +- drf_batch_requests/views.py | 15 +++++++------- drf_batch_settings/settings.py | 27 ++++++++++++++++++++----- drf_batch_settings/urls.py | 7 ++++--- runtests.py | 1 - setup.py | 9 ++++----- tests/mixins.py | 4 +++- tests/test_settings.py | 31 +++++++++++++++++++++++++++-- tests/test_view.py | 1 + tests/urls.py | 4 ++-- tests/views.py | 1 + tox.ini | 24 ++++++++++++---------- 22 files changed, 139 insertions(+), 55 deletions(-) create mode 100644 .flake8 create mode 100644 .isort.cfg diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..06536e3 --- /dev/null +++ b/.flake8 @@ -0,0 +1,19 @@ +[flake8] +max-line-length = 120 +ignore = + ; PyFlakes errors + ; F405 name may be undefined, or defined from star imports: module + ; DUO130 insecure use of "hashlib" module + DUO130 + F405 + W503 + S105 + S107 + S303 + P103 + + + +exclude = + */migrations + ./.tox diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..120994a --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,10 @@ +[settings] +line_length=120 +multi_line_output=3 +include_trailing_comma=true +balanced_wrapping=true +skip=.tox,migrations,deployer,wsgi.py + +known_django=django +known_rest=rest_framework +sections=FUTURE,STDLIB,DJANGO,REST,THIRDPARTY,FIRSTPARTY,LOCALFOLDER diff --git a/.travis.yml b/.travis.yml index ed584ec..6a644fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,8 @@ python: - "2.7" - "3.5" - "3.6" - - "3.7-dev" + - "3.7" + - "3.8" install: - pip install . - pip install tox tox-travis coverage coveralls diff --git a/drf_batch_example/urls.py b/drf_batch_example/urls.py index 02d6ca5..556f5c6 100644 --- a/drf_batch_example/urls.py +++ b/drf_batch_example/urls.py @@ -1,6 +1,6 @@ try: from django.conf.urls import url -except: +except ImportError: # django 2.0 from django.urls import re_path as url diff --git a/drf_batch_example/views.py b/drf_batch_example/views.py index 9f2026f..7b46fd7 100644 --- a/drf_batch_example/views.py +++ b/drf_batch_example/views.py @@ -1,4 +1,5 @@ from django.http import JsonResponse + from rest_framework.views import APIView diff --git a/drf_batch_requests/backends/sync.py b/drf_batch_requests/backends/sync.py index d8547f2..481495d 100644 --- a/drf_batch_requests/backends/sync.py +++ b/drf_batch_requests/backends/sync.py @@ -1,4 +1,5 @@ from django.core.handlers.base import BaseHandler + from rest_framework.status import is_success from drf_batch_requests.backends.base import RequestsConsumeBaseBackend diff --git a/drf_batch_requests/graph.py b/drf_batch_requests/graph.py index 6690adb..904fdeb 100644 --- a/drf_batch_requests/graph.py +++ b/drf_batch_requests/graph.py @@ -25,8 +25,10 @@ def fail(self, own_fail=True): @property def can_be_performed(self): - return self.status == RequestGraphNode.STATUS_NOT_STARTED and \ - all(map(lambda parent: parent.status == RequestGraphNode.STATUS_COMPLETED, self.parents)) + if not self.status == RequestGraphNode.STATUS_NOT_STARTED: + return False + + return all(map(lambda parent: parent.status == RequestGraphNode.STATUS_COMPLETED, self.parents)) def __str__(self): return self.name or super(RequestGraphNode, self).__str__() diff --git a/drf_batch_requests/request.py b/drf_batch_requests/request.py index 38fafb2..ec6b752 100644 --- a/drf_batch_requests/request.py +++ b/drf_batch_requests/request.py @@ -1,12 +1,12 @@ import json import re +from io import BytesIO +from urllib.parse import urlsplit from django.http import HttpRequest from django.http.request import QueryDict -from django.utils import six from django.utils.encoding import force_text -from django.utils.six import BytesIO -from django.utils.six.moves.urllib.parse import urlsplit + from rest_framework.exceptions import ValidationError from drf_batch_requests.exceptions import RequestAttributeError @@ -67,15 +67,16 @@ def _prepare_formdata_body(self, data, files=None): boundary = match.groupdict()['boundary'] body = '' for key, value in data.items(): - value = value if isinstance(value, six.string_types) else json.dumps(value) + value = value if isinstance(value, str) else json.dumps(value) body += '--{}\r\nContent-Disposition: form-data; name="{}"\r\n\r\n{}\r\n'.format(boundary, key, value) if files: for key, attachment in files.items(): attachment.seek(0) - body += '--{}\r\nContent-Disposition: form-data; name="{}"; filename="{}"\r\n' \ - 'Content-Type: {}\r\n' \ - 'Content-Transfer-Encoding: binary\r\n\r\n{}\r\n'.format( + attachment_body_part = '--{0}\r\nContent-Disposition: form-data; name="{1}"; filename="{2}"\r\n' \ + 'Content-Type: {3}\r\n' \ + 'Content-Transfer-Encoding: binary\r\n\r\n{4}\r\n' + body += attachment_body_part.format( boundary, key, attachment.name, attachment.content_type, attachment.read() ) @@ -108,7 +109,7 @@ def _process_attr(self, attr): raise RequestAttributeError('Empty result for {}'.format(url_param[2])) if isinstance(result, list): - result = ','.join(map(six.text_type, result)) + result = ','.join(map(str, result)) if attr == url_param[0]: attr = result @@ -126,7 +127,7 @@ def updated_obj(self, obj): if isinstance(obj, dict): for key, value in obj.items(): obj[key] = self.updated_obj(value) - elif isinstance(obj, six.string_types): + elif isinstance(obj, str): return self._process_attr(obj) return obj diff --git a/drf_batch_requests/serializers.py b/drf_batch_requests/serializers.py index ec79a65..c89612e 100644 --- a/drf_batch_requests/serializers.py +++ b/drf_batch_requests/serializers.py @@ -1,7 +1,6 @@ import json from django.core.files import File -from django.utils import six from rest_framework import serializers from rest_framework.exceptions import ValidationError @@ -43,10 +42,10 @@ def validate(self, attrs): if 'depends_on' in attrs: value = attrs['depends_on'] - if not isinstance(value, (six.string_types, list)): + if not isinstance(value, (str, list)): raise ValidationError({'depends_on': 'Incorrect value provided'}) - if isinstance(value, six.string_types): + if isinstance(value, str): attrs['depends_on'] = [value] return attrs diff --git a/drf_batch_requests/urls.py b/drf_batch_requests/urls.py index 9e8e1ac..15e1791 100644 --- a/drf_batch_requests/urls.py +++ b/drf_batch_requests/urls.py @@ -1,6 +1,6 @@ try: from django.conf.urls import url -except: +except ImportError: # django 2.0 from django.urls import re_path as url diff --git a/drf_batch_requests/utils.py b/drf_batch_requests/utils.py index c471f63..1b91071 100644 --- a/drf_batch_requests/utils.py +++ b/drf_batch_requests/utils.py @@ -33,4 +33,4 @@ def callback(): else: raise NotImplementedError - return callback \ No newline at end of file + return callback diff --git a/drf_batch_requests/views.py b/drf_batch_requests/views.py index 1a2fb30..e593c69 100644 --- a/drf_batch_requests/views.py +++ b/drf_batch_requests/views.py @@ -1,22 +1,23 @@ import json from importlib import import_module -try: - from json import JSONDecodeError -except ImportError: - JSONDecodeError = ValueError - from django.db import transaction + from rest_framework.response import Response from rest_framework.status import is_success from rest_framework.views import APIView +from drf_batch_requests import settings as app_settings from drf_batch_requests.exceptions import RequestAttributeError from drf_batch_requests.graph import RequestGraph from drf_batch_requests.request import BatchRequestsFactory -from drf_batch_requests import settings as app_settings from drf_batch_requests.utils import generate_node_callback +try: + from json import JSONDecodeError +except ImportError: + JSONDecodeError = ValueError + class BatchView(APIView): permission_classes = [] @@ -47,7 +48,7 @@ def post(self, request, *args, **kwargs): for node in available_nodes: try: current_request = requests_factory.generate_request(node.request) - except RequestAttributeError as ex: + except RequestAttributeError: # todo: set fail reason node.fail() diff --git a/drf_batch_settings/settings.py b/drf_batch_settings/settings.py index d671d0a..6e60c5a 100644 --- a/drf_batch_settings/settings.py +++ b/drf_batch_settings/settings.py @@ -23,13 +23,31 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -TEMPLATE_DEBUG = True - ALLOWED_HOSTS = [] -# Application definition +# Template configurations +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + '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', + ], + 'loaders': [ + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + ], + }, + }, +] +# Application definition INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', @@ -41,12 +59,11 @@ 'drf_batch_example' ) -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) diff --git a/drf_batch_settings/urls.py b/drf_batch_settings/urls.py index fd18284..b3d1c08 100644 --- a/drf_batch_settings/urls.py +++ b/drf_batch_settings/urls.py @@ -1,8 +1,9 @@ try: - from django.conf.urls import url, include -except: + from django.conf.urls import include, url +except ImportError: # django 2.0 - from django.urls import re_path as url, include + from django.urls import include + from django.urls import re_path as url urlpatterns = [ url(r'^batch/', include('drf_batch_requests.urls', namespace='drf_batch')), diff --git a/runtests.py b/runtests.py index 4096fd4..be82e8f 100644 --- a/runtests.py +++ b/runtests.py @@ -6,7 +6,6 @@ from django.conf import settings from django.test.utils import get_runner - if __name__ == '__main__': os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings' django.setup() diff --git a/setup.py b/setup.py index 88347c1..518de2d 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ setup( name='drf-batch-requests', - version='0.8.11', + version='0.9.0', packages=['drf_batch_requests', ], include_package_data=True, license='MIT License', @@ -21,7 +21,7 @@ author='Roman Karpovich', author_email='fpm.th13f@gmail.com', install_requires=[ - 'django>=1.9', + 'django>=2.2', 'djangorestframework', ], classifiers=[ @@ -32,11 +32,10 @@ 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - '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', ], diff --git a/tests/mixins.py b/tests/mixins.py index d4e152d..a2a5bd4 100644 --- a/tests/mixins.py +++ b/tests/mixins.py @@ -3,7 +3,9 @@ except ImportError: from django.core.urlresolvers import resolve -from rest_framework.test import APITestCase as OriginalAPITestCase, APIRequestFactory, force_authenticate +from rest_framework.test import APIRequestFactory +from rest_framework.test import APITestCase as OriginalAPITestCase +from rest_framework.test import force_authenticate class APITestCase(OriginalAPITestCase): diff --git a/tests/test_settings.py b/tests/test_settings.py index 55b2157..14e88ad 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,21 +1,48 @@ import os - BASE_DIR = os.path.dirname(os.path.abspath(__file__)) SECRET_KEY = 'secret-key' +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + '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', + ], + 'loaders': [ + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + ], + }, + }, +] INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', - + 'django.contrib.messages', 'drf_batch_requests', 'tests', ] +MIDDLEWARE = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +) + DATABASES = { 'default': { diff --git a/tests/test_view.py b/tests/test_view.py index 3b466dd..6b713a3 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -1,6 +1,7 @@ import json from django.core.files.uploadedfile import SimpleUploadedFile + from rest_framework import status from tests.mixins import APITestCase diff --git a/tests/urls.py b/tests/urls.py index a555adc..dda94cb 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,6 +1,6 @@ try: - from django.conf.urls import url, include -except: + from django.conf.urls import include, url +except ImportError: # django 2.0 from django.urls import re_path as url, include diff --git a/tests/views.py b/tests/views.py index 94abf01..1547166 100644 --- a/tests/views.py +++ b/tests/views.py @@ -1,6 +1,7 @@ from django.http import JsonResponse from django.http.response import HttpResponse as DjangoResponse from django.views.generic import View + from rest_framework.response import Response from rest_framework.views import APIView diff --git a/tox.ini b/tox.ini index 69cb0b7..ac2a6e0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,20 +1,22 @@ [tox] envlist = - {py27}-django{111}, - {py35,py36,py37}-django{111,20,21}, + py38-linter, + {py36,py37,py38}-django{22,30}, [testenv] setenv= PYTHONPATH= deps = - django111: django>=1.11,<2 - django20: django>=2.0,<2.1 - django21: django>=2.1,<2.2 + django22: django>=2.2,<3 + django30: django>=3.0,<3.1 - django19: djangorestframework>=3.6,<3.7 - django{111,20,21}: djangorestframework + django{22,30}: djangorestframework - mock - coverage + django{22,30}: mock + django{22,30}: coverage + linter: isort>=5.1 + linter: flake8 commands = - coverage erase - coverage run ./runtests.py + linter: flake8 . + linter: isort . --check-only --rr + django{22,30}: coverage erase + django{22,30}: coverage run ./runtests.py