plain 0.57.0__py3-none-any.whl → 0.59.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/CHANGELOG.md +28 -0
- plain/cli/agent.py +6 -0
- plain/cli/upgrade.py +1 -0
- plain/csrf/README.md +61 -9
- plain/csrf/middleware.py +95 -406
- plain/exceptions.py +29 -13
- plain/forms/README.md +0 -2
- plain/internal/middleware/https.py +5 -3
- plain/runtime/global_settings.py +51 -52
- plain/runtime/utils.py +20 -0
- plain/templates/README.md +0 -1
- plain/test/client.py +2 -10
- plain/views/README.md +0 -2
- plain/views/objects.py +39 -66
- plain/views/templates.py +6 -18
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/METADATA +1 -1
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/RECORD +20 -20
- plain/views/csrf.py +0 -4
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/WHEEL +0 -0
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/entry_points.txt +0 -0
- {plain-0.57.0.dist-info → plain-0.59.0.dist-info}/licenses/LICENSE +0 -0
plain/runtime/global_settings.py
CHANGED
@@ -3,31 +3,24 @@ Default Plain settings. Override these with settings in the module pointed to
|
|
3
3
|
by the PLAIN_SETTINGS_MODULE environment variable.
|
4
4
|
"""
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
from .utils import get_app_name_from_pyproject
|
7
|
+
|
8
|
+
# MARK: Core Settings
|
9
9
|
|
10
10
|
DEBUG: bool = False
|
11
11
|
|
12
|
-
|
13
|
-
# "*" matches anything, ".example.com" matches example.com and all subdomains
|
14
|
-
ALLOWED_HOSTS: list[str] = []
|
15
|
-
|
16
|
-
# Local time zone for this installation. All choices can be found here:
|
17
|
-
# https://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
|
18
|
-
# systems may support all possibilities). This is interpreted as the default
|
19
|
-
# user time zone.
|
20
|
-
TIME_ZONE: str = "UTC"
|
21
|
-
|
22
|
-
# Default charset to use for all Response objects, if a MIME type isn't
|
23
|
-
# manually specified. It's used to construct the Content-Type header.
|
24
|
-
DEFAULT_CHARSET = "utf-8"
|
12
|
+
APP_NAME: str = get_app_name_from_pyproject()
|
25
13
|
|
26
14
|
# List of strings representing installed packages.
|
27
15
|
INSTALLED_PACKAGES: list[str] = []
|
28
16
|
|
29
|
-
|
30
|
-
|
17
|
+
URLS_ROUTER: str
|
18
|
+
|
19
|
+
# MARK: HTTP and Security
|
20
|
+
|
21
|
+
# Hosts/domain names that are valid for this site.
|
22
|
+
# "*" matches anything, ".example.com" matches example.com and all subdomains
|
23
|
+
ALLOWED_HOSTS: list[str] = []
|
31
24
|
|
32
25
|
# Default headers for all responses.
|
33
26
|
DEFAULT_RESPONSE_HEADERS = {
|
@@ -42,7 +35,13 @@ DEFAULT_RESPONSE_HEADERS = {
|
|
42
35
|
|
43
36
|
# Whether to redirect all non-HTTPS requests to HTTPS.
|
44
37
|
HTTPS_REDIRECT_ENABLED = True
|
45
|
-
|
38
|
+
|
39
|
+
# Regex patterns for paths that should be exempt from HTTPS redirect
|
40
|
+
# Examples: [r"^/health$", r"/api/internal/.*", r"/dev/.*"]
|
41
|
+
HTTPS_REDIRECT_EXEMPT_PATHS: list[str] = []
|
42
|
+
|
43
|
+
# Custom host to redirect to for HTTPS. If None, uses the same host as the request.
|
44
|
+
# Useful for redirecting to a different domain (e.g., "secure.example.com")
|
46
45
|
HTTPS_REDIRECT_HOST = None
|
47
46
|
|
48
47
|
# If your Plain app is behind a proxy that sets a header to specify secure
|
@@ -68,7 +67,24 @@ SECRET_KEY: str
|
|
68
67
|
# secret key rotation.
|
69
68
|
SECRET_KEY_FALLBACKS: list[str] = []
|
70
69
|
|
71
|
-
|
70
|
+
# MARK: Internationalization
|
71
|
+
|
72
|
+
# Local time zone for this installation. All choices can be found here:
|
73
|
+
# https://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
|
74
|
+
# systems may support all possibilities). This is interpreted as the default
|
75
|
+
# user time zone.
|
76
|
+
TIME_ZONE: str = "UTC"
|
77
|
+
|
78
|
+
# Default charset to use for all Response objects, if a MIME type isn't
|
79
|
+
# manually specified. It's used to construct the Content-Type header.
|
80
|
+
DEFAULT_CHARSET = "utf-8"
|
81
|
+
|
82
|
+
# MARK: URL Configuration
|
83
|
+
|
84
|
+
# Whether to append trailing slashes to URLs.
|
85
|
+
APPEND_SLASH = True
|
86
|
+
|
87
|
+
# MARK: File Uploads
|
72
88
|
|
73
89
|
# List of upload handler classes to be applied in order.
|
74
90
|
FILE_UPLOAD_HANDLERS = [
|
@@ -100,41 +116,30 @@ FILE_UPLOAD_TEMP_DIR = None
|
|
100
116
|
# User-defined overrides for error views by status code
|
101
117
|
HTTP_ERROR_VIEWS: dict[int] = {}
|
102
118
|
|
103
|
-
|
104
|
-
# MIDDLEWARE #
|
105
|
-
##############
|
119
|
+
# MARK: Middleware
|
106
120
|
|
107
121
|
# List of middleware to use. Order is important; in the request phase, these
|
108
122
|
# middleware will be applied in the order given, and in the response
|
109
123
|
# phase the middleware will be applied in reverse order.
|
110
124
|
MIDDLEWARE: list[str] = []
|
111
125
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
#
|
117
|
-
CSRF_COOKIE_NAME = "csrftoken"
|
118
|
-
CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52 # 1 year
|
119
|
-
CSRF_COOKIE_DOMAIN = None
|
120
|
-
CSRF_COOKIE_PATH = "/"
|
121
|
-
CSRF_COOKIE_SECURE = True
|
122
|
-
CSRF_COOKIE_HTTPONLY = False
|
123
|
-
CSRF_COOKIE_SAMESITE = "Lax"
|
124
|
-
CSRF_HEADER_NAME = "CSRF-Token"
|
125
|
-
CSRF_FIELD_NAME = "_csrftoken"
|
126
|
+
# MARK: CSRF
|
127
|
+
|
128
|
+
# A list of trusted origins for unsafe (POST/PUT/DELETE etc.) requests.
|
129
|
+
# These origins will be allowed regardless of the normal CSRF checks.
|
130
|
+
# Each origin should be a full origin like "https://example.com" or "https://sub.example.com:8080"
|
126
131
|
CSRF_TRUSTED_ORIGINS: list[str] = []
|
127
132
|
|
128
|
-
|
129
|
-
#
|
130
|
-
|
133
|
+
# Regex patterns for paths that should be exempt from CSRF protection
|
134
|
+
# Examples: [r"^/api/", r"/webhooks/.*", r"/health$"]
|
135
|
+
CSRF_EXEMPT_PATHS: list[str] = []
|
136
|
+
|
137
|
+
# MARK: Logging
|
131
138
|
|
132
139
|
# Custom logging configuration.
|
133
140
|
LOGGING = {}
|
134
141
|
|
135
|
-
|
136
|
-
# ASSETS #
|
137
|
-
###############
|
142
|
+
# MARK: Assets
|
138
143
|
|
139
144
|
# Whether to redirect the original asset path to the fingerprinted path.
|
140
145
|
ASSETS_REDIRECT_ORIGINAL = True
|
@@ -143,9 +148,7 @@ ASSETS_REDIRECT_ORIGINAL = True
|
|
143
148
|
# Ex. "https://cdn.example.com/assets/"
|
144
149
|
ASSETS_BASE_URL: str = ""
|
145
150
|
|
146
|
-
|
147
|
-
# PREFLIGHT CHECKS #
|
148
|
-
####################
|
151
|
+
# MARK: Preflight Checks
|
149
152
|
|
150
153
|
# List of all issues generated by system checks that should be silenced. Light
|
151
154
|
# issues like warnings, infos or debugs will not generate a message. Silencing
|
@@ -153,14 +156,10 @@ ASSETS_BASE_URL: str = ""
|
|
153
156
|
# message, but Plain will not stop you from e.g. running server.
|
154
157
|
PREFLIGHT_SILENCED_CHECKS = []
|
155
158
|
|
156
|
-
|
157
|
-
# Templates #
|
158
|
-
#############
|
159
|
+
# MARK: Templates
|
159
160
|
|
160
161
|
TEMPLATES_JINJA_ENVIRONMENT = "plain.templates.jinja.DefaultEnvironment"
|
161
162
|
|
162
|
-
|
163
|
-
# Shell #
|
164
|
-
#########
|
163
|
+
# MARK: Shell
|
165
164
|
|
166
165
|
SHELL_IMPORT: str = ""
|
plain/runtime/utils.py
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
import tomllib
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
|
5
|
+
def get_app_name_from_pyproject():
|
6
|
+
"""Get the project name from the nearest pyproject.toml file."""
|
7
|
+
current_path = Path.cwd()
|
8
|
+
|
9
|
+
# Walk up the directory tree looking for pyproject.toml
|
10
|
+
for path in [current_path] + list(current_path.parents):
|
11
|
+
pyproject_path = path / "pyproject.toml"
|
12
|
+
if pyproject_path.exists():
|
13
|
+
try:
|
14
|
+
with pyproject_path.open("rb") as f:
|
15
|
+
pyproject = tomllib.load(f)
|
16
|
+
return pyproject.get("project", {}).get("name", "App")
|
17
|
+
except (tomllib.TOMLDecodeError, OSError):
|
18
|
+
continue
|
19
|
+
|
20
|
+
return "App"
|
plain/templates/README.md
CHANGED
plain/test/client.py
CHANGED
@@ -114,8 +114,7 @@ class ClientHandler(BaseHandler):
|
|
114
114
|
the originating WSGIRequest attached to its ``wsgi_request`` attribute.
|
115
115
|
"""
|
116
116
|
|
117
|
-
def __init__(self,
|
118
|
-
self.enforce_csrf_checks = enforce_csrf_checks
|
117
|
+
def __init__(self, *args, **kwargs):
|
119
118
|
super().__init__(*args, **kwargs)
|
120
119
|
|
121
120
|
def __call__(self, environ):
|
@@ -126,12 +125,6 @@ class ClientHandler(BaseHandler):
|
|
126
125
|
|
127
126
|
request_started.send(sender=self.__class__, environ=environ)
|
128
127
|
request = WSGIRequest(environ)
|
129
|
-
# sneaky little hack so that we can easily get round
|
130
|
-
# CsrfViewMiddleware. This makes life easier, and is probably
|
131
|
-
# required for backwards compatibility with external tests against
|
132
|
-
# admin views.
|
133
|
-
if not self.enforce_csrf_checks:
|
134
|
-
request.csrf_exempt = True
|
135
128
|
|
136
129
|
# Request goes through middleware.
|
137
130
|
response = self.get_response(request)
|
@@ -420,14 +413,13 @@ class Client(RequestFactory):
|
|
420
413
|
|
421
414
|
def __init__(
|
422
415
|
self,
|
423
|
-
enforce_csrf_checks=False,
|
424
416
|
raise_request_exception=True,
|
425
417
|
*,
|
426
418
|
headers=None,
|
427
419
|
**defaults,
|
428
420
|
):
|
429
421
|
super().__init__(headers=headers, **defaults)
|
430
|
-
self.handler = ClientHandler(
|
422
|
+
self.handler = ClientHandler()
|
431
423
|
self.raise_request_exception = raise_request_exception
|
432
424
|
self.exc_info = None
|
433
425
|
self.extra = None
|
plain/views/README.md
CHANGED
plain/views/objects.py
CHANGED
@@ -1,28 +1,55 @@
|
|
1
|
+
from functools import cached_property
|
2
|
+
|
1
3
|
from plain.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
2
4
|
from plain.forms import Form
|
3
|
-
from plain.http import Http404
|
5
|
+
from plain.http import Http404
|
4
6
|
|
5
7
|
from .forms import FormView
|
6
8
|
from .templates import TemplateView
|
7
9
|
|
8
10
|
|
11
|
+
class CreateView(FormView):
|
12
|
+
"""
|
13
|
+
View for creating a new object, with a response rendered by a template.
|
14
|
+
"""
|
15
|
+
|
16
|
+
# TODO? would rather you have to specify this...
|
17
|
+
def get_success_url(self, form):
|
18
|
+
"""Return the URL to redirect to after processing a valid form."""
|
19
|
+
if self.success_url:
|
20
|
+
url = self.success_url.format(**self.object.__dict__)
|
21
|
+
else:
|
22
|
+
try:
|
23
|
+
url = self.object.get_absolute_url()
|
24
|
+
except AttributeError:
|
25
|
+
raise ImproperlyConfigured(
|
26
|
+
"No URL to redirect to. Either provide a url or define"
|
27
|
+
" a get_absolute_url method on the Model."
|
28
|
+
)
|
29
|
+
return url
|
30
|
+
|
31
|
+
def form_valid(self, form):
|
32
|
+
"""If the form is valid, save the associated model."""
|
33
|
+
self.object = form.save()
|
34
|
+
return super().form_valid(form)
|
35
|
+
|
36
|
+
|
9
37
|
class ObjectTemplateViewMixin:
|
10
38
|
context_object_name = ""
|
11
39
|
|
12
|
-
|
13
|
-
|
14
|
-
return self.render_template()
|
15
|
-
|
16
|
-
def load_object(self) -> None:
|
40
|
+
@cached_property
|
41
|
+
def object(self):
|
17
42
|
try:
|
18
|
-
|
43
|
+
obj = self.get_object()
|
19
44
|
except ObjectDoesNotExist:
|
20
45
|
raise Http404
|
21
46
|
|
22
|
-
if not
|
47
|
+
if not obj:
|
23
48
|
# Also raise 404 if the object is None
|
24
49
|
raise Http404
|
25
50
|
|
51
|
+
return obj
|
52
|
+
|
26
53
|
def get_object(self): # Intentionally untyped... subclasses must override this.
|
27
54
|
raise NotImplementedError(
|
28
55
|
f"get_object() is not implemented on {self.__class__.__name__}"
|
@@ -50,55 +77,9 @@ class DetailView(ObjectTemplateViewMixin, TemplateView):
|
|
50
77
|
pass
|
51
78
|
|
52
79
|
|
53
|
-
class CreateView(ObjectTemplateViewMixin, FormView):
|
54
|
-
"""
|
55
|
-
View for creating a new object, with a response rendered by a template.
|
56
|
-
"""
|
57
|
-
|
58
|
-
def post(self) -> Response:
|
59
|
-
"""
|
60
|
-
Handle POST requests: instantiate a form instance with the passed
|
61
|
-
POST variables and then check if it's valid.
|
62
|
-
"""
|
63
|
-
# Context expects self.object to exist
|
64
|
-
self.load_object()
|
65
|
-
return super().post()
|
66
|
-
|
67
|
-
def load_object(self) -> None:
|
68
|
-
self.object = None
|
69
|
-
|
70
|
-
# TODO? would rather you have to specify this...
|
71
|
-
def get_success_url(self, form):
|
72
|
-
"""Return the URL to redirect to after processing a valid form."""
|
73
|
-
if self.success_url:
|
74
|
-
url = self.success_url.format(**self.object.__dict__)
|
75
|
-
else:
|
76
|
-
try:
|
77
|
-
url = self.object.get_absolute_url()
|
78
|
-
except AttributeError:
|
79
|
-
raise ImproperlyConfigured(
|
80
|
-
"No URL to redirect to. Either provide a url or define"
|
81
|
-
" a get_absolute_url method on the Model."
|
82
|
-
)
|
83
|
-
return url
|
84
|
-
|
85
|
-
def form_valid(self, form):
|
86
|
-
"""If the form is valid, save the associated model."""
|
87
|
-
self.object = form.save()
|
88
|
-
return super().form_valid(form)
|
89
|
-
|
90
|
-
|
91
80
|
class UpdateView(ObjectTemplateViewMixin, FormView):
|
92
81
|
"""View for updating an object, with a response rendered by a template."""
|
93
82
|
|
94
|
-
def post(self) -> Response:
|
95
|
-
"""
|
96
|
-
Handle POST requests: instantiate a form instance with the passed
|
97
|
-
POST variables and then check if it's valid.
|
98
|
-
"""
|
99
|
-
self.load_object()
|
100
|
-
return super().post()
|
101
|
-
|
102
83
|
def get_success_url(self, form):
|
103
84
|
"""Return the URL to redirect to after processing a valid form."""
|
104
85
|
if self.success_url:
|
@@ -115,7 +96,7 @@ class UpdateView(ObjectTemplateViewMixin, FormView):
|
|
115
96
|
|
116
97
|
def form_valid(self, form):
|
117
98
|
"""If the form is valid, save the associated model."""
|
118
|
-
|
99
|
+
form.save()
|
119
100
|
return super().form_valid(form)
|
120
101
|
|
121
102
|
def get_form_kwargs(self):
|
@@ -141,14 +122,6 @@ class DeleteView(ObjectTemplateViewMixin, FormView):
|
|
141
122
|
|
142
123
|
form_class = EmptyDeleteForm
|
143
124
|
|
144
|
-
def post(self) -> Response:
|
145
|
-
"""
|
146
|
-
Handle POST requests: instantiate a form instance with the passed
|
147
|
-
POST variables and then check if it's valid.
|
148
|
-
"""
|
149
|
-
self.load_object()
|
150
|
-
return super().post()
|
151
|
-
|
152
125
|
def get_form_kwargs(self):
|
153
126
|
"""Return the keyword arguments for instantiating the form."""
|
154
127
|
kwargs = super().get_form_kwargs()
|
@@ -169,9 +142,9 @@ class ListView(TemplateView):
|
|
169
142
|
|
170
143
|
context_object_name = ""
|
171
144
|
|
172
|
-
|
173
|
-
|
174
|
-
return
|
145
|
+
@cached_property
|
146
|
+
def objects(self):
|
147
|
+
return self.get_objects()
|
175
148
|
|
176
149
|
def get_objects(self):
|
177
150
|
raise NotImplementedError(
|
plain/views/templates.py
CHANGED
@@ -1,27 +1,11 @@
|
|
1
|
-
from plain.csrf.middleware import get_token
|
2
1
|
from plain.exceptions import ImproperlyConfigured
|
3
2
|
from plain.http import Response
|
4
3
|
from plain.runtime import settings
|
5
4
|
from plain.templates import Template, TemplateFileMissing
|
6
|
-
from plain.utils.functional import lazy
|
7
|
-
from plain.utils.html import format_html
|
8
|
-
from plain.utils.safestring import SafeString
|
9
5
|
|
10
6
|
from .base import View
|
11
7
|
|
12
8
|
|
13
|
-
def csrf_input(request):
|
14
|
-
return format_html(
|
15
|
-
'<input type="hidden" name="{}" value="{}">',
|
16
|
-
settings.CSRF_FIELD_NAME,
|
17
|
-
get_token(request),
|
18
|
-
)
|
19
|
-
|
20
|
-
|
21
|
-
csrf_input_lazy = lazy(csrf_input, SafeString, str)
|
22
|
-
csrf_token_lazy = lazy(get_token, str)
|
23
|
-
|
24
|
-
|
25
9
|
class TemplateView(View):
|
26
10
|
"""
|
27
11
|
Render a template.
|
@@ -37,8 +21,6 @@ class TemplateView(View):
|
|
37
21
|
return {
|
38
22
|
"request": self.request,
|
39
23
|
"template_names": self.get_template_names(),
|
40
|
-
"csrf_input": csrf_input_lazy(self.request),
|
41
|
-
"csrf_token": csrf_token_lazy(self.request),
|
42
24
|
"DEBUG": settings.DEBUG,
|
43
25
|
}
|
44
26
|
|
@@ -54,6 +36,12 @@ class TemplateView(View):
|
|
54
36
|
def get_template(self) -> Template:
|
55
37
|
template_names = self.get_template_names()
|
56
38
|
|
39
|
+
if isinstance(template_names, str):
|
40
|
+
raise ImproperlyConfigured(
|
41
|
+
f"{self.__class__.__name__}.get_template_names() must return a list of strings, "
|
42
|
+
f"not a string. Did you mean to return ['{template_names}']?"
|
43
|
+
)
|
44
|
+
|
57
45
|
if not template_names:
|
58
46
|
raise ImproperlyConfigured(
|
59
47
|
f"{self.__class__.__name__} requires a template_name or get_template_names()."
|
@@ -1,8 +1,8 @@
|
|
1
|
-
plain/CHANGELOG.md,sha256=
|
1
|
+
plain/CHANGELOG.md,sha256=AW5V61mourm_d7YB4u2bJ4LABXh1hJ_pZv0Bn46VP8I,10477
|
2
2
|
plain/README.md,sha256=5BJyKhf0TDanWVbOQyZ3zsi5Lov9xk-LlJYCDWofM6Y,4078
|
3
3
|
plain/__main__.py,sha256=GK39854Lc_LO_JP8DzY9Y2MIQ4cQEl7SXFJy244-lC8,110
|
4
4
|
plain/debug.py,sha256=XdjnXcbPGsi0J2SpHGaLthhYU5AjhBlkHdemaP4sbYY,758
|
5
|
-
plain/exceptions.py,sha256=
|
5
|
+
plain/exceptions.py,sha256=ljOLqgVwPKlGWqaNmjHcHQf6y053bZ9ogkLFEGcs-Gg,5973
|
6
6
|
plain/json.py,sha256=McJdsbMT1sYwkGRG--f2NSZz0hVXPMix9x3nKaaak2o,1262
|
7
7
|
plain/paginator.py,sha256=iXiOyt2r_YwNrkqCRlaU7V-M_BKaaQ8XZElUBVa6yeU,5844
|
8
8
|
plain/signing.py,sha256=r2KvCOxkrSWCULFxYa9BHYx3L3a2oLq8RDnq_92inTw,8207
|
@@ -20,7 +20,7 @@ plain/chores/__init__.py,sha256=r9TXtQCH-VbvfnIJ5F8FxgQC35GRWFOfmMZN3q9niLg,67
|
|
20
20
|
plain/chores/registry.py,sha256=V3WjuekRI22LFvJbqSkUXQtiOtuE2ZK8gKV1TRvxRUI,1866
|
21
21
|
plain/cli/README.md,sha256=5C7vsH0ISxu7q5H6buC25MBOILkI_rzdySitswpQgJw,1032
|
22
22
|
plain/cli/__init__.py,sha256=6w9T7K2WrPwh6DcaMb2oNt_CWU6Bc57nUTO2Bt1p38Y,63
|
23
|
-
plain/cli/agent.py,sha256=
|
23
|
+
plain/cli/agent.py,sha256=nf-Tuc3abxpyV-nShBn1wq0JWjfgd3zY9lLH6rAZSRs,1678
|
24
24
|
plain/cli/build.py,sha256=Lo6AYghJz0DM9fIVUSiBSOKa5vR0XCOxZWEjza6sc8Q,3172
|
25
25
|
plain/cli/changelog.py,sha256=j-k1yZk9mpm-fLZgeWastiyIisxNSuAJfXTQ2B6WQmk,3457
|
26
26
|
plain/cli/chores.py,sha256=xXSSFvr8T5jWfLWqe6E8YVMw1BkQxyOHHVuY0x9RH0A,2412
|
@@ -37,13 +37,13 @@ plain/cli/scaffold.py,sha256=mcywA9DzfwoBSqWl5-Zpgcy1mTNUGEgdvoxXUrGcEVk,1351
|
|
37
37
|
plain/cli/settings.py,sha256=9cx4bue664I2P7kUedlf4YhCPB0tSKSE4Q8mGyzEv2o,1995
|
38
38
|
plain/cli/shell.py,sha256=iIwvlTdTBjLBBUdXMAmIRWSoynszOZI79-mrBg4RegU,1373
|
39
39
|
plain/cli/startup.py,sha256=wLaFuyUb4ewWhtehBCGicrRCXIIGCRbeCT3ce9hUv-A,1022
|
40
|
-
plain/cli/upgrade.py,sha256=
|
40
|
+
plain/cli/upgrade.py,sha256=eGVWm0gpn-Pr6uPsfzojRmh_VU5--B0h9dYfQuXSzi8,5625
|
41
41
|
plain/cli/urls.py,sha256=ghCW36aRszxmTo06A50FIvYopb6kQ07QekkDzM6_A1o,3824
|
42
42
|
plain/cli/utils.py,sha256=VwlIh0z7XxzVV8I3qM2kZo07fkJFPoeeVZa1ODG616k,258
|
43
|
-
plain/csrf/README.md,sha256=
|
44
|
-
plain/csrf/middleware.py,sha256=
|
43
|
+
plain/csrf/README.md,sha256=ApWpB-qlEf0LkOKm9Yr-6f_lB9XJEvGFDo_fraw8ghI,2391
|
44
|
+
plain/csrf/middleware.py,sha256=d_vb8l0-KxzyqCivVq0jTCsFOm-ljwjmjVuZXKVYR5U,5113
|
45
45
|
plain/csrf/views.py,sha256=HwQqfI6KPelHP9gSXhjfZaTLQic71PKsoZ6DPhr1rKI,572
|
46
|
-
plain/forms/README.md,sha256=
|
46
|
+
plain/forms/README.md,sha256=7MJQxNBoKkg0rW16qF6bGpUBxZrMrWjl2DZZk6gjzAU,2258
|
47
47
|
plain/forms/__init__.py,sha256=UxqPwB8CiYPCQdHmUc59jadqaXqDmXBH8y4bt9vTPms,226
|
48
48
|
plain/forms/boundfield.py,sha256=LhydhCVR0okrli0-QBMjGjAJ8-06gTCXVEaBZhBouQk,1741
|
49
49
|
plain/forms/exceptions.py,sha256=NYk1wjYDkk-lA_XMJQDItOebQcL_m_r2eNRc2mkLQkg,315
|
@@ -70,7 +70,7 @@ plain/internal/handlers/exception.py,sha256=vfha_6-fz6S6VYCP1PMBfue2Gw-_th6jqaTE
|
|
70
70
|
plain/internal/handlers/wsgi.py,sha256=dgPT29t_F9llB-c5RYU3SHxGuZNaZ83xRjOfuOmtOl8,8209
|
71
71
|
plain/internal/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
72
72
|
plain/internal/middleware/headers.py,sha256=ENIW1Gwat54hv-ejgp2R8QTZm-PlaI7k44WU01YQrNk,964
|
73
|
-
plain/internal/middleware/https.py,sha256=
|
73
|
+
plain/internal/middleware/https.py,sha256=mS1YejfLB_5qlAoMfanh8Wn2O-RdSpOBdhvw2DRcTHs,1257
|
74
74
|
plain/internal/middleware/slash.py,sha256=JWcIfGbXEKH00I7STq1AMdHhFGmQHC8lkKENa6280ko,2846
|
75
75
|
plain/logs/README.md,sha256=ON6Zylg_WA_V7QiIbRY7sgsl2nfG7KFzIbxK2x3rPuc,1419
|
76
76
|
plain/logs/__init__.py,sha256=rASvo4qFBDIHfkACmGLNGa6lRGbG9PbNjW6FmBt95ys,168
|
@@ -90,14 +90,15 @@ plain/preflight/security.py,sha256=oxUZBp2M0bpBfUoLYepIxoex2Y90nyjlrL8XU8UTHYY,2
|
|
90
90
|
plain/preflight/urls.py,sha256=cQ-WnFa_5oztpKdtwhuIGb7pXEml__bHsjs1SWO2YNI,1468
|
91
91
|
plain/runtime/README.md,sha256=sTqXXJkckwqkk9O06XMMSNRokAYjrZBnB50JD36BsYI,4873
|
92
92
|
plain/runtime/__init__.py,sha256=8GtvKROf3HUCtneDYXTbEioPcCtwnV76dP57n2PnjuE,2343
|
93
|
-
plain/runtime/global_settings.py,sha256=
|
93
|
+
plain/runtime/global_settings.py,sha256=EddJhfS3LZE6XIKSgib9dzmrw72Z1BbCluO8OgUQU48,5740
|
94
94
|
plain/runtime/user_settings.py,sha256=OzMiEkE6ZQ50nxd1WIqirXPiNuMAQULklYHEzgzLWgA,11027
|
95
|
+
plain/runtime/utils.py,sha256=oiWGyC9J1xrypCUkawdB6mGUuKpiK4OL0ztHoHOB08g,674
|
95
96
|
plain/signals/README.md,sha256=XefXqROlDhzw7Z5l_nx6Mhq6n9jjQ-ECGbH0vvhKWYg,272
|
96
97
|
plain/signals/__init__.py,sha256=eAs0kLqptuP6I31dWXeAqRNji3svplpAV4Ez6ktjwXM,131
|
97
98
|
plain/signals/dispatch/__init__.py,sha256=FzEygqV9HsM6gopio7O2Oh_X230nA4d5Q9s0sUjMq0E,292
|
98
99
|
plain/signals/dispatch/dispatcher.py,sha256=VxSlqn9PCOTghPPJLOqZPs6FNQZfV2BJpMfFMSg6Dtc,11531
|
99
100
|
plain/signals/dispatch/license.txt,sha256=o9EhDhsC4Q5HbmD-IfNGVTEkXtNE33r5rIt3lleJ8gc,1727
|
100
|
-
plain/templates/README.md,sha256=
|
101
|
+
plain/templates/README.md,sha256=QAQxoygpc0CE13fh4eH4ZILwl2xc-oMdGKtiZLLrNCk,2565
|
101
102
|
plain/templates/__init__.py,sha256=bX76FakE9T7mfK3N0deN85HlwHNQpeigytSC9Z8LcOs,451
|
102
103
|
plain/templates/core.py,sha256=mbcH0yTeFOI3XOg9dYSroXRIcdv9sETEy4HzY-ugwco,1258
|
103
104
|
plain/templates/jinja/__init__.py,sha256=xvYif0feMYR9pWjN0czthq2dk3qI4D5UQjgj9yp4dNA,2776
|
@@ -107,7 +108,7 @@ plain/templates/jinja/filters.py,sha256=ft5XUr4OLeQayn-MSxrycpFLyyN_yEo7j5WhWMwp
|
|
107
108
|
plain/templates/jinja/globals.py,sha256=VMpuMZvwWOmb5MbzKK-ox-QEX_WSsXFxq0mm8biJgaU,558
|
108
109
|
plain/test/README.md,sha256=wem0myAd-ij74nNV1MB9g4iH6FZUgii7-_euRq45oHs,1142
|
109
110
|
plain/test/__init__.py,sha256=MhNHtp7MYBl9kq-pMRGY11kJ6kU1I6vOkjNkit1TYRg,94
|
110
|
-
plain/test/client.py,sha256=
|
111
|
+
plain/test/client.py,sha256=OcL8wQDOu6PUNYvwcmslT5IGt81ffcPsXn05n-2n9oA,25128
|
111
112
|
plain/test/encoding.py,sha256=YJBOIE-BQRA5yl4zHnQy-9l67mJDTFmfy1DQXK0Wk-Q,3199
|
112
113
|
plain/test/exceptions.py,sha256=b-GHicg87Gh73W3g8QGWuSHi9PrQFVsxgWvEXDLt8gQ,290
|
113
114
|
plain/urls/README.md,sha256=026RkCK6I0GdqK3RE2QBLcCLIsiwtyKxgI2F0KBX95E,3882
|
@@ -142,18 +143,17 @@ plain/utils/text.py,sha256=42hJv06sadbWfsaAHNhqCQaP1W9qZ69trWDTS-Xva7k,9496
|
|
142
143
|
plain/utils/timesince.py,sha256=a_-ZoPK_s3Pt998CW4rWp0clZ1XyK2x04hCqak2giII,5928
|
143
144
|
plain/utils/timezone.py,sha256=6u0sE-9RVp0_OCe0Y1KiYYQoq5THWLokZFQYY8jf78g,6221
|
144
145
|
plain/utils/tree.py,sha256=wdWzmfsgc26YDF2wxhAY3yVxXTixQYqYDKE9mL3L3ZY,4383
|
145
|
-
plain/views/README.md,sha256=
|
146
|
+
plain/views/README.md,sha256=JmRGCQJgSr17uzUfsJfZbix1md3Qj6mmCkzWuoTFurQ,7223
|
146
147
|
plain/views/__init__.py,sha256=a-N1nkklVohJTtz0yD1MMaS0g66HviEjsKydNVVjvVc,392
|
147
148
|
plain/views/base.py,sha256=CC9UvMZeAjVvi90vGjoZzsQ0jnhbg3-7qCKQ8-Pb6cg,4184
|
148
|
-
plain/views/csrf.py,sha256=7q6l5xzLWhRnMY64aNj0hR6G-3pxI2yhRwG6k_5j00E,144
|
149
149
|
plain/views/errors.py,sha256=jbNCJIzowwCsEvqyJ3opMeZpPDqTyhtrbqb0VnAm2HE,1263
|
150
150
|
plain/views/exceptions.py,sha256=b4euI49ZUKS9O8AGAcFfiDpstzkRAuuj_uYQXzWNHME,138
|
151
151
|
plain/views/forms.py,sha256=ESZOXuo6IeYixp1RZvPb94KplkowRiwO2eGJCM6zJI0,2400
|
152
|
-
plain/views/objects.py,sha256=
|
152
|
+
plain/views/objects.py,sha256=YNb8MO1I99HTmQghC5nFk25TQmaB_s45K5yg5BGt4qY,5018
|
153
153
|
plain/views/redirect.py,sha256=Xpb3cB7nZYvKgkNqcAxf9Jwm2SWcQ0u2xz4oO5M3vP8,1909
|
154
|
-
plain/views/templates.py,sha256=
|
155
|
-
plain-0.
|
156
|
-
plain-0.
|
157
|
-
plain-0.
|
158
|
-
plain-0.
|
159
|
-
plain-0.
|
154
|
+
plain/views/templates.py,sha256=oAlebEyfES0rzBhfyEJzFmgLkpkbleA6Eip-8zDp-yk,1863
|
155
|
+
plain-0.59.0.dist-info/METADATA,sha256=7JQkFKbt0rGqL6RYe5uQBtXLpk3aPUCDtj0kTtIGcVY,4488
|
156
|
+
plain-0.59.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
157
|
+
plain-0.59.0.dist-info/entry_points.txt,sha256=nn4uKTRRZuEKOJv3810s3jtSMW0Gew7XDYiKIvBRR6M,93
|
158
|
+
plain-0.59.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
159
|
+
plain-0.59.0.dist-info/RECORD,,
|
plain/views/csrf.py
DELETED
File without changes
|
File without changes
|
File without changes
|