Michael Merickel
2018-10-15 bda1306749c62ef4f11cfe567ed7d56c8ad94240
commit | author | age
0e0cb7 1 # (c) 2005 Ian Bicking and contributors; written for Paste
CM 2 # (http://pythonpaste.org) Licensed under the MIT license:
3 # http://www.opensource.org/licenses/mit-license.php
4
5 import re
6 import sys
7 import os
8
0c29cf 9 from pyramid.compat import native_, bytes_
0e0cb7 10
CM 11 from pyramid.scaffolds import copydir
12
13 fsenc = sys.getfilesystemencoding()
14
0c29cf 15
0e0cb7 16 class Template(object):
bfd4b3 17     """ Inherit from this base class and override methods to use the Pyramid
CM 18     scaffolding system."""
0c29cf 19
MM 20     copydir = copydir  # for testing
71260a 21     _template_dir = None
0e0cb7 22
CM 23     def __init__(self, name):
24         self.name = name
25
bfd4b3 26     def render_template(self, content, vars, filename=None):
CM 27         """ Return a bytestring representing a templated file based on the
28         input (content) and the variable names defined (vars).  ``filename``
29         is used for exception reporting."""
30         # this method must not be named "template_renderer" fbo of extension
31         # scaffolds that need to work under pyramid 1.2 and 1.3, and which
32         # need to do "template_renderer =
33         # staticmethod(paste_script_template_renderer)"
0e0cb7 34         content = native_(content, fsenc)
CM 35         try:
36             return bytes_(
db388f 37                 substitute_escaped_double_braces(
0c29cf 38                     substitute_double_braces(content, TypeMapper(vars))
MM 39                 ),
40                 fsenc,
41             )
0e0cb7 42         except Exception as e:
CM 43             _add_except(e, ' in file %s' % filename)
44             raise
45
46     def module_dir(self):
47         mod = sys.modules[self.__class__.__module__]
48         return os.path.dirname(mod.__file__)
49
50     def template_dir(self):
bfd4b3 51         """ Return the template directory of the scaffold.  By default, it
CM 52         returns the value of ``os.path.join(self.module_dir(),
53         self._template_dir)`` (``self.module_dir()`` returns the module in
54         which your subclass has been defined).  If ``self._template_dir`` is
55         a tuple this method just returns the value instead of trying to
56         construct a path.  If _template_dir is a tuple, it should be a
57         2-element tuple: ``(package_name, package_relative_path)``."""
0e0cb7 58         assert self._template_dir is not None, (
0c29cf 59             "Template %r didn't set _template_dir" % self
MM 60         )
bfd4b3 61         if isinstance(self._template_dir, tuple):
0e0cb7 62             return self._template_dir
CM 63         else:
64             return os.path.join(self.module_dir(), self._template_dir)
65
66     def run(self, command, output_dir, vars):
67         self.pre(command, output_dir, vars)
68         self.write_files(command, output_dir, vars)
69         self.post(command, output_dir, vars)
70
0c29cf 71     def pre(self, command, output_dir, vars):  # pragma: no cover
0e0cb7 72         """
CM 73         Called before template is applied.
74         """
75         pass
76
0c29cf 77     def post(self, command, output_dir, vars):  # pragma: no cover
0e0cb7 78         """
CM 79         Called after template is applied.
80         """
81         pass
82
83     def write_files(self, command, output_dir, vars):
84         template_dir = self.template_dir()
71260a 85         if not self.exists(output_dir):
CM 86             self.out("Creating directory %s" % output_dir)
b7f994 87             if not command.args.simulate:
0e0cb7 88                 # Don't let copydir create this top-level directory,
CM 89                 # since copydir will svn add it sometimes:
71260a 90                 self.makedirs(output_dir)
CM 91         self.copydir.copy_dir(
92             template_dir,
93             output_dir,
94             vars,
b77e7f 95             verbosity=command.verbosity,
b7f994 96             simulate=command.args.simulate,
SP 97             interactive=command.args.interactive,
98             overwrite=command.args.overwrite,
71260a 99             indent=1,
bfd4b3 100             template_renderer=self.render_template,
0c29cf 101         )
0e0cb7 102
0c29cf 103     def makedirs(self, dir):  # pragma: no cover
71260a 104         return os.makedirs(dir)
CM 105
0c29cf 106     def exists(self, path):  # pragma: no cover
71260a 107         return os.path.exists(path)
CM 108
0c29cf 109     def out(self, msg):  # pragma: no cover
71260a 110         print(msg)
0e0cb7 111
bfd4b3 112     # hair for exit with usage when paster create is used under 1.3 instead
CM 113     # of pcreate for extension scaffolds which need to support multiple
114     # versions of pyramid; the check_vars method is called by pastescript
115     # only as the result of "paster create"; pyramid doesn't use it.  the
116     # required_templates tuple is required to allow it to get as far as
117     # calling check_vars.
118     required_templates = ()
0c29cf 119
bfd4b3 120     def check_vars(self, vars, other):
CM 121         raise RuntimeError(
122             'Under Pyramid 1.3, you should use the "pcreate" command rather '
0c29cf 123             'than "paster create"'
MM 124         )
125
bfd4b3 126
0e0cb7 127 class TypeMapper(dict):
CM 128     def __getitem__(self, item):
129         options = item.split('|')
130         for op in options[:-1]:
131             try:
132                 value = eval_with_catch(op, dict(self.items()))
133                 break
134             except (NameError, KeyError):
135                 pass
136         else:
137             value = eval(options[-1], dict(self.items()))
138         if value is None:
139             return ''
140         else:
141             return str(value)
142
0c29cf 143
0e0cb7 144 def eval_with_catch(expr, vars):
CM 145     try:
146         return eval(expr, vars)
147     except Exception as e:
148         _add_except(e, 'in expression %r' % expr)
149         raise
150
0c29cf 151
0e0cb7 152 double_brace_pattern = re.compile(r'{{(?P<braced>.*?)}}')
0c29cf 153
0e0cb7 154
CM 155 def substitute_double_braces(content, values):
156     def double_bracerepl(match):
157         value = match.group('braced').strip()
158         return values[value]
0c29cf 159
0e0cb7 160     return double_brace_pattern.sub(double_bracerepl, content)
de6b28 161
0c29cf 162
MM 163 escaped_double_brace_pattern = re.compile(
164     r'\\{\\{(?P<escape_braced>[^\\]*?)\\}\\}'
165 )
166
db388f 167
DF 168 def substitute_escaped_double_braces(content):
169     def escaped_double_bracerepl(match):
170         value = match.group('escape_braced').strip()
171         return "{{%(value)s}}" % locals()
0c29cf 172
db388f 173     return escaped_double_brace_pattern.sub(escaped_double_bracerepl, content)
de6b28 174
0c29cf 175
MM 176 def _add_except(exc, info):  # pragma: no cover
0e0cb7 177     if not hasattr(exc, 'args') or exc.args is None:
CM 178         return
179     args = list(exc.args)
180     if args:
181         args[0] += ' ' + info
182     else:
183         args = [info]
184     exc.args = tuple(args)
185     return