Pyjoyment 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. Pyjo/App/ASGI.py +735 -0
  2. Pyjo/App/CGI.py +773 -0
  3. Pyjo/App/FastCGI.py +631 -0
  4. Pyjo/App/Lambda.py +987 -0
  5. Pyjo/App/WSGI.py +427 -0
  6. Pyjo/App/__init__.py +409 -0
  7. Pyjo/Asset/File.py +377 -0
  8. Pyjo/Asset/Memory.py +235 -0
  9. Pyjo/Asset/__init__.py +200 -0
  10. Pyjo/Base.py +215 -0
  11. Pyjo/Cache.py +119 -0
  12. Pyjo/Collection.py +570 -0
  13. Pyjo/Content/MultiPart.py +325 -0
  14. Pyjo/Content/Single.py +193 -0
  15. Pyjo/Content/__init__.py +806 -0
  16. Pyjo/Cookie/Request.py +90 -0
  17. Pyjo/Cookie/Response.py +294 -0
  18. Pyjo/Cookie/__init__.py +143 -0
  19. Pyjo/DOM/CSS.py +1019 -0
  20. Pyjo/DOM/HTML.py +550 -0
  21. Pyjo/DOM/__init__.py +1329 -0
  22. Pyjo/Date.py +294 -0
  23. Pyjo/EventEmitter.py +522 -0
  24. Pyjo/Exception.py +494 -0
  25. Pyjo/File.py +959 -0
  26. Pyjo/GetOpt.py +605 -0
  27. Pyjo/Headers.py +760 -0
  28. Pyjo/HelloWorld.py +77 -0
  29. Pyjo/Home.py +109 -0
  30. Pyjo/IOLoop/ChildProcess.py +591 -0
  31. Pyjo/IOLoop/Client.py +736 -0
  32. Pyjo/IOLoop/Datagram.py +982 -0
  33. Pyjo/IOLoop/Messages.py +664 -0
  34. Pyjo/IOLoop/Server.py +458 -0
  35. Pyjo/IOLoop/Stdio.py +446 -0
  36. Pyjo/IOLoop/Stream.py +644 -0
  37. Pyjo/IOLoop/Subprocess.py +419 -0
  38. Pyjo/IOLoop/TLS.py +360 -0
  39. Pyjo/IOLoop/__init__.py +1036 -0
  40. Pyjo/IOLoop/certs/README.md +41 -0
  41. Pyjo/IOLoop/certs/ca.crt +20 -0
  42. Pyjo/IOLoop/certs/server.crt +21 -0
  43. Pyjo/IOLoop/certs/server.key +28 -0
  44. Pyjo/JSON/Pointer.py +135 -0
  45. Pyjo/JSON/RPC.py +772 -0
  46. Pyjo/JSON/Schema.py +2506 -0
  47. Pyjo/JSON/__init__.py +573 -0
  48. Pyjo/Loader.py +342 -0
  49. Pyjo/Log.py +723 -0
  50. Pyjo/Message/Request.py +725 -0
  51. Pyjo/Message/Response.py +476 -0
  52. Pyjo/Message/__init__.py +830 -0
  53. Pyjo/Parameters.py +472 -0
  54. Pyjo/Path.py +419 -0
  55. Pyjo/Promise.py +894 -0
  56. Pyjo/Prove.py +1305 -0
  57. Pyjo/Reactor/Asyncio.py +854 -0
  58. Pyjo/Reactor/Poll.py +439 -0
  59. Pyjo/Reactor/__init__.py +525 -0
  60. Pyjo/SSE.py +119 -0
  61. Pyjo/Server/ASGI.py +486 -0
  62. Pyjo/Server/CGI.py +259 -0
  63. Pyjo/Server/Daemon.py +1007 -0
  64. Pyjo/Server/FastCGI.py +517 -0
  65. Pyjo/Server/Hot/__init__.py +687 -0
  66. Pyjo/Server/Hot/__main__.py +5 -0
  67. Pyjo/Server/Lambda.py +641 -0
  68. Pyjo/Server/Prefork.py +917 -0
  69. Pyjo/Server/Prethread.py +394 -0
  70. Pyjo/Server/Reload/Backend/Poll.py +114 -0
  71. Pyjo/Server/Reload/Backend/__init__.py +126 -0
  72. Pyjo/Server/Reload/__init__.py +482 -0
  73. Pyjo/Server/Reload/__main__.py +5 -0
  74. Pyjo/Server/Stdio.py +228 -0
  75. Pyjo/Server/WSGI.py +349 -0
  76. Pyjo/Server/__init__.py +396 -0
  77. Pyjo/String/Bytes.py +532 -0
  78. Pyjo/String/HTML.py +210 -0
  79. Pyjo/String/Mixin.py +333 -0
  80. Pyjo/String/Template.py +470 -0
  81. Pyjo/String/Unicode.py +553 -0
  82. Pyjo/String/__init__.py +0 -0
  83. Pyjo/TOML.py +232 -0
  84. Pyjo/Template.py +1290 -0
  85. Pyjo/Test/Pyjo.py +1454 -0
  86. Pyjo/Test/__init__.py +1233 -0
  87. Pyjo/Test/pytest_plugin.py +305 -0
  88. Pyjo/Transaction/HTTP.py +374 -0
  89. Pyjo/Transaction/WebSocket.py +709 -0
  90. Pyjo/Transaction/__init__.py +429 -0
  91. Pyjo/URL/Template.py +459 -0
  92. Pyjo/URL/__init__.py +651 -0
  93. Pyjo/Upload.py +139 -0
  94. Pyjo/UserAgent/CookieJar.py +391 -0
  95. Pyjo/UserAgent/Proxy.py +138 -0
  96. Pyjo/UserAgent/Server.py +239 -0
  97. Pyjo/UserAgent/Transactor.py +766 -0
  98. Pyjo/UserAgent/__init__.py +1243 -0
  99. Pyjo/Util.py +3567 -0
  100. Pyjo/WebSocket.py +332 -0
  101. Pyjo/YAML.py +1167 -0
  102. Pyjo/__init__.py +441 -0
  103. Pyjo/__main__.py +35 -0
  104. Pyjo/py.typed +0 -0
  105. Pyjoyment/Command/Author/__init__.py +20 -0
  106. Pyjoyment/Command/Author/generate/__init__.py +106 -0
  107. Pyjoyment/Command/Author/generate/app.py +299 -0
  108. Pyjoyment/Command/Author/generate/dockerfile.py +281 -0
  109. Pyjoyment/Command/Author/generate/kubernetes.py +514 -0
  110. Pyjoyment/Command/Author/generate/lambda_zip.py +372 -0
  111. Pyjoyment/Command/Author/generate/lite_app.py +113 -0
  112. Pyjoyment/Command/Author/generate/plugin.py +267 -0
  113. Pyjoyment/Command/Author/generate/pyproject.py +123 -0
  114. Pyjoyment/Command/Author/inflate.py +101 -0
  115. Pyjoyment/Command/__init__.py +332 -0
  116. Pyjoyment/Command/asgi.py +92 -0
  117. Pyjoyment/Command/cgi.py +90 -0
  118. Pyjoyment/Command/daemon.py +160 -0
  119. Pyjoyment/Command/eval.py +128 -0
  120. Pyjoyment/Command/fastcgi.py +99 -0
  121. Pyjoyment/Command/get.py +305 -0
  122. Pyjoyment/Command/lambda.py +117 -0
  123. Pyjoyment/Command/prefork.py +181 -0
  124. Pyjoyment/Command/prethread.py +126 -0
  125. Pyjoyment/Command/routes.py +133 -0
  126. Pyjoyment/Command/stdio.py +92 -0
  127. Pyjoyment/Command/version.py +141 -0
  128. Pyjoyment/Command/wsgi.py +93 -0
  129. Pyjoyment/Commands.py +546 -0
  130. Pyjoyment/Controller.py +1307 -0
  131. Pyjoyment/HelloWorld.py +56 -0
  132. Pyjoyment/Lite.py +494 -0
  133. Pyjoyment/Plugin/AllowedHosts.py +205 -0
  134. Pyjoyment/Plugin/Config.py +248 -0
  135. Pyjoyment/Plugin/DefaultHelpers.py +1202 -0
  136. Pyjoyment/Plugin/EPLRenderer.py +134 -0
  137. Pyjoyment/Plugin/EPRenderer.py +212 -0
  138. Pyjoyment/Plugin/HeaderCondition.py +98 -0
  139. Pyjoyment/Plugin/Health.py +325 -0
  140. Pyjoyment/Plugin/JSONConfig.py +172 -0
  141. Pyjoyment/Plugin/Mount.py +223 -0
  142. Pyjoyment/Plugin/PyRenderer.py +209 -0
  143. Pyjoyment/Plugin/TOMLConfig.py +141 -0
  144. Pyjoyment/Plugin/TagHelpers.py +959 -0
  145. Pyjoyment/Plugin/YAMLConfig.py +178 -0
  146. Pyjoyment/Plugin/__init__.py +80 -0
  147. Pyjoyment/Plugins.py +215 -0
  148. Pyjoyment/Renderer.py +673 -0
  149. Pyjoyment/Routes/Match.py +251 -0
  150. Pyjoyment/Routes/Pattern.py +443 -0
  151. Pyjoyment/Routes/Route.py +827 -0
  152. Pyjoyment/Routes/__init__.py +419 -0
  153. Pyjoyment/Sessions.py +255 -0
  154. Pyjoyment/Static.py +421 -0
  155. Pyjoyment/Types.py +238 -0
  156. Pyjoyment/Validator/Validation.py +395 -0
  157. Pyjoyment/Validator/__init__.py +304 -0
  158. Pyjoyment/__init__.py +1083 -0
  159. Pyjoyment/__main__.py +5 -0
  160. Pyjoyment/py.typed +0 -0
  161. Pyjoyment/resources/public/favicon.ico +0 -0
  162. Pyjoyment/resources/public/favicon.svg +345 -0
  163. Pyjoyment/resources/public/pyjo/bootstrap/LICENSE +21 -0
  164. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.bundle.min.js.map +1 -0
  165. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.css +6 -0
  166. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.js +7 -0
  167. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.min.css.map +1 -0
  168. Pyjoyment/resources/public/pyjo/failunicat.png +0 -0
  169. Pyjoyment/resources/public/pyjo/failunicat.svg +50 -0
  170. Pyjoyment/resources/public/pyjo/highlight.js/LICENSE +29 -0
  171. Pyjoyment/resources/public/pyjo/highlight.js/highlight-mojo-dark.css +56 -0
  172. Pyjoyment/resources/public/pyjo/highlight.js/highlight.min.js +1262 -0
  173. Pyjoyment/resources/public/pyjo/highlight.js/pyjoyment.min.js +1 -0
  174. Pyjoyment/resources/public/pyjo/logo-black-4x.png +0 -0
  175. Pyjoyment/resources/public/pyjo/logo-black-4x.svg +346 -0
  176. Pyjoyment/resources/public/pyjo/logo-white-2x.png +0 -0
  177. Pyjoyment/resources/public/pyjo/logo-white-2x.svg +346 -0
  178. Pyjoyment/resources/public/pyjo/logo-white-4x.png +0 -0
  179. Pyjoyment/resources/public/pyjo/logo-white-4x.svg +346 -0
  180. Pyjoyment/resources/public/pyjo/logo-white.png +0 -0
  181. Pyjoyment/resources/public/pyjo/logo-white.svg +346 -0
  182. Pyjoyment/resources/public/pyjo/logo.png +0 -0
  183. Pyjoyment/resources/public/pyjo/logo.svg +346 -0
  184. Pyjoyment/resources/public/pyjo/notfound.png +0 -0
  185. Pyjoyment/resources/public/pyjo/notfound.svg +3 -0
  186. Pyjoyment/resources/public/pyjo/nounicat.png +0 -0
  187. Pyjoyment/resources/public/pyjo/nounicat.svg +5 -0
  188. Pyjoyment/resources/public/pyjo/pinstripe-dark.png +0 -0
  189. Pyjoyment/resources/public/pyjo/pinstripe-dark.svg +4 -0
  190. Pyjoyment/resources/public/pyjo/pinstripe-light.png +0 -0
  191. Pyjoyment/resources/public/pyjo/pinstripe-light.svg +4 -0
  192. Pyjoyment/resources/public/pyjo/pyjo.css +204 -0
  193. Pyjoyment/resources/templates/pyjo/debug.html.ep +346 -0
  194. Pyjoyment/resources/templates/pyjo/exception.html.ep +32 -0
  195. Pyjoyment/resources/templates/pyjo/not_found.html.ep +45 -0
  196. pyjoyment-0.1.0.dist-info/METADATA +376 -0
  197. pyjoyment-0.1.0.dist-info/RECORD +200 -0
  198. pyjoyment-0.1.0.dist-info/WHEEL +4 -0
  199. pyjoyment-0.1.0.dist-info/entry_points.txt +8 -0
  200. pyjoyment-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -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