django-resonant-settings 0.44.0__tar.gz → 0.45.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/.gitignore +18 -20
  2. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/PKG-INFO +3 -2
  3. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/pyproject.toml +55 -21
  4. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/_env.py +2 -0
  5. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/allauth.py +16 -9
  6. django_resonant_settings-0.45.0/resonant_settings/allauth_support/adapter.py +16 -0
  7. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/allauth_support/apps.py +2 -0
  8. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/allauth_support/createsuperuser.py +6 -6
  9. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/allauth_support/management/commands/createsuperuser.py +8 -4
  10. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/allauth_support/receiver.py +10 -5
  11. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/allauth_support/utils.py +6 -2
  12. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/celery.py +12 -8
  13. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/development/celery.py +2 -0
  14. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/development/debug_toolbar.py +5 -1
  15. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/development/minio_storage.py +11 -6
  16. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/django.py +4 -2
  17. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/django_extensions.py +3 -1
  18. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/logging.py +12 -5
  19. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/oauth_toolkit.py +8 -1
  20. django_resonant_settings-0.45.0/resonant_settings/production/__init__.py +0 -0
  21. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/production/email.py +3 -1
  22. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/production/https.py +5 -3
  23. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/production/s3_storage.py +7 -5
  24. django_resonant_settings-0.45.0/resonant_settings/py.typed +0 -0
  25. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/rest_framework.py +4 -2
  26. django_resonant_settings-0.44.0/resonant_settings/allauth_support/adapter.py +0 -10
  27. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/LICENSE +0 -0
  28. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/NOTICE +0 -0
  29. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/README.md +0 -0
  30. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/__init__.py +0 -0
  31. {django_resonant_settings-0.44.0 → django_resonant_settings-0.45.0}/resonant_settings/allauth_support/__init__.py +0 -0
  32. {django_resonant_settings-0.44.0/resonant_settings/development → django_resonant_settings-0.45.0/resonant_settings/allauth_support/management}/__init__.py +0 -0
  33. {django_resonant_settings-0.44.0/resonant_settings/production → django_resonant_settings-0.45.0/resonant_settings/allauth_support/management/commands}/__init__.py +0 -0
  34. /django_resonant_settings-0.44.0/resonant_settings/py.typed → /django_resonant_settings-0.45.0/resonant_settings/development/__init__.py +0 -0
@@ -1,9 +1,22 @@
1
- # Created by https://www.toptal.com/developers/gitignore/api/python
2
- # Edit at https://www.toptal.com/developers/gitignore?templates=python
1
+ # Created by https://www.toptal.com/developers/gitignore/api/django
2
+ # Edit at https://www.toptal.com/developers/gitignore?templates=django
3
3
 
4
- ### Python ###
5
- # Byte-compiled / optimized / DLL files
4
+ ### Django ###
5
+ *.log
6
+ *.pot
7
+ *.pyc
6
8
  __pycache__/
9
+ local_settings.py
10
+ db.sqlite3
11
+ db.sqlite3-journal
12
+ media
13
+
14
+ # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
15
+ # in your Git repository. Update and uncomment the following line accordingly.
16
+ # <django-project-name>/staticfiles/
17
+
18
+ ### Django.Python Stack ###
19
+ # Byte-compiled / optimized / DLL files
7
20
  *.py[cod]
8
21
  *$py.class
9
22
 
@@ -57,13 +70,8 @@ cover/
57
70
 
58
71
  # Translations
59
72
  *.mo
60
- *.pot
61
73
 
62
74
  # Django stuff:
63
- *.log
64
- local_settings.py
65
- db.sqlite3
66
- db.sqlite3-journal
67
75
 
68
76
  # Flask stuff:
69
77
  instance/
@@ -163,14 +171,4 @@ cython_debug/
163
171
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
164
172
  #.idea/
165
173
 
