plain 0.1.0__py3-none-any.whl

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 (169) hide show
  1. plain/README.md +33 -0
  2. plain/__main__.py +5 -0
  3. plain/assets/README.md +56 -0
  4. plain/assets/__init__.py +6 -0
  5. plain/assets/finders.py +233 -0
  6. plain/assets/preflight.py +14 -0
  7. plain/assets/storage.py +916 -0
  8. plain/assets/utils.py +52 -0
  9. plain/assets/whitenoise/__init__.py +5 -0
  10. plain/assets/whitenoise/base.py +259 -0
  11. plain/assets/whitenoise/compress.py +189 -0
  12. plain/assets/whitenoise/media_types.py +137 -0
  13. plain/assets/whitenoise/middleware.py +197 -0
  14. plain/assets/whitenoise/responders.py +286 -0
  15. plain/assets/whitenoise/storage.py +178 -0
  16. plain/assets/whitenoise/string_utils.py +13 -0
  17. plain/cli/README.md +123 -0
  18. plain/cli/__init__.py +3 -0
  19. plain/cli/cli.py +439 -0
  20. plain/cli/formatting.py +61 -0
  21. plain/cli/packages.py +73 -0
  22. plain/cli/print.py +9 -0
  23. plain/cli/startup.py +33 -0
  24. plain/csrf/README.md +3 -0
  25. plain/csrf/middleware.py +466 -0
  26. plain/csrf/views.py +10 -0
  27. plain/debug.py +23 -0
  28. plain/exceptions.py +242 -0
  29. plain/forms/README.md +14 -0
  30. plain/forms/__init__.py +8 -0
  31. plain/forms/boundfield.py +58 -0
  32. plain/forms/exceptions.py +11 -0
  33. plain/forms/fields.py +1030 -0
  34. plain/forms/forms.py +297 -0
  35. plain/http/README.md +1 -0
  36. plain/http/__init__.py +51 -0
  37. plain/http/cookie.py +20 -0
  38. plain/http/multipartparser.py +743 -0
  39. plain/http/request.py +754 -0
  40. plain/http/response.py +719 -0
  41. plain/internal/__init__.py +0 -0
  42. plain/internal/files/README.md +3 -0
  43. plain/internal/files/__init__.py +3 -0
  44. plain/internal/files/base.py +161 -0
  45. plain/internal/files/locks.py +127 -0
  46. plain/internal/files/move.py +102 -0
  47. plain/internal/files/temp.py +79 -0
  48. plain/internal/files/uploadedfile.py +150 -0
  49. plain/internal/files/uploadhandler.py +254 -0
  50. plain/internal/files/utils.py +78 -0
  51. plain/internal/handlers/__init__.py +0 -0
  52. plain/internal/handlers/base.py +133 -0
  53. plain/internal/handlers/exception.py +145 -0
  54. plain/internal/handlers/wsgi.py +216 -0
  55. plain/internal/legacy/__init__.py +0 -0
  56. plain/internal/legacy/__main__.py +12 -0
  57. plain/internal/legacy/management/__init__.py +414 -0
  58. plain/internal/legacy/management/base.py +692 -0
  59. plain/internal/legacy/management/color.py +113 -0
  60. plain/internal/legacy/management/commands/__init__.py +0 -0
  61. plain/internal/legacy/management/commands/collectstatic.py +297 -0
  62. plain/internal/legacy/management/sql.py +67 -0
  63. plain/internal/legacy/management/utils.py +175 -0
  64. plain/json.py +40 -0
  65. plain/logs/README.md +24 -0
  66. plain/logs/__init__.py +5 -0
  67. plain/logs/configure.py +39 -0
  68. plain/logs/loggers.py +74 -0
  69. plain/logs/utils.py +46 -0
  70. plain/middleware/README.md +3 -0
  71. plain/middleware/__init__.py +0 -0
  72. plain/middleware/clickjacking.py +52 -0
  73. plain/middleware/common.py +87 -0
  74. plain/middleware/gzip.py +64 -0
  75. plain/middleware/security.py +64 -0
  76. plain/packages/README.md +41 -0
  77. plain/packages/__init__.py +4 -0
  78. plain/packages/config.py +259 -0
  79. plain/packages/registry.py +438 -0
  80. plain/paginator.py +187 -0
  81. plain/preflight/README.md +3 -0
  82. plain/preflight/__init__.py +38 -0
  83. plain/preflight/compatibility/__init__.py +0 -0
  84. plain/preflight/compatibility/django_4_0.py +20 -0
  85. plain/preflight/files.py +19 -0
  86. plain/preflight/messages.py +88 -0
  87. plain/preflight/registry.py +72 -0
  88. plain/preflight/security/__init__.py +0 -0
  89. plain/preflight/security/base.py +268 -0
  90. plain/preflight/security/csrf.py +40 -0
  91. plain/preflight/urls.py +117 -0
  92. plain/runtime/README.md +75 -0
  93. plain/runtime/__init__.py +61 -0
  94. plain/runtime/global_settings.py +199 -0
  95. plain/runtime/user_settings.py +353 -0
  96. plain/signals/README.md +14 -0
  97. plain/signals/__init__.py +5 -0
  98. plain/signals/dispatch/__init__.py +9 -0
  99. plain/signals/dispatch/dispatcher.py +320 -0
  100. plain/signals/dispatch/license.txt +35 -0
  101. plain/signing.py +299 -0
  102. plain/templates/README.md +20 -0
  103. plain/templates/__init__.py +6 -0
  104. plain/templates/core.py +24 -0
  105. plain/templates/jinja/README.md +227 -0
  106. plain/templates/jinja/__init__.py +22 -0
  107. plain/templates/jinja/defaults.py +119 -0
  108. plain/templates/jinja/extensions.py +39 -0
  109. plain/templates/jinja/filters.py +28 -0
  110. plain/templates/jinja/globals.py +19 -0
  111. plain/test/README.md +3 -0
  112. plain/test/__init__.py +16 -0
  113. plain/test/client.py +985 -0
  114. plain/test/utils.py +255 -0
  115. plain/urls/README.md +3 -0
  116. plain/urls/__init__.py +40 -0
  117. plain/urls/base.py +118 -0
  118. plain/urls/conf.py +94 -0
  119. plain/urls/converters.py +66 -0
  120. plain/urls/exceptions.py +9 -0
  121. plain/urls/resolvers.py +731 -0
  122. plain/utils/README.md +3 -0
  123. plain/utils/__init__.py +0 -0
  124. plain/utils/_os.py +52 -0
  125. plain/utils/cache.py +327 -0
  126. plain/utils/connection.py +84 -0
  127. plain/utils/crypto.py +76 -0
  128. plain/utils/datastructures.py +345 -0
  129. plain/utils/dateformat.py +329 -0
  130. plain/utils/dateparse.py +154 -0
  131. plain/utils/dates.py +76 -0
  132. plain/utils/deconstruct.py +54 -0
  133. plain/utils/decorators.py +90 -0
  134. plain/utils/deprecation.py +6 -0
  135. plain/utils/duration.py +44 -0
  136. plain/utils/email.py +12 -0
  137. plain/utils/encoding.py +235 -0
  138. plain/utils/functional.py +456 -0
  139. plain/utils/hashable.py +26 -0
  140. plain/utils/html.py +401 -0
  141. plain/utils/http.py +374 -0
  142. plain/utils/inspect.py +73 -0
  143. plain/utils/ipv6.py +46 -0
  144. plain/utils/itercompat.py +8 -0
  145. plain/utils/module_loading.py +69 -0
  146. plain/utils/regex_helper.py +353 -0
  147. plain/utils/safestring.py +72 -0
  148. plain/utils/termcolors.py +221 -0
  149. plain/utils/text.py +518 -0
  150. plain/utils/timesince.py +138 -0
  151. plain/utils/timezone.py +244 -0
  152. plain/utils/tree.py +126 -0
  153. plain/validators.py +603 -0
  154. plain/views/README.md +268 -0
  155. plain/views/__init__.py +18 -0
  156. plain/views/base.py +107 -0
  157. plain/views/csrf.py +24 -0
  158. plain/views/errors.py +25 -0
  159. plain/views/exceptions.py +4 -0
  160. plain/views/forms.py +76 -0
  161. plain/views/objects.py +229 -0
  162. plain/views/redirect.py +72 -0
  163. plain/views/templates.py +66 -0
  164. plain/wsgi.py +11 -0
  165. plain-0.1.0.dist-info/LICENSE +85 -0
  166. plain-0.1.0.dist-info/METADATA +51 -0
  167. plain-0.1.0.dist-info/RECORD +169 -0
  168. plain-0.1.0.dist-info/WHEEL +4 -0
  169. plain-0.1.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,40 @@
