plain 0.66.0__py3-none-any.whl → 0.101.2__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.
Files changed (197) hide show
  1. plain/CHANGELOG.md +684 -0
  2. plain/README.md +1 -1
  3. plain/assets/compile.py +25 -12
  4. plain/assets/finders.py +24 -17
  5. plain/assets/fingerprints.py +10 -7
  6. plain/assets/urls.py +1 -1
  7. plain/assets/views.py +47 -33
  8. plain/chores/README.md +25 -23
  9. plain/chores/__init__.py +2 -1
  10. plain/chores/core.py +27 -0
  11. plain/chores/registry.py +23 -53
  12. plain/cli/README.md +185 -16
  13. plain/cli/__init__.py +2 -1
  14. plain/cli/agent.py +236 -0
  15. plain/cli/build.py +7 -8
  16. plain/cli/changelog.py +11 -5
  17. plain/cli/chores.py +32 -34
  18. plain/cli/core.py +112 -28
  19. plain/cli/docs.py +52 -11
  20. plain/cli/formatting.py +40 -17
  21. plain/cli/install.py +10 -54
  22. plain/cli/{agent/llmdocs.py → llmdocs.py} +21 -9
  23. plain/cli/output.py +6 -2
  24. plain/cli/preflight.py +175 -102
  25. plain/cli/print.py +4 -4
  26. plain/cli/registry.py +95 -26
  27. plain/cli/request.py +206 -0
  28. plain/cli/runtime.py +45 -0
  29. plain/cli/scaffold.py +2 -7
  30. plain/cli/server.py +153 -0
  31. plain/cli/settings.py +53 -49
  32. plain/cli/shell.py +15 -12
  33. plain/cli/startup.py +9 -8
  34. plain/cli/upgrade.py +17 -104
  35. plain/cli/urls.py +12 -7
  36. plain/cli/utils.py +3 -3
  37. plain/csrf/README.md +65 -40
  38. plain/csrf/middleware.py +53 -43
  39. plain/debug.py +5 -2
  40. plain/exceptions.py +22 -114
  41. plain/forms/README.md +453 -24
  42. plain/forms/__init__.py +55 -4
  43. plain/forms/boundfield.py +15 -8
  44. plain/forms/exceptions.py +1 -1
  45. plain/forms/fields.py +346 -143
  46. plain/forms/forms.py +75 -45
  47. plain/http/README.md +356 -9
  48. plain/http/__init__.py +41 -26
  49. plain/http/cookie.py +15 -7
  50. plain/http/exceptions.py +65 -0
  51. plain/http/middleware.py +32 -0
  52. plain/http/multipartparser.py +99 -88
  53. plain/http/request.py +362 -250
  54. plain/http/response.py +99 -197
  55. plain/internal/__init__.py +8 -1
  56. plain/internal/files/base.py +35 -19
  57. plain/internal/files/locks.py +19 -11
  58. plain/internal/files/move.py +8 -3
  59. plain/internal/files/temp.py +25 -6
  60. plain/internal/files/uploadedfile.py +47 -28
  61. plain/internal/files/uploadhandler.py +64 -58
  62. plain/internal/files/utils.py +24 -10
  63. plain/internal/handlers/base.py +34 -23
  64. plain/internal/handlers/exception.py +68 -65
  65. plain/internal/handlers/wsgi.py +65 -54
  66. plain/internal/middleware/headers.py +37 -11
  67. plain/internal/middleware/hosts.py +11 -13
  68. plain/internal/middleware/https.py +17 -7
  69. plain/internal/middleware/slash.py +14 -9
  70. plain/internal/reloader.py +77 -0
  71. plain/json.py +2 -1
  72. plain/logs/README.md +161 -62
  73. plain/logs/__init__.py +1 -1
  74. plain/logs/{loggers.py → app.py} +71 -67
  75. plain/logs/configure.py +63 -14
  76. plain/logs/debug.py +17 -6
  77. plain/logs/filters.py +15 -0
  78. plain/logs/formatters.py +7 -4
  79. plain/packages/README.md +105 -23
  80. plain/packages/config.py +15 -7
  81. plain/packages/registry.py +40 -15
  82. plain/paginator.py +31 -21
  83. plain/preflight/README.md +208 -23
  84. plain/preflight/__init__.py +5 -24
  85. plain/preflight/checks.py +12 -0
  86. plain/preflight/files.py +19 -13
  87. plain/preflight/registry.py +80 -58
  88. plain/preflight/results.py +37 -0
  89. plain/preflight/security.py +65 -71
  90. plain/preflight/settings.py +54 -0
  91. plain/preflight/urls.py +10 -48
  92. plain/runtime/README.md +115 -47
  93. plain/runtime/__init__.py +10 -6
  94. plain/runtime/global_settings.py +43 -33
  95. plain/runtime/secret.py +20 -0
  96. plain/runtime/user_settings.py +110 -38
  97. plain/runtime/utils.py +1 -1
  98. plain/server/LICENSE +35 -0
  99. plain/server/README.md +155 -0
  100. plain/server/__init__.py +9 -0
  101. plain/server/app.py +52 -0
  102. plain/server/arbiter.py +555 -0
  103. plain/server/config.py +118 -0
  104. plain/server/errors.py +31 -0
  105. plain/server/glogging.py +292 -0
  106. plain/server/http/__init__.py +12 -0
  107. plain/server/http/body.py +283 -0
  108. plain/server/http/errors.py +155 -0
  109. plain/server/http/message.py +400 -0
  110. plain/server/http/parser.py +70 -0
  111. plain/server/http/unreader.py +88 -0
  112. plain/server/http/wsgi.py +421 -0
  113. plain/server/pidfile.py +92 -0
  114. plain/server/sock.py +240 -0
  115. plain/server/util.py +317 -0
  116. plain/server/workers/__init__.py +6 -0
  117. plain/server/workers/base.py +304 -0
  118. plain/server/workers/sync.py +212 -0
  119. plain/server/workers/thread.py +399 -0
  120. plain/server/workers/workertmp.py +50 -0
  121. plain/signals/README.md +170 -1
  122. plain/signals/__init__.py +0 -1
  123. plain/signals/dispatch/dispatcher.py +49 -27
  124. plain/signing.py +131 -35
  125. plain/skills/README.md +36 -0
  126. plain/skills/plain-docs/SKILL.md +25 -0
  127. plain/skills/plain-install/SKILL.md +26 -0
  128. plain/skills/plain-request/SKILL.md +39 -0
  129. plain/skills/plain-shell/SKILL.md +24 -0
  130. plain/skills/plain-upgrade/SKILL.md +35 -0
  131. plain/templates/README.md +211 -20
  132. plain/templates/jinja/__init__.py +14 -27
  133. plain/templates/jinja/environments.py +5 -4
  134. plain/templates/jinja/extensions.py +12 -5
  135. plain/templates/jinja/filters.py +7 -2
  136. plain/templates/jinja/globals.py +2 -2
  137. plain/test/README.md +184 -22
  138. plain/test/client.py +340 -222
  139. plain/test/encoding.py +9 -6
  140. plain/test/exceptions.py +7 -2
  141. plain/urls/README.md +157 -73
  142. plain/urls/converters.py +18 -15
  143. plain/urls/exceptions.py +2 -2
  144. plain/urls/patterns.py +56 -40
  145. plain/urls/resolvers.py +38 -28
  146. plain/urls/utils.py +5 -1
  147. plain/utils/README.md +250 -3
  148. plain/utils/cache.py +17 -11
  149. plain/utils/crypto.py +21 -5
  150. plain/utils/datastructures.py +89 -56
  151. plain/utils/dateparse.py +9 -6
  152. plain/utils/deconstruct.py +15 -7
  153. plain/utils/decorators.py +5 -1
  154. plain/utils/dotenv.py +373 -0
  155. plain/utils/duration.py +8 -4
  156. plain/utils/encoding.py +14 -7
  157. plain/utils/functional.py +66 -49
  158. plain/utils/hashable.py +5 -1
  159. plain/utils/html.py +36 -22
  160. plain/utils/http.py +16 -9
  161. plain/utils/inspect.py +14 -6
  162. plain/utils/ipv6.py +7 -3
  163. plain/utils/itercompat.py +6 -1
  164. plain/utils/module_loading.py +7 -3
  165. plain/utils/regex_helper.py +37 -23
  166. plain/utils/safestring.py +14 -6
  167. plain/utils/text.py +41 -23
  168. plain/utils/timezone.py +33 -22
  169. plain/utils/tree.py +35 -19
  170. plain/validators.py +94 -52
  171. plain/views/README.md +156 -79
  172. plain/views/__init__.py +0 -1
  173. plain/views/base.py +25 -18
  174. plain/views/errors.py +13 -5
  175. plain/views/exceptions.py +4 -1
  176. plain/views/forms.py +6 -6
  177. plain/views/objects.py +52 -49
  178. plain/views/redirect.py +18 -15
  179. plain/views/templates.py +5 -3
  180. plain/wsgi.py +3 -1
  181. {plain-0.66.0.dist-info → plain-0.101.2.dist-info}/METADATA +4 -2
  182. plain-0.101.2.dist-info/RECORD +201 -0
  183. {plain-0.66.0.dist-info → plain-0.101.2.dist-info}/WHEEL +1 -1
  184. plain-0.101.2.dist-info/entry_points.txt +2 -0
  185. plain/AGENTS.md +0 -18
  186. plain/cli/agent/__init__.py +0 -20
  187. plain/cli/agent/docs.py +0 -80
  188. plain/cli/agent/md.py +0 -87
  189. plain/cli/agent/prompt.py +0 -45
  190. plain/cli/agent/request.py +0 -181
  191. plain/csrf/views.py +0 -31
  192. plain/logs/utils.py +0 -46
  193. plain/preflight/messages.py +0 -81
  194. plain/templates/AGENTS.md +0 -3
  195. plain-0.66.0.dist-info/RECORD +0 -168
  196. plain-0.66.0.dist-info/entry_points.txt +0 -4
  197. {plain-0.66.0.dist-info → plain-0.101.2.dist-info}/licenses/LICENSE +0 -0
