plain 0.12.1__py3-none-any.whl → 0.13.1__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.
- plain/cli/cli.py +5 -14
- plain/debug.py +8 -6
- plain/runtime/global_settings.py +1 -2
- plain/templates/__init__.py +11 -0
- plain/templates/jinja/README.md +3 -17
- plain/templates/jinja/__init__.py +73 -7
- plain/templates/jinja/environments.py +63 -0
- {plain-0.12.1.dist-info → plain-0.13.1.dist-info}/METADATA +1 -1
- {plain-0.12.1.dist-info → plain-0.13.1.dist-info}/RECORD +12 -12
- plain/templates/jinja/defaults.py +0 -119
- {plain-0.12.1.dist-info → plain-0.13.1.dist-info}/LICENSE +0 -0
- {plain-0.12.1.dist-info → plain-0.13.1.dist-info}/WHEEL +0 -0
- {plain-0.12.1.dist-info → plain-0.13.1.dist-info}/entry_points.txt +0 -0
plain/cli/cli.py
CHANGED
@@ -446,8 +446,6 @@ class PlainCommandCollection(click.CommandCollection):
|
|
446
446
|
sources = []
|
447
447
|
|
448
448
|
try:
|
449
|
-
# Setup has to run before the installed packages CLI work
|
450
|
-
# and it also does the .env file loading right now...
|
451
449
|
plain.runtime.setup()
|
452
450
|
|
453
451
|
sources = [
|
@@ -457,6 +455,7 @@ class PlainCommandCollection(click.CommandCollection):
|
|
457
455
|
plain_cli,
|
458
456
|
]
|
459
457
|
except plain.runtime.AppPathNotFound:
|
458
|
+
# Allow some commands to work regardless of being in a valid app
|
460
459
|
click.secho(
|
461
460
|
"Plain `app` directory not found. Some commands may be missing.",
|
462
461
|
fg="yellow",
|
@@ -468,19 +467,16 @@ class PlainCommandCollection(click.CommandCollection):
|
|
468
467
|
plain_cli,
|
469
468
|
]
|
470
469
|
except ImproperlyConfigured as e:
|
470
|
+
# Show what was configured incorrectly and exit
|
471
471
|
click.secho(
|
472
472
|
str(e),
|
473
473
|
fg="red",
|
474
474
|
err=True,
|
475
475
|
)
|
476
476
|
|
477
|
-
|
478
|
-
sources = [
|
479
|
-
EntryPointGroup(),
|
480
|
-
AppCLIGroup(),
|
481
|
-
plain_cli,
|
482
|
-
]
|
477
|
+
exit(1)
|
483
478
|
except Exception as e:
|
479
|
+
# Show the exception and exit
|
484
480
|
print("---")
|
485
481
|
print(traceback.format_exc())
|
486
482
|
print("---")
|
@@ -491,12 +487,7 @@ class PlainCommandCollection(click.CommandCollection):
|
|
491
487
|
err=True,
|
492
488
|
)
|
493
489
|
|
494
|
-
|
495
|
-
sources = [
|
496
|
-
EntryPointGroup(),
|
497
|
-
AppCLIGroup(),
|
498
|
-
plain_cli,
|
499
|
-
]
|
490
|
+
exit(1)
|
500
491
|
|
501
492
|
super().__init__(*args, **kwargs)
|
502
493
|
|
plain/debug.py
CHANGED
@@ -6,20 +6,22 @@ from plain.http import Response
|
|
6
6
|
from plain.views.exceptions import ResponseException
|
7
7
|
|
8
8
|
|
9
|
-
def dd(
|
9
|
+
def dd(*objs):
|
10
10
|
"""
|
11
11
|
Dump and die.
|
12
12
|
|
13
13
|
Dump the object and raise a ResponseException with the dump as the response content.
|
14
14
|
"""
|
15
|
-
|
15
|
+
dump_strs = [
|
16
|
+
Markup("<pre><code>") + escape(pformat(obj)) + Markup("</code></pre>")
|
17
|
+
for obj in objs
|
18
|
+
]
|
19
|
+
combined_dump_str = Markup("\n\n").join(dump_strs)
|
16
20
|
|
17
|
-
print(f"Dumping
|
21
|
+
print(f"Dumping objects:\n{combined_dump_str}")
|
18
22
|
|
19
23
|
response = Response()
|
20
24
|
response.status_code = 500
|
21
|
-
response.content =
|
22
|
-
Markup("<pre><code>") + escape(dump_str) + Markup("</code></pre>")
|
23
|
-
)
|
25
|
+
response.content = combined_dump_str
|
24
26
|
response.content_type = "text/html"
|
25
27
|
raise ResponseException(response)
|
plain/runtime/global_settings.py
CHANGED
@@ -167,5 +167,4 @@ SILENCED_PREFLIGHT_CHECKS = []
|
|
167
167
|
# Templates #
|
168
168
|
#############
|
169
169
|
|
170
|
-
|
171
|
-
JINJA_ENVIRONMENT = "plain.templates.jinja.defaults.create_default_environment"
|
170
|
+
TEMPLATES_JINJA_ENVIRONMENT = "plain.templates.jinja.DefaultEnvironment"
|
plain/templates/__init__.py
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
from .core import Template, TemplateFileMissing
|
2
|
+
from .jinja import (
|
3
|
+
register_template_extension,
|
4
|
+
register_template_filter,
|
5
|
+
register_template_global,
|
6
|
+
)
|
2
7
|
|
3
8
|
__all__ = [
|
4
9
|
"Template",
|
5
10
|
"TemplateFileMissing",
|
11
|
+
# Technically these are jinja-specific,
|
12
|
+
# but expected to be used pretty frequently so
|
13
|
+
# the shorter import is handy.
|
14
|
+
"register_template_extension",
|
15
|
+
"register_template_filter",
|
16
|
+
"register_template_global",
|
6
17
|
]
|
plain/templates/jinja/README.md
CHANGED
@@ -105,25 +105,11 @@ TODO - example middleware, `|localtime` filter
|
|
105
105
|
|
106
106
|
## Settings
|
107
107
|
|
108
|
-
Most Jinja customization happens in `
|
108
|
+
Most Jinja customization happens in `templates.py` at the root of your app or in `INSTALLED_PACKAGES`.
|
109
109
|
But if you need to further customize the environment,
|
110
|
-
you can define your own
|
110
|
+
you can define your own class via `TEMPLATES_JINJA_ENVIRONMENT`.
|
111
111
|
|
112
|
-
|
113
|
-
# app/settings.py
|
114
|
-
JINJA_ENVIRONMENT = "myjinja.create_environment"
|
115
|
-
```
|
116
|
-
|
117
|
-
```python
|
118
|
-
# app/myjinja.py
|
119
|
-
from jinja2 import Environment
|
120
|
-
|
121
|
-
|
122
|
-
def create_environment():
|
123
|
-
return Environment(
|
124
|
-
# ...
|
125
|
-
)
|
126
|
-
```
|
112
|
+
TODO
|
127
113
|
|
128
114
|
## HTML Components
|
129
115
|
|
@@ -1,22 +1,88 @@
|
|
1
|
+
from importlib import import_module
|
2
|
+
|
3
|
+
from plain.packages import packages
|
1
4
|
from plain.runtime import settings
|
2
5
|
from plain.utils.functional import LazyObject
|
3
|
-
from plain.utils.module_loading import import_string
|
6
|
+
from plain.utils.module_loading import import_string, module_has_submodule
|
4
7
|
|
5
|
-
from .
|
8
|
+
from .environments import DefaultEnvironment, get_template_dirs
|
6
9
|
|
7
10
|
|
8
11
|
class JinjaEnvironment(LazyObject):
|
12
|
+
def __init__(self, *args, **kwargs):
|
13
|
+
self.__dict__["_imported_modules"] = set()
|
14
|
+
super().__init__(*args, **kwargs)
|
15
|
+
|
9
16
|
def _setup(self):
|
10
|
-
environment_setting = settings.
|
17
|
+
environment_setting = settings.TEMPLATES_JINJA_ENVIRONMENT
|
11
18
|
|
12
19
|
if isinstance(environment_setting, str):
|
13
|
-
|
20
|
+
env = import_string(environment_setting)()
|
14
21
|
else:
|
15
|
-
|
22
|
+
env = environment_setting()
|
23
|
+
|
24
|
+
# We have to set _wrapped before we trigger the autoloading of "register" commands
|
25
|
+
self._wrapped = env
|
26
|
+
|
27
|
+
def _maybe_import_module(name):
|
28
|
+
if name not in self._imported_modules:
|
29
|
+
import_module(name)
|
30
|
+
self._imported_modules.add(name)
|
31
|
+
|
32
|
+
for package_config in packages.get_package_configs():
|
33
|
+
if module_has_submodule(package_config.module, "templates"):
|
34
|
+
# Allow this to fail in case there are import errors inside of their file
|
35
|
+
_maybe_import_module(f"{package_config.name}.templates")
|
16
36
|
|
17
|
-
|
37
|
+
app = import_module("app")
|
38
|
+
if module_has_submodule(app, "templates"):
|
39
|
+
# Allow this to fail in case there are import errors inside of their file
|
40
|
+
_maybe_import_module("app.templates")
|
18
41
|
|
19
42
|
|
20
43
|
environment = JinjaEnvironment()
|
21
44
|
|
22
|
-
|
45
|
+
|
46
|
+
def register_template_extension(extension_class):
|
47
|
+
environment.add_extension(extension_class)
|
48
|
+
return extension_class
|
49
|
+
|
50
|
+
|
51
|
+
def register_template_global(value, name=None):
|
52
|
+
"""
|
53
|
+
Adds a global to the Jinja environment.
|
54
|
+
|
55
|
+
Can be used as a decorator on a function:
|
56
|
+
|
57
|
+
@register_template_global
|
58
|
+
def my_global():
|
59
|
+
return "Hello, world!"
|
60
|
+
|
61
|
+
Or as a function:
|
62
|
+
|
63
|
+
register_template_global("Hello, world!", name="my_global")
|
64
|
+
"""
|
65
|
+
if callable(value):
|
66
|
+
environment.globals[name or value.__name__] = value
|
67
|
+
elif name:
|
68
|
+
environment.globals[name] = value
|
69
|
+
else:
|
70
|
+
raise ValueError("name must be provided if value is not callable")
|
71
|
+
|
72
|
+
return value
|
73
|
+
|
74
|
+
|
75
|
+
def register_template_filter(func, name=None):
|
76
|
+
"""Adds a filter to the Jinja environment."""
|
77
|
+
environment.filters[name or func.__name__] = func
|
78
|
+
return func
|
79
|
+
|
80
|
+
|
81
|
+
__all__ = [
|
82
|
+
"environment",
|
83
|
+
"DefaultEnvironment",
|
84
|
+
"get_template_dirs",
|
85
|
+
"register_template_extension",
|
86
|
+
"register_template_filter",
|
87
|
+
"register_template_global",
|
88
|
+
]
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import functools
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
from jinja2 import Environment, StrictUndefined
|
5
|
+
from jinja2.loaders import FileSystemLoader
|
6
|
+
|
7
|
+
from plain.packages import packages
|
8
|
+
from plain.runtime import settings
|
9
|
+
|
10
|
+
from .filters import default_filters
|
11
|
+
from .globals import default_globals
|
12
|
+
|
13
|
+
|
14
|
+
def finalize_callable_error(obj):
|
15
|
+
"""Prevent direct rendering of a callable (likely just forgotten ()) by raising a TypeError"""
|
16
|
+
if callable(obj):
|
17
|
+
raise TypeError(f"{obj} is callable, did you forget parentheses?")
|
18
|
+
|
19
|
+
# TODO find a way to prevent <object representation> from being rendered
|
20
|
+
# if obj.__class__.__str__ is object.__str__:
|
21
|
+
# raise TypeError(f"{obj} does not have a __str__ method")
|
22
|
+
|
23
|
+
return obj
|
24
|
+
|
25
|
+
|
26
|
+
def get_template_dirs():
|
27
|
+
jinja_templates = Path(__file__).parent / "templates"
|
28
|
+
app_templates = settings.path.parent / "templates"
|
29
|
+
return (jinja_templates, app_templates) + _get_app_template_dirs()
|
30
|
+
|
31
|
+
|
32
|
+
@functools.lru_cache
|
33
|
+
def _get_app_template_dirs():
|
34
|
+
"""
|
35
|
+
Return an iterable of paths of directories to load app templates from.
|
36
|
+
|
37
|
+
dirname is the name of the subdirectory containing templates inside
|
38
|
+
installed applications.
|
39
|
+
"""
|
40
|
+
dirname = "templates"
|
41
|
+
template_dirs = [
|
42
|
+
Path(package_config.path) / dirname
|
43
|
+
for package_config in packages.get_package_configs()
|
44
|
+
if package_config.path and (Path(package_config.path) / dirname).is_dir()
|
45
|
+
]
|
46
|
+
# Immutable return value because it will be cached and shared by callers.
|
47
|
+
return tuple(template_dirs)
|
48
|
+
|
49
|
+
|
50
|
+
class DefaultEnvironment(Environment):
|
51
|
+
def __init__(self):
|
52
|
+
super().__init__(
|
53
|
+
loader=FileSystemLoader(get_template_dirs()),
|
54
|
+
autoescape=True,
|
55
|
+
auto_reload=settings.DEBUG,
|
56
|
+
undefined=StrictUndefined,
|
57
|
+
finalize=finalize_callable_error,
|
58
|
+
extensions=["jinja2.ext.loopcontrols", "jinja2.ext.debug"],
|
59
|
+
)
|
60
|
+
|
61
|
+
# Load the top-level defaults
|
62
|
+
self.globals.update(default_globals)
|
63
|
+
self.filters.update(default_filters)
|
@@ -9,7 +9,7 @@ plain/assets/urls.py,sha256=ZTIoM1Zq35JaXZ3wFhXhfGa7VoITDNlH9i5RS0R5xow,933
|
|
9
9
|
plain/assets/views.py,sha256=dhjIpMu0GDR_VGbXM90_6RnC84C2C4bFv1RxDVklGBk,9173
|
10
10
|
plain/cli/README.md,sha256=xjr1K-sIMTi5OWxdxL--O7aoo16Pd1xdawIZtz6BL7Q,2464
|
11
11
|
plain/cli/__init__.py,sha256=9ByBOIdM8DebChjNz-RH2atdz4vWe8somlwNEsbhwh4,40
|
12
|
-
plain/cli/cli.py,sha256=
|
12
|
+
plain/cli/cli.py,sha256=o3h6ZQLvxytNwuzTUjRg-TbNw6_FrTBgbmYxjBt6CuI,14543
|
13
13
|
plain/cli/formatting.py,sha256=1hZH13y1qwHcU2K2_Na388nw9uvoeQH8LrWL-O9h8Yc,2207
|
14
14
|
plain/cli/packages.py,sha256=69VH1bIi1-5N5l2jlBcR5EP0pt-v16sPar9arO3gCSE,2052
|
15
15
|
plain/cli/print.py,sha256=XraUYrgODOJquIiEv78wSCYGRBplHXtXSS9QtFG5hqY,217
|
@@ -17,7 +17,7 @@ plain/cli/startup.py,sha256=PJYA-tNWGia-QbTlT0e5HvC8C7yDSq8wkAkIxgfKkvw,680
|
|
17
17
|
plain/csrf/README.md,sha256=RXMWMtHmzf30gVVNOfj0kD4xlSqFIPgJh-n7dIciaEM,163
|
18
18
|
plain/csrf/middleware.py,sha256=MlDQ55B4eRXySbzauFNs8gKhgQy32yWspBfPI0a3PzA,17775
|
19
19
|
plain/csrf/views.py,sha256=YDgT451X16iUdCxpQ6rcHIy7nD0u7DAvCQl5-Mx5i9Y,219
|
20
|
-
plain/debug.py,sha256=
|
20
|
+
plain/debug.py,sha256=NxiWJHB4Gi5JfJ4cnxzl-PZ6IgpSnUxg7a_dTQNq_lQ,705
|
21
21
|
plain/exceptions.py,sha256=tDS6l0epe_L9IlxpEdT2k2hWgEoAu8YBNIumNCtJ-WY,6333
|
22
22
|
plain/forms/README.md,sha256=fglB9MmHiEgfGGdZmcRstNl6eYaFljrElu2mzapK52M,377
|
23
23
|
plain/forms/__init__.py,sha256=UxqPwB8CiYPCQdHmUc59jadqaXqDmXBH8y4bt9vTPms,226
|
@@ -69,7 +69,7 @@ plain/preflight/security.py,sha256=sNpv5AHobPcaO48cOUGRNe2EjusTducjY8vyShR8EhI,2
|
|
69
69
|
plain/preflight/urls.py,sha256=O4PQ_v205VA2872fQlhPfxaihDDRCsVp0ZVKQ92aX4k,3019
|
70
70
|
plain/runtime/README.md,sha256=Q8VVO7JRGuYrDxzuYL6ptoilhclbecxKzpRXKgbWGkU,2061
|
71
71
|
plain/runtime/__init__.py,sha256=DH8TwKTGJhjviOy4yh_d051v8YGaAWMlFBPhK8ZuC9g,1499
|
72
|
-
plain/runtime/global_settings.py,sha256
|
72
|
+
plain/runtime/global_settings.py,sha256=-tr_nUT7iwZpvQlCI6AmJsLRoakBYGp2O2ZsCXgye9U,5498
|
73
73
|
plain/runtime/user_settings.py,sha256=-1xXUggueuOF3YlgnLfeyG55CUvR3azOGWr2UkTOmfs,11259
|
74
74
|
plain/signals/README.md,sha256=cd3tKEgH-xc88CUWyDxl4-qv-HBXx8VT32BXVwA5azA,230
|
75
75
|
plain/signals/__init__.py,sha256=eAs0kLqptuP6I31dWXeAqRNji3svplpAV4Ez6ktjwXM,131
|
@@ -78,11 +78,11 @@ plain/signals/dispatch/dispatcher.py,sha256=VxSlqn9PCOTghPPJLOqZPs6FNQZfV2BJpMfF
|
|
78
78
|
plain/signals/dispatch/license.txt,sha256=o9EhDhsC4Q5HbmD-IfNGVTEkXtNE33r5rIt3lleJ8gc,1727
|
79
79
|
plain/signing.py,sha256=V6A6PTDYWekuwtQRI1iFD8dud5OHPZTv4EkeoZEHoXo,8737
|
80
80
|
plain/templates/README.md,sha256=VfA2HmrklG5weE1md85q9g84cWnMBEiXAynKzM7S1Sk,464
|
81
|
-
plain/templates/__init__.py,sha256=
|
81
|
+
plain/templates/__init__.py,sha256=bX76FakE9T7mfK3N0deN85HlwHNQpeigytSC9Z8LcOs,451
|
82
82
|
plain/templates/core.py,sha256=iw58EAmyyv8N5HDA-Sq4-fLgz_qx8v8WJfurgR116jw,625
|
83
|
-
plain/templates/jinja/README.md,sha256=
|
84
|
-
plain/templates/jinja/__init__.py,sha256=
|
85
|
-
plain/templates/jinja/
|
83
|
+
plain/templates/jinja/README.md,sha256=ft4781b4IAVI6fsIdAHIpOigdsZ6wGg06LK7BHxoj-g,6996
|
84
|
+
plain/templates/jinja/__init__.py,sha256=jGjtygVB5Bwknpd3u_pBSrtUSGlFkYo6MDo0E3IWVrs,2653
|
85
|
+
plain/templates/jinja/environments.py,sha256=T5e8rjVteN3-6IWCXNRsvPgMe0OVvTkX-r6_Q9gxhv8,2046
|
86
86
|
plain/templates/jinja/extensions.py,sha256=AEmmmHDbdRW8fhjYDzq9eSSNbp9WHsXenD8tPthjc0s,1351
|
87
87
|
plain/templates/jinja/filters.py,sha256=3KJKKbxcv9dLzUDWPcaa88k3NU2m1GG3iMIgFhzXrBA,860
|
88
88
|
plain/templates/jinja/globals.py,sha256=qhvQuikkRkOTpHSW5FwdsvoViJNlRgHq3-O7ZyeajsE,669
|
@@ -139,8 +139,8 @@ plain/views/objects.py,sha256=9QBYyb8PgkRirXCQ8-Pms4_yMzP37dfeL30hWRYmtZg,7909
|
|
139
139
|
plain/views/redirect.py,sha256=KLnlktzK6ZNMTlaEiZpMKQMEP5zeTgGLJ9BIkIJfwBo,1733
|
140
140
|
plain/views/templates.py,sha256=nF9CcdhhjAyp3LB0RrSYnBaHpHzMfPSw719RCdcXk7o,2007
|
141
141
|
plain/wsgi.py,sha256=R6k5FiAElvGDApEbMPTT0MPqSD7n2e2Az5chQqJZU0I,236
|
142
|
-
plain-0.
|
143
|
-
plain-0.
|
144
|
-
plain-0.
|
145
|
-
plain-0.
|
146
|
-
plain-0.
|
142
|
+
plain-0.13.1.dist-info/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
143
|
+
plain-0.13.1.dist-info/METADATA,sha256=YwduCEEt6PAHfsEylk-zaY-ThC2V_T01w6zPm0R13UQ,2722
|
144
|
+
plain-0.13.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
145
|
+
plain-0.13.1.dist-info/entry_points.txt,sha256=7O1RZTmMasKYB73bfqQcTwIhsXo7RjEIKv2WbtTtOIM,39
|
146
|
+
plain-0.13.1.dist-info/RECORD,,
|
@@ -1,119 +0,0 @@
|
|
1
|
-
import functools
|
2
|
-
from importlib import import_module
|
3
|
-
from pathlib import Path
|
4
|
-
|
5
|
-
from jinja2 import Environment, StrictUndefined
|
6
|
-
|
7
|
-
from plain.packages import packages
|
8
|
-
from plain.runtime import settings
|
9
|
-
from plain.utils.module_loading import import_string, module_has_submodule
|
10
|
-
|
11
|
-
from .filters import default_filters
|
12
|
-
from .globals import default_globals
|
13
|
-
|
14
|
-
|
15
|
-
@functools.lru_cache
|
16
|
-
def _get_app_template_dirs():
|
17
|
-
"""
|
18
|
-
Return an iterable of paths of directories to load app templates from.
|
19
|
-
|
20
|
-
dirname is the name of the subdirectory containing templates inside
|
21
|
-
installed applications.
|
22
|
-
"""
|
23
|
-
dirname = "templates"
|
24
|
-
template_dirs = [
|
25
|
-
Path(package_config.path) / dirname
|
26
|
-
for package_config in packages.get_package_configs()
|
27
|
-
if package_config.path and (Path(package_config.path) / dirname).is_dir()
|
28
|
-
]
|
29
|
-
# Immutable return value because it will be cached and shared by callers.
|
30
|
-
return tuple(template_dirs)
|
31
|
-
|
32
|
-
|
33
|
-
def _get_installed_extensions() -> tuple[list, dict, dict]:
|
34
|
-
"""Automatically load extensions, globals, filters from INSTALLED_PACKAGES jinja module and root jinja module"""
|
35
|
-
extensions = []
|
36
|
-
globals = {}
|
37
|
-
filters = {}
|
38
|
-
|
39
|
-
for package_config in packages.get_package_configs():
|
40
|
-
if module_has_submodule(package_config.module, "jinja"):
|
41
|
-
module = import_module(f"{package_config.name}.jinja")
|
42
|
-
else:
|
43
|
-
continue
|
44
|
-
|
45
|
-
if hasattr(module, "extensions"):
|
46
|
-
extensions.extend(module.extensions)
|
47
|
-
|
48
|
-
if hasattr(module, "globals"):
|
49
|
-
globals.update(module.globals)
|
50
|
-
|
51
|
-
if hasattr(module, "filters"):
|
52
|
-
filters.update(module.filters)
|
53
|
-
|
54
|
-
try:
|
55
|
-
import jinja
|
56
|
-
|
57
|
-
if hasattr(jinja, "extensions"):
|
58
|
-
extensions.extend(jinja.extensions)
|
59
|
-
|
60
|
-
if hasattr(jinja, "globals"):
|
61
|
-
globals.update(jinja.globals)
|
62
|
-
|
63
|
-
if hasattr(jinja, "filters"):
|
64
|
-
filters.update(jinja.filters)
|
65
|
-
except ImportError:
|
66
|
-
pass
|
67
|
-
|
68
|
-
return extensions, globals, filters
|
69
|
-
|
70
|
-
|
71
|
-
def finalize_callable_error(obj):
|
72
|
-
"""Prevent direct rendering of a callable (likely just forgotten ()) by raising a TypeError"""
|
73
|
-
if callable(obj):
|
74
|
-
raise TypeError(f"{obj} is callable, did you forget parentheses?")
|
75
|
-
|
76
|
-
# TODO find a way to prevent <object representation> from being rendered
|
77
|
-
# if obj.__class__.__str__ is object.__str__:
|
78
|
-
# raise TypeError(f"{obj} does not have a __str__ method")
|
79
|
-
|
80
|
-
return obj
|
81
|
-
|
82
|
-
|
83
|
-
def get_template_dirs():
|
84
|
-
jinja_templates = Path(__file__).parent / "templates"
|
85
|
-
app_templates = settings.path.parent / "templates"
|
86
|
-
return (jinja_templates, app_templates) + _get_app_template_dirs()
|
87
|
-
|
88
|
-
|
89
|
-
def create_default_environment(include_packages=True, **environment_kwargs):
|
90
|
-
"""
|
91
|
-
This default jinja environment, also used by the error rendering and internal views so
|
92
|
-
customization needs to happen by using this function, not settings that hook in internally.
|
93
|
-
"""
|
94
|
-
loader = import_string(settings.JINJA_LOADER)(get_template_dirs())
|
95
|
-
kwargs = {
|
96
|
-
"loader": loader,
|
97
|
-
"autoescape": True,
|
98
|
-
"auto_reload": settings.DEBUG,
|
99
|
-
"undefined": StrictUndefined,
|
100
|
-
"finalize": finalize_callable_error,
|
101
|
-
"extensions": ["jinja2.ext.loopcontrols", "jinja2.ext.debug"],
|
102
|
-
}
|
103
|
-
kwargs.update(**environment_kwargs)
|
104
|
-
env = Environment(**kwargs)
|
105
|
-
|
106
|
-
# Load the top-level defaults
|
107
|
-
env.globals.update(default_globals)
|
108
|
-
env.filters.update(default_filters)
|
109
|
-
|
110
|
-
if include_packages:
|
111
|
-
app_extensions, app_globals, app_filters = _get_installed_extensions()
|
112
|
-
|
113
|
-
for extension in app_extensions:
|
114
|
-
env.add_extension(extension)
|
115
|
-
|
116
|
-
env.globals.update(app_globals)
|
117
|
-
env.filters.update(app_filters)
|
118
|
-
|
119
|
-
return env
|
File without changes
|
File without changes
|
File without changes
|