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,134 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjoyment.Plugin.EPLRenderer - Embedded Python Lite renderer plugin
|
|
3
|
+
===================================================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
# Pyjoyment
|
|
7
|
+
app.plugin('EPLRenderer')
|
|
8
|
+
|
|
9
|
+
# Pyjoyment.Lite
|
|
10
|
+
plugin('EPLRenderer')
|
|
11
|
+
|
|
12
|
+
:mod:`Pyjoyment.Plugin.EPLRenderer` is a renderer for ``epl`` templates,
|
|
13
|
+
which are pretty much just raw :mod:`Pyjo.Template`. The template gets the
|
|
14
|
+
controller as its first argument. ::
|
|
15
|
+
|
|
16
|
+
% c = args[0]
|
|
17
|
+
Hello <%= c.param('name') %>!
|
|
18
|
+
|
|
19
|
+
This is a core plugin, that means it is always enabled and its code a good
|
|
20
|
+
example for learning to build new plugins, you're welcome to fork it.
|
|
21
|
+
|
|
22
|
+
See :mod:`Pyjoyment.Plugins` for a list of plugins that are available by
|
|
23
|
+
default.
|
|
24
|
+
|
|
25
|
+
See also
|
|
26
|
+
--------
|
|
27
|
+
|
|
28
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
29
|
+
|
|
30
|
+
Ported from `Mojolicious::Plugin::EPLRenderer
|
|
31
|
+
<https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojolicious/Plugin/EPLRenderer.pm>`_ of Mojolicious 9.49.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
import Pyjo.Exception
|
|
35
|
+
import Pyjo.Template
|
|
36
|
+
import Pyjoyment.Plugin
|
|
37
|
+
|
|
38
|
+
from Pyjo.Util import encode, md5_sum
|
|
39
|
+
|
|
40
|
+
import io
|
|
41
|
+
|
|
42
|
+
from typing import TYPE_CHECKING, Any
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
import Pyjoyment
|
|
47
|
+
import Pyjoyment.Controller
|
|
48
|
+
import Pyjoyment.Renderer
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Pyjoyment_Plugin_EPLRenderer(Pyjoyment.Plugin.object):
|
|
52
|
+
"""
|
|
53
|
+
:mod:`Pyjoyment.Plugin.EPLRenderer` inherits all methods from
|
|
54
|
+
:mod:`Pyjoyment.Plugin` and implements the following new ones.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def register(self, app: 'Pyjoyment.object', conf: dict[str, Any] | None = None) -> None:
|
|
58
|
+
"""::
|
|
59
|
+
|
|
60
|
+
plugin.register(Pyjoyment.new())
|
|
61
|
+
|
|
62
|
+
Register renderer in :mod:`Pyjoyment` application.
|
|
63
|
+
"""
|
|
64
|
+
app.renderer.add_handler('epl', _handler)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _handler(
|
|
68
|
+
renderer: 'Pyjoyment.Renderer.object',
|
|
69
|
+
c: 'Pyjoyment.Controller.object',
|
|
70
|
+
output: io.StringIO,
|
|
71
|
+
options: dict[str, Any],
|
|
72
|
+
) -> None:
|
|
73
|
+
_render(renderer, c, output, options, Pyjo.Template.new(), c)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _render(
|
|
77
|
+
renderer: 'Pyjoyment.Renderer.object',
|
|
78
|
+
c: 'Pyjoyment.Controller.object',
|
|
79
|
+
output: io.StringIO,
|
|
80
|
+
options: dict[str, Any],
|
|
81
|
+
mt: Pyjo.Template.object,
|
|
82
|
+
*args: Any,
|
|
83
|
+
) -> None:
|
|
84
|
+
log = c.helpers.log()
|
|
85
|
+
|
|
86
|
+
# Cached
|
|
87
|
+
if mt.compiled is not None:
|
|
88
|
+
log.trace(f'Rendering cached {mt.name}')
|
|
89
|
+
result = mt.process(*args)
|
|
90
|
+
|
|
91
|
+
# Not cached
|
|
92
|
+
else:
|
|
93
|
+
inline = options.get('inline')
|
|
94
|
+
name = md5_sum(encode('UTF-8', inline)) if inline is not None else renderer.template_name(options)
|
|
95
|
+
if name is None:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# Inline
|
|
99
|
+
if inline is not None:
|
|
100
|
+
log.trace(f'Rendering inline template "{name}"')
|
|
101
|
+
mt.name = f'inline template "{name}"'
|
|
102
|
+
result = mt.render(inline, *args)
|
|
103
|
+
|
|
104
|
+
# File
|
|
105
|
+
else:
|
|
106
|
+
if encoding := renderer.encoding:
|
|
107
|
+
mt.encoding = encoding
|
|
108
|
+
|
|
109
|
+
# Try template
|
|
110
|
+
if (path := renderer.template_path(options)) is not None:
|
|
111
|
+
log.trace(f'Rendering template "{name}"')
|
|
112
|
+
mt.name = f'template "{name}"'
|
|
113
|
+
result = mt.render_file(path, *args)
|
|
114
|
+
|
|
115
|
+
# Try DATA section
|
|
116
|
+
elif (d := renderer.get_data_template(options)) is not None:
|
|
117
|
+
log.trace(f'Rendering template "{name}" from DATA section')
|
|
118
|
+
mt.name = f'template "{name}" from DATA section'
|
|
119
|
+
result = mt.render(d if isinstance(d, str) else d.decode('latin-1'), *args)
|
|
120
|
+
|
|
121
|
+
# No template
|
|
122
|
+
else:
|
|
123
|
+
log.trace(f'Template "{name}" not found')
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
# Exception
|
|
127
|
+
if isinstance(result, Pyjo.Exception.object):
|
|
128
|
+
raise result
|
|
129
|
+
output.write(result)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
new = Pyjoyment_Plugin_EPLRenderer.new
|
|
133
|
+
object = Pyjoyment_Plugin_EPLRenderer
|
|
134
|
+
EPLRenderer = Pyjoyment_Plugin_EPLRenderer
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjoyment.Plugin.EPRenderer - Embedded Python renderer plugin
|
|
3
|
+
=============================================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
# Pyjoyment
|
|
7
|
+
app.plugin('EPRenderer')
|
|
8
|
+
app.plugin('EPRenderer', name='foo')
|
|
9
|
+
app.plugin('EPRenderer', name='bar', template={'line_start': '.'})
|
|
10
|
+
|
|
11
|
+
# Pyjoyment.Lite
|
|
12
|
+
plugin('EPRenderer')
|
|
13
|
+
plugin('EPRenderer', name='foo')
|
|
14
|
+
plugin('EPRenderer', name='bar', template={'line_start': '.'})
|
|
15
|
+
|
|
16
|
+
:mod:`Pyjoyment.Plugin.EPRenderer` is a renderer for Embedded Python
|
|
17
|
+
templates, ``ep``, the default handler of :mod:`Pyjoyment`. They are
|
|
18
|
+
templates of :mod:`Pyjo.Template` with automatic escaping, in which the
|
|
19
|
+
values of the stash are variables, ``self`` and ``c`` are the current
|
|
20
|
+
controller, and the helpers are functions. ::
|
|
21
|
+
|
|
22
|
+
% title('Welcome!')
|
|
23
|
+
<p>Hello <%= name %>, this is <%= url_for('index') %>.</p>
|
|
24
|
+
|
|
25
|
+
A value of the stash hides a helper of the same name, which is still a
|
|
26
|
+
method of ``c``, as is the helper ``c``, which the variable ``c`` hides. ::
|
|
27
|
+
|
|
28
|
+
% c.title('Welcome!')
|
|
29
|
+
%= c.c('a', 'b', 'c').join(', ')
|
|
30
|
+
|
|
31
|
+
This is a core plugin, that means it is always enabled and its code a good
|
|
32
|
+
example for learning to build new plugins, you're welcome to fork it.
|
|
33
|
+
|
|
34
|
+
See :mod:`Pyjoyment.Plugins` for a list of plugins that are available by
|
|
35
|
+
default.
|
|
36
|
+
|
|
37
|
+
Options
|
|
38
|
+
-------
|
|
39
|
+
|
|
40
|
+
:mod:`Pyjoyment.Plugin.EPRenderer` supports the following options.
|
|
41
|
+
|
|
42
|
+
name ::
|
|
43
|
+
|
|
44
|
+
# Pyjoyment.Lite
|
|
45
|
+
plugin('EPRenderer', name='foo')
|
|
46
|
+
|
|
47
|
+
Handler name, defaults to ``ep``.
|
|
48
|
+
|
|
49
|
+
template ::
|
|
50
|
+
|
|
51
|
+
# Pyjoyment.Lite
|
|
52
|
+
plugin('EPRenderer', template={'line_start': '.'})
|
|
53
|
+
|
|
54
|
+
Attribute values passed to :mod:`Pyjo.Template` objects used to render
|
|
55
|
+
templates.
|
|
56
|
+
|
|
57
|
+
See also
|
|
58
|
+
--------
|
|
59
|
+
|
|
60
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
61
|
+
|
|
62
|
+
Ported from `Mojolicious::Plugin::EPRenderer
|
|
63
|
+
<https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojolicious/Plugin/EPRenderer.pm>`_ of Mojolicious 9.49.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
import Pyjo.Base
|
|
67
|
+
import Pyjo.Exception
|
|
68
|
+
import Pyjo.Template
|
|
69
|
+
import Pyjoyment.Plugin.EPLRenderer
|
|
70
|
+
|
|
71
|
+
from Pyjo.Util import encode, md5_sum
|
|
72
|
+
|
|
73
|
+
import io
|
|
74
|
+
import re
|
|
75
|
+
import sys
|
|
76
|
+
import types
|
|
77
|
+
import weakref
|
|
78
|
+
|
|
79
|
+
from collections.abc import Callable
|
|
80
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if TYPE_CHECKING:
|
|
84
|
+
import Pyjoyment
|
|
85
|
+
import Pyjoyment.Controller
|
|
86
|
+
import Pyjoyment.Renderer
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Pyjoyment_Plugin_EPRenderer(Pyjoyment.Plugin.EPLRenderer.object):
|
|
90
|
+
"""
|
|
91
|
+
:mod:`Pyjoyment.Plugin.EPRenderer` inherits all methods from
|
|
92
|
+
:mod:`Pyjoyment.Plugin.EPLRenderer` and implements the following new ones.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def register(self, app: 'Pyjoyment.object', conf: dict[str, Any] | None = None) -> None:
|
|
96
|
+
"""::
|
|
97
|
+
|
|
98
|
+
plugin.register(Pyjoyment.new())
|
|
99
|
+
plugin.register(Pyjoyment.new(), {'name': 'foo'})
|
|
100
|
+
|
|
101
|
+
Register renderer in :mod:`Pyjoyment` application.
|
|
102
|
+
"""
|
|
103
|
+
conf = conf or {}
|
|
104
|
+
self._helpers = False
|
|
105
|
+
|
|
106
|
+
# Auto escape by default to prevent XSS attacks
|
|
107
|
+
ep: dict[str, Any] = {'auto_escape': True, **(conf.get('template') or {}), 'vars': True}
|
|
108
|
+
ns = ep['namespace'] = ep.get('namespace') or f'Pyjo.Template.Sandbox.{md5_sum(repr(self).encode())}'
|
|
109
|
+
|
|
110
|
+
# Unlike Mojolicious: the namespace is a module, which the plugin makes when it is not loaded, and the plugin
|
|
111
|
+
# removes the names that it adds, where Mojo removes all the names of the package, even of one that the option
|
|
112
|
+
# "template" names
|
|
113
|
+
module = sys.modules.get(ns)
|
|
114
|
+
if module is None:
|
|
115
|
+
module = sys.modules[ns] = types.ModuleType(ns)
|
|
116
|
+
|
|
117
|
+
# No spec, as the module has no source that linecache could read, which warns of a spec without a loader
|
|
118
|
+
del module.__spec__
|
|
119
|
+
else:
|
|
120
|
+
ns = None
|
|
121
|
+
names: list[str] = []
|
|
122
|
+
weakref.finalize(self, _teardown, module, names, ns)
|
|
123
|
+
|
|
124
|
+
# Make "self" and "c" available in templates
|
|
125
|
+
ep['prepend'] = 'self = c = _C()' + (f'\n{ep["prepend"]}' if ep.get('prepend') else '')
|
|
126
|
+
|
|
127
|
+
# Add "ep" handler and make it the default
|
|
128
|
+
def handler(
|
|
129
|
+
renderer: 'Pyjoyment.Renderer.object',
|
|
130
|
+
c: 'Pyjoyment.Controller.object',
|
|
131
|
+
output: io.StringIO,
|
|
132
|
+
options: dict[str, Any],
|
|
133
|
+
) -> None:
|
|
134
|
+
name = options.get('inline')
|
|
135
|
+
if name is None:
|
|
136
|
+
name = renderer.template_name(options)
|
|
137
|
+
if name is None:
|
|
138
|
+
return
|
|
139
|
+
key = md5_sum(encode('UTF-8', name))
|
|
140
|
+
|
|
141
|
+
cache = renderer.cache
|
|
142
|
+
if (mt := cache.get(key)) is None:
|
|
143
|
+
cache.set(key, mt := Pyjo.Template.new(**ep))
|
|
144
|
+
|
|
145
|
+
# Export helpers only once
|
|
146
|
+
if not self._helpers:
|
|
147
|
+
self._helpers = True
|
|
148
|
+
_helpers(module, renderer.helpers, names)
|
|
149
|
+
|
|
150
|
+
# Make current controller available and render with "epl" handler
|
|
151
|
+
saved = module.__dict__.get('_C', _MISSING)
|
|
152
|
+
module.__dict__['_C'] = lambda: c
|
|
153
|
+
try:
|
|
154
|
+
Pyjoyment.Plugin.EPLRenderer._render( # pyright: ignore[reportPrivateUsage]
|
|
155
|
+
renderer, c, output, options, cast(Pyjo.Template.object, mt), c.stash()
|
|
156
|
+
)
|
|
157
|
+
except Pyjo.Exception.object as e:
|
|
158
|
+
# Not in Mojolicious: a value of the stash that hides a helper, which Perl tells apart by its sigil
|
|
159
|
+
if 'object is not callable' in e.message and (
|
|
160
|
+
hidden := [k for k, v in c.stash().items() if k in names and not callable(v)]
|
|
161
|
+
):
|
|
162
|
+
e.message += ''.join(
|
|
163
|
+
f', the value "{k}" of the stash hides the helper {k}(), which c.{k}(...) calls' for k in hidden
|
|
164
|
+
)
|
|
165
|
+
raise
|
|
166
|
+
finally:
|
|
167
|
+
if saved is _MISSING:
|
|
168
|
+
module.__dict__.pop('_C', None)
|
|
169
|
+
else:
|
|
170
|
+
module.__dict__['_C'] = saved
|
|
171
|
+
|
|
172
|
+
app.renderer.default_handler = 'ep'
|
|
173
|
+
app.renderer.add_handler(conf.get('name') or 'ep', handler)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
_MISSING: Any = Pyjo.Base.object()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# A helper of the templates, which the controller of the template calls, and whose missing attribute names the call of
|
|
180
|
+
# the helper, as ua.get_p(...) is a common mistake for ua().get_p(...)
|
|
181
|
+
class _Helper:
|
|
182
|
+
__slots__ = ('_module', '_name', '_sub')
|
|
183
|
+
|
|
184
|
+
def __init__(self, module: types.ModuleType, name: str, sub: Callable[..., Any]) -> None:
|
|
185
|
+
self._module, self._name, self._sub = module, name, sub
|
|
186
|
+
|
|
187
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
188
|
+
return self._sub(self._module.__dict__['_C'](), *args, **kwargs)
|
|
189
|
+
|
|
190
|
+
def __getattr__(self, name: str) -> Any:
|
|
191
|
+
if name.startswith('_'):
|
|
192
|
+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'", name=name, obj=self)
|
|
193
|
+
message = f'Helper "{self._name}" has no attribute "{name}", call the helper first: {self._name}().{name}'
|
|
194
|
+
raise AttributeError(message, name=name, obj=self)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _helpers(module: types.ModuleType, helpers: dict[str, Callable[..., Any]], names: list[str]) -> None:
|
|
198
|
+
for method in [method for method in helpers if re.fullmatch(r'\w+', method)]:
|
|
199
|
+
setattr(module, method, _Helper(module, method, helpers[method]))
|
|
200
|
+
names.append(method)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _teardown(module: types.ModuleType, names: list[str], ns: str | None) -> None:
|
|
204
|
+
for name in [*names, '_C']:
|
|
205
|
+
module.__dict__.pop(name, None)
|
|
206
|
+
if ns is not None and sys.modules.get(ns) is module:
|
|
207
|
+
del sys.modules[ns]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
new = Pyjoyment_Plugin_EPRenderer.new
|
|
211
|
+
object = Pyjoyment_Plugin_EPRenderer
|
|
212
|
+
EPRenderer = Pyjoyment_Plugin_EPRenderer
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjoyment.Plugin.HeaderCondition - Header condition plugin
|
|
3
|
+
==========================================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
# Pyjoyment
|
|
7
|
+
app.plugin('HeaderCondition')
|
|
8
|
+
app.get('/foo').requires(headers={'Referer': re.compile(r'example\\.com')})
|
|
9
|
+
|
|
10
|
+
# Pyjoyment.Lite
|
|
11
|
+
plugin('HeaderCondition')
|
|
12
|
+
|
|
13
|
+
@get('/').requires(headers={'Referer': re.compile(r'example\\.com')})
|
|
14
|
+
def index(c): ...
|
|
15
|
+
|
|
16
|
+
# All headers need to match
|
|
17
|
+
app.get('/foo').requires(headers={'X-Secret-Header': 'Foo', 'Referer': re.compile(r'example\\.com')})
|
|
18
|
+
|
|
19
|
+
# The "agent" condition is a shortcut for the "User-Agent" header
|
|
20
|
+
@get('/').requires(agent=re.compile(r'Firefox'))
|
|
21
|
+
def firefox(c): ...
|
|
22
|
+
|
|
23
|
+
# The "host" condition is a shortcut for the detected host
|
|
24
|
+
@get('/').requires(host=re.compile(r'mojolicious\\.org'))
|
|
25
|
+
def mojolicious(c): ...
|
|
26
|
+
|
|
27
|
+
:mod:`Pyjoyment.Plugin.HeaderCondition` is a route condition for header-based
|
|
28
|
+
routes.
|
|
29
|
+
|
|
30
|
+
This is a core plugin, that means it is always enabled and its code a good
|
|
31
|
+
example for learning to build new plugins, you're welcome to fork it.
|
|
32
|
+
|
|
33
|
+
See :mod:`Pyjoyment.Plugins` for a list of plugins that are available by
|
|
34
|
+
default.
|
|
35
|
+
|
|
36
|
+
See also
|
|
37
|
+
--------
|
|
38
|
+
|
|
39
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
40
|
+
|
|
41
|
+
Ported from `Mojolicious::Plugin::HeaderCondition
|
|
42
|
+
<https://github.com/mojolicious/mojo/blob/v9.53/lib/Mojolicious/Plugin/HeaderCondition.pm>`_ of Mojolicious 9.53.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
import Pyjoyment.Plugin
|
|
46
|
+
|
|
47
|
+
import re
|
|
48
|
+
|
|
49
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if TYPE_CHECKING:
|
|
53
|
+
import Pyjoyment
|
|
54
|
+
import Pyjoyment.Controller
|
|
55
|
+
import Pyjoyment.Routes.Route
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Pyjoyment_Plugin_HeaderCondition(Pyjoyment.Plugin.object):
|
|
59
|
+
"""
|
|
60
|
+
:mod:`Pyjoyment.Plugin.HeaderCondition` inherits all methods from
|
|
61
|
+
:mod:`Pyjoyment.Plugin` and implements the following new ones.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def register(self, app: 'Pyjoyment.object', conf: dict[str, Any] | None = None) -> None:
|
|
65
|
+
"""::
|
|
66
|
+
|
|
67
|
+
plugin.register(Pyjoyment.new())
|
|
68
|
+
|
|
69
|
+
Register conditions in :mod:`Pyjoyment` application.
|
|
70
|
+
"""
|
|
71
|
+
app.routes.add_condition('headers', _headers)
|
|
72
|
+
app.routes.add_condition('agent', lambda r, c, captures, agent: _headers(r, c, captures, {'User-Agent': agent}))
|
|
73
|
+
app.routes.add_condition('host', lambda r, c, captures, host: _check(c.req.url.to_abs().host, host))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _check(value: str | None, pattern: Any) -> bool:
|
|
77
|
+
if value and isinstance(pattern, re.Pattern) and cast(re.Pattern[str], pattern).search(value):
|
|
78
|
+
return True
|
|
79
|
+
return bool(value) and pattern is not None and pattern == value
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _headers(
|
|
83
|
+
route: 'Pyjoyment.Routes.Route.object', c: 'Pyjoyment.Controller.object', captures: dict[str, Any], patterns: Any
|
|
84
|
+
) -> bool:
|
|
85
|
+
if not patterns or not isinstance(patterns, dict):
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
# All headers need to match
|
|
89
|
+
headers = c.req.headers
|
|
90
|
+
for name, pattern in cast(dict[str, Any], patterns).items():
|
|
91
|
+
if not _check(headers.header(name), pattern):
|
|
92
|
+
return False
|
|
93
|
+
return True
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
new = Pyjoyment_Plugin_HeaderCondition.new
|
|
97
|
+
object = Pyjoyment_Plugin_HeaderCondition
|
|
98
|
+
HeaderCondition = Pyjoyment_Plugin_HeaderCondition
|