blob: b308b90dd0261159eded998def3de3e3eaf4e9dc [file] [log] [blame]
Greg Ward2689e3d1999-03-22 14:52:19 +00001"""distutils.util
2
3General-purpose utility functions used throughout the Distutils
4(especially in command classes). Mostly filesystem manipulation, but
5not limited to that. The functions in this module generally raise
6DistutilsFileError when they have problems with the filesystem, because
7os.error in pre-1.5.2 Python only gives the error message and not the
8file causing it."""
9
10# created 1999/03/08, Greg Ward
11
Greg Ward3ce77fd2000-03-02 01:49:45 +000012__revision__ = "$Id$"
Greg Ward2689e3d1999-03-22 14:52:19 +000013
Greg Ward1b4ede52000-03-22 00:22:44 +000014import os, string, re, shutil
Greg Ward2689e3d1999-03-22 14:52:19 +000015from distutils.errors import *
16
17
Greg Wardac1424a1999-09-21 18:37:51 +000018# cache for by mkpath() -- in addition to cheapening redundant calls,
19# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
20PATH_CREATED = {}
21
Greg Ward2689e3d1999-03-22 14:52:19 +000022# I don't use os.makedirs because a) it's new to Python 1.5.2, and
23# b) it blows up if the directory already exists (I want to silently
24# succeed in that case).
Greg Warde765a3b1999-04-04 02:54:20 +000025def mkpath (name, mode=0777, verbose=0, dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +000026 """Create a directory and any missing ancestor directories. If the
27 directory already exists, return silently. Raise
28 DistutilsFileError if unable to create some directory along the
29 way (eg. some sub-path exists, but is a file rather than a
30 directory). If 'verbose' is true, print a one-line summary of
31 each mkdir to stdout."""
32
Greg Wardac1424a1999-09-21 18:37:51 +000033 global PATH_CREATED
34
Greg Ward2689e3d1999-03-22 14:52:19 +000035 # XXX what's the better way to handle verbosity? print as we create
36 # each directory in the path (the current behaviour), or only announce
Greg Wardac1424a1999-09-21 18:37:51 +000037 # the creation of the whole path? (quite easy to do the latter since
38 # we're not using a recursive algorithm)
Greg Ward2689e3d1999-03-22 14:52:19 +000039
Greg Wardf3b997a1999-10-03 20:50:41 +000040 name = os.path.normpath (name)
41
Greg Ward96182d72000-03-03 03:00:02 +000042 if os.path.isdir (name) or name == '':
Greg Ward2689e3d1999-03-22 14:52:19 +000043 return
Greg Wardac1424a1999-09-21 18:37:51 +000044 if PATH_CREATED.get (name):
45 return
Greg Ward2689e3d1999-03-22 14:52:19 +000046
47 (head, tail) = os.path.split (name)
48 tails = [tail] # stack of lone dirs to create
49
50 while head and tail and not os.path.isdir (head):
51 #print "splitting '%s': " % head,
52 (head, tail) = os.path.split (head)
53 #print "to ('%s','%s')" % (head, tail)
54 tails.insert (0, tail) # push next higher dir onto stack
55
56 #print "stack of tails:", tails
57
Greg Warde765a3b1999-04-04 02:54:20 +000058 # now 'head' contains the deepest directory that already exists
59 # (that is, the child of 'head' in 'name' is the highest directory
60 # that does *not* exist)
Greg Ward2689e3d1999-03-22 14:52:19 +000061 for d in tails:
62 #print "head = %s, d = %s: " % (head, d),
63 head = os.path.join (head, d)
Greg Wardcd1486f1999-09-29 12:14:16 +000064 if PATH_CREATED.get (head):
65 continue
66
Greg Ward2689e3d1999-03-22 14:52:19 +000067 if verbose:
68 print "creating", head
Greg Warde765a3b1999-04-04 02:54:20 +000069
70 if not dry_run:
71 try:
72 os.mkdir (head)
73 except os.error, (errno, errstr):
Greg Ward1b4ede52000-03-22 00:22:44 +000074 raise DistutilsFileError, \
75 "could not create '%s': %s" % (head, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +000076
Greg Wardac1424a1999-09-21 18:37:51 +000077 PATH_CREATED[head] = 1
78
Greg Ward2689e3d1999-03-22 14:52:19 +000079# mkpath ()
80
81
Greg Ward138ce651999-09-13 03:09:38 +000082def newer (source, target):
83 """Return true if 'source' exists and is more recently modified than
84 'target', or if 'source' exists and 'target' doesn't. Return
85 false if both exist and 'target' is the same age or younger than
86 'source'. Raise DistutilsFileError if 'source' does not
87 exist."""
Greg Ward2689e3d1999-03-22 14:52:19 +000088
Greg Ward138ce651999-09-13 03:09:38 +000089 if not os.path.exists (source):
90 raise DistutilsFileError, "file '%s' does not exist" % source
91 if not os.path.exists (target):
Greg Ward2689e3d1999-03-22 14:52:19 +000092 return 1
93
Greg Ward138ce651999-09-13 03:09:38 +000094 from stat import ST_MTIME
95 mtime1 = os.stat(source)[ST_MTIME]
96 mtime2 = os.stat(target)[ST_MTIME]
Greg Ward2689e3d1999-03-22 14:52:19 +000097
98 return mtime1 > mtime2
99
100# newer ()
101
102
Greg Ward138ce651999-09-13 03:09:38 +0000103def newer_pairwise (sources, targets):
Greg Ward95526652000-03-06 03:44:32 +0000104 """Walk two filename lists in parallel, testing if each source is newer
105 than its corresponding target. Return a pair of lists (sources,
106 targets) where source is newer than target, according to the
107 semantics of 'newer()'."""
Greg Ward138ce651999-09-13 03:09:38 +0000108
109 if len (sources) != len (targets):
110 raise ValueError, "'sources' and 'targets' must be same length"
111
Greg Ward95526652000-03-06 03:44:32 +0000112 # build a pair of lists (sources, targets) where source is newer
113 n_sources = []
114 n_targets = []
115 for i in range (len (sources)):
116 if newer (sources[i], targets[i]):
117 n_sources.append (sources[i])
118 n_targets.append (targets[i])
119
120 return (n_sources, n_targets)
Greg Ward138ce651999-09-13 03:09:38 +0000121
122# newer_pairwise ()
123
124
Greg Ward7b7679e2000-01-09 22:48:59 +0000125def newer_group (sources, target, missing='error'):
Greg Ward138ce651999-09-13 03:09:38 +0000126 """Return true if 'target' is out-of-date with respect to any
127 file listed in 'sources'. In other words, if 'target' exists and
128 is newer than every file in 'sources', return false; otherwise
Greg Ward7b7679e2000-01-09 22:48:59 +0000129 return true. 'missing' controls what we do when a source file is
130 missing; the default ("error") is to blow up with an OSError from
131 inside 'stat()'; if it is "ignore", we silently drop any missing
132 source files; if it is "newer", any missing source files make us
133 assume that 'target' is out-of-date (this is handy in "dry-run"
134 mode: it'll make you pretend to carry out commands that wouldn't
135 work because inputs are missing, but that doesn't matter because
136 you're not actually going to run the commands)."""
Greg Ward138ce651999-09-13 03:09:38 +0000137
138 # If the target doesn't even exist, then it's definitely out-of-date.
139 if not os.path.exists (target):
140 return 1
141
142 # Otherwise we have to find out the hard way: if *any* source file
143 # is more recent than 'target', then 'target' is out-of-date and
144 # we can immediately return true. If we fall through to the end
145 # of the loop, then 'target' is up-to-date and we return false.
146 from stat import ST_MTIME
147 target_mtime = os.stat (target)[ST_MTIME]
148 for source in sources:
Greg Ward7b7679e2000-01-09 22:48:59 +0000149 if not os.path.exists (source):
150 if missing == 'error': # blow up when we stat() the file
151 pass
152 elif missing == 'ignore': # missing source dropped from
153 continue # target's dependency list
154 elif missing == 'newer': # missing source means target is
155 return 1 # out-of-date
156
Greg Ward138ce651999-09-13 03:09:38 +0000157 source_mtime = os.stat(source)[ST_MTIME]
158 if source_mtime > target_mtime:
159 return 1
160 else:
161 return 0
162
163# newer_group ()
164
165
Greg Wardf3b997a1999-10-03 20:50:41 +0000166# XXX this isn't used anywhere, and worse, it has the same name as a method
167# in Command with subtly different semantics. (This one just has one
168# source -> one dest; that one has many sources -> one dest.) Nuke it?
Greg Ward2689e3d1999-03-22 14:52:19 +0000169def make_file (src, dst, func, args,
170 verbose=0, update_message=None, noupdate_message=None):
171 """Makes 'dst' from 'src' (both filenames) by calling 'func' with
172 'args', but only if it needs to: i.e. if 'dst' does not exist or
173 'src' is newer than 'dst'."""
174
175 if newer (src, dst):
176 if verbose and update_message:
177 print update_message
178 apply (func, args)
179 else:
180 if verbose and noupdate_message:
181 print noupdate_message
182
183# make_file ()
184
185
186def _copy_file_contents (src, dst, buffer_size=16*1024):
187 """Copy the file 'src' to 'dst'; both must be filenames. Any error
188 opening either file, reading from 'src', or writing to 'dst',
189 raises DistutilsFileError. Data is read/written in chunks of
190 'buffer_size' bytes (default 16k). No attempt is made to handle
191 anything apart from regular files."""
192
193 # Stolen from shutil module in the standard library, but with
194 # custom error-handling added.
195
196 fsrc = None
197 fdst = None
198 try:
199 try:
200 fsrc = open(src, 'rb')
201 except os.error, (errno, errstr):
Greg Ward96182d72000-03-03 03:00:02 +0000202 raise DistutilsFileError, \
203 "could not open '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000204
205 try:
206 fdst = open(dst, 'wb')
207 except os.error, (errno, errstr):
Greg Ward96182d72000-03-03 03:00:02 +0000208 raise DistutilsFileError, \
209 "could not create '%s': %s" % (dst, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000210
211 while 1:
212 try:
213 buf = fsrc.read (buffer_size)
214 except os.error, (errno, errstr):
215 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000216 "could not read from '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000217
218 if not buf:
219 break
220
221 try:
222 fdst.write(buf)
223 except os.error, (errno, errstr):
224 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000225 "could not write to '%s': %s" % (dst, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000226
227 finally:
228 if fdst:
229 fdst.close()
230 if fsrc:
231 fsrc.close()
232
233# _copy_file_contents()
234
235
236def copy_file (src, dst,
237 preserve_mode=1,
238 preserve_times=1,
239 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000240 verbose=0,
241 dry_run=0):
Greg Ward2689e3d1999-03-22 14:52:19 +0000242
243 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
244 is copied there with the same name; otherwise, it must be a
245 filename. (If the file exists, it will be ruthlessly clobbered.)
246 If 'preserve_mode' is true (the default), the file's mode (type
247 and permission bits, or whatever is analogous on the current
248 platform) is copied. If 'preserve_times' is true (the default),
249 the last-modified and last-access times are copied as well. If
250 'update' is true, 'src' will only be copied if 'dst' does not
251 exist, or if 'dst' does exist but is older than 'src'. If
252 'verbose' is true, then a one-line summary of the copy will be
Greg Ward884df451999-05-02 21:42:05 +0000253 printed to stdout.
254
255 Return true if the file was copied (or would have been copied),
256 false otherwise (ie. 'update' was true and the destination is
257 up-to-date)."""
Greg Ward2689e3d1999-03-22 14:52:19 +0000258
259 # XXX doesn't copy Mac-specific metadata
260
Greg Ward2689e3d1999-03-22 14:52:19 +0000261 from stat import *
262
263 if not os.path.isfile (src):
264 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000265 "can't copy '%s': not a regular file" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000266
267 if os.path.isdir (dst):
268 dir = dst
269 dst = os.path.join (dst, os.path.basename (src))
270 else:
271 dir = os.path.dirname (dst)
272
273 if update and not newer (src, dst):
Greg Ward884df451999-05-02 21:42:05 +0000274 if verbose:
275 print "not copying %s (output up-to-date)" % src
276 return 0
Greg Ward2689e3d1999-03-22 14:52:19 +0000277
278 if verbose:
279 print "copying %s -> %s" % (src, dir)
280
Greg Warde765a3b1999-04-04 02:54:20 +0000281 if dry_run:
Greg Ward884df451999-05-02 21:42:05 +0000282 return 1
Greg Warde765a3b1999-04-04 02:54:20 +0000283
Greg Ward911d8662000-03-07 03:34:16 +0000284 # On a Mac, use the native file copy routine
285 if os.name == 'mac':
286 import macostools
287 try:
288 macostools.copy (src, dst, 0, preserve_times)
289 except OSError, exc:
290 raise DistutilsFileError, \
291 "could not copy '%s' to '%s': %s" % (src, dst, exc[-1])
292 return 1
293
294 # Otherwise use custom routine
Greg Warde765a3b1999-04-04 02:54:20 +0000295 _copy_file_contents (src, dst)
Greg Ward2689e3d1999-03-22 14:52:19 +0000296 if preserve_mode or preserve_times:
297 st = os.stat (src)
Greg Ward5116f901999-06-08 17:05:21 +0000298
299 # According to David Ascher <da@ski.org>, utime() should be done
300 # before chmod() (at least under NT).
Greg Ward2689e3d1999-03-22 14:52:19 +0000301 if preserve_times:
302 os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
Greg Ward5116f901999-06-08 17:05:21 +0000303 if preserve_mode:
304 os.chmod (dst, S_IMODE (st[ST_MODE]))
Greg Ward2689e3d1999-03-22 14:52:19 +0000305
Greg Ward884df451999-05-02 21:42:05 +0000306 return 1
307
Greg Ward2689e3d1999-03-22 14:52:19 +0000308# copy_file ()
309
310
311def copy_tree (src, dst,
312 preserve_mode=1,
313 preserve_times=1,
314 preserve_symlinks=0,
315 update=0,
Greg Warde765a3b1999-04-04 02:54:20 +0000316 verbose=0,
317 dry_run=0):
318
Greg Ward2689e3d1999-03-22 14:52:19 +0000319
320 """Copy an entire directory tree 'src' to a new location 'dst'. Both
321 'src' and 'dst' must be directory names. If 'src' is not a
322 directory, raise DistutilsFileError. If 'dst' does not exist, it
Greg Wardf3b997a1999-10-03 20:50:41 +0000323 is created with 'mkpath()'. The end result of the copy is that
Greg Ward2689e3d1999-03-22 14:52:19 +0000324 every file in 'src' is copied to 'dst', and directories under
Greg Ward884df451999-05-02 21:42:05 +0000325 'src' are recursively copied to 'dst'. Return the list of files
326 copied (under their output names) -- note that if 'update' is true,
327 this might be less than the list of files considered. Return
328 value is not affected by 'dry_run'.
Greg Ward2689e3d1999-03-22 14:52:19 +0000329
330 'preserve_mode' and 'preserve_times' are the same as for
331 'copy_file'; note that they only apply to regular files, not to
332 directories. If 'preserve_symlinks' is true, symlinks will be
333 copied as symlinks (on platforms that support them!); otherwise
334 (the default), the destination of the symlink will be copied.
335 'update' and 'verbose' are the same as for 'copy_file'."""
336
Greg Ward138ce651999-09-13 03:09:38 +0000337 if not dry_run and not os.path.isdir (src):
Greg Ward2689e3d1999-03-22 14:52:19 +0000338 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000339 "cannot copy tree '%s': not a directory" % src
Greg Ward2689e3d1999-03-22 14:52:19 +0000340 try:
341 names = os.listdir (src)
342 except os.error, (errno, errstr):
Greg Ward138ce651999-09-13 03:09:38 +0000343 if dry_run:
344 names = []
345 else:
346 raise DistutilsFileError, \
Greg Ward96182d72000-03-03 03:00:02 +0000347 "error listing files in '%s': %s" % (src, errstr)
Greg Ward2689e3d1999-03-22 14:52:19 +0000348
Greg Warde765a3b1999-04-04 02:54:20 +0000349 if not dry_run:
350 mkpath (dst, verbose=verbose)
Greg Ward2689e3d1999-03-22 14:52:19 +0000351
Greg Ward884df451999-05-02 21:42:05 +0000352 outputs = []
353
Greg Ward2689e3d1999-03-22 14:52:19 +0000354 for n in names:
355 src_name = os.path.join (src, n)
356 dst_name = os.path.join (dst, n)
357
358 if preserve_symlinks and os.path.islink (src_name):
359 link_dest = os.readlink (src_name)
Greg Warde765a3b1999-04-04 02:54:20 +0000360 if verbose:
361 print "linking %s -> %s" % (dst_name, link_dest)
362 if not dry_run:
363 os.symlink (link_dest, dst_name)
Greg Ward884df451999-05-02 21:42:05 +0000364 outputs.append (dst_name)
365
Greg Ward2689e3d1999-03-22 14:52:19 +0000366 elif os.path.isdir (src_name):
Greg Warda002edc2000-01-30 19:57:48 +0000367 outputs.extend (
Greg Ward884df451999-05-02 21:42:05 +0000368 copy_tree (src_name, dst_name,
369 preserve_mode, preserve_times, preserve_symlinks,
Greg Warda002edc2000-01-30 19:57:48 +0000370 update, verbose, dry_run))
Greg Ward2689e3d1999-03-22 14:52:19 +0000371 else:
Greg Ward884df451999-05-02 21:42:05 +0000372 if (copy_file (src_name, dst_name,
373 preserve_mode, preserve_times,
374 update, verbose, dry_run)):
375 outputs.append (dst_name)
376
377 return outputs
Greg Ward2689e3d1999-03-22 14:52:19 +0000378
379# copy_tree ()
Greg Ward138ce651999-09-13 03:09:38 +0000380
381
Greg Wardb98fe362000-03-18 15:42:22 +0000382def remove_tree (directory, verbose=0, dry_run=0):
383 """Recursively remove an entire directory tree. Any errors are ignored
384 (apart from being reported to stdout if 'verbose' is true)."""
385
386 if verbose:
387 print "removing '%s' (and everything under it)" % directory
388 if dry_run:
389 return
390 try:
391 shutil.rmtree(directory,1)
392 except (IOError, OSError), exc:
393 if verbose:
394 if exc.filename:
395 print "error removing %s: %s (%s)" % \
396 (directory, exc.strerror, exc.filename)
397 else:
398 print "error removing %s: %s" % (directory, exc.strerror)
399
400
Greg Ward138ce651999-09-13 03:09:38 +0000401# XXX I suspect this is Unix-specific -- need porting help!
402def move_file (src, dst,
403 verbose=0,
404 dry_run=0):
405
406 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file
407 will be moved into it with the same name; otherwise, 'src' is
408 just renamed to 'dst'. Return the new full name of the file.
409
410 Handles cross-device moves on Unix using
411 'copy_file()'. What about other systems???"""
412
413 from os.path import exists, isfile, isdir, basename, dirname
414
415 if verbose:
416 print "moving %s -> %s" % (src, dst)
417
418 if dry_run:
419 return dst
420
421 if not isfile (src):
422 raise DistutilsFileError, \
423 "can't move '%s': not a regular file" % src
424
425 if isdir (dst):
426 dst = os.path.join (dst, basename (src))
427 elif exists (dst):
428 raise DistutilsFileError, \
429 "can't move '%s': destination '%s' already exists" % \
430 (src, dst)
431
432 if not isdir (dirname (dst)):
433 raise DistutilsFileError, \
434 "can't move '%s': destination '%s' not a valid path" % \
435 (src, dst)
436
437 copy_it = 0
438 try:
439 os.rename (src, dst)
440 except os.error, (num, msg):
441 if num == errno.EXDEV:
442 copy_it = 1
443 else:
444 raise DistutilsFileError, \
445 "couldn't move '%s' to '%s': %s" % (src, dst, msg)
446
447 if copy_it:
448 copy_file (src, dst)
449 try:
450 os.unlink (src)
451 except os.error, (num, msg):
452 try:
453 os.unlink (dst)
454 except os.error:
455 pass
456 raise DistutilsFileError, \
457 ("couldn't move '%s' to '%s' by copy/delete: " +
458 "delete '%s' failed: %s") % \
459 (src, dst, src, msg)
460
461 return dst
462
463# move_file ()
Greg Wardac1424a1999-09-21 18:37:51 +0000464
465
466def write_file (filename, contents):
Greg Wardf3b997a1999-10-03 20:50:41 +0000467 """Create a file with the specified name and write 'contents' (a
Greg Wardac1424a1999-09-21 18:37:51 +0000468 sequence of strings without line terminators) to it."""
469
470 f = open (filename, "w")
471 for line in contents:
472 f.write (line + "\n")
473 f.close ()
Greg Ward585df892000-03-01 14:40:15 +0000474
475
476def get_platform ():
477 """Return a string (suitable for tacking onto directory names) that
478 identifies the current platform. Under Unix, identifies both the OS
479 and hardware architecture, e.g. "linux-i586", "solaris-sparc",
480 "irix-mips". For Windows and Mac OS, just returns 'sys.platform' --
481 i.e. "???" or "???"."""
482
483 if os.name == 'posix':
484 uname = os.uname()
485 OS = uname[0]
486 arch = uname[4]
487 return "%s-%s" % (string.lower (OS), string.lower (arch))
488 else:
489 return sys.platform
490
491# get_platform()
Greg Ward50919292000-03-07 03:27:08 +0000492
493
494def native_path (pathname):
495 """Return 'pathname' as a name that will work on the native
496 filesystem, i.e. split it on '/' and put it back together again
497 using the current directory separator. Needed because filenames in
498 the setup script are always supplied in Unix style, and have to be
499 converted to the local convention before we can actually use them in
500 the filesystem. Raises DistutilsValueError if 'pathname' is
501 absolute (starts with '/') or contains local directory separators
502 (unless the local separator is '/', of course)."""
503
504 if pathname[0] == '/':
505 raise DistutilsValueError, "path '%s' cannot be absolute" % pathname
506 if pathname[-1] == '/':
507 raise DistutilsValueError, "path '%s' cannot end with '/'" % pathname
Greg Ward1b4ede52000-03-22 00:22:44 +0000508 if os.sep != '/' and os.sep in pathname:
509 raise DistutilsValueError, \
510 "path '%s' cannot contain '%c' character" % \
511 (pathname, os.sep)
Greg Ward50919292000-03-07 03:27:08 +0000512
513 paths = string.split (pathname, '/')
514 return apply (os.path.join, paths)
515 else:
516 return pathname
517
518# native_path ()
Greg Ward1b4ede52000-03-22 00:22:44 +0000519
520
521def _check_environ ():
522 """Ensure that 'os.environ' has all the environment variables we
523 guarantee that users can use in config files, command-line
524 options, etc. Currently this includes:
525 HOME - user's home directory (Unix only)
526 PLAT - desription of the current platform, including hardware
527 and OS (see 'get_platform()')
528 """
529
530 if os.name == 'posix' and not os.environ.has_key('HOME'):
531 import pwd
532 os.environ['HOME'] = pwd.getpwuid (os.getuid())[5]
533
534 if not os.environ.has_key('PLAT'):
535 os.environ['PLAT'] = get_platform ()
536
537
538def subst_vars (str, local_vars):
539 """Perform shell/Perl-style variable substitution on 'string'.
540 Every occurence of '$' followed by a name, or a name enclosed in
541 braces, is considered a variable. Every variable is substituted by
542 the value found in the 'local_vars' dictionary, or in 'os.environ'
543 if it's not in 'local_vars'. 'os.environ' is first checked/
544 augmented to guarantee that it contains certain values: see
545 '_check_environ()'. Raise ValueError for any variables not found in
546 either 'local_vars' or 'os.environ'."""
547
548 _check_environ ()
549 def _subst (match, local_vars=local_vars):
550 var_name = match.group(1)
551 if local_vars.has_key (var_name):
552 return str (local_vars[var_name])
553 else:
554 return os.environ[var_name]
555
556 return re.sub (r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str)
557
558# subst_vars ()