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/App/__init__.py
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjo.App - Duct tape for the HTML5 web
|
|
3
|
+
======================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjo.App import App
|
|
7
|
+
|
|
8
|
+
class MyApp(App):
|
|
9
|
+
|
|
10
|
+
# All the complexities of CGI, WSGI, ASGI, HTTP and WebSockets get
|
|
11
|
+
# reduced to a single method call!
|
|
12
|
+
def handler(self, tx):
|
|
13
|
+
|
|
14
|
+
# Resume transaction at the end of the block
|
|
15
|
+
with tx:
|
|
16
|
+
|
|
17
|
+
# Request
|
|
18
|
+
method = tx.req.method
|
|
19
|
+
path = tx.req.url.path
|
|
20
|
+
|
|
21
|
+
# Response
|
|
22
|
+
tx.res.code = 200
|
|
23
|
+
tx.res.headers.content_type = 'text/plain'
|
|
24
|
+
tx.res.body = f'{method} request for {path}!'.encode()
|
|
25
|
+
|
|
26
|
+
# Or import the module, as in Google Python Style Guide
|
|
27
|
+
|
|
28
|
+
import Pyjo.App
|
|
29
|
+
|
|
30
|
+
class MyApp(Pyjo.App.object):
|
|
31
|
+
|
|
32
|
+
# All the complexities of CGI, WSGI, ASGI, HTTP and WebSockets get
|
|
33
|
+
# reduced to a single method call!
|
|
34
|
+
def handler(self, tx):
|
|
35
|
+
|
|
36
|
+
# Resume transaction at the end of the block
|
|
37
|
+
with tx:
|
|
38
|
+
|
|
39
|
+
# Request
|
|
40
|
+
method = tx.req.method
|
|
41
|
+
path = tx.req.url.path
|
|
42
|
+
|
|
43
|
+
# Response
|
|
44
|
+
tx.res.code = 200
|
|
45
|
+
tx.res.headers.content_type = 'text/plain'
|
|
46
|
+
tx.res.body = f'{method} request for {path}!'.encode()
|
|
47
|
+
|
|
48
|
+
:mod:`Pyjo.App` is the base class of applications of :mod:`Pyjo`, a flexible
|
|
49
|
+
runtime environment for real-time web frameworks, with all the basic tools and
|
|
50
|
+
helpers needed to write simple web applications and higher level web
|
|
51
|
+
frameworks, such as :mod:`Pyjoyment`.
|
|
52
|
+
|
|
53
|
+
The end of a ``with`` block closes the connections and the servers of its
|
|
54
|
+
:attr:`~Pyjo_App.ua`.
|
|
55
|
+
|
|
56
|
+
See also
|
|
57
|
+
--------
|
|
58
|
+
|
|
59
|
+
:mod:`Pyjo.App.WSGI`, :mod:`Pyjo.HelloWorld`, :mod:`Pyjoyment`, https://mojolicious.org.
|
|
60
|
+
|
|
61
|
+
Inspired by `Mojo <https://github.com/mojolicious/mojo/blob/v6.0/lib/Mojo.pm>`_ of Mojolicious 6.0, the base class of
|
|
62
|
+
Mojolicious until Mojolicious 7.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
import Pyjo.Base
|
|
66
|
+
import Pyjo.Home
|
|
67
|
+
import Pyjo.Log
|
|
68
|
+
import Pyjo.Promise
|
|
69
|
+
import Pyjo.Server
|
|
70
|
+
import Pyjo.Transaction.HTTP
|
|
71
|
+
import Pyjo.UserAgent
|
|
72
|
+
import Pyjo.Util
|
|
73
|
+
|
|
74
|
+
import os
|
|
75
|
+
import sys
|
|
76
|
+
|
|
77
|
+
from types import TracebackType
|
|
78
|
+
from typing import Any, Self, overload
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Pyjo_App(Pyjo.Base.object):
|
|
82
|
+
"""
|
|
83
|
+
:mod:`Pyjo.App` inherits all methods from :mod:`Pyjo.Base` and implements
|
|
84
|
+
the following new ones.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
# The methods that keep a stash, whose values the arguments of the constructor are
|
|
88
|
+
_stashes: tuple[str, ...] = ('config',)
|
|
89
|
+
|
|
90
|
+
def __init__(self, attrs: dict[str, Any] | None = None, /, **kwargs: Any) -> None:
|
|
91
|
+
"""::
|
|
92
|
+
|
|
93
|
+
app = MyApp.new()
|
|
94
|
+
app = MyApp.new(moniker='foo_bar')
|
|
95
|
+
app = MyApp.new({'moniker': 'foo_bar'})
|
|
96
|
+
|
|
97
|
+
Construct a new application, with the attributes as keyword arguments
|
|
98
|
+
or a dict, and the arguments of
|
|
99
|
+
:meth:`Pyjo.Server.Pyjo_Server.load_app` for its script. The value of
|
|
100
|
+
``config`` is the dict of :meth:`config`.
|
|
101
|
+
"""
|
|
102
|
+
self._home: Pyjo.Home.object | None = None
|
|
103
|
+
self._log: Pyjo.Log.object | None = None
|
|
104
|
+
self._moniker: str | None = None
|
|
105
|
+
self._ua: Pyjo.UserAgent.object | None = None
|
|
106
|
+
|
|
107
|
+
# Not in Mojolicious: the value of a method that keeps a stash, as config, is its dict, as Mojo keeps the stash in
|
|
108
|
+
# the hash of the object, which the arguments of new fill
|
|
109
|
+
for name, value in {**(attrs or {}), **kwargs, **Pyjo.Server.args_override}.items():
|
|
110
|
+
setattr(self, f'_{name}' if name in self._stashes else name, value)
|
|
111
|
+
|
|
112
|
+
# Not in Mojolicious: a context manager, which closes the connections and the servers of its user agent where the
|
|
113
|
+
# block ends, as PyPy frees the object only when its garbage collector runs
|
|
114
|
+
def __enter__(self) -> Self:
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
def __exit__(
|
|
118
|
+
self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
|
|
119
|
+
) -> None:
|
|
120
|
+
self.close()
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def home(self) -> Pyjo.Home.object:
|
|
124
|
+
"""::
|
|
125
|
+
|
|
126
|
+
home = app.home
|
|
127
|
+
app.home = Pyjo.Home.new()
|
|
128
|
+
|
|
129
|
+
The home directory of your application, defaults to a :mod:`Pyjo.Home`
|
|
130
|
+
object which stringifies to the actual path. ::
|
|
131
|
+
|
|
132
|
+
# Portably generate path relative to home directory
|
|
133
|
+
path = app.home.child('data', 'important.txt')
|
|
134
|
+
"""
|
|
135
|
+
if self._home is None:
|
|
136
|
+
self._home = Pyjo.Home.new().detect(type(self))
|
|
137
|
+
return self._home
|
|
138
|
+
|
|
139
|
+
@home.setter
|
|
140
|
+
def home(self, value: Pyjo.Home.object) -> None:
|
|
141
|
+
self._home = value
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def log(self) -> Pyjo.Log.object:
|
|
145
|
+
"""::
|
|
146
|
+
|
|
147
|
+
log = app.log
|
|
148
|
+
app.log = Pyjo.Log.new()
|
|
149
|
+
|
|
150
|
+
The logging layer of your application, defaults to a :mod:`Pyjo.Log`
|
|
151
|
+
object. The level will default to the ``PYJO_LOG_LEVEL`` environment
|
|
152
|
+
variable, or ``trace``, and the messages will be written to
|
|
153
|
+
``sys.stderr``. ::
|
|
154
|
+
|
|
155
|
+
# Log debug message
|
|
156
|
+
app.log.debug('It works')
|
|
157
|
+
"""
|
|
158
|
+
if self._log is None:
|
|
159
|
+
log = Pyjo.Log.new()
|
|
160
|
+
# The level of the environment, as an application of Pyjoyment has it, which the tests of Pyjo.Test.Pyjo
|
|
161
|
+
# set, so that an application of Pyjo, as an adapter of another framework, logs as quietly
|
|
162
|
+
if level := os.environ.get('PYJO_LOG_LEVEL'):
|
|
163
|
+
log.level = level
|
|
164
|
+
self._log = log
|
|
165
|
+
return self._log
|
|
166
|
+
|
|
167
|
+
@log.setter
|
|
168
|
+
def log(self, value: Pyjo.Log.object) -> None:
|
|
169
|
+
self._log = value
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def moniker(self) -> str:
|
|
173
|
+
"""::
|
|
174
|
+
|
|
175
|
+
moniker = app.moniker
|
|
176
|
+
app.moniker = 'foo_bar'
|
|
177
|
+
|
|
178
|
+
Moniker of this application, often used as default filename for
|
|
179
|
+
configuration files and the like, defaults to decamelizing the
|
|
180
|
+
application class with :func:`Pyjo.Util.decamelize`: the module of a
|
|
181
|
+
class that the module names ``object``, as ``pyjo-hello_world`` of
|
|
182
|
+
:mod:`Pyjo.HelloWorld`, or the class in its module.
|
|
183
|
+
"""
|
|
184
|
+
if self._moniker is None:
|
|
185
|
+
self._moniker = Pyjo.Util.decamelize(_package(type(self)).replace('.', '::'))
|
|
186
|
+
return self._moniker
|
|
187
|
+
|
|
188
|
+
@moniker.setter
|
|
189
|
+
def moniker(self, value: str) -> None:
|
|
190
|
+
self._moniker = value
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def ua(self) -> Pyjo.UserAgent.object:
|
|
194
|
+
"""::
|
|
195
|
+
|
|
196
|
+
ua = app.ua
|
|
197
|
+
app.ua = Pyjo.UserAgent.new()
|
|
198
|
+
|
|
199
|
+
A full featured HTTP user agent for use in your applications, defaults
|
|
200
|
+
to a :mod:`Pyjo.UserAgent` object. ::
|
|
201
|
+
|
|
202
|
+
# Perform blocking request
|
|
203
|
+
print(app.ua.get('example.com').result.body)
|
|
204
|
+
"""
|
|
205
|
+
if self._ua is None:
|
|
206
|
+
self._ua = Pyjo.UserAgent.new()
|
|
207
|
+
return self._ua
|
|
208
|
+
|
|
209
|
+
@ua.setter
|
|
210
|
+
def ua(self, value: Pyjo.UserAgent.object) -> None:
|
|
211
|
+
self._ua = value
|
|
212
|
+
|
|
213
|
+
def build_tx(self) -> Pyjo.Transaction.HTTP.object:
|
|
214
|
+
"""::
|
|
215
|
+
|
|
216
|
+
tx = app.build_tx()
|
|
217
|
+
|
|
218
|
+
Transaction builder, defaults to building a
|
|
219
|
+
:mod:`Pyjo.Transaction.HTTP` object.
|
|
220
|
+
"""
|
|
221
|
+
return Pyjo.Transaction.HTTP.new()
|
|
222
|
+
|
|
223
|
+
# Not in Mojolicious: the release of the user agent, which Perl does in DESTROY, also at the end of a with block
|
|
224
|
+
def close(self) -> None:
|
|
225
|
+
"""::
|
|
226
|
+
|
|
227
|
+
app.close()
|
|
228
|
+
|
|
229
|
+
Close the connections and the servers of :attr:`ua`, as the end of a
|
|
230
|
+
``with`` block does.
|
|
231
|
+
"""
|
|
232
|
+
if self._ua is not None:
|
|
233
|
+
self._ua.close()
|
|
234
|
+
|
|
235
|
+
# config(**values) with no values is config(), which the first overload types
|
|
236
|
+
@overload
|
|
237
|
+
def config(self) -> dict[str, Any]: ... # pyright: ignore[reportOverlappingOverload]
|
|
238
|
+
|
|
239
|
+
@overload
|
|
240
|
+
def config(self, name: str, /) -> Any: ...
|
|
241
|
+
|
|
242
|
+
@overload
|
|
243
|
+
def config(self, values: dict[str, Any], /) -> Self: ...
|
|
244
|
+
|
|
245
|
+
@overload
|
|
246
|
+
def config(self, **values: Any) -> Self: ...
|
|
247
|
+
|
|
248
|
+
def config(self, *args: Any, **values: Any) -> Any:
|
|
249
|
+
"""::
|
|
250
|
+
|
|
251
|
+
hash = app.config()
|
|
252
|
+
foo = app.config('foo')
|
|
253
|
+
app = app.config({'foo': 'bar', 'baz': 23})
|
|
254
|
+
app = app.config(foo='bar', baz=23)
|
|
255
|
+
|
|
256
|
+
Application configuration. ::
|
|
257
|
+
|
|
258
|
+
# Remove value
|
|
259
|
+
foo = app.config().pop('foo')
|
|
260
|
+
|
|
261
|
+
# Assign multiple values at once
|
|
262
|
+
app.config(foo='test', bar=23)
|
|
263
|
+
"""
|
|
264
|
+
return Pyjo.Util._stash('config', self, args, values) # pyright: ignore[reportPrivateUsage]
|
|
265
|
+
|
|
266
|
+
def handler(self, tx: Any) -> None:
|
|
267
|
+
"""::
|
|
268
|
+
|
|
269
|
+
app.handler(Pyjo.Transaction.HTTP.new())
|
|
270
|
+
|
|
271
|
+
The handler is the main entry point to your application or framework
|
|
272
|
+
and will be called for each new transaction, which will usually be a
|
|
273
|
+
:mod:`Pyjo.Transaction.HTTP` or :mod:`Pyjo.Transaction.WebSocket`
|
|
274
|
+
object. A route of :mod:`Pyjoyment` that embeds the application, as
|
|
275
|
+
:mod:`Pyjoyment.Plugin.Mount` does, calls it with its controller
|
|
276
|
+
instead, whose ``tx`` is the transaction and whose stash has the rest of
|
|
277
|
+
the path in ``path``. Meant to be overloaded in a subclass. ::
|
|
278
|
+
|
|
279
|
+
def handler(self, tx):
|
|
280
|
+
...
|
|
281
|
+
"""
|
|
282
|
+
raise NotImplementedError('Method "handler" not implemented by subclass')
|
|
283
|
+
|
|
284
|
+
def server(self, server: Pyjo.Server.object) -> None:
|
|
285
|
+
"""::
|
|
286
|
+
|
|
287
|
+
app.server(Pyjo.Server.new())
|
|
288
|
+
|
|
289
|
+
Called by the server before it starts. Meant to be overloaded in a
|
|
290
|
+
subclass.
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
# Not in Mojolicious: the end of a server, as server is its start, after which the application cleans up what it has
|
|
294
|
+
# started, as the lifespan of an ASGI application
|
|
295
|
+
def server_stopped(self, server: Pyjo.Server.object) -> 'Pyjo.Promise.object | None':
|
|
296
|
+
"""::
|
|
297
|
+
|
|
298
|
+
promise = app.server_stopped(Pyjo.Server.new())
|
|
299
|
+
|
|
300
|
+
Called by the server once it has stopped gracefully, in each process
|
|
301
|
+
and thread that has run the application, with the event loop no longer
|
|
302
|
+
running. A promise that it returns is waited for, so that the
|
|
303
|
+
application can clean up with the event loop. Meant to be overloaded
|
|
304
|
+
in a subclass.
|
|
305
|
+
"""
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
def start(self, *args: str) -> Self:
|
|
309
|
+
"""::
|
|
310
|
+
|
|
311
|
+
app = app.start()
|
|
312
|
+
|
|
313
|
+
Start the application. When the environment variable
|
|
314
|
+
``PYJO_APP_LOADER`` is set, as :meth:`Pyjo.Server.Pyjo_Server.load_app`
|
|
315
|
+
does, it is kept in :data:`Pyjo.Server.loaded_app` for the server.
|
|
316
|
+
"""
|
|
317
|
+
# Application loader
|
|
318
|
+
if os.environ.get('PYJO_APP_LOADER') is not None:
|
|
319
|
+
Pyjo.Server.loaded_app = self
|
|
320
|
+
return self
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
# The name of a class as Mojo has the package: the module that names the class its object, or the class in the module,
|
|
324
|
+
# and the class only in a script
|
|
325
|
+
def _package(cls: type) -> str:
|
|
326
|
+
module = cls.__module__
|
|
327
|
+
if getattr(sys.modules.get(module), 'object', None) is cls:
|
|
328
|
+
return module
|
|
329
|
+
return cls.__qualname__ if module == '__main__' else f'{module}.{cls.__qualname__}'
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
# The path of the request for an application of another framework, as escaped strings: the path that a route embeds the
|
|
333
|
+
# application under, after the base path, as SCRIPT_NAME of WSGI and root_path of ASGI, and the rest of it, which the
|
|
334
|
+
# route left in the stash
|
|
335
|
+
def _mount(tx: Any, c: Any) -> tuple[str, str]: # pyright: ignore[reportUnusedFunction]
|
|
336
|
+
url = tx.req.url
|
|
337
|
+
path = url.path.to_abs_str()
|
|
338
|
+
script = url.base.path.to_str().rstrip('/')
|
|
339
|
+
|
|
340
|
+
if c is not None and (rest := c.stash().get('path')) is not None:
|
|
341
|
+
route = url.path.to_route()
|
|
342
|
+
if not route.endswith(rest):
|
|
343
|
+
path = rest
|
|
344
|
+
else:
|
|
345
|
+
# The rest begins at the slash after the ones of the path above it
|
|
346
|
+
end = len(path)
|
|
347
|
+
if len(rest):
|
|
348
|
+
end = -1
|
|
349
|
+
for _ in range(route[: len(route) - len(rest)].count('/') + 1):
|
|
350
|
+
if (end := path.find('/', end + 1)) < 0:
|
|
351
|
+
end = len(path)
|
|
352
|
+
break
|
|
353
|
+
script += path[:end]
|
|
354
|
+
path = path[end:]
|
|
355
|
+
|
|
356
|
+
return script, path
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
# Not in Mojolicious: a route embeds an application of another framework at a part of the path that ends with a slash,
|
|
360
|
+
# or at the whole path, as SCRIPT_NAME of CGI and WSGI and root_path of ASGI can only be, where /php of /phpx is not
|
|
361
|
+
def _within(c: Any) -> bool: # pyright: ignore[reportUnusedFunction]
|
|
362
|
+
return c is None or not (rest := c.stash().get('path')) or rest.startswith('/')
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
# The environment of CGI for the request, as an application of another framework gets it: the path that a route embeds
|
|
366
|
+
# the application under is SCRIPT_NAME, and the rest of it is PATH_INFO, as unescaped bytes in the characters 0-255
|
|
367
|
+
def _cgi(tx: Any, c: Any) -> dict[str, str]: # pyright: ignore[reportUnusedFunction]
|
|
368
|
+
req = tx.req
|
|
369
|
+
url = req.url
|
|
370
|
+
base = url.base
|
|
371
|
+
script, path = _mount(tx, c)
|
|
372
|
+
|
|
373
|
+
https = req.is_secure
|
|
374
|
+
env = {
|
|
375
|
+
'REQUEST_METHOD': req.method,
|
|
376
|
+
'SCRIPT_NAME': _bytes(script),
|
|
377
|
+
'PATH_INFO': _bytes(path),
|
|
378
|
+
'QUERY_STRING': url.query.to_str(),
|
|
379
|
+
'SERVER_NAME': base.host or tx.local_address or 'localhost',
|
|
380
|
+
'SERVER_PORT': str(base.port or tx.local_port or (443 if https else 80)),
|
|
381
|
+
'SERVER_PROTOCOL': f'HTTP/{req.version}',
|
|
382
|
+
}
|
|
383
|
+
if (address := tx.remote_address) is not None:
|
|
384
|
+
env['REMOTE_ADDR'] = address
|
|
385
|
+
if (port := tx.remote_port) is not None:
|
|
386
|
+
env['REMOTE_PORT'] = str(port)
|
|
387
|
+
|
|
388
|
+
# Headers, joined as CGI has them, and the length of the body, which is whole, as it has been read
|
|
389
|
+
headers = req.headers
|
|
390
|
+
for name in headers.names():
|
|
391
|
+
key = name.upper().replace('-', '_')
|
|
392
|
+
if key == 'CONTENT_TYPE':
|
|
393
|
+
env[key] = headers.header(name)
|
|
394
|
+
elif key not in ('CONTENT_LENGTH', 'TRANSFER_ENCODING'):
|
|
395
|
+
env[f'HTTP_{key}'] = ('; ' if key == 'COOKIE' else ', ').join(headers.every_header(name))
|
|
396
|
+
if size := req.content.body_size:
|
|
397
|
+
env['CONTENT_LENGTH'] = str(size)
|
|
398
|
+
|
|
399
|
+
return env
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
# A path of CGI: the unescaped bytes as the characters 0-255
|
|
403
|
+
def _bytes(path: str) -> str:
|
|
404
|
+
return Pyjo.Util.url_unescape(path.encode()).decode('latin-1')
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
new = Pyjo_App.new
|
|
408
|
+
object = Pyjo_App
|
|
409
|
+
App = Pyjo_App
|