Skip to content

Commit 0e0cb76

Browse files
committed
add pserve command and move template code out of glue; add a wsgiref paste.server_runner entry point
1 parent a30a39a commit 0e0cb76

File tree

7 files changed

+1159
-6
lines changed

7 files changed

+1159
-6
lines changed

pyramid/compat.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,13 @@ def is_nonstr_iter(v):
224224
from html import escape
225225
else:
226226
from cgi import escape
227+
228+
try:
229+
input_ = raw_input
230+
except NameError:
231+
input_ = input
232+
233+
try: # pragma: no cover
234+
import configparser
235+
except ImportError:
236+
import ConfigParser as configparser

pyramid/scaffolds/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
native_
77
)
88

9-
from glue.template import Template
9+
from pyramid.scaffolds.template import Template
1010

1111
class PyramidTemplate(Template):
1212
def pre(self, command, output_dir, vars):

pyramid/scaffolds/copydir.py

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
# (c) 2005 Ian Bicking and contributors; written for Paste
2+
# (http://pythonpaste.org) Licensed under the MIT license:
3+
# http://www.opensource.org/licenses/mit-license.php
4+
5+
import os
6+
import sys
7+
import pkg_resources
8+
import cgi
9+
import urllib
10+
11+
from pyramid.compat import (
12+
input_,
13+
native_
14+
)
15+
16+
fsenc = sys.getfilesystemencoding()
17+
18+
class SkipTemplate(Exception):
19+
"""
20+
Raised to indicate that the template should not be copied over.
21+
Raise this exception during the substitution of your template
22+
"""
23+
24+
def copy_dir(source, dest, vars, verbosity, simulate, indent=0,
25+
sub_vars=True, interactive=False, overwrite=True,
26+
template_renderer=None):
27+
"""
28+
Copies the ``source`` directory to the ``dest`` directory.
29+
30+
``vars``: A dictionary of variables to use in any substitutions.
31+
32+
``verbosity``: Higher numbers will show more about what is happening.
33+
34+
``simulate``: If true, then don't actually *do* anything.
35+
36+
``indent``: Indent any messages by this amount.
37+
38+
``sub_vars``: If true, variables in ``_tmpl`` files and ``+var+``
39+
in filenames will be substituted.
40+
41+
``overwrite``: If false, then don't every overwrite anything.
42+
43+
``interactive``: If you are overwriting a file and interactive is
44+
true, then ask before overwriting.
45+
46+
``template_renderer``: This is a function for rendering templates (if you
47+
don't want to use string.Template). It should have the signature
48+
``template_renderer(content_as_string, vars_as_dict,
49+
filename=filename)``.
50+
"""
51+
# This allows you to use a leading +dot+ in filenames which would
52+
# otherwise be skipped because leading dots make the file hidden:
53+
vars.setdefault('dot', '.')
54+
vars.setdefault('plus', '+')
55+
use_pkg_resources = isinstance(source, tuple)
56+
if use_pkg_resources:
57+
names = sorted(pkg_resources.resource_listdir(source[0], source[1]))
58+
else:
59+
names = sorted(os.listdir(source))
60+
pad = ' '*(indent*2)
61+
if not os.path.exists(dest):
62+
if verbosity >= 1:
63+
print('%sCreating %s/' % (pad, dest))
64+
if not simulate:
65+
makedirs(dest, verbosity=verbosity, pad=pad)
66+
elif verbosity >= 2:
67+
print('%sDirectory %s exists' % (pad, dest))
68+
for name in names:
69+
if use_pkg_resources:
70+
full = '/'.join([source[1], name])
71+
else:
72+
full = os.path.join(source, name)
73+
reason = should_skip_file(name)
74+
if reason:
75+
if verbosity >= 2:
76+
reason = pad + reason % {'filename': full}
77+
print(reason)
78+
continue
79+
if sub_vars:
80+
dest_full = os.path.join(dest, substitute_filename(name, vars))
81+
sub_file = False
82+
if dest_full.endswith('_tmpl'):
83+
dest_full = dest_full[:-5]
84+
sub_file = sub_vars
85+
if use_pkg_resources and pkg_resources.resource_isdir(source[0], full):
86+
if verbosity:
87+
print('%sRecursing into %s' % (pad, os.path.basename(full)))
88+
copy_dir((source[0], full), dest_full, vars, verbosity, simulate,
89+
indent=indent+1,
90+
sub_vars=sub_vars, interactive=interactive,
91+
template_renderer=template_renderer)
92+
continue
93+
elif not use_pkg_resources and os.path.isdir(full):
94+
if verbosity:
95+
print('%sRecursing into %s' % (pad, os.path.basename(full)))
96+
copy_dir(full, dest_full, vars, verbosity, simulate,
97+
indent=indent+1,
98+
sub_vars=sub_vars, interactive=interactive,
99+
template_renderer=template_renderer)
100+
continue
101+
elif use_pkg_resources:
102+
content = pkg_resources.resource_string(source[0], full)
103+
else:
104+
f = open(full, 'rb')
105+
content = f.read()
106+
f.close()
107+
if sub_file:
108+
try:
109+
content = substitute_content(
110+
content, vars, filename=full,
111+
template_renderer=template_renderer
112+
)
113+
except SkipTemplate:
114+
continue
115+
if content is None:
116+
continue
117+
already_exists = os.path.exists(dest_full)
118+
if already_exists:
119+
f = open(dest_full, 'rb')
120+
old_content = f.read()
121+
f.close()
122+
if old_content == content:
123+
if verbosity:
124+
print('%s%s already exists (same content)' %
125+
(pad, dest_full))
126+
continue
127+
if interactive:
128+
if not query_interactive(
129+
native_(full, fsenc), native_(dest_full, fsenc),
130+
native_(content, fsenc), native_(old_content, fsenc),
131+
simulate=simulate):
132+
continue
133+
elif not overwrite:
134+
continue
135+
if verbosity and use_pkg_resources:
136+
print('%sCopying %s to %s' % (pad, full, dest_full))
137+
elif verbosity:
138+
print(
139+
'%sCopying %s to %s' % (pad, os.path.basename(full), dest_full))
140+
if not simulate:
141+
f = open(dest_full, 'wb')
142+
f.write(content)
143+
f.close()
144+
145+
def should_skip_file(name):
146+
"""
147+
Checks if a file should be skipped based on its name.
148+
149+
If it should be skipped, returns the reason, otherwise returns
150+
None.
151+
"""
152+
if name.startswith('.'):
153+
return 'Skipping hidden file %(filename)s'
154+
if name.endswith('~') or name.endswith('.bak'):
155+
return 'Skipping backup file %(filename)s'
156+
if name.endswith('.pyc') or name.endswith('.pyo'):
157+
return 'Skipping %s file %(filename)s' % os.path.splitext(name)[1]
158+
if name.endswith('$py.class'):
159+
return 'Skipping $py.class file %(filename)s'
160+
if name in ('CVS', '_darcs'):
161+
return 'Skipping version control directory %(filename)s'
162+
return None
163+
164+
# Overridden on user's request:
165+
all_answer = None
166+
167+
def query_interactive(src_fn, dest_fn, src_content, dest_content,
168+
simulate):
169+
global all_answer
170+
from difflib import unified_diff, context_diff
171+
u_diff = list(unified_diff(
172+
dest_content.splitlines(),
173+
src_content.splitlines(),
174+
dest_fn, src_fn))
175+
c_diff = list(context_diff(
176+
dest_content.splitlines(),
177+
src_content.splitlines(),
178+
dest_fn, src_fn))
179+
added = len([l for l in u_diff if l.startswith('+')
180+
and not l.startswith('+++')])
181+
removed = len([l for l in u_diff if l.startswith('-')
182+
and not l.startswith('---')])
183+
if added > removed:
184+
msg = '; %i lines added' % (added-removed)
185+
elif removed > added:
186+
msg = '; %i lines removed' % (removed-added)
187+
else:
188+
msg = ''
189+
print('Replace %i bytes with %i bytes (%i/%i lines changed%s)' % (
190+
len(dest_content), len(src_content),
191+
removed, len(dest_content.splitlines()), msg))
192+
prompt = 'Overwrite %s [y/n/d/B/?] ' % dest_fn
193+
while 1:
194+
if all_answer is None:
195+
response = input_(prompt).strip().lower()
196+
else:
197+
response = all_answer
198+
if not response or response[0] == 'b':
199+
import shutil
200+
new_dest_fn = dest_fn + '.bak'
201+
n = 0
202+
while os.path.exists(new_dest_fn):
203+
n += 1
204+
new_dest_fn = dest_fn + '.bak' + str(n)
205+
print('Backing up %s to %s' % (dest_fn, new_dest_fn))
206+
if not simulate:
207+
shutil.copyfile(dest_fn, new_dest_fn)
208+
return True
209+
elif response.startswith('all '):
210+
rest = response[4:].strip()
211+
if not rest or rest[0] not in ('y', 'n', 'b'):
212+
print(query_usage)
213+
continue
214+
response = all_answer = rest[0]
215+
if response[0] == 'y':
216+
return True
217+
elif response[0] == 'n':
218+
return False
219+
elif response == 'dc':
220+
print('\n'.join(c_diff))
221+
elif response[0] == 'd':
222+
print('\n'.join(u_diff))
223+
else:
224+
print(query_usage)
225+
226+
query_usage = """\
227+
Responses:
228+
Y(es): Overwrite the file with the new content.
229+
N(o): Do not overwrite the file.
230+
D(iff): Show a unified diff of the proposed changes (dc=context diff)
231+
B(ackup): Save the current file contents to a .bak file
232+
(and overwrite)
233+
Type "all Y/N/B" to use Y/N/B for answer to all future questions
234+
"""
235+
236+
def makedirs(dir, verbosity, pad):
237+
parent = os.path.dirname(os.path.abspath(dir))
238+
if not os.path.exists(parent):
239+
makedirs(parent, verbosity, pad)
240+
os.mkdir(dir)
241+
242+
def substitute_filename(fn, vars):
243+
for var, value in vars.items():
244+
fn = fn.replace('+%s+' % var, str(value))
245+
return fn
246+
247+
def substitute_content(content, vars, filename='<string>',
248+
template_renderer=None):
249+
v = standard_vars.copy()
250+
v.update(vars)
251+
return template_renderer(content, v, filename=filename)
252+
253+
def html_quote(s):
254+
if s is None:
255+
return ''
256+
return cgi.escape(str(s), 1)
257+
258+
def url_quote(s):
259+
if s is None:
260+
return ''
261+
return urllib.quote(str(s))
262+
263+
def test(conf, true_cond, false_cond=None):
264+
if conf:
265+
return true_cond
266+
else:
267+
return false_cond
268+
269+
def skip_template(condition=True, *args):
270+
"""
271+
Raise SkipTemplate, which causes copydir to skip the template
272+
being processed. If you pass in a condition, only raise if that
273+
condition is true (allows you to use this with string.Template)
274+
275+
If you pass any additional arguments, they will be used to
276+
instantiate SkipTemplate (generally use like
277+
``skip_template(license=='GPL', 'Skipping file; not using GPL')``)
278+
"""
279+
if condition:
280+
raise SkipTemplate(*args)
281+
282+
standard_vars = {
283+
'nothing': None,
284+
'html_quote': html_quote,
285+
'url_quote': url_quote,
286+
'empty': '""',
287+
'test': test,
288+
'repr': repr,
289+
'str': str,
290+
'bool': bool,
291+
'SkipTemplate': SkipTemplate,
292+
'skip_template': skip_template,
293+
}
294+

0 commit comments

Comments
 (0)