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