plain 0.34.1__py3-none-any.whl → 0.36.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.
plain/csrf/middleware.py CHANGED
@@ -156,7 +156,7 @@ class RejectRequest(Exception):
156
156
 
157
157
  class CsrfViewMiddleware:
158
158
  """
159
- Require a present and correct csrfmiddlewaretoken for POST requests that
159
+ Require a present and correct _csrftoken for POST requests that
160
160
  have a CSRF cookie, and set an outgoing CSRF cookie.
161
161
 
162
162
  This middleware should be used in conjunction with the {% csrf_token %}
@@ -364,7 +364,7 @@ class CsrfViewMiddleware:
364
364
  request_csrf_token = ""
365
365
  if request.method == "POST":
366
366
  try:
367
- request_csrf_token = request.POST.get("csrfmiddlewaretoken", "")
367
+ request_csrf_token = request.POST.get(settings.CSRF_POST_NAME, "")
368
368
  except UnreadablePostError:
369
369
  # Handle a broken connection before we've completed reading the
370
370
  # POST data. process_view shouldn't raise any exceptions, so
@@ -380,7 +380,7 @@ class CsrfViewMiddleware:
380
380
  # depending on whether the client obtained the token from
381
381
  # the DOM or the cookie (and if the cookie, whether the cookie
382
382
  # was masked or unmasked).
383
- request_csrf_token = request.META[settings.CSRF_HEADER_NAME]
383
+ request_csrf_token = request.headers[settings.CSRF_HEADER_NAME]
384
384
  except KeyError:
385
385
  raise RejectRequest(REASON_CSRF_TOKEN_MISSING)
386
386
  token_source = settings.CSRF_HEADER_NAME
plain/http/README.md CHANGED
@@ -17,7 +17,7 @@ class ExampleView(View):
17
17
  print(self.request.GET.get("example"))
18
18
 
19
19
  # Creating a response
20
- response = Response("Hello, world!", status=200)
20
+ response = Response("Hello, world!", status_code=200)
21
21
 
22
22
  # Setting a response header
23
23
  response.headers["Example-Header"] = "Example Value"
plain/http/response.py CHANGED
@@ -108,7 +108,13 @@ class ResponseBase:
108
108
  status_code = 200
109
109
 
110
110
  def __init__(
111
- self, content_type=None, status=None, reason=None, charset=None, headers=None
111
+ self,
112
+ *,
113
+ content_type=None,
114
+ status_code=None,
115
+ reason=None,
116
+ charset=None,
117
+ headers=None,
112
118
  ):
113
119
  self.headers = ResponseHeaders(headers)
114
120
  self._charset = charset
@@ -127,9 +133,9 @@ class ResponseBase:
127
133
  self._handler_class = None
128
134
  self.cookies = SimpleCookie()
129
135
  self.closed = False
130
- if status is not None:
136
+ if status_code is not None:
131
137
  try:
132
- self.status_code = int(status)
138
+ self.status_code = int(status_code)
133
139
  except (ValueError, TypeError):
134
140
  raise TypeError("HTTP status code must be an integer.")
135
141
 
@@ -353,8 +359,8 @@ class Response(ResponseBase):
353
359
  ]
354
360
  )
355
361
 
356
- def __init__(self, content=b"", *args, **kwargs):
357
- super().__init__(*args, **kwargs)
362
+ def __init__(self, content=b"", **kwargs):
363
+ super().__init__(**kwargs)
358
364
  # Content is a bytestring. See the `content` property methods.
359
365
  self.content = content
360
366
 
@@ -434,8 +440,8 @@ class StreamingResponse(ResponseBase):
434
440
 
435
441
  streaming = True
436
442
 
437
- def __init__(self, streaming_content=(), *args, **kwargs):
438
- super().__init__(*args, **kwargs)
443
+ def __init__(self, streaming_content=(), **kwargs):
444
+ super().__init__(**kwargs)
439
445
  # `streaming_content` should be an iterable of bytestrings.
440
446
  # See the `streaming_content` property methods.
441
447
  self.streaming_content = streaming_content
@@ -560,8 +566,8 @@ class FileResponse(StreamingResponse):
560
566
  class ResponseRedirectBase(Response):
561
567
  allowed_schemes = ["http", "https", "ftp"]
562
568
 
563
- def __init__(self, redirect_to, *args, **kwargs):
564
- super().__init__(*args, **kwargs)
569
+ def __init__(self, redirect_to, **kwargs):
570
+ super().__init__(**kwargs)
565
571
  self.headers["Location"] = iri_to_uri(redirect_to)
566
572
  parsed = urlparse(str(redirect_to))
567
573
  if parsed.scheme and parsed.scheme not in self.allowed_schemes:
@@ -133,7 +133,8 @@ CSRF_COOKIE_PATH = "/"
133
133
  CSRF_COOKIE_SECURE = True
134
134
  CSRF_COOKIE_HTTPONLY = False
135
135
  CSRF_COOKIE_SAMESITE = "Lax"
136
- CSRF_HEADER_NAME = "HTTP_X_CSRFTOKEN"
136
+ CSRF_HEADER_NAME = "CSRF-Token"
137
+ CSRF_POST_NAME = "_csrftoken"
137
138
  CSRF_TRUSTED_ORIGINS: list[str] = []
138
139
 
139
140
  ###########
@@ -169,3 +170,9 @@ PREFLIGHT_SILENCED_CHECKS = []
169
170
  #############
170
171
 
171
172
  TEMPLATES_JINJA_ENVIRONMENT = "plain.templates.jinja.DefaultEnvironment"
173
+
174
+ #########
175
+ # Shell #
176
+ #########
177
+
178
+ SHELL_IMPORT: str = ""
plain/test/client.py CHANGED
@@ -17,7 +17,6 @@ from plain.urls import get_resolver
17
17
  from plain.utils.encoding import force_bytes
18
18
  from plain.utils.functional import SimpleLazyObject
19
19
  from plain.utils.http import urlencode
20
- from plain.utils.module_loading import import_string
21
20
  from plain.utils.regex_helper import _lazy_re_compile
22
21
 
23
22
  from .encoding import encode_multipart
@@ -776,11 +775,12 @@ class Client(RequestFactory):
776
775
  @property
777
776
  def session(self):
778
777
  """Return the current session variables."""
779
- Session = import_string(settings.SESSION_CLASS)
778
+ from plain.sessions import SessionStore
779
+
780
780
  cookie = self.cookies.get(settings.SESSION_COOKIE_NAME)
781
781
  if cookie:
782
- return Session(cookie.value)
783
- session = Session()
782
+ return SessionStore(cookie.value)
783
+ session = SessionStore()
784
784
  session.save()
785
785
  self.cookies[settings.SESSION_COOKIE_NAME] = session.session_key
786
786
  return session
@@ -790,14 +790,14 @@ class Client(RequestFactory):
790
790
 
791
791
  def _login(self, user):
792
792
  from plain.auth import login
793
+ from plain.sessions import SessionStore
793
794
 
794
795
  # Create a fake request to store login details.
795
796
  request = HttpRequest()
796
797
  if self.session:
797
798
  request.session = self.session
798
799
  else:
799
- Session = import_string(settings.SESSION_CLASS)
800
- request.session = Session()
800
+ request.session = SessionStore()
801
801
  login(request, user)
802
802
  # Save the session values.
803
803
  request.session.save()
@@ -816,14 +816,14 @@ class Client(RequestFactory):
816
816
  def logout(self):
817
817
  """Log out the user by removing the cookies and session object."""
818
818
  from plain.auth import get_user, logout
819
+ from plain.sessions import SessionStore
819
820
 
820
821
  request = HttpRequest()
821
822
  if self.session:
822
823
  request.session = self.session
823
824
  request.user = get_user(request)
824
825
  else:
825
- Session = import_string(settings.SESSION_CLASS)
826
- request.session = Session()
826
+ request.session = SessionStore()
827
827
  logout(request)
828
828
  self.cookies = SimpleCookie()
829
829
 
plain/views/README.md CHANGED
@@ -214,7 +214,7 @@ class ExampleView(DetailView):
214
214
  def get_object(self):
215
215
  if self.request.user.exceeds_rate_limit:
216
216
  raise ResponseException(
217
- Response("Rate limit exceeded", status=429)
217
+ Response("Rate limit exceeded", status_code=429)
218
218
  )
219
219
 
220
220
  return AnExpensiveObject()
plain/views/base.py CHANGED
@@ -85,7 +85,8 @@ class View:
85
85
  return result
86
86
 
87
87
  if isinstance(result, str):
88
- return Response(result, content_type="text/plain")
88
+ # Assume the str is rendered HTML
89
+ return Response(result)
89
90
 
90
91
  if isinstance(result, list):
91
92
  return JsonResponse(result, safe=False)
@@ -94,7 +95,7 @@ class View:
94
95
  return JsonResponse(result)
95
96
 
96
97
  if isinstance(result, int):
97
- return Response(status=result)
98
+ return Response(status_code=result)
98
99
 
99
100
  # Allow tuple for (status_code, content)?
100
101
 
plain/views/objects.py CHANGED
@@ -7,7 +7,7 @@ from .templates import TemplateView
7
7
 
8
8
 
9
9
  class ObjectTemplateViewMixin:
10
- context_object_name = ""
10
+ context_object_name = "object"
11
11
 
12
12
  def get(self) -> Response:
13
13
  self.load_object()
@@ -31,39 +31,9 @@ class ObjectTemplateViewMixin:
31
31
  def get_template_context(self) -> dict:
32
32
  """Insert the single object into the context dict."""
33
33
  context = super().get_template_context() # type: ignore
34
- context["object"] = self.object
35
- if self.context_object_name:
36
- context[self.context_object_name] = self.object
37
- elif hasattr(self.object, "_meta"):
38
- context[self.object._meta.model_name] = self.object
34
+ context[self.context_object_name] = self.object
39
35
  return context
40
36
 
41
- def get_template_names(self) -> list[str]:
42
- """
43
- Return a list of template names to be used for the request. May not be
44
- called if render_to_response() is overridden. Return the following list:
45
-
46
- * the value of ``template_name`` on the view (if provided)
47
- object instance that the view is operating upon (if available)
48
- * ``<package_label>/<model_name><template_name_suffix>.html``
49
- """
50
- if self.template_name: # type: ignore
51
- return [self.template_name] # type: ignore
52
-
53
- # If template_name isn't specified, it's not a problem --
54
- # we just start with an empty list.
55
- names = []
56
-
57
- # The least-specific option is the default <app>/<model>_detail.html;
58
- # only use this if the object in question is a model.
59
- if hasattr(self.object, "_meta"):
60
- object_meta = self.object._meta
61
- names.append(
62
- f"{object_meta.package_label}/{object_meta.model_name}{self.template_name_suffix}.html"
63
- )
64
-
65
- return names
66
-
67
37
 
68
38
  class DetailView(ObjectTemplateViewMixin, TemplateView):
69
39
  """
