[Distutils] Windows wizards: I need porting help!

Greg Ward gward@cnri.reston.va.us
Sun, 3 Oct 1999 17:31:15 -0400

Anyone who is a reasonably competent Windows/Python programmer can help
me out here.  (At least) two functions in the distutils.util module need
help: 'mkpath()' and 'move_file()'.  Actually, 'mkpath()' might be
working fine now, but I'd like some of you Windows folks to take a look
at it, beat on it, make sure it'll really work.  Here's the code:

# cache for by mkpath() -- in addition to cheapening redundant calls,
# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode

# I don't use os.makedirs because a) it's new to Python 1.5.2, and
# b) it blows up if the directory already exists (I want to silently
# succeed in that case).
def mkpath (name, mode=0777, verbose=0, dry_run=0):
    """Create a directory and any missing ancestor directories.  If the
       directory already exists, return silently.  Raise
       DistutilsFileError if unable to create some directory along the
       way (eg. some sub-path exists, but is a file rather than a
       directory).  If 'verbose' is true, print a one-line summary of
       each mkdir to stdout."""

    global PATH_CREATED

    # XXX what's the better way to handle verbosity? print as we create
    # each directory in the path (the current behaviour), or only announce
    # the creation of the whole path? (quite easy to do the latter since
    # we're not using a recursive algorithm)

    name = os.path.normpath (name)

    if os.path.isdir (name):
    if PATH_CREATED.get (name):

    (head, tail) = os.path.split (name)
    tails = [tail]                      # stack of lone dirs to create
    while head and tail and not os.path.isdir (head):
        #print "splitting '%s': " % head,
        (head, tail) = os.path.split (head)
        #print "to ('%s','%s')" % (head, tail)
        tails.insert (0, tail)          # push next higher dir onto stack

    #print "stack of tails:", tails

    # now 'head' contains the deepest directory that already exists
    # (that is, the child of 'head' in 'name' is the highest directory
    # that does *not* exist)
    for d in tails:
        #print "head = %s, d = %s: " % (head, d),
        head = os.path.join (head, d)
        if PATH_CREATED.get (head):

        if verbose:
            print "creating", head

        if not dry_run:
                os.mkdir (head)
            except os.error, (errno, errstr):
                raise DistutilsFileError, "%s: %s" % (head, errstr)

        PATH_CREATED[head] = 1

# mkpath ()

The other one, 'move_file()', almost certainly has portability
problems.  Please read it, bash on it, and help me fix it!  Here's the
code for it:

def move_file (src, dst,

    """Move a file 'src' to 'dst'.  If 'dst' is a directory, the file
       will be moved into it with the same name; otherwise, 'src' is
       just renamed to 'dst'.  Return the new full name of the file.

       Handles cross-device moves on Unix using
       'copy_file()'.  What about other systems???"""

    from os.path import exists, isfile, isdir, basename, dirname

    if verbose:
        print "moving %s -> %s" % (src, dst)

    if dry_run:
        return dst

    if not isfile (src):
        raise DistutilsFileError, \
              "can't move '%s': not a regular file" % src

    if isdir (dst):
        dst = os.path.join (dst, basename (src))
    elif exists (dst):
        raise DistutilsFileError, \
              "can't move '%s': destination '%s' already exists" % \
              (src, dst)

    if not isdir (dirname (dst)):
        raise DistutilsFileError, \
              "can't move '%s': destination '%s' not a valid path" % \
              (src, dst)

    copy_it = 0
        os.rename (src, dst)
    except os.error, (num, msg):
        if num == errno.EXDEV:
            copy_it = 1
            raise DistutilsFileError, \
                  "couldn't move '%s' to '%s': %s" % (src, dst, msg)

    if copy_it:
        copy_file (src, dst)
            os.unlink (src)
        except os.error, (num, msg):
                os.unlink (dst)
            except os.error:
            raise DistutilsFileError, \
                  ("couldn't move '%s' to '%s' by copy/delete: " + 
                   "delete '%s' failed: %s") % \
                  (src, dst, src, msg)

    return dst

# move_file ()


Greg Ward - software developer                    gward@cnri.reston.va.us
Corporation for National Research Initiatives    
1895 Preston White Drive                           voice: +1-703-620-8990
Reston, Virginia, USA  20191-5434                    fax: +1-703-620-0913