plain 0.35.0__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/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
@@ -95,7 +95,7 @@ class View:
95
95
  return JsonResponse(result)
96
96
 
97
97
  if isinstance(result, int):
98
- return Response(status=result)
98
+ return Response(status_code=result)
99
99
 
100
100
  # Allow tuple for (status_code, content)?
101
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/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.35.0
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
@@ -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=E6TjfCJYbf7Jh2P-Zd2YsLJbC99Gj9bN21Y_CMqQAao,3275
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
142
+ plain/views/objects.py,sha256=giLe9-XespeKwyEd289hfApO_9Mjwz0oddB1_2-8ko8,5713
135
143
  plain/views/redirect.py,sha256=vMXx8430FtyKcT0V0gyY92SkLtyULBX52KhX4eu4gEA,1985
136
- plain/views/templates.py,sha256=gwPqRki3SVcaTmtSCEp1MycUB-1saDYeYzmZZTQLxjE,2117
137
- plain-0.35.0.dist-info/METADATA,sha256=GAMRCUdN1HOOzB3UOXvHSKZ2q04mhjmFZK2eJJYLdWM,3944
138
- plain-0.35.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
139
- plain-0.35.0.dist-info/entry_points.txt,sha256=1Ys2lsSeMepD1vz8RSrJopna0RQfUd951vYvCRsvl6A,45
140
- plain-0.35.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
141
- plain-0.35.0.dist-info/RECORD,,
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