Coverage for gws-app/gws/plugin/template/text/__init__.py: 52%
69 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
1"""Text templates.
3The ``text`` template is written in the Jump template language, like the
4``html`` template, but has none of its custom commands (``@map``,
5``@legend``, ``@page`` and so on) and produces text output only. The
6output MIME type is the ``mimeOut`` of the render input, or the first
7configured ``mimeTypes`` entry, or plain text. If a template explicitly
8returns a :obj:`gws.Response` object, it is returned as the render result.
10Example::
12 templates+ {
13 subject "feature.label"
14 type "text"
15 text "{{name}}"
16 }
17"""
19from typing import Optional
21import gws
22import gws.base.legend
23import gws.base.template
24import gws.gis.render
25import gws.lib.htmlx
26import gws.lib.mime
27import gws.lib.osx
28import gws.lib.pdf
29import gws.lib.vendor.jump
32@gws.ext.config.template('text')
33class Config(gws.base.template.Config):
34 """Jump template for plain text output."""
36 path: Optional[gws.FilePath]
37 """Template file."""
38 text: str = ''
39 """Template source."""
42@gws.ext.props.template('text')
43class Props(gws.base.template.Props):
44 pass
47@gws.ext.object.template('text')
48class Object(gws.base.template.Object):
49 """Jump template for text output."""
51 path: str
52 """Template file path."""
53 text: str
54 """Template source."""
55 compiledTime: float = 0
56 """Time of the last compilation."""
57 compiledFn = None
58 """Compiled template function."""
60 def configure(self):
61 self.path = self.cfg('path')
62 self.text = self.cfg('text', default='')
63 if not self.path and not self.text:
64 raise gws.Error('either "path" or "text" required')
66 def render(self, tri):
67 self.notify(tri, 'begin_print')
69 engine = Engine()
70 self.compile(engine)
72 args = self.prepare_args(tri)
73 res = engine.call(self.compiledFn, args=args, error=self.error_handler)
75 if not isinstance(res, gws.Response):
76 res = self.finalize(tri, res, args, engine)
78 self.notify(tri, 'end_print')
79 return res
81 def compile(self, engine: 'Engine'):
82 """Compile the template if needed.
84 The template file is read again if it has changed since the last
85 compilation. With the developer option ``template.always_reload``,
86 the template is compiled on each call; with
87 ``template.save_compiled``, the translated source is written to a
88 debug file.
90 Args:
91 engine: Jump engine.
92 """
94 if self.path and (not self.text or gws.lib.osx.file_mtime(self.path) > self.compiledTime):
95 self.text = gws.u.read_file(self.path)
96 self.compiledFn = None
98 if self.root.app.developer_option('template.always_reload'):
99 self.compiledFn = None
101 if not self.compiledFn:
102 gws.log.debug(f'compiling {self} {self.path=}')
103 if self.root.app.developer_option('template.save_compiled'):
104 gws.u.write_debug_file(f'compiled_template_{self.uid}', engine.translate(self.text, path=self.path))
106 self.compiledFn = engine.compile(self.text, path=self.path)
107 self.compiledTime = gws.u.utime()
109 def error_handler(self, exc, path, line, env):
110 """Handle a template runtime error.
112 The error is logged. With the developer option
113 ``template.raise_errors``, the error is raised, otherwise rendering
114 continues.
116 Args:
117 exc: The exception.
118 path: Template path.
119 line: Template line.
120 env: Template environment.
122 Returns:
123 ``True`` to continue rendering, ``False`` to raise the error.
124 """
125 if self.root.app.developer_option('template.raise_errors'):
126 gws.log.error(f'TEMPLATE_ERROR: {self}: {exc} IN {path}:{line}')
127 return False
129 gws.log.warning(f'TEMPLATE_ERROR: {self}: {exc} IN {path}:{line}')
130 return True
132 ##
134 def finalize(self, tri: gws.TemplateRenderInput, res: str, args: dict, main_engine: 'Engine'):
135 """Wrap the generated text in a content response.
137 Args:
138 tri: Template render input.
139 res: Generated text.
140 args: Template arguments.
141 main_engine: Engine that rendered the text.
143 Returns:
144 Content response.
145 """
146 self.notify(tri, 'finalize_print')
148 mime_type = tri.mimeOut
149 if not mime_type and self.mimeTypes:
150 mime_type = self.mimeTypes[0]
151 if not mime_type:
152 mime_type = gws.lib.mime.TXT
154 return gws.ContentResponse(mimeType=mime_type, content=res)
157##
160class Engine(gws.lib.vendor.jump.Engine):
161 """Jump engine for text templates, without custom commands."""
163 pass