plain/views/objects.py CHANGED
@@ -1,9 +1,16 @@
1
+ from abc import ABC, abstractmethod
1
2
  from functools import cached_property
2
3
  from typing import Any
3
4
 
4
- from plain.exceptions import ImproperlyConfigured, ObjectDoesNotExist
5
- from plain.forms import Form
6
- from plain.http import Http404
5
+ from plain.exceptions import ImproperlyConfigured
6
+
7
+ try:
8
+ from plain.models.exceptions import ObjectDoesNotExist
9
+ except ImportError:
10
+ ObjectDoesNotExist = None # type: ignore[misc, assignment]
11
+
12
+ from plain.forms import BaseForm, Form
13
+ from plain.http import NotFoundError404
7
14
 
8
15
  from .forms import FormView
9
16
  from .templates import TemplateView
@@ -15,10 +22,10 @@ class CreateView(FormView):
15
22
  """
16
23
 
17
24
  # TODO? would rather you have to specify this...
18
- def get_success_url(self, form):
25
+ def get_success_url(self, form: BaseForm) -> str:
19
26
  """Return the URL to redirect to after processing a valid form."""
20
27
  if self.success_url:
21
- url = self.success_url.format(**self.object.__dict__)
28
+ url = str(self.success_url).format(**self.object.__dict__)
22
29
  else:
23
30
  try:
24
31
  url = self.object.get_absolute_url()
@@ -29,36 +36,45 @@ class CreateView(FormView):
29
36
  )
30
37
  return url
31
38
 
32
- def form_valid(self, form):
39
+ def form_valid(self, form: BaseForm) -> Any:
33
40
  """If the form is valid, save the associated model."""
34
- self.object = form.save()
41
+ self.object = form.save() # type: ignore[attr-defined]
35
42
  return super().form_valid(form)
36
43
 
37
44
 
38
- class ObjectTemplateViewMixin:
45
+ class DetailView(TemplateView, ABC):
46
+ """
47
+ Render a "detail" view of an object.
48
+
49
+ By default this is a model instance looked up from `self.queryset`, but the
50
+ view will support display of *any* object by overriding `self.get_object()`.
51
+ """
52
+
39
53
  context_object_name = ""
40
54
 
41
55
  @cached_property
42
56
  def object(self) -> Any:
43
57
  try:
44
58
  obj = self.get_object()
45
- except ObjectDoesNotExist:
46
- raise Http404
59
+ except Exception as e:
60
+ # If ObjectDoesNotExist is available and this is that exception, raise 404
61
+ if ObjectDoesNotExist and isinstance(e, ObjectDoesNotExist):
62
+ raise NotFoundError404
63
+ # Otherwise, let other exceptions bubble up
64
+ raise
47
65
 
48
66
  # Also raise 404 if get_object() returns None
49
67
  if not obj:
50
- raise Http404
68
+ raise NotFoundError404
51
69
 
52
70
  return obj
53
71
 
54
- def get_object(self) -> Any:
55
- raise NotImplementedError(
56
- f"get_object() is not implemented on {self.__class__.__name__}"
57
- )
72
+ @abstractmethod
73
+ def get_object(self) -> Any: ...
58
74
 
59
- def get_template_context(self) -> dict:
75
+ def get_template_context(self) -> dict[str, Any]:
60
76
  """Insert the single object into the context dict."""
61
- context = super().get_template_context() # type: ignore
77
+ context = super().get_template_context()
62
78
  context["object"] = (
63
79
  self.object
64
80
  ) # Some templates can benefit by always knowing a primary "object" can be present
@@ -67,24 +83,13 @@ class ObjectTemplateViewMixin:
67
83
  return context
68
84
 
69
85
 
70
- class DetailView(ObjectTemplateViewMixin, TemplateView):
71
- """
72
- Render a "detail" view of an object.
73
-
74
- By default this is a model instance looked up from `self.queryset`, but the
75
- view will support display of *any* object by overriding `self.get_object()`.
76
- """
77
-
78
- pass
79
-
80
-
81
- class UpdateView(ObjectTemplateViewMixin, FormView):
86
+ class UpdateView(DetailView, FormView):
82
87
  """View for updating an object, with a response rendered by a template."""
83
88
 
84
- def get_success_url(self, form):
89
+ def get_success_url(self, form: BaseForm) -> str:
85
90
  """Return the URL to redirect to after processing a valid form."""
86
91
  if self.success_url:
87
- url = self.success_url.format(**self.object.__dict__)
92
+ url = str(self.success_url).format(**self.object.__dict__)
88
93
  else:
89
94
  try:
90
95
  url = self.object.get_absolute_url()
@@ -95,47 +100,47 @@ class UpdateView(ObjectTemplateViewMixin, FormView):
95
100
  )
96
101
  return url
97
102
 
98
- def form_valid(self, form):
103
+ def form_valid(self, form: BaseForm) -> Any:
99
104
  """If the form is valid, save the associated model."""
100
- form.save()
105
+ form.save() # type: ignore[attr-defined]
101
106
  return super().form_valid(form)
102
107
 
103
- def get_form_kwargs(self):
108
+ def get_form_kwargs(self) -> dict[str, Any]:
104
109
  """Return the keyword arguments for instantiating the form."""
105
110
  kwargs = super().get_form_kwargs()
106
111
  kwargs.update({"instance": self.object})
107
112
  return kwargs
108
113
 
109
114
 
110
- class DeleteView(ObjectTemplateViewMixin, FormView):
115
+ class DeleteView(DetailView, FormView):
111
116
  """
