0001 """distutils.util 0002 0003 Miscellaneous utility functions -- anything that doesn't fit into 0004 one of the other *util.py modules. 0005 """ 0006 0007 __revision__ = "$Id: util.py,v 1.76 2004/07/18 06:14:42 tim_one Exp $" 0008 0009 import sys, os, string, re 0010 from distutils.errors import DistutilsPlatformError 0011 from distutils.dep_util import newer 0012 from distutils.spawn import spawn 0013 from distutils import log 0014 0015 def get_platform (): 0016 """Return a string that identifies the current platform. This is used 0017 mainly to distinguish platform-specific build directories and 0018 platform-specific built distributions. Typically includes the OS name 0019 and version and the architecture (as supplied by 'os.uname()'), 0020 although the exact information included depends on the OS; eg. for IRIX 0021 the architecture isn't particularly important (IRIX only runs on SGI 0022 hardware), but for Linux the kernel version isn't particularly 0023 important. 0024 0025 Examples of returned values: 0026 linux-i586 0027 linux-alpha (?) 0028 solaris-2.6-sun4u 0029 irix-5.3 0030 irix64-6.2 0031 0032 For non-POSIX platforms, currently just returns 'sys.platform'. 0033 """ 0034 if os.name != "posix" or not hasattr(os, 'uname'): 0035 # XXX what about the architecture? NT is Intel or Alpha, 0036 # Mac OS is M68k or PPC, etc. 0037 return sys.platform 0038 0039 # Try to distinguish various flavours of Unix 0040 0041 (osname, host, release, version, machine) = os.uname() 0042 0043 # Convert the OS name to lowercase, remove '/' characters 0044 # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") 0045 osname = string.lower(osname) 0046 osname = string.replace(osname, '/', '') 0047 machine = string.replace(machine, ' ', '_') 0048 0049 if osname[:5] == "linux": 0050 # At least on Linux/Intel, 'machine' is the processor -- 0051 # i386, etc. 0052 # XXX what about Alpha, SPARC, etc? 0053 return "%s-%s" % (osname, machine) 0054 elif osname[:5] == "sunos": 0055 if release[0] >= "5": # SunOS 5 == Solaris 2 0056 osname = "solaris" 0057 release = "%d.%s" % (int(release[0]) - 3, release[2:]) 0058 # fall through to standard osname-release-machine representation 0059 elif osname[:4] == "irix": # could be "irix64"! 0060 return "%s-%s" % (osname, release) 0061 elif osname[:3] == "aix": 0062 return "%s-%s.%s" % (osname, version, release) 0063 elif osname[:6] == "cygwin": 0064 osname = "cygwin" 0065 rel_re = re.compile (r'[\d.]+') 0066 m = rel_re.match(release) 0067 if m: 0068 release = m.group() 0069 0070 return "%s-%s-%s" % (osname, release, machine) 0071 0072 # get_platform () 0073 0074 0075 def convert_path (pathname): 0076 """Return 'pathname' as a name that will work on the native filesystem, 0077 i.e. split it on '/' and put it back together again using the current 0078 directory separator. Needed because filenames in the setup script are 0079 always supplied in Unix style, and have to be converted to the local 0080 convention before we can actually use them in the filesystem. Raises 0081 ValueError on non-Unix-ish systems if 'pathname' either starts or 0082 ends with a slash. 0083 """ 0084 if os.sep == '/': 0085 return pathname 0086 if not pathname: 0087 return pathname 0088 if pathname[0] == '/': 0089 raise ValueError, "path '%s' cannot be absolute" % pathname 0090 if pathname[-1] == '/': 0091 raise ValueError, "path '%s' cannot end with '/'" % pathname 0092 0093 paths = string.split(pathname, '/') 0094 while '.' in paths: 0095 paths.remove('.') 0096 if not paths: 0097 return os.curdir 0098 return apply(os.path.join, paths) 0099 0100 # convert_path () 0101 0102 0103 def change_root (new_root, pathname): 0104 """Return 'pathname' with 'new_root' prepended. If 'pathname' is 0105 relative, this is equivalent to "os.path.join(new_root,pathname)". 0106 Otherwise, it requires making 'pathname' relative and then joining the 0107 two, which is tricky on DOS/Windows and Mac OS. 0108 """ 0109 if os.name == 'posix': 0110 if not os.path.isabs(pathname): 0111 return os.path.join(new_root, pathname) 0112 else: 0113 return os.path.join(new_root, pathname[1:]) 0114 0115 elif os.name == 'nt': 0116 (drive, path) = os.path.splitdrive(pathname) 0117 if path[0] == '\\': 0118 path = path[1:] 0119 return os.path.join(new_root, path) 0120 0121 elif os.name == 'os2': 0122 (drive, path) = os.path.splitdrive(pathname) 0123 if path[0] == os.sep: 0124 path = path[1:] 0125 return os.path.join(new_root, path) 0126 0127 elif os.name == 'mac': 0128 if not os.path.isabs(pathname): 0129 return os.path.join(new_root, pathname) 0130 else: 0131 # Chop off volume name from start of path 0132 elements = string.split(pathname, ":", 1) 0133 pathname = ":" + elements[1] 0134 return os.path.join(new_root, pathname) 0135 0136 else: 0137 raise DistutilsPlatformError, \ 0138 "nothing known about platform '%s'" % os.name 0139 0140 0141 _environ_checked = 0 0142 def check_environ (): 0143 """Ensure that 'os.environ' has all the environment variables we 0144 guarantee that users can use in config files, command-line options, 0145 etc. Currently this includes: 0146 HOME - user's home directory (Unix only) 0147 PLAT - description of the current platform, including hardware 0148 and OS (see 'get_platform()') 0149 """ 0150 global _environ_checked 0151 if _environ_checked: 0152 return 0153 0154 if os.name == 'posix' and not os.environ.has_key('HOME'): 0155 import pwd 0156 os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] 0157 0158 if not os.environ.has_key('PLAT'): 0159 os.environ['PLAT'] = get_platform() 0160 0161 _environ_checked = 1 0162 0163 0164 def subst_vars (s, local_vars): 0165 """Perform shell/Perl-style variable substitution on 'string'. Every 0166 occurrence of '$' followed by a name is considered a variable, and 0167 variable is substituted by the value found in the 'local_vars' 0168 dictionary, or in 'os.environ' if it's not in 'local_vars'. 0169 'os.environ' is first checked/augmented to guarantee that it contains 0170 certain values: see 'check_environ()'. Raise ValueError for any 0171 variables not found in either 'local_vars' or 'os.environ'. 0172 """ 0173 check_environ() 0174 def _subst (match, local_vars=local_vars): 0175 var_name = match.group(1) 0176 if local_vars.has_key(var_name): 0177 return str(local_vars[var_name]) 0178 else: 0179 return os.environ[var_name] 0180 0181 try: 0182 return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) 0183 except KeyError, var: 0184 raise ValueError, "invalid variable '$%s'" % var 0185 0186 # subst_vars () 0187 0188 0189 def grok_environment_error (exc, prefix="error: "): 0190 """Generate a useful error message from an EnvironmentError (IOError or 0191 OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and 0192 does what it can to deal with exception objects that don't have a 0193 filename (which happens when the error is due to a two-file operation, 0194 such as 'rename()' or 'link()'. Returns the error message as a string 0195 prefixed with 'prefix'. 0196 """ 0197 # check for Python 1.5.2-style {IO,OS}Error exception objects 0198 if hasattr(exc, 'filename') and hasattr(exc, 'strerror'): 0199 if exc.filename: 0200 error = prefix + "%s: %s" % (exc.filename, exc.strerror) 0201 else: 0202 # two-argument functions in posix module don't 0203 # include the filename in the exception object! 0204 error = prefix + "%s" % exc.strerror 0205 else: 0206 error = prefix + str(exc[-1]) 0207 0208 return error 0209 0210 0211 # Needed by 'split_quoted()' 0212 _wordchars_re = _squote_re = _dquote_re = None 0213 def _init_regex(): 0214 global _wordchars_re, _squote_re, _dquote_re 0215 _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) 0216 _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") 0217 _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') 0218 0219 def split_quoted (s): 0220 """Split a string up according to Unix shell-like rules for quotes and 0221 backslashes. In short: words are delimited by spaces, as long as those 0222 spaces are not escaped by a backslash, or inside a quoted string. 0223 Single and double quotes are equivalent, and the quote characters can 0224 be backslash-escaped. The backslash is stripped from any two-character 0225 escape sequence, leaving only the escaped character. The quote 0226 characters are stripped from any quoted string. Returns a list of 0227 words. 0228 """ 0229 0230 # This is a nice algorithm for splitting up a single string, since it 0231 # doesn't require character-by-character examination. It was a little 0232 # bit of a brain-bender to get it working right, though... 0233 if _wordchars_re is None: _init_regex() 0234 0235 s = string.strip(s) 0236 words = [] 0237 pos = 0 0238 0239 while s: 0240 m = _wordchars_re.match(s, pos) 0241 end = m.end() 0242 if end == len(s): 0243 words.append(s[:end]) 0244 break 0245 0246 if s[end] in string.whitespace: # unescaped, unquoted whitespace: now 0247 words.append(s[:end]) # we definitely have a word delimiter 0248 s = string.lstrip(s[end:]) 0249 pos = 0 0250 0251 elif s[end] == '\\': # preserve whatever is being escaped; 0252 # will become part of the current word 0253 s = s[:end] + s[end+1:] 0254 pos = end+1 0255 0256 else: 0257 if s[end] == "'": # slurp singly-quoted string 0258 m = _squote_re.match(s, end) 0259 elif s[end] == '"': # slurp doubly-quoted string 0260 m = _dquote_re.match(s, end) 0261 else: 0262 raise RuntimeError, \ 0263 "this can't happen (bad char '%c')" % s[end] 0264 0265 if m is None: 0266 raise ValueError, \ 0267 "bad string (mismatched %s quotes?)" % s[end] 0268 0269 (beg, end) = m.span() 0270 s = s[:beg] + s[beg+1:end-1] + s[end:] 0271 pos = m.end() - 2 0272 0273 if pos >= len(s): 0274 words.append(s) 0275 break 0276 0277 return words 0278 0279 # split_quoted () 0280 0281 0282 def execute (func, args, msg=None, verbose=0, dry_run=0): 0283 """Perform some action that affects the outside world (eg. by 0284 writing to the filesystem). Such actions are special because they 0285 are disabled by the 'dry_run' flag. This method takes care of all 0286 that bureaucracy for you; all you have to do is supply the 0287 function to call and an argument tuple for it (to embody the 0288 "external action" being performed), and an optional message to 0289 print. 0290 """ 0291 if msg is None: 0292 msg = "%s%r" % (func.__name__, args) 0293 if msg[-2:] == ',)': # correct for singleton tuple 0294 msg = msg[0:-2] + ')' 0295 0296 log.info(msg) 0297 if not dry_run: 0298 apply(func, args) 0299 0300 0301 def strtobool (val): 0302 """Convert a string representation of truth to true (1) or false (0). 0303 0304 True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values 0305 are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 0306 'val' is anything else. 0307 """ 0308 val = string.lower(val) 0309 if val in ('y', 'yes', 't', 'true', 'on', '1'): 0310 return 1 0311 elif val in ('n', 'no', 'f', 'false', 'off', '0'): 0312 return 0 0313 else: 0314 raise ValueError, "invalid truth value %r" % (val,) 0315 0316 0317 def byte_compile (py_files, 0318 optimize=0, force=0, 0319 prefix=None, base_dir=None, 0320 verbose=1, dry_run=0, 0321 direct=None): 0322 """Byte-compile a collection of Python source files to either .pyc 0323 or .pyo files in the same directory. 'py_files' is a list of files 0324 to compile; any files that don't end in ".py" are silently skipped. 0325 'optimize' must be one of the following: 0326 0 - don't optimize (generate .pyc) 0327 1 - normal optimization (like "python -O") 0328 2 - extra optimization (like "python -OO") 0329 If 'force' is true, all files are recompiled regardless of 0330 timestamps. 0331 0332 The source filename encoded in each bytecode file defaults to the 0333 filenames listed in 'py_files'; you can modify these with 'prefix' and 0334 'basedir'. 'prefix' is a string that will be stripped off of each 0335 source filename, and 'base_dir' is a directory name that will be 0336 prepended (after 'prefix' is stripped). You can supply either or both 0337 (or neither) of 'prefix' and 'base_dir', as you wish. 0338 0339 If 'dry_run' is true, doesn't actually do anything that would 0340 affect the filesystem. 0341 0342 Byte-compilation is either done directly in this interpreter process 0343 with the standard py_compile module, or indirectly by writing a 0344 temporary script and executing it. Normally, you should let 0345 'byte_compile()' figure out to use direct compilation or not (see 0346 the source for details). The 'direct' flag is used by the script 0347 generated in indirect mode; unless you know what you're doing, leave 0348 it set to None. 0349 """ 0350 0351 # First, if the caller didn't force us into direct or indirect mode, 0352 # figure out which mode we should be in. We take a conservative 0353 # approach: choose direct mode *only* if the current interpreter is 0354 # in debug mode and optimize is 0. If we're not in debug mode (-O 0355 # or -OO), we don't know which level of optimization this 0356 # interpreter is running with, so we can't do direct 0357 # byte-compilation and be certain that it's the right thing. Thus, 0358 # always compile indirectly if the current interpreter is in either 0359 # optimize mode, or if either optimization level was requested by 0360 # the caller. 0361 if direct is None: 0362 direct = (__debug__ and optimize == 0) 0363 0364 # "Indirect" byte-compilation: write a temporary script and then 0365 # run it with the appropriate flags. 0366 if not direct: 0367 try: 0368 from tempfile import mkstemp 0369 (script_fd, script_name) = mkstemp(".py") 0370 except ImportError: 0371 from tempfile import mktemp 0372 (script_fd, script_name) = None, mktemp(".py") 0373 log.info("writing byte-compilation script '%s'", script_name) 0374 if not dry_run: 0375 if script_fd is not None: 0376 script = os.fdopen(script_fd, "w") 0377 else: 0378 script = open(script_name, "w") 0379 0380 script.write("""\ 0381 from distutils.util import byte_compile 0382 files = [ 0383 """) 0384 0385 # XXX would be nice to write absolute filenames, just for 0386 # safety's sake (script should be more robust in the face of 0387 # chdir'ing before running it). But this requires abspath'ing 0388 # 'prefix' as well, and that breaks the hack in build_lib's 0389 # 'byte_compile()' method that carefully tacks on a trailing 0390 # slash (os.sep really) to make sure the prefix here is "just 0391 # right". This whole prefix business is rather delicate -- the 0392 # problem is that it's really a directory, but I'm treating it 0393 # as a dumb string, so trailing slashes and so forth matter. 0394 0395 #py_files = map(os.path.abspath, py_files) 0396 #if prefix: 0397 # prefix = os.path.abspath(prefix) 0398 0399 script.write(string.join(map(repr, py_files), ",\n") + "]\n") 0400 script.write(""" 0401 byte_compile(files, optimize=%r, force=%r, 0402 prefix=%r, base_dir=%r, 0403 verbose=%r, dry_run=0, 0404 direct=1) 0405 """ % (optimize, force, prefix, base_dir, verbose)) 0406 0407 script.close() 0408 0409 cmd = [sys.executable, script_name] 0410 if optimize == 1: 0411 cmd.insert(1, "-O") 0412 elif optimize == 2: 0413 cmd.insert(1, "-OO") 0414 spawn(cmd, dry_run=dry_run) 0415 execute(os.remove, (script_name,), "removing %s" % script_name, 0416 dry_run=dry_run) 0417 0418 # "Direct" byte-compilation: use the py_compile module to compile 0419 # right here, right now. Note that the script generated in indirect 0420 # mode simply calls 'byte_compile()' in direct mode, a weird sort of 0421 # cross-process recursion. Hey, it works! 0422 else: 0423 from py_compile import compile 0424 0425 for file in py_files: 0426 if file[-3:] != ".py": 0427 # This lets us be lazy and not filter filenames in 0428 # the "install_lib" command. 0429 continue 0430 0431 # Terminology from the py_compile module: 0432 # cfile - byte-compiled file 0433 # dfile - purported source filename (same as 'file' by default) 0434 cfile = file + (__debug__ and "c" or "o") 0435 dfile = file 0436 if prefix: 0437 if file[:len(prefix)] != prefix: 0438 raise ValueError, \ 0439 ("invalid prefix: filename %r doesn't start with %r" 0440 % (file, prefix)) 0441 dfile = dfile[len(prefix):] 0442 if base_dir: 0443 dfile = os.path.join(base_dir, dfile) 0444 0445 cfile_base = os.path.basename(cfile) 0446 if direct: 0447 if force or newer(file, cfile): 0448 log.info("byte-compiling %s to %s", file, cfile_base) 0449 if not dry_run: 0450 compile(file, cfile, dfile) 0451 else: 0452 log.debug("skipping byte-compilation of %s to %s", 0453 file, cfile_base) 0454 0455 # byte_compile () 0456 0457 def rfc822_escape (header): 0458 """Return a version of the string escaped for inclusion in an 0459 RFC-822 header, by ensuring there are 8 spaces space after each newline. 0460 """ 0461 lines = string.split(header, '\n') 0462 lines = map(string.strip, lines) 0463 header = string.join(lines, '\n' + 8*' ') 0464 return header 0465
Generated by PyXR 0.9.4