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.
- Pyjo/App/ASGI.py +735 -0
- Pyjo/App/CGI.py +773 -0
- Pyjo/App/FastCGI.py +631 -0
- Pyjo/App/Lambda.py +987 -0
- Pyjo/App/WSGI.py +427 -0
- Pyjo/App/__init__.py +409 -0
- Pyjo/Asset/File.py +377 -0
- Pyjo/Asset/Memory.py +235 -0
- Pyjo/Asset/__init__.py +200 -0
- Pyjo/Base.py +215 -0
- Pyjo/Cache.py +119 -0
- Pyjo/Collection.py +570 -0
- Pyjo/Content/MultiPart.py +325 -0
- Pyjo/Content/Single.py +193 -0
- Pyjo/Content/__init__.py +806 -0
- Pyjo/Cookie/Request.py +90 -0
- Pyjo/Cookie/Response.py +294 -0
- Pyjo/Cookie/__init__.py +143 -0
- Pyjo/DOM/CSS.py +1019 -0
- Pyjo/DOM/HTML.py +550 -0
- Pyjo/DOM/__init__.py +1329 -0
- Pyjo/Date.py +294 -0
- Pyjo/EventEmitter.py +522 -0
- Pyjo/Exception.py +494 -0
- Pyjo/File.py +959 -0
- Pyjo/GetOpt.py +605 -0
- Pyjo/Headers.py +760 -0
- Pyjo/HelloWorld.py +77 -0
- Pyjo/Home.py +109 -0
- Pyjo/IOLoop/ChildProcess.py +591 -0
- Pyjo/IOLoop/Client.py +736 -0
- Pyjo/IOLoop/Datagram.py +982 -0
- Pyjo/IOLoop/Messages.py +664 -0
- Pyjo/IOLoop/Server.py +458 -0
- Pyjo/IOLoop/Stdio.py +446 -0
- Pyjo/IOLoop/Stream.py +644 -0
- Pyjo/IOLoop/Subprocess.py +419 -0
- Pyjo/IOLoop/TLS.py +360 -0
- Pyjo/IOLoop/__init__.py +1036 -0
- Pyjo/IOLoop/certs/README.md +41 -0
- Pyjo/IOLoop/certs/ca.crt +20 -0
- Pyjo/IOLoop/certs/server.crt +21 -0
- Pyjo/IOLoop/certs/server.key +28 -0
- Pyjo/JSON/Pointer.py +135 -0
- Pyjo/JSON/RPC.py +772 -0
- Pyjo/JSON/Schema.py +2506 -0
- Pyjo/JSON/__init__.py +573 -0
- Pyjo/Loader.py +342 -0
- Pyjo/Log.py +723 -0
- Pyjo/Message/Request.py +725 -0
- Pyjo/Message/Response.py +476 -0
- Pyjo/Message/__init__.py +830 -0
- Pyjo/Parameters.py +472 -0
- Pyjo/Path.py +419 -0
- Pyjo/Promise.py +894 -0
- Pyjo/Prove.py +1305 -0
- Pyjo/Reactor/Asyncio.py +854 -0
- Pyjo/Reactor/Poll.py +439 -0
- Pyjo/Reactor/__init__.py +525 -0
- Pyjo/SSE.py +119 -0
- Pyjo/Server/ASGI.py +486 -0
- Pyjo/Server/CGI.py +259 -0
- Pyjo/Server/Daemon.py +1007 -0
- Pyjo/Server/FastCGI.py +517 -0
- Pyjo/Server/Hot/__init__.py +687 -0
- Pyjo/Server/Hot/__main__.py +5 -0
- Pyjo/Server/Lambda.py +641 -0
- Pyjo/Server/Prefork.py +917 -0
- Pyjo/Server/Prethread.py +394 -0
- Pyjo/Server/Reload/Backend/Poll.py +114 -0
- Pyjo/Server/Reload/Backend/__init__.py +126 -0
- Pyjo/Server/Reload/__init__.py +482 -0
- Pyjo/Server/Reload/__main__.py +5 -0
- Pyjo/Server/Stdio.py +228 -0
- Pyjo/Server/WSGI.py +349 -0
- Pyjo/Server/__init__.py +396 -0
- Pyjo/String/Bytes.py +532 -0
- Pyjo/String/HTML.py +210 -0
- Pyjo/String/Mixin.py +333 -0
- Pyjo/String/Template.py +470 -0
- Pyjo/String/Unicode.py +553 -0
- Pyjo/String/__init__.py +0 -0
- Pyjo/TOML.py +232 -0
- Pyjo/Template.py +1290 -0
- Pyjo/Test/Pyjo.py +1454 -0
- Pyjo/Test/__init__.py +1233 -0
- Pyjo/Test/pytest_plugin.py +305 -0
- Pyjo/Transaction/HTTP.py +374 -0
- Pyjo/Transaction/WebSocket.py +709 -0
- Pyjo/Transaction/__init__.py +429 -0
- Pyjo/URL/Template.py +459 -0
- Pyjo/URL/__init__.py +651 -0
- Pyjo/Upload.py +139 -0
- Pyjo/UserAgent/CookieJar.py +391 -0
- Pyjo/UserAgent/Proxy.py +138 -0
- Pyjo/UserAgent/Server.py +239 -0
- Pyjo/UserAgent/Transactor.py +766 -0
- Pyjo/UserAgent/__init__.py +1243 -0
- Pyjo/Util.py +3567 -0
- Pyjo/WebSocket.py +332 -0
- Pyjo/YAML.py +1167 -0
- Pyjo/__init__.py +441 -0
- Pyjo/__main__.py +35 -0
- Pyjo/py.typed +0 -0
- Pyjoyment/Command/Author/__init__.py +20 -0
- Pyjoyment/Command/Author/generate/__init__.py +106 -0
- Pyjoyment/Command/Author/generate/app.py +299 -0
- Pyjoyment/Command/Author/generate/dockerfile.py +281 -0
- Pyjoyment/Command/Author/generate/kubernetes.py +514 -0
- Pyjoyment/Command/Author/generate/lambda_zip.py +372 -0
- Pyjoyment/Command/Author/generate/lite_app.py +113 -0
- Pyjoyment/Command/Author/generate/plugin.py +267 -0
- Pyjoyment/Command/Author/generate/pyproject.py +123 -0
- Pyjoyment/Command/Author/inflate.py +101 -0
- Pyjoyment/Command/__init__.py +332 -0
- Pyjoyment/Command/asgi.py +92 -0
- Pyjoyment/Command/cgi.py +90 -0
- Pyjoyment/Command/daemon.py +160 -0
- Pyjoyment/Command/eval.py +128 -0
- Pyjoyment/Command/fastcgi.py +99 -0
- Pyjoyment/Command/get.py +305 -0
- Pyjoyment/Command/lambda.py +117 -0
- Pyjoyment/Command/prefork.py +181 -0
- Pyjoyment/Command/prethread.py +126 -0
- Pyjoyment/Command/routes.py +133 -0
- Pyjoyment/Command/stdio.py +92 -0
- Pyjoyment/Command/version.py +141 -0
- Pyjoyment/Command/wsgi.py +93 -0
- Pyjoyment/Commands.py +546 -0
- Pyjoyment/Controller.py +1307 -0
- Pyjoyment/HelloWorld.py +56 -0
- Pyjoyment/Lite.py +494 -0
- Pyjoyment/Plugin/AllowedHosts.py +205 -0
- Pyjoyment/Plugin/Config.py +248 -0
- Pyjoyment/Plugin/DefaultHelpers.py +1202 -0
- Pyjoyment/Plugin/EPLRenderer.py +134 -0
- Pyjoyment/Plugin/EPRenderer.py +212 -0
- Pyjoyment/Plugin/HeaderCondition.py +98 -0
- Pyjoyment/Plugin/Health.py +325 -0
- Pyjoyment/Plugin/JSONConfig.py +172 -0
- Pyjoyment/Plugin/Mount.py +223 -0
- Pyjoyment/Plugin/PyRenderer.py +209 -0
- Pyjoyment/Plugin/TOMLConfig.py +141 -0
- Pyjoyment/Plugin/TagHelpers.py +959 -0
- Pyjoyment/Plugin/YAMLConfig.py +178 -0
- Pyjoyment/Plugin/__init__.py +80 -0
- Pyjoyment/Plugins.py +215 -0
- Pyjoyment/Renderer.py +673 -0
- Pyjoyment/Routes/Match.py +251 -0
- Pyjoyment/Routes/Pattern.py +443 -0
- Pyjoyment/Routes/Route.py +827 -0
- Pyjoyment/Routes/__init__.py +419 -0
- Pyjoyment/Sessions.py +255 -0
- Pyjoyment/Static.py +421 -0
- Pyjoyment/Types.py +238 -0
- Pyjoyment/Validator/Validation.py +395 -0
- Pyjoyment/Validator/__init__.py +304 -0
- Pyjoyment/__init__.py +1083 -0
- Pyjoyment/__main__.py +5 -0
- Pyjoyment/py.typed +0 -0
- Pyjoyment/resources/public/favicon.ico +0 -0
- Pyjoyment/resources/public/favicon.svg +345 -0
- Pyjoyment/resources/public/pyjo/bootstrap/LICENSE +21 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.bundle.min.js.map +1 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.css +6 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.js +7 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.min.css.map +1 -0
- Pyjoyment/resources/public/pyjo/failunicat.png +0 -0
- Pyjoyment/resources/public/pyjo/failunicat.svg +50 -0
- Pyjoyment/resources/public/pyjo/highlight.js/LICENSE +29 -0
- Pyjoyment/resources/public/pyjo/highlight.js/highlight-mojo-dark.css +56 -0
- Pyjoyment/resources/public/pyjo/highlight.js/highlight.min.js +1262 -0
- Pyjoyment/resources/public/pyjo/highlight.js/pyjoyment.min.js +1 -0
- Pyjoyment/resources/public/pyjo/logo-black-4x.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-black-4x.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo-white-2x.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-white-2x.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo-white-4x.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-white-4x.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo-white.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-white.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo.png +0 -0
- Pyjoyment/resources/public/pyjo/logo.svg +346 -0
- Pyjoyment/resources/public/pyjo/notfound.png +0 -0
- Pyjoyment/resources/public/pyjo/notfound.svg +3 -0
- Pyjoyment/resources/public/pyjo/nounicat.png +0 -0
- Pyjoyment/resources/public/pyjo/nounicat.svg +5 -0
- Pyjoyment/resources/public/pyjo/pinstripe-dark.png +0 -0
- Pyjoyment/resources/public/pyjo/pinstripe-dark.svg +4 -0
- Pyjoyment/resources/public/pyjo/pinstripe-light.png +0 -0
- Pyjoyment/resources/public/pyjo/pinstripe-light.svg +4 -0
- Pyjoyment/resources/public/pyjo/pyjo.css +204 -0
- Pyjoyment/resources/templates/pyjo/debug.html.ep +346 -0
- Pyjoyment/resources/templates/pyjo/exception.html.ep +32 -0
- Pyjoyment/resources/templates/pyjo/not_found.html.ep +45 -0
- pyjoyment-0.1.0.dist-info/METADATA +376 -0
- pyjoyment-0.1.0.dist-info/RECORD +200 -0
- pyjoyment-0.1.0.dist-info/WHEEL +4 -0
- pyjoyment-0.1.0.dist-info/entry_points.txt +8 -0
- 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
|
Pyjo/Cookie/Response.py
ADDED
|
@@ -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
|
Pyjo/Cookie/__init__.py
ADDED
|
@@ -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
|