@@ -73,7 +43,7 @@ class DetailView(ObjectTemplateViewMixin, TemplateView):
73
43
  view will support display of *any* object by overriding `self.get_object()`.
74
44
  """
75
45
 
76
- template_name_suffix = "_detail"
46
+ pass
77
47
 
78
48
 
79
49
  class CreateView(ObjectTemplateViewMixin, FormView):
@@ -81,8 +51,6 @@ class CreateView(ObjectTemplateViewMixin, FormView):
81
51
  View for creating a new object, with a response rendered by a template.
82
52
  """
83
53
 
84
- template_name_suffix = "_form"
85
-
86
54
  def post(self) -> Response:
87
55
  """
88
56
  Handle POST requests: instantiate a form instance with the passed
@@ -119,8 +87,6 @@ class CreateView(ObjectTemplateViewMixin, FormView):
119
87
  class UpdateView(ObjectTemplateViewMixin, FormView):
120
88
  """View for updating an object, with a response rendered by a template."""
121
89
 
122
- template_name_suffix = "_form"
123
-
124
90
  def post(self) -> Response:
125
91
  """
126
92
  Handle POST requests: instantiate a form instance with the passed
@@ -170,7 +136,6 @@ class DeleteView(ObjectTemplateViewMixin, FormView):
170
136
  self.instance.delete()
