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 |