plain 0.35.0__py3-none-any.whl → 0.37.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/README.md +37 -37
- plain/assets/views.py +3 -3
- plain/cli/build.py +107 -0
- plain/cli/core.py +17 -685
- plain/cli/docs.py +211 -0
- plain/cli/preflight.py +127 -0
- plain/cli/scaffold.py +53 -0
- plain/cli/settings.py +60 -0
- plain/cli/shell.py +56 -0
- plain/cli/startup.py +24 -12
- plain/cli/urls.py +87 -0
- plain/cli/utils.py +15 -0
- plain/csrf/README.md +1 -1
- plain/csrf/middleware.py +3 -3
- plain/http/README.md +1 -1
- plain/http/response.py +15 -9
- plain/runtime/global_settings.py +8 -1
- plain/views/README.md +1 -1
- plain/views/base.py +1 -1
- plain/views/objects.py +8 -64
- plain/views/templates.py +11 -9
- plain-0.37.0.dist-info/METADATA +77 -0
- {plain-0.35.0.dist-info → plain-0.37.0.dist-info}/RECORD +26 -18
- plain-0.35.0.dist-info/METADATA +0 -77
- {plain-0.35.0.dist-info → plain-0.37.0.dist-info}/WHEEL +0 -0
- {plain-0.35.0.dist-info → plain-0.37.0.dist-info}/entry_points.txt +0 -0
- {plain-0.35.0.dist-info → plain-0.37.0.dist-info}/licenses/LICENSE +0 -0
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
|
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(
|
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.
|
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!",
|
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,
|
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
|
136
|
+
if status_code is not None:
|
131
137
|
try:
|
132
|
-
self.status_code = int(
|
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"",
|
357
|
-
super().__init__(
|
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=(),
|
438
|
-
super().__init__(
|
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,
|
564
|
-
super().__init__(
|
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:
|
plain/runtime/global_settings.py
CHANGED
@@ -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 = "
|
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",
|
217
|
+
Response("Rate limit exceeded", status_code=429)
|
218
218
|
)
|
219
219
|
|
220
220
|
return AnExpensiveObject()
|
plain/views/base.py
CHANGED
plain/views/objects.py
CHANGED
@@ -31,39 +31,13 @@ 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"] =
|
34
|
+
context["object"] = (
|
35
|
+
self.object
|
36
|
+
) # Some templates can benefit by always knowing a primary "object" can be present
|
35
37
|
if self.context_object_name:
|
36
38
|
context[self.context_object_name] = self.object
|
37
|
-
elif hasattr(self.object, "_meta"):
|
38
|
-
context[self.object._meta.model_name] = self.object
|
39
39
|
return context
|
40
40
|
|
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
41
|
|
68
42
|
class DetailView(ObjectTemplateViewMixin, TemplateView):
|
69
43
|
"""
|
@@ -73,7 +47,7 @@ class DetailView(ObjectTemplateViewMixin, TemplateView):
|
|
73
47
|
view will support display of *any* object by overriding `self.get_object()`.
|
74
48
|
"""
|
75
49
|
|
76
|
-
|
50
|
+
pass
|
77
51
|
|
78
52
|
|
79
53
|
class CreateView(ObjectTemplateViewMixin, FormView):
|
@@ -81,8 +55,6 @@ class CreateView(ObjectTemplateViewMixin, FormView):
|
|
81
55
|
View for creating a new object, with a response rendered by a template.
|
82
56
|
"""
|
83
57
|
|
84
|
-
template_name_suffix = "_form"
|
85
|
-
|
86
58
|
def post(self) -> Response:
|
87
59
|
"""
|
88
60
|
Handle POST requests: instantiate a form instance with the passed
|
@@ -119,8 +91,6 @@ class CreateView(ObjectTemplateViewMixin, FormView):
|
|
119
91
|
class UpdateView(ObjectTemplateViewMixin, FormView):
|
120
92
|
"""View for updating an object, with a response rendered by a template."""
|
121
93
|
|
122
|
-
template_name_suffix = "_form"
|
123
|
-
|
124
94
|
def post(self) -> Response:
|
125
95
|
"""
|
126
96
|
Handle POST requests: instantiate a form instance with the passed
|
@@ -170,7 +140,6 @@ class DeleteView(ObjectTemplateViewMixin, FormView):
|
|
170
140
|
self.instance.delete()
|
171
141
|
|
172
142
|
form_class = EmptyDeleteForm
|
173
|
-
template_name_suffix = "_confirm_delete"
|
174
143
|
|
175
144
|
def post(self) -> Response:
|
176
145
|
"""
|
@@ -198,8 +167,7 @@ class ListView(TemplateView):
|
|
198
167
|
rendered by a template.
|
199
168
|
"""
|
200
169
|
|
201
|
-
|
202
|
-
context_object_name = "objects"
|
170
|
+
context_object_name = ""
|
203
171
|
|
204
172
|
def get(self) -> Response:
|
205
173
|
self.objects = self.get_objects()
|
@@ -213,31 +181,7 @@ class ListView(TemplateView):
|
|
213
181
|
def get_template_context(self) -> dict:
|
214
182
|
"""Insert the single object into the context dict."""
|
215
183
|
context = super().get_template_context() # type: ignore
|
216
|
-
context[
|
184
|
+
context["objects"] = self.objects
|
185
|
+
if self.context_object_name:
|
186
|
+
context[self.context_object_name] = self.objects
|
217
187
|
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="
|
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.
|
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
|
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)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: plain
|
3
|
+
Version: 0.37.0
|
4
|
+
Summary: A web framework for building products with Python.
|
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
|
10
|
+
Description-Content-Type: text/markdown
|
11
|
+
|
12
|
+
# Plain
|
13
|
+
|
14
|
+
**Plain is a web framework for building products with Python.**
|
15
|
+
|
16
|
+
The core `plain` package provides the backbone of a Python web application (similar to [Flask](https://flask.palletsprojects.com/en/stable/)), while the additional first-party packages can power a more fully-featured database-backed app (similar to [Django](https://www.djangoproject.com/)).
|
17
|
+
|
18
|
+
All Plain packages are designed to work together and use [PEP 420](https://peps.python.org/pep-0420/) to share the `plain` namespace.
|
19
|
+
|
20
|
+
To quickly get started with Plain, visit [plainframework.com/start/](https://plainframework.com/start/).
|
21
|
+
|
22
|
+
## Core Modules
|
23
|
+
|
24
|
+
The `plain` package includes everything you need to start handling web requests with Python:
|
25
|
+
|
26
|
+
- [assets](./assets/README.md) - Serve static files and assets.
|
27
|
+
- [cli](./cli/README.md) - The `plain` CLI, powered by Click.
|
28
|
+
- [csrf](./csrf/README.md) - Cross-Site Request Forgery protection.
|
29
|
+
- [forms](./forms/README.md) - HTML forms and form validation.
|
30
|
+
- [http](./http/README.md) - HTTP request and response handling.
|
31
|
+
- [logs](./logs/README.md) - Logging configuration and utilities.
|
32
|
+
- [preflight](./preflight/README.md) - Preflight checks for your app.
|
33
|
+
- [runtime](./runtime/README.md) - Runtime settings and configuration.
|
34
|
+
- [templates](./templates/README.md) - Jinja2 templates and rendering.
|
35
|
+
- [test](./test/README.md) - Test utilities and fixtures.
|
36
|
+
- [urls](./urls/README.md) - URL routing and request dispatching.
|
37
|
+
- [views](./views/README.md) - Class-based views and request handlers.
|
38
|
+
|
39
|
+
## Foundational Packages
|
40
|
+
|
41
|
+
- [plain.models](/plain-models/plain/models/README.md) - Define and interact with your database models.
|
42
|
+
- [plain.cache](/plain-cache/plain/cache/README.md) - A database-driven general purpose cache.
|
43
|
+
- [plain.email](/plain-email/plain/email/README.md) - Send emails with SMTP or custom backends.
|
44
|
+
- [plain.sessions](/plain-sessions/plain/sessions/README.md) - User sessions and cookies.
|
45
|
+
- [plain.worker](/plain-worker/plain/worker/README.md) - Backgrounb jobs stored in the database.
|
46
|
+
- [plain.api](/plain-api/plain/api/README.md) - Build APIs with Plain views.
|
47
|
+
|
48
|
+
## Auth Packages
|
49
|
+
|
50
|
+
- [plain.auth](/plain-auth/plain/auth/README.md) - User authentication and authorization.
|
51
|
+
- [plain.oauth](/plain-oauth/plain/oauth/README.md) - OAuth authentication and API access.
|
52
|
+
- [plain.passwords](/plain-passwords/plain/passwords/README.md) - Password-based login and registration.
|
53
|
+
- [plain.loginlink](/plain-loginlink/plain/loginlink/README.md) - Login links for passwordless authentication.
|
54
|
+
|
55
|
+
## Admin Packages
|
56
|
+
|
57
|
+
- [plain.admin](/plain-admin/plain/admin/README.md) - An admin interface for back-office tasks.
|
58
|
+
- [plain.flags](/plain-flags/plain/flags/README.md) - Feature flags.
|
59
|
+
- [plain.support](/plain-support/plain/support/README.md) - Customer support forms.
|
60
|
+
- [plain.redirection](/plain-redirection/plain/redirection/README.md) - Redirects managed in the database.
|
61
|
+
- [plain.pageviews](/plain-pageviews/plain/pageviews/README.md) - Basic self-hosted page view tracking and reporting.
|
62
|
+
|
63
|
+
## Dev Packages
|
64
|
+
|
65
|
+
- [plain.dev](/plain-dev/plain/dev/README.md) - A single command for local development.
|
66
|
+
- [plain.pytest](/plain-pytest/plain/pytest/README.md) - Pytest fixtures and helpers.
|
67
|
+
- [plain.code](/plain-code/plain/code/README.md) - Code formatting and linting.
|
68
|
+
- [plain.tunnel](/plain-tunnel/plain/tunnel/README.md) - Expose your local server to the internet.
|
69
|
+
|
70
|
+
## Frontend Packages
|
71
|
+
|
72
|
+
- [plain.tailwind](/plain-tailwind/plain/tailwind/README.md) - Tailwind CSS integration without Node.js.
|
73
|
+
- [plain.htmx](/plain-htmx/plain/htmx/README.md) - HTMX integrated into views and templates.
|
74
|
+
- [plain.elements](/plain-elements/plain/elements/README.md) - Server-side HTML components.
|
75
|
+
- [plain.pages](/plain-pages/plain/pages/README.md) - Static pages with Markdown and Jinja2.
|
76
|
+
- [plain.esbuild](/plain-esbuild/plain/esbuild/README.md) - Simple JavaScript bundling and minification.
|
77
|
+
- [plain.vendor](/plain-vendor/plain/vendor/README.md) - Vendor JavaScript and CSS libraries.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
plain/README.md,sha256=
|
1
|
+
plain/README.md,sha256=TV_cAASLTSFjlmW7LD8J8Md1iKSjZpuZdEQGBCEXGUE,3986
|
2
2
|
plain/__main__.py,sha256=GK39854Lc_LO_JP8DzY9Y2MIQ4cQEl7SXFJy244-lC8,110
|
3
3
|
plain/debug.py,sha256=abPkJY4aSbBYGEYSZST_ZY3ohXPGDdz9uWQBYRqfd3M,730
|
4
4
|
plain/exceptions.py,sha256=Z9cbPE5im_Y-bjVq8cqC85gBoqOr80rLFG5wTKixrwE,5894
|
@@ -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=
|
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/
|
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/
|
24
|
-
plain/
|
25
|
-
plain/
|
26
|
+
plain/cli/scaffold.py,sha256=OY794msQkZAQCbDTpPXSKHdM1NX42zLBPGd1nx7q3vM,1330
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
142
|
+
plain/views/objects.py,sha256=GGbcfg_9fPZ-PiaBwIHG2e__8GfWDR7JQtQ15wTyiHg,5970
|
135
143
|
plain/views/redirect.py,sha256=vMXx8430FtyKcT0V0gyY92SkLtyULBX52KhX4eu4gEA,1985
|
136
|
-
plain/views/templates.py,sha256=
|
137
|
-
plain-0.
|
138
|
-
plain-0.
|
139
|
-
plain-0.
|
140
|
-
plain-0.
|
141
|
-
plain-0.
|
144
|
+
plain/views/templates.py,sha256=kMcHKkKNvucF91SFGkaq-ugjrCwn4zJBpFV1JkwA544,2027
|
145
|
+
plain-0.37.0.dist-info/METADATA,sha256=Oj1bF-2a2hpbWTokTcq-TFKjKyfaZhvpBFSqTxapSNU,4297
|
146
|
+
plain-0.37.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
147
|
+
plain-0.37.0.dist-info/entry_points.txt,sha256=1Ys2lsSeMepD1vz8RSrJopna0RQfUd951vYvCRsvl6A,45
|
148
|
+
plain-0.37.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
149
|
+
plain-0.37.0.dist-info/RECORD,,
|
plain-0.35.0.dist-info/METADATA
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: plain
|
3
|
-
Version: 0.35.0
|
4
|
-
Summary: A web framework for building products with Python.
|
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
|
10
|
-
Description-Content-Type: text/markdown
|
11
|
-
|
12
|
-
# Plain
|
13
|
-
|
14
|
-
**Plain is a web framework for building products with Python.**
|
15
|
-
|
16
|
-
The core `plain` package provides the backbone of a Python web application (similar to [Flask](https://flask.palletsprojects.com/en/stable/)), while the additional first-party packages can power a more fully-featured database-backed app (similar to [Django](https://www.djangoproject.com/)).
|
17
|
-
|
18
|
-
All Plain packages are designed to work together and use [PEP 420](https://peps.python.org/pep-0420/) to share the `plain` namespace.
|
19
|
-
|
20
|
-
To quickly get started with Plain, visit [plainframework.com/start/](https://plainframework.com/start/).
|
21
|
-
|
22
|
-
## Core Modules
|
23
|
-
|
24
|
-
The `plain` package includes everything you need to start handling web requests with Python:
|
25
|
-
|
26
|
-
- [assets](assets/README.md) - Serve static files and assets.
|
27
|
-
- [cli](cli/README.md) - The `plain` CLI, powered by Click.
|
28
|
-
- [csrf](csrf/README.md) - Cross-Site Request Forgery protection.
|
29
|
-
- [forms](forms/README.md) - HTML forms and form validation.
|
30
|
-
- [http](http/README.md) - HTTP request and response handling.
|
31
|
-
- [logs](logs/README.md) - Logging configuration and utilities.
|
32
|
-
- [preflight](preflight/README.md) - Preflight checks for your app.
|
33
|
-
- [runtime](runtime/README.md) - Runtime settings and configuration.
|
34
|
-
- [templates](templates/README.md) - Jinja2 templates and rendering.
|
35
|
-
- [test](test/README.md) - Test utilities and fixtures.
|
36
|
-
- [urls](urls/README.md) - URL routing and request dispatching.
|
37
|
-
- [views](views/README.md) - Class-based views and request handlers.
|
38
|
-
|
39
|
-
## Foundational Packages
|
40
|
-
|
41
|
-
- [plain.models](/plain-models/README.md) - Define and interact with your database models.
|
42
|
-
- [plain.cache](/plain-cache/README.md) - A database-driven general purpose cache.
|
43
|
-
- [plain.email](/plain-email/README.md) - Send emails with SMTP or custom backends.
|
44
|
-
- [plain.sessions](/plain-sessions/README.md) - User sessions and cookies.
|
45
|
-
- [plain.worker](/plain-worker/README.md) - Backgrounb jobs stored in the database.
|
46
|
-
- [plain.api](/plain-api/README.md) - Build APIs with Plain views.
|
47
|
-
|
48
|
-
## Auth Packages
|
49
|
-
|
50
|
-
- [plain.auth](/plain-auth/README.md) - User authentication and authorization.
|
51
|
-
- [plain.oauth](/plain-oauth/README.md) - OAuth authentication and API access.
|
52
|
-
- [plain.passwords](/plain-passwords/README.md) - Password-based login and registration.
|
53
|
-
- [plain.loginlink](/plain-loginlink/README.md) - Login links for passwordless authentication.
|
54
|
-
|
55
|
-
## Admin Packages
|
56
|
-
|
57
|
-
- [plain.admin](/plain-admin/README.md) - An admin interface for back-office tasks.
|
58
|
-
- [plain.flags](/plain-flags/README.md) - Feature flags.
|
59
|
-
- [plain.support](/plain-support/README.md) - Customer support forms.
|
60
|
-
- [plain.redirection](/plain-redirection/README.md) - Redirects managed in the database.
|
61
|
-
- [plain.pageviews](/plain-pageviews/README.md) - Basic self-hosted page view tracking and reporting.
|
62
|
-
|
63
|
-
## Dev Packages
|
64
|
-
|
65
|
-
- [plain.dev](/plain-dev/README.md) - A single command for local development.
|
66
|
-
- [plain.pytest](/plain-pytest/README.md) - Pytest fixtures and helpers.
|
67
|
-
- [plain.code](/plain-code/README.md) - Code formatting and linting.
|
68
|
-
- [plain.tunnel](/plain-tunnel/README.md) - Expose your local server to the internet.
|
69
|
-
|
70
|
-
## Frontend Packages
|
71
|
-
|
72
|
-
- [plain.tailwind](/plain-tailwind/README.md) - Tailwind CSS integration without Node.js.
|
73
|
-
- [plain.htmx](/plain-htmx/README.md) - HTMX integrated into views and templates.
|
74
|
-
- [plain.elements](/plain-elements/README.md) - Server-side HTML components.
|
75
|
-
- [plain.pages](/plain-pages/README.md) - Static pages with Markdown and Jinja2.
|
76
|
-
- [plain.esbuild](/plain-esbuild/README.md) - Simple JavaScript bundling and minification.
|
77
|
-
- [plain.vendor](/plain-vendor/README.md) - Vendor JavaScript and CSS libraries.
|
File without changes
|
File without changes
|
File without changes
|