112
117
  View for deleting an object retrieved with self.get_object(), with a
113
118
  response rendered by a template.
114
119
  """
115
120
 
116
121
  class EmptyDeleteForm(Form):
117
- def __init__(self, instance, *args, **kwargs):
122
+ def __init__(self, instance: Any, **kwargs: Any) -> None:
118
123
  self.instance = instance
119
- super().__init__(*args, **kwargs)
124
+ super().__init__(**kwargs)
120
125
 
121
- def save(self):
126
+ def save(self) -> None:
122
127
  self.instance.delete()
123
128
 
124
129
  form_class = EmptyDeleteForm
125
130
 
126
- def get_form_kwargs(self):
131
+ def get_form_kwargs(self) -> dict[str, Any]:
127
132
  """Return the keyword arguments for instantiating the form."""
128
133
  kwargs = super().get_form_kwargs()
129
134
  kwargs.update({"instance": self.object})
130
135
  return kwargs
131
136
 
132
- def form_valid(self, form):
137
+ def form_valid(self, form: BaseForm) -> Any:
133
138
  """If the form is valid, save the associated model."""
134
- form.save()
139
+ form.save() # type: ignore[attr-defined]
135
140
  return super().form_valid(form)
136
141
 
137
142
 
138
- class ListView(TemplateView):
143
+ class ListView(TemplateView, ABC):
139
144
  """
140
145
  Render some list of objects, set by `self.get_queryset()`, with a response
141
146
  rendered by a template.
@@ -144,17 +149,15 @@ class ListView(TemplateView):
144
149
  context_object_name = ""
145
150
 
146
151
  @cached_property
147
- def objects(self):
152
+ def objects(self) -> Any:
148
153
  return self.get_objects()
149
154
 
150
- def get_objects(self):
151
- raise NotImplementedError(
152
- f"get_objects() is not implemented on {self.__class__.__name__}"
153
- )
155
+ @abstractmethod
156
+ def get_objects(self) -> Any: ...
154
157
 
155
- def get_template_context(self) -> dict:
158
+ def get_template_context(self) -> dict[str, Any]:
156
159
  """Insert the single object into the context dict."""
157
- context = super().get_template_context() # type: ignore
160
+ context = super().get_template_context()
158
161
  context["objects"] = self.objects
159
162
  if self.context_object_name:
160
163
  context[self.context_object_name] = self.objects
plain/views/redirect.py CHANGED
@@ -1,4 +1,4 @@
1
- from plain.http import ResponseRedirect
1
+ from plain.http import RedirectResponse
2
2
  from plain.urls import reverse
3
3
 
4
4
  from .base import View
@@ -13,8 +13,12 @@ class RedirectView(View):
13
13
  preserve_query_params = False
14
14
 
15
15
  def __init__(
16
- self, url=None, status_code=None, url_name=None, preserve_query_params=None
17
- ):
16
+ self,
17
+ url: str | None = None,
18
+ status_code: int | None = None,
19
+ url_name: str | None = None,
20
+ preserve_query_params: bool | None = None,
21
+ ) -> None:
18
22
  # Allow attributes to be set in RedirectView.as_view(url="...", status_code=301, etc.)
19
23
  self.url = url or self.url
20
24
  self.status_code = status_code if status_code is not None else self.status_code
@@ -25,7 +29,7 @@ class RedirectView(View):
25
29
  else self.preserve_query_params
26
30
  )
27
31
 
