~ubuntu-branches/ubuntu/maverick/python3.1/maverick

« back to all changes in this revision

Viewing changes to Lib/distutils/dir_util.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-03-23 00:01:27 UTC
  • Revision ID: james.westby@ubuntu.com-20090323000127-5fstfxju4ufrhthq
Tags: upstream-3.1~a1+20090322
ImportĀ upstreamĀ versionĀ 3.1~a1+20090322

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""distutils.dir_util
 
2
 
 
3
Utility functions for manipulating directories and directory trees."""
 
4
 
 
5
__revision__ = "$Id: dir_util.py 69336 2009-02-06 00:53:43Z tarek.ziade $"
 
6
 
 
7
import os, sys
 
8
from distutils.errors import DistutilsFileError, DistutilsInternalError
 
9
from distutils import log
 
10
 
 
11
# cache for by mkpath() -- in addition to cheapening redundant calls,
 
12
# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
 
13
_path_created = {}
 
14
 
 
15
# I don't use os.makedirs because a) it's new to Python 1.5.2, and
 
16
# b) it blows up if the directory already exists (I want to silently
 
17
# succeed in that case).
 
18
def mkpath (name, mode=0o777, verbose=1, dry_run=0):
 
19
    """Create a directory and any missing ancestor directories.  If the
 
20
       directory already exists (or if 'name' is the empty string, which
 
21
       means the current directory, which of course exists), then do
 
22
       nothing.  Raise DistutilsFileError if unable to create some
 
23
       directory along the way (eg. some sub-path exists, but is a file
 
24
       rather than a directory).  If 'verbose' is true, print a one-line
 
25
       summary of each mkdir to stdout.  Return the list of directories
 
26
       actually created."""
 
27
 
 
28
    global _path_created
 
29
 
 
30
    # Detect a common bug -- name is None
 
31
    if not isinstance(name, str):
 
32
        raise DistutilsInternalError(
 
33
              "mkpath: 'name' must be a string (got %r)" % (name,))
 
34
 
 
35
    # 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
 
37
    # the creation of the whole path? (quite easy to do the latter since
 
38
    # we're not using a recursive algorithm)
 
39
 
 
40
    name = os.path.normpath(name)
 
41
    created_dirs = []
 
42
    if os.path.isdir(name) or name == '':
 
43
        return created_dirs
 
44
    if _path_created.get(os.path.abspath(name)):
 
45
        return created_dirs
 
46
 
 
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
        (head, tail) = os.path.split(head)
 
52
        tails.insert(0, tail)          # push next higher dir onto stack
 
53
 
 
54
    # now 'head' contains the deepest directory that already exists
 
55
    # (that is, the child of 'head' in 'name' is the highest directory
 
56
    # that does *not* exist)
 
57
    for d in tails:
 
58
        #print "head = %s, d = %s: " % (head, d),
 
59
        head = os.path.join(head, d)
 
60
        abs_head = os.path.abspath(head)
 
61
 
 
62
        if _path_created.get(abs_head):
 
63
            continue
 
64
 
 
65
        if verbose >= 1:
 
66
            log.info("creating %s", head)
 
67
 
 
68
        if not dry_run:
 
69
            try:
 
70
                os.mkdir(head)
 
71
                created_dirs.append(head)
 
72
            except OSError as exc:
 
73
                raise DistutilsFileError(
 
74
                      "could not create '%s': %s" % (head, exc.args[-1]))
 
75
 
 
76
        _path_created[abs_head] = 1
 
77
    return created_dirs
 
78
 
 
79
# mkpath ()
 
80
 
 
81
 
 
82
def create_tree (base_dir, files, mode=0o777, verbose=1, dry_run=0):
 
83
 
 
84
    """Create all the empty directories under 'base_dir' needed to
 
85
       put 'files' there.  'base_dir' is just the a name of a directory
 
86
       which doesn't necessarily exist yet; 'files' is a list of filenames
 
87
       to be interpreted relative to 'base_dir'.  'base_dir' + the
 
88
       directory portion of every file in 'files' will be created if it
 
89
       doesn't already exist.  'mode', 'verbose' and 'dry_run' flags are as
 
90
       for 'mkpath()'."""
 
91
 
 
92
    # First get the list of directories to create
 
93
    need_dir = set()
 
94
    for file in files:
 
95
        need_dir.add(os.path.join(base_dir, os.path.dirname(file)))
 
96
 
 
97
    # Now create them
 
98
    for dir in sorted(need_dir):
 
99
        mkpath(dir, mode, verbose=verbose, dry_run=dry_run)
 
100
 
 
101
# create_tree ()
 
102
 
 
103
 
 
104
def copy_tree (src, dst,
 
105
               preserve_mode=1,
 
106
               preserve_times=1,
 
107
               preserve_symlinks=0,
 
108
               update=0,
 
109
               verbose=1,
 
110
               dry_run=0):
 
111
 
 
112
    """Copy an entire directory tree 'src' to a new location 'dst'.  Both
 