171
137
 
172
138
  form_class = EmptyDeleteForm
173
- template_name_suffix = "_confirm_delete"
174
139
 
175
140
  def post(self) -> Response:
176
141
  """
@@ -198,7 +163,6 @@ class ListView(TemplateView):
198
163
  rendered by a template.
199
164
  """
200
165
 
201
- template_name_suffix = "_list"
202
166
  context_object_name = "objects"
203
167
 
204
168
  def get(self) -> Response:
@@ -215,29 +179,3 @@ class ListView(TemplateView):
215
179
  context = super().get_template_context() # type: ignore
216
180
  context[self.context_object_name] = self.objects
217
181
  return context
218
-
219
- def get_template_names(self) -> list[str]:
220
- """
221
- Return a list of template names to be used for the request. May not be
222
- called if render_to_response() is overridden. Return the following list:
223
-
224
- * the value of ``template_name`` on the view (if provided)
225
- object instance that the view is operating upon (if available)
226
- * ``<package_label>/<model_name><template_name_suffix>.html``
227
- """
228
- if self.template_name: # type: ignore
229
- return [self.template_name] # type: ignore
230
-
231
- # If template_name isn't specified, it's not a problem --
232
- # we just start with an empty list.
233
- names = []
234
-
235
- # The least-specific option is the default <app>/<model>_detail.html;
236
- # only use this if the object in question is a model.
237
- if hasattr(self.objects, "model") and hasattr(self.objects.model, "_meta"):
238
- object_meta = self.objects.model._meta
239
- names.append(
240
- f"{object_meta.package_label}/{object_meta.model_name}{self.template_name_suffix}.html"
241
- )
242
-
243
- return names
plain/views/redirect.py CHANGED
@@ -20,6 +20,11 @@ class RedirectView(View):
20
20
  pattern_name: str | None = None
