plain 0.12.0__py3-none-any.whl → 0.13.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.
- plain/debug.py +8 -6
- plain/preflight/security.py +2 -1
- 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.0.dist-info → plain-0.13.0.dist-info}/METADATA +1 -1
- {plain-0.12.0.dist-info → plain-0.13.0.dist-info}/RECORD +12 -12
- plain/templates/jinja/defaults.py +0 -119
- {plain-0.12.0.dist-info → plain-0.13.0.dist-info}/LICENSE +0 -0
- {plain-0.12.0.dist-info → plain-0.13.0.dist-info}/WHEEL +0 -0
- {plain-0.12.0.dist-info → plain-0.13.0.dist-info}/entry_points.txt +0 -0
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/preflight/security.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
from plain.exceptions import ImproperlyConfigured
|
2
2
|
from plain.runtime import settings
|
3
3
|
|
4
|
-
from
|
4
|
+
from .messages import Warning
|
5
|
+
from .registry import register
|
5
6
|
|
6
7
|
SECRET_KEY_INSECURE_PREFIX = "plain-insecure-"
|
7
8
|
SECRET_KEY_MIN_LENGTH = 50
|
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)
|
@@ -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
|
@@ -65,11 +65,11 @@ plain/preflight/__init__.py,sha256=H-TNRvaddPtOGmv4RXoc1fxDV1AOb7_K3u7ECF8mV58,6
|
|
65
65
|
plain/preflight/files.py,sha256=wbHCNgps7o1c1zQNBd8FDCaVaqX90UwuvLgEQ_DbUpY,510
|
66
66
|
plain/preflight/messages.py,sha256=u0oc7q7YmBlKYJRcF5SQpzncfOkEzDhZTcpyclQDfHg,2427
|
67
67
|
plain/preflight/registry.py,sha256=ZpxnZPIklXuT8xZVTxCUp_IER3zhd7DdfsmqIpAbLj4,2306
|
68
|
-
plain/preflight/security.py,sha256=
|
68
|
+
plain/preflight/security.py,sha256=sNpv5AHobPcaO48cOUGRNe2EjusTducjY8vyShR8EhI,2645
|
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.0.dist-info/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
143
|
+
plain-0.13.0.dist-info/METADATA,sha256=q1mlnliDkSLRmD-daEgJ6FUgkHbv0MciFYYr5Wp9qUs,2722
|
144
|
+
plain-0.13.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
145
|
+
plain-0.13.0.dist-info/entry_points.txt,sha256=7O1RZTmMasKYB73bfqQcTwIhsXo7RjEIKv2WbtTtOIM,39
|
146
|
+
plain-0.13.0.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
|