Pyjoyment 0.1.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.
Files changed (200) hide show
  1. Pyjo/App/ASGI.py +735 -0
  2. Pyjo/App/CGI.py +773 -0
  3. Pyjo/App/FastCGI.py +631 -0
  4. Pyjo/App/Lambda.py +987 -0
  5. Pyjo/App/WSGI.py +427 -0
  6. Pyjo/App/__init__.py +409 -0
  7. Pyjo/Asset/File.py +377 -0
  8. Pyjo/Asset/Memory.py +235 -0
  9. Pyjo/Asset/__init__.py +200 -0
  10. Pyjo/Base.py +215 -0
  11. Pyjo/Cache.py +119 -0
  12. Pyjo/Collection.py +570 -0
  13. Pyjo/Content/MultiPart.py +325 -0
  14. Pyjo/Content/Single.py +193 -0
  15. Pyjo/Content/__init__.py +806 -0
  16. Pyjo/Cookie/Request.py +90 -0
  17. Pyjo/Cookie/Response.py +294 -0
  18. Pyjo/Cookie/__init__.py +143 -0
  19. Pyjo/DOM/CSS.py +1019 -0
  20. Pyjo/DOM/HTML.py +550 -0
  21. Pyjo/DOM/__init__.py +1329 -0
  22. Pyjo/Date.py +294 -0
  23. Pyjo/EventEmitter.py +522 -0
  24. Pyjo/Exception.py +494 -0
  25. Pyjo/File.py +959 -0
  26. Pyjo/GetOpt.py +605 -0
  27. Pyjo/Headers.py +760 -0
  28. Pyjo/HelloWorld.py +77 -0
  29. Pyjo/Home.py +109 -0
  30. Pyjo/IOLoop/ChildProcess.py +591 -0
  31. Pyjo/IOLoop/Client.py +736 -0
  32. Pyjo/IOLoop/Datagram.py +982 -0
  33. Pyjo/IOLoop/Messages.py +664 -0
  34. Pyjo/IOLoop/Server.py +458 -0
  35. Pyjo/IOLoop/Stdio.py +446 -0
  36. Pyjo/IOLoop/Stream.py +644 -0
  37. Pyjo/IOLoop/Subprocess.py +419 -0
  38. Pyjo/IOLoop/TLS.py +360 -0
  39. Pyjo/IOLoop/__init__.py +1036 -0
  40. Pyjo/IOLoop/certs/README.md +41 -0
  41. Pyjo/IOLoop/certs/ca.crt +20 -0
  42. Pyjo/IOLoop/certs/server.crt +21 -0
  43. Pyjo/IOLoop/certs/server.key +28 -0
  44. Pyjo/JSON/Pointer.py +135 -0
  45. Pyjo/JSON/RPC.py +772 -0
  46. Pyjo/JSON/Schema.py +2506 -0
  47. Pyjo/JSON/__init__.py +573 -0
  48. Pyjo/Loader.py +342 -0
  49. Pyjo/Log.py +723 -0
  50. Pyjo/Message/Request.py +725 -0
  51. Pyjo/Message/Response.py +476 -0
  52. Pyjo/Message/__init__.py +830 -0
  53. Pyjo/Parameters.py +472 -0
  54. Pyjo/Path.py +419 -0
  55. Pyjo/Promise.py +894 -0
  56. Pyjo/Prove.py +1305 -0
  57. Pyjo/Reactor/Asyncio.py +854 -0
  58. Pyjo/Reactor/Poll.py +439 -0
  59. Pyjo/Reactor/__init__.py +525 -0
  60. Pyjo/SSE.py +119 -0
  61. Pyjo/Server/ASGI.py +486 -0
  62. Pyjo/Server/CGI.py +259 -0
  63. Pyjo/Server/Daemon.py +1007 -0
  64. Pyjo/Server/FastCGI.py +517 -0
  65. Pyjo/Server/Hot/__init__.py +687 -0
  66. Pyjo/Server/Hot/__main__.py +5 -0
  67. Pyjo/Server/Lambda.py +641 -0
  68. Pyjo/Server/Prefork.py +917 -0
  69. Pyjo/Server/Prethread.py +394 -0
  70. Pyjo/Server/Reload/Backend/Poll.py +114 -0
  71. Pyjo/Server/Reload/Backend/__init__.py +126 -0
  72. Pyjo/Server/Reload/__init__.py +482 -0
  73. Pyjo/Server/Reload/__main__.py +5 -0
  74. Pyjo/Server/Stdio.py +228 -0
  75. Pyjo/Server/WSGI.py +349 -0
  76. Pyjo/Server/__init__.py +396 -0
  77. Pyjo/String/Bytes.py +532 -0
  78. Pyjo/String/HTML.py +210 -0
  79. Pyjo/String/Mixin.py +333 -0
  80. Pyjo/String/Template.py +470 -0
  81. Pyjo/String/Unicode.py +553 -0
  82. Pyjo/String/__init__.py +0 -0
  83. Pyjo/TOML.py +232 -0
  84. Pyjo/Template.py +1290 -0
  85. Pyjo/Test/Pyjo.py +1454 -0
  86. Pyjo/Test/__init__.py +1233 -0
  87. Pyjo/Test/pytest_plugin.py +305 -0
  88. Pyjo/Transaction/HTTP.py +374 -0
  89. Pyjo/Transaction/WebSocket.py +709 -0
  90. Pyjo/Transaction/__init__.py +429 -0
  91. Pyjo/URL/Template.py +459 -0
  92. Pyjo/URL/__init__.py +651 -0
  93. Pyjo/Upload.py +139 -0
  94. Pyjo/UserAgent/CookieJar.py +391 -0
  95. Pyjo/UserAgent/Proxy.py +138 -0
  96. Pyjo/UserAgent/Server.py +239 -0
  97. Pyjo/UserAgent/Transactor.py +766 -0
  98. Pyjo/UserAgent/__init__.py +1243 -0
  99. Pyjo/Util.py +3567 -0
  100. Pyjo/WebSocket.py +332 -0
  101. Pyjo/YAML.py +1167 -0
  102. Pyjo/__init__.py +441 -0
  103. Pyjo/__main__.py +35 -0
  104. Pyjo/py.typed +0 -0
  105. Pyjoyment/Command/Author/__init__.py +20 -0
  106. Pyjoyment/Command/Author/generate/__init__.py +106 -0
  107. Pyjoyment/Command/Author/generate/app.py +299 -0
  108. Pyjoyment/Command/Author/generate/dockerfile.py +281 -0
  109. Pyjoyment/Command/Author/generate/kubernetes.py +514 -0
  110. Pyjoyment/Command/Author/generate/lambda_zip.py +372 -0
  111. Pyjoyment/Command/Author/generate/lite_app.py +113 -0
  112. Pyjoyment/Command/Author/generate/plugin.py +267 -0
  113. Pyjoyment/Command/Author/generate/pyproject.py +123 -0
  114. Pyjoyment/Command/Author/inflate.py +101 -0
  115. Pyjoyment/Command/__init__.py +332 -0
  116. Pyjoyment/Command/asgi.py +92 -0
  117. Pyjoyment/Command/cgi.py +90 -0
  118. Pyjoyment/Command/daemon.py +160 -0
  119. Pyjoyment/Command/eval.py +128 -0
  120. Pyjoyment/Command/fastcgi.py +99 -0
  121. Pyjoyment/Command/get.py +305 -0
  122. Pyjoyment/Command/lambda.py +117 -0
  123. Pyjoyment/Command/prefork.py +181 -0
  124. Pyjoyment/Command/prethread.py +126 -0
  125. Pyjoyment/Command/routes.py +133 -0
  126. Pyjoyment/Command/stdio.py +92 -0
  127. Pyjoyment/Command/version.py +141 -0
  128. Pyjoyment/Command/wsgi.py +93 -0
  129. Pyjoyment/Commands.py +546 -0
  130. Pyjoyment/Controller.py +1307 -0
  131. Pyjoyment/HelloWorld.py +56 -0
  132. Pyjoyment/Lite.py +494 -0
  133. Pyjoyment/Plugin/AllowedHosts.py +205 -0
  134. Pyjoyment/Plugin/Config.py +248 -0
  135. Pyjoyment/Plugin/DefaultHelpers.py +1202 -0
  136. Pyjoyment/Plugin/EPLRenderer.py +134 -0
  137. Pyjoyment/Plugin/EPRenderer.py +212 -0
  138. Pyjoyment/Plugin/HeaderCondition.py +98 -0
  139. Pyjoyment/Plugin/Health.py +325 -0
  140. Pyjoyment/Plugin/JSONConfig.py +172 -0
  141. Pyjoyment/Plugin/Mount.py +223 -0
  142. Pyjoyment/Plugin/PyRenderer.py +209 -0
  143. Pyjoyment/Plugin/TOMLConfig.py +141 -0
  144. Pyjoyment/Plugin/TagHelpers.py +959 -0
  145. Pyjoyment/Plugin/YAMLConfig.py +178 -0
  146. Pyjoyment/Plugin/__init__.py +80 -0
  147. Pyjoyment/Plugins.py +215 -0
  148. Pyjoyment/Renderer.py +673 -0
  149. Pyjoyment/Routes/Match.py +251 -0
  150. Pyjoyment/Routes/Pattern.py +443 -0
  151. Pyjoyment/Routes/Route.py +827 -0
  152. Pyjoyment/Routes/__init__.py +419 -0
  153. Pyjoyment/Sessions.py +255 -0
  154. Pyjoyment/Static.py +421 -0
  155. Pyjoyment/Types.py +238 -0
  156. Pyjoyment/Validator/Validation.py +395 -0
  157. Pyjoyment/Validator/__init__.py +304 -0
  158. Pyjoyment/__init__.py +1083 -0
  159. Pyjoyment/__main__.py +5 -0
  160. Pyjoyment/py.typed +0 -0
  161. Pyjoyment/resources/public/favicon.ico +0 -0
  162. Pyjoyment/resources/public/favicon.svg +345 -0
  163. Pyjoyment/resources/public/pyjo/bootstrap/LICENSE +21 -0
  164. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.bundle.min.js.map +1 -0
  165. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.css +6 -0
  166. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.js +7 -0
  167. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.min.css.map +1 -0
  168. Pyjoyment/resources/public/pyjo/failunicat.png +0 -0
  169. Pyjoyment/resources/public/pyjo/failunicat.svg +50 -0
  170. Pyjoyment/resources/public/pyjo/highlight.js/LICENSE +29 -0
  171. Pyjoyment/resources/public/pyjo/highlight.js/highlight-mojo-dark.css +56 -0
  172. Pyjoyment/resources/public/pyjo/highlight.js/highlight.min.js +1262 -0
  173. Pyjoyment/resources/public/pyjo/highlight.js/pyjoyment.min.js +1 -0
  174. Pyjoyment/resources/public/pyjo/logo-black-4x.png +0 -0
  175. Pyjoyment/resources/public/pyjo/logo-black-4x.svg +346 -0
  176. Pyjoyment/resources/public/pyjo/logo-white-2x.png +0 -0
  177. Pyjoyment/resources/public/pyjo/logo-white-2x.svg +346 -0
  178. Pyjoyment/resources/public/pyjo/logo-white-4x.png +0 -0
  179. Pyjoyment/resources/public/pyjo/logo-white-4x.svg +346 -0
  180. Pyjoyment/resources/public/pyjo/logo-white.png +0 -0
  181. Pyjoyment/resources/public/pyjo/logo-white.svg +346 -0
  182. Pyjoyment/resources/public/pyjo/logo.png +0 -0
  183. Pyjoyment/resources/public/pyjo/logo.svg +346 -0
  184. Pyjoyment/resources/public/pyjo/notfound.png +0 -0
  185. Pyjoyment/resources/public/pyjo/notfound.svg +3 -0
  186. Pyjoyment/resources/public/pyjo/nounicat.png +0 -0
  187. Pyjoyment/resources/public/pyjo/nounicat.svg +5 -0
  188. Pyjoyment/resources/public/pyjo/pinstripe-dark.png +0 -0
  189. Pyjoyment/resources/public/pyjo/pinstripe-dark.svg +4 -0
  190. Pyjoyment/resources/public/pyjo/pinstripe-light.png +0 -0
  191. Pyjoyment/resources/public/pyjo/pinstripe-light.svg +4 -0
  192. Pyjoyment/resources/public/pyjo/pyjo.css +204 -0
  193. Pyjoyment/resources/templates/pyjo/debug.html.ep +346 -0
  194. Pyjoyment/resources/templates/pyjo/exception.html.ep +32 -0
  195. Pyjoyment/resources/templates/pyjo/not_found.html.ep +45 -0
  196. pyjoyment-0.1.0.dist-info/METADATA +376 -0
  197. pyjoyment-0.1.0.dist-info/RECORD +200 -0
  198. pyjoyment-0.1.0.dist-info/WHEEL +4 -0
  199. pyjoyment-0.1.0.dist-info/entry_points.txt +8 -0
  200. pyjoyment-0.1.0.dist-info/licenses/LICENSE +201 -0