166
- ### Python Patch ###
167
- # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
168
- poetry.toml
169
-
170
- # ruff
171
- .ruff_cache/
172
-
173
- # LSP config files
174
- pyrightconfig.json
175
-
176
- # End of https://www.toptal.com/developers/gitignore/api/python
174
+ # End of https://www.toptal.com/developers/gitignore/api/django
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-resonant-settings
3
- Version: 0.44.0
3
+ Version: 0.45.0
4
4
  Summary: Shared Django settings for Resonant applications.
5
5
  Project-URL: Repository, https://github.com/kitware-resonant/cookiecutter-resonant
6
6
  Project-URL: Bug Reports, https://github.com/kitware-resonant/cookiecutter-resonant/issues
@@ -8,7 +8,7 @@ Maintainer-email: "Kitware, Inc." <kitware@kitware.com>
8
8
  License-Expression: Apache-2.0
9
9
  License-File: LICENSE
10
10
  License-File: NOTICE
11
- Keywords: django,resonant,setting,settings
11
+ Keywords: django,kitware-resonant,resonant,setting,settings
12
12
  Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Environment :: Web Environment
14
14
  Classifier: Framework :: Django
@@ -26,6 +26,7 @@ Classifier: Programming Language :: Python :: 3.13
26
26
  Classifier: Programming Language :: Python :: 3.14
27
27
  Requires-Python: >=3.10
28
28
  Requires-Dist: django-environ
29
+ Requires-Dist: django>=5.1
29
30
  Provides-Extra: allauth
30
31
  Requires-Dist: django-allauth; extra == 'allauth'
31
32
  Provides-Extra: celery
@@ -13,27 +13,29 @@ maintainers = [{ name = "Kitware, Inc.", email = "kitware@kitware.com" }]
13
13
  keywords = [
14
14
  "django",
15
15
  "resonant",
16
+ "kitware-resonant",
16
17
  "setting",
17
18
  "settings",
18
19
  ]
19
20
  classifiers = [
20
21
  "Development Status :: 3 - Alpha",
21
22
  "Environment :: Web Environment",
23
+ "Framework :: Django",
22
24
  "Framework :: Django :: 5",
23
25
  "Framework :: Django :: 5.1",
24
26
  "Framework :: Django :: 5.2",
25
- "Framework :: Django",
26
27
  "Intended Audience :: Developers",
27
28
  "Operating System :: OS Independent",
29
+ "Programming Language :: Python",
28
30
  "Programming Language :: Python :: 3",
29
31
  "Programming Language :: Python :: 3.10",
30
32
  "Programming Language :: Python :: 3.11",
31
33
  "Programming Language :: Python :: 3.12",
32
34
  "Programming Language :: Python :: 3.13",
33
35
  "Programming Language :: Python :: 3.14",
34
- "Programming Language :: Python",
35
36
  ]
36
37
  dependencies = [
38
+ "django>=5.1",
37
39
  "django-environ",
38
40
  ]
39
41
  dynamic = ["version"]
@@ -56,16 +58,7 @@ dev = [
56
58
  "tox-uv",
57
59
  ]
58
60
  lint = [
59
- "flake8",
60
- "flake8-black",
61
- "flake8-bugbear",
62
- "flake8-docstrings",
63
- "flake8-isort",
64
- "pep8-naming",
65
- ]
66
- format = [
67
- "black",
68
- "isort",
61
+ "ruff",
69
62
  ]