1
+ from plain.runtime import settings
2
+
3
+ from .. import Warning, register
4
+
5
+ W003 = Warning(
6
+ "You don't appear to be using Plain's built-in "
7
+ "cross-site request forgery protection via the middleware "
8
+ "('plain.csrf.middleware.CsrfViewMiddleware' is not in your "
9
+ "MIDDLEWARE). Enabling the middleware is the safest approach "
10
+ "to ensure you don't leave any holes.",
11
+ id="security.W003",
12
+ )
13
+
14
+ W016 = Warning(
15
+ "You have 'plain.csrf.middleware.CsrfViewMiddleware' in your "
16
+ "MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. "
17
+ "Using a secure-only CSRF cookie makes it more difficult for network "
18
+ "traffic sniffers to steal the CSRF token.",
19
+ id="security.W016",
20
+ )
21
+
22
+
23
+ def _csrf_middleware():
24
+ return "plain.csrf.middleware.CsrfViewMiddleware" in settings.MIDDLEWARE
25
+
26
+
27
+ @register(deploy=True)
28
+ def check_csrf_middleware(package_configs, **kwargs):
29
+ passed_check = _csrf_middleware()
30
+ return [] if passed_check else [W003]
31
+
32
+
33
+ @register(deploy=True)
34
+ def check_csrf_cookie_secure(package_configs, **kwargs):
35
+ passed_check = (
36
+ settings.CSRF_USE_SESSIONS
37
+ or not _csrf_middleware()
38
+ or settings.CSRF_COOKIE_SECURE is True
39
+ )
40
+ return [] if passed_check else [W016]
@@ -0,0 +1,117 @@
1
+ from collections import Counter
2
+
3
+ from plain.runtime import settings
4
+
5
+ from . import Error, Warning, register
6
+
7
+
8
+ @register
9
+ def check_url_config(package_configs, **kwargs):
10
+ if getattr(settings, "ROOT_URLCONF", None):
11
+ from plain.urls import get_resolver
12
+
13
+ resolver = get_resolver()
14
+ return check_resolver(resolver)
15
+ return []
16
+
17
+
18
+ def check_resolver(resolver):
19
+ """
20
+ Recursively check the resolver.
21
+ """
22
+ check_method = getattr(resolver, "check", None)
23
+ if check_method is not None:
24
+ return check_method()
25
+ elif not hasattr(resolver, "resolve"):
26
+ return get_warning_for_invalid_pattern(resolver)
27
+ else:
28
+ return []
29
+
30
+
31
+ @register
32
+ def check_url_namespaces_unique(package_configs, **kwargs):
33
+ """
34
+ Warn if URL namespaces used in applications aren't unique.
35
+ """
36
+ if not getattr(settings, "ROOT_URLCONF", None):
37
+ return []
38
+
39
+ from plain.urls import get_resolver
40
+
41
+ resolver = get_resolver()
42
+ all_namespaces = _load_all_namespaces(resolver)
43
+ counter = Counter(all_namespaces)
44
+ non_unique_namespaces = [n for n, count in counter.items() if count > 1]
45
+ errors = []
46
+ for namespace in non_unique_namespaces:
47
+ errors.append(
48
+ Warning(
49
+ "URL namespace '{}' isn't unique. You may not be able to reverse "
50
+ "all URLs in this namespace".format(namespace),
51
+ id="urls.W005",
52
+ )
53
+ )
54
+ return errors
55
+
56
+
57
+ def _load_all_namespaces(resolver, parents=()):
58
+ """
59
+ Recursively load all namespaces from URL patterns.
60
+ """
61
+ url_patterns = getattr(resolver, "url_patterns", [])
62
+ namespaces = [
63
+ ":".join(parents + (url.namespace,))
64
+ for url in url_patterns
65
+ if getattr(url, "namespace", None) is not None
66
+ ]
67
+ for pattern in url_patterns:
68
+ namespace = getattr(pattern, "namespace", None)
69
+ current = parents
70
+ if namespace is not None:
71
+ current += (namespace,)
72
+ namespaces.extend(_load_all_namespaces(pattern, current))
73
+ return namespaces
74
+
75
+
76
+ def get_warning_for_invalid_pattern(pattern):
77
+ """
78
+ Return a list containing a warning that the pattern is invalid.
79
+
80
+ describe_pattern() cannot be used here, because we cannot rely on the
81
+ urlpattern having regex or name attributes.
82
+ """
83
+ if isinstance(pattern, str):
84
+ hint = (
85
+ f"Try removing the string '{pattern}'. The list of urlpatterns should not "
86
+ "have a prefix string as the first element."
87
+ )
88
+ elif isinstance(pattern, tuple):
89
+ hint = "Try using path() instead of a tuple."
90
+ else:
91
+ hint = None
92
+
93
+ return [
94
+ Error(
95
+ "Your URL pattern {!r} is invalid. Ensure that urlpatterns is a list "
96
+ "of path() and/or re_path() instances.".format(pattern),
97
+ hint=hint,
98
+ id="urls.E004",
99
+ )
100
+ ]
101
+
102
+
103
+ @register
104
+ def check_url_settings(package_configs, **kwargs):
105
+ errors = []
106
+ for name in ("ASSETS_URL",):
107
+ value = getattr(settings, name)
108
+ if value and not value.endswith("/"):
109
+ errors.append(E006(name))
110
+ return errors
111
+
112
+
113
+ def E006(name):
114
+ return Error(
115
+ f"The {name} setting must end with a slash.",
116
+ id="urls.E006",
117
+ )
@@ -0,0 +1,75 @@
1
+ # Runtime
2
+
3
+ Leverage user-settings at runtime.
4
+
5
+ ## Settings
6
+
7
+ ### Single-file
8
+
9
+ All of your settings go in `app/settings.py`.
10
+ That's how you do it!
11
+
12
+ The file itself is not much different than how Django does it,
13
+ but the location,
14
+ and a strong recommendation to only use the one file makes a big difference.
15
+
16
+ ### Environment variables
17
+
18
+ It seems pretty well-accepted these days that storing settings in env vars is a good idea ([12factor.net](https://12factor.net/config)).
19
+
20
+ Your settings file should be looking at the environment for secrets or other values that might change between environments. For example:
21
+
22
+ ```python
23
+ # app/settings.py
24
+ STRIPE_SECRET_KEY = environ["STRIPE_SECRET_KEY"]
25
+ ```
26
+
27
+ #### Local development
28
+
29
+ In local development,
30
+ you should use `.env` files to set these values.
31
+ The `.env` should then be in your `.gitignore`!
32
+
33
+ It would seem like `.env.dev` would be a good idea,
34
+ but there's a chicken-and-egg problem with that.
35
+ You would then have to prefix most (or all) of your local commands with `PLAIN_ENV=dev` or otherwise configure your environment to do that for you.
36
+ Generally speaking,
37
+ a production `.env` shouldn't be committed in your repo anyway,
38
+ so using `.env` for local development is ok.
39
+ The downside to this is that it's harder to share your local settings with others,
40
+ but these also often contain real secrets which shouldn't be committed to your repo either!
41
+ More advanced `.env` sharing patterns are currently beyond the scope of Plain...
42
+
43
+ #### Production
44
+
45
+ TODO
46
+
47
+ ## Minimum required settings
48
+
49
+ ```python
50
+ # SECURITY WARNING: keep the secret key used in production secret!
51
+ SECRET_KEY = environ["SECRET_KEY"]
52
+
53
+ # SECURITY WARNING: don't run with debug turned on in production!
54
+ DEBUG = environ.get("DEBUG", "false").lower() in ("true", "1", "yes")
55
+
56
+ MIDDLEWARE = [
57
+ "plain.middleware.security.SecurityMiddleware",
58
+ "plain.assets.whitenoise.middleware.WhiteNoiseMiddleware",
59
+ "plain.sessions.middleware.SessionMiddleware",
60
+ "plain.middleware.common.CommonMiddleware",
61
+ "plain.csrf.middleware.CsrfViewMiddleware",
62
+ "plain.auth.middleware.AuthenticationMiddleware",
63
+ "plain.middleware.clickjacking.XFrameOptionsMiddleware",
64
+ ]
65
+
66
+ if DEBUG:
67
+ INSTALLED_PACKAGES += [
68
+ "plain.dev",
69
+ ]
70
+ MIDDLEWARE += [
71
+ "plain.dev.RequestsMiddleware",
72
+ ]
73
+
74
+ TIME_ZONE = "America/Chicago"
75
+ ```
@@ -0,0 +1,61 @@
1
+ import importlib.metadata
2
+ import sys
3
+ from os import environ
4
+ from pathlib import Path
5
+
6
+ from dotenv import load_dotenv
7
+
8
+ from .user_settings import LazySettings
9
+
10
+ try:
11
+ __version__ = importlib.metadata.version("plainframework")
12
+ except importlib.metadata.PackageNotFoundError:
13
+ __version__ = "dev"
14
+
15
+
16
+ # Made available without setup or settings
17
+ APP_PATH = Path.cwd() / "app"
18
+
19
+
20
+ # from plain.runtime import settings
21
+ settings = LazySettings()
22
+
23
+
24
+ class AppPathNotFound(RuntimeError):
25
+ pass
26
+
27
+
28
+ def setup():
29
+ """
30
+ Configure the settings (this happens as a side effect of accessing the
31
+ first setting), configure logging and populate the app registry.
32
+ """
33
+ from plain.logs import configure_logging
34
+ from plain.packages import packages
35
+
36
+ if not APP_PATH.exists():
37
+ raise AppPathNotFound(
38
+ "No app directory found. Are you sure you're in a Plain project?"
39
+ )
40
+
41
+ # Automatically put the app dir on the Python path for convenience
42
+ if APP_PATH not in sys.path:
43
+ sys.path.insert(0, APP_PATH.as_posix())
44
+
45
+ # Load .env files automatically before settings
46
+ if app_env := environ.get("PLAIN_ENV", ""):
47
+ load_dotenv(f".env.{app_env}")
48
+ else:
49
+ load_dotenv(".env")
50
+
51
+ configure_logging(settings.LOGGING)
52
+
53
+ packages.populate(settings.INSTALLED_PACKAGES)
54
+
55
+
56
+ __all__ = [
57
+ "setup",
58
+ "settings",
59
+ "APP_PATH",
60
+ "__version__",
61
+ ]
@@ -0,0 +1,199 @@
1
+ """
2
+ Default Plain settings. Override these with settings in the module pointed to
3
+ by the PLAIN_SETTINGS_MODULE environment variable.
4
+ """
5
+ from pathlib import Path
6
+
7
+ from plain.runtime import APP_PATH as default_app_path
8
+
9
+ ####################
10
+ # CORE #
11
+ ####################
12
+
13
+ DEBUG: bool = False
14
+
15
+ PLAIN_TEMP_PATH: Path = default_app_path.parent / ".plain"
16
+
17
+ # Hosts/domain names that are valid for this site.
18
+ # "*" matches anything, ".example.com" matches example.com and all subdomains
19
+ ALLOWED_HOSTS: list[str] = []
20
+
21
+ # Local time zone for this installation. All choices can be found here:
22
+ # https://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
23
+ # systems may support all possibilities). When USE_TZ is True, this is
24
+ # interpreted as the default user time zone.
25
+ TIME_ZONE = "America/Chicago"
26
+
27
+ # If you set this to True, Plain will use timezone-aware datetimes.
28
+ USE_TZ = True
29
+
30
+ # Default charset to use for all Response objects, if a MIME type isn't
31
+ # manually specified. It's used to construct the Content-Type header.
32
+ DEFAULT_CHARSET = "utf-8"
33
+
34
+ # List of strings representing installed packages.
35
+ INSTALLED_PACKAGES: list = []
36
+
37
+ # Whether to append trailing slashes to URLs.
38
+ APPEND_SLASH = True
39
+
40
+ # A secret key for this particular Plain installation. Used in secret-key
41
+ # hashing algorithms. Set this in your settings, or Plain will complain
42
+ # loudly.
43
+ SECRET_KEY: str
44
+
45
+ # List of secret keys used to verify the validity of signatures. This allows
46
+ # secret key rotation.
47
+ SECRET_KEY_FALLBACKS: list[str] = []
48
+
49
+ ROOT_URLCONF = "urls"
50
+
51
+ # List of upload handler classes to be applied in order.
52
+ FILE_UPLOAD_HANDLERS = [
53
+ "plain.internal.files.uploadhandler.MemoryFileUploadHandler",
54
+ "plain.internal.files.uploadhandler.TemporaryFileUploadHandler",
55
+ ]
56
+
57
+ # Maximum size, in bytes, of a request before it will be streamed to the
58
+ # file system instead of into memory.
59
+ FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 # i.e. 2.5 MB
60
+
61
+ # Maximum size in bytes of request data (excluding file uploads) that will be
62
+ # read before a SuspiciousOperation (RequestDataTooBig) is raised.
63
+ DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440 # i.e. 2.5 MB
64
+
65
+ # Maximum number of GET/POST parameters that will be read before a
66
+ # SuspiciousOperation (TooManyFieldsSent) is raised.
67
+ DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000
68
+
69
+ # Maximum number of files encoded in a multipart upload that will be read
70
+ # before a SuspiciousOperation (TooManyFilesSent) is raised.
71
+ DATA_UPLOAD_MAX_NUMBER_FILES = 100
72
+
73
+ # Directory in which upload streamed files will be temporarily saved. A value of
74
+ # `None` will make Plain use the operating system's default temporary directory
75
+ # (i.e. "/tmp" on *nix systems).
76
+ FILE_UPLOAD_TEMP_DIR = None
77
+
78
+ # The numeric mode to set newly-uploaded files to. The value should be a mode
79
+ # you'd pass directly to os.chmod; see
80
+ # https://docs.python.org/library/os.html#files-and-directories.
81
+ FILE_UPLOAD_PERMISSIONS = 0o644
82
+
83
+ # The numeric mode to assign to newly-created directories, when uploading files.
84
+ # The value should be a mode as you'd pass to os.chmod;
85
+ # see https://docs.python.org/library/os.html#files-and-directories.
86
+ FILE_UPLOAD_DIRECTORY_PERMISSIONS = None
87
+
88
+ # Default X-Frame-Options header value
89
+ X_FRAME_OPTIONS = "DENY"
90
+
91
+ USE_X_FORWARDED_HOST = False
92
+ USE_X_FORWARDED_PORT = False
93
+
94
+ # User-defined overrides for error views by status code
95
+ HTTP_ERROR_VIEWS: dict[int] = {}
96
+
97
+ # If your Plain app is behind a proxy that sets a header to specify secure
98
+ # connections, AND that proxy ensures that user-submitted headers with the
99
+ # same name are ignored (so that people can't spoof it), set this value to
100
+ # a tuple of (header_name, header_value). For any requests that come in with
101
+ # that header/value, request.is_secure() will return True.
102
+ # WARNING! Only set this if you fully understand what you're doing. Otherwise,
103
+ # you may be opening yourself up to a security risk.
104
+ SECURE_PROXY_SSL_HEADER = None
105
+
106
+ ##############
107
+ # MIDDLEWARE #
108
+ ##############
109
+
110
+ # List of middleware to use. Order is important; in the request phase, these
111
+ # middleware will be applied in the order given, and in the response
112
+ # phase the middleware will be applied in reverse order.
113
+ MIDDLEWARE = [
114
+ "plain.middleware.security.SecurityMiddleware",
115
+ "plain.assets.whitenoise.middleware.WhiteNoiseMiddleware",
116
+ "plain.middleware.common.CommonMiddleware",
117
+ "plain.csrf.middleware.CsrfViewMiddleware",
118
+ "plain.middleware.clickjacking.XFrameOptionsMiddleware",
119
+ ]
120
+
121
+ ###########
122
+ # SIGNING #
123
+ ###########
124
+
125
+ SIGNING_BACKEND = "plain.signing.TimestampSigner"
126
+
127
+ ########
128
+ # CSRF #
129
+ ########
130
+
131
+ # Settings for CSRF cookie.
132
+ CSRF_COOKIE_NAME = "csrftoken"
133
+ CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52
134
+ CSRF_COOKIE_DOMAIN = None
135
+ CSRF_COOKIE_PATH = "/"
136
+ CSRF_COOKIE_SECURE = False
137
+ CSRF_COOKIE_HTTPONLY = False
138
+ CSRF_COOKIE_SAMESITE = "Lax"
139
+ CSRF_HEADER_NAME = "HTTP_X_CSRFTOKEN"
140
+ CSRF_TRUSTED_ORIGINS: list[str] = []
141
+ CSRF_USE_SESSIONS = False
142
+
143
+ ###########
144
+ # LOGGING #
145
+ ###########
146
+
147
+ # Custom logging configuration.
148
+ LOGGING = {}
149
+
150
+ ###############
151
+ # ASSETS #
152
+ ###############
153
+
154
+ ASSETS_BACKEND = "plain.assets.whitenoise.storage.CompressedManifestStaticFilesStorage"
155
+
156
+ # List of finder classes that know how to find assets files in
157
+ # various locations.
158
+ ASSETS_FINDERS = [
159
+ "plain.assets.finders.FileSystemFinder",
160
+ "plain.assets.finders.PackageDirectoriesFinder",
161
+ ]
162
+
163
+ # Absolute path to the directory assets files should be collected to.
164
+ # Example: "/var/www/example.com/assets/"
165
+ ASSETS_ROOT = PLAIN_TEMP_PATH / "assets_collected"
166
+
167
+ # URL that handles the assets files served from ASSETS_ROOT.
168
+ # Example: "http://example.com/assets/", "http://assets.example.com/"
169
+ ASSETS_URL = "/assets/"
170
+
171
+ ####################
172
+ # PREFLIGHT CHECKS #
173
+ ####################
174
+
175
+ # List of all issues generated by system checks that should be silenced. Light
176
+ # issues like warnings, infos or debugs will not generate a message. Silencing
177
+ # serious issues like errors and criticals does not result in hiding the
178
+ # message, but Plain will not stop you from e.g. running server.
179
+ SILENCED_PREFLIGHT_CHECKS = []
180
+
181
+ #######################
182
+ # SECURITY MIDDLEWARE #
183
+ #######################
184
+ SECURE_CONTENT_TYPE_NOSNIFF = True
185
+ SECURE_CROSS_ORIGIN_OPENER_POLICY = "same-origin"
186
+ SECURE_HSTS_INCLUDE_SUBDOMAINS = False
187
+ SECURE_HSTS_PRELOAD = False
188
+ SECURE_HSTS_SECONDS = 0
189
+ SECURE_REDIRECT_EXEMPT = []
190
+ SECURE_REFERRER_POLICY = "same-origin"
191
+ SECURE_SSL_HOST = None
192
+ SECURE_SSL_REDIRECT = False
193
+
194
+ #############
195
+ # Templates #
196
+ #############
197
+
198
+ JINJA_LOADER = "jinja2.loaders.FileSystemLoader"
199
+ JINJA_ENVIRONMENT = "plain.templates.jinja.defaults.create_default_environment"