PyXR

c:\python24\lib \ distutils \ util.py



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
SourceForge.net Logo