Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/pyramid_mako/__init__.py : 17%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import os
2import posixpath
3import sys
5from pyramid.asset import (
6 abspath_from_asset_spec,
7 )
9from pyramid.path import AssetResolver
11from pyramid.settings import asbool, aslist
13from mako.lookup import TemplateLookup
14from mako.exceptions import (
15 TemplateLookupException,
16 TopLevelLookupException,
17 text_error_template,
18)
20from .compat import (
21 is_nonstr_iter,
22 reraise,
23)
25class PkgResourceTemplateLookup(TemplateLookup):
26 """TemplateLookup subclass that handles asset specification URIs"""
27 def adjust_uri(self, uri, relativeto):
28 """Called from within a Mako template, avoids adjusting the
29 uri if it looks like an asset specification"""
30 # Don't adjust asset spec names
31 isabs = os.path.isabs(uri)
32 if (not isabs) and (':' in uri):
33 return uri
34 if not(isabs) and ('$' in uri):
35 return uri.replace('$', ':')
36 if relativeto is not None:
37 relativeto = relativeto.replace('$', ':')
38 if not(':' in uri) and (':' in relativeto):
39 if uri.startswith('/'):
40 return uri
41 pkg, relto = relativeto.split(':')
42 _uri = posixpath.join(posixpath.dirname(relto), uri)
43 return '{0}:{1}'.format(pkg, _uri)
44 if not(':' in uri) and not(':' in relativeto):
45 return posixpath.join(posixpath.dirname(relativeto), uri)
46 return TemplateLookup.adjust_uri(self, uri, relativeto)
48 def get_template(self, uri):
49 """Fetch a template from the cache, or check the filesystem
50 for it
52 In addition to the basic filesystem lookup, this subclass will
53 use pkg_resource to load a file using the asset
54 specification syntax.
56 """
57 isabs = os.path.isabs(uri)
58 if (not isabs) and (':' in uri):
59 # Windows can't cope with colons in filenames, so we replace the
60 # colon with a dollar sign in the filename mako uses to actually
61 # store the generated python code in the mako module_directory or
62 # in the temporary location of mako's modules
63 adjusted = uri.replace(':', '$')
64 try:
65 if self.filesystem_checks:
66 return self._check(adjusted, self._collection[adjusted])
67 else:
68 return self._collection[adjusted]
69 except KeyError:
70 asset = AssetResolver().resolve(uri)
71 if asset.exists():
72 srcfile = asset.abspath()
73 return self._load(srcfile, adjusted)
74 raise TopLevelLookupException(
75 "Can not locate template for uri %r" % uri)
76 try:
77 return TemplateLookup.get_template(self, uri)
78 except TemplateLookupException:
79 if isabs:
80 return self._load(uri, uri)
81 else:
82 raise
84class MakoRenderingException(Exception):
85 def __init__(self, text):
86 self.text = text
88 def __repr__(self):
89 return self.text
91 __str__ = __repr__
93class MakoLookupTemplateRenderer(object):
94 """ Render a :term:`Mako` template using the ``template``.
95 If a ``defname`` is defined, in the form of
96 ``package:path/to/template#defname.mako``, a function named ``defname``
97 inside the ``template`` will then be rendered.
98 """
100 @property
101 def template(self):
102 spec = self.spec
103 isabspath = os.path.isabs(spec)
104 colon_in_name = ':' in spec
105 isabsspec = colon_in_name and (not isabspath)
106 isrelspec = (not isabsspec) and (not isabspath)
108 try:
109 # try to find the template using default search paths
110 template = self.lookup.get_template(spec)
111 except TemplateLookupException:
112 if isrelspec:
113 # convert relative asset spec to absolute asset spec
114 resolver = AssetResolver(self.package)
115 asset = resolver.resolve(spec)
116 spec = asset.absspec()
117 template = self.lookup.get_template(spec)
118 else:
119 raise
121 return template
123 def __init__(self, lookup, spec, defname, package):
124 self.lookup = lookup
125 self.spec = spec
126 self.defname = defname
127 self.package = package
129 def __call__(self, value, system):
130 # Update the system dictionary with the values from the user
131 try:
132 system.update(value)
133 except (TypeError, ValueError):
134 raise ValueError('renderer was passed non-dictionary as value')
136 # Check if 'context' in the dictionary
137 context = system.pop('context', None)
139 # Rename 'context' to '_context' because Mako internally already has a
140 # variable named 'context'
141 if context is not None:
142 system['_context'] = context
144 template = self.template
145 if self.defname is not None:
146 template = template.get_def(self.defname)
147 try:
148 result = template.render_unicode(**system)
149 except:
150 try:
151 exc_info = sys.exc_info()
152 errtext = text_error_template().render(
153 error=exc_info[1],
154 traceback=exc_info[2]
155 )
156 reraise(MakoRenderingException(errtext), None, exc_info[2])
157 finally:
158 del exc_info
160 return result
162class MakoRendererFactory(object):
163 lookup = None
164 renderer_factory = staticmethod(MakoLookupTemplateRenderer) # testing
166 def __call__(self, info):
167 defname = None
168 asset, ext = info.name.rsplit('.', 1)
169 if '#' in asset:
170 asset, defname = asset.rsplit('#', 1)
172 spec = '%s.%s' % (asset, ext)
174 return self.renderer_factory(self.lookup, spec, defname, info.package)
176def parse_options_from_settings(settings, settings_prefix, maybe_dotted):
177 """ Parse options for use with Mako's TemplateLookup from settings."""
178 def sget(name, default=None):
179 return settings.get(settings_prefix + name, default)
181 reload_templates = sget('reload_templates', None)
182 if reload_templates is None:
183 reload_templates = settings.get('pyramid.reload_templates', None)
184 reload_templates = asbool(reload_templates)
185 directories = sget('directories', [])
186 module_directory = sget('module_directory', None)
187 input_encoding = sget('input_encoding', 'utf-8')
188 error_handler = sget('error_handler', None)
189 default_filters = sget('default_filters', 'h')
190 imports = sget('imports', None)
191 future_imports = sget('future_imports', None)
192 strict_undefined = asbool(sget('strict_undefined', False))
193 preprocessor = sget('preprocessor', None)
194 preprocessor_wants_settings = asbool(sget('preprocessor_wants_settings', None))
195 if not is_nonstr_iter(directories):
196 # Since we parse a value that comes from an .ini config,
197 # we treat whitespaces and newline characters equally as list item separators.
198 directories = aslist(directories, flatten=True)
199 directories = [abspath_from_asset_spec(d) for d in directories]
201 if module_directory is not None:
202 module_directory = abspath_from_asset_spec(module_directory)
204 if error_handler is not None:
205 error_handler = maybe_dotted(error_handler)
207 if default_filters is not None:
208 if not is_nonstr_iter(default_filters):
209 default_filters = aslist(default_filters)
211 if imports is not None:
212 if not is_nonstr_iter(imports):
213 imports = aslist(imports, flatten=False)
215 if future_imports is not None:
216 if not is_nonstr_iter(future_imports):
217 future_imports = aslist(future_imports)
219 if preprocessor is not None:
220 preprocessor_function = maybe_dotted(preprocessor)
221 if preprocessor_wants_settings:
222 def preprocessor_injector(template):
223 return preprocessor_function(template, settings)
224 preprocessor = preprocessor_injector
225 else:
226 preprocessor = preprocessor_function
228 return dict(
229 directories=directories,
230 module_directory=module_directory,
231 input_encoding=input_encoding,
232 error_handler=error_handler,
233 default_filters=default_filters,
234 imports=imports,
235 future_imports=future_imports,
236 filesystem_checks=reload_templates,
237 strict_undefined=strict_undefined,
238 preprocessor=preprocessor,
239 )
241def add_mako_renderer(config, extension, settings_prefix='mako.'):
242 """ Register a Mako renderer for a template extension.
244 This function is available on the Pyramid configurator after
245 including the package:
247 .. code-block:: python
249 config.add_mako_renderer('.html', settings_prefix='mako.')
251 The renderer will load its configuration from a prefix in the Pyramid
252 settings dictionary. The default prefix is 'mako.'.
253 """
254 renderer_factory = MakoRendererFactory()
255 config.add_renderer(extension, renderer_factory)
257 def register():
258 registry = config.registry
259 opts = parse_options_from_settings(
260 registry.settings, settings_prefix, config.maybe_dotted)
261 lookup = PkgResourceTemplateLookup(**opts)
263 renderer_factory.lookup = lookup
265 config.action(('mako-renderer', extension), register)
267def includeme(config):
268 """ Set up standard configurator registrations. Use via:
270 .. code-block:: python
272 config = Configurator()
273 config.include('pyramid_mako')
275 Once this function has been invoked, the ``.mako`` and ``.mak`` renderers
276 are available for use in Pyramid. This can be overridden and more may be
277 added via the ``config.add_mako_renderer`` directive. See
278 :func:`~pyramid_mako.add_mako_renderer` documentation for more information.
279 """
280 config.add_directive('add_mako_renderer', add_mako_renderer)
282 config.add_mako_renderer('.mako')
283 config.add_mako_renderer('.mak')