21
21
  query_string = False
22
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
+
23
28
  def get_redirect_url(self):
24
29
  """
25
30
  Return the URL redirect to. Keyword arguments from the URL pattern
plain/views/templates.py CHANGED
@@ -11,7 +11,8 @@ from .base import View
11
11
 
12
12
  def csrf_input(request):
13
13
  return format_html(
14
- '<input type="hidden" name="csrfmiddlewaretoken" value="{}">',
14
+ '<input type="hidden" name="{}" value="{}">',
15
+ settings.CSRF_POST_NAME,
15
16
  get_token(request),
16
17
  )
17
18
 
@@ -41,20 +42,21 @@ class TemplateView(View):
41
42
 
42
43
  def get_template_names(self) -> list[str]:
43
44
  """
44
- Return a list of template names to be used for the request. Must return
45
- a list. May not be called if render_to_response() is overridden.
45
+ Return a list of template names to be used for the request.
46
46
  """
47
- if self.template_name is None:
48
- raise ImproperlyConfigured(
49
- "TemplateView requires either a definition of "
50
- "'template_name' or an implementation of 'get_template_names()'"
51
- )
52
- else:
47
+ if self.template_name:
53
48
  return [self.template_name]
54
49
 
50
+ return []
51
+
55
52
  def get_template(self) -> Template:
56
53
  template_names = self.get_template_names()
57
54
 
55
+ if not template_names:
56
+ raise ImproperlyConfigured(
57
+ f"{self.__class__.__name__} requires a template_name or get_template_names()."
58
+ )
59
+
58
60
  for template_name in template_names:
59
61
  try:
60
62
  return Template(template_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain
3
- Version: 0.34.1
3
+ Version: 0.36.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
@@ -13,16 +13,24 @@ plain/assets/compile.py,sha256=rABDHj7k_YAIPVvS7JJLCukEgUhb7h5oz9ajt5fGirI,3298
13
13
  plain/assets/finders.py,sha256=2k8QZAbfUbc1LykxbzdazTSB6xNxJZnsZaGhWbSFZZs,1452
14
14
  plain/assets/fingerprints.py,sha256=2LPHLUkoITMseLDmemTpBtMRDWCR2H5GAHjC6AN4gz0,1367
15
15
  plain/assets/urls.py,sha256=zQUA8bAlh9qVqskPJJrqWd9DjvetOi5jPSqy4vUX0J4,1161
16
- plain/assets/views.py,sha256=XEaw155u4F3tZRcBHebWJJsGd-m_5sw0nRABc8mnjlg,9432
16
+ plain/assets/views.py,sha256=T_0Qh6v9qBerEBYbhToigwOzsij-x1z_R-1zETQcIh0,9447
17
17
  plain/cli/README.md,sha256=ompPcgzY2Fdpm579ITmCpFIaZIsiXYbfD61mqkq312M,1860
18
18
  plain/cli/__init__.py,sha256=6w9T7K2WrPwh6DcaMb2oNt_CWU6Bc57nUTO2Bt1p38Y,63
19
- plain/cli/core.py,sha256=KL7f5NDC-gvu9nG-1tA_ufiP1k13HD44bD9lekAdK5E,25691
19
+ plain/cli/build.py,sha256=dKUYBNegvb6QNckR7XZ7CJJtINwZcmDvbUdv2dWwjf8,3226
20
+ plain/cli/core.py,sha256=N7xUO8Ox2xz8wJyWZ0fSyCfqVFmKz9czH9gulRyQuM4,2809
21
+ plain/cli/docs.py,sha256=dHInHcweVLOew3hCNspVvUQ8epEfovDh3yI6O_FEsnI,7546
20
22
  plain/cli/formatting.py,sha256=1hZH13y1qwHcU2K2_Na388nw9uvoeQH8LrWL-O9h8Yc,2207
23
+ plain/cli/preflight.py,sha256=NKyYjcoDjigzfJIDhf7A7degYadaUI05Bw7U9OQ73vs,4170
21
24
  plain/cli/print.py,sha256=XraUYrgODOJquIiEv78wSCYGRBplHXtXSS9QtFG5hqY,217
22
25
  plain/cli/registry.py,sha256=yKVMSDjW8g10nlV9sPXFGJQmhC_U-k4J4kM7N2OQVLA,1467
23
- plain/cli/startup.py,sha256=3LIz9JrIZoF52Sa0j0SCypQwEaBDkhvuGaBdtiQLr5Q,680
24
- plain/csrf/README.md,sha256=nYbUcR9p7VNxB-8z00OluB1C0EJ0WRKJIhqX6RkjiiU,708
25
- plain/csrf/middleware.py,sha256=FYhT7KPJ664Sm0nKjeej1OIXalvVTYiotQX3ytI0dfY,17417
26
+ plain/cli/scaffold.py,sha256=EEyiexgp9jqmIet3Wndh405xErV0lT1jQevqhGkbudw,1326
27
+ plain/cli/settings.py,sha256=9cx4bue664I2P7kUedlf4YhCPB0tSKSE4Q8mGyzEv2o,1995
28
+ plain/cli/shell.py,sha256=iIwvlTdTBjLBBUdXMAmIRWSoynszOZI79-mrBg4RegU,1373
29
+ plain/cli/startup.py,sha256=wLaFuyUb4ewWhtehBCGicrRCXIIGCRbeCT3ce9hUv-A,1022
30
+ plain/cli/urls.py,sha256=CS9NFpwZBWveAR8F3YsoUNySDEK_PwF73oSgLDfkOdI,3776
31
+ plain/cli/utils.py,sha256=VwlIh0z7XxzVV8I3qM2kZo07fkJFPoeeVZa1ODG616k,258
32
+ plain/csrf/README.md,sha256=Xqb3pRZcIF6TJlOzEksQN0zue4mY7OTebNta9cq0T8g,699
33
+ plain/csrf/middleware.py,sha256=bn8KOE45aucSam0m3F9g8FVfnmjjt-6jxS6iSNwHamU,17413
26
34
  plain/csrf/views.py,sha256=HwQqfI6KPelHP9gSXhjfZaTLQic71PKsoZ6DPhr1rKI,572
27
35
  plain/forms/README.md,sha256=TawBdy1jP0-8HUsfUzd7vvgkwl3EJGejDxFhWR8F-80,2242
28
36
  plain/forms/__init__.py,sha256=UxqPwB8CiYPCQdHmUc59jadqaXqDmXBH8y4bt9vTPms,226
@@ -30,12 +38,12 @@ plain/forms/boundfield.py,sha256=LhydhCVR0okrli0-QBMjGjAJ8-06gTCXVEaBZhBouQk,174
30
38
  plain/forms/exceptions.py,sha256=XCLDRl5snIEDu5-8mLB0NnU_tegcBfyIHMiJxqvbxnc,164
31
39
  plain/forms/fields.py,sha256=Fw77LP06aO5h6ZdJmS2S_2On4YSrsl4gu142Y6nGF50,34987
32
40
  plain/forms/forms.py,sha256=fEKBee1b8I_DJ-FufzWJGtSQoUoyieYfqUaGEre9B4Q,10418
33
- plain/http/README.md,sha256=yoIx-igIU0rYBZuyKWkXph4Zvhei04ZYz6RhQuMe1kA,708
41
+ plain/http/README.md,sha256=G662f56feQWiEHw9tTncZqK8kNB_nbR1HTGeOR1ygNM,713
34
42
  plain/http/__init__.py,sha256=DIsDRbBsCGa4qZgq-fUuQS0kkxfbTU_3KpIM9VvH04w,1067
35
43
  plain/http/cookie.py,sha256=11FnSG3Plo6T3jZDbPoCw7SKh9ExdBio3pTmIO03URg,597
36
44
  plain/http/multipartparser.py,sha256=Cyk_UZhxf8JwNza_Yl4_nKCYkmnG7xY9PSVcf9Us57U,27266
37
45
  plain/http/request.py,sha256=kq3AuM0EWyAD_kqMlorTccm5mzIQ6ZefkCa-jXUntnI,25514
38
- plain/http/response.py,sha256=RR2sUG-ONWKWcZyIbztjWvtFyh0cR-CoxQvnWOyN0io,23619
46
+ plain/http/response.py,sha256=IGSg22ioob6kSRkKGQsQF_exb2Lch8RKiZr_Xoi1_xA,23644
39
47
  plain/internal/__init__.py,sha256=fVBaYLCXEQc-7riHMSlw3vMTTuF7-0Bj2I8aGzv0o0w,171
40
48
  plain/internal/files/__init__.py,sha256=VctFgox4Q1AWF3klPaoCC5GIw5KeLafYjY5JmN8mAVw,63
41
49
  plain/internal/files/base.py,sha256=-JpRMzv2bgVSZ9dcxh13gGRTVeEd_Tjd02iQMOXsRgQ,4126
@@ -71,7 +79,7 @@ plain/preflight/security.py,sha256=oxUZBp2M0bpBfUoLYepIxoex2Y90nyjlrL8XU8UTHYY,2
71
79
  plain/preflight/urls.py,sha256=cQ-WnFa_5oztpKdtwhuIGb7pXEml__bHsjs1SWO2YNI,1468
72
80
  plain/runtime/README.md,sha256=S_FIOmSq8LkVQHh9Xm6s3EJWKTVdlSr5A_bNXgh02X8,4740
73
81
  plain/runtime/__init__.py,sha256=o2RVETiL8U0lMFBpbtfnxflhw_4MFllMV6CEpX3RqZs,1965
74
- plain/runtime/global_settings.py,sha256=4YpUJ9wN8t3UteekTpYTvZaO4Dkv4EVjvIlkgISKGbw,5500
82
+ plain/runtime/global_settings.py,sha256=gg9XhMJmzbQqfxzst5JaI0hDIzPvTwX5M0AonJk2ISY,5579
75
83
  plain/runtime/user_settings.py,sha256=uRHHVfzUvHon91_fOKj7K2WaBYwJ1gCPLfeXqKj5CTs,10902
76
84
  plain/signals/README.md,sha256=FInfJXdVQkb7u93PvD8XgPbz_f6m0l2xIu_4PyttV1E,234
77
85
  plain/signals/__init__.py,sha256=eAs0kLqptuP6I31dWXeAqRNji3svplpAV4Ez6ktjwXM,131
@@ -88,7 +96,7 @@ plain/templates/jinja/filters.py,sha256=3KJKKbxcv9dLzUDWPcaa88k3NU2m1GG3iMIgFhzX
88
96
  plain/templates/jinja/globals.py,sha256=VMpuMZvwWOmb5MbzKK-ox-QEX_WSsXFxq0mm8biJgaU,558
89
97
  plain/test/README.md,sha256=fv4YzziU2QxgcNHSgv7aDUO45sDOofVuCNrV1NPbWzo,1106
90
98
  plain/test/__init__.py,sha256=MhNHtp7MYBl9kq-pMRGY11kJ6kU1I6vOkjNkit1TYRg,94
91
- plain/test/client.py,sha256=kQiC-lu2x1p9Hvcs9_aa3DmOfLRaHIiarKtMmLmlJl4,26994
99
+ plain/test/client.py,sha256=lqzOf8EJo_u54dW_oiPy0m5mW-Nt3-CWyrBh9_MJnms,26930
92
100
  plain/test/encoding.py,sha256=YJBOIE-BQRA5yl4zHnQy-9l67mJDTFmfy1DQXK0Wk-Q,3199
93
101
  plain/test/exceptions.py,sha256=b-GHicg87Gh73W3g8QGWuSHi9PrQFVsxgWvEXDLt8gQ,290
94
102
  plain/urls/README.md,sha256=ijFGmrkUY9buBqO_i1GZaN3V55vl9xwEADtHOx_ZHPY,3724
@@ -124,18 +132,18 @@ plain/utils/text.py,sha256=42hJv06sadbWfsaAHNhqCQaP1W9qZ69trWDTS-Xva7k,9496
124
132
  plain/utils/timesince.py,sha256=a_-ZoPK_s3Pt998CW4rWp0clZ1XyK2x04hCqak2giII,5928
125
133
  plain/utils/timezone.py,sha256=6u0sE-9RVp0_OCe0Y1KiYYQoq5THWLokZFQYY8jf78g,6221
126
134
  plain/utils/tree.py,sha256=wdWzmfsgc26YDF2wxhAY3yVxXTixQYqYDKE9mL3L3ZY,4383
127
- plain/views/README.md,sha256=JyqAWMXYdaVnRncmb12yvlBhC629zRtWR7uzkKFwc9Y,5998
135
+ plain/views/README.md,sha256=mveTtHrd2z3knSAchW2jTwfg59_bowwhAacx-3QBBHw,6003
128
136
  plain/views/__init__.py,sha256=a-N1nkklVohJTtz0yD1MMaS0g66HviEjsKydNVVjvVc,392
129
- plain/views/base.py,sha256=6Q67PEioF7b0OHqrYCu56KUCHqJ-Gr4vefMwuAXt38w,3256
137
+ plain/views/base.py,sha256=LomceHi01okLk41YfUr6cBE0VB5FNsSVODTOQ-Rmv0I,3280
130
138
  plain/views/csrf.py,sha256=7q6l5xzLWhRnMY64aNj0hR6G-3pxI2yhRwG6k_5j00E,144
131
139
  plain/views/errors.py,sha256=jbNCJIzowwCsEvqyJ3opMeZpPDqTyhtrbqb0VnAm2HE,1263
132
140
  plain/views/exceptions.py,sha256=b4euI49ZUKS9O8AGAcFfiDpstzkRAuuj_uYQXzWNHME,138
133
141
  plain/views/forms.py,sha256=5L6dYkwcZFMD3-w_QC2QDElo9hhSPrhVVFq9CB5yL9k,2692
134
- plain/views/objects.py,sha256=g5Lzno0Zsv0K449UpcCtxwCoO7WMRAWqKlxxV2V0_qg,8263
135
- plain/views/redirect.py,sha256=9zHZgKvtSkdrMX9KmsRM8hJTPmBktxhc4d8OitbuniI,1724
136
- plain/views/templates.py,sha256=gwPqRki3SVcaTmtSCEp1MycUB-1saDYeYzmZZTQLxjE,2117
137
- plain-0.34.1.dist-info/METADATA,sha256=dLvkActYXqeXXsOPWzMPyteeRejGun_Zg77EY2UQRRI,3944
138
- plain-0.34.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
139
- plain-0.34.1.dist-info/entry_points.txt,sha256=1Ys2lsSeMepD1vz8RSrJopna0RQfUd951vYvCRsvl6A,45
140
- plain-0.34.1.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
141
- plain-0.34.1.dist-info/RECORD,,
142
+ plain/views/objects.py,sha256=giLe9-XespeKwyEd289hfApO_9Mjwz0oddB1_2-8ko8,5713
143
+ plain/views/redirect.py,sha256=vMXx8430FtyKcT0V0gyY92SkLtyULBX52KhX4eu4gEA,1985
144
+ plain/views/templates.py,sha256=kMcHKkKNvucF91SFGkaq-ugjrCwn4zJBpFV1JkwA544,2027
145
+ plain-0.36.0.dist-info/METADATA,sha256=8WWGgPUMTMgzmwIpFovdG2nz8t-0GfokN_tdHxtFHnY,3944
146
+ plain-0.36.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
147
+ plain-0.36.0.dist-info/entry_points.txt,sha256=1Ys2lsSeMepD1vz8RSrJopna0RQfUd951vYvCRsvl6A,45
148
+ plain-0.36.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
149
+ plain-0.36.0.dist-info/RECORD,,
File without changes