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,93 @@
1
+ """
2
+ Pyjoyment.Command.wsgi - WSGI command
3
+ =====================================
4
+ ::
5
+
6
+ Usage: APPLICATION wsgi [OPTIONS]
7
+
8
+ application = app.start('wsgi')
9
+ PYJO_WSGI=1 gunicorn myapp:application
10
+
11
+ Options:
12
+ -h, --help Show this summary of available options
13
+ --home <path> Path to home directory of your application,
14
+ defaults to the value of PYJO_HOME or
15
+ auto-detection
16
+ --log-format <name> Format of log messages, "text" or "json",
17
+ defaults to the value of PYJO_LOG_FORMAT or
18
+ "text"
19
+ -m, --mode <name> Operating mode for your application, defaults to
20
+ the value of PYJO_MODE or "development"
21
+
22
+ :mod:`Pyjoyment.Command.wsgi` starts applications with the
23
+ :mod:`Pyjo.Server.WSGI` backend: it returns the WSGI application, which a
24
+ script keeps for the WSGI server that imports it. The script of an application
25
+ that ends with ``application = app.start()`` runs this command when the
26
+ ``PYJO_WSGI`` environment variable is set, and the command of its arguments
27
+ otherwise.
28
+
29
+ This is a core command, that means it is always enabled and its code a good
30
+ example for learning to build new commands, you're welcome to fork it.
31
+
32
+ See :mod:`Pyjoyment.Commands` for a list of commands that are available by
33
+ default.
34
+
35
+ See also
36
+ --------
37
+
38
+ :mod:`Pyjoyment`, https://mojolicious.org.
39
+
40
+ Ported from `Mojolicious::Command::psgi
41
+ <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojolicious/Command/psgi.pm>`_ of Mojolicious 9.49, for WSGI
42
+ instead of PSGI.
43
+
44
+ Classes
45
+ -------
46
+ """
47
+
48
+ # Renamed from Mojolicious: wsgi, where Mojo has psgi, as WSGI is the PSGI of Python
49
+
50
+ import Pyjo.Server.WSGI
51
+ import Pyjoyment.Command
52
+
53
+ from Pyjo.Util import extract_usage
54
+
55
+ from typing import Any
56
+
57
+
58
+ class Pyjoyment_Command_wsgi(Pyjoyment.Command.object):
59
+ """
60
+ :mod:`Pyjoyment.Command.wsgi` inherits all attributes and methods from
61
+ :mod:`Pyjoyment.Command` and implements the following new ones.
62
+ """
63
+
64
+ description = 'Start application with WSGI'
65
+ """::
66
+
67
+ description = wsgi.description
68
+ wsgi.description = 'Foo'
69
+
70
+ Short description of this command, used for the command list.
71
+ """
72
+
73
+ usage = extract_usage()
74
+ """::
75
+
76
+ usage = wsgi.usage
77
+ wsgi.usage = 'Foo'
78
+
79
+ Usage information for this command, used for the help screen.
80
+ """
81
+
82
+ def run(self, *args: str) -> Any:
83
+ """::
84
+
85
+ application = wsgi.run()
86
+
87
+ Run this command.
88
+ """
89
+ return Pyjo.Server.WSGI.new(app=self.app).to_wsgi_app()
90
+
91
+
92
+ new = Pyjoyment_Command_wsgi.new
93
+ object = Pyjoyment_Command_wsgi
Pyjoyment/Commands.py ADDED
@@ -0,0 +1,546 @@
1
+ """
2
+ Pyjoyment.Commands - Command line interface
3
+ ===========================================
4
+ ::
5
+
6
+ Usage: APPLICATION COMMAND [OPTIONS]
7
+
8
+ pyjo version
9
+ pyjo generate lite-app
10
+ ./myapp.py daemon -m production -l http://*:8080
11
+ ./myapp.py get /foo
12
+ ./myapp.py routes -v
13
+
14
+ Tip: CGI, FastCGI and AWS Lambda environments, and WSGI and ASGI ones with
15
+ PYJO_WSGI and PYJO_ASGI, can be automatically detected very often and
16
+ work without commands.
17
+
18
+ Options (for all commands):
19
+ -h, --help Get more information on a specific command
20
+ --home <path> Path to home directory of your application,
21
+ defaults to the value of PYJO_HOME or
22
+ auto-detection
23
+ --log-format <name> Format of log messages, "text" or "json",
24
+ defaults to the value of PYJO_LOG_FORMAT or
25
+ "text"
26
+ -m, --mode <name> Operating mode for your application, defaults to
27
+ the value of PYJO_MODE or "development"
28
+
29
+ :mod:`Pyjoyment.Commands` is the interactive command line interface for the
30
+ :mod:`Pyjoyment` framework. It will automatically detect available commands in
31
+ the ``Pyjoyment.Command`` and ``Pyjoyment.Command.Author`` namespaces.
32
+
33
+ The command line interface of :mod:`Pyjoyment.HelloWorld` is the ``pyjo``
34
+ script, or ``python -m Pyjoyment``.
35
+
36
+ Commands
37
+ --------
38
+
39
+ These commands are available by default.
40
+
41
+ asgi
42
+ ~~~~
43
+ ::
44
+
45
+ application = app.start('asgi')
46
+
47
+ Use :mod:`Pyjoyment.Command.asgi` to start application with ASGI backend,
48
+ auto detected with the ``PYJO_ASGI`` environment variable.
49
+
50
+ cgi
51
+ ~~~
52
+ ::
53
+
54
+ $ ./myapp.py cgi
55
+
56
+ Use :mod:`Pyjoyment.Command.cgi` to start application with CGI backend,
57
+ usually auto detected.
58
+
59
+ daemon
60
+ ~~~~~~
61
+ ::
62
+
63
+ $ ./myapp.py daemon
64
+
65
+ Use :mod:`Pyjoyment.Command.daemon` to start application with standalone
66
+ HTTP and WebSocket server.
67
+
68
+ eval
69
+ ~~~~
70
+ ::
71
+
72
+ $ ./myapp.py eval 'print(app.home)'
73
+
74
+ Use :mod:`Pyjoyment.Command.eval` to run code against application.
75
+
76
+ fastcgi
77
+ ~~~~~~~
78
+ ::
79
+
80
+ $ ./myapp.py fastcgi
81
+
82
+ Use :mod:`Pyjoyment.Command.fastcgi` to start application with the FastCGI
83
+ of a web server that starts it, usually auto detected.
84
+
85
+ generate
86
+ ~~~~~~~~
87
+ ::
88
+
89
+ $ pyjo generate
90
+ $ pyjo generate help
91
+ $ ./myapp.py generate help
92
+
93
+ List available generator commands with short descriptions. ::
94
+
95
+ $ pyjo generate help <generator>
96
+ $ ./myapp.py generate help <generator>
97
+
98
+ List available options for generator command with short descriptions.
99
+
100
+ generate app
101
+ ~~~~~~~~~~~~
102
+ ::
103
+
104
+ $ pyjo generate app <AppName>
105
+
106
+ Use :mod:`Pyjoyment.Command.Author.generate.app` to generate application
107
+ directory structure for a fully functional :mod:`Pyjoyment` application.
108
+
109
+ generate dockerfile
110
+ ~~~~~~~~~~~~~~~~~~~
111
+ ::
112
+
113
+ $ ./myapp.py generate dockerfile
114
+ $ python -m MyApp generate dockerfile
115
+
116
+ Use :mod:`Pyjoyment.Command.Author.generate.dockerfile` to generate
117
+ ``Dockerfile`` for application.
118
+
119
+ generate kubernetes
120
+ ~~~~~~~~~~~~~~~~~~~
121
+ ::
122
+
123
+ $ ./myapp.py generate kubernetes
124
+ $ python -m MyApp generate kubernetes
125
+
126
+ Use :mod:`Pyjoyment.Command.Author.generate.kubernetes` to generate the
127
+ manifests of Kubernetes for application, with the overlays of Kustomize for
128
+ development and production.
129
+
130
+ generate lambda-zip
131
+ ~~~~~~~~~~~~~~~~~~~
132
+ ::
133
+
134
+ $ ./myapp.py generate lambda-zip
135
+ $ python -m MyApp generate lambda-zip
136
+
137
+ Use :mod:`Pyjoyment.Command.Author.generate.lambda_zip` to generate the ZIP
138
+ files of an event function of AWS Lambda on a custom runtime, with a layer of
139
+ Python.
140
+
141
+ generate lite-app
142
+ ~~~~~~~~~~~~~~~~~
143
+ ::
144
+
145
+ $ pyjo generate lite-app
146
+
147
+ Use :mod:`Pyjoyment.Command.Author.generate.lite_app` to generate a fully
148
+ functional :mod:`Pyjoyment.Lite` application.
149
+
150
+ generate plugin
151
+ ~~~~~~~~~~~~~~~
152
+ ::
153
+
154
+ $ pyjo generate plugin <PluginName>
155
+
156
+ Use :mod:`Pyjoyment.Command.Author.generate.plugin` to generate directory
157
+ structure for a fully functional :mod:`Pyjoyment` plugin.
158
+
159
+ generate pyproject
160
+ ~~~~~~~~~~~~~~~~~~
161
+ ::
162
+
163
+ $ pyjo generate pyproject
164
+ $ ./myapp.py generate pyproject
165
+
166
+ Use :mod:`Pyjoyment.Command.Author.generate.pyproject` to generate
167
+ ``pyproject.toml`` file for application.
168
+
169
+ get
170
+ ~~~
171
+ ::
172
+
173
+ $ pyjo get https://mojolicious.org
174
+ $ ./myapp.py get /foo
175
+
176
+ Use :mod:`Pyjoyment.Command.get` to perform requests to remote host or local
177
+ application.
178
+
179
+ help
180
+ ~~~~
181
+ ::
182
+
183
+ $ pyjo
184
+ $ pyjo help
185
+ $ ./myapp.py help
186
+
187
+ List available commands with short descriptions. ::
188
+
189
+ $ pyjo help <command>
190
+ $ ./myapp.py help <command>
191
+
192
+ List available options for the command with short descriptions.
193
+
194
+ inflate
195
+ ~~~~~~~
196
+ ::
197
+
198
+ $ ./myapp.py inflate
199
+
200
+ Use :mod:`Pyjoyment.Command.Author.inflate` to turn templates and static files
201
+ embedded in the ``DATA`` sections of your application into real files.
202
+
203
+ lambda
204
+ ~~~~~~
205
+ ::
206
+
207
+ handler = app.start('lambda')
208
+
209
+ Use :mod:`Pyjoyment.Command.lambda` to start application as an event function
210
+ of AWS Lambda, auto detected with the ``AWS_LAMBDA_RUNTIME_API`` environment
211
+ variable.
212
+
213
+ prefork
214
+ ~~~~~~~
215
+ ::
216
+
217
+ $ ./myapp.py prefork
218
+
219
+ Use :mod:`Pyjoyment.Command.prefork` to start application with standalone
220
+ pre-forking HTTP and WebSocket server.
221
+
222
+ prethread
223
+ ~~~~~~~~~
224
+ ::
225
+
226
+ $ ./myapp.py prethread
227
+
228
+ Use :mod:`Pyjoyment.Command.prethread` to start application with standalone
229
+ pre-threading HTTP and WebSocket server.
230
+
231
+ routes
232
+ ~~~~~~
233
+ ::
234
+
235
+ $ ./myapp.py routes
236
+
237
+ Use :mod:`Pyjoyment.Command.routes` to list application routes.
238
+
239
+ stdio
240
+ ~~~~~
241
+ ::
242
+
243
+ $ ./myapp.py stdio
244
+
245
+ Use :mod:`Pyjoyment.Command.stdio` to start application on the standard input
246
+ and output, with the protocol that the application or a plugin gives.
247
+
248
+ version
249
+ ~~~~~~~
250
+ ::
251
+
252
+ $ pyjo version
253
+ $ ./myapp.py version
254
+
255
+ Use :mod:`Pyjoyment.Command.version` to show version information for
256
+ available core and optional modules, very useful for debugging.
257
+
258
+ wsgi
259
+ ~~~~
260
+ ::
261
+
262
+ application = app.start('wsgi')
263
+
264
+ Use :mod:`Pyjoyment.Command.wsgi` to start application with WSGI backend,
265
+ auto detected with the ``PYJO_WSGI`` environment variable.
266
+
267
+ See also
268
+ --------
269
+
270
+ :mod:`Pyjoyment`, https://mojolicious.org.
271
+
272
+ Ported from `Mojolicious::Commands <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojolicious/Commands.pm>`_
273
+ of Mojolicious 9.49.
274
+
275
+ Classes
276
+ -------
277
+ """
278
+
279
+ # Left out from Mojolicious: the command cpanify, as uv publish uploads distributions to PyPI
280
+
281
+ import Pyjo.Exception
282
+ import Pyjo.GetOpt
283
+ import Pyjo.Loader
284
+ import Pyjo.Server
285
+ import Pyjoyment.Command
286
+
287
+ from Pyjo.Util import getopt, setenv, tablify
288
+
289
+ import errno
290
+ import os
291
+ import re
292
+ import socket
293
+ import sys
294
+
295
+ from collections.abc import Sequence
296
+ from typing import Any
297
+
298
+
299
+ class Pyjoyment_Commands(Pyjoyment.Command.object):
300
+ """
301
+ :mod:`Pyjoyment.Commands` inherits all attributes and methods from
302
+ :mod:`Pyjoyment.Command` and implements the following new ones.
303
+ """
304
+
305
+ hint: str = "\nSee 'APPLICATION help COMMAND' for more information on a specific command.\n"
306
+ """::
307
+
308
+ hint = commands.hint
309
+ commands.hint = 'Foo'
310
+
311
+ Short hint shown after listing available commands.
312
+ """
313
+
314
+ def __init__(self, **kwargs: Any) -> None:
315
+ self._message: str | None = None
316
+ self.namespaces: list[str] = ['Pyjoyment.Command.Author', 'Pyjoyment.Command']
317
+ """::
318
+
319
+ namespaces = commands.namespaces
320
+ commands.namespaces = ['MyApp.Command']
321
+
322
+ Namespaces to load commands from, defaults to
323
+ ``Pyjoyment.Command.Author`` and ``Pyjoyment.Command``. ::
324
+
325
+ # Add another namespace to load commands from
326
+ commands.namespaces.append('MyApp.Command')
327
+ """
328
+ super().__init__(**kwargs)
329
+
330
+ @property
331
+ def message(self) -> str:
332
+ """::
333
+
334
+ msg = commands.message
335
+ commands.message = 'Hello World!'
336
+
337
+ Short usage message shown before listing available commands.
338
+ """
339
+ if self._message is None:
340
+ self._message = self.extract_usage() + '\nCommands:\n'
341
+ return self._message
342
+
343
+ @message.setter
344
+ def message(self, value: str) -> None:
345
+ self._message = value
346
+
347
+ @staticmethod
348
+ def detect() -> str | None:
349
+ """::
350
+
351
+ env = commands.detect()
352
+
353
+ Try to detect environment, or return ``None`` if none could be
354
+ detected.
355
+ """
356
+ # WSGI
357
+ # Renamed from Mojolicious: PYJO_WSGI, as WSGI has no environment variable of its own, where Mojo detects PSGI
358
+ # with PLACK_ENV
359
+ if os.environ.get('PYJO_WSGI') is not None:
360
+ return 'wsgi'
361
+
362
+ # Not in Mojolicious: ASGI, with PYJO_ASGI
363
+ if os.environ.get('PYJO_ASGI') is not None:
364
+ return 'asgi'
365
+
366
+ # Not in Mojolicious: an event function of AWS Lambda, whose runtime has the endpoint of its API
367
+ if os.environ.get('AWS_LAMBDA_RUNTIME_API') is not None:
368
+ return 'lambda'
369
+
370
+ # Not in Mojolicious: FastCGI, whose web server gives the application its listen socket as the file descriptor 0
371
+ if _fastcgi():
372
+ return 'fastcgi'
373
+
374
+ # CGI
375
+ if os.environ.get('PATH_INFO') is not None or os.environ.get('GATEWAY_INTERFACE') is not None:
376
+ return 'cgi'
377
+
378
+ # Nothing
379
+ return None
380
+
381
+ def run(self, name: str | None = None, *args: str) -> Any:
382
+ """::
383
+
384
+ commands.run()
385
+ commands.run(*sys.argv[1:])
386
+
387
+ Load and run commands. Automatic deployment environment detection can
388
+ be disabled with the ``PYJO_NO_DETECT`` environment variable. A
389
+ detected environment, CGI, FastCGI, AWS Lambda, or WSGI and ASGI with
390
+ the ``PYJO_WSGI`` and ``PYJO_ASGI`` environment variables, runs its
391
+ command, whatever the arguments are, as a WSGI or an ASGI server and
392
+ AWS Lambda import the script of the application with their own
393
+ arguments. ::
394
+
395
+ # The WSGI application of a script, for PYJO_WSGI=1 gunicorn myapp:application
396
+ application = app.start()
397
+ """
398
+ # Application loader
399
+ # Not in Mojolicious: the application for Pyjo.Server.load_app, which Mojo returns from the script
400
+ if os.environ.get('PYJO_APP_LOADER') is not None:
401
+ Pyjo.Server.loaded_app = self.app
402
+ return self.app
403
+
404
+ # Try to detect environment
405
+ if not os.environ.get('PYJO_NO_DETECT') and (env := self.detect()):
406
+ name = env
407
+
408
+ # Run command
409
+ arguments = list(args)
410
+ if name and re.fullmatch(r'\w[\w-]+', name) and (name != 'help' or arguments):
411
+ # Help
412
+ help = name == 'help'
413
+ if help:
414
+ name = arguments.pop(0)
415
+ saved = os.environ.get('PYJO_HELP')
416
+ help = bool(saved) or help
417
+ setenv('PYJO_HELP', '1' if help else None)
418
+ try:
419
+ name = name.replace('-', '_')
420
+
421
+ # Remove options shared by all commands before loading the command
422
+ _args(arguments)
423
+ module = None
424
+ for namespace in self.namespaces:
425
+ if module := _command(f'{namespace}.{name}', True):
426
+ break
427
+
428
+ # Unknown command
429
+ if not module:
430
+ raise Pyjo.Exception.new(f'Unknown command "{name}", maybe you need to install it?\n')
431
+
432
+ # Run command
433
+ app = self.app
434
+ command = module.new(app=app)
435
+ if help:
436
+ return command.help(*arguments)
437
+ app.plugins.emit_hook('before_command', command, arguments)
438
+ return command.run(*arguments)
439
+ finally:
440
+ setenv('PYJO_HELP', saved)
441
+
442
+ elif name and name not in ('help', '--help', '-h'):
443
+ raise Pyjo.Exception.new(f'Invalid command "{name}".\n')
444
+
445
+ # Hide list for tests
446
+ # Renamed from Mojolicious: PYJO_HARNESS_ACTIVE, which Pyjo.Prove sets, as prove sets HARNESS_ACTIVE
447
+ if os.environ.get('PYJO_HARNESS_ACTIVE', '0') not in ('', '0'):
448
+ return True
449
+
450
+ # Find all available commands
451
+ all: dict[str, str] = {}
452
+ for namespace in self.namespaces:
453
+ for module in [*Pyjo.Loader.find_modules(namespace), *Pyjo.Loader.find_packages(namespace)]:
454
+ if (cls := _command(module)) is not None:
455
+ all.setdefault(module[len(namespace) + 1 :], cls.new().description)
456
+
457
+ rows = [[' ' + re.sub(r'(?<!^)_', '-', name), all[name]] for name in sorted(all)]
458
+ print(self.message, tablify(rows), self.hint, sep='', end='')
459
+ return True
460
+
461
+ @staticmethod
462
+ def start_app(app: str | type, *args: str) -> Any:
463
+ """::
464
+
465
+ Pyjoyment.Commands.start_app('MyApp')
466
+ Pyjoyment.Commands.start_app('MyApp', *sys.argv[1:])
467
+
468
+ Load application from class and start the command line interface for
469
+ it. Note that the options ``-h``/``--help``, ``--home`` and
470
+ ``-m``/``--mode``, which are shared by all commands, will be parsed
471
+ from ``sys.argv`` when the module is imported. ::
472
+
473
+ # Always start daemon for application
474
+ Pyjoyment.Commands.start_app('MyApp', 'daemon', '-l', 'http://*:8080')
475
+ """
476
+ application: Any = Pyjo.Server.new().build_app(app)
477
+ return application.start(*args)
478
+
479
+
480
+ # Command line options for PYJO_HELP, PYJO_HOME, PYJO_LOG_FORMAT and PYJO_MODE
481
+ def _args(args: list[str]) -> None:
482
+ if not Pyjoyment_Commands.detect():
483
+ getopt(
484
+ args,
485
+ {
486
+ 'h|help': Pyjo.GetOpt.env('PYJO_HELP'),
487
+ 'home=s': Pyjo.GetOpt.env('PYJO_HOME'),
488
+ # Not in Mojolicious: the format of the log messages of Pyjo.Log, text or JSON
489
+ 'log-format=s': Pyjo.GetOpt.env('PYJO_LOG_FORMAT'),
490
+ 'm|mode=s': Pyjo.GetOpt.env('PYJO_MODE'),
491
+ },
492
+ pass_through=True,
493
+ )
494
+
495
+
496
+ def _command(module: str, fatal: bool = False) -> type[Pyjoyment.Command.object] | None:
497
+ if not (e := Pyjo.Loader.load_class(module)):
498
+ cls = Pyjo.Loader._class(module) # pyright: ignore[reportPrivateUsage]
499
+ return cls if cls is not None and issubclass(cls, Pyjoyment.Command.object) else None
500
+ if fatal and isinstance(e, Exception):
501
+ raise e
502
+ return None
503
+
504
+
505
+ # Not in Mojolicious: the file descriptor 0 is a listen socket, which has no peer, as the specification of FastCGI tells
506
+ # an application to check
507
+ def _fastcgi() -> bool:
508
+ try:
509
+ fd = os.dup(0)
510
+ except OSError:
511
+ return False
512
+ try:
513
+ handle = socket.socket(fileno=fd)
514
+ except OSError:
515
+ os.close(fd)
516
+ return False
517
+ with handle:
518
+ try:
519
+ handle.getpeername()
520
+ except OSError as e:
521
+ return e.errno == errno.ENOTCONN
522
+ return False
523
+
524
+
525
+ # Do not remove options from sys.argv
526
+ _args(sys.argv[1:])
527
+
528
+
529
+ # Not in Mojolicious: script/mojo, the command line interface of Pyjoyment.HelloWorld
530
+ def main(argv: Sequence[str] | None = None) -> None:
531
+ """::
532
+
533
+ main(['version'])
534
+
535
+ The command line interface of the ``pyjo`` script: start the command line
536
+ interface of :mod:`Pyjoyment.HelloWorld`.
537
+ """
538
+ start_app('Pyjoyment.HelloWorld', *(argv or ()))
539
+
540
+
541
+ start_app = Pyjoyment_Commands.start_app
542
+
543
+
544
+ new = Pyjoyment_Commands.new
545
+ object = Pyjoyment_Commands
546
+ Commands = Pyjoyment_Commands