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/Cookie/Request.py ADDED
@@ -0,0 +1,90 @@
1
+ """
2
+ Pyjo.Cookie.Request - HTTP request cookie
3
+ =========================================
4
+ ::
5
+
6
+ from Pyjo.Cookie.Request import Request
7
+
8
+ cookie = Request()
9
+ cookie.name = 'foo'
10
+ cookie.value = 'bar'
11
+ print(cookie)
12
+
13
+ # Or import the module, as in Google Python Style Guide
14
+
15
+ import Pyjo.Cookie.Request
16
+
17
+ cookie = Pyjo.Cookie.Request.new()
18
+ cookie.name = 'foo'
19
+ cookie.value = 'bar'
20
+ print(cookie)
21
+
22
+ :mod:`Pyjo.Cookie.Request` is a container for HTTP request cookies, based on
23
+ :rfc:`6265`.
24
+
25
+ See also
26
+ --------
27
+
28
+ :mod:`Pyjoyment`, https://mojolicious.org.
29
+
30
+ Ported from `Mojo::Cookie::Request <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/Cookie/Request.pm>`_
31
+ of Mojolicious 9.49.
32
+
33
+ Classes
34
+ -------
35
+ """
36
+
37
+ import Pyjo.Cookie
38
+
39
+ from Pyjo.Util import quote, split_header
40
+
41
+ import re
42
+
43
+ from http.cookies import BaseCookie
44
+
45
+
46
+ class Pyjo_Cookie_Request(Pyjo.Cookie.object):
47
+ """
48
+ :mod:`Pyjo.Cookie.Request` inherits all attributes and methods from
49
+ :mod:`Pyjo.Cookie` and implements the following new ones.
50
+ """
51
+
52
+ @classmethod
53
+ def parse[C: Pyjo_Cookie_Request](cls: type[C], string: str | BaseCookie[str] | None = None) -> list[C]:
54
+ """::
55
+
56
+ cookies = Pyjo.Cookie.Request.parse('f=b; g=a')
57
+ cookies = Pyjo.Cookie.Request.parse(SimpleCookie('f=b; g=a'))
58
+
59
+ Parse cookies, also from a :class:`http.cookies.SimpleCookie` of the standard
60
+ library.
61
+ """
62
+ # Not in Mojolicious: http.cookies of the standard library
63
+ if isinstance(string, BaseCookie):
64
+ return [cls.new(morsel) for morsel in string.values()]
65
+
66
+ cookies: list[C] = []
67
+ tree = split_header(string or '')
68
+ for name, value in (pair for pairs in tree for pair in pairs):
69
+ if name.startswith('$'):
70
+ continue
71
+ cookies.append(cls.new(name=name, value=value or ''))
72
+ return cookies
73
+
74
+ def to_str(self) -> str:
75
+ """::
76
+
77
+ string = cookie.to_str()
78
+
79
+ Render cookie.
80
+ """
81
+ if not (name := self.name or ''):
82
+ return ''
83
+ value = self.value or ''
84
+ return '='.join([name, quote(value) if re.search(r'[,;" ]', value) else value])
85
+
86
+
87
+ parse = Pyjo_Cookie_Request.parse
88
+ new = Pyjo_Cookie_Request.new
89
+ object = Pyjo_Cookie_Request
90
+ Request = Pyjo_Cookie_Request
@@ -0,0 +1,294 @@
1
+ """
2
+ Pyjo.Cookie.Response - HTTP response cookie
3
+ ===========================================
4
+ ::
5
+
6
+ from Pyjo.Cookie.Response import Response
7
+
8
+ cookie = Response()
9
+ cookie.name = 'foo'
10
+ cookie.value = 'bar'
11
+ print(cookie)
12
+
13
+ # Or import the module, as in Google Python Style Guide
14
+
15
+ import Pyjo.Cookie.Response
16
+
17
+ cookie = Pyjo.Cookie.Response.new()
18
+ cookie.name = 'foo'
19
+ cookie.value = 'bar'
20
+ print(cookie)
21
+
22
+ :mod:`Pyjo.Cookie.Response` is a container for HTTP response cookies, based on
23
+ :rfc:`6265`.
24
+
25
+ See also
26
+ --------
27
+
28
+ :mod:`Pyjoyment`, https://mojolicious.org.
29
+
30
+ Ported from `Mojo::Cookie::Response <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/Cookie/Response.pm>`_
31
+ of Mojolicious 9.49.
32
+
33
+ Classes
34
+ -------
35
+ """
36
+
37
+ import Pyjo.Cookie
38
+ import Pyjo.Date
39
+
40
+ from Pyjo.Util import quote, split_cookie_header
41
+
42
+ import contextlib
43
+ import re
44
+
45
+ from http.cookies import BaseCookie, CookieError, Morsel
46
+
47
+
48
+ ATTRS = {'domain', 'expires', 'httponly', 'max-age', 'partitioned', 'path', 'samesite', 'secure'}
49
+
50
+
51
+ class Pyjo_Cookie_Response(Pyjo.Cookie.object):
52
+ """
53
+ :mod:`Pyjo.Cookie.Response` inherits all attributes and methods from
54
+ :mod:`Pyjo.Cookie` and implements the following new ones.
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ morsel: Morsel[str] | None = None,
60
+ *,
61
+ name: str | None = None,
62
+ value: str | None = None,
63
+ domain: str | None = None,
64
+ expires: int | float | None = None,
65
+ host_only: bool | None = None,
66
+ httponly: bool | None = None,
67
+ max_age: int | None = None,
68
+ partitioned: bool | None = None,
69
+ path: str | None = None,
70
+ samesite: str | None = None,
71
+ secure: bool | None = None,
72
+ ) -> None:
73
+ super().__init__(morsel, name=name, value=value)
74
+
75
+ self.domain = domain
76
+ """::
77
+
78
+ domain = cookie.domain
79
+ cookie.domain = 'localhost'
80
+
81
+ Cookie domain.
82
+ """
83
+
84
+ self.expires = expires
85
+ """::
86
+
87
+ expires = cookie.expires
88
+ cookie.expires = time.time() + 60
89
+
90
+ Expiration for cookie.
91
+ """
92
+
93
+ self.host_only = host_only
94
+ """::
95
+
96
+ boolean = cookie.host_only
97
+ cookie.host_only = boolean
98
+
99
+ Host-only flag, indicating that the canonicalized request-host is identical
100
+ to the cookie's :attr:`domain`.
101
+ """
102
+
103
+ self.httponly = httponly
104
+ """::
105
+
106
+ boolean = cookie.httponly
107
+ cookie.httponly = boolean
108
+
109
+ HttpOnly flag, which can prevent client-side scripts from accessing this
110
+ cookie.
111
+ """
112
+
113
+ self.max_age = max_age
114
+ """::
115
+
116
+ max_age = cookie.max_age
117
+ cookie.max_age = 60
118
+
119
+ Max age for cookie.
120
+ """
121
+
122
+ self.partitioned = partitioned
123
+ """::
124
+
125
+ partitioned = cookie.partitioned
126
+ cookie.partitioned = True
127
+
128
+ Partitioned flag, this is to be used in accordance to the CHIPS amendment to
129
+ :rfc:`6265`. Note that this attribute is **EXPERIMENTAL** because even though
130
+ most commonly used browsers support the feature, there is no specification
131
+ yet besides `this draft
132
+ <https://www.ietf.org/archive/id/draft-cutler-httpbis-partitioned-cookies-00.html>`_.
133
+ """
134
+
135
+ self.path = path
136
+ """::
137
+
138
+ path = cookie.path
139
+ cookie.path = '/test'
140
+
141
+ Cookie path.
142
+ """
143
+
144
+ self.samesite = samesite
145
+ """::
146
+
147
+ samesite = cookie.samesite
148
+ cookie.samesite = 'Lax'
149
+
150
+ SameSite value.
151
+ """
152
+
153
+ self.secure = secure
154
+ """::
155
+
156
+ boolean = cookie.secure
157
+ cookie.secure = boolean
158
+
159
+ Secure flag, which instructs browsers to only send this cookie over HTTPS
160
+ connections.
161
+ """
162
+
163
+ # Not in Mojolicious: the attributes of a Morsel of http.cookies, as a
164
+ # "Set-Cookie" header gives them
165
+ if morsel is not None:
166
+ vars(self).update(vars(self.parse(morsel.OutputString())[0]))
167
+
168
+ @classmethod
169
+ def parse[C: Pyjo_Cookie_Response](cls: type[C], string: str | BaseCookie[str] | None = None) -> list[C]:
170
+ """::
171
+
172
+ cookies = Pyjo.Cookie.Response.parse('f=b; path=/')
173
+ cookies = Pyjo.Cookie.Response.parse(SimpleCookie('f=b; path=/'))
174
+
175
+ Parse cookies, also from a :class:`http.cookies.SimpleCookie` of the standard
176
+ library.
177
+ """
178
+ # Not in Mojolicious: http.cookies of the standard library
179
+ if isinstance(string, BaseCookie):
180
+ return [cls.new(morsel) for morsel in string.values()]
181
+
182
+ cookies: list[C] = []
183
+ tree = split_cookie_header(string or '')
184
+ for pairs in tree:
185
+ (name, value), *attrs = pairs
186
+ cookie = cls.new(name=name, value=value or '')
187
+ cookies.append(cookie)
188
+
189
+ for name, value in attrs:
190
+ if (attr := name.lower()) not in ATTRS:
191
+ continue
192
+ if attr == 'domain':
193
+ cookie.domain = value.removeprefix('.') if value is not None else None
194
+ elif attr == 'expires':
195
+ cookie.expires = Pyjo.Date.new(value or '').epoch
196
+ elif attr == 'max-age':
197
+ # Unlike Mojolicious: a value that is not an integer is ignored, as RFC 6265
198
+ # says; Mojo keeps it
199
+ if value is not None and re.fullmatch(r'-?\d+', value):
200
+ cookie.max_age = int(value)
201
+ elif attr == 'path':
202
+ cookie.path = value
203
+ elif attr == 'samesite':
204
+ cookie.samesite = value
205
+ else:
206
+ setattr(cookie, attr, True)
207
+
208
+ return cookies
209
+
210
+ # Not in Mojolicious: http.cookies of the standard library
211
+ def to_morsel(self) -> Morsel[str]:
212
+ """::
213
+
214
+ morsel = cookie.to_morsel()
215
+
216
+ Turn cookie into a :class:`http.cookies.Morsel`, for the standard library.
217
+ The partitioned flag is left out before Python 3.14, as ``Morsel`` has no
218
+ such attribute there. ::
219
+
220
+ # "foo=bar; Path=/; Secure"
221
+ Pyjo.Cookie.Response.new(name='foo', value='bar', path='/', secure=True).to_morsel().OutputString()
222
+ """
223
+ morsel = super().to_morsel()
224
+ if (expires := self.expires) is not None:
225
+ morsel['expires'] = Pyjo.Date.new(expires).to_str()
226
+ if domain := self.domain:
227
+ morsel['domain'] = domain
228
+ if path := self.path:
229
+ morsel['path'] = path
230
+ if self.partitioned:
231
+ with contextlib.suppress(CookieError):
232
+ morsel['partitioned'] = True
233
+ if self.secure:
234
+ morsel['secure'] = True
235
+ if self.httponly:
236
+ morsel['httponly'] = True
237
+ if samesite := self.samesite:
238
+ morsel['samesite'] = samesite
239
+ if (max_age := self.max_age) is not None:
240
+ morsel['max-age'] = str(max_age)
241
+ return morsel
242
+
243
+ def to_str(self) -> str:
244
+ """::
245
+
246
+ string = cookie.to_str()
247
+
248
+ Render cookie.
249
+ """
250
+ # Name and value
251
+ if not (name := self.name or ''):
252
+ return ''
253
+ value = self.value or ''
254
+ cookie = '='.join([name, quote(value) if re.search(r'[,;" ]', value) else value])
255
+
256
+ # "expires"
257
+ if (expires := self.expires) is not None:
258
+ cookie += f'; expires={Pyjo.Date.new(expires)}'
259
+
260
+ # "domain"
261
+ if domain := self.domain:
262
+ cookie += f'; domain={domain}'
263
+
264
+ # "path"
265
+ if path := self.path:
266
+ cookie += f'; path={path}'
267
+
268
+ # "Partitioned"
269
+ if self.partitioned:
270
+ cookie += '; Partitioned'
271
+
272
+ # "secure"
273
+ if self.secure:
274
+ cookie += '; secure'
275
+
276
+ # "HttpOnly"
277
+ if self.httponly:
278
+ cookie += '; HttpOnly'
279
+
280
+ # "Same-Site"
281
+ if samesite := self.samesite:
282
+ cookie += f'; SameSite={samesite}'
283
+
284
+ # "Max-Age"
285
+ if (max_age := self.max_age) is not None:
286
+ cookie += f'; Max-Age={max_age}'
287
+
288
+ return cookie
289
+
290
+
291
+ parse = Pyjo_Cookie_Response.parse
292
+ new = Pyjo_Cookie_Response.new
293
+ object = Pyjo_Cookie_Response
294
+ Response = Pyjo_Cookie_Response
@@ -0,0 +1,143 @@
1
+ """
2
+ Pyjo.Cookie - HTTP cookie base class
3
+ ====================================
4
+ ::
5
+
6
+ from Pyjo.Cookie import Cookie
7
+
8
+ class MyCookie(Cookie):
9
+ @classmethod
10
+ def parse(cls, string=None): ...
11
+
12
+ def to_str(self): ...
13
+
14
+ # Or import the module, as in Google Python Style Guide
15
+
16
+ import Pyjo.Cookie
17
+
18
+ class MyCookie(Pyjo.Cookie.object):
19
+ @classmethod
20
+ def parse(cls, string=None): ...
21
+
22
+ def to_str(self): ...
23
+
24
+ :mod:`Pyjo.Cookie` is an abstract base class for HTTP cookie containers, based on
25
+ :rfc:`6265`, like :mod:`Pyjo.Cookie.Request` and :mod:`Pyjo.Cookie.Response`.
26
+
27
+ See also
28
+ --------
29
+
30
+ :mod:`Pyjoyment`, https://mojolicious.org.
31
+
32
+ Ported from `Mojo::Cookie <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/Cookie.pm>`_
33
+ of Mojolicious 9.49.
34
+
35
+ Classes
36
+ -------
37
+ """
38
+
39
+ import Pyjo.Base
40
+ import Pyjo.String.Mixin
41
+
42
+ from Pyjo.Util import quote
43
+
44
+ import re
45
+
46
+ from http.cookies import BaseCookie, Morsel
47
+
48
+
49
+ class Pyjo_Cookie(Pyjo.Base.object, Pyjo.String.Mixin.object):
50
+ """
51
+ :mod:`Pyjo.Cookie` inherits all methods from :mod:`Pyjo.Base` and
52
+ :mod:`Pyjo.String.Mixin` and implements the following new ones.
53
+ """
54
+
55
+ def __init__(self, morsel: Morsel[str] | None = None, *, name: str | None = None, value: str | None = None) -> None:
56
+ """::
57
+
58
+ cookie = Pyjo.Cookie.Request.new()
59
+ cookie = Pyjo.Cookie.Request.new(name='foo', value='bar')
60
+ cookie = Pyjo.Cookie.Request.new(SimpleCookie('foo=bar')['foo'])
61
+
62
+ Construct a new cookie, from a :class:`http.cookies.Morsel` of the standard
63
+ library if one is given.
64
+ """
65
+ self.name = name
66
+ """::
67
+
68
+ name = cookie.name
69
+ cookie.name = 'foo'
70
+
71
+ Cookie name.
72
+ """
73
+
74
+ self.value = value
75
+ """::
76
+
77
+ value = cookie.value
78
+ cookie.value = '/test'
79
+
80
+ Cookie value.
81
+ """
82
+
83
+ # Not in Mojolicious: a Morsel of http.cookies of the standard library
84
+ if morsel is not None:
85
+ self.name, self.value = morsel.key, morsel.value
86
+
87
+ def __bool__(self) -> bool:
88
+ """::
89
+
90
+ boolean = bool(cookie)
91
+
92
+ Always true.
93
+ """
94
+ return True
95
+
96
+ def __str__(self) -> str:
97
+ """::
98
+
99
+ string = str(cookie)
100
+
101
+ Alias for :meth:`to_str`.
102
+ """
103
+ return self.to_str()
104
+
105
+ @classmethod
106
+ def parse[C: Pyjo_Cookie](cls: type[C], string: str | BaseCookie[str] | None = None) -> list[C]:
107
+ """::
108
+
109
+ cookies = cookie.parse(string)
110
+
111
+ Parse cookies. Meant to be overloaded in a subclass.
112
+ """
113
+ raise NotImplementedError('Method "parse" not implemented by subclass')
114
+
115
+ def to_str(self) -> str:
116
+ """::
117
+
118
+ string = cookie.to_str()
119
+
120
+ Render cookie. Meant to be overloaded in a subclass.
121
+ """
122
+ raise NotImplementedError('Method "to_str" not implemented by subclass')
123
+
124
+ # Not in Mojolicious: http.cookies of the standard library
125
+ def to_morsel(self) -> Morsel[str]:
126
+ """::
127
+
128
+ morsel = cookie.to_morsel()
129
+
130
+ Turn cookie into a :class:`http.cookies.Morsel`, for the standard library. ::
131
+
132
+ # "foo=\"b r\""
133
+ Pyjo.Cookie.Request.new(name='foo', value='b r').to_morsel().OutputString()
134
+ """
135
+ morsel: Morsel[str] = Morsel()
136
+ value = self.value or ''
137
+ morsel.set(self.name or '', value, quote(value) if re.search(r'[,;" ]', value) else value)
138
+ return morsel
139
+
140
+
141
+ new = Pyjo_Cookie.new
142
+ object = Pyjo_Cookie
143
+ Cookie = Pyjo_Cookie