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
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjoyment.Command.eval - Eval command
|
|
3
|
+
=====================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
Usage: APPLICATION eval [OPTIONS] CODE
|
|
7
|
+
|
|
8
|
+
./myapp.py eval 'print(app.ua.get("/").result.body)'
|
|
9
|
+
./myapp.py eval 'print(*sorted(app.renderer.helpers), sep="\\n")'
|
|
10
|
+
./myapp.py eval -v 'app.home'
|
|
11
|
+
./myapp.py eval -V 'app.renderer.paths'
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
-h, --help Show this summary of available options
|
|
15
|
+
--home <path> Path to home directory of your application,
|
|
16
|
+
defaults to the value of PYJO_HOME or
|
|
17
|
+
auto-detection
|
|
18
|
+
--log-format <name> Format of log messages, "text" or "json",
|
|
19
|
+
defaults to the value of PYJO_LOG_FORMAT or
|
|
20
|
+
"text"
|
|
21
|
+
-m, --mode <name> Operating mode for your application, defaults to
|
|
22
|
+
the value of PYJO_MODE or "development"
|
|
23
|
+
-v, --verbose Print return value to sys.stdout
|
|
24
|
+
-V Print returned data structure to sys.stdout
|
|
25
|
+
|
|
26
|
+
:mod:`Pyjoyment.Command.eval` runs code against applications. The code
|
|
27
|
+
is Python, with the application as ``app``, and its result is the value of its
|
|
28
|
+
last expression. If the result is a promise (then-able), it will wait until
|
|
29
|
+
the promise is fulfilled or rejected and the result is returned.
|
|
30
|
+
|
|
31
|
+
This is a core command, that means it is always enabled and its code a good
|
|
32
|
+
example for learning to build new commands, you're welcome to fork it.
|
|
33
|
+
|
|
34
|
+
See :mod:`Pyjoyment.Commands` for a list of commands that are available by
|
|
35
|
+
default.
|
|
36
|
+
|
|
37
|
+
See also
|
|
38
|
+
--------
|
|
39
|
+
|
|
40
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
41
|
+
|
|
42
|
+
Ported from `Mojolicious::Command::eval
|
|
43
|
+
<https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojolicious/Command/eval.pm>`_ of Mojolicious 9.49.
|
|
44
|
+
|
|
45
|
+
Classes
|
|
46
|
+
-------
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
import Pyjo.Exception
|
|
50
|
+
import Pyjo.Promise
|
|
51
|
+
import Pyjoyment.Command
|
|
52
|
+
|
|
53
|
+
from Pyjo.Util import extract_usage, getopt
|
|
54
|
+
|
|
55
|
+
import ast
|
|
56
|
+
|
|
57
|
+
from typing import Any
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Pyjoyment_Command_eval(Pyjoyment.Command.object):
|
|
61
|
+
"""
|
|
62
|
+
:mod:`Pyjoyment.Command.eval` inherits all attributes and methods from
|
|
63
|
+
:mod:`Pyjoyment.Command` and implements the following new ones.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
description = 'Run code against application'
|
|
67
|
+
"""::
|
|
68
|
+
|
|
69
|
+
description = eval.description
|
|
70
|
+
eval.description = 'Foo'
|
|
71
|
+
|
|
72
|
+
Short description of this command, used for the command list.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
usage = extract_usage()
|
|
76
|
+
"""::
|
|
77
|
+
|
|
78
|
+
usage = eval.usage
|
|
79
|
+
eval.usage = 'Foo'
|
|
80
|
+
|
|
81
|
+
Usage information for this command, used for the help screen.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def run(self, *args: str) -> Any:
|
|
85
|
+
"""::
|
|
86
|
+
|
|
87
|
+
eval.run(*sys.argv[1:])
|
|
88
|
+
|
|
89
|
+
Run this command.
|
|
90
|
+
"""
|
|
91
|
+
arguments = list(args)
|
|
92
|
+
if not (opts := getopt(arguments, {'v|verbose': None, 'V': None})):
|
|
93
|
+
raise Pyjo.Exception.new(self.usage)
|
|
94
|
+
code = arguments.pop(0) if arguments else ''
|
|
95
|
+
|
|
96
|
+
# Run code against application
|
|
97
|
+
app = self.app
|
|
98
|
+
# Unlike Mojolicious: Python code, whose value is the one of its last expression, as the value of Perl code is
|
|
99
|
+
# the one of its last statement
|
|
100
|
+
result = _eval(code, {'__name__': '__main__', 'app': app})
|
|
101
|
+
|
|
102
|
+
# Handle promises
|
|
103
|
+
values: list[Any] = []
|
|
104
|
+
errors: list[Any] = []
|
|
105
|
+
Pyjo.Promise.resolve(result).then(values.append, errors.append).wait()
|
|
106
|
+
if errors:
|
|
107
|
+
raise errors[0] if isinstance(errors[0], BaseException) else Pyjo.Exception.new(str(errors[0]))
|
|
108
|
+
result = values[0] if values else None
|
|
109
|
+
if result is None or not (opts.verbose or opts.V):
|
|
110
|
+
return result
|
|
111
|
+
if opts.V:
|
|
112
|
+
print(app.dumper(result), end='')
|
|
113
|
+
else:
|
|
114
|
+
print(result)
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _eval(code: str, namespace: dict[str, Any]) -> Any:
|
|
119
|
+
tree = ast.parse(code, '-e')
|
|
120
|
+
last = tree.body.pop() if tree.body and isinstance(tree.body[-1], ast.Expr) else None
|
|
121
|
+
exec(compile(tree, '-e', 'exec'), namespace)
|
|
122
|
+
if not isinstance(last, ast.Expr):
|
|
123
|
+
return None
|
|
124
|
+
return eval(compile(ast.Expression(last.value), '-e', 'eval'), namespace)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
new = Pyjoyment_Command_eval.new
|
|
128
|
+
object = Pyjoyment_Command_eval
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjoyment.Command.fastcgi - FastCGI command
|
|
3
|
+
===========================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
Usage: APPLICATION fastcgi [OPTIONS]
|
|
7
|
+
|
|
8
|
+
./myapp.py fastcgi
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
-h, --help Show this summary of available options
|
|
12
|
+
--home <path> Path to home directory of your application,
|
|
13
|
+
defaults to the value of PYJO_HOME or
|
|
14
|
+
auto-detection
|
|
15
|
+
--log-format <name> Format of log messages, "text" or "json",
|
|
16
|
+
defaults to the value of PYJO_LOG_FORMAT or
|
|
17
|
+
"text"
|
|
18
|
+
-m, --mode <name> Operating mode for your application, defaults to
|
|
19
|
+
the value of PYJO_MODE or "development"
|
|
20
|
+
|
|
21
|
+
:mod:`Pyjoyment.Command.fastcgi` starts applications that a web server starts
|
|
22
|
+
as FastCGI applications, as Apache with ``mod_fcgid`` and lighttpd do: it
|
|
23
|
+
serves the listen socket that the web server gives the application as the
|
|
24
|
+
file descriptor ``0``, with :mod:`Pyjo.Server.Daemon` and
|
|
25
|
+
:mod:`Pyjo.Server.FastCGI`, and is detected by :mod:`Pyjoyment.Commands`
|
|
26
|
+
when the file descriptor ``0`` is such a socket. A FastCGI application that
|
|
27
|
+
listens on a socket of its own, which the web server connects to, is started
|
|
28
|
+
with the commands ``daemon`` and ``prefork`` and a listen location
|
|
29
|
+
``fcgi://`` or ``fcgi+unix://``.
|
|
30
|
+
|
|
31
|
+
This is a core command, that means it is always enabled and its code a good
|
|
32
|
+
example for learning to build new commands, you're welcome to fork it.
|
|
33
|
+
|
|
34
|
+
See :mod:`Pyjoyment.Commands` for a list of commands that are available by
|
|
35
|
+
default.
|
|
36
|
+
|
|
37
|
+
See also
|
|
38
|
+
--------
|
|
39
|
+
|
|
40
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
41
|
+
|
|
42
|
+
It has no counterpart in Mojolicious; :mod:`Pyjoyment.Command.cgi` is the
|
|
43
|
+
counterpart of `Mojolicious::Command::cgi
|
|
44
|
+
<https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojolicious/Command/cgi.pm>`_.
|
|
45
|
+
|
|
46
|
+
Classes
|
|
47
|
+
-------
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
import Pyjo.Exception
|
|
51
|
+
import Pyjo.Server.Daemon
|
|
52
|
+
import Pyjoyment.Command
|
|
53
|
+
|
|
54
|
+
from Pyjo.Util import extract_usage, getopt
|
|
55
|
+
|
|
56
|
+
from typing import Any
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Pyjoyment_Command_fastcgi(Pyjoyment.Command.object):
|
|
60
|
+
"""
|
|
61
|
+
:mod:`Pyjoyment.Command.fastcgi` inherits all attributes and methods from
|
|
62
|
+
:mod:`Pyjoyment.Command` and implements the following new ones.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
description = 'Start application with FastCGI of the web server'
|
|
66
|
+
"""::
|
|
67
|
+
|
|
68
|
+
description = fastcgi.description
|
|
69
|
+
fastcgi.description = 'Foo'
|
|
70
|
+
|
|
71
|
+
Short description of this command, used for the command list.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
usage = extract_usage()
|
|
75
|
+
"""::
|
|
76
|
+
|
|
77
|
+
usage = fastcgi.usage
|
|
78
|
+
fastcgi.usage = 'Foo'
|
|
79
|
+
|
|
80
|
+
Usage information for this command, used for the help screen.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def run(self, *args: str) -> Any:
|
|
84
|
+
"""::
|
|
85
|
+
|
|
86
|
+
fastcgi.run(*sys.argv[1:])
|
|
87
|
+
|
|
88
|
+
Run this command.
|
|
89
|
+
"""
|
|
90
|
+
arguments = list(args)
|
|
91
|
+
if not getopt(arguments, {}) or len(arguments):
|
|
92
|
+
raise Pyjo.Exception.new(self.usage)
|
|
93
|
+
|
|
94
|
+
# The listen socket of the web server, silent, as the web server does not read the output of the application
|
|
95
|
+
Pyjo.Server.Daemon.new(app=self.app, listen=['fcgi://*?fd=0'], silent=True).run()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
new = Pyjoyment_Command_fastcgi.new
|
|
99
|
+
object = Pyjoyment_Command_fastcgi
|
Pyjoyment/Command/get.py
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjoyment.Command.get - Get command
|
|
3
|
+
===================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
Usage: APPLICATION get [OPTIONS] URL [SELECTOR|JSON-POINTER] [COMMANDS]
|
|
7
|
+
|
|
8
|
+
./myapp.py get /
|
|
9
|
+
./myapp.py get -H 'Accept: text/html' /hello.html 'head > title' text
|
|
10
|
+
./myapp.py get //sri:secr3t@/secrets.json /1/content
|
|
11
|
+
pyjo get mojolicious.org
|
|
12
|
+
pyjo get -v -r -o 25 -i 50 google.com
|
|
13
|
+
pyjo get -v -H 'Host: mojolicious.org' -H 'Accept: */*' mojolicious.org
|
|
14
|
+
pyjo get -u 'sri:s3cret' https://mojolicious.org
|
|
15
|
+
pyjo get mojolicious.org > example.html
|
|
16
|
+
pyjo get -M PUT mojolicious.org < example.html
|
|
17
|
+
pyjo get -f 'q=Mojolicious' -f 'size=5' https://metacpan.org/search
|
|
18
|
+
pyjo get -M POST -f 'upload=@example.html' mojolicious.org
|
|
19
|
+
pyjo get mojolicious.org 'head > title' text
|
|
20
|
+
pyjo get mojolicious.org .footer all
|
|
21
|
+
pyjo get mojolicious.org a attr href
|
|
22
|
+
pyjo get mojolicious.org '*' attr id
|
|
23
|
+
pyjo get mojolicious.org 'h1, h2, h3' 3 text
|
|
24
|
+
pyjo get https://pypi.org/pypi/Pyjoyment/json /info/version
|
|
25
|
+
pyjo get -H 'Host: example.com' http+unix://%2Ftmp%2Fmyapp.sock/index.html
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
-C, --charset <charset> Charset of HTML/XML content, defaults
|
|
29
|
+
to auto-detection
|
|
30
|
+
-c, --content <content> Content to send with request
|
|
31
|
+
-f, --form <name=value> One or more form values and file
|
|
32
|
+
uploads
|
|
33
|
+
-H, --header <name:value> One or more additional HTTP headers
|
|
34
|
+
-h, --help Show this summary of available options
|
|
35
|
+
--home <path> Path to home directory of your
|
|
36
|
+
application, defaults to the value of
|
|
37
|
+
PYJO_HOME or auto-detection
|
|
38
|
+
-i, --inactivity-timeout <seconds> Inactivity timeout, defaults to the
|
|
39
|
+
value of PYJO_INACTIVITY_TIMEOUT or 40
|
|
40
|
+
-k, --insecure Do not require a valid TLS certificate
|
|
41
|
+
to access HTTPS sites
|
|
42
|
+
--log-format <name> Format of log messages, "text" or
|
|
43
|
+
"json", defaults to the value of
|
|
44
|
+
PYJO_LOG_FORMAT or "text"
|
|
45
|
+
-M, --method <method> HTTP method to use, defaults to "GET"
|
|
46
|
+
-m, --mode <name> Operating mode for your application,
|
|
47
|
+
defaults to the value of PYJO_MODE or
|
|
48
|
+
"development"
|
|
49
|
+
-o, --connect-timeout <seconds> Connect timeout, defaults to the value
|
|
50
|
+
of PYJO_CONNECT_TIMEOUT or 10
|
|
51
|
+
-r, --redirect Follow up to 10 redirects
|
|
52
|
+
-S, --response-size <size> Maximum response size in bytes,
|
|
53
|
+
defaults to 2147483648 (2GiB)
|
|
54
|
+
-u, --user <userinfo> Alternate mechanism for specifying
|
|
55
|
+
colon-separated username and password
|
|
56
|
+
-v, --verbose Print request and response headers to
|
|
57
|
+
STDERR
|
|
58
|
+
|
|
59
|
+
:mod:`Pyjoyment.Command.get` performs requests to remote hosts or local
|
|
60
|
+
applications.
|
|
61
|
+
|
|
62
|
+
This is a core command, that means it is always enabled and its code a good
|
|
63
|
+
example for learning to build new commands, you're welcome to fork it.
|
|
64
|
+
|
|
65
|
+
See :mod:`Pyjoyment.Commands` for a list of commands that are available by
|
|
66
|
+
default.
|
|
67
|
+
|
|
68
|
+
See also
|
|
69
|
+
--------
|
|
70
|
+
|
|
71
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
72
|
+
|
|
73
|
+
Ported from `Mojolicious::Command::get
|
|
74
|
+
<https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojolicious/Command/get.pm>`_ of Mojolicious 9.49.
|
|
75
|
+
|
|
76
|
+
Classes
|
|
77
|
+
-------
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
import Pyjo.Collection
|
|
81
|
+
import Pyjo.DOM
|
|
82
|
+
import Pyjo.Exception
|
|
83
|
+
import Pyjo.IOLoop
|
|
84
|
+
import Pyjo.JSON.Pointer
|
|
85
|
+
import Pyjo.Message
|
|
86
|
+
import Pyjo.URL
|
|
87
|
+
import Pyjo.UserAgent
|
|
88
|
+
import Pyjoyment.Command
|
|
89
|
+
|
|
90
|
+
from Pyjo.JSON import j, to_json
|
|
91
|
+
from Pyjo.Util import decode, extract_usage, getopt, warn
|
|
92
|
+
|
|
93
|
+
import re
|
|
94
|
+
import select
|
|
95
|
+
import sys
|
|
96
|
+
import weakref
|
|
97
|
+
|
|
98
|
+
from typing import Any, cast
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Pyjoyment_Command_get(Pyjoyment.Command.object):
|
|
102
|
+
"""
|
|
103
|
+
:mod:`Pyjoyment.Command.get` inherits all attributes and methods from
|
|
104
|
+
:mod:`Pyjoyment.Command` and implements the following new ones.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
description = 'Perform HTTP request'
|
|
108
|
+
"""::
|
|
109
|
+
|
|
110
|
+
description = get.description
|
|
111
|
+
get.description = 'Foo'
|
|
112
|
+
|
|
113
|
+
Short description of this command, used for the command list.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
usage = extract_usage()
|
|
117
|
+
"""::
|
|
118
|
+
|
|
119
|
+
usage = get.usage
|
|
120
|
+
get.usage = 'Foo'
|
|
121
|
+
|
|
122
|
+
Usage information for this command, used for the help screen.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def run(self, *args: str) -> None:
|
|
126
|
+
"""::
|
|
127
|
+
|
|
128
|
+
get.run(*sys.argv[1:])
|
|
129
|
+
|
|
130
|
+
Run this command.
|
|
131
|
+
"""
|
|
132
|
+
# Data from STDIN
|
|
133
|
+
content = _stdin()
|
|
134
|
+
|
|
135
|
+
ua: Any = Pyjo.UserAgent.new(ioloop=Pyjo.IOLoop.singleton)
|
|
136
|
+
arguments = list(args)
|
|
137
|
+
form: dict[str, list[Any]] = {}
|
|
138
|
+
headers: list[str] = []
|
|
139
|
+
opts = getopt(
|
|
140
|
+
arguments,
|
|
141
|
+
{
|
|
142
|
+
'C|charset=s': None,
|
|
143
|
+
'c|content=s': None,
|
|
144
|
+
'f|form=s': lambda name, value: _form(form, value),
|
|
145
|
+
'H|header=s': headers,
|
|
146
|
+
'i|inactivity-timeout=i': lambda name, value: setattr(ua, 'inactivity_timeout', value),
|
|
147
|
+
'k|insecure': lambda name, value: setattr(ua, 'insecure', True),
|
|
148
|
+
'M|method=s': None,
|
|
149
|
+
'o|connect-timeout=i': lambda name, value: setattr(ua, 'connect_timeout', value),
|
|
150
|
+
'r|redirect': None,
|
|
151
|
+
'S|response-size=i': lambda name, value: setattr(ua, 'max_response_size', value),
|
|
152
|
+
'u|user=s': None,
|
|
153
|
+
'v|verbose': None,
|
|
154
|
+
},
|
|
155
|
+
)
|
|
156
|
+
if not opts:
|
|
157
|
+
raise Pyjo.Exception.new(self.usage)
|
|
158
|
+
if opts.content is not None:
|
|
159
|
+
content = opts.content
|
|
160
|
+
method: str = opts.method or 'GET'
|
|
161
|
+
redirect, verbose = opts.redirect, opts.verbose
|
|
162
|
+
|
|
163
|
+
# Left out from Mojolicious: decoding the arguments, which Python has as characters already
|
|
164
|
+
if not arguments:
|
|
165
|
+
raise Pyjo.Exception.new(self.usage)
|
|
166
|
+
url: Any = arguments.pop(0)
|
|
167
|
+
selector = arguments.pop(0) if arguments else None
|
|
168
|
+
|
|
169
|
+
# Parse header pairs
|
|
170
|
+
pairs = {m[1]: m[2] for header in headers if (m := re.match(r'^\s*([^:]+)\s*:\s*(.*)$', header))}
|
|
171
|
+
|
|
172
|
+
# Detect proxy for absolute URLs
|
|
173
|
+
if not url.startswith('/'):
|
|
174
|
+
ua.proxy.detect()
|
|
175
|
+
else:
|
|
176
|
+
ua.server.app = self.app
|
|
177
|
+
if opts.user:
|
|
178
|
+
url = Pyjo.URL.new(url).set(userinfo=opts.user)
|
|
179
|
+
if redirect:
|
|
180
|
+
ua.max_redirects = 10
|
|
181
|
+
|
|
182
|
+
buffer = bytearray()
|
|
183
|
+
|
|
184
|
+
@ua.on('start')
|
|
185
|
+
def start(ua: Any, tx: Any) -> None:
|
|
186
|
+
# Verbose
|
|
187
|
+
tx = weakref.proxy(tx)
|
|
188
|
+
if verbose:
|
|
189
|
+
|
|
190
|
+
@tx.res.content.on('body')
|
|
191
|
+
def body(content: Any) -> None:
|
|
192
|
+
warn(_header(tx.req), _header(tx.res))
|
|
193
|
+
|
|
194
|
+
# Stream content (ignore redirects)
|
|
195
|
+
@tx.res.content.unsubscribe('read').on('read')
|
|
196
|
+
def read(content: Any, chunk: bytes) -> None:
|
|
197
|
+
if redirect and tx.res.is_redirect:
|
|
198
|
+
return
|
|
199
|
+
if selector is not None:
|
|
200
|
+
buffer.extend(chunk)
|
|
201
|
+
else:
|
|
202
|
+
_write(chunk)
|
|
203
|
+
|
|
204
|
+
# Switch to verbose for HEAD requests
|
|
205
|
+
if method == 'HEAD':
|
|
206
|
+
verbose = True
|
|
207
|
+
if form:
|
|
208
|
+
tx = ua.start(ua.build_tx(method, url, pairs, form=form))
|
|
209
|
+
elif content is not None:
|
|
210
|
+
tx = ua.start(ua.build_tx(method, url, pairs, content))
|
|
211
|
+
else:
|
|
212
|
+
tx = ua.start(ua.build_tx(method, url, pairs))
|
|
213
|
+
res = tx.result
|
|
214
|
+
|
|
215
|
+
# JSON Pointer
|
|
216
|
+
if selector is None:
|
|
217
|
+
return None
|
|
218
|
+
if not selector or selector.startswith('/'):
|
|
219
|
+
return _json(bytes(buffer), selector)
|
|
220
|
+
|
|
221
|
+
# Selector
|
|
222
|
+
charset = opts.charset or res.content.charset or res.default_charset
|
|
223
|
+
return _select(bytes(buffer), selector, charset, *arguments)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _form(form: dict[str, list[Any]], value: str) -> None:
|
|
227
|
+
if m := re.match(r'^(.+)=(@?)(.+)$', value, re.S):
|
|
228
|
+
form.setdefault(m[1], []).append({'file': m[3]} if m[2] else m[3])
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _header(msg: Pyjo.Message.object) -> str:
|
|
232
|
+
return msg.build_start_line().decode('latin-1') + msg.headers.to_str() + '\n\n'
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _json(buffer: bytes, pointer: str) -> None:
|
|
236
|
+
if not (data := j(buffer)):
|
|
237
|
+
return
|
|
238
|
+
if (data := Pyjo.JSON.Pointer.new(data).get(pointer)) is None:
|
|
239
|
+
return
|
|
240
|
+
_say(to_json(cast(object, data)) if isinstance(data, (dict, list)) else data)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _say(*values: Any) -> None:
|
|
244
|
+
for value in values:
|
|
245
|
+
if value is not None and (text := str(value)):
|
|
246
|
+
print(text)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _select(buffer: bytes, selector: str, charset: str | None, *args: str) -> None:
|
|
250
|
+
arguments = list(args)
|
|
251
|
+
|
|
252
|
+
# Keep a strong reference to the root
|
|
253
|
+
text = (decode(charset, buffer) if charset else None) or buffer.decode('latin-1')
|
|
254
|
+
dom = Pyjo.DOM.new(text)
|
|
255
|
+
results: Any = dom.find(selector)
|
|
256
|
+
|
|
257
|
+
while arguments:
|
|
258
|
+
command = arguments.pop(0)
|
|
259
|
+
|
|
260
|
+
# Number
|
|
261
|
+
if re.fullmatch(r'\d+', command):
|
|
262
|
+
index = int(command)
|
|
263
|
+
results = Pyjo.Collection.c(*results[index : index + 1])
|
|
264
|
+
continue
|
|
265
|
+
|
|
266
|
+
# Text
|
|
267
|
+
if command == 'text':
|
|
268
|
+
return _say(*results.map('text').each())
|
|
269
|
+
|
|
270
|
+
# All text
|
|
271
|
+
if command == 'all':
|
|
272
|
+
return _say(*results.map('all_text').each())
|
|
273
|
+
|
|
274
|
+
# Attribute
|
|
275
|
+
if command == 'attr':
|
|
276
|
+
return _say(*results.map('attr', arguments[0] if arguments else '').each())
|
|
277
|
+
|
|
278
|
+
# Unknown
|
|
279
|
+
raise Pyjo.Exception.new(f'Unknown command "{command}".\n')
|
|
280
|
+
|
|
281
|
+
_say(*results.each())
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# Data from sys.stdin, unless it is a terminal or has nothing to read yet
|
|
285
|
+
def _stdin() -> str | None:
|
|
286
|
+
try:
|
|
287
|
+
if sys.stdin.isatty() or not select.select([sys.stdin], [], [], 0)[0]:
|
|
288
|
+
return None
|
|
289
|
+
except (AttributeError, OSError, ValueError):
|
|
290
|
+
return None
|
|
291
|
+
return sys.stdin.read()
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# The bytes of the response, as Mojo prints them, after what sys.stdout has buffered
|
|
295
|
+
def _write(chunk: bytes) -> None:
|
|
296
|
+
sys.stdout.flush()
|
|
297
|
+
if (out := getattr(sys.stdout, 'buffer', None)) is not None:
|
|
298
|
+
out.write(chunk)
|
|
299
|
+
out.flush()
|
|
300
|
+
else:
|
|
301
|
+
sys.stdout.write(chunk.decode('latin-1'))
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
new = Pyjoyment_Command_get.new
|
|
305
|
+
object = Pyjoyment_Command_get
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjoyment.Command.lambda - AWS Lambda command
|
|
3
|
+
=============================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
Usage: APPLICATION lambda [OPTIONS]
|
|
7
|
+
|
|
8
|
+
handler = app.start('lambda')
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
-h, --help Show this summary of available options
|
|
12
|
+
--home <path> Path to home directory of your application,
|
|
13
|
+
defaults to the value of PYJO_HOME or
|
|
14
|
+
auto-detection
|
|
15
|
+
--log-format <name> Format of log messages, "text" or "json",
|
|
16
|
+
defaults to the value of PYJO_LOG_FORMAT or
|
|
17
|
+
"text"
|
|
18
|
+
-m, --mode <name> Operating mode for your application, defaults to
|
|
19
|
+
the value of PYJO_MODE or "development"
|
|
20
|
+
|
|
21
|
+
:mod:`Pyjoyment.Command.lambda` starts applications with the
|
|
22
|
+
:mod:`Pyjo.Server.Lambda` backend: it returns the handler of an event
|
|
23
|
+
function of AWS Lambda, which a script keeps for the runtime that imports it.
|
|
24
|
+
The script of an application that ends with ``handler = app.start()``, as
|
|
25
|
+
``myapp.py`` with the handler ``myapp.handler``, runs this command in the
|
|
26
|
+
runtime of AWS Lambda, which has the ``AWS_LAMBDA_RUNTIME_API`` environment
|
|
27
|
+
variable, and the command of its arguments otherwise.
|
|
28
|
+
|
|
29
|
+
The script that runs as the main program with that variable, as the
|
|
30
|
+
``bootstrap`` of a custom runtime or the command of an image, is the runtime
|
|
31
|
+
itself: the command runs the application for the invocations of the Runtime
|
|
32
|
+
API with :meth:`Pyjo.Server.Lambda.Pyjo_Server_Lambda.run_runtime`, until the
|
|
33
|
+
process ends, where a runtime that imports the script takes its handler. ::
|
|
34
|
+
|
|
35
|
+
#!/bin/sh
|
|
36
|
+
# The bootstrap of a custom runtime, as provided.al2023
|
|
37
|
+
exec python3 "$LAMBDA_TASK_ROOT/myapp.py"
|
|
38
|
+
|
|
39
|
+
This is a core command, that means it is always enabled and its code a good
|
|
40
|
+
example for learning to build new commands, you're welcome to fork it.
|
|
41
|
+
|
|
42
|
+
See :mod:`Pyjoyment.Commands` for a list of commands that are available by
|
|
43
|
+
default.
|
|
44
|
+
|
|
45
|
+
See also
|
|
46
|
+
--------
|
|
47
|
+
|
|
48
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
49
|
+
|
|
50
|
+
Classes
|
|
51
|
+
-------
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Not in Mojolicious: the command of AWS Lambda, as Pyjoyment.Command.wsgi is the one of WSGI; its module has the name
|
|
55
|
+
# of a keyword of Python, which only the loader of the commands imports
|
|
56
|
+
|
|
57
|
+
import Pyjo.Server.Lambda
|
|
58
|
+
import Pyjoyment.Command
|
|
59
|
+
|
|
60
|
+
from Pyjo.Util import extract_usage
|
|
61
|
+
|
|
62
|
+
import inspect
|
|
63
|
+
import os
|
|
64
|
+
|
|
65
|
+
from typing import Any
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Pyjoyment_Command_lambda(Pyjoyment.Command.object):
|
|
69
|
+
"""
|
|
70
|
+
:mod:`Pyjoyment.Command.lambda` inherits all attributes and methods from
|
|
71
|
+
:mod:`Pyjoyment.Command` and implements the following new ones.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
description = 'Start application with AWS Lambda'
|
|
75
|
+
"""::
|
|
76
|
+
|
|
77
|
+
description = command.description
|
|
78
|
+
command.description = 'Foo'
|
|
79
|
+
|
|
80
|
+
Short description of this command, used for the command list.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
usage = extract_usage()
|
|
84
|
+
"""::
|
|
85
|
+
|
|
86
|
+
usage = command.usage
|
|
87
|
+
command.usage = 'Foo'
|
|
88
|
+
|
|
89
|
+
Usage information for this command, used for the help screen.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def run(self, *args: str) -> Any:
|
|
93
|
+
"""::
|
|
94
|
+
|
|
95
|
+
handler = command.run()
|
|
96
|
+
|
|
97
|
+
Run this command.
|
|
98
|
+
"""
|
|
99
|
+
server = Pyjo.Server.Lambda.new(app=self.app)
|
|
100
|
+
|
|
101
|
+
# The script of the application as the runtime, where a runtime that imports it takes its handler
|
|
102
|
+
if os.environ.get('AWS_LAMBDA_RUNTIME_API') and _is_main():
|
|
103
|
+
return server.run_runtime()
|
|
104
|
+
return server.to_lambda_app()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# The module that starts the application is the main program, outside of the frames of Pyjoyment, where it is a module
|
|
108
|
+
# that a runtime imports otherwise
|
|
109
|
+
def _is_main() -> bool:
|
|
110
|
+
frame = inspect.currentframe()
|
|
111
|
+
while frame is not None and str(frame.f_globals.get('__name__', '')).split('.')[0] in ('Pyjo', 'Pyjoyment'):
|
|
112
|
+
frame = frame.f_back
|
|
113
|
+
return frame is not None and frame.f_globals.get('__name__') == '__main__'
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
new = Pyjoyment_Command_lambda.new
|
|
117
|
+
object = Pyjoyment_Command_lambda
|