70
63
  type = [
71
64
  "mypy",
@@ -74,7 +67,7 @@ type = [
74
67
  ]
75
68
 
76
69
  [tool.hatch.build]
77
- packages = [
70
+ only-include = [
78
71
  "resonant_settings",
79
72
  ]
80
73
 
@@ -82,17 +75,57 @@ packages = [
82
75
  source = "vcs"
83
76
  raw-options = { root = ".." }
84
77
 
85
- [tool.black]
78
+ [tool.ruff]
86
79
  line-length = 100
87
- target-version = ["py310"]
88
80
 
89
- [tool.isort]
90
- profile = "black"
91
- line_length = 100
81
+ [tool.ruff.lint]
82
+ select = ["ALL"]
83
+ ignore = [
84
+ # Incompatible with formatter
85
+ # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
86
+ "COM812", # missing-trailing-comma
87
+ "COM819", # prohibited-trailing-comma
88
+ "D206", # docstring-tab-indentation
89
+ "D300", # triple-single-quotes
90
+ "E111", # indentation-with-invalid-multiple
91
+ "E114", # indentation-with-invalid-multiple-comment
92
+ "E117", # over-indented
93
+ "ISC001", # single-line-implicit-string-concatenation
94
+ "ISC002", # multi-line-implicit-string-concatenation
95
+ "Q", # flake8-quotes
96
+ "W191", # tab-indentation
97
+
98
+ # Incompatible with Django
99
+ "A003", # builtin-attribute-shadowing
100
+ "ARG001", # unused-function-argument
101
+ "ARG002", # unused-method-argument
102
+ "RUF012", # mutable-class-default
103
+
104
+ # Overly burdensome
105
+ "ANN", # flake8-annotations
106
+ "D1", # undocumented-*
107
+ "EM101", # raw-string-in-exception
108
+ "ERA001", # commented-out-code
109
+ "FIX", # flake8-fixme
110
+ "RET506", # superfluous-else-raise
111
+ "TD002", # missing-todo-author
112
+ "TD003", # missing-todo-link
113
+ "TRY003", # raise-vanilla-args
114
+
115
+ # Project-specific
116
+ ]
117
+
118
+ [tool.ruff.lint.flake8-self]
119
+ extend-ignore-names = ["_base_manager", "_default_manager", "_meta"]
120
+
121
+ [tool.ruff.lint.isort]
92
122
  # Sort by name, don't cluster "from" vs "import"
93
- force_sort_within_sections = true
94
- # Combines "as" imports on the same line
95
- combine_as_imports = true
123
+ force-sort-within-sections = true
124
+ # Deferred annotations allows TC* rules to move more imports into TYPE_CHECKING blocks
125
+ required-imports = ["from __future__ import annotations"]
126
+
127
+ [tool.ruff.lint.pydocstyle]
128
+ convention = "pep257"
96
129
 
97
130
  [tool.mypy]
98
131
  files = [
@@ -100,6 +133,7 @@ files = [
100
133
  ]
101
134
  check_untyped_defs = true
102
135
  show_error_codes = true
136
+ strict = true
103
137
  warn_redundant_casts = true
104
138
  warn_unused_configs = true
105
139
  warn_unused_ignores = true
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import environ
2
4
 
3
5
  env = environ.Env()
@@ -1,5 +1,7 @@
1
1
  """
2
- Configure django-allauth with the following features:
2
+ Configure django-allauth.
3
+
4
+ This provides the following features:
3
5
  * Disable usernames for end users, using exclusively email addresses for login
4
6
  * Require email verification
5
7
  * Quality of life improvements for users
@@ -8,7 +10,12 @@ This requires the `django-allauth` package to be installed and requires
8
10
  `resonant_settings.allauth_support` to be added to INSTALLED_APPS.
9
11
  """
10
12
 
11
- from collections.abc import Sequence
13
+ from __future__ import annotations
14
+
15
+ from typing import TYPE_CHECKING
16
+
17
+ if TYPE_CHECKING:
18
+ from collections.abc import Sequence
12
19
 
13
20
  # The sites framework requires this to be set.
14
21
  # In the unlikely case where a database's pk sequence for the django_site table is not reset,
@@ -49,16 +56,16 @@ ACCOUNT_CONFIRM_EMAIL_ON_GET = True
49
56
  ACCOUNT_PRESERVE_USERNAME_CASING = False
50
57
 
51
58
  __all__ = [
52
- "SITE_ID",
53
- "AUTHENTICATION_BACKENDS",
59
+ "ACCOUNT_ADAPTER",
60
+ "ACCOUNT_CONFIRM_EMAIL_ON_GET",
54
61
  "ACCOUNT_EMAIL_VERIFICATION",
55
62
  "ACCOUNT_LOGIN_METHODS",
56
- "ACCOUNT_SIGNUP_FIELDS",
57
- "ACCOUNT_ADAPTER",
58
- "ACCOUNT_USER_MODEL_USERNAME_FIELD",
59
- "ACCOUNT_SESSION_REMEMBER",
60
63
  "ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION",
61
64
  "ACCOUNT_LOGIN_ON_PASSWORD_RESET",
62
- "ACCOUNT_CONFIRM_EMAIL_ON_GET",
63
65
  "ACCOUNT_PRESERVE_USERNAME_CASING",
66
+ "ACCOUNT_SESSION_REMEMBER",
67
+ "ACCOUNT_SIGNUP_FIELDS",
68
+ "ACCOUNT_USER_MODEL_USERNAME_FIELD",
69
+ "AUTHENTICATION_BACKENDS",
70
+ "SITE_ID",
64
71
  ]
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from allauth.account.adapter import DefaultAccountAdapter
6
+
7
+ if TYPE_CHECKING:
8
+ from django.contrib.auth.models import AbstractUser
9
+ from django.http import HttpRequest
10
+
11
+
12
+ class EmailAsUsernameAccountAdapter(DefaultAccountAdapter): # type: ignore[misc]
13
+ """Automatically populate the username as the email address."""
14
+
15
+ def populate_username(self, request: HttpRequest, user: AbstractUser) -> None:
16
+ user.username = user.email
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.apps import AppConfig
2
4
 
3
5
 
@@ -31,12 +31,12 @@ class Command(createsuperuser.Command):
31
31
  with temporarily_change_attributes(self.username_field, _unique=True):
32
32
  # Normalize (as it would be done before saving) for better duplicate detection
33
33
  username = self.UserModel.normalize_username(username)
34
- return super()._validate_username( # type: ignore[misc]
34
+ return super()._validate_username( # type: ignore[misc,no-any-return]
35
35
  username, verbose_field_name, database
36
36
  )
37
37
 
38
38
 
39
- class EmailAsUsernameProxyUserManager(UserManager):
39
+ class EmailAsUsernameProxyUserManager(UserManager["EmailAsUsernameProxyUser"]):
40
40
  # This version of "create_superuser" makes the "username" argument optional
41
41
  def create_superuser(
42
42
  self,
@@ -46,16 +46,16 @@ class EmailAsUsernameProxyUserManager(UserManager):
46
46
  **extra_fields: Any,
47
47
  ) -> EmailAsUsernameProxyUser:
48
48
  # Practically, email will always be provided
49
- assert email
50
- user = super().create_superuser(
49
+ if email is None:
50
+ raise ValueError("Email address must be provided.")
51
+ return super().create_superuser(
51
52
  username=email, email=email, password=password, **extra_fields
52
53
  )
53
- return user
54
54
 
55
55
 
56
56
  class EmailAsUsernameProxyUser(User):
57
57
  # https://github.com/typeddjango/django-stubs/issues/2112
58
- class Meta(User.Meta): # type: ignore[name-defined]
58
+ class Meta(User.Meta): # type: ignore[misc,name-defined]
59
59
  proxy = True
60
60
 
61
61
  objects = EmailAsUsernameProxyUserManager()
@@ -1,15 +1,19 @@
1
- from typing import cast
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, cast
2
4
 
3
5
  from allauth.account import app_settings as allauth_settings
4
6
  from django.contrib.auth import get_user_model
5
7
  from django.contrib.auth.management.commands import createsuperuser as django_createsuperuser
6
- from django.contrib.auth.models import AbstractUser
7
- from django.core.management import BaseCommand
8
8
  from django.db.models.signals import post_save
9
9
 
10
10
  from resonant_settings.allauth_support import createsuperuser as allauth_support_createsuperuser
11
11
  from resonant_settings.allauth_support.receiver import verify_email_address_on_user_post_save
12
12
 
13
+ if TYPE_CHECKING:
14
+ from django.contrib.auth.models import AbstractUser
15
+ from django.core.management import BaseCommand
16
+
13
17
  """
14
18
  When Allauth is configured to use a User's `email` as the `username`, override the `createsuperuser`
15
19
  management command to only prompt for an email address.
@@ -28,7 +32,7 @@ if not username_required:
28
32
  else:
29
33
  # Expose the pristine upstream version of the command
30
34
  Command = django_createsuperuser.Command
31
- user_model = cast(type[AbstractUser], get_user_model())
35
+ user_model = cast("type[AbstractUser]", get_user_model())
32
36
 
33
37
  # Always automatically verify email addresses of newly created superusers
34
38
  post_save.connect(verify_email_address_on_user_post_save, sender=user_model)
@@ -1,13 +1,18 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
- from typing import Any
4
+ from typing import TYPE_CHECKING, Any
3
5
 
4
6
  from allauth.account.models import EmailAddress
5
- from django.contrib.auth.models import AbstractUser
6
7
 
7
- logger = logging.getLogger(__file__)
8
+ if TYPE_CHECKING:
9
+ from django.contrib.auth.models import AbstractUser
10
+
11
+ logger = logging.getLogger(__name__)
8
12
 
9
13
 
10
14
  def verify_email_address_on_user_post_save(
15
+ *,
11
16
  sender: type[AbstractUser],
12
17
  instance: AbstractUser,
13
18
  created: bool,
@@ -25,7 +30,7 @@ def verify_email_address_on_user_post_save(
25
30
  # uniqueness.
26
31
  # So, make a conservative effort at setting the email address as verified, since failure is
27
32
  # not critical and can be resolved by the user.
28
- email_address, created = EmailAddress.objects.get_or_create(
33
+ _email_address, created = EmailAddress.objects.get_or_create(
29
34
  email__iexact=instance.email,
30
35
  defaults={
31
36
  "user": instance,
@@ -35,4 +40,4 @@ def verify_email_address_on_user_post_save(
35
40
  },
36
41
  )
37
42
  if not created:
38
- logger.warning(f'Could not automatically verify email address "{instance.email}".')
43
+ logger.warning('Could not automatically verify email address "%s".', instance.email)
@@ -1,6 +1,10 @@
1
- from collections.abc import Generator
1
+ from __future__ import annotations
2
+
2
3
  from contextlib import contextmanager
3
- from typing import Any
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ if TYPE_CHECKING:
7
+ from collections.abc import Generator
4
8
 
5
9
 
6
10
  # From https://stackoverflow.com/a/38532086
@@ -1,5 +1,7 @@
1
1
  """
2
- Configure Celery with the following features:
2
+ Configure Celery.
3
+
4
+ This provides the following features:
3
5
  * Disable the results backend
4
6
  * Ensure that tasks will never be lost, but tasks themselves must be idempotent
5
7
  * Optimize the network connection to CloudAMQP
@@ -7,7 +9,9 @@ Configure Celery with the following features:
7
9
  This requires the `celery` package to be installed.
8
10
  """
9
11
 
10
- import celery.app.trace # type: ignore[import-not-found]
12
+ from __future__ import annotations
13
+
14
+ import celery.app.trace
11
15
 
12
16
  from resonant_settings._env import env
13
17
 
@@ -71,16 +75,16 @@ Task %(name)s[%(id)s] received: (%(args)s, %(kwargs)s)\
71
75
  """
72
76
 
73
77
  __all__ = [
78
+ "CELERY_BROKER_CONNECTION_TIMEOUT",
79
+ "CELERY_BROKER_HEARTBEAT",
80
+ "CELERY_BROKER_POOL_LIMIT",
74
81
  "CELERY_BROKER_URL",
82
+ "CELERY_EVENT_QUEUE_EXPIRES",
75
83
  "CELERY_RESULT_BACKEND",
76
84
  "CELERY_TASK_ACKS_LATE",
77
- "CELERY_TASK_REJECT_ON_WORKER_LOST",
78
85
  "CELERY_TASK_ACKS_ON_FAILURE_OR_TIMEOUT",
86
+ "CELERY_TASK_REJECT_ON_WORKER_LOST",
79
87
  "CELERY_WORKER_CANCEL_LONG_RUNNING_TASKS_ON_CONNECTION_LOSS",
80
- "CELERY_BROKER_POOL_LIMIT",
81
- "CELERY_BROKER_HEARTBEAT",
82
- "CELERY_BROKER_CONNECTION_TIMEOUT",
83
- "CELERY_EVENT_QUEUE_EXPIRES",
84
- "CELERY_WORKER_PREFETCH_MULTIPLIER",
85
88
  "CELERY_WORKER_CONCURRENCY",
89
+ "CELERY_WORKER_PREFETCH_MULTIPLIER",
86
90
  ]
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from resonant_settings._env import env
2
4
 
3
5
  # Acknowledge early in development, which will help prevent failing or
@@ -1,10 +1,14 @@
1
1
  """
2
- Configure Django Debug Toolbar with the following features:
2
+ Configure Django Debug Toolbar.
3
+
4
+ This provides the following features:
3
5
  * Improve performance with large queries
4
6
 
5
7
  This requires the `django-debug-toolbar` package to be installed.
6
8
  """
7
9
 
10
+ from __future__ import annotations
11
+
8
12
  from typing import Any
9
13
 
10
14
  DEBUG_TOOLBAR_CONFIG: dict[str, Any] = {
@@ -4,10 +4,15 @@ Configure MinioMediaStorage.
4
4
  This requires the `django-minio-storage` package to be installed.
5
5
  """
6
6
 
7
- from urllib.parse import ParseResult
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING
8
10
 
9
11
  from resonant_settings._env import env
10
12
 
13
+ if TYPE_CHECKING:
14
+ from urllib.parse import ParseResult
15
+
11
16
  minio_url: ParseResult = env.url("DJANGO_MINIO_STORAGE_URL")
12
17
  MINIO_STORAGE_USE_HTTPS = minio_url.scheme == "https"
13
18
  MINIO_STORAGE_ENDPOINT = (
@@ -28,13 +33,13 @@ MINIO_STORAGE_AUTO_CREATE_MEDIA_POLICY = "NONE"
28
33
  MINIO_STORAGE_MEDIA_USE_PRESIGNED = True
29
34
 
30
35
  __all__ = [
31
- "MINIO_STORAGE_USE_HTTPS",
32
- "MINIO_STORAGE_ENDPOINT",
33
36
  "MINIO_STORAGE_ACCESS_KEY",
34
- "MINIO_STORAGE_SECRET_KEY",
35
- "MINIO_STORAGE_MEDIA_BUCKET_NAME",
36
- "MINIO_STORAGE_MEDIA_URL",
37
37
  "MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET",
38
38
  "MINIO_STORAGE_AUTO_CREATE_MEDIA_POLICY",
39
+ "MINIO_STORAGE_ENDPOINT",
40
+ "MINIO_STORAGE_MEDIA_BUCKET_NAME",
41
+ "MINIO_STORAGE_MEDIA_URL",
39
42
  "MINIO_STORAGE_MEDIA_USE_PRESIGNED",
43
+ "MINIO_STORAGE_SECRET_KEY",
44
+ "MINIO_STORAGE_USE_HTTPS",
40
45
  ]
@@ -1,5 +1,7 @@
1
1
  """Configure a basic Django project."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from typing import Any
4
6
 
5
7
  TEMPLATES: list[dict[str, Any]] = [
@@ -37,7 +39,7 @@ AUTH_PASSWORD_VALIDATORS: list[dict[str, str]] = [
37
39
  ]
38
40
 
39
41
  __all__ = [
40
- "TEMPLATES",
41
- "PASSWORD_HASHERS",
42
42
  "AUTH_PASSWORD_VALIDATORS",
43
+ "PASSWORD_HASHERS",
44
+ "TEMPLATES",
43
45
  ]
@@ -4,12 +4,14 @@ Configure Django Extensions.
4
4
  This requires the `django-extensions` package to be installed.
5
5
  """
6
6
 
7
+ from __future__ import annotations
8
+
7
9
  SHELL_PLUS_PRINT_SQL = True
8
10
  SHELL_PLUS_PRINT_SQL_TRUNCATE = None
9
11
  RUNSERVER_PLUS_PRINT_SQL_TRUNCATE = None
10
12
 
11
13
  __all__ = [
14
+ "RUNSERVER_PLUS_PRINT_SQL_TRUNCATE",
12
15
  "SHELL_PLUS_PRINT_SQL",
13
16
  "SHELL_PLUS_PRINT_SQL_TRUNCATE",
14
- "RUNSERVER_PLUS_PRINT_SQL_TRUNCATE",
15
17
  ]
@@ -1,5 +1,7 @@
1
1
  """
2
- Configure Django logging with the following features:
2
+ Configure Django logging.
3
+
4
+ This provides the following features:
3
5
  * Emit all logs to stdout
4
6
  * Exclude favicons and static files from request and server logs
5
7
  * Improve log formatting and add colorization
@@ -7,9 +9,14 @@ Configure Django logging with the following features:
7
9
  This requires the `rich` package to be installed.
8
10
  """
9
11
 
10
- import logging
12
+ from __future__ import annotations
13
+
14
+ from typing import TYPE_CHECKING
15
+
16
+ if TYPE_CHECKING:
17
+ import logging
11
18
 
12
- from django.http import HttpRequest
19
+ from django.http import HttpRequest
13
20
 
14
21
 
15
22
  def _filter_favicon_requests(record: logging.LogRecord) -> bool:
@@ -18,7 +25,7 @@ def _filter_favicon_requests(record: logging.LogRecord) -> bool:
18
25
  if request and request.path == "/favicon.ico":
19
26
  return False
20
27
 
21
- if (
28
+ if ( # noqa: SIM103
22
29
  record.name == "django.server"
23
30
  and isinstance(record.args, tuple)
24
31
  and len(record.args) >= 1
@@ -30,7 +37,7 @@ def _filter_favicon_requests(record: logging.LogRecord) -> bool:
30
37
 
31
38
 
32
39
  def _filter_static_requests(record: logging.LogRecord) -> bool:
33
- if (
40
+ if ( # noqa: SIM103
34
41
  record.name == "django.server"
35
42
  and isinstance(record.args, tuple)
36
43
  and len(record.args) >= 1
@@ -1,5 +1,7 @@
1
1
  """
2
- Configure Django OAuth Toolkit with the following features:
2
+ Configure Django OAuth Toolkit.
3
+
4
+ This provides the following features:
3
5
  * Harden security
4
6
  * Improve usability of token scopes
5
7
  * Improve quality of live for out of band flows and non-refreshing clients
@@ -7,11 +9,16 @@ Configure Django OAuth Toolkit with the following features:
7
9
  This requires the `django-oauth-toolkit` package to be installed.
8
10
  """
9
11
 
12
+ from __future__ import annotations
13
+
10
14
  from datetime import timedelta
11
15
  from typing import Any
12
16
 
13
17
  OAUTH2_PROVIDER: dict[str, Any] = {
14
18
  "ALLOWED_REDIRECT_URI_SCHEMES": ["https"],
19
+ # This doesn't immediately change behavior, it just authorizes the use of wildcards in
20
+ # Application redirect URIs, which is necessary to support SPA branch previews
21
+ "ALLOW_URI_WILDCARDS": True,
15
22
  # Don't require users to re-approve scopes each time
16
23
  "REQUEST_APPROVAL_PROMPT": "auto",
17
24
  # ERROR_RESPONSE_WITH_SCOPES is only used with the "permission_classes" helpers for scopes.
@@ -8,6 +8,8 @@ The following environment variables must be externally set:
8
8
  * `DJANGO_DEFAULT_FROM_EMAIL`, as the default From address for outgoing email.
9
9
  """
10
10
 
11
+ from __future__ import annotations
12
+
11
13
  from typing import Any
12
14
 
13
15
  from resonant_settings._env import env
@@ -19,7 +21,7 @@ DEFAULT_FROM_EMAIL: str = env.str("DJANGO_DEFAULT_FROM_EMAIL")
19
21
  SERVER_EMAIL = DEFAULT_FROM_EMAIL
20
22
 
21
23
 
22
- __all__ = [
24
+ __all__ = [ # noqa: PLE0604
23
25
  *email_config.keys(),
24
26
  "DEFAULT_FROM_EMAIL",
25
27
  "SERVER_EMAIL",
@@ -1,5 +1,7 @@
1
1
  """Configure Django's security middleware to use and require HTTPS."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from datetime import timedelta
4
6
 
5
7
  SECURE_SSL_REDIRECT = True
@@ -18,10 +20,10 @@ SECURE_HSTS_INCLUDE_SUBDOMAINS = False
18
20
  SECURE_HSTS_PRELOAD = False
19
21
 
20
22
  __all__ = [
21
- "SECURE_SSL_REDIRECT",
22
- "SESSION_COOKIE_SECURE",
23
23
  "CSRF_COOKIE_SECURE",
24
- "SECURE_HSTS_SECONDS",
25
24
  "SECURE_HSTS_INCLUDE_SUBDOMAINS",
26
25
  "SECURE_HSTS_PRELOAD",
26
+ "SECURE_HSTS_SECONDS",
27
+ "SECURE_SSL_REDIRECT",
28
+ "SESSION_COOKIE_SECURE",
27
29
  ]
@@ -10,6 +10,8 @@ The following environment variables must be externally set:
10
10
  This requires the `django-storages[s3]` package to be installed.
11
11
  """
12
12
 
13
+ from __future__ import annotations
14
+
13
15
  from datetime import timedelta
14
16
 
15
17
  from resonant_settings._env import env
@@ -35,12 +37,12 @@ AWS_S3_FILE_OVERWRITE = True
35
37
  AWS_QUERYSTRING_EXPIRE = int(timedelta(hours=6).total_seconds())
36
38
 
37
39
  __all__ = [
38
- "AWS_S3_REGION_NAME",
40
+ "AWS_QUERYSTRING_EXPIRE",
39
41
  "AWS_S3_ACCESS_KEY_ID",
42
+ "AWS_S3_FILE_OVERWRITE",
43
+ "AWS_S3_MAX_MEMORY_SIZE",
44
+ "AWS_S3_REGION_NAME",
40
45
  "AWS_S3_SECRET_ACCESS_KEY",
41
- "AWS_STORAGE_BUCKET_NAME",
42
46
  "AWS_S3_SIGNATURE_VERSION",
43
- "AWS_S3_MAX_MEMORY_SIZE",
44
- "AWS_S3_FILE_OVERWRITE",
45
- "AWS_QUERYSTRING_EXPIRE",
47
+ "AWS_STORAGE_BUCKET_NAME",
46
48
  ]
@@ -4,6 +4,8 @@ Configure Django REST framework and drf-yasg.
4
4
  This requires the `django-oauth-toolkit` and `drf-yasg` packages to be installed.
5
5
  """
6
6
 
7
+ from __future__ import annotations
8
+
7
9
  from typing import Any
8
10
 
9
11
  # When SessionAuthentication is allowed, it's critical that the following settings
@@ -77,9 +79,9 @@ SWAGGER_SETTINGS: dict[str, Any] = {
77
79
  REDOC_SETTINGS: dict[str, Any] = {}
78
80
 
79
81
  __all__ = [
80
- "SESSION_COOKIE_SAMESITE",
81
82
  "CORS_ALLOW_CREDENTIALS",
83
+ "REDOC_SETTINGS",
82
84
  "REST_FRAMEWORK",
85
+ "SESSION_COOKIE_SAMESITE",
83
86
  "SWAGGER_SETTINGS",
84
- "REDOC_SETTINGS",
85
87
  ]
@@ -1,10 +0,0 @@
1
- from allauth.account.adapter import DefaultAccountAdapter
2
- from django.contrib.auth.models import AbstractUser
3
- from django.http import HttpRequest
4
-
5
-
6
- class EmailAsUsernameAccountAdapter(DefaultAccountAdapter):
7
- """Automatically populate the username as the email address."""
8
-
9
- def populate_username(self, request: HttpRequest, user: AbstractUser) -> None:
10
- user.username = user.email