1 |
commit: 0b3a26d733ea8e8b3fce044be1d2c872138d74d5 |
2 |
Author: Brian Dolbec <dolsen <AT> gentoo <DOT> org> |
3 |
AuthorDate: Sun Jan 20 19:22:27 2013 +0000 |
4 |
Commit: Brian Dolbec <dolsen <AT> gentoo <DOT> org> |
5 |
CommitDate: Thu Jan 1 05:58:05 2015 +0000 |
6 |
URL: http://sources.gentoo.org/gitweb/?p=proj/catalyst.git;a=commit;h=0b3a26d7 |
7 |
|
8 |
Some spacing, comment and indent cleanup |
9 |
|
10 |
--- |
11 |
catalyst/support.py | 405 +++++++++++++++++++++++++++------------------------- |
12 |
1 file changed, 213 insertions(+), 192 deletions(-) |
13 |
|
14 |
diff --git a/catalyst/support.py b/catalyst/support.py |
15 |
index feaa645..e2d64a1 100644 |
16 |
--- a/catalyst/support.py |
17 |
+++ b/catalyst/support.py |
18 |
@@ -1,12 +1,19 @@ |
19 |
|
20 |
-import sys,string,os,types,re,signal,traceback,time |
21 |
-#import md5,sha |
22 |
+import sys |
23 |
+import string |
24 |
+import os |
25 |
+import types |
26 |
+import re |
27 |
+import signal |
28 |
+import traceback |
29 |
+import time |
30 |
|
31 |
from catalyst.defaults import verbosity, valid_config_file_values |
32 |
|
33 |
selinux_capable = False |
34 |
#userpriv_capable = (os.getuid() == 0) |
35 |
#fakeroot_capable = False |
36 |
+ |
37 |
BASH_BINARY = "/bin/bash" |
38 |
|
39 |
# set it to 0 for the soft limit, 1 for the hard limit |
40 |
@@ -25,35 +32,35 @@ spawned_pids = [] |
41 |
|
42 |
|
43 |
def cleanup(pids,block_exceptions=True): |
44 |
- """function to go through and reap the list of pids passed to it""" |
45 |
- global spawned_pids |
46 |
- if type(pids) == int: |
47 |
- pids = [pids] |
48 |
- for x in pids: |
49 |
- try: |
50 |
- os.kill(x,signal.SIGTERM) |
51 |
- if os.waitpid(x,os.WNOHANG)[1] == 0: |
52 |
- # feisty bugger, still alive. |
53 |
- os.kill(x,signal.SIGKILL) |
54 |
- os.waitpid(x,0) |
55 |
- |
56 |
- except OSError, oe: |
57 |
- if block_exceptions: |
58 |
- pass |
59 |
- if oe.errno not in (10,3): |
60 |
- raise oe |
61 |
- except SystemExit: |
62 |
- raise |
63 |
- except Exception: |
64 |
- if block_exceptions: |
65 |
- pass |
66 |
- try: spawned_pids.remove(x) |
67 |
- except IndexError: pass |
68 |
- |
69 |
- |
70 |
- |
71 |
-# a function to turn a string of non-printable characters into a string of |
72 |
-# hex characters |
73 |
+ """function to go through and reap the list of pids passed to it""" |
74 |
+ global spawned_pids |
75 |
+ if type(pids) == int: |
76 |
+ pids = [pids] |
77 |
+ for x in pids: |
78 |
+ try: |
79 |
+ os.kill(x,signal.SIGTERM) |
80 |
+ if os.waitpid(x,os.WNOHANG)[1] == 0: |
81 |
+ # feisty bugger, still alive. |
82 |
+ os.kill(x,signal.SIGKILL) |
83 |
+ os.waitpid(x,0) |
84 |
+ except OSError, oe: |
85 |
+ if block_exceptions: |
86 |
+ pass |
87 |
+ if oe.errno not in (10,3): |
88 |
+ raise oe |
89 |
+ except SystemExit: |
90 |
+ raise |
91 |
+ except Exception: |
92 |
+ if block_exceptions: |
93 |
+ pass |
94 |
+ try: |
95 |
+ spawned_pids.remove(x) |
96 |
+ except IndexError: |
97 |
+ pass |
98 |
+ |
99 |
+ |
100 |
+# a function to turn a string of non-printable characters |
101 |
+# into a string of hex characters |
102 |
def hexify(str): |
103 |
hexStr = string.hexdigits |
104 |
r = '' |
105 |
@@ -61,7 +68,6 @@ def hexify(str): |
106 |
i = ord(ch) |
107 |
r = r + hexStr[(i >> 4) & 0xF] + hexStr[i & 0xF] |
108 |
return r |
109 |
-# hexify() |
110 |
|
111 |
|
112 |
def read_from_clst(file): |
113 |
@@ -77,7 +83,6 @@ def read_from_clst(file): |
114 |
myline = myline + line |
115 |
myf.close() |
116 |
return myline |
117 |
-# read_from_clst |
118 |
|
119 |
|
120 |
def list_bashify(mylist): |
121 |
@@ -92,6 +97,7 @@ def list_bashify(mylist): |
122 |
mypack=string.join(mypack) |
123 |
return mypack |
124 |
|
125 |
+ |
126 |
def list_to_string(mylist): |
127 |
if type(mylist)==types.StringType: |
128 |
mypack=[mylist] |
129 |
@@ -104,6 +110,7 @@ def list_to_string(mylist): |
130 |
mypack=string.join(mypack) |
131 |
return mypack |
132 |
|
133 |
+ |
134 |
class CatalystError(Exception): |
135 |
def __init__(self, message): |
136 |
if message: |
137 |
@@ -115,84 +122,83 @@ class CatalystError(Exception): |
138 |
print "!!! catalyst: "+message |
139 |
print |
140 |
|
141 |
+ |
142 |
def die(msg=None): |
143 |
warn(msg) |
144 |
sys.exit(1) |
145 |
|
146 |
+ |
147 |
def warn(msg): |
148 |
print "!!! catalyst: "+msg |
149 |
|
150 |
+ |
151 |
def find_binary(myc): |
152 |
"""look through the environmental path for an executable file named whatever myc is""" |
153 |
- # this sucks. badly. |
154 |
- p=os.getenv("PATH") |
155 |
- if p == None: |
156 |
- return None |
157 |
- for x in p.split(":"): |
158 |
- #if it exists, and is executable |
159 |
- if os.path.exists("%s/%s" % (x,myc)) and os.stat("%s/%s" % (x,myc))[0] & 0x0248: |
160 |
- return "%s/%s" % (x,myc) |
161 |
- return None |
162 |
+ # this sucks. badly. |
163 |
+ p=os.getenv("PATH") |
164 |
+ if p == None: |
165 |
+ return None |
166 |
+ for x in p.split(":"): |
167 |
+ #if it exists, and is executable |
168 |
+ if os.path.exists("%s/%s" % (x,myc)) and os.stat("%s/%s" % (x,myc))[0] & 0x0248: |
169 |
+ return "%s/%s" % (x,myc) |
170 |
+ return None |
171 |
+ |
172 |
|
173 |
def spawn_bash(mycommand,env={},debug=False,opt_name=None,**keywords): |
174 |
"""spawn mycommand as an arguement to bash""" |
175 |
args=[BASH_BINARY] |
176 |
if not opt_name: |
177 |
- opt_name=mycommand.split()[0] |
178 |
+ opt_name=mycommand.split()[0] |
179 |
if "BASH_ENV" not in env: |
180 |
- env["BASH_ENV"] = "/etc/spork/is/not/valid/profile.env" |
181 |
+ env["BASH_ENV"] = "/etc/spork/is/not/valid/profile.env" |
182 |
if debug: |
183 |
- args.append("-x") |
184 |
+ args.append("-x") |
185 |
args.append("-c") |
186 |
args.append(mycommand) |
187 |
return spawn(args,env=env,opt_name=opt_name,**keywords) |
188 |
|
189 |
-#def spawn_get_output(mycommand,spawn_type=spawn,raw_exit_code=False,emulate_gso=True, \ |
190 |
-# collect_fds=[1],fd_pipes=None,**keywords): |
191 |
|
192 |
def spawn_get_output(mycommand,raw_exit_code=False,emulate_gso=True, \ |
193 |
- collect_fds=[1],fd_pipes=None,**keywords): |
194 |
- """call spawn, collecting the output to fd's specified in collect_fds list |
195 |
- emulate_gso is a compatability hack to emulate commands.getstatusoutput's return, minus the |
196 |
- requirement it always be a bash call (spawn_type controls the actual spawn call), and minus the |
197 |
- 'lets let log only stdin and let stderr slide by'. |
198 |
- |
199 |
- emulate_gso was deprecated from the day it was added, so convert your code over. |
200 |
- spawn_type is the passed in function to call- typically spawn_bash, spawn, spawn_sandbox, or spawn_fakeroot""" |
201 |
- global selinux_capable |
202 |
- pr,pw=os.pipe() |
203 |
- |
204 |
- #if type(spawn_type) not in [types.FunctionType, types.MethodType]: |
205 |
- # s="spawn_type must be passed a function, not",type(spawn_type),spawn_type |
206 |
- # raise Exception,s |
207 |
- |
208 |
- if fd_pipes==None: |
209 |
- fd_pipes={} |
210 |
- fd_pipes[0] = 0 |
211 |
- |
212 |
- for x in collect_fds: |
213 |
- fd_pipes[x] = pw |
214 |
- keywords["returnpid"]=True |
215 |
- |
216 |
- mypid=spawn_bash(mycommand,fd_pipes=fd_pipes,**keywords) |
217 |
- os.close(pw) |
218 |
- if type(mypid) != types.ListType: |
219 |
- os.close(pr) |
220 |
- return [mypid, "%s: No such file or directory" % mycommand.split()[0]] |
221 |
- |
222 |
- fd=os.fdopen(pr,"r") |
223 |
- mydata=fd.readlines() |
224 |
- fd.close() |
225 |
- if emulate_gso: |
226 |
- mydata=string.join(mydata) |
227 |
- if len(mydata) and mydata[-1] == "\n": |
228 |
- mydata=mydata[:-1] |
229 |
- retval=os.waitpid(mypid[0],0)[1] |
230 |
- cleanup(mypid) |
231 |
- if raw_exit_code: |
232 |
- return [retval,mydata] |
233 |
- retval=process_exit_code(retval) |
234 |
- return [retval, mydata] |
235 |
+ collect_fds=[1],fd_pipes=None,**keywords): |
236 |
+ """call spawn, collecting the output to fd's specified in collect_fds list |
237 |
+ emulate_gso is a compatability hack to emulate commands.getstatusoutput's return, minus the |
238 |
+ requirement it always be a bash call (spawn_type controls the actual spawn call), and minus the |
239 |
+ 'lets let log only stdin and let stderr slide by'. |
240 |
+ |
241 |
+ emulate_gso was deprecated from the day it was added, so convert your code over. |
242 |
+ spawn_type is the passed in function to call- typically spawn_bash, spawn, spawn_sandbox, or spawn_fakeroot""" |
243 |
+ global selinux_capable |
244 |
+ pr,pw=os.pipe() |
245 |
+ |
246 |
+ if fd_pipes==None: |
247 |
+ fd_pipes={} |
248 |
+ fd_pipes[0] = 0 |
249 |
+ |
250 |
+ for x in collect_fds: |
251 |
+ fd_pipes[x] = pw |
252 |
+ keywords["returnpid"]=True |
253 |
+ |
254 |
+ mypid=spawn_bash(mycommand,fd_pipes=fd_pipes,**keywords) |
255 |
+ os.close(pw) |
256 |
+ if type(mypid) != types.ListType: |
257 |
+ os.close(pr) |
258 |
+ return [mypid, "%s: No such file or directory" % mycommand.split()[0]] |
259 |
+ |
260 |
+ fd=os.fdopen(pr,"r") |
261 |
+ mydata=fd.readlines() |
262 |
+ fd.close() |
263 |
+ if emulate_gso: |
264 |
+ mydata=string.join(mydata) |
265 |
+ if len(mydata) and mydata[-1] == "\n": |
266 |
+ mydata=mydata[:-1] |
267 |
+ retval=os.waitpid(mypid[0],0)[1] |
268 |
+ cleanup(mypid) |
269 |
+ if raw_exit_code: |
270 |
+ return [retval,mydata] |
271 |
+ retval=process_exit_code(retval) |
272 |
+ return [retval, mydata] |
273 |
+ |
274 |
|
275 |
# base spawn function |
276 |
def spawn(mycommand,env={},raw_exit_code=False,opt_name=None,fd_pipes=None,returnpid=False,\ |
277 |
@@ -230,8 +236,8 @@ def spawn(mycommand,env={},raw_exit_code=False,opt_name=None,fd_pipes=None,retur |
278 |
return None |
279 |
myc = find_binary(myc) |
280 |
if myc == None: |
281 |
- return None |
282 |
- mypid=[] |
283 |
+ return None |
284 |
+ mypid=[] |
285 |
if logfile: |
286 |
pr,pw=os.pipe() |
287 |
mypid.extend(spawn(('tee','-i','-a',logfile),returnpid=True,fd_pipes={0:pr,1:1,2:2})) |
288 |
@@ -295,77 +301,77 @@ def spawn(mycommand,env={},raw_exit_code=False,opt_name=None,fd_pipes=None,retur |
289 |
if x not in trg_fd: |
290 |
try: |
291 |
os.close(x) |
292 |
- except SystemExit, e: |
293 |
- raise |
294 |
- except: |
295 |
- pass |
296 |
- |
297 |
- # note this order must be preserved- can't change gid/groups if you change uid first. |
298 |
- if selinux_capable and selinux_context: |
299 |
- import selinux |
300 |
- selinux.setexec(selinux_context) |
301 |
- if gid: |
302 |
- os.setgid(gid) |
303 |
- if groups: |
304 |
- os.setgroups(groups) |
305 |
- if uid: |
306 |
- os.setuid(uid) |
307 |
- if umask: |
308 |
- os.umask(umask) |
309 |
- else: |
310 |
- os.umask(022) |
311 |
- |
312 |
- try: |
313 |
- #print "execing", myc, myargs |
314 |
- if func_call: |
315 |
- # either use a passed in func for interpretting the results, or return if no exception. |
316 |
- # note the passed in list, and dict are expanded. |
317 |
- if len(mycommand) == 4: |
318 |
- os._exit(mycommand[3](mycommand[0](*mycommand[1],**mycommand[2]))) |
319 |
- try: |
320 |
- mycommand[0](*mycommand[1],**mycommand[2]) |
321 |
- except Exception,e: |
322 |
- print "caught exception",e," in forked func",mycommand[0] |
323 |
- sys.exit(0) |
324 |
- |
325 |
- #os.execvp(myc,myargs) |
326 |
- os.execve(myc,myargs,env) |
327 |
- except SystemExit, e: |
328 |
- raise |
329 |
- except Exception, e: |
330 |
- if not func_call: |
331 |
- raise str(e)+":\n "+myc+" "+string.join(myargs) |
332 |
- print "func call failed" |
333 |
- |
334 |
- # If the execve fails, we need to report it, and exit |
335 |
- # *carefully* --- report error here |
336 |
- os._exit(1) |
337 |
- sys.exit(1) |
338 |
- return # should never get reached |
339 |
- |
340 |
- # if we were logging, kill the pipes. |
341 |
- if logfile: |
342 |
- os.close(pr) |
343 |
- os.close(pw) |
344 |
- |
345 |
- if returnpid: |
346 |
- return mypid |
347 |
- |
348 |
- # loop through pids (typically one, unless logging), either waiting on their death, or waxing them |
349 |
- # if the main pid (mycommand) returned badly. |
350 |
- while len(mypid): |
351 |
- retval=os.waitpid(mypid[-1],0)[1] |
352 |
- if retval != 0: |
353 |
- cleanup(mypid[0:-1],block_exceptions=False) |
354 |
- # at this point we've killed all other kid pids generated via this call. |
355 |
- # return now. |
356 |
- if raw_exit_code: |
357 |
- return retval |
358 |
- return process_exit_code(retval,throw_signals=raise_signals) |
359 |
- else: |
360 |
- mypid.pop(-1) |
361 |
- cleanup(mypid) |
362 |
- return 0 |
363 |
+ except SystemExit, e: |
364 |
+ raise |
365 |
+ except: |
366 |
+ pass |
367 |
+ |
368 |
+ # note this order must be preserved- can't change gid/groups if you change uid first. |
369 |
+ if selinux_capable and selinux_context: |
370 |
+ import selinux |
371 |
+ selinux.setexec(selinux_context) |
372 |
+ if gid: |
373 |
+ os.setgid(gid) |
374 |
+ if groups: |
375 |
+ os.setgroups(groups) |
376 |
+ if uid: |
377 |
+ os.setuid(uid) |
378 |
+ if umask: |
379 |
+ os.umask(umask) |
380 |
+ else: |
381 |
+ os.umask(022) |
382 |
+ |
383 |
+ try: |
384 |
+ #print "execing", myc, myargs |
385 |
+ if func_call: |
386 |
+ # either use a passed in func for interpretting the results, or return if no exception. |
387 |
+ # note the passed in list, and dict are expanded. |
388 |
+ if len(mycommand) == 4: |
389 |
+ os._exit(mycommand[3](mycommand[0](*mycommand[1],**mycommand[2]))) |
390 |
+ try: |
391 |
+ mycommand[0](*mycommand[1],**mycommand[2]) |
392 |
+ except Exception,e: |
393 |
+ print "caught exception",e," in forked func",mycommand[0] |
394 |
+ sys.exit(0) |
395 |
+ |
396 |
+ os.execve(myc,myargs,env) |
397 |
+ except SystemExit, e: |
398 |
+ raise |
399 |
+ except Exception, e: |
400 |
+ if not func_call: |
401 |
+ raise str(e)+":\n "+myc+" "+string.join(myargs) |
402 |
+ print "func call failed" |
403 |
+ |
404 |
+ # If the execve fails, we need to report it, and exit |
405 |
+ # *carefully* --- report error here |
406 |
+ os._exit(1) |
407 |
+ sys.exit(1) |
408 |
+ return # should never get reached |
409 |
+ |
410 |
+ # if we were logging, kill the pipes. |
411 |
+ if logfile: |
412 |
+ os.close(pr) |
413 |
+ os.close(pw) |
414 |
+ |
415 |
+ if returnpid: |
416 |
+ return mypid |
417 |
+ |
418 |
+ # loop through pids (typically one, unless logging), either waiting on their death, or waxing them |
419 |
+ # if the main pid (mycommand) returned badly. |
420 |
+ while len(mypid): |
421 |
+ retval=os.waitpid(mypid[-1],0)[1] |
422 |
+ if retval != 0: |
423 |
+ cleanup(mypid[0:-1],block_exceptions=False) |
424 |
+ # at this point we've killed all other kid pids generated via this call. |
425 |
+ # return now. |
426 |
+ if raw_exit_code: |
427 |
+ return retval |
428 |
+ return process_exit_code(retval,throw_signals=raise_signals) |
429 |
+ else: |
430 |
+ mypid.pop(-1) |
431 |
+ cleanup(mypid) |
432 |
+ return 0 |
433 |
+ |
434 |
|
435 |
def cmd(mycmd,myexc="",env={}): |
436 |
try: |
437 |
@@ -376,19 +382,21 @@ def cmd(mycmd,myexc="",env={}): |
438 |
except: |
439 |
raise |
440 |
|
441 |
+ |
442 |
def process_exit_code(retval,throw_signals=False): |
443 |
- """process a waitpid returned exit code, returning exit code if it exit'd, or the |
444 |
- signal if it died from signalling |
445 |
- if throw_signals is on, it raises a SystemExit if the process was signaled. |
446 |
- This is intended for usage with threads, although at the moment you can't signal individual |
447 |
- threads in python, only the master thread, so it's a questionable option.""" |
448 |
- if (retval & 0xff)==0: |
449 |
- return retval >> 8 # return exit code |
450 |
- else: |
451 |
- if throw_signals: |
452 |
- #use systemexit, since portage is stupid about exception catching. |
453 |
- raise SystemExit() |
454 |
- return (retval & 0xff) << 8 # interrupted by signal |
455 |
+ """process a waitpid returned exit code, returning exit code if it exit'd, or the |
456 |
+ signal if it died from signalling |
457 |
+ if throw_signals is on, it raises a SystemExit if the process was signaled. |
458 |
+ This is intended for usage with threads, although at the moment you can't signal individual |
459 |
+ threads in python, only the master thread, so it's a questionable option.""" |
460 |
+ if (retval & 0xff)==0: |
461 |
+ return retval >> 8 # return exit code |
462 |
+ else: |
463 |
+ if throw_signals: |
464 |
+ #use systemexit, since portage is stupid about exception catching. |
465 |
+ raise SystemExit() |
466 |
+ return (retval & 0xff) << 8 # interrupted by signal |
467 |
+ |
468 |
|
469 |
def file_locate(settings,filelist,expand=1): |
470 |
#if expand=1, non-absolute paths will be accepted and |
471 |
@@ -398,15 +406,18 @@ def file_locate(settings,filelist,expand=1): |
472 |
#filenames such as cdtar are optional, so we don't assume the variable is defined. |
473 |
pass |
474 |
else: |
475 |
- if len(settings[myfile])==0: |
476 |
- raise CatalystError, "File variable \""+myfile+"\" has a length of zero (not specified.)" |
477 |
- if settings[myfile][0]=="/": |
478 |
- if not os.path.exists(settings[myfile]): |
479 |
- raise CatalystError, "Cannot locate specified "+myfile+": "+settings[myfile] |
480 |
- elif expand and os.path.exists(os.getcwd()+"/"+settings[myfile]): |
481 |
- settings[myfile]=os.getcwd()+"/"+settings[myfile] |
482 |
- else: |
483 |
- raise CatalystError, "Cannot locate specified "+myfile+": "+settings[myfile]+" (2nd try)" |
484 |
+ if len(settings[myfile])==0: |
485 |
+ raise CatalystError("File variable \"" + myfile + |
486 |
+ "\" has a length of zero (not specified.)") |
487 |
+ if settings[myfile][0]=="/": |
488 |
+ if not os.path.exists(settings[myfile]): |
489 |
+ raise CatalystError("Cannot locate specified " + myfile + |
490 |
+ ": "+settings[myfile]) |
491 |
+ elif expand and os.path.exists(os.getcwd()+"/"+settings[myfile]): |
492 |
+ settings[myfile]=os.getcwd()+"/"+settings[myfile] |
493 |
+ else: |
494 |
+ raise CatalystError("Cannot locate specified " + myfile + |
495 |
+ ": "+settings[myfile]+" (2nd try)" + |
496 |
""" |
497 |
Spec file format: |
498 |
|
499 |
@@ -427,6 +438,8 @@ that the order of multiple-value items is preserved, but the order that the item |
500 |
defined are not preserved. In other words, "foo", "bar", "oni" ordering is preserved but "item1" |
501 |
"item2" "item3" ordering is not, as the item strings are stored in a dictionary (hash). |
502 |
""" |
503 |
+ ) |
504 |
+ |
505 |
|
506 |
def parse_makeconf(mylines): |
507 |
mymakeconf={} |
508 |
@@ -450,6 +463,7 @@ def parse_makeconf(mylines): |
509 |
mymakeconf[mobj.group(1)]=clean_string |
510 |
return mymakeconf |
511 |
|
512 |
+ |
513 |
def read_makeconf(mymakeconffile): |
514 |
if os.path.exists(mymakeconffile): |
515 |
try: |
516 |
@@ -475,10 +489,12 @@ def read_makeconf(mymakeconffile): |
517 |
makeconf={} |
518 |
return makeconf |
519 |
|
520 |
+ |
521 |
def msg(mymsg,verblevel=1): |
522 |
if verbosity>=verblevel: |
523 |
print mymsg |
524 |
|
525 |
+ |
526 |
def pathcompare(path1,path2): |
527 |
# Change double slashes to slash |
528 |
path1 = re.sub(r"//",r"/",path1) |
529 |
@@ -491,6 +507,7 @@ def pathcompare(path1,path2): |
530 |
return 1 |
531 |
return 0 |
532 |
|
533 |
+ |
534 |
def ismount(path): |
535 |
"enhanced to handle bind mounts" |
536 |
if os.path.ismount(path): |
537 |
@@ -504,6 +521,7 @@ def ismount(path): |
538 |
return 1 |
539 |
return 0 |
540 |
|
541 |
+ |
542 |
def addl_arg_parse(myspec,addlargs,requiredspec,validspec): |
543 |
"helper function to help targets parse additional arguments" |
544 |
global valid_config_file_values |
545 |
@@ -522,6 +540,7 @@ def addl_arg_parse(myspec,addlargs,requiredspec,validspec): |
546 |
if messages: |
547 |
raise CatalystError, '\n\tAlso: '.join(messages) |
548 |
|
549 |
+ |
550 |
def touch(myfile): |
551 |
try: |
552 |
myf=open(myfile,"w") |
553 |
@@ -529,8 +548,9 @@ def touch(myfile): |
554 |
except IOError: |
555 |
raise CatalystError, "Could not touch "+myfile+"." |
556 |
|
557 |
+ |
558 |
def countdown(secs=5, doing="Starting"): |
559 |
- if secs: |
560 |
+ if secs: |
561 |
print ">>> Waiting",secs,"seconds before starting..." |
562 |
print ">>> (Control-C to abort)...\n"+doing+" in: ", |
563 |
ticks=range(secs) |
564 |
@@ -541,14 +561,15 @@ def countdown(secs=5, doing="Starting"): |
565 |
time.sleep(1) |
566 |
print |
567 |
|
568 |
+ |
569 |
def normpath(mypath): |
570 |
TrailingSlash=False |
571 |
- if mypath[-1] == "/": |
572 |
- TrailingSlash=True |
573 |
- newpath = os.path.normpath(mypath) |
574 |
- if len(newpath) > 1: |
575 |
- if newpath[:2] == "//": |
576 |
- newpath = newpath[1:] |
577 |
+ if mypath[-1] == "/": |
578 |
+ TrailingSlash=True |
579 |
+ newpath = os.path.normpath(mypath) |
580 |
+ if len(newpath) > 1: |
581 |
+ if newpath[:2] == "//": |
582 |
+ newpath = newpath[1:] |
583 |
if TrailingSlash: |
584 |
- newpath=newpath+'/' |
585 |
- return newpath |
586 |
+ newpath=newpath+'/' |
587 |
+ return newpath |