From 07bb4ec1468d77886f2d8f4c6d1a1c9d934f6637 Mon Sep 17 00:00:00 2001 From: Brian Helba Date: Wed, 25 Oct 2023 19:45:06 -0400 Subject: [PATCH 1/2] Remove Python 3.8 and 3.9 support, add 3.11 and 3.12 support --- .github/workflows/ci.yml | 2 +- pyproject.toml | 2 +- setup.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00e6cc2..9dcdae0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} diff --git a/pyproject.toml b/pyproject.toml index 862e4bb..7ffba6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta" [tool.black] line-length = 100 skip-string-normalization = true -target-version = ["py38"] +target-version = ["py310"] exclude='\.eggs|\.git|\.mypy_cache|\.tox|\.venv|_build|buck-out|build|dist' [tool.isort] diff --git a/setup.py b/setup.py index f3c21c5..2031f85 100644 --- a/setup.py +++ b/setup.py @@ -48,12 +48,12 @@ 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Programming Language :: Python', ], - python_requires='>=3.8', + python_requires='>=3.10', install_requires=[ 'django-configurations[database,email]', ], From 299a23ba971dbf7e14237288e8b37e37116a69d6 Mon Sep 17 00:00:00 2001 From: Brian Helba Date: Wed, 25 Oct 2023 19:49:18 -0400 Subject: [PATCH 2/2] Run pyupgrade --- composed_configuration/_allauth.py | 4 +--- .../_allauth_support/createsuperuser.py | 10 +++++----- .../management/commands/createsuperuser.py | 8 ++++---- composed_configuration/_allauth_support/receiver.py | 3 +-- composed_configuration/_base.py | 3 +-- composed_configuration/_configuration.py | 6 ++---- composed_configuration/_cors.py | 4 +--- composed_configuration/_database.py | 4 +--- composed_configuration/_debug.py | 4 +--- composed_configuration/_django.py | 4 ++-- composed_configuration/_docker.py | 2 +- composed_configuration/_email.py | 6 +++--- composed_configuration/_extensions.py | 4 +--- composed_configuration/_filter.py | 4 +--- composed_configuration/_girder_utils.py | 4 +--- composed_configuration/_https.py | 4 +--- composed_configuration/_logging.py | 3 +-- composed_configuration/_rest_framework.py | 8 ++++---- composed_configuration/_sentry.py | 4 +--- composed_configuration/_static.py | 5 ++--- 20 files changed, 35 insertions(+), 59 deletions(-) diff --git a/composed_configuration/_allauth.py b/composed_configuration/_allauth.py index 0ae64e9..69e0fdb 100644 --- a/composed_configuration/_allauth.py +++ b/composed_configuration/_allauth.py @@ -1,5 +1,3 @@ -from typing import Type - from ._base import ComposedConfiguration, ConfigMixin @@ -11,7 +9,7 @@ class AllauthMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += [ 'django.contrib.sites', 'allauth', diff --git a/composed_configuration/_allauth_support/createsuperuser.py b/composed_configuration/_allauth_support/createsuperuser.py index cd9137f..4b423e4 100644 --- a/composed_configuration/_allauth_support/createsuperuser.py +++ b/composed_configuration/_allauth_support/createsuperuser.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import ClassVar, List, Optional +from typing import ClassVar from django.contrib.auth.management.commands import createsuperuser from django.contrib.auth.models import User, UserManager @@ -30,9 +30,9 @@ class EmailAsUsernameProxyUserManager(UserManager): # This version of "create_superuser" makes the "username" argument optional def create_superuser( self, - username: Optional[str] = None, - email: Optional[str] = None, - password: Optional[str] = None, + username: str | None = None, + email: str | None = None, + password: str | None = None, **extra_fields, ) -> EmailAsUsernameProxyUser: # Practically, email will always be provided @@ -55,7 +55,7 @@ class Meta(User.Meta): # Don't include "email" in "REQUIRED_FIELDS", to prevent adding that field twice to the # "createsuperuser.Command" argument parser - REQUIRED_FIELDS: ClassVar[List[str]] = [] + REQUIRED_FIELDS: ClassVar[list[str]] = [] @classmethod def normalize_username(cls, username: str) -> str: diff --git a/composed_configuration/_allauth_support/management/commands/createsuperuser.py b/composed_configuration/_allauth_support/management/commands/createsuperuser.py index 741a79f..0fda7d0 100644 --- a/composed_configuration/_allauth_support/management/commands/createsuperuser.py +++ b/composed_configuration/_allauth_support/management/commands/createsuperuser.py @@ -1,4 +1,4 @@ -from typing import Type, cast +from typing import cast from allauth.account import app_settings as allauth_settings from django.contrib.auth import get_user_model @@ -20,13 +20,13 @@ # If using email as username if not allauth_settings.USERNAME_REQUIRED: # Expose the modified command - Command: Type[BaseCommand] = allauth_support_createsuperuser.Command - user_model: Type[AbstractUser] = allauth_support_createsuperuser.EmailAsUsernameProxyUser + Command: type[BaseCommand] = allauth_support_createsuperuser.Command + user_model: type[AbstractUser] = allauth_support_createsuperuser.EmailAsUsernameProxyUser else: # Expose the pristine upstream version of the command Command = django_createsuperuser.Command - user_model = cast(Type[AbstractUser], get_user_model()) + user_model = cast(type[AbstractUser], get_user_model()) # Always automatically verify email addresses of newly created superusers post_save.connect(verify_email_address_on_user_post_save, sender=user_model) diff --git a/composed_configuration/_allauth_support/receiver.py b/composed_configuration/_allauth_support/receiver.py index 5aa0f6b..b1e6179 100644 --- a/composed_configuration/_allauth_support/receiver.py +++ b/composed_configuration/_allauth_support/receiver.py @@ -1,5 +1,4 @@ import logging -from typing import Type from allauth.account.models import EmailAddress from django.contrib.auth.models import AbstractUser @@ -8,7 +7,7 @@ def verify_email_address_on_user_post_save( - sender: Type[AbstractUser], + sender: type[AbstractUser], instance: AbstractUser, created: bool, **kwargs, diff --git a/composed_configuration/_base.py b/composed_configuration/_base.py index ed3bf52..3b05664 100644 --- a/composed_configuration/_base.py +++ b/composed_configuration/_base.py @@ -1,5 +1,4 @@ import contextlib -from typing import Type import warnings from configurations import Configuration, values @@ -62,7 +61,7 @@ class ConfigMixin: """Abstract mixin for composable Config sections.""" @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: """ Mutate the configuration before values are fully bound with environment variables. diff --git a/composed_configuration/_configuration.py b/composed_configuration/_configuration.py index a00d902..046521b 100644 --- a/composed_configuration/_configuration.py +++ b/composed_configuration/_configuration.py @@ -1,5 +1,3 @@ -from typing import List, Optional, Tuple - from configurations import values from ._allauth import AllauthMixin @@ -68,7 +66,7 @@ class TestingBaseConfiguration(MinioStorageMixin, _BaseConfiguration): SECRET_KEY = 'testingsecret' # Testing will add 'testserver' to ALLOWED_HOSTS - ALLOWED_HOSTS: List[str] = [] + ALLOWED_HOSTS: list[str] = [] MINIO_STORAGE_MEDIA_BUCKET_NAME = 'test-django-storage' @@ -97,7 +95,7 @@ class _HerokuMixin: environ_name='CLOUDAMQP_URL', environ_prefix=None, environ_required=True ) # https://help.heroku.com/J2R1S4T8/can-heroku-force-an-application-to-use-ssl-tls - SECURE_PROXY_SSL_HEADER: Optional[Tuple[str, str]] = ('HTTP_X_FORWARDED_PROTO', 'https') + SECURE_PROXY_SSL_HEADER: tuple[str, str] | None = ('HTTP_X_FORWARDED_PROTO', 'https') # This may be provided by https://github.com/ianpurvis/heroku-buildpack-version or similar # The commit SHA is the preferred release tag for Git-based projects: # https://docs.sentry.io/platforms/python/configuration/releases/#bind-the-version diff --git a/composed_configuration/_cors.py b/composed_configuration/_cors.py index f21df62..2a2c568 100644 --- a/composed_configuration/_cors.py +++ b/composed_configuration/_cors.py @@ -1,5 +1,3 @@ -from typing import Type - from configurations import values from ._base import ComposedConfiguration, ConfigMixin @@ -18,7 +16,7 @@ class CorsMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += ['corsheaders'] # CorsMiddleware must be added immediately before WhiteNoiseMiddleware, so this can diff --git a/composed_configuration/_database.py b/composed_configuration/_database.py index 19bf1fb..718dd20 100644 --- a/composed_configuration/_database.py +++ b/composed_configuration/_database.py @@ -1,5 +1,3 @@ -from typing import Type - from configurations import values from ._base import ComposedConfiguration, ConfigMixin @@ -16,7 +14,7 @@ class DatabaseMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += ['django.contrib.postgres'] # This cannot have a default value, since the password and database diff --git a/composed_configuration/_debug.py b/composed_configuration/_debug.py index 2ae00fb..6bd24dd 100644 --- a/composed_configuration/_debug.py +++ b/composed_configuration/_debug.py @@ -1,5 +1,3 @@ -from typing import Type - from ._base import ComposedConfiguration, ConfigMixin @@ -11,7 +9,7 @@ class DebugMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += ['debug_toolbar'] # Include Debug Toolbar middleware as early as possible in the list. diff --git a/composed_configuration/_django.py b/composed_configuration/_django.py index fc72d63..2b063ad 100644 --- a/composed_configuration/_django.py +++ b/composed_configuration/_django.py @@ -1,4 +1,4 @@ -from typing import NoReturn, Type +from typing import NoReturn from configurations import values @@ -21,7 +21,7 @@ class DjangoMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: # These are often extended, so update their values to avoid fragility due to mixin ordering configuration.INSTALLED_APPS += [ 'django.contrib.admin', diff --git a/composed_configuration/_docker.py b/composed_configuration/_docker.py index c9f272e..2d8b281 100644 --- a/composed_configuration/_docker.py +++ b/composed_configuration/_docker.py @@ -21,7 +21,7 @@ def _is_docker() -> bool: return False -class _AlwaysContains(object): +class _AlwaysContains: """An object which always returns True for `x in _AlwaysContains()` operations.""" def __contains__(self, item) -> bool: diff --git a/composed_configuration/_email.py b/composed_configuration/_email.py index d0ced01..8ec9637 100644 --- a/composed_configuration/_email.py +++ b/composed_configuration/_email.py @@ -1,4 +1,4 @@ -from typing import Dict, Type, cast +from typing import cast from configurations import values @@ -23,9 +23,9 @@ class SmtpEmailMixin(_EmailMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: email = cast( - Dict[str, str], + dict[str, str], values.EmailURLValue( environ_name='EMAIL_URL', environ_prefix='DJANGO', diff --git a/composed_configuration/_extensions.py b/composed_configuration/_extensions.py index 42a6434..0f1e946 100644 --- a/composed_configuration/_extensions.py +++ b/composed_configuration/_extensions.py @@ -1,5 +1,3 @@ -from typing import Type - from ._base import ComposedConfiguration, ConfigMixin @@ -11,7 +9,7 @@ class ExtensionsMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += ['django_extensions'] SHELL_PLUS_PRINT_SQL = True diff --git a/composed_configuration/_filter.py b/composed_configuration/_filter.py index 08a06f2..3b348c8 100644 --- a/composed_configuration/_filter.py +++ b/composed_configuration/_filter.py @@ -1,5 +1,3 @@ -from typing import Type - from ._base import ComposedConfiguration, ConfigMixin @@ -11,5 +9,5 @@ class FilterMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += ['django_filters'] diff --git a/composed_configuration/_girder_utils.py b/composed_configuration/_girder_utils.py index 00342d3..0859110 100644 --- a/composed_configuration/_girder_utils.py +++ b/composed_configuration/_girder_utils.py @@ -1,5 +1,3 @@ -from typing import Type - from ._base import ComposedConfiguration, ConfigMixin @@ -11,5 +9,5 @@ class GirderUtilsMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += ['girder_utils'] diff --git a/composed_configuration/_https.py b/composed_configuration/_https.py index e45ad76..f2f8474 100644 --- a/composed_configuration/_https.py +++ b/composed_configuration/_https.py @@ -1,5 +1,3 @@ -from typing import Optional, Tuple - from ._base import ConfigMixin @@ -9,7 +7,7 @@ class HttpsMixin(ConfigMixin): SECURE_SSL_REDIRECT = True # This must be set when deployed behind a proxy - SECURE_PROXY_SSL_HEADER: Optional[Tuple[str, str]] = None + SECURE_PROXY_SSL_HEADER: tuple[str, str] | None = None SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True diff --git a/composed_configuration/_logging.py b/composed_configuration/_logging.py index edf2df4..649aa70 100644 --- a/composed_configuration/_logging.py +++ b/composed_configuration/_logging.py @@ -1,5 +1,4 @@ import logging -from typing import Optional from django.http import HttpRequest @@ -8,7 +7,7 @@ def _filter_favicon_requests(record: logging.LogRecord) -> bool: if record.name == 'django.request': - request: Optional[HttpRequest] = getattr(record, 'request', None) + request: HttpRequest | None = getattr(record, 'request', None) if request and request.path == '/favicon.ico': return False diff --git a/composed_configuration/_rest_framework.py b/composed_configuration/_rest_framework.py index fd43b7d..7f22493 100644 --- a/composed_configuration/_rest_framework.py +++ b/composed_configuration/_rest_framework.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Type +from typing import Any from ._base import ComposedConfiguration, ConfigMixin @@ -12,7 +12,7 @@ class RestFrameworkMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += [ 'rest_framework', 'rest_framework.authtoken', @@ -106,7 +106,7 @@ def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: 'REFRESH_TOKEN_EXPIRE_SECONDS': 30 * 24 * 60 * 60, } - SWAGGER_SETTINGS: Dict[str, Any] = { + SWAGGER_SETTINGS: dict[str, Any] = { # The default security definition ("basic") is not supported by this DRF configuration, # so expect all logins to come via the Django session, which there's no OpenAPI # security definition for. @@ -114,4 +114,4 @@ def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: 'USE_SESSION_AUTH': True, } - REDOC_SETTINGS: Dict[str, Any] = {} + REDOC_SETTINGS: dict[str, Any] = {} diff --git a/composed_configuration/_sentry.py b/composed_configuration/_sentry.py index 58b687e..008d153 100644 --- a/composed_configuration/_sentry.py +++ b/composed_configuration/_sentry.py @@ -1,5 +1,3 @@ -from typing import Type - from configurations import values from ._base import ComposedConfiguration, ConfigMixin @@ -18,7 +16,7 @@ class SentryMixin(ConfigMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += [ 'composed_configuration.sentry.apps.SentryConfig', ] diff --git a/composed_configuration/_static.py b/composed_configuration/_static.py index bec0dc2..62078bc 100644 --- a/composed_configuration/_static.py +++ b/composed_configuration/_static.py @@ -1,5 +1,4 @@ from pathlib import Path -from typing import Type from ._base import ComposedConfiguration, ConfigMixin from ._values import DirectoryPathValue @@ -41,7 +40,7 @@ def STATIC_ROOT(self): # noqa: N802 ) @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: configuration.INSTALLED_APPS += ['django.contrib.staticfiles'] @@ -53,7 +52,7 @@ class WhitenoiseStaticFileMixin(StaticFileMixin): """ @staticmethod - def mutate_configuration(configuration: Type[ComposedConfiguration]) -> None: + def mutate_configuration(configuration: type[ComposedConfiguration]) -> None: # Insert immediately before staticfiles app staticfiles_index = configuration.INSTALLED_APPS.index('django.contrib.staticfiles') configuration.INSTALLED_APPS.insert(staticfiles_index, 'whitenoise.runserver_nostatic')