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/Asset/__init__.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjo.Asset - HTTP content storage base class
|
|
3
|
+
============================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjo.Asset import Asset
|
|
7
|
+
|
|
8
|
+
class MyAsset(Asset):
|
|
9
|
+
def add_chunk(self, chunk): ...
|
|
10
|
+
def contains(self, string): ...
|
|
11
|
+
def get_chunk(self, offset, max=None): ...
|
|
12
|
+
def move_to(self, to): ...
|
|
13
|
+
@property
|
|
14
|
+
def mtime(self): ...
|
|
15
|
+
@property
|
|
16
|
+
def size(self): ...
|
|
17
|
+
def slurp(self): ...
|
|
18
|
+
def to_file(self): ...
|
|
19
|
+
|
|
20
|
+
# Or import the module, as in Google Python Style Guide
|
|
21
|
+
|
|
22
|
+
import Pyjo.Asset
|
|
23
|
+
|
|
24
|
+
class MyAsset(Pyjo.Asset.object):
|
|
25
|
+
def add_chunk(self, chunk): ...
|
|
26
|
+
def contains(self, string): ...
|
|
27
|
+
def get_chunk(self, offset, max=None): ...
|
|
28
|
+
def move_to(self, to): ...
|
|
29
|
+
@property
|
|
30
|
+
def mtime(self): ...
|
|
31
|
+
@property
|
|
32
|
+
def size(self): ...
|
|
33
|
+
def slurp(self): ...
|
|
34
|
+
def to_file(self): ...
|
|
35
|
+
|
|
36
|
+
:mod:`Pyjo.Asset` is an abstract base class for HTTP content storage
|
|
37
|
+
backends, like :mod:`Pyjo.Asset.File` and :mod:`Pyjo.Asset.Memory`.
|
|
38
|
+
|
|
39
|
+
Events
|
|
40
|
+
------
|
|
41
|
+
|
|
42
|
+
:mod:`Pyjo.Asset` inherits all events from :mod:`Pyjo.EventEmitter`.
|
|
43
|
+
|
|
44
|
+
See also
|
|
45
|
+
--------
|
|
46
|
+
|
|
47
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
48
|
+
|
|
49
|
+
Ported from `Mojo::Asset <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/Asset.pm>`_
|
|
50
|
+
of Mojolicious 9.49.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
import Pyjo.EventEmitter
|
|
54
|
+
|
|
55
|
+
import builtins
|
|
56
|
+
|
|
57
|
+
from typing import Self
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
type Anything = builtins.object
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Pyjo_Asset(Pyjo.EventEmitter.object):
|
|
64
|
+
"""
|
|
65
|
+
:mod:`Pyjo.Asset` inherits all methods from :mod:`Pyjo.EventEmitter` and
|
|
66
|
+
implements the following new ones.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, *, end_range: int | None = None, start_range: int = 0) -> None:
|
|
70
|
+
"""::
|
|
71
|
+
|
|
72
|
+
asset = Pyjo.Asset.new()
|
|
73
|
+
asset = Pyjo.Asset.new(start_range=3, end_range=8)
|
|
74
|
+
|
|
75
|
+
Construct a new :mod:`Pyjo.Asset` object.
|
|
76
|
+
"""
|
|
77
|
+
super().__init__()
|
|
78
|
+
|
|
79
|
+
self.end_range = end_range
|
|
80
|
+
"""::
|
|
81
|
+
|
|
82
|
+
end = asset.end_range
|
|
83
|
+
asset.end_range = 8
|
|
84
|
+
|
|
85
|
+
Pretend file ends earlier.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
self.start_range = start_range
|
|
89
|
+
"""::
|
|
90
|
+
|
|
91
|
+
start = asset.start_range
|
|
92
|
+
asset.start_range = 3
|
|
93
|
+
|
|
94
|
+
Pretend file starts later.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def add_chunk(self, chunk: bytes) -> 'Pyjo_Asset':
|
|
98
|
+
"""::
|
|
99
|
+
|
|
100
|
+
asset = asset.add_chunk(b'foo bar baz')
|
|
101
|
+
|
|
102
|
+
Add chunk of data to asset. Meant to be overloaded in a subclass.
|
|
103
|
+
"""
|
|
104
|
+
raise NotImplementedError('Method "add_chunk" not implemented by subclass')
|
|
105
|
+
|
|
106
|
+
def contains(self, string: bytes) -> int:
|
|
107
|
+
"""::
|
|
108
|
+
|
|
109
|
+
position = asset.contains(b'bar')
|
|
110
|
+
|
|
111
|
+
Check if asset contains a specific string. Meant to be overloaded in a
|
|
112
|
+
subclass.
|
|
113
|
+
"""
|
|
114
|
+
raise NotImplementedError('Method "contains" not implemented by subclass')
|
|
115
|
+
|
|
116
|
+
def get_chunk(self, offset: int, max: int | None = None) -> bytes:
|
|
117
|
+
"""::
|
|
118
|
+
|
|
119
|
+
chunk = asset.get_chunk(offset)
|
|
120
|
+
chunk = asset.get_chunk(offset, max)
|
|
121
|
+
|
|
122
|
+
Get chunk of data starting from a specific position, defaults to a
|
|
123
|
+
maximum chunk size of ``131072`` bytes (128KiB). Meant to be overloaded
|
|
124
|
+
in a subclass.
|
|
125
|
+
"""
|
|
126
|
+
raise NotImplementedError('Method "get_chunk" not implemented by subclass')
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def is_file(self) -> bool:
|
|
130
|
+
"""::
|
|
131
|
+
|
|
132
|
+
boolean = asset.is_file
|
|
133
|
+
|
|
134
|
+
False, this is not a :mod:`Pyjo.Asset.File` object.
|
|
135
|
+
"""
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def is_range(self) -> bool:
|
|
140
|
+
"""::
|
|
141
|
+
|
|
142
|
+
boolean = asset.is_range
|
|
143
|
+
|
|
144
|
+
Check if asset has a :attr:`start_range` or :attr:`end_range`.
|
|
145
|
+
"""
|
|
146
|
+
return bool(self.end_range or self.start_range)
|
|
147
|
+
|
|
148
|
+
def move_to(self, to: Anything) -> Self:
|
|
149
|
+
"""::
|
|
150
|
+
|
|
151
|
+
asset = asset.move_to('/home/sri/foo.txt')
|
|
152
|
+
|
|
153
|
+
Move asset data into a specific file. Meant to be overloaded in a
|
|
154
|
+
subclass.
|
|
155
|
+
"""
|
|
156
|
+
raise NotImplementedError('Method "move_to" not implemented by subclass')
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def mtime(self) -> float:
|
|
160
|
+
"""::
|
|
161
|
+
|
|
162
|
+
mtime = asset.mtime
|
|
163
|
+
|
|
164
|
+
Modification time of asset. Meant to be overloaded in a subclass.
|
|
165
|
+
"""
|
|
166
|
+
raise NotImplementedError('Method "mtime" not implemented by subclass')
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def size(self) -> int:
|
|
170
|
+
"""::
|
|
171
|
+
|
|
172
|
+
size = asset.size
|
|
173
|
+
|
|
174
|
+
Size of asset data in bytes. Meant to be overloaded in a subclass.
|
|
175
|
+
"""
|
|
176
|
+
raise NotImplementedError('Method "size" not implemented by subclass')
|
|
177
|
+
|
|
178
|
+
def slurp(self) -> bytes:
|
|
179
|
+
"""::
|
|
180
|
+
|
|
181
|
+
content = asset.slurp()
|
|
182
|
+
|
|
183
|
+
Read all asset data at once. Meant to be overloaded in a subclass.
|
|
184
|
+
"""
|
|
185
|
+
raise NotImplementedError('Method "slurp" not implemented by subclass')
|
|
186
|
+
|
|
187
|
+
def to_file(self) -> 'Pyjo_Asset':
|
|
188
|
+
"""::
|
|
189
|
+
|
|
190
|
+
file = asset.to_file()
|
|
191
|
+
|
|
192
|
+
Convert asset to :mod:`Pyjo.Asset.File` object. Meant to be overloaded
|
|
193
|
+
in a subclass.
|
|
194
|
+
"""
|
|
195
|
+
raise NotImplementedError('Method "to_file" not implemented by subclass')
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
new = Pyjo_Asset.new
|
|
199
|
+
object = Pyjo_Asset
|
|
200
|
+
Asset = Pyjo_Asset
|
Pyjo/Base.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Pyjo.Base - Minimal base class for Pyjo projects
|
|
3
|
+
================================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjo.Base import Base
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Cat(Base):
|
|
12
|
+
name: str = 'Nyan'
|
|
13
|
+
age: int = 4
|
|
14
|
+
|
|
15
|
+
mew = Cat(name='Longcat')
|
|
16
|
+
print(mew.age)
|
|
17
|
+
|
|
18
|
+
# Or import the module, as in Google Python Style Guide
|
|
19
|
+
|
|
20
|
+
import Pyjo.Base
|
|
21
|
+
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class Cat(Pyjo.Base.object):
|
|
26
|
+
name: str = 'Nyan'
|
|
27
|
+
age: int = 4
|
|
28
|
+
weight: int = 4
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class Tiger(Cat):
|
|
32
|
+
friend: Cat = field(default_factory=Cat)
|
|
33
|
+
stripes: int = 42
|
|
34
|
+
|
|
35
|
+
mew = Cat.new(name='Longcat')
|
|
36
|
+
print(mew.age)
|
|
37
|
+
print(mew.set(age=3, weight=5).age)
|
|
38
|
+
|
|
39
|
+
rawr = Tiger.new(stripes=38, weight=250)
|
|
40
|
+
print(rawr.tap(lambda rawr: rawr.friend.set(name='Tacgnol')).weight)
|
|
41
|
+
|
|
42
|
+
:mod:`Pyjo.Base` is a simple base class for :mod:`Pyjo` projects with fluent
|
|
43
|
+
interfaces.
|
|
44
|
+
|
|
45
|
+
The attributes of an object are the attributes of Python. A dataclass, as
|
|
46
|
+
above, or ``__init__`` gives them their default values, and a property builds
|
|
47
|
+
a default value the first time it is read, as a callback of ``has`` does in
|
|
48
|
+
Mojolicious. ::
|
|
49
|
+
|
|
50
|
+
class Lion(Cat):
|
|
51
|
+
@property
|
|
52
|
+
def pride(self):
|
|
53
|
+
if not hasattr(self, '_pride'):
|
|
54
|
+
self._pride = [Cat.new(), Cat.new()]
|
|
55
|
+
return self._pride
|
|
56
|
+
|
|
57
|
+
Constructors
|
|
58
|
+
------------
|
|
59
|
+
|
|
60
|
+
The constructor of a class and :meth:`~Pyjo_Base.new` make the same object, and
|
|
61
|
+
both are correct. :meth:`~Pyjo_Base.new` is the constructor of Mojolicious,
|
|
62
|
+
which the documentation uses, after ``import`` of the module. Each module has
|
|
63
|
+
its class under the name of the module too, as ``Path`` of :mod:`Pyjo.Path`,
|
|
64
|
+
for the constructor of Python, after ``from ... import`` of the class. ::
|
|
65
|
+
|
|
66
|
+
import Pyjo.Path
|
|
67
|
+
|
|
68
|
+
path = Pyjo.Path.new('/foo/bar')
|
|
69
|
+
|
|
70
|
+
# Or import the class
|
|
71
|
+
|
|
72
|
+
from Pyjo.Path import Path
|
|
73
|
+
|
|
74
|
+
path = Path('/foo/bar')
|
|
75
|
+
|
|
76
|
+
The synopsis of each module shows its first example in both forms.
|
|
77
|
+
|
|
78
|
+
Fluent interfaces
|
|
79
|
+
-----------------
|
|
80
|
+
|
|
81
|
+
Fluent interfaces are a way to design object-oriented APIs around method
|
|
82
|
+
chaining to create domain-specific languages, with the goal of making the
|
|
83
|
+
readability of the source code close to written prose. ::
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class Duck(Pyjo.Base.object):
|
|
87
|
+
name: str | None = None
|
|
88
|
+
|
|
89
|
+
def quack(self):
|
|
90
|
+
print(f'{self.name}: Quack!')
|
|
91
|
+
|
|
92
|
+
:mod:`Pyjo.Base` will help you with this by having :meth:`~Pyjo_Base.set`
|
|
93
|
+
return its invocant (``self``) when it is used to assign new attribute values. ::
|
|
94
|
+
|
|
95
|
+
Duck.new().set(name='Donald').quack()
|
|
96
|
+
|
|
97
|
+
In this case :meth:`~Pyjo_Base.set` is called on the object created by
|
|
98
|
+
``Duck.new()``. It assigns a new attribute value and then returns the ``Duck``
|
|
99
|
+
object, so the ``quack`` method can be called on it afterwards. These method
|
|
100
|
+
chains can continue until one of the methods called does not return the
|
|
101
|
+
``Duck`` object.
|
|
102
|
+
|
|
103
|
+
See also
|
|
104
|
+
--------
|
|
105
|
+
|
|
106
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
107
|
+
|
|
108
|
+
Ported from `Mojo::Base <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/Base.pm>`_
|
|
109
|
+
of Mojolicious 9.49.
|
|
110
|
+
|
|
111
|
+
Classes
|
|
112
|
+
-------
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
from collections.abc import Callable
|
|
116
|
+
from typing import Any, Self
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# Left out from Mojolicious: the flags of import, -strict, -base, -role, -signatures and -async_await, as Python has
|
|
120
|
+
# them on its own, and has, as the attributes of Python take its place
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class Pyjo_Base:
|
|
124
|
+
"""
|
|
125
|
+
:mod:`Pyjo.Base` implements the following methods.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
# Not in Mojolicious: no __dict__ of its own, so that a subclass with __slots__ has none either
|
|
129
|
+
__slots__ = ()
|
|
130
|
+
|
|
131
|
+
# Left out from Mojolicious: attr, as the attributes of Python, the properties and weakref.proxy take its place
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def new(cls, *args: Any, **kwargs: Any) -> Self:
|
|
135
|
+
"""::
|
|
136
|
+
|
|
137
|
+
obj = SubClass.new()
|
|
138
|
+
obj = SubClass.new(name='value')
|
|
139
|
+
obj = SubClass.new(**{'name': 'value'})
|
|
140
|
+
|
|
141
|
+
This base class provides a basic constructor, which passes its
|
|
142
|
+
arguments to the constructor of the class: the attribute values are
|
|
143
|
+
keyword arguments.
|
|
144
|
+
"""
|
|
145
|
+
return cls(*args, **kwargs)
|
|
146
|
+
|
|
147
|
+
# Not in Mojolicious: the expression form of an assignment, as the accessors of Mojo chain when they set a value
|
|
148
|
+
def set(self, **kwargs: Any) -> Self:
|
|
149
|
+
"""::
|
|
150
|
+
|
|
151
|
+
obj = obj.set(name='value')
|
|
152
|
+
obj = obj.set(name='value', other='value')
|
|
153
|
+
|
|
154
|
+
Assign attribute values and return the object, so that a chain of
|
|
155
|
+
method calls can continue. ::
|
|
156
|
+
|
|
157
|
+
# Longer version
|
|
158
|
+
obj.name = 'value'
|
|
159
|
+
"""
|
|
160
|
+
for k, v in sorted(kwargs.items()):
|
|
161
|
+
setattr(self, k, v)
|
|
162
|
+
return self
|
|
163
|
+
|
|
164
|
+
def tap(self, cb: Callable[..., Any] | str, *args: Any, **kwargs: Any) -> Self:
|
|
165
|
+
"""::
|
|
166
|
+
|
|
167
|
+
obj = obj.tap(lambda obj: ...)
|
|
168
|
+
obj = obj.tap('some_method')
|
|
169
|
+
obj = obj.tap('some_method', *args, **kwargs)
|
|
170
|
+
|
|
171
|
+
Tap into a method chain to perform operations on an object within the
|
|
172
|
+
chain (also known as a K combinator or Kestrel). The object will be
|
|
173
|
+
the first argument passed to the callback. The callback's return value
|
|
174
|
+
will be ignored; instead, the object (the callback's first argument)
|
|
175
|
+
will be the return value. In this way, arbitrary code can be used
|
|
176
|
+
within (i.e., spliced or tapped into) a chained set of object method
|
|
177
|
+
calls. A name of an attribute that is not a method is read, which
|
|
178
|
+
builds the default value of a property. ::
|
|
179
|
+
|
|
180
|
+
# Longer version
|
|
181
|
+
obj = obj.tap(lambda obj: obj.some_method(*args))
|
|
182
|
+
|
|
183
|
+
# Inject side effects into a method chain
|
|
184
|
+
obj.set(foo='A').tap(lambda obj: print(obj.foo)).set(foo='B')
|
|
185
|
+
"""
|
|
186
|
+
tap(self, cb, *args, **kwargs)
|
|
187
|
+
return self
|
|
188
|
+
|
|
189
|
+
# Left out from Mojolicious: with_roles, as Pyjoyment has no roles
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# Not in Mojolicious: a function of its own, for the classes that are not Pyjo.Base, as a list and a string
|
|
193
|
+
def tap(obj: Any, cb: Callable[..., Any] | str, *args: Any, **kwargs: Any) -> None:
|
|
194
|
+
"""::
|
|
195
|
+
|
|
196
|
+
tap(obj, lambda obj: ...)
|
|
197
|
+
tap(obj, 'some_method', *args, **kwargs)
|
|
198
|
+
|
|
199
|
+
Call the callback with the object, or its method of the name with the
|
|
200
|
+
arguments, or read its attribute of the name, as :meth:`Pyjo_Base.tap` and
|
|
201
|
+
the ``tap`` of the classes that are not :mod:`Pyjo.Base` do.
|
|
202
|
+
"""
|
|
203
|
+
if callable(cb):
|
|
204
|
+
cb(obj, *args, **kwargs)
|
|
205
|
+
return
|
|
206
|
+
value = getattr(obj, cb)
|
|
207
|
+
if callable(value):
|
|
208
|
+
value(*args, **kwargs)
|
|
209
|
+
elif args or kwargs:
|
|
210
|
+
raise TypeError(f'{cb!r} of {type(obj).__name__} is not callable')
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
new = Pyjo_Base.new
|
|
214
|
+
object = Pyjo_Base
|
|
215
|
+
Base = Pyjo_Base
|
Pyjo/Cache.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjo.Cache - Naive in-memory cache
|
|
3
|
+
==================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjo.Cache import Cache
|
|
7
|
+
|
|
8
|
+
cache = Cache(max_keys=50)
|
|
9
|
+
cache.set('foo', 'bar')
|
|
10
|
+
foo = cache.get('foo')
|
|
11
|
+
|
|
12
|
+
# Or import the module, as in Google Python Style Guide
|
|
13
|
+
|
|
14
|
+
import Pyjo.Cache
|
|
15
|
+
|
|
16
|
+
cache = Pyjo.Cache.new(max_keys=50)
|
|
17
|
+
cache.set('foo', 'bar')
|
|
18
|
+
foo = cache.get('foo')
|
|
19
|
+
|
|
20
|
+
:mod:`Pyjo.Cache` is a naive in-memory cache with size limits.
|
|
21
|
+
|
|
22
|
+
See also
|
|
23
|
+
--------
|
|
24
|
+
|
|
25
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
26
|
+
|
|
27
|
+
Ported from `Mojo::Cache <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/Cache.pm>`_
|
|
28
|
+
of Mojolicious 9.49.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import Pyjo.Base
|
|
32
|
+
|
|
33
|
+
import builtins
|
|
34
|
+
|
|
35
|
+
from collections.abc import Hashable
|
|
36
|
+
from typing import Self
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
type Anything = builtins.object
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Pyjo_Cache[K: Hashable, V](Pyjo.Base.object):
|
|
43
|
+
"""
|
|
44
|
+
:mod:`Pyjo.Cache` inherits all methods from :mod:`Pyjo.Base` and
|
|
45
|
+
implements the following new ones.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, max_keys: int = 100) -> None:
|
|
49
|
+
"""::
|
|
50
|
+
|
|
51
|
+
cache = Pyjo.Cache.new()
|
|
52
|
+
cache = Pyjo.Cache.new(max_keys=50)
|
|
53
|
+
|
|
54
|
+
Construct a new :mod:`Pyjo.Cache` object.
|
|
55
|
+
"""
|
|
56
|
+
self.max_keys = max_keys
|
|
57
|
+
"""::
|
|
58
|
+
|
|
59
|
+
max = cache.max_keys
|
|
60
|
+
cache.max_keys = 50
|
|
61
|
+
|
|
62
|
+
Maximum number of cache keys, defaults to ``100``. Setting the value to
|
|
63
|
+
``0`` will disable caching.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
# The order of the keys of a dict is the queue of Mojo
|
|
67
|
+
self._cache: dict[K, V] = {}
|
|
68
|
+
|
|
69
|
+
# Not in Mojolicious: the protocol of a container
|
|
70
|
+
def __contains__(self, key: Anything) -> bool:
|
|
71
|
+
"""::
|
|
72
|
+
|
|
73
|
+
boolean = 'foo' in cache
|
|
74
|
+
|
|
75
|
+
Check if a value is cached.
|
|
76
|
+
"""
|
|
77
|
+
return key in self._cache
|
|
78
|
+
|
|
79
|
+
# Not in Mojolicious: the protocol of a container
|
|
80
|
+
def __len__(self) -> int:
|
|
81
|
+
"""::
|
|
82
|
+
|
|
83
|
+
count = len(cache)
|
|
84
|
+
|
|
85
|
+
Number of cached values.
|
|
86
|
+
"""
|
|
87
|
+
return len(self._cache)
|
|
88
|
+
|
|
89
|
+
def get(self, key: K) -> V | None:
|
|
90
|
+
"""::
|
|
91
|
+
|
|
92
|
+
value = cache.get('foo')
|
|
93
|
+
|
|
94
|
+
Get cached value.
|
|
95
|
+
"""
|
|
96
|
+
return self._cache.get(key)
|
|
97
|
+
|
|
98
|
+
# The set() of Pyjo.Base sets attributes, the set() of Mojo caches a value
|
|
99
|
+
def set(self, key: K, value: V) -> Self: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
100
|
+
"""::
|
|
101
|
+
|
|
102
|
+
cache = cache.set('foo', 'bar')
|
|
103
|
+
|
|
104
|
+
Set cached value.
|
|
105
|
+
"""
|
|
106
|
+
if (max := self.max_keys) <= 0:
|
|
107
|
+
return self
|
|
108
|
+
|
|
109
|
+
cache = self._cache
|
|
110
|
+
while len(cache) >= max:
|
|
111
|
+
del cache[next(iter(cache))]
|
|
112
|
+
cache[key] = value
|
|
113
|
+
|
|
114
|
+
return self
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
new = Pyjo_Cache.new
|
|
118
|
+
object = Pyjo_Cache
|
|
119
|
+
Cache = Pyjo_Cache
|