plain 0.13.1__py3-none-any.whl → 0.14.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/cli/README.md +2 -2
- plain/cli/cli.py +7 -11
- plain/cli/packages.py +7 -3
- plain/cli/startup.py +2 -2
- plain/csrf/middleware.py +1 -0
- plain/exceptions.py +2 -1
- plain/forms/forms.py +5 -5
- plain/http/multipartparser.py +5 -5
- plain/http/request.py +5 -5
- plain/http/response.py +19 -14
- plain/internal/files/locks.py +1 -0
- plain/internal/files/move.py +1 -2
- plain/internal/files/uploadhandler.py +1 -0
- plain/internal/files/utils.py +3 -3
- plain/internal/handlers/base.py +3 -7
- plain/internal/handlers/exception.py +1 -3
- plain/internal/handlers/wsgi.py +1 -1
- plain/internal/middleware/slash.py +5 -8
- plain/packages/config.py +9 -15
- plain/packages/registry.py +12 -12
- plain/paginator.py +1 -2
- plain/preflight/messages.py +3 -10
- plain/preflight/registry.py +2 -2
- plain/preflight/urls.py +4 -4
- plain/runtime/global_settings.py +1 -0
- plain/runtime/user_settings.py +6 -6
- plain/signing.py +4 -4
- plain/test/client.py +22 -21
- plain/urls/base.py +1 -1
- plain/urls/conf.py +2 -1
- plain/urls/resolvers.py +20 -33
- plain/utils/cache.py +1 -0
- plain/utils/crypto.py +2 -1
- plain/utils/datastructures.py +2 -2
- plain/utils/dateformat.py +13 -12
- plain/utils/dateparse.py +1 -1
- plain/utils/decorators.py +1 -1
- plain/utils/html.py +7 -7
- plain/utils/http.py +8 -8
- plain/utils/ipv6.py +1 -1
- plain/utils/module_loading.py +2 -2
- plain/utils/regex_helper.py +7 -6
- plain/utils/text.py +7 -7
- plain/utils/timesince.py +1 -1
- plain/utils/timezone.py +5 -5
- plain/validators.py +1 -3
- plain/views/forms.py +4 -4
- plain/views/objects.py +2 -2
- {plain-0.13.1.dist-info → plain-0.14.0.dist-info}/METADATA +7 -12
- {plain-0.13.1.dist-info → plain-0.14.0.dist-info}/RECORD +56 -57
- {plain-0.13.1.dist-info → plain-0.14.0.dist-info}/WHEEL +1 -1
- plain-0.14.0.dist-info/entry_points.txt +2 -0
- plain/utils/termcolors.py +0 -221
- plain-0.13.1.dist-info/entry_points.txt +0 -3
- {plain-0.13.1.dist-info → plain-0.14.0.dist-info/licenses}/LICENSE +0 -0
plain/utils/http.py
CHANGED
@@ -51,8 +51,8 @@ def urlencode(query, doseq=False):
|
|
51
51
|
for key, value in query:
|
52
52
|
if value is None:
|
53
53
|
raise TypeError(
|
54
|
-
"Cannot encode None for key '
|
55
|
-
"mean to pass an empty string or omit the value?"
|
54
|
+
f"Cannot encode None for key '{key}' in a query string. Did you "
|
55
|
+
"mean to pass an empty string or omit the value?"
|
56
56
|
)
|
57
57
|
elif not doseq or isinstance(value, str | bytes):
|
58
58
|
query_val = value
|
@@ -68,9 +68,9 @@ def urlencode(query, doseq=False):
|
|
68
68
|
for item in itr:
|
69
69
|
if item is None:
|
70
70
|
raise TypeError(
|
71
|
-
"Cannot encode None for key '
|
71
|
+
f"Cannot encode None for key '{key}' in a query "
|
72
72
|
"string. Did you mean to pass an empty string or "
|
73
|
-
"omit the value?"
|
73
|
+
"omit the value?"
|
74
74
|
)
|
75
75
|
elif not isinstance(item, bytes):
|
76
76
|
item = str(item)
|
@@ -110,9 +110,9 @@ def parse_http_date(date):
|
|
110
110
|
if m is not None:
|
111
111
|
break
|
112
112
|
else:
|
113
|
-
raise ValueError("
|
113
|
+
raise ValueError(f"{date!r} is not in a valid HTTP date format")
|
114
114
|
try:
|
115
|
-
tz = datetime.
|
115
|
+
tz = datetime.UTC
|
116
116
|
year = int(m["year"])
|
117
117
|
if year < 100:
|
118
118
|
current_year = datetime.datetime.now(tz=tz).year
|
@@ -131,7 +131,7 @@ def parse_http_date(date):
|
|
131
131
|
result = datetime.datetime(year, month, day, hour, min, sec, tzinfo=tz)
|
132
132
|
return int(result.timestamp())
|
133
133
|
except Exception as exc:
|
134
|
-
raise ValueError("
|
134
|
+
raise ValueError(f"{date!r} is not a valid date") from exc
|
135
135
|
|
136
136
|
|
137
137
|
def parse_http_date_safe(date):
|
@@ -216,7 +216,7 @@ def quote_etag(etag_str):
|
|
216
216
|
if ETAG_MATCH.match(etag_str):
|
217
217
|
return etag_str
|
218
218
|
else:
|
219
|
-
return '"
|
219
|
+
return f'"{etag_str}"'
|
220
220
|
|
221
221
|
|
222
222
|
def is_same_domain(host, pattern):
|
plain/utils/ipv6.py
CHANGED
plain/utils/module_loading.py
CHANGED
@@ -23,7 +23,7 @@ def import_string(dotted_path):
|
|
23
23
|
try:
|
24
24
|
module_path, class_name = dotted_path.rsplit(".", 1)
|
25
25
|
except ValueError as err:
|
26
|
-
raise ImportError("
|
26
|
+
raise ImportError(f"{dotted_path} doesn't look like a module path") from err
|
27
27
|
|
28
28
|
try:
|
29
29
|
return cached_import(module_path, class_name)
|
@@ -66,4 +66,4 @@ def module_dir(module):
|
|
66
66
|
filename = getattr(module, "__file__", None)
|
67
67
|
if filename is not None:
|
68
68
|
return os.path.dirname(filename)
|
69
|
-
raise ValueError("Cannot determine directory containing
|
69
|
+
raise ValueError(f"Cannot determine directory containing {module}")
|
plain/utils/regex_helper.py
CHANGED
@@ -5,6 +5,7 @@ Used internally by Plain and not intended for external use.
|
|
5
5
|
This is not, and is not intended to be, a complete reg-exp decompiler. It
|
6
6
|
should be good enough for a large class of URLS, however.
|
7
7
|
"""
|
8
|
+
|
8
9
|
import re
|
9
10
|
|
10
11
|
from plain.utils.functional import SimpleLazyObject
|
@@ -111,9 +112,9 @@ def normalize(pattern):
|
|
111
112
|
ch, escaped = next(pattern_iter)
|
112
113
|
if ch != "?" or escaped:
|
113
114
|
# A positional group
|
114
|
-
name = "_%d" % num_args
|
115
|
+
name = "_%d" % num_args # noqa: UP031
|
115
116
|
num_args += 1
|
116
|
-
result.append(Group((("
|
117
|
+
result.append(Group(((f"%({name})s"), name)))
|
117
118
|
walk_to_end(ch, pattern_iter)
|
118
119
|
else:
|
119
120
|
ch, escaped = next(pattern_iter)
|
@@ -127,12 +128,12 @@ def normalize(pattern):
|
|
127
128
|
elif ch != "P":
|
128
129
|
# Anything else, other than a named group, is something
|
129
130
|
# we cannot reverse.
|
130
|
-
raise ValueError("Non-reversible reg-exp portion: '(
|
131
|
+
raise ValueError(f"Non-reversible reg-exp portion: '(?{ch}'")
|
131
132
|
else:
|
132
133
|
ch, escaped = next(pattern_iter)
|
133
134
|
if ch not in ("<", "="):
|
134
135
|
raise ValueError(
|
135
|
-
"Non-reversible reg-exp portion: '(?P
|
136
|
+
f"Non-reversible reg-exp portion: '(?P{ch}'"
|
136
137
|
)
|
137
138
|
# We are in a named capturing group. Extra the name and
|
138
139
|
# then skip to the end.
|
@@ -150,10 +151,10 @@ def normalize(pattern):
|
|
150
151
|
# Named backreferences have already consumed the
|
151
152
|
# parenthesis.
|
152
153
|
if terminal_char != ")":
|
153
|
-
result.append(Group((("
|
154
|
+
result.append(Group(((f"%({param})s"), param)))
|
154
155
|
walk_to_end(ch, pattern_iter)
|
155
156
|
else:
|
156
|
-
result.append(Group((("
|
157
|
+
result.append(Group(((f"%({param})s"), None)))
|
157
158
|
elif ch in "*?+{":
|
158
159
|
# Quantifiers affect the previous item in the result list.
|
159
160
|
count, ch = get_quantifier(ch, pattern_iter)
|
plain/utils/text.py
CHANGED
@@ -53,7 +53,7 @@ def wrap(text, width):
|
|
53
53
|
yield line
|
54
54
|
line = ""
|
55
55
|
break
|
56
|
-
yield "
|
56
|
+
yield f"{line[: space - 1]}\n"
|
57
57
|
line = line[space:]
|
58
58
|
max_width = min((line.endswith("\n") and width + 1 or width), width)
|
59
59
|
if line:
|
@@ -224,7 +224,7 @@ class Truncator(SimpleLazyObject):
|
|
224
224
|
out += truncate_text
|
225
225
|
# Close any tags still open
|
226
226
|
for tag in open_tags:
|
227
|
-
out += "
|
227
|
+
out += f"</{tag}>"
|
228
228
|
# Return string
|
229
229
|
return out
|
230
230
|
|
@@ -242,7 +242,7 @@ def get_valid_filename(name):
|
|
242
242
|
s = str(name).strip().replace(" ", "_")
|
243
243
|
s = re.sub(r"(?u)[^-\w.]", "", s)
|
244
244
|
if s in {"", ".", ".."}:
|
245
|
-
raise SuspiciousFileOperation("Could not derive file name from '
|
245
|
+
raise SuspiciousFileOperation(f"Could not derive file name from '{name}'")
|
246
246
|
return s
|
247
247
|
|
248
248
|
|
@@ -407,9 +407,9 @@ def unescape_string_literal(s):
|
|
407
407
|
"'ab' c"
|
408
408
|
"""
|
409
409
|
if not s or s[0] not in "\"'" or s[-1] != s[0]:
|
410
|
-
raise ValueError("Not a string literal:
|
410
|
+
raise ValueError(f"Not a string literal: {s!r}")
|
411
411
|
quote = s[0]
|
412
|
-
return s[1:-1].replace(
|
412
|
+
return s[1:-1].replace(rf"\{quote}", quote).replace(r"\\", "\\")
|
413
413
|
|
414
414
|
|
415
415
|
@keep_lazy_text
|
@@ -475,9 +475,9 @@ def pluralize_lazy(singular, plural, number):
|
|
475
475
|
return values[number]
|
476
476
|
except KeyError:
|
477
477
|
raise KeyError(
|
478
|
-
"Your dictionary lacks key '
|
478
|
+
f"Your dictionary lacks key '{number}'. Please provide "
|
479
479
|
"it, because it is required to determine whether "
|
480
|
-
"string is singular or plural."
|
480
|
+
"string is singular or plural."
|
481
481
|
)
|
482
482
|
|
483
483
|
def _translate(self, number_value):
|
plain/utils/timesince.py
CHANGED
@@ -63,7 +63,7 @@ def timesince(d, now=None, reversed=False, time_strings=None, depth=2):
|
|
63
63
|
if now and not isinstance(now, datetime.datetime):
|
64
64
|
now = datetime.datetime(now.year, now.month, now.day)
|
65
65
|
|
66
|
-
now = now or datetime.datetime.now(datetime.
|
66
|
+
now = now or datetime.datetime.now(datetime.UTC if is_aware(d) else None)
|
67
67
|
|
68
68
|
if reversed:
|
69
69
|
d, now = now, d
|
plain/utils/timezone.py
CHANGED
@@ -5,7 +5,7 @@ Timezone-related classes and functions.
|
|
5
5
|
import functools
|
6
6
|
import zoneinfo
|
7
7
|
from contextlib import ContextDecorator
|
8
|
-
from datetime import datetime, timedelta, timezone, tzinfo
|
8
|
+
from datetime import UTC, datetime, timedelta, timezone, tzinfo
|
9
9
|
from threading import local
|
10
10
|
|
11
11
|
from plain.runtime import settings
|
@@ -33,7 +33,7 @@ def get_fixed_timezone(offset):
|
|
33
33
|
if isinstance(offset, timedelta):
|
34
34
|
offset = offset.total_seconds() // 60
|
35
35
|
sign = "-" if offset < 0 else "+"
|
36
|
-
hhmm = "%02d%02d" % divmod(abs(offset), 60)
|
36
|
+
hhmm = "%02d%02d" % divmod(abs(offset), 60) # noqa: UP031
|
37
37
|
name = sign + hhmm
|
38
38
|
return timezone(timedelta(minutes=offset), name)
|
39
39
|
|
@@ -95,7 +95,7 @@ def activate(timezone):
|
|
95
95
|
elif isinstance(timezone, str):
|
96
96
|
_active.value = zoneinfo.ZoneInfo(timezone)
|
97
97
|
else:
|
98
|
-
raise ValueError("Invalid timezone:
|
98
|
+
raise ValueError(f"Invalid timezone: {timezone!r}")
|
99
99
|
|
100
100
|
|
101
101
|
def deactivate():
|
@@ -165,7 +165,7 @@ def now():
|
|
165
165
|
"""
|
166
166
|
Return a timezone aware datetime.
|
167
167
|
"""
|
168
|
-
return datetime.now(tz=
|
168
|
+
return datetime.now(tz=UTC)
|
169
169
|
|
170
170
|
|
171
171
|
# By design, these four functions don't perform any checks on their arguments.
|
@@ -204,7 +204,7 @@ def make_aware(value, timezone=None):
|
|
204
204
|
timezone = get_current_timezone()
|
205
205
|
# Check that we won't overwrite the timezone of an aware datetime.
|
206
206
|
if is_aware(value):
|
207
|
-
raise ValueError("make_aware expects a naive datetime, got
|
207
|
+
raise ValueError(f"make_aware expects a naive datetime, got {value}")
|
208
208
|
# This may be wrong around DST changes!
|
209
209
|
return value.replace(tzinfo=timezone)
|
210
210
|
|
plain/validators.py
CHANGED
@@ -314,9 +314,7 @@ def ip_address_validators(protocol, unpack_ipv4):
|
|
314
314
|
return ip_address_validator_map[protocol.lower()]
|
315
315
|
except KeyError:
|
316
316
|
raise ValueError(
|
317
|
-
"The protocol '{}' is unknown. Supported: {}"
|
318
|
-
protocol, list(ip_address_validator_map)
|
319
|
-
)
|
317
|
+
f"The protocol '{protocol}' is unknown. Supported: {list(ip_address_validator_map)}"
|
320
318
|
)
|
321
319
|
|
322
320
|
|
plain/views/forms.py
CHANGED
@@ -20,8 +20,8 @@ class FormView(TemplateView):
|
|
20
20
|
"""Return an instance of the form to be used in this view."""
|
21
21
|
if not self.form_class:
|
22
22
|
raise ImproperlyConfigured(
|
23
|
-
"No form class provided. Define {
|
24
|
-
"{
|
23
|
+
f"No form class provided. Define {self.__class__.__name__}.form_class or override "
|
24
|
+
f"{self.__class__.__name__}.get_form()."
|
25
25
|
)
|
26
26
|
return self.form_class(**self.get_form_kwargs())
|
27
27
|
|
@@ -40,7 +40,7 @@ class FormView(TemplateView):
|
|
40
40
|
)
|
41
41
|
return kwargs
|
42
42
|
|
43
|
-
def get_success_url(self) -> str:
|
43
|
+
def get_success_url(self, form: "BaseForm") -> str:
|
44
44
|
"""Return the URL to redirect to after processing a valid form."""
|
45
45
|
if not self.success_url:
|
46
46
|
raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
|
@@ -48,7 +48,7 @@ class FormView(TemplateView):
|
|
48
48
|
|
49
49
|
def form_valid(self, form: "BaseForm") -> Response:
|
50
50
|
"""If the form is valid, redirect to the supplied URL."""
|
51
|
-
return ResponseRedirect(self.get_success_url())
|
51
|
+
return ResponseRedirect(self.get_success_url(form))
|
52
52
|
|
53
53
|
def form_invalid(self, form: "BaseForm") -> Response:
|
54
54
|
"""If the form is invalid, render the invalid form."""
|
plain/views/objects.py
CHANGED
@@ -93,7 +93,7 @@ class CreateView(ObjectTemplateViewMixin, FormView):
|
|
93
93
|
self.object = None
|
94
94
|
|
95
95
|
# TODO? would rather you have to specify this...
|
96
|
-
def get_success_url(self):
|
96
|
+
def get_success_url(self, form):
|
97
97
|
"""Return the URL to redirect to after processing a valid form."""
|
98
98
|
if self.success_url:
|
99
99
|
url = self.success_url.format(**self.object.__dict__)
|
@@ -126,7 +126,7 @@ class UpdateView(ObjectTemplateViewMixin, FormView):
|
|
126
126
|
self.load_object()
|
127
127
|
return super().post()
|
128
128
|
|
129
|
-
def get_success_url(self):
|
129
|
+
def get_success_url(self, form):
|
130
130
|
"""Return the URL to redirect to after processing a valid form."""
|
131
131
|
if self.success_url:
|
132
132
|
url = self.success_url.format(**self.object.__dict__)
|
@@ -1,16 +1,12 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: plain
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.14.0
|
4
4
|
Summary: A web framework for building products with Python.
|
5
|
-
Author: Dave Gaeddert
|
6
|
-
|
7
|
-
Requires-Python: >=3.11
|
8
|
-
|
9
|
-
|
10
|
-
Classifier: Programming Language :: Python :: 3.12
|
11
|
-
Classifier: Programming Language :: Python :: 3.13
|
12
|
-
Requires-Dist: click (>=8.0.0)
|
13
|
-
Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
|
5
|
+
Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
|
6
|
+
License-File: LICENSE
|
7
|
+
Requires-Python: >=3.11
|
8
|
+
Requires-Dist: click>=8.0.0
|
9
|
+
Requires-Dist: jinja2>=3.1.2
|
14
10
|
Description-Content-Type: text/markdown
|
15
11
|
|
16
12
|
<!-- This file is compiled from plain/plain/README.md. Do not edit this file directly. -->
|
@@ -48,4 +44,3 @@ With the official Plain ecosystem packages you can:
|
|
48
44
|
- Build [staff tooling and admin dashboards](https://plainframework.com/docs/plain-staff/)
|
49
45
|
|
50
46
|
Learn more at [plainframework.com](https://plainframework.com).
|
51
|
-
|
@@ -1,5 +1,12 @@
|
|
1
1
|
plain/README.md,sha256=mFsLBpqiHvuznV98eewupvo1JoQeQG0q87wNlXit21A,1695
|
2
2
|
plain/__main__.py,sha256=BiYbF-txGNbeRqp_CHQ9EZ_bCbbKq2iw51Z8RRUgIBY,105
|
3
|
+
plain/debug.py,sha256=NxiWJHB4Gi5JfJ4cnxzl-PZ6IgpSnUxg7a_dTQNq_lQ,705
|
4
|
+
plain/exceptions.py,sha256=pPAO8cIzNoqa2YrxG0d31C4P2V9SZ2OprHcjY9-qhIU,6332
|
5
|
+
plain/json.py,sha256=McJdsbMT1sYwkGRG--f2NSZz0hVXPMix9x3nKaaak2o,1262
|
6
|
+
plain/paginator.py,sha256=zCHCmCG8FEet6C28w2rUko9yiGFnOGCQxRbM_ssa9XY,6037
|
7
|
+
plain/signing.py,sha256=sf7g1Mp-FzdjFAEoLxHyu2YvbUl5w4FOtTVDAfq6TO0,8733
|
8
|
+
plain/validators.py,sha256=2tZh2Bvp955gejltwVtFGwfqw5-9VAOhKqos3Me4paY,19923
|
9
|
+
plain/wsgi.py,sha256=R6k5FiAElvGDApEbMPTT0MPqSD7n2e2Az5chQqJZU0I,236
|
3
10
|
plain/assets/README.md,sha256=048BzyQ2-BcsRiv6NiuLHHijOw96RK-e6lJ_Eq7g2pc,2857
|
4
11
|
plain/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
12
|
plain/assets/compile.py,sha256=lsnciN85YjHe6d8VIKJi1L8r7NGHNzMOe9L87wObM5I,3287
|
@@ -7,49 +14,46 @@ plain/assets/finders.py,sha256=WEKAnXTmpuQ7UKLZz7vHPrKFdDOkuZSP6_JTKmIxrKg,1268
|
|
7
14
|
plain/assets/fingerprints.py,sha256=1NKAnnXVlncY5iimXztr0NL3RIjBKsNlZRIe6nmItJc,931
|
8
15
|
plain/assets/urls.py,sha256=ZTIoM1Zq35JaXZ3wFhXhfGa7VoITDNlH9i5RS0R5xow,933
|
9
16
|
plain/assets/views.py,sha256=dhjIpMu0GDR_VGbXM90_6RnC84C2C4bFv1RxDVklGBk,9173
|
10
|
-
plain/cli/README.md,sha256=
|
17
|
+
plain/cli/README.md,sha256=TvWCnNwb1rNthPzJglCRMKacN5H_RLeEjYBMe62Uz4M,2461
|
11
18
|
plain/cli/__init__.py,sha256=9ByBOIdM8DebChjNz-RH2atdz4vWe8somlwNEsbhwh4,40
|
12
|
-
plain/cli/cli.py,sha256=
|
19
|
+
plain/cli/cli.py,sha256=akk7koDuFXNpFPsSv4mzJNbtEuXTSXTeMFhFGUbbB9A,14426
|
13
20
|
plain/cli/formatting.py,sha256=1hZH13y1qwHcU2K2_Na388nw9uvoeQH8LrWL-O9h8Yc,2207
|
14
|
-
plain/cli/packages.py,sha256=
|
21
|
+
plain/cli/packages.py,sha256=FqRJaizaxpQ8lSS4nYNjqIGpYGDbeTmCXCvkGxusGOM,2160
|
15
22
|
plain/cli/print.py,sha256=XraUYrgODOJquIiEv78wSCYGRBplHXtXSS9QtFG5hqY,217
|
16
|
-
plain/cli/startup.py,sha256=
|
23
|
+
plain/cli/startup.py,sha256=3LIz9JrIZoF52Sa0j0SCypQwEaBDkhvuGaBdtiQLr5Q,680
|
17
24
|
plain/csrf/README.md,sha256=RXMWMtHmzf30gVVNOfj0kD4xlSqFIPgJh-n7dIciaEM,163
|
18
|
-
plain/csrf/middleware.py,sha256=
|
25
|
+
plain/csrf/middleware.py,sha256=pD9un9oLK6YNAEPW0rsVDsHyhyxmAA_IDE5hXs6mHiA,17776
|
19
26
|
plain/csrf/views.py,sha256=YDgT451X16iUdCxpQ6rcHIy7nD0u7DAvCQl5-Mx5i9Y,219
|
20
|
-
plain/debug.py,sha256=NxiWJHB4Gi5JfJ4cnxzl-PZ6IgpSnUxg7a_dTQNq_lQ,705
|
21
|
-
plain/exceptions.py,sha256=tDS6l0epe_L9IlxpEdT2k2hWgEoAu8YBNIumNCtJ-WY,6333
|
22
27
|
plain/forms/README.md,sha256=fglB9MmHiEgfGGdZmcRstNl6eYaFljrElu2mzapK52M,377
|
23
28
|
plain/forms/__init__.py,sha256=UxqPwB8CiYPCQdHmUc59jadqaXqDmXBH8y4bt9vTPms,226
|
24
29
|
plain/forms/boundfield.py,sha256=LhydhCVR0okrli0-QBMjGjAJ8-06gTCXVEaBZhBouQk,1741
|
25
30
|
plain/forms/exceptions.py,sha256=XCLDRl5snIEDu5-8mLB0NnU_tegcBfyIHMiJxqvbxnc,164
|
26
31
|
plain/forms/fields.py,sha256=86ZE9jac6Zyg5vKsYGgyOUOIQLKxO--UomGXwA65tk4,35103
|
27
|
-
plain/forms/forms.py,sha256
|
32
|
+
plain/forms/forms.py,sha256=CiONLo9VhE7E1-u-UejX4XWxxwu6MvR-lVPaVAY7VQM,10441
|
28
33
|
plain/http/README.md,sha256=00zLFQ-FPjYXu3A8QsLhCCXxaT0ImvI5I-8xd3dp8WA,7
|
29
34
|
plain/http/__init__.py,sha256=DIsDRbBsCGa4qZgq-fUuQS0kkxfbTU_3KpIM9VvH04w,1067
|
30
35
|
plain/http/cookie.py,sha256=11FnSG3Plo6T3jZDbPoCw7SKh9ExdBio3pTmIO03URg,597
|
31
|
-
plain/http/multipartparser.py,sha256=
|
32
|
-
plain/http/request.py,sha256=
|
33
|
-
plain/http/response.py,sha256=
|
36
|
+
plain/http/multipartparser.py,sha256=k6BhpilFENQQ1cuGix6aa-jGwbhBVms2A2O01-s3_4c,27304
|
37
|
+
plain/http/request.py,sha256=sqE83ZaB1P2i7iEUQ6d-K_6X4xhpdRdsPiTVraiDoNU,25984
|
38
|
+
plain/http/response.py,sha256=QtJM4m8_imbYXSkwOAtMu9WCRZktvTsa4ZmVUAvz6bI,24250
|
34
39
|
plain/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
35
40
|
plain/internal/files/README.md,sha256=kMux-NU5qiH0o1K8IajYQT8VjrYl_jLk9LkGG_kGuSc,45
|
36
41
|
plain/internal/files/__init__.py,sha256=VctFgox4Q1AWF3klPaoCC5GIw5KeLafYjY5JmN8mAVw,63
|
37
42
|
plain/internal/files/base.py,sha256=CSq_BPKWY2kUib7pnoIvzi40isEyrKdkx1yHb8nPtLc,4817
|
38
|
-
plain/internal/files/locks.py,sha256=
|
39
|
-
plain/internal/files/move.py,sha256=
|
43
|
+
plain/internal/files/locks.py,sha256=z03q7IZD4tPMK3s1HKF3w_uetkFj6w6FTheLUxZsfB0,3616
|
44
|
+
plain/internal/files/move.py,sha256=jfdD29QhamxZjXRgqmZS4dJoJ4sK6M7QK1Km-69jWeo,3238
|
40
45
|
plain/internal/files/temp.py,sha256=UJJnCI8dqPIC8XXHU3-jG2-0svbkrgGlBs4yhciLm4c,2506
|
41
46
|
plain/internal/files/uploadedfile.py,sha256=JRB7T3quQjg-1y3l1ASPxywtSQZhaeMc45uFPIxvl7c,4192
|
42
|
-
plain/internal/files/uploadhandler.py,sha256=
|
43
|
-
plain/internal/files/utils.py,sha256=
|
47
|
+
plain/internal/files/uploadhandler.py,sha256=eEnd5onstypjHYtg367PnVWwCaF1kAPlLPSV7goIf_E,7198
|
48
|
+
plain/internal/files/utils.py,sha256=xN4HTJXDRdcoNyrL1dFd528MBwodRlHZM8DGTD_oBIg,2646
|
44
49
|
plain/internal/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
45
|
-
plain/internal/handlers/base.py,sha256=
|
46
|
-
plain/internal/handlers/exception.py,sha256=
|
47
|
-
plain/internal/handlers/wsgi.py,sha256=
|
50
|
+
plain/internal/handlers/base.py,sha256=I6549DBZUb-anqox7arzL9wpypH4bUCxZkitO76hljI,4794
|
51
|
+
plain/internal/handlers/exception.py,sha256=DH9gh1FyqgetFpMaB8yLIVE6phBTVPKQLA1GIn9MOeI,4555
|
52
|
+
plain/internal/handlers/wsgi.py,sha256=EWoH9EeetfgiL0BtoAe2Aof0VWaBrxl74Y9EP4xx0wc,7553
|
48
53
|
plain/internal/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
54
|
plain/internal/middleware/headers.py,sha256=UnJWnlVVLD-10h7PB_QSYeREBzLo-TS3C-_ahmZ6w0I,636
|
50
55
|
plain/internal/middleware/https.py,sha256=XpuQK8HicYX1jNanQHqNgyQ9rqe4NLUOZO3ZzKdsP8k,1203
|
51
|
-
plain/internal/middleware/slash.py,sha256=
|
52
|
-
plain/json.py,sha256=McJdsbMT1sYwkGRG--f2NSZz0hVXPMix9x3nKaaak2o,1262
|
56
|
+
plain/internal/middleware/slash.py,sha256=1rZsSSzXAA-vBb-dc2RlZaKW9gTT8YM8fJzwYGL4swA,2575
|
53
57
|
plain/logs/README.md,sha256=H6uVXdInYlasq0Z1WnhWnPmNwYQoZ1MSLPDQ4ZE7u4A,492
|
54
58
|
plain/logs/__init__.py,sha256=rASvo4qFBDIHfkACmGLNGa6lRGbG9PbNjW6FmBt95ys,168
|
55
59
|
plain/logs/configure.py,sha256=6mV7d1IxkDYT3VBz61qhIj0Esuy5l5QdQfsHaGCfI6w,1063
|
@@ -57,26 +61,24 @@ plain/logs/loggers.py,sha256=iz9SYcwP9w5QAuwpULl48SFkVyJuuMoQ_fdLgdCHpNg,2121
|
|
57
61
|
plain/logs/utils.py,sha256=9UzdCCQXJinGDs71Ngw297mlWkhgZStSd67ya4NOW98,1257
|
58
62
|
plain/packages/README.md,sha256=Vq1Nw3mmEmZ2IriQavuVi4BjcQC2nb8k7YIbnm8QjIg,799
|
59
63
|
plain/packages/__init__.py,sha256=DnHN1wwHXiXib4Y9BV__x9WrbUaTovoTIxW-tVyScTU,106
|
60
|
-
plain/packages/config.py,sha256
|
61
|
-
plain/packages/registry.py,sha256=
|
62
|
-
plain/paginator.py,sha256=4v5SbYotJH9HoNdzf-1j-AEy4ZLbLPuysf-VME4-6e0,6055
|
64
|
+
plain/packages/config.py,sha256=-je13ViDkXMEQXCSi0G539dG5_HPoFBKaK7cBEd_NAY,11070
|
65
|
+
plain/packages/registry.py,sha256=C1yv-JlzMs4HE-AYam_EdiTKNQwE8IN4qgt22PKxHDE,17920
|
63
66
|
plain/preflight/README.md,sha256=fgcfVRD6rq7IO8AffQhk49c-6akxaE8MQidRp69InDQ,59
|
64
67
|
plain/preflight/__init__.py,sha256=H-TNRvaddPtOGmv4RXoc1fxDV1AOb7_K3u7ECF8mV58,607
|
65
68
|
plain/preflight/files.py,sha256=wbHCNgps7o1c1zQNBd8FDCaVaqX90UwuvLgEQ_DbUpY,510
|
66
|
-
plain/preflight/messages.py,sha256=
|
67
|
-
plain/preflight/registry.py,sha256=
|
69
|
+
plain/preflight/messages.py,sha256=HwatjA6MRFfzFAnSOa_uAw1Pvk_CLuNfW3IYi71_1Mk,2322
|
70
|
+
plain/preflight/registry.py,sha256=7s7f_iEwURzv-Ye515P5lJWcHltd5Ca2fsX1Wpbf1wQ,2306
|
68
71
|
plain/preflight/security.py,sha256=sNpv5AHobPcaO48cOUGRNe2EjusTducjY8vyShR8EhI,2645
|
69
|
-
plain/preflight/urls.py,sha256=
|
72
|
+
plain/preflight/urls.py,sha256=VjK_B6tg22ugPO5VLY9gBCkDPW0mhcRr-64iv_12zCw,3003
|
70
73
|
plain/runtime/README.md,sha256=Q8VVO7JRGuYrDxzuYL6ptoilhclbecxKzpRXKgbWGkU,2061
|
71
74
|
plain/runtime/__init__.py,sha256=DH8TwKTGJhjviOy4yh_d051v8YGaAWMlFBPhK8ZuC9g,1499
|
72
|
-
plain/runtime/global_settings.py,sha256
|
73
|
-
plain/runtime/user_settings.py,sha256
|
75
|
+
plain/runtime/global_settings.py,sha256=T871IxXVARnpvxLOGh2YT0PTmafxcH18TuPUNNTE8vo,5499
|
76
|
+
plain/runtime/user_settings.py,sha256=r6uQ-h0QxX3gSB_toJJekEbSikXXdNSb8ykUtwGTpdY,11280
|
74
77
|
plain/signals/README.md,sha256=cd3tKEgH-xc88CUWyDxl4-qv-HBXx8VT32BXVwA5azA,230
|
75
78
|
plain/signals/__init__.py,sha256=eAs0kLqptuP6I31dWXeAqRNji3svplpAV4Ez6ktjwXM,131
|
76
79
|
plain/signals/dispatch/__init__.py,sha256=FzEygqV9HsM6gopio7O2Oh_X230nA4d5Q9s0sUjMq0E,292
|
77
80
|
plain/signals/dispatch/dispatcher.py,sha256=VxSlqn9PCOTghPPJLOqZPs6FNQZfV2BJpMfFMSg6Dtc,11531
|
78
81
|
plain/signals/dispatch/license.txt,sha256=o9EhDhsC4Q5HbmD-IfNGVTEkXtNE33r5rIt3lleJ8gc,1727
|
79
|
-
plain/signing.py,sha256=V6A6PTDYWekuwtQRI1iFD8dud5OHPZTv4EkeoZEHoXo,8737
|
80
82
|
plain/templates/README.md,sha256=VfA2HmrklG5weE1md85q9g84cWnMBEiXAynKzM7S1Sk,464
|
81
83
|
plain/templates/__init__.py,sha256=bX76FakE9T7mfK3N0deN85HlwHNQpeigytSC9Z8LcOs,451
|
82
84
|
plain/templates/core.py,sha256=iw58EAmyyv8N5HDA-Sq4-fLgz_qx8v8WJfurgR116jw,625
|
@@ -88,59 +90,56 @@ plain/templates/jinja/filters.py,sha256=3KJKKbxcv9dLzUDWPcaa88k3NU2m1GG3iMIgFhzX
|
|
88
90
|
plain/templates/jinja/globals.py,sha256=qhvQuikkRkOTpHSW5FwdsvoViJNlRgHq3-O7ZyeajsE,669
|
89
91
|
plain/test/README.md,sha256=Zso3Ir7a8vQerzKB6egjROQWkpveLAbscn7VTROPAiU,37
|
90
92
|
plain/test/__init__.py,sha256=rXe88Y602NP8DBnReSyXb7dUzKoWweLuT43j-qwOUl4,138
|
91
|
-
plain/test/client.py,sha256=
|
93
|
+
plain/test/client.py,sha256=MYAvM1HOSfEktNEOSb9662qkhHfvnxYlY00Ckj3dL3I,31349
|
92
94
|
plain/urls/README.md,sha256=pWnCvgYkWN7rG7hSyBOtX4ZUP3iO7FhqM6lvwwYll6c,33
|
93
95
|
plain/urls/__init__.py,sha256=3UzwIufXjIks2K_X_Vms2MV19IqvyPLrXUeHU3WP47c,753
|
94
|
-
plain/urls/base.py,sha256=
|
95
|
-
plain/urls/conf.py,sha256=
|
96
|
+
plain/urls/base.py,sha256=Q1TzI5WvqYkN1U81fDom1q-AID4dXbszEMF6wAeTAI0,3717
|
97
|
+
plain/urls/conf.py,sha256=3R2dCr4iryMPXgUZqESZvqrhTLa5I7PzNY98SuCYYec,3491
|
96
98
|
plain/urls/converters.py,sha256=s2JZVOdzZC16lgobsI93hygcdH5L0Kj4742WEkXsVcs,1193
|
97
99
|
plain/urls/exceptions.py,sha256=q4iPh3Aa-zHbA-tw8v6WyX1J1n5WdAady2xvxFuyXB0,114
|
98
|
-
plain/urls/resolvers.py,sha256=
|
100
|
+
plain/urls/resolvers.py,sha256=Fb2F1cdTxKDn0ZvNX81zHBglZs_aKMgnFuSIJVxNiUk,27508
|
99
101
|
plain/utils/README.md,sha256=Bf5OG-MkOJDz_U8RGVreDfAI4M4nnPaLtk-LdinxHSc,99
|
100
102
|
plain/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
101
103
|
plain/utils/_os.py,sha256=oqfiKZRbmHwwWSZP36SIajQnFDbImbB6gcyK0idAGl4,1988
|
102
|
-
plain/utils/cache.py,sha256=
|
104
|
+
plain/utils/cache.py,sha256=SM0jXxWU3XbaMPkthCsYWDYdZH5eOgGzKUgxV1ISNyE,11476
|
103
105
|
plain/utils/connection.py,sha256=NN7xRhy6qIWuOOhi1x9YdGcFcYhKepTiMUETeEMS0vY,2501
|
104
|
-
plain/utils/crypto.py,sha256=
|
105
|
-
plain/utils/datastructures.py,sha256=
|
106
|
-
plain/utils/dateformat.py,sha256=
|
107
|
-
plain/utils/dateparse.py,sha256=
|
106
|
+
plain/utils/crypto.py,sha256=zFDydnaqNMGYFHUc-CAn8f93685a17BhGusAcITH1lI,2662
|
107
|
+
plain/utils/datastructures.py,sha256=g4UYTbxIb_n8F9JWMP4dHPwUz71591fHreGATPO4qEc,10240
|
108
|
+
plain/utils/dateformat.py,sha256=nsy71l16QuPN0ozGpVlCU5Et101fhk9L38F-wqT5p5I,10203
|
109
|
+
plain/utils/dateparse.py,sha256=u9_tF85YteXSjW9KQzNg_pcCEFDZS3EGorCddcWU0vE,5351
|
108
110
|
plain/utils/dates.py,sha256=hSDKz8eIb3W5QjmGiklFZZELB0inYXsfiRUy49Cx-2Q,1226
|
109
111
|
plain/utils/deconstruct.py,sha256=7NwEFIDCiadAArUBFmiErzDgfIgDWeKqqQFDXwSgQoQ,1830
|
110
|
-
plain/utils/decorators.py,sha256=
|
112
|
+
plain/utils/decorators.py,sha256=6pcFQ5TezYZoO5Ys1KnImOsv0nUMsFCdxal-S6brUOc,3468
|
111
113
|
plain/utils/deprecation.py,sha256=qtj33kHEmJU7mGSrNcKidZsMo5W8MN-zrXzUq3aVVy8,131
|
112
114
|
plain/utils/duration.py,sha256=l0Gc41-DeyyAmpdy2XG-YO5UKxMf1NDpWIlQuD5hAn0,1162
|
113
115
|
plain/utils/email.py,sha256=puRTBVuz44YvpnqV3LT4nNIKqdqfY3L8zbDJIkqHk2Y,328
|
114
116
|
plain/utils/encoding.py,sha256=z8c7HxYW6wQiE4csx5Ui3WvzgbDzLGXY2aCP04_GZd4,7900
|
115
117
|
plain/utils/functional.py,sha256=7Z1LuPpS1y0ignX7QDzPq6zJqDZdCf_rFYLRR1rTSG0,14806
|
116
118
|
plain/utils/hashable.py,sha256=uLWobCCh7VcEPJ7xzVGPgigNVuTazYJbyzRzHTCI_wo,739
|
117
|
-
plain/utils/html.py,sha256=
|
118
|
-
plain/utils/http.py,sha256=
|
119
|
+
plain/utils/html.py,sha256=K8nJSiNRU54htQOfhezZAYspXaDGN3LjOE3SxmnQml8,13513
|
120
|
+
plain/utils/http.py,sha256=OmnqW_nYUaFN-pAEwjTEh9AkpfjKGmE51Ge6inNGB10,12710
|
119
121
|
plain/utils/inspect.py,sha256=lhDEOtmSLEub5Jj__MIgW3AyWOEVkaA6doJKKwBhZ6A,2235
|
120
|
-
plain/utils/ipv6.py,sha256=
|
122
|
+
plain/utils/ipv6.py,sha256=pISQ2AIlG8xXlxpphn388q03fq-fOrlu4GZR0YYjQXw,1267
|
121
123
|
plain/utils/itercompat.py,sha256=lacIDjczhxbwG4ON_KfG1H6VNPOGOpbRhnVhbedo2CY,184
|
122
|
-
plain/utils/module_loading.py,sha256=
|
123
|
-
plain/utils/regex_helper.py,sha256=
|
124
|
+
plain/utils/module_loading.py,sha256=CWl7Shoax9Zkevf1pM9PpS_0V69J5Cukjyj078UPCAw,2252
|
125
|
+
plain/utils/regex_helper.py,sha256=pAdh_xG52BOyXLsiuIMPFgduUAoWOEje1ZpjhcefxiA,12769
|
124
126
|
plain/utils/safestring.py,sha256=SHGhpbX6FFDKSYOY9zYAgAQX0g0exzRba7dM2bJalWs,1873
|
125
|
-
plain/utils/
|
126
|
-
plain/utils/
|
127
|
-
plain/utils/
|
128
|
-
plain/utils/timezone.py,sha256=AZ7lcmUjofUTfQUb08pHXu0u7TDuPJpMRB5lgvE4E0w,6212
|
127
|
+
plain/utils/text.py,sha256=qX7vECGH4Xk96qZRH9A1IyZA-mrJ-j62j3kDcLTdWK0,16586
|
128
|
+
plain/utils/timesince.py,sha256=d-9w_fqKS24IrHsh9-UX6SPNwrQvDp6QhZ_aJzM6AKY,4749
|
129
|
+
plain/utils/timezone.py,sha256=6u0sE-9RVp0_OCe0Y1KiYYQoq5THWLokZFQYY8jf78g,6221
|
129
130
|
plain/utils/tree.py,sha256=wdWzmfsgc26YDF2wxhAY3yVxXTixQYqYDKE9mL3L3ZY,4383
|
130
|
-
plain/validators.py,sha256=L9v9KtTe4iZhZVramZdKGf33R5Tt95FCdg2AJD2-2n0,19963
|
131
131
|
plain/views/README.md,sha256=qndsXKyNMnipPlLaAvgQeGxqXknNQwlFh31Yxk8rHp8,5994
|
132
132
|
plain/views/__init__.py,sha256=a-N1nkklVohJTtz0yD1MMaS0g66HviEjsKydNVVjvVc,392
|
133
133
|
plain/views/base.py,sha256=wMkCAbr3XqXyP8dJr-O9atA1-N6K4-cTFflLhSYGOpY,3227
|
134
134
|
plain/views/csrf.py,sha256=gO9npd_Ut_LoYF_u7Qb_ZsPRfSeE3aTPG97XlMp4oEo,724
|
135
135
|
plain/views/errors.py,sha256=Y4oGX4Z6D2COKcDEfINvXE1acE8Ad15KwNNWPs5BCfc,967
|
136
136
|
plain/views/exceptions.py,sha256=b4euI49ZUKS9O8AGAcFfiDpstzkRAuuj_uYQXzWNHME,138
|
137
|
-
plain/views/forms.py,sha256=
|
138
|
-
plain/views/objects.py,sha256=
|
137
|
+
plain/views/forms.py,sha256=RhlaUcZCkeqokY_fvv-NOS-kgZAG4XhDLOPbf9K_Zlc,2691
|
138
|
+
plain/views/objects.py,sha256=fRfS6KNehIGqkbPw4nSafj8HStxYExHmbggolBbzcxs,7921
|
139
139
|
plain/views/redirect.py,sha256=KLnlktzK6ZNMTlaEiZpMKQMEP5zeTgGLJ9BIkIJfwBo,1733
|
140
140
|
plain/views/templates.py,sha256=nF9CcdhhjAyp3LB0RrSYnBaHpHzMfPSw719RCdcXk7o,2007
|
141
|
-
plain/
|
142
|
-
plain-0.
|
143
|
-
plain-0.
|
144
|
-
plain-0.
|
145
|
-
plain-0.
|
146
|
-
plain-0.13.1.dist-info/RECORD,,
|
141
|
+
plain-0.14.0.dist-info/METADATA,sha256=UKEFaEDF6gB5zjqdWzqqolLCSoSl7Yi71ucPB32Kk4E,2518
|
142
|
+
plain-0.14.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
143
|
+
plain-0.14.0.dist-info/entry_points.txt,sha256=DHHprvufgd7xypiBiqMANYRnpJ9xPPYhYbnPGwOkWqE,40
|
144
|
+
plain-0.14.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
145
|
+
plain-0.14.0.dist-info/RECORD,,
|