28
- def get_redirect_url(self):
32
+ def get_redirect_url(self) -> str:
29
33
  """
30
34
  Return the URL redirect to. Keyword arguments from the URL pattern
31
35
  match generating the redirect request are provided as kwargs to this
@@ -38,29 +42,28 @@ class RedirectView(View):
38
42
  else:
39
43
  raise ValueError("RedirectView requires either url or url_name to be set")
40
44
 
41
- args = self.request.meta.get("QUERY_STRING", "")
42
- if args and self.preserve_query_params:
43
- url = f"{url}?{args}"
45
+ if self.preserve_query_params and self.request.query_string:
46
+ url = f"{url}?{self.request.query_string}"
44
47
  return url
45
48
 
46
- def get(self):
49
+ def get(self) -> RedirectResponse:
47
50
  url = self.get_redirect_url()
48
- return ResponseRedirect(url, status_code=self.status_code)
51
+ return RedirectResponse(url, status_code=self.status_code)
49
52
 
50
- def head(self):
53
+ def head(self) -> RedirectResponse:
51
54
  return self.get()
52
55
 
53
- def post(self):
56
+ def post(self) -> RedirectResponse:
54
57
  return self.get()
55
58
 
56
- def options(self):
59
+ def options(self) -> RedirectResponse:
57
60
  return self.get()
58
61
 
59
- def delete(self):
62
+ def delete(self) -> RedirectResponse:
60
63
  return self.get()
61
64
 
62
- def put(self):
65
+ def put(self) -> RedirectResponse:
63
66
  return self.get()
64
67
 
65
- def patch(self):
68
+ def patch(self) -> RedirectResponse:
66
69
  return self.get()
plain/views/templates.py CHANGED
@@ -1,3 +1,5 @@
1
+ from typing import Any
2
+
1
3
  from plain.exceptions import ImproperlyConfigured
2
4
  from plain.http import Response
3
5
  from plain.runtime import settings
@@ -13,11 +15,11 @@ class TemplateView(View):
13
15
 
14
16
  template_name: str | None = None
15
17
 
16
- def __init__(self, template_name=None):
18
+ def __init__(self, template_name: str | None = None) -> None:
17
19
  # Allow template_name to be passed in as_view()
18
20
  self.template_name = template_name or self.template_name
19
21
 
20
- def get_template_context(self) -> dict:
22
+ def get_template_context(self) -> dict[str, Any]:
21
23
  return {
22
24
  "request": self.request,
23
25
  "template_names": self.get_template_names(),
@@ -58,5 +60,5 @@ class TemplateView(View):
58
60
  def render_template(self) -> str:
59
61
  return self.get_template().render(self.get_template_context())
60
62
 
61
- def get(self) -> Response:
63
+ def get(self) -> Response | Any:
62
64
  return Response(self.render_template())
plain/wsgi.py CHANGED
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import plain.runtime
2
4
  from plain.internal.handlers.wsgi import WSGIHandler
3
5
 
4
6
 
5
- def _get_wsgi_application():
7
+ def _get_wsgi_application() -> WSGIHandler:
6
8
  plain.runtime.setup()
7
9
  return WSGIHandler()
8
10
 
@@ -1,14 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain
3
- Version: 0.66.0
3
+ Version: 0.101.2
4
4
  Summary: A web framework for building products with Python.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
+ License-Expression: BSD-3-Clause
6
7
  License-File: LICENSE
7
8
  Requires-Python: >=3.13
8
9
  Requires-Dist: click>=8.0.0
9
10
  Requires-Dist: jinja2>=3.1.2
10
11
  Requires-Dist: opentelemetry-api>=1.34.1
11
12
  Requires-Dist: opentelemetry-semantic-conventions>=0.55b1
13
+ Requires-Dist: watchfiles>=0.18.0
12
14
  Description-Content-Type: text/markdown
13
15
 
14
16
  # Plain
@@ -44,7 +46,7 @@ The `plain` package includes everything you need to start handling web requests
44
46
  - [plain.cache](/plain-cache/plain/cache/README.md) - A database-driven general purpose cache.
45
47
  - [plain.email](/plain-email/plain/email/README.md) - Send emails with SMTP or custom backends.
46
48
  - [plain.sessions](/plain-sessions/plain/sessions/README.md) - User sessions and cookies.
47
- - [plain.worker](/plain-worker/plain/worker/README.md) - Background jobs stored in the database.
49
+ - [plain.jobs](/plain-jobs/plain/jobs/README.md) - Background jobs stored in the database.
48
50
  - [plain.api](/plain-api/plain/api/README.md) - Build APIs with Plain views.
49
51
 
50
52
  ## Auth Packages
@@ -0,0 +1,201 @@
1
+ plain/CHANGELOG.md,sha256=hvYn3NacP4SzrolTGeNiW9GT4__mq84UI2pxdZtpMfw,55077
2
+ plain/README.md,sha256=VvzhXNvf4I6ddmjBP9AExxxFXxs7RwyoxdgFm-W5dsg,4072
3
+ plain/__main__.py,sha256=GK39854Lc_LO_JP8DzY9Y2MIQ4cQEl7SXFJy244-lC8,110
4
+ plain/debug.py,sha256=C2OnFHtRGMrpCiHSt-P2r58JypgQZ62qzDBpV4mhtFM,855
5
+ plain/exceptions.py,sha256=zV_HXXrxA11YwP0CVHLTPd_5YA7N311V32lFrO3LtB4,4523
6
+ plain/json.py,sha256=SC0CWPBhlQs3F_xlMcvZGUGxVBEMqA378uqIC3Q00aI,1297
7
+ plain/paginator.py,sha256=uModWWSnXISNt1Ecb5C17yoaKiTcHltFHLrfqQ-lio0,6240
8
+ plain/signing.py,sha256=aFwfZew9Ot7_26F88wSOU82MdNUQ3i1A7kI1qKS1q-4,11634
9
+ plain/validators.py,sha256=JBeycZFilWs69ai-QQb_4snpDgs1fjMGT0ICwVB1mvE,21297
10
+ plain/wsgi.py,sha256=MWQ09DFNV2GxX752hYJMWka9LTEwzCp6XEC_4TdXF9g,287
11
+ plain/assets/README.md,sha256=iRZHHoXZCEFooXoDYSomF12XTcDFl7oNbRHpAFwxTM8,4349
12
+ plain/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ plain/assets/compile.py,sha256=a-e_nKHJLN3fOp9nw1MF7f7oP6q5qX8oYelowXKb_EE,3587
14
+ plain/assets/finders.py,sha256=Ldcn3bB3opab1AhRHef1arMEjOYDXH9TAt8sLYyieo0,1689
15
+ plain/assets/fingerprints.py,sha256=efOh65ueJxEP73teil_e_5wvn6K8kCzCi3RgPdT9pik,1515
16
+ plain/assets/urls.py,sha256=Qe0ctXYAQjAlLIKuX6JeLVBOFqzJxggR0RSGkRXmX78,1173
17
+ plain/assets/views.py,sha256=wVhFn4wIYzYwZN26HmgFgbNXHs4CmGZkc7tWB6mPQHk,10383
18
+ plain/chores/README.md,sha256=7Dv5MCyqPaohQxAk74HRzKiTvt_yR-IOhbu7R5bdz6M,2437
19
+ plain/chores/__init__.py,sha256=wEdt-oAKS1kz7Ln-puhcCt8XGNdfCP4S3wHESaPU8nI,100
20
+ plain/chores/core.py,sha256=BxsCSJDQvMjYsAH4QhEoW9ZUEAUIwJgTYyHovPGSLjk,578
21
+ plain/chores/registry.py,sha256=IRpx3f6Z1qlqcEpHTe6O6JocNmaplLu7BVOqGrafSXU,1221
22
+ plain/cli/README.md,sha256=8OmOhvKvgFkndwGn8lW6q62mvIB6Zq_NAhEhBf7PVV0,6305
23
+ plain/cli/__init__.py,sha256=o-dmnnNmXM3fZrKwW1qcoAPlcfG5pcFTFYWEBCocf8A,117
24
+ plain/cli/agent.py,sha256=ApEeJRWt_oTksuURvmSTHHe1_eIKWe_2AtqYV20U8hc,8212
25
+ plain/cli/build.py,sha256=UxgyAris4xtOZYS4FLMIOQLQ9DflHZkkenUJ650kZuA,3142
26
+ plain/cli/changelog.py,sha256=ckuP99HtnUSJXf_AELWgmD9S0XjldCzh5urxPiDpRcI,3647
27
+ plain/cli/chores.py,sha256=jhLKxFs_YpyQDvo5chNGiKnFc2_KK5atq53EpkGmMo0,2542
28
+ plain/cli/core.py,sha256=_Kg_YnYeDZB3EKlzdejl4eJepzRd2aF1LSs0Ei2uCI8,6923
29
+ plain/cli/docs.py,sha256=wC9ShvzCwJZoymV8G_Rs4AaPEU-_Eh-EkI0OFYJxKKA,2599
30
+ plain/cli/formatting.py,sha256=t0kxJ-r-his8kGcY38g38p699CfFGUgqEI1tJkEbjGs,3802
31
+ plain/cli/install.py,sha256=28lXobloqqN50duuKlUqalAmNHUJdz-JlYYmdpXpzCs,1260
32
+ plain/cli/llmdocs.py,sha256=xI2ta85zSEmVJePqrecxPd4EDhRSSY29pwSGor1DNJc,5599
33
+ plain/cli/output.py,sha256=uZTHZR-Axeoi2r6fgcDCpDA7iQSRrktBtTf1yBT5djI,1426
34
+ plain/cli/preflight.py,sha256=zWnw9RFmN0CQQByAB2vWLbwlDNrQzUQTtKKySVzRucE,7036
35
+ plain/cli/print.py,sha256=s_yNxtA4vg-AWn4C9TtFH-gOMnzMsXuEr7ak4-oOFPI,265
36
+ plain/cli/registry.py,sha256=Xk6uwVT0j0GeweFtYd4k4PnxXs6-jDLjEl-fFd0_e5Y,3849
37
+ plain/cli/request.py,sha256=vHtVN50in7Z3OQFxr-ezHRPYPn46koWQ3-2ITbDY22Y,6680
38
+ plain/cli/runtime.py,sha256=xpGBlLV70VC541DpyKL4cwqiL2xr8xz_cCuSZ3RC33s,1124
39
+ plain/cli/scaffold.py,sha256=v0bbtelz8-j2LP48W0uXvL1xgZ9ce6IfAYDxqKXqesk,1203
40
+ plain/cli/server.py,sha256=2MIy5TwzqhZMzy-wPUlNqAhOTlsnR1ZYh6Ja9eghXJM,3637
41
+ plain/cli/settings.py,sha256=aVGttxVIpVmxE-vfOW4r3JCLWlhZEA65SwHl6SfUSv8,1916
42
+ plain/cli/shell.py,sha256=QOh2g4bkkdWdR6VxUQogqgh1ehHFPqi5gAS9DsviRvY,1946
43
+ plain/cli/startup.py,sha256=sdnIpCP1ruyMtZFoVf1sff9sb5IE44pyoAuGc7nYTXI,1105
44
+ plain/cli/upgrade.py,sha256=rQMSIgkWBdO3F9Fgv2279y6cKeZE52kPoAbrUmVrybk,2375
45
+ plain/cli/urls.py,sha256=hKzRfXxZxUT4ePDMThEvy-X9XNqfIeVhrxK2vAEqjbI,3976
46
+ plain/cli/utils.py,sha256=CuEAN41fmxr0KS_Rek_9zWri6frh8MkJUrmN-JVAfEo,292
47
+ plain/csrf/README.md,sha256=plU22iqc2XOxrZ1ipRlrZf6evIxGZBluudRGJqUJzic,5130
48
+ plain/csrf/middleware.py,sha256=A4Ko6K0rBYo3IdizRuZZgoALqOIsbTbdd5QkFOi5KiA,6038
49
+ plain/forms/README.md,sha256=IDRkWVxjipTvhhOW6fnVeNj-MJ7CKGZ-P71QfE0mlqE,13445
50
+ plain/forms/__init__.py,sha256=gZrZY-_se_8PRgu3MwkSaYQ3S9YC-neZugMI0U1VvIg,1075
51
+ plain/forms/boundfield.py,sha256=Wkzo_4w_G4d372oxCGGDhwhggaA7boPmoHbRgDhOjNA,1991
52
+ plain/forms/exceptions.py,sha256=MuMUF-33Qsc_HWk5zI3rcWzdvXzQbEOKAZvJKkbrL58,320
53
+ plain/forms/fields.py,sha256=RE3erdMUyN5wPPT7HXuQ9UJNfi5uT-Od1r1GTzefBz0,42313
54
+ plain/forms/forms.py,sha256=ZMTCpZUzq3kwP7L-RkStfuw9FqkH90a6DsH_8n4AXfU,11976
55
+ plain/http/README.md,sha256=PFLkg8aljvdzZeXYiG9zxIZzE4_DwqjFHpgC_eu_XYA,9428
56
+ plain/http/__init__.py,sha256=ny0Rr4coLrQG7OvV1J2yLI1HlgvwJBboWiXlBvkIaP4,1429
57
+ plain/http/cookie.py,sha256=x13G3LIr0jxnPK1NQRptmi0DrAq9PsivQnQTm4LKaW0,2191
58
+ plain/http/exceptions.py,sha256=XJUckCrDhrTCAacmH2Qt-1xr0UZ1-LgpEYizyeL5kpQ,1446
59
+ plain/http/middleware.py,sha256=TPs585IIFjgp-5uUAJtIoigH6uwTS3FJqwFSsQdayd4,960
60
+ plain/http/multipartparser.py,sha256=Jm7dQzIL8ulYRh1MBjmQuUWF6KBhgZBLAVBBBZOTeaA,28133
61
+ plain/http/request.py,sha256=8HFHUw1UMEoXwkVY1rxqekZuq0mQ4HWbbkri6uZwxaY,28584
62
+ plain/http/response.py,sha256=b1jFpOtVmJMFI3oQ9NMGXK45BAsxz_r6udM8tsbSQI0,22079
63
+ plain/internal/__init__.py,sha256=n2AgdfNelt_tp8CS9JDzHMy_aiTUMPGZiFFwKmNz2fg,262
64
+ plain/internal/reloader.py,sha256=n7B-F-WeUXp37pAnvzKX9tcEbUxHSlYqa4gItyA_zko,2662
65
+ plain/internal/files/__init__.py,sha256=VctFgox4Q1AWF3klPaoCC5GIw5KeLafYjY5JmN8mAVw,63
66
+ plain/internal/files/base.py,sha256=fp8lo-4ec3RofJCClIkR3yZE-zyW_gDy9NeWTlV3ID8,4713
67
+ plain/internal/files/locks.py,sha256=jvLL9kroOo50kUo8dbuajDiFvgSL5NH6x5hudRPPjiQ,4022
68
+ plain/internal/files/move.py,sha256=qE1nAVdJO8PJyxcZyoPORFYNPLf2EEt_dyOyk7cg9HE,3338
69
+ plain/internal/files/temp.py,sha256=Y7Kb4j26NqZQuNqWMe91d3KJ63DP289prM31iRyAN3w,2969
70
+ plain/internal/files/uploadedfile.py,sha256=LohB2bBjfh3BwN9qCo_JtdnZe3knUBtT_vHAMlXErWE,4940
71
+ plain/internal/files/uploadhandler.py,sha256=bOTa-no1e2Q1ZCMSywhYaH7II8uOj9lQUB9j9FgGky8,7537
72
+ plain/internal/files/utils.py,sha256=ERcjMX5Qalpt9EKOonkfOc2Lw8fGgpu2eXLOCAd9aiY,3003
73
+ plain/internal/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
+ plain/internal/handlers/base.py,sha256=9H4bWTq0Q-X_RXOHOYgR5Vgmw2RstBZ-sUXxbeJK6Js,6541
75
+ plain/internal/handlers/exception.py,sha256=oGA5Gg1GHc0eTOxOMayKlQOMytuACWcWWmZRstHG7qw,5070
76
+ plain/internal/handlers/wsgi.py,sha256=UAeny1rKCCClPZKE6JGemEEFKKbfeMBKd4RzHyS0DZs,8950
77
+ plain/internal/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
+ plain/internal/middleware/headers.py,sha256=6ortXSnoZ8c7d-s2pWdajLaVIjQ3Glw9HMrFObXG7Kk,2002
79
+ plain/internal/middleware/hosts.py,sha256=ZGG4TVSVLv7LBm5ckZEvfqeBFRAANbP0F9d3SYmUoO8,5890
80
+ plain/internal/middleware/https.py,sha256=-R1Aa9Auai3GyhQHUggaUQI_TuMR_35J9u3xE9HX2xQ,1117
81
+ plain/internal/middleware/slash.py,sha256=6Srrmyaleu8PIRbHeodCmc8oa51gDDdX_OwPz5H1DEQ,3073
82
+ plain/logs/README.md,sha256=dw8Za2nOaqV116D2LMIzJUpGYnn5nW67rXzYYxq0h1Q,7195
83
+ plain/logs/__init__.py,sha256=QzurbfNXbx04QjZBsYxnIc3pwD9uN_mnuDArksEDsw8,54
84
+ plain/logs/app.py,sha256=T5ofCgMPpE3glf1GluZ0xu3yzzgGTm-r9kVggM-jITc,5605
85
+ plain/logs/configure.py,sha256=nz1bAbg4XDyJWpbdsvTKTJ6geUeTex4z__PP-GzWRN8,2717
86
+ plain/logs/debug.py,sha256=vz6GYy5QJTBLYHyIsYvPq5BOziPJSzo0pxqTid4beDs,1372
87
+ plain/logs/filters.py,sha256=7e90DSsqDg6BibywiEnh1x-R9JqcOkdQuMF2uQyurEs,457
88
+ plain/logs/formatters.py,sha256=8EULZzGY7xUXM4X9ZOwG3QURwkVlHpjMkL6UEBt9Gsg,2529
89
+ plain/packages/README.md,sha256=ZbEKfx_zwB0ihvQGe6GrjpDvVfqvHfzxSoTZEdiJXfM,4852
90
+ plain/packages/__init__.py,sha256=OpQny0xLplPdPpozVUUkrW2gB-IIYyDT1b4zMzOcCC4,160
91
+ plain/packages/config.py,sha256=0c53dWlaTHNPdIOHP7-TWwz0xcIpLQBY8d7NOWNasBs,3167
92
+ plain/packages/registry.py,sha256=PIPmFFrTnk8O0tdMZAy4Ogk61IeSxd-TIohWxmjRsU4,9036
93
+ plain/preflight/README.md,sha256=-Z2pMt3en6H-92HFw6uHNYN4hypSv77ww93GfttNhvM,7521
94
+ plain/preflight/__init__.py,sha256=6jDCcm5QosjKne_HKcTZdUv5X_jk_NCYxrzupJEsoTE,467
95
+ plain/preflight/checks.py,sha256=kJcr-Hq5bsjKw1BUYR6r6nFg3Ecgrd1DS5SudUr4rSU,289
96
+ plain/preflight/files.py,sha256=OjD76e-l_cDXJGHMk21LsoPp6V_HxD5v0zyvOKkEDu0,840
97
+ plain/preflight/registry.py,sha256=87FvZRsbbbuFJGKJ2PC5-o9aZ1MIKgLPYOTR3ghlzK4,2920
98
+ plain/preflight/results.py,sha256=V2LeyZeL8tM4ixnGZjsHuI7kYCIbaoaWHP_t0BjH5yA,1121
99
+ plain/preflight/security.py,sha256=mTVRACvAmGuK9LvPSFuuW0XHdhRRREmLzfkDcoh6snE,3067
100
+ plain/preflight/settings.py,sha256=Qr_aOdxDh6_XL6HwAKflDICdeawmd3oGcGU2wmESELo,2083
101
+ plain/preflight/urls.py,sha256=Asw_vq-70NRqr15yuBAYL0JCZ04liumORYT3I3KmF_k,437
102
+ plain/runtime/README.md,sha256=AXj420OhD7ecdoVQ4I-7XY7VzlXY35_wBNm4yoN4Prk,5989
103
+ plain/runtime/__init__.py,sha256=qwZuhx4hpJsW9YkgtGzumm0YXY-dK29X0V_p9ugJags,2605
104
+ plain/runtime/global_settings.py,sha256=3b4Sy6vC4Z4Ho75zT-fBHOHv9DTOu0BfioLH2l5j36U,6457
105
+ plain/runtime/secret.py,sha256=UTlKNhM4ut_IHtQ8T0eXSvhrI-O7r_X20fEdpSIb3EI,435
106
+ plain/runtime/user_settings.py,sha256=sHfPlCgMDKDcqgaHLwOu9_M8h0uVTXLLbmkAdJpTHbs,14054
107
+ plain/runtime/utils.py,sha256=sHOv9SWCalBtg32GtZofimM2XaQtf_jAyyf6RQuOlGc,851
108
+ plain/server/LICENSE,sha256=Xt_dw4qYwQI9qSi2u8yMZeb4HuMRp5tESRKhtvvJBgA,1707
109
+ plain/server/README.md,sha256=HQEIDN8zJ10tYXopKrFTjTXGPsIJNFL75BLGbz55suU,5622
110
+ plain/server/__init__.py,sha256=DtRgEcr4IxF4mrtCHloIprk_Q4k1oju4F2VHoyvu4ow,212
111
+ plain/server/app.py,sha256=ozaqdb-a_T3ps7T5EJwIPM63F_497J4o7kw9Pbq7Ga0,1229
112
+ plain/server/arbiter.py,sha256=ChlA_qQPT8sAVsOkD5f_d18Q8cpoR-uSoa92iBxolG4,17461
113
+ plain/server/config.py,sha256=-T1w8dbUwwLd898P1HNufe8Kw8VJDYJaeKY-UuKzvTo,3221
114
+ plain/server/errors.py,sha256=sKl_OJ5Uw-a_r_dZ2o4I8JaKeTrjvY_LR12F6B_p4-g,956
115
+ plain/server/glogging.py,sha256=9FS-kRPja3BfNAdjyCghoS2st8VXHUc1--4dvQ3Ph18,9662
116
+ plain/server/pidfile.py,sha256=3BrA5DgFqAp3jWQdZjRdnVS4vdTytg209jHN4JCGDNI,2548
117
+ plain/server/sock.py,sha256=PXmlVNTv4lJR8Rfe0t7VeXqm0MtZgudNM_84NkwsR58,7098
118
+ plain/server/util.py,sha256=BetOuZ344wXu6VwgbRDcp1SIWBSCDY1L8m38L_y7eU4,8873
119
+ plain/server/http/__init__.py,sha256=kQwTk1l3hYJwVrzr1p-XNAbWYe0icsD7l0ZyGRXMbOI,300
120
+ plain/server/http/body.py,sha256=fxl9qV8uIYxwXbsJxcmsVX2aRfIb5jpSJxKm9Ug69B0,8352
121
+ plain/server/http/errors.py,sha256=hoy9NnXLWWbYJ4DkMnM0lcFEqaggI-KfCVgaAYIB7Dw,3827
122
+ plain/server/http/message.py,sha256=9bntA2glNchIJUK6O4NNNCCkrtr2xsDUqldvPTnP0gk,15135
123
+ plain/server/http/parser.py,sha256=8z1WZNsEvKh7qSePy3YkSSsRySGj8uz7Hz1N10bk2DQ,1803
124
+ plain/server/http/unreader.py,sha256=jD2PGZ574FGmQOZlqWfs1VWzp-ttfIso_GzYaId6KYQ,2238
125
+ plain/server/http/wsgi.py,sha256=ERn8JQeoFIA9g7Os1gPIRg32p_CYqtcipYvnIx9U1is,13923
126
+ plain/server/workers/__init__.py,sha256=sLq8nrIIf9Wjw5_qQsh6cnHnY7eIqCpzSedKrNmmn7s,145
127
+ plain/server/workers/base.py,sha256=aeZIfZ2pBjlnYdVuCIBsDUXGAg-tJ1vIQQTfYwQk0sQ,9721
128
+ plain/server/workers/sync.py,sha256=xzE-t-6LW1DtGmwhIRVBgTnc3SwBe1CgL81MJQ7WFnY,7292
129
+ plain/server/workers/thread.py,sha256=xx0lq0JKI4UPn4RUjTETdOEOyWWVR_X0n34JIY82yek,13860
130
+ plain/server/workers/workertmp.py,sha256=egGReVvldlOBQfQGcpLpjt0zvPwR4C_N-UJKG-U_6w4,1299
131
+ plain/signals/README.md,sha256=5HR6of7tgwBiZQqIdZMrwgiMjUH9qjY8inl-tSmR8Lo,5500
132
+ plain/signals/__init__.py,sha256=VDhotllLUQVg3eA1LuAJM9pwGTaf_bzRGzc4mJhd-sY,98
133
+ plain/signals/dispatch/__init__.py,sha256=FzEygqV9HsM6gopio7O2Oh_X230nA4d5Q9s0sUjMq0E,292
134
+ plain/signals/dispatch/dispatcher.py,sha256=ofpu8wMEx8O-I9SO9Rkk1ABH09-71QnmGZlt8vsZAw8,12317
135
+ plain/signals/dispatch/license.txt,sha256=o9EhDhsC4Q5HbmD-IfNGVTEkXtNE33r5rIt3lleJ8gc,1727
136
+ plain/skills/README.md,sha256=WUhXiLU42pIdcXiT-zugnsOXj4OjiCmec_XcM85KAyo,1434
137
+ plain/skills/plain-docs/SKILL.md,sha256=CoOEhTsOpNczBuGxnHbGIVZ9Yahreq0RoXzqhVwtajY,560
138
+ plain/skills/plain-install/SKILL.md,sha256=lPhUMmKAQpjrVMp73T5pBkwkIaJjWTxAqtyS8Hio8mM,746
139
+ plain/skills/plain-request/SKILL.md,sha256=C37GEWRIpJFr-fbnEb08ENOJg-A7WUZxoYPneFAkHO4,771
140
+ plain/skills/plain-shell/SKILL.md,sha256=njxD1dXVmSVZkcUnEm9_62hn_P6JMQmCpVOEzq29RvY,400
141
+ plain/skills/plain-upgrade/SKILL.md,sha256=79otYHv68Tfk3j08kRbg7o9eiNzzFZFyXCfLuIlT2Ho,927
142
+ plain/templates/README.md,sha256=zhAayenfxtwq6r1-NjoaD-wjaazyhm76BCacGF24ACc,8803
143
+ plain/templates/__init__.py,sha256=bX76FakE9T7mfK3N0deN85HlwHNQpeigytSC9Z8LcOs,451
144
+ plain/templates/core.py,sha256=mbcH0yTeFOI3XOg9dYSroXRIcdv9sETEy4HzY-ugwco,1258
145
+ plain/templates/jinja/__init__.py,sha256=7odJxZfn1p2zhzuuehdcLPIZm8jZDXMYAKnu-fpt2r8,2199
146
+ plain/templates/jinja/environments.py,sha256=vadQQ7UhZOJPbeyI3ErKYE4_IwpWyABSDYS_aq5PA10,2141
147
+ plain/templates/jinja/extensions.py,sha256=qFSNUoV1rxdmKORvQhu698KxbSayltcnaGVbA9YMviE,1566
148
+ plain/templates/jinja/filters.py,sha256=g70cw1jzvYco2v-u4SeceOWBX_qxHI5k9AODMn8ewsY,1590
149
+ plain/templates/jinja/globals.py,sha256=2UwXclm0q_VILYvkQzTmoFaMS30FVZPmk9QZ8wKB7QA,572
150
+ plain/test/README.md,sha256=0XEKluidRikGk2V3wQJgvVChxRh4eXOeUSklTQS-msc,5844
151
+ plain/test/__init__.py,sha256=MhNHtp7MYBl9kq-pMRGY11kJ6kU1I6vOkjNkit1TYRg,94
152
+ plain/test/client.py,sha256=79M2aUYCqcegIW_DazP1Vx-TczzNC-pnMFPHZEvyNRU,30853
153
+ plain/test/encoding.py,sha256=txj_FCbC4GxH-JCkopW5LaZz8cGsrKQiculjFkjkzuY,3372
154
+ plain/test/exceptions.py,sha256=VHet3oylks4JdWWAiVUrhPMdDr1Dx0uuQE6irXS-e-U,392
155
+ plain/urls/README.md,sha256=O-3CFXJnliMJ5bUmvq75Tbi1_Yx2vdbVMgEIVZSGPxE,6896
156
+ plain/urls/__init__.py,sha256=DFO2OL1IllHW5USPIb5uYvvzf_G-Bl0Qu1zrRLHmWyM,542
157
+ plain/urls/converters.py,sha256=7_1eiQcmhLf57O3AioC279_BrclSqi1ZNESXOPed7Y4,1410
158
+ plain/urls/exceptions.py,sha256=eRLlvH11txmTUlBITXtJ_FDz0W7Cn0Ikmciuoj6flF4,132
159
+ plain/urls/patterns.py,sha256=CSuJEOCOjBwIFeJ2BTZbb2Fw-pgbFnWOj8dsszwH4KE,9036
160
+ plain/urls/resolvers.py,sha256=XhURKhoeZ562htjv1TF6Ja5Zh5JSggn05teB_-1S3uI,13280
161
+ plain/urls/routers.py,sha256=B1fX-FQTxmr-b_YeHQUMPuilLVqOi6-RqgPgu6MtFDY,2831
162
+ plain/urls/utils.py,sha256=VP91p0ilpWUimQSfiILUKjBnWolKXgYvgZD9X374R1g,1326
163
+ plain/utils/README.md,sha256=MyNOfltVv24C5m13dEFnKEV3Adq7uwjYoHv3RGEsLxw,7080
164
+ plain/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
+ plain/utils/cache.py,sha256=e5bfuEqNfna3dZTE9ZDGKwQKtbRJFRSJeEXXKq2LbGo,5642
166
+ plain/utils/crypto.py,sha256=JU8ZrCz8gKusmBoPvbZEg9W9Am3K6GAfI_uijvg7u3c,2819
167
+ plain/utils/datastructures.py,sha256=cZGG7181A2BakVfBma89Pek2VbrSyYpBEh52Ws19x6I,11937
168
+ plain/utils/dateparse.py,sha256=Z330Erc5FEXkes5ndI6drkvL-2sDf4z6-3SUR_WrfQk,5569
169
+ plain/utils/deconstruct.py,sha256=FnNtkOoEPtMYOWhveIJkVlCbKeZX7snutJJpK2sMa04,2164
170
+ plain/utils/decorators.py,sha256=IoJ-oH3JtJaLH8ndeP6uU06BbkOAzMmtUdabG9FkIdw,455
171
+ plain/utils/dotenv.py,sha256=3ZW4S7u-GId1XHSwcLinp6Mqs5od4qXe3K0N2KexDNE,11417
172
+ plain/utils/duration.py,sha256=QBFh-iLvRXuw0N0UUdFqabeJvZVqkgFME_c2gRSLwhI,1340
173
+ plain/utils/encoding.py,sha256=40siECldXUFyZ_IkyQCeOkyQuXb7i4Rpys3nFWFHnuc,4482
174
+ plain/utils/functional.py,sha256=ujpUXAeo1M4sTTBMouJZi2dq3N8SflNzmD6ER7Vh3z4,14833
175
+ plain/utils/hashable.py,sha256=1Kh_SFxsaR2d3xQMNEtHb4eKSHugtNt66iZ3ZvKjt50,811
176
+ plain/utils/html.py,sha256=pjmEuhoVt_nikyjzwfJNFfnZaKeC9xe1vHZwsX-fHGs,4255
177
+ plain/utils/http.py,sha256=SlcrPXgCUGlZirceKWE3uCO2Z32p6v2k-CBncAd7HS4,5847
178
+ plain/utils/inspect.py,sha256=3RyZG4J9A16Lx3eDcn5vyRiTowixMID4cI5QJu8sSDc,1478
179
+ plain/utils/ipv6.py,sha256=TLOQVN0aqKGM4eS_HwarTgO-bGIql-k5AipDPgtQdCA,1352
180
+ plain/utils/itercompat.py,sha256=2v8UaAMgRCju_ZUH3CKg6Q4XYUaQPqZ7_cRKSgUYs8w,258
181
+ plain/utils/module_loading.py,sha256=JRltNNz7XoQpaMdd7RYHuEWVN2ycsrLJ4Yw7XTp97uQ,1776
182
+ plain/utils/regex_helper.py,sha256=zoPaCHo8EfT_Y7EgXknv9dpXkSaQTKSfC6iT-E0H7kA,13390
183
+ plain/utils/safestring.py,sha256=6YR1yimQVmSsk8tNiYxKuhsSIiOP2PFV1jSKc6--x_c,2131
184
+ plain/utils/text.py,sha256=cuACozKwAeR3x5POAOvNt3KYwn3jSbJJYnEVR-OFmuY,10169
185
+ plain/utils/timesince.py,sha256=a_-ZoPK_s3Pt998CW4rWp0clZ1XyK2x04hCqak2giII,5928
186
+ plain/utils/timezone.py,sha256=2q9ayPO_991CHlyYFHcRMngbWskYSieGVni6FnKXrSw,6877
187
+ plain/utils/tree.py,sha256=FbgAJ_eH4WsBEDWrxuo1SHXaMCBgZ3M_SH5eL4QKAow,4858
188
+ plain/views/README.md,sha256=7C3tn3zvbwT38x1dRWpspTQcUbtUrejFFhqB4xp83U4,9944
189
+ plain/views/__init__.py,sha256=M-R2AgHnlGk75KdL4ZN_Cz2HuSOg2mV8gGnPhhvhsYA,371
190
+ plain/views/base.py,sha256=pWm_P0Dlbk7uZ1pyOOI7r0T4ojAAs_pnCBRkkYdZaP0,4487
191
+ plain/views/errors.py,sha256=3Vvl_Xg7fnVQMPazHup8s5Cu4CneLa05qycWccY9HoU,1501
192
+ plain/views/exceptions.py,sha256=-YKH1Jd9Zm_yXiz797PVjJB6VWaPCTXClHIUkG2fq78,198
193
+ plain/views/forms.py,sha256=j5WAMLeW1efU9M0q1W6SFHMDMBZfkFfW4WRLKwrXjqk,2435
194
+ plain/views/objects.py,sha256=qKK8lYQKK7DTBPrMUebZ2HevgU9MmyWeXFWG1lT5ZbM,5393
195
+ plain/views/redirect.py,sha256=rt5RF1Rs2yYFLole3Mznu5iU9aeumu49VAaX4WCd_xk,2139
196
+ plain/views/templates.py,sha256=ElyqgpbkoJt72yU1gF7b626TB3R8viDAf-LYloulUBA,1925
197
+ plain-0.101.2.dist-info/METADATA,sha256=EeQAZpll3Jn51d0GwuHSbstJjL_78xe-43GS_fSsQpU,4550
198
+ plain-0.101.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
199
+ plain-0.101.2.dist-info/entry_points.txt,sha256=1Ys2lsSeMepD1vz8RSrJopna0RQfUd951vYvCRsvl6A,45
200
+ plain-0.101.2.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
201
+ plain-0.101.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ plain = plain.cli.core:cli
plain/AGENTS.md DELETED
@@ -1,18 +0,0 @@
1
- # Plain AGENTS.md
2
-
3
- Plain is a Python web framework that was originally forked from Django. While it still has a lot in common with Django, there are also significant changes -- don't solely rely on knowledge of Django when working with Plain.
4
-
5
- ## Commands
6
-
7
- The `plain` CLI is the main entrypoint for the framework. If `plain` is not available by itself, try `uv run plain`.
8
-
9
- - `plain shell -c <command>`: Run a Python command with Plain configured.
10
- - `plain run <filename>`: Run a Python script with Plain configured.
11
- - `plain agent docs <package>`: Show README.md and symbolicated source files for a specific package.
12
- - `plain agent docs --list`: List packages with docs available.
13
- - `plain agent request <path> --user <user_id>`: Make an authenticated request to the application and inspect the output.
14
- - `plain --help`: List all available commands (including those from installed packages).
15
-
16
- ## Code style
17
-
18
- - Imports should be at the top of the file, unless there is a specific reason to import later (e.g. to avoid circular imports).
@@ -1,20 +0,0 @@
1
- import click
2
-
3
- from .docs import docs
4
- from .md import md
5
- from .request import request
6
-
7
-
8
- @click.group("agent", invoke_without_command=True)
9
- @click.pass_context
10
- def agent(ctx):
11
- """Tools for coding agents."""
12
- if ctx.invoked_subcommand is None:
13
- # If no subcommand provided, show all AGENTS.md files
14
- ctx.invoke(md, show_all=True, show_list=False, package="")
15
-
16
-
17
- # Add commands to the group
18
- agent.add_command(docs)
19
- agent.add_command(md)
20
- agent.add_command(request)