113
       'src' and 'dst' must be directory names.  If 'src' is not a
 
114
       directory, raise DistutilsFileError.  If 'dst' does not exist, it is
 
115
       created with 'mkpath()'.  The end result of the copy is that every
 
116
       file in 'src' is copied to 'dst', and directories under 'src' are
 
117
       recursively copied to 'dst'.  Return the list of files that were
 
118
       copied or might have been copied, using their output name.  The
 
119
       return value is unaffected by 'update' or 'dry_run': it is simply
 
120
       the list of all files under 'src', with the names changed to be
 
121
       under 'dst'.
 
122
 
 
123
       'preserve_mode' and 'preserve_times' are the same as for
 
124
       'copy_file'; note that they only apply to regular files, not to
 
125
       directories.  If 'preserve_symlinks' is true, symlinks will be
 
126
       copied as symlinks (on platforms that support them!); otherwise
 
127
       (the default), the destination of the symlink will be copied.
 
128
       'update' and 'verbose' are the same as for 'copy_file'."""
 
129
 
 
130
    from distutils.file_util import copy_file
 
131
 
 
132
    if not dry_run and not os.path.isdir(src):
 
133
        raise DistutilsFileError(
 
134
              "cannot copy tree '%s': not a directory" % src)
 
135
    try:
 
136
        names = os.listdir(src)
 
137
    except os.error as e:
 
138
        (errno, errstr) = e
 
139
        if dry_run:
 
140
            names = []
 
141
        else:
 
142
            raise DistutilsFileError(
 
143
                  "error listing files in '%s': %s" % (src, errstr))
 
144
 
 
145
    if not dry_run:
 
146
        mkpath(dst, verbose=verbose)
 
147
 
 
148
    outputs = []
 
149
 
 
150
    for n in names:
 
151
        src_name = os.path.join(src, n)
 
152
        dst_name = os.path.join(dst, n)
 
153
 
 
154
        if preserve_symlinks and os.path.islink(src_name):
 
155
            link_dest = os.readlink(src_name)
 
156
            if verbose >= 1:
 
157
                log.info("linking %s -> %s", dst_name, link_dest)
 
158
            if not dry_run:
 
159
                os.symlink(link_dest, dst_name)
 
160
            outputs.append(dst_name)
 
161
 
 
162
        elif os.path.isdir(src_name):
 
163
            outputs.extend(
 
164
                copy_tree(src_name, dst_name, preserve_mode,
 
165
                          preserve_times, preserve_symlinks, update,
 
166
                          verbose=verbose, dry_run=dry_run))
 
167
        else:
 
168
            copy_file(src_name, dst_name, preserve_mode,
 
169
                      preserve_times, update, verbose=verbose,
 
170
                      dry_run=dry_run)
 
171
            outputs.append(dst_name)
 
172
 
 
173
    return outputs
 
174
 
 
175
# Helper for remove_tree()
 
176
def _build_cmdtuple(path, cmdtuples):
 
177
    for f in os.listdir(path):
 
178
        real_f = os.path.join(path,f)
 
179
        if os.path.isdir(real_f) and not os.path.islink(real_f):
 
180
            _build_cmdtuple(real_f, cmdtuples)
 
181
        else:
 
182
            cmdtuples.append((os.remove, real_f))
 
183
    cmdtuples.append((os.rmdir, path))
 
184
 
 
185
 
 
186
def remove_tree (directory, verbose=1, dry_run=0):
 
187
    """Recursively remove an entire directory tree.  Any errors are ignored
 
188
    (apart from being reported to stdout if 'verbose' is true).
 
189
    """
 
190
    from distutils.util import grok_environment_error
 
191
    global _path_created
 
192
 
 
193
    if verbose >= 1:
 
194
        log.info("removing '%s' (and everything under it)", directory)
 
195
    if dry_run:
 
196
        return
 
197
    cmdtuples = []
 
198
    _build_cmdtuple(directory, cmdtuples)
 
199
    for cmd in cmdtuples:
 
200
        try:
 
201
            cmd[0](cmd[1])
 
202
            # remove dir from cache if it's already there
 
203
            abspath = os.path.abspath(cmd[1])
 
204
            if abspath in _path_created:
 
205
                del _path_created[abspath]
 
206
        except (IOError, OSError) as exc:
 
207
            log.warn(grok_environment_error(
 
208
                    exc, "error removing %s: " % directory))
 
209
 
 
210
 
 
211
def ensure_relative (path):
 
212
    """Take the full path 'path', and make it a relative path so
 
213
    it can be the second argument to os.path.join().
 
214
    """
 
215
    drive, path = os.path.splitdrive(path)
 
216
    if sys.platform == 'mac':
 
217
        return os.sep + path
 
218
    else:
 
219
        if path[0:1] == os.sep:
 
220
            path = drive + path[1:]
 
221
        return path