Pyjo/TOML.py ADDED
@@ -0,0 +1,232 @@
1
+ """
2
+ Pyjo.TOML - TOML loader and dumper
3
+ ==================================
4
+ ::
5
+
6
+ from Pyjo.TOML import dump, load
7
+
8
+ config = load('name = "Pyjoyment"\\n\\n[server]\\nworkers = 4\\n')
9
+
10
+ string = dump({'name': 'Pyjoyment', 'server': {'workers': 4}})
11
+
12
+ :mod:`Pyjo.TOML` loads `TOML 1.0 <https://toml.io>`_ with :mod:`tomllib`
13
+ of the standard library, which has no writer of TOML, and dumps it, the
14
+ format of :mod:`Pyjoyment.Plugin.TOMLConfig` and of the stash value ``toml``
15
+ of the renderer of :mod:`Pyjoyment`.
16
+
17
+ :func:`dump` writes a :class:`dict` as a table, whose keys come first, and
18
+ then its tables, ``[server]``, a :class:`dict` of a :class:`dict`, and its
19
+ arrays of tables, ``[[products]]``, a :class:`list` of :class:`dict`, whose
20
+ tables follow each of them. A table with keys only in its tables has no line
21
+ of its own, as ``[a.b]`` defines ``a``. Other lists and tuples are arrays,
22
+ whose dicts are inline tables, ``[{ name = "x" }, 2]``. A string is a basic
23
+ string, with the escapes of TOML, and a string of several lines is a
24
+ multi-line basic string, ``\"\"\"...\"\"\"``. :class:`datetime.datetime`,
25
+ :class:`datetime.date` and :class:`datetime.time` are the date-times, the
26
+ dates and the times of TOML, an aware one with its offset. A key is bare
27
+ where TOML allows it, and quoted otherwise. ::
28
+
29
+ # 'name = "app"\\n\\n[server]\\nworkers = 4\\n'
30
+ dump({'name': 'app', 'server': {'workers': 4}})
31
+
32
+ TOML has no null and no bytes, so ``None`` and :class:`bytes` raise an error,
33
+ as do a key that is not a string, a value at the top that is not a table, and
34
+ a collection inside itself. Another object is what :mod:`Pyjo.JSON` encodes,
35
+ the value of its method ``__json__`` or its string.
36
+
37
+ See also
38
+ --------
39
+
40
+ :mod:`Pyjoyment.Plugin.TOMLConfig`, :mod:`Pyjo.YAML`, https://mojolicious.org.
41
+ """
42
+
43
+ import Pyjo.Exception
44
+
45
+ from Pyjo.YAML import _data as _yaml_data # pyright: ignore[reportPrivateUsage]
46
+
47
+ import datetime
48
+ import math
49
+ import re
50
+ import tomllib
51
+
52
+ from typing import Any, cast
53
+
54
+
55
+ # A key that does not need its quotes
56
+ _RE_BARE = re.compile(r'[A-Za-z0-9_-]+')
57
+
58
+ # The characters that a basic string escapes, and the ones that a multi-line basic string escapes, without the line
59
+ # break and the tab
60
+ _RE_ESCAPE = re.compile(r'["\\\x00-\x1f\x7f\ud800-\udfff]')
61
+ _RE_ESCAPE_MULTILINE = re.compile(r'["\\\x00-\x08\x0b-\x1f\x7f\ud800-\udfff]')
62
+
63
+ _ESCAPED = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r'}
64
+
65
+
66
+ def dump(value: Any, *, sort_keys: bool = False) -> str:
67
+ """::
68
+
69
+ string = dump(value)
70
+ string = dump(value, sort_keys=True)
71
+
72
+ Dump a table to a TOML string, with the keys of a table in the order of the
73
+ :class:`dict`, or sorted with ``sort_keys``. ::
74
+
75
+ # 'name = "Pyjoyment"\\n'
76
+ dump({'name': 'Pyjoyment'})
77
+ """
78
+ table = _data(value)
79
+ if not isinstance(table, dict):
80
+ raise _error(f'needs a table at the top, not a {type(table).__name__}')
81
+ lines: list[str] = []
82
+ _Dumper(lines, sort_keys).table(cast(dict[Any, Any], table), [], None)
83
+ return ''.join(f'{line}\n' for line in lines)
84
+
85
+
86
+ def load(string: str) -> dict[str, Any]:
87
+ """::
88
+
89
+ value = load(string)
90
+
91
+ Load a TOML string with :func:`tomllib.loads`, whose error is a
92
+ :mod:`Pyjo.Exception`. ::
93
+
94
+ # {'name': 'Pyjoyment'}
95
+ load('name = "Pyjoyment"')
96
+ """
97
+ try:
98
+ return tomllib.loads(string)
99
+ except tomllib.TOMLDecodeError as e:
100
+ raise _error(f'failed to parse: {e}') from None
101
+
102
+
103
+ # An object as Pyjo.YAML has it, and a time, which TOML has a type of
104
+ def _data(value: Any) -> Any:
105
+ return value if isinstance(value, datetime.time) else _yaml_data(value)
106
+
107
+
108
+ def _error(message: str) -> Pyjo.Exception.object:
109
+ return Pyjo.Exception.new(f'TOML {message}')
110
+
111
+
112
+ class _Dumper:
113
+ def __init__(self, lines: list[str], sort_keys: bool = False) -> None:
114
+ self.lines = lines
115
+ self.sort_keys = sort_keys
116
+ self.parents: set[int] = set()
117
+
118
+ def enter(self, value: Any) -> None:
119
+ if id(value) in self.parents:
120
+ raise _error('found a collection inside itself')
121
+ self.parents.add(id(value))
122
+
123
+ def header(self, header: str) -> None:
124
+ if self.lines:
125
+ self.lines.append('')
126
+ self.lines.append(header)
127
+
128
+ def inline(self, value: Any) -> str:
129
+ # A value on the line of its key or in an array
130
+ value = _data(value)
131
+ if isinstance(value, dict):
132
+ mapping = cast(dict[Any, Any], value)
133
+ self.enter(mapping)
134
+ pairs = [f'{_key(key)} = {self.inline(item)}' for key, item in self.items(mapping)]
135
+ self.parents.remove(id(mapping))
136
+ return f'{{ {", ".join(pairs)} }}' if pairs else '{}'
137
+ if isinstance(value, (list, tuple)):
138
+ sequence = cast(list[Any] | tuple[Any, ...], value)
139
+ self.enter(sequence)
140
+ items = [self.inline(item) for item in sequence]
141
+ self.parents.remove(id(sequence))
142
+ return f'[{", ".join(items)}]'
143
+ return _scalar(value)
144
+
145
+ def table(self, table: dict[Any, Any], path: list[str], header: str | None) -> None:
146
+ self.enter(table)
147
+ keys: list[tuple[str, Any]] = []
148
+ tables: list[tuple[str, dict[Any, Any]]] = []
149
+ arrays: list[tuple[str, list[dict[Any, Any]]]] = []
150
+ for key, value in self.items(table):
151
+ name = _key(key)
152
+ value = _data(value)
153
+ if isinstance(value, dict):
154
+ tables.append((name, cast(dict[Any, Any], value)))
155
+ elif _is_array_of_tables(value):
156
+ arrays.append((name, [_data(item) for item in cast(list[Any] | tuple[Any, ...], value)]))
157
+ else:
158
+ keys.append((name, value))
159
+
160
+ # The line of the table, which a table with keys only in its tables does without
161
+ if header is not None and (header.startswith('[[') or keys or not tables and not arrays):
162
+ self.header(header)
163
+ for name, value in keys:
164
+ self.lines.append(f'{name} = {self.multiline(value)}')
165
+ for name, value in tables:
166
+ self.table(value, [*path, name], f'[{".".join([*path, name])}]')
167
+ for name, items in arrays:
168
+ for item in items:
169
+ self.table(item, [*path, name], f'[[{".".join([*path, name])}]]')
170
+ self.parents.remove(id(table))
171
+
172
+ def items(self, mapping: dict[Any, Any]) -> list[tuple[Any, Any]]:
173
+ # The keys and the values of a table, in its order or sorted
174
+ items = list(mapping.items())
175
+ if self.sort_keys:
176
+ items.sort(key=lambda item: str(item[0]))
177
+ return items
178
+
179
+ def multiline(self, value: Any) -> str:
180
+ # A string of several lines in a multi-line basic string, whose line break after the quotes is not its own
181
+ if isinstance(value, str) and '\n' in value:
182
+ string = str.__str__(value)
183
+ return '"""\n' + _RE_ESCAPE_MULTILINE.sub(_escape, string) + '"""'
184
+ return self.inline(value)
185
+
186
+
187
+ def _escape(m: re.Match[str]) -> str:
188
+ if (escaped := _ESCAPED.get(c := m[0])) is not None:
189
+ return escaped
190
+ if 0xD800 <= ord(c) <= 0xDFFF:
191
+ raise _error(f'does not support the surrogate \\u{ord(c):04x}')
192
+ return f'\\u{ord(c):04X}'
193
+
194
+
195
+ # A list of tables, which is an array of tables, where an empty list is an array
196
+ def _is_array_of_tables(value: Any) -> bool:
197
+ if not value or not isinstance(value, (list, tuple)):
198
+ return False
199
+ return all(isinstance(_data(item), dict) for item in cast(list[Any] | tuple[Any, ...], value))
200
+
201
+
202
+ def _key(key: Any) -> str:
203
+ if not isinstance(key, str):
204
+ raise _error(f"does not support a key of the type '{type(key).__name__}'")
205
+ string = str.__str__(key)
206
+ return string if _RE_BARE.fullmatch(string) else _string(string)
207
+
208
+
209
+ def _scalar(value: Any) -> str:
210
+ if isinstance(value, str):
211
+ return _string(str.__str__(value))
212
+ if isinstance(value, bool):
213
+ return 'true' if value else 'false'
214
+ if isinstance(value, int):
215
+ return int.__repr__(value)
216
+ if isinstance(value, float):
217
+ if math.isnan(value):
218
+ return 'nan'
219
+ if math.isinf(value):
220
+ return 'inf' if value > 0 else '-inf'
221
+ return float.__repr__(value)
222
+ if isinstance(value, (datetime.datetime, datetime.date)):
223
+ return value.isoformat()
224
+ if isinstance(value, datetime.time):
225
+ if value.tzinfo is not None:
226
+ raise _error('does not support a time with an offset')
227
+ return value.isoformat()
228
+ raise _error(f"does not support a value of the type '{type(value).__name__}'")
229
+
230
+
231
+ def _string(string: str) -> str:
232
+ return '"' + _RE_ESCAPE.sub(_escape, string) + '"'