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
Pyjoyment/HelloWorld.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjoyment.HelloWorld - Hello World!
|
|
3
|
+
===================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjoyment.HelloWorld import HelloWorld
|
|
7
|
+
|
|
8
|
+
hello = HelloWorld()
|
|
9
|
+
hello.start()
|
|
10
|
+
|
|
11
|
+
# Or import the module, as in Google Python Style Guide
|
|
12
|
+
|
|
13
|
+
import Pyjoyment.HelloWorld
|
|
14
|
+
|
|
15
|
+
hello = Pyjoyment.HelloWorld.new()
|
|
16
|
+
hello.start()
|
|
17
|
+
|
|
18
|
+
:mod:`Pyjoyment.HelloWorld` is the default :mod:`Pyjoyment` application, used
|
|
19
|
+
mostly for testing. :mod:`Pyjo.Server` defaults to :mod:`Pyjo.HelloWorld`, its
|
|
20
|
+
counterpart without :mod:`Pyjoyment`.
|
|
21
|
+
|
|
22
|
+
See also
|
|
23
|
+
--------
|
|
24
|
+
|
|
25
|
+
:mod:`Pyjoyment`, :mod:`Pyjo.HelloWorld`, https://mojolicious.org.
|
|
26
|
+
|
|
27
|
+
Ported from `Mojo::HelloWorld <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/HelloWorld.pm>`_ of
|
|
28
|
+
Mojolicious 9.49.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import Pyjoyment
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Pyjoyment_HelloWorld(Pyjoyment.object):
|
|
35
|
+
"""
|
|
36
|
+
:mod:`Pyjoyment.HelloWorld` inherits all attributes and methods from
|
|
37
|
+
:mod:`Pyjoyment` and implements the following new ones.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def startup(self) -> None:
|
|
41
|
+
"""::
|
|
42
|
+
|
|
43
|
+
hello.startup()
|
|
44
|
+
|
|
45
|
+
Creates a catch-all route that renders a text message.
|
|
46
|
+
"""
|
|
47
|
+
self.preload_namespaces = []
|
|
48
|
+
self.log.level = 'error'
|
|
49
|
+
self.log.path = None
|
|
50
|
+
# Unlike Mojolicious: it answers "Your Pyjoyment is working!", as the moniker names it
|
|
51
|
+
self.routes.any('/*whatever', {'whatever': '', 'text': 'Your Pyjoyment is working!'})
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
new = Pyjoyment_HelloWorld.new
|
|
55
|
+
object = Pyjoyment_HelloWorld
|
|
56
|
+
HelloWorld = Pyjoyment_HelloWorld
|
Pyjoyment/Lite.py
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjoyment.Lite - Micro real-time web framework
|
|
3
|
+
==============================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjoyment.Lite import *
|
|
7
|
+
|
|
8
|
+
# Route with placeholder
|
|
9
|
+
@get('/:foo')
|
|
10
|
+
def foo(c):
|
|
11
|
+
foo = c.param('foo')
|
|
12
|
+
c.render(text=f'Hello from {foo}.')
|
|
13
|
+
|
|
14
|
+
# Start the Pyjoyment command system
|
|
15
|
+
app.start()
|
|
16
|
+
|
|
17
|
+
:mod:`Pyjoyment.Lite` is a tiny domain specific language built around
|
|
18
|
+
:mod:`Pyjoyment`, made up of only about a dozen Python functions.
|
|
19
|
+
|
|
20
|
+
The module that imports the functions gets an application of its own, as
|
|
21
|
+
``from Pyjoyment.Lite import *`` makes one for it, named after its file, with
|
|
22
|
+
its home in the directory of the file and its ``DATA`` templates in its
|
|
23
|
+
docstring after a line ``__DATA__``, which is there before ``app.start()``
|
|
24
|
+
runs. A route is a decorator of its callback too, which returns the route. ::
|
|
25
|
+
|
|
26
|
+
from Pyjoyment.Lite import *
|
|
27
|
+
|
|
28
|
+
get('/', {'text': 'Hello World!'})
|
|
29
|
+
|
|
30
|
+
@get('/:foo')
|
|
31
|
+
def foo(c):
|
|
32
|
+
c.render(text=f'Hello from {c.param("foo")}.')
|
|
33
|
+
|
|
34
|
+
app.start()
|
|
35
|
+
|
|
36
|
+
Growing
|
|
37
|
+
-------
|
|
38
|
+
|
|
39
|
+
Here we have collected a few snippets that illustrate very well just how
|
|
40
|
+
similar :mod:`Pyjoyment.Lite` and :mod:`Pyjoyment` applications are.
|
|
41
|
+
|
|
42
|
+
Routes
|
|
43
|
+
^^^^^^
|
|
44
|
+
|
|
45
|
+
The functions ``get``, ``post`` and friends all have equivalent methods. ::
|
|
46
|
+
|
|
47
|
+
# Pyjoyment.Lite
|
|
48
|
+
@get('/foo')
|
|
49
|
+
def foo(c):
|
|
50
|
+
c.render(text='Hello World!')
|
|
51
|
+
|
|
52
|
+
# Pyjoyment
|
|
53
|
+
class MyApp(Pyjoyment.object):
|
|
54
|
+
def startup(self):
|
|
55
|
+
routes = self.routes
|
|
56
|
+
routes.get('/foo', lambda c: c.render(text='Hello World!'))
|
|
57
|
+
|
|
58
|
+
Application
|
|
59
|
+
^^^^^^^^^^^
|
|
60
|
+
|
|
61
|
+
The application object you can access with the name ``app`` is the object
|
|
62
|
+
passed as ``self`` to the ``startup`` method. ::
|
|
63
|
+
|
|
64
|
+
# Pyjoyment.Lite
|
|
65
|
+
app.max_request_size = 16777216
|
|
66
|
+
|
|
67
|
+
# Pyjoyment
|
|
68
|
+
class MyApp(Pyjoyment.object):
|
|
69
|
+
def startup(self):
|
|
70
|
+
self.max_request_size = 16777216
|
|
71
|
+
|
|
72
|
+
Plugins
|
|
73
|
+
^^^^^^^
|
|
74
|
+
|
|
75
|
+
Instead of the ``plugin`` function you just use the method
|
|
76
|
+
:meth:`Pyjoyment.Pyjoyment_App.plugin`. ::
|
|
77
|
+
|
|
78
|
+
# Pyjoyment.Lite
|
|
79
|
+
plugin('Config')
|
|
80
|
+
|
|
81
|
+
# Pyjoyment
|
|
82
|
+
class MyApp(Pyjoyment.object):
|
|
83
|
+
def startup(self):
|
|
84
|
+
self.plugin('Config')
|
|
85
|
+
|
|
86
|
+
Helpers
|
|
87
|
+
^^^^^^^
|
|
88
|
+
|
|
89
|
+
Similar to plugins, instead of the ``helper`` function you just use the
|
|
90
|
+
method :meth:`Pyjoyment.Pyjoyment_App.helper`. ::
|
|
91
|
+
|
|
92
|
+
# Pyjoyment.Lite
|
|
93
|
+
@helper('two')
|
|
94
|
+
def two(c):
|
|
95
|
+
return 1 + 1
|
|
96
|
+
|
|
97
|
+
# Pyjoyment
|
|
98
|
+
class MyApp(Pyjoyment.object):
|
|
99
|
+
def startup(self):
|
|
100
|
+
self.helper('two', lambda c: 1 + 1)
|
|
101
|
+
|
|
102
|
+
Templates
|
|
103
|
+
^^^^^^^^^
|
|
104
|
+
|
|
105
|
+
Instead of the ``template`` function you just use the method
|
|
106
|
+
:meth:`Pyjoyment.Pyjoyment_App.template`. ::
|
|
107
|
+
|
|
108
|
+
# Pyjoyment.Lite
|
|
109
|
+
@template('hello.html')
|
|
110
|
+
def hello(c, name):
|
|
111
|
+
return t('<p>Hello {name}!</p>')
|
|
112
|
+
|
|
113
|
+
# Pyjoyment
|
|
114
|
+
class MyApp(Pyjoyment.object):
|
|
115
|
+
def startup(self):
|
|
116
|
+
self.template('hello.html', lambda c, name: t('<p>Hello {name}!</p>', name=name))
|
|
117
|
+
|
|
118
|
+
Under
|
|
119
|
+
^^^^^
|
|
120
|
+
|
|
121
|
+
Instead of sequential function calls, we can use methods to build a tree with
|
|
122
|
+
nested routes, that much better illustrates how routes work internally. ::
|
|
123
|
+
|
|
124
|
+
# Pyjoyment.Lite
|
|
125
|
+
under('/foo')
|
|
126
|
+
get('/bar', lambda c: ...)
|
|
127
|
+
|
|
128
|
+
# Pyjoyment
|
|
129
|
+
class MyApp(Pyjoyment.object):
|
|
130
|
+
def startup(self):
|
|
131
|
+
routes = self.routes
|
|
132
|
+
foo = routes.under('/foo')
|
|
133
|
+
foo.get('/bar', lambda c: ...)
|
|
134
|
+
|
|
135
|
+
Functions
|
|
136
|
+
---------
|
|
137
|
+
|
|
138
|
+
:mod:`Pyjoyment.Lite` implements the following functions, which are
|
|
139
|
+
automatically exported.
|
|
140
|
+
|
|
141
|
+
any ::
|
|
142
|
+
|
|
143
|
+
route = any('/:foo', lambda c: ...)
|
|
144
|
+
route = any('/:foo', lambda c: ..., 'name')
|
|
145
|
+
route = any('/:foo', {'foo': 'bar'}, lambda c: ...)
|
|
146
|
+
route = any('/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
147
|
+
route = any(['GET', 'POST'], '/:foo', lambda c: ...)
|
|
148
|
+
route = any(['GET', 'POST'], '/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
149
|
+
route = any(['GET', 'POST'], '/:foo', lambda c: ...).requires(agent=re.compile(r'Firefox'))
|
|
150
|
+
|
|
151
|
+
Generate route with :meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.any`,
|
|
152
|
+
matching any of the listed HTTP request methods or all.
|
|
153
|
+
|
|
154
|
+
app ::
|
|
155
|
+
|
|
156
|
+
app = app
|
|
157
|
+
|
|
158
|
+
The :mod:`Pyjoyment.Lite` application object, which is a subclass of
|
|
159
|
+
:mod:`Pyjoyment`. ::
|
|
160
|
+
|
|
161
|
+
# Use all the available attributes and methods
|
|
162
|
+
app.log.level = 'error'
|
|
163
|
+
app.defaults(foo='bar')
|
|
164
|
+
|
|
165
|
+
delete ::
|
|
166
|
+
|
|
167
|
+
route = delete('/:foo', lambda c: ...)
|
|
168
|
+
route = delete('/:foo', lambda c: ..., 'name')
|
|
169
|
+
route = delete('/:foo', {'foo': 'bar'}, lambda c: ...)
|
|
170
|
+
route = delete('/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
171
|
+
route = delete('/:foo', lambda c: ...).requires(agent=re.compile(r'Firefox'))
|
|
172
|
+
|
|
173
|
+
Generate route with
|
|
174
|
+
:meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.delete`, matching only
|
|
175
|
+
``DELETE`` requests.
|
|
176
|
+
|
|
177
|
+
get ::
|
|
178
|
+
|
|
179
|
+
route = get('/:foo', lambda c: ...)
|
|
180
|
+
route = get('/:foo', lambda c: ..., 'name')
|
|
181
|
+
route = get('/:foo', {'foo': 'bar'}, lambda c: ...)
|
|
182
|
+
route = get('/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
183
|
+
route = get('/:foo', lambda c: ...).requires(agent=re.compile(r'Firefox'))
|
|
184
|
+
|
|
185
|
+
Generate route with :meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.get`,
|
|
186
|
+
matching only ``GET`` requests.
|
|
187
|
+
|
|
188
|
+
group ::
|
|
189
|
+
|
|
190
|
+
with group():
|
|
191
|
+
...
|
|
192
|
+
|
|
193
|
+
Start a new route group, which ends with the block. A function can be the
|
|
194
|
+
group too: ``group(lambda: ...)``.
|
|
195
|
+
|
|
196
|
+
helper ::
|
|
197
|
+
|
|
198
|
+
helper('foo', lambda c, *args: ...)
|
|
199
|
+
|
|
200
|
+
Add a new helper with :meth:`Pyjoyment.Pyjoyment_App.helper`.
|
|
201
|
+
|
|
202
|
+
hook ::
|
|
203
|
+
|
|
204
|
+
hook('after_dispatch', lambda c: ...)
|
|
205
|
+
|
|
206
|
+
Share code with :meth:`Pyjoyment.Pyjoyment_App.hook`.
|
|
207
|
+
|
|
208
|
+
options ::
|
|
209
|
+
|
|
210
|
+
route = options('/:foo', lambda c: ...)
|
|
211
|
+
route = options('/:foo', lambda c: ..., 'name')
|
|
212
|
+
route = options('/:foo', {'foo': 'bar'}, lambda c: ...)
|
|
213
|
+
route = options('/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
214
|
+
route = options('/:foo', lambda c: ...).requires(agent=re.compile(r'Firefox'))
|
|
215
|
+
|
|
216
|
+
Generate route with
|
|
217
|
+
:meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.options`, matching only
|
|
218
|
+
``OPTIONS`` requests.
|
|
219
|
+
|
|
220
|
+
patch ::
|
|
221
|
+
|
|
222
|
+
route = patch('/:foo', lambda c: ...)
|
|
223
|
+
route = patch('/:foo', lambda c: ..., 'name')
|
|
224
|
+
route = patch('/:foo', {'foo': 'bar'}, lambda c: ...)
|
|
225
|
+
route = patch('/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
226
|
+
route = patch('/:foo', lambda c: ...).requires(agent=re.compile(r'Firefox'))
|
|
227
|
+
|
|
228
|
+
Generate route with
|
|
229
|
+
:meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.patch`, matching only
|
|
230
|
+
``PATCH`` requests.
|
|
231
|
+
|
|
232
|
+
plugin ::
|
|
233
|
+
|
|
234
|
+
plugin('SomePlugin', foo=23)
|
|
235
|
+
|
|
236
|
+
Load a plugin with :meth:`Pyjoyment.Pyjoyment_App.plugin`.
|
|
237
|
+
|
|
238
|
+
post ::
|
|
239
|
+
|
|
240
|
+
route = post('/:foo', lambda c: ...)
|
|
241
|
+
route = post('/:foo', lambda c: ..., 'name')
|
|
242
|
+
route = post('/:foo', {'foo': 'bar'}, lambda c: ...)
|
|
243
|
+
route = post('/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
244
|
+
route = post('/:foo', lambda c: ...).requires(agent=re.compile(r'Firefox'))
|
|
245
|
+
|
|
246
|
+
Generate route with
|
|
247
|
+
:meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.post`, matching only
|
|
248
|
+
``POST`` requests.
|
|
249
|
+
|
|
250
|
+
put ::
|
|
251
|
+
|
|
252
|
+
route = put('/:foo', lambda c: ...)
|
|
253
|
+
route = put('/:foo', lambda c: ..., 'name')
|
|
254
|
+
route = put('/:foo', {'foo': 'bar'}, lambda c: ...)
|
|
255
|
+
route = put('/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
256
|
+
route = put('/:foo', lambda c: ...).requires(agent=re.compile(r'Firefox'))
|
|
257
|
+
|
|
258
|
+
Generate route with :meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.put`,
|
|
259
|
+
matching only ``PUT`` requests.
|
|
260
|
+
|
|
261
|
+
query ::
|
|
262
|
+
|
|
263
|
+
route = query('/:foo', lambda c: ...)
|
|
264
|
+
route = query('/:foo', lambda c: ..., 'name')
|
|
265
|
+
route = query('/:foo', {'foo': 'bar'}, lambda c: ...)
|
|
266
|
+
route = query('/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
267
|
+
route = query('/:foo', lambda c: ...).requires(agent=re.compile(r'Firefox'))
|
|
268
|
+
|
|
269
|
+
Generate route with
|
|
270
|
+
:meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.query`, matching only
|
|
271
|
+
``QUERY`` requests.
|
|
272
|
+
|
|
273
|
+
template ::
|
|
274
|
+
|
|
275
|
+
template('index.html', lambda c: t('<p>Hello!</p>'))
|
|
276
|
+
|
|
277
|
+
@template('index.html')
|
|
278
|
+
def index(c): ...
|
|
279
|
+
|
|
280
|
+
Add a template of a function with :meth:`Pyjoyment.Pyjoyment_App.template`,
|
|
281
|
+
the counterpart of a template of the ``DATA`` section for template strings,
|
|
282
|
+
see :mod:`Pyjoyment.Plugin.PyRenderer`.
|
|
283
|
+
|
|
284
|
+
under ::
|
|
285
|
+
|
|
286
|
+
route = under(lambda c: ...)
|
|
287
|
+
route = under('/:foo', lambda c: ...)
|
|
288
|
+
route = under('/:foo', {'foo': 'bar'})
|
|
289
|
+
route = under('/:foo', foo=re.compile(r'\\w+'))
|
|
290
|
+
route = under('/:foo').requires(agent=re.compile(r'Firefox'))
|
|
291
|
+
route = under(format=['json', 'yaml'])
|
|
292
|
+
|
|
293
|
+
Generate nested route with
|
|
294
|
+
:meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.under`, to which all
|
|
295
|
+
following routes are automatically appended.
|
|
296
|
+
|
|
297
|
+
websocket ::
|
|
298
|
+
|
|
299
|
+
route = websocket('/:foo', lambda c: ...)
|
|
300
|
+
route = websocket('/:foo', lambda c: ..., 'name')
|
|
301
|
+
route = websocket('/:foo', {'foo': 'bar'}, lambda c: ...)
|
|
302
|
+
route = websocket('/:foo', lambda c: ..., foo=re.compile(r'\\w+'))
|
|
303
|
+
route = websocket('/:foo', lambda c: ...).requires(agent=re.compile(r'Firefox'))
|
|
304
|
+
|
|
305
|
+
Generate route with
|
|
306
|
+
:meth:`Pyjoyment.Routes.Route.Pyjoyment_Routes_Route.websocket`, matching
|
|
307
|
+
only WebSocket handshakes.
|
|
308
|
+
|
|
309
|
+
See also
|
|
310
|
+
--------
|
|
311
|
+
|
|
312
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
313
|
+
|
|
314
|
+
Ported from `Mojolicious::Lite <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojolicious/Lite.pm>`_
|
|
315
|
+
of Mojolicious 9.49.
|
|
316
|
+
|
|
317
|
+
Classes
|
|
318
|
+
-------
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
import Pyjo.File
|
|
322
|
+
import Pyjo.UserAgent.Server
|
|
323
|
+
import Pyjoyment
|
|
324
|
+
import Pyjoyment.Routes.Route
|
|
325
|
+
|
|
326
|
+
import contextlib
|
|
327
|
+
import os
|
|
328
|
+
import sys
|
|
329
|
+
import types
|
|
330
|
+
|
|
331
|
+
from collections.abc import Callable, Generator
|
|
332
|
+
from typing import TYPE_CHECKING, Any
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# Renamed from Mojolicious: del is delete, as del is a keyword of Python, and del_ is its alias
|
|
336
|
+
__all__ = [
|
|
337
|
+
'any',
|
|
338
|
+
'app',
|
|
339
|
+
'del_',
|
|
340
|
+
'delete',
|
|
341
|
+
'get',
|
|
342
|
+
'group',
|
|
343
|
+
'helper',
|
|
344
|
+
'hook',
|
|
345
|
+
'options',
|
|
346
|
+
'patch',
|
|
347
|
+
'plugin',
|
|
348
|
+
'post',
|
|
349
|
+
'put',
|
|
350
|
+
'query',
|
|
351
|
+
'template',
|
|
352
|
+
'under',
|
|
353
|
+
'websocket',
|
|
354
|
+
]
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
class Pyjoyment_Lite(Pyjoyment.object):
|
|
358
|
+
"""
|
|
359
|
+
:mod:`Pyjoyment.Lite` inherits all attributes and methods from
|
|
360
|
+
:mod:`Pyjoyment`.
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
# Unlike Mojolicious: Mojo exports the functions into the package that uses it, and Python imports a module once, so
|
|
365
|
+
# the functions are attributes of this module that its __getattr__ makes for the module that imports them, which
|
|
366
|
+
# gets an application of its own, as a package of Perl does
|
|
367
|
+
def __getattr__(name: str) -> Any:
|
|
368
|
+
if name not in __all__:
|
|
369
|
+
raise AttributeError(f'module {__name__!r} has no attribute {name!r}', name=name)
|
|
370
|
+
return _functions(sys._getframe(1))[name] # pyright: ignore[reportPrivateUsage]
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
# The functions of the module of a frame, which import them, skipping the frames of the import system; they are kept in
|
|
374
|
+
# the module, as Mojo puts them in the package, so that a script that runs again, as load_app runs it, gets an
|
|
375
|
+
# application of its own
|
|
376
|
+
def _functions(frame: types.FrameType) -> dict[str, Any]:
|
|
377
|
+
while frame.f_back is not None and frame.f_code.co_filename.startswith('<frozen importlib'):
|
|
378
|
+
frame = frame.f_back
|
|
379
|
+
namespace = frame.f_globals
|
|
380
|
+
if (functions := namespace.get('__pyjoyment_lite__')) is None:
|
|
381
|
+
caller: str = namespace.get('__name__', '__main__')
|
|
382
|
+
functions = namespace['__pyjoyment_lite__'] = _import(caller, namespace.get('__file__'))
|
|
383
|
+
return functions
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _import(caller: str, file: str | None) -> dict[str, Any]:
|
|
387
|
+
# Remember executable for later
|
|
388
|
+
if not os.environ.get('PYJO_EXE') and file:
|
|
389
|
+
os.environ['PYJO_EXE'] = file
|
|
390
|
+
exe = os.environ.get('PYJO_EXE') or file or sys.argv[0]
|
|
391
|
+
|
|
392
|
+
# Reuse home directory if possible
|
|
393
|
+
home = os.environ.get('PYJO_HOME')
|
|
394
|
+
try:
|
|
395
|
+
if not home and exe:
|
|
396
|
+
os.environ['PYJO_HOME'] = Pyjo.File.path(exe).to_abs().dirname().to_str()
|
|
397
|
+
|
|
398
|
+
# Generate moniker based on filename
|
|
399
|
+
moniker = Pyjo.File.path(exe).basename('.py') if exe else 'pyjoyment'
|
|
400
|
+
app = Pyjoyment_Lite.new(moniker=moniker)
|
|
401
|
+
finally:
|
|
402
|
+
if not home:
|
|
403
|
+
os.environ.pop('PYJO_HOME', None)
|
|
404
|
+
|
|
405
|
+
# Initialize routes without namespaces
|
|
406
|
+
routes = app.routes
|
|
407
|
+
routes.namespaces = []
|
|
408
|
+
app.static.classes[0] = app.renderer.classes[0] = caller
|
|
409
|
+
|
|
410
|
+
# Unlike Mojolicious: Mojo makes the package that uses it a subclass of Mojolicious, with the functions new and app
|
|
411
|
+
# that return the application, and a module is not a class, so the class is kept with the functions of the module,
|
|
412
|
+
# where the routes find it by the name of the module
|
|
413
|
+
cls = type(
|
|
414
|
+
'Pyjoyment_Lite',
|
|
415
|
+
(Pyjoyment_Lite,),
|
|
416
|
+
{'__module__': caller, 'new': classmethod(lambda cls, *args, **kwargs: app)},
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
# The Pyjoyment.Lite DSL
|
|
420
|
+
dsl = _DSL(app, routes)
|
|
421
|
+
functions: dict[str, Any] = {
|
|
422
|
+
name: dsl.route(name) for name in ('any', 'get', 'options', 'patch', 'post', 'put', 'query', 'websocket')
|
|
423
|
+
}
|
|
424
|
+
functions['delete'] = functions['del_'] = dsl.route('delete')
|
|
425
|
+
functions.update(app=app, group=dsl.group, helper=app.helper, hook=app.hook, plugin=app.plugin, under=dsl.under)
|
|
426
|
+
functions['class'] = cls
|
|
427
|
+
|
|
428
|
+
# Not in Mojolicious: the templates of functions, for the handler "py"
|
|
429
|
+
functions['template'] = app.template
|
|
430
|
+
|
|
431
|
+
# Make sure there's a default application for testing
|
|
432
|
+
if Pyjo.UserAgent.Server.app() is None:
|
|
433
|
+
Pyjo.UserAgent.Server.app(app)
|
|
434
|
+
|
|
435
|
+
# Left out from Mojolicious: strict mode of Mojo::Base, which Python does not need
|
|
436
|
+
return functions
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
class _DSL:
|
|
440
|
+
def __init__(self, app: Pyjoyment_Lite, routes: Pyjoyment.Routes.Route.object) -> None:
|
|
441
|
+
self.app = app
|
|
442
|
+
self.routes: Pyjoyment.Routes.Route.object = routes
|
|
443
|
+
self.root: Pyjoyment.Routes.Route.object = routes
|
|
444
|
+
|
|
445
|
+
def group(self, cb: Callable[[], Any] | None = None) -> Any:
|
|
446
|
+
if cb is None:
|
|
447
|
+
return self._group()
|
|
448
|
+
with self._group():
|
|
449
|
+
cb()
|
|
450
|
+
return None
|
|
451
|
+
|
|
452
|
+
def route(self, name: str) -> Callable[..., Pyjoyment.Routes.Route.object]:
|
|
453
|
+
def route(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object:
|
|
454
|
+
return getattr(self.routes, name)(*args, **constraints)
|
|
455
|
+
|
|
456
|
+
return route
|
|
457
|
+
|
|
458
|
+
def under(self, *args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object:
|
|
459
|
+
self.routes = self.root.under(*args, **constraints)
|
|
460
|
+
return self.routes
|
|
461
|
+
|
|
462
|
+
@contextlib.contextmanager
|
|
463
|
+
def _group(self) -> Generator[None]:
|
|
464
|
+
old, self.root = self.root, self.routes
|
|
465
|
+
try:
|
|
466
|
+
yield
|
|
467
|
+
finally:
|
|
468
|
+
self.routes, self.root = self.root, old
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
if TYPE_CHECKING:
|
|
472
|
+
app: Pyjoyment_Lite
|
|
473
|
+
|
|
474
|
+
def any(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
475
|
+
def del_(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
476
|
+
def delete(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
477
|
+
def get(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
478
|
+
def group(cb: Callable[[], Any] | None = None) -> Any: ...
|
|
479
|
+
def helper(name: str, cb: Callable[..., Any] | None = None) -> Any: ...
|
|
480
|
+
def hook(name: str, cb: Callable[..., Any] | None = None) -> Any: ...
|
|
481
|
+
def options(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
482
|
+
def patch(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
483
|
+
def plugin(name: str, conf: dict[str, Any] | None = None, /, **values: Any) -> Any: ...
|
|
484
|
+
def post(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
485
|
+
def put(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
486
|
+
def query(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
487
|
+
def template(name: str, cb: Callable[..., Any] | None = None) -> Any: ...
|
|
488
|
+
def under(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
489
|
+
def websocket(*args: Any, **constraints: Any) -> Pyjoyment.Routes.Route.object: ...
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
new = Pyjoyment_Lite.new
|
|
493
|
+
object = Pyjoyment_Lite
|
|
494
|
+
Lite = Pyjoyment_Lite
|