plain 0.56.1__tar.gz → 0.57.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 (170) hide show
  1. {plain-0.56.1 → plain-0.57.0}/PKG-INFO +1 -1
  2. {plain-0.56.1 → plain-0.57.0}/plain/CHANGELOG.md +15 -0
  3. {plain-0.56.1 → plain-0.57.0}/plain/exceptions.py +0 -6
  4. {plain-0.56.1 → plain-0.57.0}/plain/http/__init__.py +0 -2
  5. {plain-0.56.1 → plain-0.57.0}/plain/http/response.py +7 -22
  6. {plain-0.56.1 → plain-0.57.0}/plain/internal/middleware/https.py +4 -2
  7. {plain-0.56.1 → plain-0.57.0}/plain/internal/middleware/slash.py +4 -2
  8. plain-0.57.0/plain/views/redirect.py +66 -0
  9. {plain-0.56.1 → plain-0.57.0}/pyproject.toml +1 -1
  10. plain-0.56.1/plain/views/redirect.py +0 -77
  11. {plain-0.56.1 → plain-0.57.0}/.gitignore +0 -0
  12. {plain-0.56.1 → plain-0.57.0}/LICENSE +0 -0
  13. {plain-0.56.1 → plain-0.57.0}/README.md +0 -0
  14. {plain-0.56.1 → plain-0.57.0}/plain/README.md +0 -0
  15. {plain-0.56.1 → plain-0.57.0}/plain/__main__.py +0 -0
  16. {plain-0.56.1 → plain-0.57.0}/plain/assets/README.md +0 -0
  17. {plain-0.56.1 → plain-0.57.0}/plain/assets/__init__.py +0 -0
  18. {plain-0.56.1 → plain-0.57.0}/plain/assets/compile.py +0 -0
  19. {plain-0.56.1 → plain-0.57.0}/plain/assets/finders.py +0 -0
  20. {plain-0.56.1 → plain-0.57.0}/plain/assets/fingerprints.py +0 -0
  21. {plain-0.56.1 → plain-0.57.0}/plain/assets/urls.py +0 -0
  22. {plain-0.56.1 → plain-0.57.0}/plain/assets/views.py +0 -0
  23. {plain-0.56.1 → plain-0.57.0}/plain/chores/README.md +0 -0
  24. {plain-0.56.1 → plain-0.57.0}/plain/chores/__init__.py +0 -0
  25. {plain-0.56.1 → plain-0.57.0}/plain/chores/registry.py +0 -0
  26. {plain-0.56.1 → plain-0.57.0}/plain/cli/README.md +0 -0
  27. {plain-0.56.1 → plain-0.57.0}/plain/cli/__init__.py +0 -0
  28. {plain-0.56.1 → plain-0.57.0}/plain/cli/agent.py +0 -0
  29. {plain-0.56.1 → plain-0.57.0}/plain/cli/build.py +0 -0
  30. {plain-0.56.1 → plain-0.57.0}/plain/cli/changelog.py +0 -0
  31. {plain-0.56.1 → plain-0.57.0}/plain/cli/chores.py +0 -0
  32. {plain-0.56.1 → plain-0.57.0}/plain/cli/core.py +0 -0
  33. {plain-0.56.1 → plain-0.57.0}/plain/cli/docs.py +0 -0
  34. {plain-0.56.1 → plain-0.57.0}/plain/cli/formatting.py +0 -0
  35. {plain-0.56.1 → plain-0.57.0}/plain/cli/help.py +0 -0
  36. {plain-0.56.1 → plain-0.57.0}/plain/cli/install.py +0 -0
  37. {plain-0.56.1 → plain-0.57.0}/plain/cli/output.py +0 -0
  38. {plain-0.56.1 → plain-0.57.0}/plain/cli/preflight.py +0 -0
  39. {plain-0.56.1 → plain-0.57.0}/plain/cli/print.py +0 -0
  40. {plain-0.56.1 → plain-0.57.0}/plain/cli/registry.py +0 -0
  41. {plain-0.56.1 → plain-0.57.0}/plain/cli/scaffold.py +0 -0
  42. {plain-0.56.1 → plain-0.57.0}/plain/cli/settings.py +0 -0
  43. {plain-0.56.1 → plain-0.57.0}/plain/cli/shell.py +0 -0
  44. {plain-0.56.1 → plain-0.57.0}/plain/cli/startup.py +0 -0
  45. {plain-0.56.1 → plain-0.57.0}/plain/cli/upgrade.py +0 -0
  46. {plain-0.56.1 → plain-0.57.0}/plain/cli/urls.py +0 -0
  47. {plain-0.56.1 → plain-0.57.0}/plain/cli/utils.py +0 -0
  48. {plain-0.56.1 → plain-0.57.0}/plain/csrf/README.md +0 -0
  49. {plain-0.56.1 → plain-0.57.0}/plain/csrf/middleware.py +0 -0
  50. {plain-0.56.1 → plain-0.57.0}/plain/csrf/views.py +0 -0
  51. {plain-0.56.1 → plain-0.57.0}/plain/debug.py +0 -0
  52. {plain-0.56.1 → plain-0.57.0}/plain/forms/README.md +0 -0
  53. {plain-0.56.1 → plain-0.57.0}/plain/forms/__init__.py +0 -0
  54. {plain-0.56.1 → plain-0.57.0}/plain/forms/boundfield.py +0 -0
  55. {plain-0.56.1 → plain-0.57.0}/plain/forms/exceptions.py +0 -0
  56. {plain-0.56.1 → plain-0.57.0}/plain/forms/fields.py +0 -0
  57. {plain-0.56.1 → plain-0.57.0}/plain/forms/forms.py +0 -0
  58. {plain-0.56.1 → plain-0.57.0}/plain/http/README.md +0 -0
  59. {plain-0.56.1 → plain-0.57.0}/plain/http/cookie.py +0 -0
  60. {plain-0.56.1 → plain-0.57.0}/plain/http/multipartparser.py +0 -0
  61. {plain-0.56.1 → plain-0.57.0}/plain/http/request.py +0 -0
  62. {plain-0.56.1 → plain-0.57.0}/plain/internal/__init__.py +0 -0
  63. {plain-0.56.1 → plain-0.57.0}/plain/internal/files/__init__.py +0 -0
  64. {plain-0.56.1 → plain-0.57.0}/plain/internal/files/base.py +0 -0
  65. {plain-0.56.1 → plain-0.57.0}/plain/internal/files/locks.py +0 -0
  66. {plain-0.56.1 → plain-0.57.0}/plain/internal/files/move.py +0 -0
  67. {plain-0.56.1 → plain-0.57.0}/plain/internal/files/temp.py +0 -0
  68. {plain-0.56.1 → plain-0.57.0}/plain/internal/files/uploadedfile.py +0 -0
  69. {plain-0.56.1 → plain-0.57.0}/plain/internal/files/uploadhandler.py +0 -0
  70. {plain-0.56.1 → plain-0.57.0}/plain/internal/files/utils.py +0 -0
  71. {plain-0.56.1 → plain-0.57.0}/plain/internal/handlers/__init__.py +0 -0
  72. {plain-0.56.1 → plain-0.57.0}/plain/internal/handlers/base.py +0 -0
  73. {plain-0.56.1 → plain-0.57.0}/plain/internal/handlers/exception.py +0 -0
  74. {plain-0.56.1 → plain-0.57.0}/plain/internal/handlers/wsgi.py +0 -0
  75. {plain-0.56.1 → plain-0.57.0}/plain/internal/middleware/__init__.py +0 -0
  76. {plain-0.56.1 → plain-0.57.0}/plain/internal/middleware/headers.py +0 -0
  77. {plain-0.56.1 → plain-0.57.0}/plain/json.py +0 -0
  78. {plain-0.56.1 → plain-0.57.0}/plain/logs/README.md +0 -0
  79. {plain-0.56.1 → plain-0.57.0}/plain/logs/__init__.py +0 -0
  80. {plain-0.56.1 → plain-0.57.0}/plain/logs/configure.py +0 -0
  81. {plain-0.56.1 → plain-0.57.0}/plain/logs/loggers.py +0 -0
  82. {plain-0.56.1 → plain-0.57.0}/plain/logs/utils.py +0 -0
  83. {plain-0.56.1 → plain-0.57.0}/plain/packages/README.md +0 -0
  84. {plain-0.56.1 → plain-0.57.0}/plain/packages/__init__.py +0 -0
  85. {plain-0.56.1 → plain-0.57.0}/plain/packages/config.py +0 -0
  86. {plain-0.56.1 → plain-0.57.0}/plain/packages/registry.py +0 -0
  87. {plain-0.56.1 → plain-0.57.0}/plain/paginator.py +0 -0
  88. {plain-0.56.1 → plain-0.57.0}/plain/preflight/README.md +0 -0
  89. {plain-0.56.1 → plain-0.57.0}/plain/preflight/__init__.py +0 -0
  90. {plain-0.56.1 → plain-0.57.0}/plain/preflight/files.py +0 -0
  91. {plain-0.56.1 → plain-0.57.0}/plain/preflight/messages.py +0 -0
  92. {plain-0.56.1 → plain-0.57.0}/plain/preflight/registry.py +0 -0
  93. {plain-0.56.1 → plain-0.57.0}/plain/preflight/security.py +0 -0
  94. {plain-0.56.1 → plain-0.57.0}/plain/preflight/urls.py +0 -0
  95. {plain-0.56.1 → plain-0.57.0}/plain/runtime/README.md +0 -0
  96. {plain-0.56.1 → plain-0.57.0}/plain/runtime/__init__.py +0 -0
  97. {plain-0.56.1 → plain-0.57.0}/plain/runtime/global_settings.py +0 -0
  98. {plain-0.56.1 → plain-0.57.0}/plain/runtime/user_settings.py +0 -0
  99. {plain-0.56.1 → plain-0.57.0}/plain/signals/README.md +0 -0
  100. {plain-0.56.1 → plain-0.57.0}/plain/signals/__init__.py +0 -0
  101. {plain-0.56.1 → plain-0.57.0}/plain/signals/dispatch/__init__.py +0 -0
  102. {plain-0.56.1 → plain-0.57.0}/plain/signals/dispatch/dispatcher.py +0 -0
  103. {plain-0.56.1 → plain-0.57.0}/plain/signals/dispatch/license.txt +0 -0
  104. {plain-0.56.1 → plain-0.57.0}/plain/signing.py +0 -0
  105. {plain-0.56.1 → plain-0.57.0}/plain/templates/README.md +0 -0
  106. {plain-0.56.1 → plain-0.57.0}/plain/templates/__init__.py +0 -0
  107. {plain-0.56.1 → plain-0.57.0}/plain/templates/core.py +0 -0
  108. {plain-0.56.1 → plain-0.57.0}/plain/templates/jinja/__init__.py +0 -0
  109. {plain-0.56.1 → plain-0.57.0}/plain/templates/jinja/environments.py +0 -0
  110. {plain-0.56.1 → plain-0.57.0}/plain/templates/jinja/extensions.py +0 -0
  111. {plain-0.56.1 → plain-0.57.0}/plain/templates/jinja/filters.py +0 -0
  112. {plain-0.56.1 → plain-0.57.0}/plain/templates/jinja/globals.py +0 -0
  113. {plain-0.56.1 → plain-0.57.0}/plain/test/README.md +0 -0
  114. {plain-0.56.1 → plain-0.57.0}/plain/test/__init__.py +0 -0
  115. {plain-0.56.1 → plain-0.57.0}/plain/test/client.py +0 -0
  116. {plain-0.56.1 → plain-0.57.0}/plain/test/encoding.py +0 -0
  117. {plain-0.56.1 → plain-0.57.0}/plain/test/exceptions.py +0 -0
  118. {plain-0.56.1 → plain-0.57.0}/plain/urls/README.md +0 -0
  119. {plain-0.56.1 → plain-0.57.0}/plain/urls/__init__.py +0 -0
  120. {plain-0.56.1 → plain-0.57.0}/plain/urls/converters.py +0 -0
  121. {plain-0.56.1 → plain-0.57.0}/plain/urls/exceptions.py +0 -0
  122. {plain-0.56.1 → plain-0.57.0}/plain/urls/patterns.py +0 -0
  123. {plain-0.56.1 → plain-0.57.0}/plain/urls/resolvers.py +0 -0
  124. {plain-0.56.1 → plain-0.57.0}/plain/urls/routers.py +0 -0
  125. {plain-0.56.1 → plain-0.57.0}/plain/urls/utils.py +0 -0
  126. {plain-0.56.1 → plain-0.57.0}/plain/utils/README.md +0 -0
  127. {plain-0.56.1 → plain-0.57.0}/plain/utils/__init__.py +0 -0
  128. {plain-0.56.1 → plain-0.57.0}/plain/utils/cache.py +0 -0
  129. {plain-0.56.1 → plain-0.57.0}/plain/utils/crypto.py +0 -0
  130. {plain-0.56.1 → plain-0.57.0}/plain/utils/datastructures.py +0 -0
  131. {plain-0.56.1 → plain-0.57.0}/plain/utils/dateparse.py +0 -0
  132. {plain-0.56.1 → plain-0.57.0}/plain/utils/deconstruct.py +0 -0
  133. {plain-0.56.1 → plain-0.57.0}/plain/utils/decorators.py +0 -0
  134. {plain-0.56.1 → plain-0.57.0}/plain/utils/duration.py +0 -0
  135. {plain-0.56.1 → plain-0.57.0}/plain/utils/encoding.py +0 -0
  136. {plain-0.56.1 → plain-0.57.0}/plain/utils/functional.py +0 -0
  137. {plain-0.56.1 → plain-0.57.0}/plain/utils/hashable.py +0 -0
  138. {plain-0.56.1 → plain-0.57.0}/plain/utils/html.py +0 -0
  139. {plain-0.56.1 → plain-0.57.0}/plain/utils/http.py +0 -0
  140. {plain-0.56.1 → plain-0.57.0}/plain/utils/inspect.py +0 -0
  141. {plain-0.56.1 → plain-0.57.0}/plain/utils/ipv6.py +0 -0
  142. {plain-0.56.1 → plain-0.57.0}/plain/utils/itercompat.py +0 -0
  143. {plain-0.56.1 → plain-0.57.0}/plain/utils/module_loading.py +0 -0
  144. {plain-0.56.1 → plain-0.57.0}/plain/utils/regex_helper.py +0 -0
  145. {plain-0.56.1 → plain-0.57.0}/plain/utils/safestring.py +0 -0
  146. {plain-0.56.1 → plain-0.57.0}/plain/utils/text.py +0 -0
  147. {plain-0.56.1 → plain-0.57.0}/plain/utils/timesince.py +0 -0
  148. {plain-0.56.1 → plain-0.57.0}/plain/utils/timezone.py +0 -0
  149. {plain-0.56.1 → plain-0.57.0}/plain/utils/tree.py +0 -0
  150. {plain-0.56.1 → plain-0.57.0}/plain/validators.py +0 -0
  151. {plain-0.56.1 → plain-0.57.0}/plain/views/README.md +0 -0
  152. {plain-0.56.1 → plain-0.57.0}/plain/views/__init__.py +0 -0
  153. {plain-0.56.1 → plain-0.57.0}/plain/views/base.py +0 -0
  154. {plain-0.56.1 → plain-0.57.0}/plain/views/csrf.py +0 -0
  155. {plain-0.56.1 → plain-0.57.0}/plain/views/errors.py +0 -0
  156. {plain-0.56.1 → plain-0.57.0}/plain/views/exceptions.py +0 -0
  157. {plain-0.56.1 → plain-0.57.0}/plain/views/forms.py +0 -0
  158. {plain-0.56.1 → plain-0.57.0}/plain/views/objects.py +0 -0
  159. {plain-0.56.1 → plain-0.57.0}/plain/views/templates.py +0 -0
  160. {plain-0.56.1 → plain-0.57.0}/plain/wsgi.py +0 -0
  161. {plain-0.56.1 → plain-0.57.0}/tests/.gitignore +0 -0
  162. {plain-0.56.1 → plain-0.57.0}/tests/app/.gitignore +0 -0
  163. {plain-0.56.1 → plain-0.57.0}/tests/app/settings.py +0 -0
  164. {plain-0.56.1 → plain-0.57.0}/tests/app/test/__init__.py +0 -0
  165. {plain-0.56.1 → plain-0.57.0}/tests/app/test/default_settings.py +0 -0
  166. {plain-0.56.1 → plain-0.57.0}/tests/app/urls.py +0 -0
  167. {plain-0.56.1 → plain-0.57.0}/tests/conftest.py +0 -0
  168. {plain-0.56.1 → plain-0.57.0}/tests/test_cli.py +0 -0
  169. {plain-0.56.1 → plain-0.57.0}/tests/test_runtime.py +0 -0
  170. {plain-0.56.1 → plain-0.57.0}/tests/test_wsgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain
3
- Version: 0.56.1
3
+ Version: 0.57.0
4
4
  Summary: A web framework for building products with Python.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-File: LICENSE
@@ -1,5 +1,20 @@
1
1
  # plain changelog
2
2
 
3
+ ## [0.57.0](https://github.com/dropseed/plain/releases/plain@0.57.0) (2025-08-15)
4
+
5
+ ### What's changed
6
+
7
+ - The `ResponsePermanentRedirect` class has been removed; use `ResponseRedirect` with `status_code=301` instead ([d5735ea](https://github.com/dropseed/plain/commit/d5735ea4f8))
8
+ - The `RedirectView.permanent` attribute has been replaced with `status_code` for more flexible redirect status codes ([12dda16](https://github.com/dropseed/plain/commit/12dda16731))
9
+ - Updated `RedirectView` initialization parameters: `url_name` replaces `pattern_name`, `preserve_query_params` replaces `query_string`, and removed 410 Gone functionality ([3b9ca71](https://github.com/dropseed/plain/commit/3b9ca713bf))
10
+
11
+ ### Upgrade instructions
12
+
13
+ - Replace `ResponsePermanentRedirect` imports with `ResponseRedirect` and pass `status_code=301` to the constructor
14
+ - Update `RedirectView` subclasses to use `status_code=301` instead of `permanent=True`
15
+ - Replace `pattern_name` with `url_name` in RedirectView usage
16
+ - Replace `query_string=True` with `preserve_query_params=True` in RedirectView usage
17
+
3
18
  ## [0.56.1](https://github.com/dropseed/plain/releases/plain@0.56.1) (2025-07-30)
4
19
 
5
20
  ### What's changed
@@ -53,12 +53,6 @@ class DisallowedHost(SuspiciousOperation):
53
53
  pass
54
54
 
55
55
 
56
- class DisallowedRedirect(SuspiciousOperation):
57
- """Redirect to scheme not in allowed list"""
58
-
59
- pass
60
-
61
-
62
56
  class TooManyFieldsSent(SuspiciousOperation):
63
57
  """
64
58
  The number of fields in a GET or POST request exceeded
@@ -19,7 +19,6 @@ from plain.http.response import (
19
19
  ResponseNotAllowed,
20
20
  ResponseNotFound,
21
21
  ResponseNotModified,
22
- ResponsePermanentRedirect,
23
22
  ResponseRedirect,
24
23
  ResponseServerError,
25
24
  StreamingResponse,
@@ -36,7 +35,6 @@ __all__ = [
36
35
  "ResponseBase",
37
36
  "StreamingResponse",
38
37
  "ResponseRedirect",
39
- "ResponsePermanentRedirect",
40
38
  "ResponseNotModified",
41
39
  "ResponseBadRequest",
42
40
  "ResponseForbidden",
@@ -10,10 +10,8 @@ from email.header import Header
10
10
  from functools import cached_property
11
11
  from http.client import responses
12
12
  from http.cookies import SimpleCookie
13
- from urllib.parse import urlparse
14
13
 
15
14
  from plain import signals
16
- from plain.exceptions import DisallowedRedirect
17
15
  from plain.http.cookie import sign_cookie_value
18
16
  from plain.json import PlainJSONEncoder
19
17
  from plain.runtime import settings
@@ -566,19 +564,18 @@ class FileResponse(StreamingResponse):
566
564
  self.headers["Content-Disposition"] = content_disposition
567
565
 
568
566
 
569
- class ResponseRedirectBase(Response):
570
- allowed_schemes = ["http", "https", "ftp"]
567
+ class ResponseRedirect(Response):
568
+ """HTTP redirect response"""
569
+
570
+ status_code = 302
571
571
 
572
572
  def __init__(self, redirect_to, **kwargs):
573
573
  super().__init__(**kwargs)
574
574
  self.headers["Location"] = iri_to_uri(redirect_to)
575
- parsed = urlparse(str(redirect_to))
576
- if parsed.scheme and parsed.scheme not in self.allowed_schemes:
577
- raise DisallowedRedirect(
578
- f"Unsafe redirect to URL with protocol '{parsed.scheme}'"
579
- )
580
575
 
581
- url = property(lambda self: self.headers["Location"])
576
+ @property
577
+ def url(self):
578
+ return self.headers["Location"]
582
579
 
583
580
  def __repr__(self):
584
581
  return (
@@ -592,18 +589,6 @@ class ResponseRedirectBase(Response):
592
589
  )
593
590
 
594
591
 
595
- class ResponseRedirect(ResponseRedirectBase):
596
- """HTTP 302 response"""
597
-
598
- status_code = 302
599
-
600
-
601
- class ResponsePermanentRedirect(ResponseRedirectBase):
602
- """HTTP 301 response"""
603
-
604
- status_code = 301
605
-
606
-
607
592
  class ResponseNotModified(Response):
608
593
  """HTTP 304 response"""
609
594
 
@@ -1,6 +1,6 @@
1
1
  import re
2
2
 
3
- from plain.http import ResponsePermanentRedirect
3
+ from plain.http import ResponseRedirect
4
4
  from plain.runtime import settings
5
5
 
6
6
 
@@ -33,4 +33,6 @@ class HttpsRedirectMiddleware:
33
33
  and not any(pattern.search(path) for pattern in self.https_redirect_exempt)
34
34
  ):
35
35
  host = self.https_redirect_host or request.get_host()
36
- return ResponsePermanentRedirect(f"https://{host}{request.get_full_path()}")
36
+ return ResponseRedirect(
37
+ f"https://{host}{request.get_full_path()}", status_code=301
38
+ )
@@ -1,4 +1,4 @@
1
- from plain.http import ResponsePermanentRedirect
1
+ from plain.http import ResponseRedirect
2
2
  from plain.runtime import settings
3
3
  from plain.urls import Resolver404, get_resolver
4
4
  from plain.utils.http import escape_leading_slashes
@@ -22,7 +22,9 @@ class RedirectSlashMiddleware:
22
22
  # If the given URL is "Not Found", then check if we should redirect to
23
23
  # a path with a slash appended.
24
24
  if response.status_code == 404 and self.should_redirect_with_slash(request):
25
- return ResponsePermanentRedirect(self.get_full_path_with_slash(request))
25
+ return ResponseRedirect(
26
+ self.get_full_path_with_slash(request), status_code=301
27
+ )
26
28
 
27
29
  return response
28
30
 
@@ -0,0 +1,66 @@
1
+ from plain.http import ResponseRedirect
2
+ from plain.urls import reverse
3
+
4
+ from .base import View
5
+
6
+
7
+ class RedirectView(View):
8
+ """Provide a redirect on any GET request."""
9
+
10
+ status_code = 302
11
+ url: str | None = None
12
+ url_name: str | None = None
13
+ preserve_query_params = False
14
+
15
+ def __init__(
16
+ self, url=None, status_code=None, url_name=None, preserve_query_params=None
17
+ ):
18
+ # Allow attributes to be set in RedirectView.as_view(url="...", status_code=301, etc.)
19
+ self.url = url or self.url
20
+ self.status_code = status_code if status_code is not None else self.status_code
21
+ self.url_name = url_name or self.url_name
22
+ self.preserve_query_params = (
23
+ preserve_query_params
24
+ if preserve_query_params is not None
25
+ else self.preserve_query_params
26
+ )
27
+
28
+ def get_redirect_url(self):
29
+ """
30
+ Return the URL redirect to. Keyword arguments from the URL pattern
31
+ match generating the redirect request are provided as kwargs to this
32
+ method.
33
+ """
34
+ if self.url:
35
+ url = self.url % self.url_kwargs
36
+ elif self.url_name:
37
+ url = reverse(self.url_name, *self.url_args, **self.url_kwargs)
38
+ else:
39
+ raise ValueError("RedirectView requires either url or url_name to be set")
40
+
41
+ args = self.request.meta.get("QUERY_STRING", "")
42
+ if args and self.preserve_query_params:
43
+ url = f"{url}?{args}"
44
+ return url
45
+
46
+ def get(self):
47
+ url = self.get_redirect_url()
48
+ return ResponseRedirect(url, status_code=self.status_code)
49
+
50
+ def head(self):
51
+ return self.get()
52
+
53
+ def post(self):
54
+ return self.get()
55
+
56
+ def options(self):
57
+ return self.get()
58
+
59
+ def delete(self):
60
+ return self.get()
61
+
62
+ def put(self):
63
+ return self.get()
64
+
65
+ def patch(self):
66
+ return self.get()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain"
3
- version = "0.56.1"
3
+ version = "0.57.0"
4
4
  description = "A web framework for building products with Python."
5
5
  authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
6
6
  readme = "README.md"
@@ -1,77 +0,0 @@
1
- import logging
2
-
3
- from plain.http import (
4
- ResponseGone,
5
- ResponsePermanentRedirect,
6
- ResponseRedirect,
7
- )
8
- from plain.urls import reverse
9
-
10
- from .base import View
11
-
12
- logger = logging.getLogger("plain.request")
13
-
14
-
15
- class RedirectView(View):
16
- """Provide a redirect on any GET request."""
17
-
18
- permanent = False
19
- url: str | None = None
20
- pattern_name: str | None = None
21
- query_string = False
22
-
23
- def __init__(self, url=None, permanent=None):
24
- # Allow url and permanent to be set in RedirectView.as_view(url="...", permanent=True)
25
- self.url = url or self.url
26
- self.permanent = permanent if permanent is not None else self.permanent
27
-
28
- def get_redirect_url(self):
29
- """
30
- Return the URL redirect to. Keyword arguments from the URL pattern
31
- match generating the redirect request are provided as kwargs to this
32
- method.
33
- """
34
- if self.url:
35
- url = self.url % self.url_kwargs
36
- elif self.pattern_name:
37
- url = reverse(self.pattern_name, *self.url_args, **self.url_kwargs)
38
- else:
39
- return None
40
-
41
- args = self.request.meta.get("QUERY_STRING", "")
42
- if args and self.query_string:
43
- url = f"{url}?{args}"
44
- return url
45
-
46
- def get(self):
47
- url = self.get_redirect_url()
48
- if url:
49
- if self.permanent:
50
- return ResponsePermanentRedirect(url)
51
- else:
52
- return ResponseRedirect(url)
53
- else:
54
- logger.warning(
55
- "Gone: %s",
56
- self.request.path,
57
- extra={"status_code": 410, "request": self.request},
58
- )
59
- return ResponseGone()
60
-
61
- def head(self):
62
- return self.get()
63
-
64
- def post(self):
65
- return self.get()
66
-
67
- def options(self):
68
- return self.get()
69
-
70
- def delete(self):
71
- return self.get()
72
-
73
- def put(self):
74
- return self.get()
75
-
76
- def patch(self):
77
- return self.get()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes