]> git.proxmox.com Git - mirror_edk2.git/blame - AppPkg/Applications/Python/Python-2.7.10/Lib/shutil.py
AppPkg/Applications/Python/Python-2.7.10: Initial Checkin part 4/5.
[mirror_edk2.git] / AppPkg / Applications / Python / Python-2.7.10 / Lib / shutil.py
CommitLineData
3257aa99
DM
1"""Utility functions for copying and archiving files and directory trees.\r
2\r
3XXX The functions here don't copy the resource fork or other metadata on Mac.\r
4\r
5"""\r
6\r
7import os\r
8import sys\r
9import stat\r
10from os.path import abspath\r
11import fnmatch\r
12import collections\r
13import errno\r
14\r
15try:\r
16 from pwd import getpwnam\r
17except ImportError:\r
18 getpwnam = None\r
19\r
20try:\r
21 from grp import getgrnam\r
22except ImportError:\r
23 getgrnam = None\r
24\r
25__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2",\r
26 "copytree", "move", "rmtree", "Error", "SpecialFileError",\r
27 "ExecError", "make_archive", "get_archive_formats",\r
28 "register_archive_format", "unregister_archive_format",\r
29 "ignore_patterns"]\r
30\r
31class Error(EnvironmentError):\r
32 pass\r
33\r
34class SpecialFileError(EnvironmentError):\r
35 """Raised when trying to do a kind of operation (e.g. copying) which is\r
36 not supported on a special file (e.g. a named pipe)"""\r
37\r
38class ExecError(EnvironmentError):\r
39 """Raised when a command could not be executed"""\r
40\r
41try:\r
42 WindowsError\r
43except NameError:\r
44 WindowsError = None\r
45\r
46def copyfileobj(fsrc, fdst, length=16*1024):\r
47 """copy data from file-like object fsrc to file-like object fdst"""\r
48 while 1:\r
49 buf = fsrc.read(length)\r
50 if not buf:\r
51 break\r
52 fdst.write(buf)\r
53\r
54def _samefile(src, dst):\r
55 # Macintosh, Unix.\r
56 if hasattr(os.path, 'samefile'):\r
57 try:\r
58 return os.path.samefile(src, dst)\r
59 except OSError:\r
60 return False\r
61\r
62 # All other platforms: check for same pathname.\r
63 return (os.path.normcase(os.path.abspath(src)) ==\r
64 os.path.normcase(os.path.abspath(dst)))\r
65\r
66def copyfile(src, dst):\r
67 """Copy data from src to dst"""\r
68 if _samefile(src, dst):\r
69 raise Error("`%s` and `%s` are the same file" % (src, dst))\r
70\r
71 for fn in [src, dst]:\r
72 try:\r
73 st = os.stat(fn)\r
74 except OSError:\r
75 # File most likely does not exist\r
76 pass\r
77 else:\r
78 # XXX What about other special files? (sockets, devices...)\r
79 if stat.S_ISFIFO(st.st_mode):\r
80 raise SpecialFileError("`%s` is a named pipe" % fn)\r
81\r
82 with open(src, 'rb') as fsrc:\r
83 with open(dst, 'wb') as fdst:\r
84 copyfileobj(fsrc, fdst)\r
85\r
86def copymode(src, dst):\r
87 """Copy mode bits from src to dst"""\r
88 if hasattr(os, 'chmod'):\r
89 st = os.stat(src)\r
90 mode = stat.S_IMODE(st.st_mode)\r
91 os.chmod(dst, mode)\r
92\r
93def copystat(src, dst):\r
94 """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""\r
95 st = os.stat(src)\r
96 mode = stat.S_IMODE(st.st_mode)\r
97 if hasattr(os, 'utime'):\r
98 os.utime(dst, (st.st_atime, st.st_mtime))\r
99 if hasattr(os, 'chmod'):\r
100 os.chmod(dst, mode)\r
101 if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):\r
102 try:\r
103 os.chflags(dst, st.st_flags)\r
104 except OSError, why:\r
105 for err in 'EOPNOTSUPP', 'ENOTSUP':\r
106 if hasattr(errno, err) and why.errno == getattr(errno, err):\r
107 break\r
108 else:\r
109 raise\r
110\r
111def copy(src, dst):\r
112 """Copy data and mode bits ("cp src dst").\r
113\r
114 The destination may be a directory.\r
115\r
116 """\r
117 if os.path.isdir(dst):\r
118 dst = os.path.join(dst, os.path.basename(src))\r
119 copyfile(src, dst)\r
120 copymode(src, dst)\r
121\r
122def copy2(src, dst):\r
123 """Copy data and all stat info ("cp -p src dst").\r
124\r
125 The destination may be a directory.\r
126\r
127 """\r
128 if os.path.isdir(dst):\r
129 dst = os.path.join(dst, os.path.basename(src))\r
130 copyfile(src, dst)\r
131 copystat(src, dst)\r
132\r
133def ignore_patterns(*patterns):\r
134 """Function that can be used as copytree() ignore parameter.\r
135\r
136 Patterns is a sequence of glob-style patterns\r
137 that are used to exclude files"""\r
138 def _ignore_patterns(path, names):\r
139 ignored_names = []\r
140 for pattern in patterns:\r
141 ignored_names.extend(fnmatch.filter(names, pattern))\r
142 return set(ignored_names)\r
143 return _ignore_patterns\r
144\r
145def copytree(src, dst, symlinks=False, ignore=None):\r
146 """Recursively copy a directory tree using copy2().\r
147\r
148 The destination directory must not already exist.\r
149 If exception(s) occur, an Error is raised with a list of reasons.\r
150\r
151 If the optional symlinks flag is true, symbolic links in the\r
152 source tree result in symbolic links in the destination tree; if\r
153 it is false, the contents of the files pointed to by symbolic\r
154 links are copied.\r
155\r
156 The optional ignore argument is a callable. If given, it\r
157 is called with the `src` parameter, which is the directory\r
158 being visited by copytree(), and `names` which is the list of\r
159 `src` contents, as returned by os.listdir():\r
160\r
161 callable(src, names) -> ignored_names\r
162\r
163 Since copytree() is called recursively, the callable will be\r
164 called once for each directory that is copied. It returns a\r
165 list of names relative to the `src` directory that should\r
166 not be copied.\r
167\r
168 XXX Consider this example code rather than the ultimate tool.\r
169\r
170 """\r
171 names = os.listdir(src)\r
172 if ignore is not None:\r
173 ignored_names = ignore(src, names)\r
174 else:\r
175 ignored_names = set()\r
176\r
177 os.makedirs(dst)\r
178 errors = []\r
179 for name in names:\r
180 if name in ignored_names:\r
181 continue\r
182 srcname = os.path.join(src, name)\r
183 dstname = os.path.join(dst, name)\r
184 try:\r
185 if symlinks and os.path.islink(srcname):\r
186 linkto = os.readlink(srcname)\r
187 os.symlink(linkto, dstname)\r
188 elif os.path.isdir(srcname):\r
189 copytree(srcname, dstname, symlinks, ignore)\r
190 else:\r
191 # Will raise a SpecialFileError for unsupported file types\r
192 copy2(srcname, dstname)\r
193 # catch the Error from the recursive copytree so that we can\r
194 # continue with other files\r
195 except Error, err:\r
196 errors.extend(err.args[0])\r
197 except EnvironmentError, why:\r
198 errors.append((srcname, dstname, str(why)))\r
199 try:\r
200 copystat(src, dst)\r
201 except OSError, why:\r
202 if WindowsError is not None and isinstance(why, WindowsError):\r
203 # Copying file access times may fail on Windows\r
204 pass\r
205 else:\r
206 errors.append((src, dst, str(why)))\r
207 if errors:\r
208 raise Error, errors\r
209\r
210def rmtree(path, ignore_errors=False, onerror=None):\r
211 """Recursively delete a directory tree.\r
212\r
213 If ignore_errors is set, errors are ignored; otherwise, if onerror\r
214 is set, it is called to handle the error with arguments (func,\r
215 path, exc_info) where func is os.listdir, os.remove, or os.rmdir;\r
216 path is the argument to that function that caused it to fail; and\r
217 exc_info is a tuple returned by sys.exc_info(). If ignore_errors\r
218 is false and onerror is None, an exception is raised.\r
219\r
220 """\r
221 if ignore_errors:\r
222 def onerror(*args):\r
223 pass\r
224 elif onerror is None:\r
225 def onerror(*args):\r
226 raise\r
227 try:\r
228 if os.path.islink(path):\r
229 # symlinks to directories are forbidden, see bug #1669\r
230 raise OSError("Cannot call rmtree on a symbolic link")\r
231 except OSError:\r
232 onerror(os.path.islink, path, sys.exc_info())\r
233 # can't continue even if onerror hook returns\r
234 return\r
235 names = []\r
236 try:\r
237 names = os.listdir(path)\r
238 except os.error, err:\r
239 onerror(os.listdir, path, sys.exc_info())\r
240 for name in names:\r
241 fullname = os.path.join(path, name)\r
242 try:\r
243 mode = os.lstat(fullname).st_mode\r
244 except os.error:\r
245 mode = 0\r
246 if stat.S_ISDIR(mode):\r
247 rmtree(fullname, ignore_errors, onerror)\r
248 else:\r
249 try:\r
250 os.remove(fullname)\r
251 except os.error, err:\r
252 onerror(os.remove, fullname, sys.exc_info())\r
253 try:\r
254 os.rmdir(path)\r
255 except os.error:\r
256 onerror(os.rmdir, path, sys.exc_info())\r
257\r
258\r
259def _basename(path):\r
260 # A basename() variant which first strips the trailing slash, if present.\r
261 # Thus we always get the last component of the path, even for directories.\r
262 sep = os.path.sep + (os.path.altsep or '')\r
263 return os.path.basename(path.rstrip(sep))\r
264\r
265def move(src, dst):\r
266 """Recursively move a file or directory to another location. This is\r
267 similar to the Unix "mv" command.\r
268\r
269 If the destination is a directory or a symlink to a directory, the source\r
270 is moved inside the directory. The destination path must not already\r
271 exist.\r
272\r
273 If the destination already exists but is not a directory, it may be\r
274 overwritten depending on os.rename() semantics.\r
275\r
276 If the destination is on our current filesystem, then rename() is used.\r
277 Otherwise, src is copied to the destination and then removed.\r
278 A lot more could be done here... A look at a mv.c shows a lot of\r
279 the issues this implementation glosses over.\r
280\r
281 """\r
282 real_dst = dst\r
283 if os.path.isdir(dst):\r
284 if _samefile(src, dst):\r
285 # We might be on a case insensitive filesystem,\r
286 # perform the rename anyway.\r
287 os.rename(src, dst)\r
288 return\r
289\r
290 real_dst = os.path.join(dst, _basename(src))\r
291 if os.path.exists(real_dst):\r
292 raise Error, "Destination path '%s' already exists" % real_dst\r
293 try:\r
294 os.rename(src, real_dst)\r
295 except OSError:\r
296 if os.path.isdir(src):\r
297 if _destinsrc(src, dst):\r
298 raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst)\r
299 copytree(src, real_dst, symlinks=True)\r
300 rmtree(src)\r
301 else:\r
302 copy2(src, real_dst)\r
303 os.unlink(src)\r
304\r
305def _destinsrc(src, dst):\r
306 src = abspath(src)\r
307 dst = abspath(dst)\r
308 if not src.endswith(os.path.sep):\r
309 src += os.path.sep\r
310 if not dst.endswith(os.path.sep):\r
311 dst += os.path.sep\r
312 return dst.startswith(src)\r
313\r
314def _get_gid(name):\r
315 """Returns a gid, given a group name."""\r
316 if getgrnam is None or name is None:\r
317 return None\r
318 try:\r
319 result = getgrnam(name)\r
320 except KeyError:\r
321 result = None\r
322 if result is not None:\r
323 return result[2]\r
324 return None\r
325\r
326def _get_uid(name):\r
327 """Returns an uid, given a user name."""\r
328 if getpwnam is None or name is None:\r
329 return None\r
330 try:\r
331 result = getpwnam(name)\r
332 except KeyError:\r
333 result = None\r
334 if result is not None:\r
335 return result[2]\r
336 return None\r
337\r
338def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,\r
339 owner=None, group=None, logger=None):\r
340 """Create a (possibly compressed) tar file from all the files under\r
341 'base_dir'.\r
342\r
343 'compress' must be "gzip" (the default), "bzip2", or None.\r
344\r
345 'owner' and 'group' can be used to define an owner and a group for the\r
346 archive that is being built. If not provided, the current owner and group\r
347 will be used.\r
348\r
349 The output tar file will be named 'base_name' + ".tar", possibly plus\r
350 the appropriate compression extension (".gz", or ".bz2").\r
351\r
352 Returns the output filename.\r
353 """\r
354 tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: ''}\r
355 compress_ext = {'gzip': '.gz', 'bzip2': '.bz2'}\r
356\r
357 # flags for compression program, each element of list will be an argument\r
358 if compress is not None and compress not in compress_ext.keys():\r
359 raise ValueError, \\r
360 ("bad value for 'compress': must be None, 'gzip' or 'bzip2'")\r
361\r
362 archive_name = base_name + '.tar' + compress_ext.get(compress, '')\r
363 archive_dir = os.path.dirname(archive_name)\r
364\r
365 if archive_dir and not os.path.exists(archive_dir):\r
366 if logger is not None:\r
367 logger.info("creating %s", archive_dir)\r
368 if not dry_run:\r
369 os.makedirs(archive_dir)\r
370\r
371\r
372 # creating the tarball\r
373 import tarfile # late import so Python build itself doesn't break\r
374\r
375 if logger is not None:\r
376 logger.info('Creating tar archive')\r
377\r
378 uid = _get_uid(owner)\r
379 gid = _get_gid(group)\r
380\r
381 def _set_uid_gid(tarinfo):\r
382 if gid is not None:\r
383 tarinfo.gid = gid\r
384 tarinfo.gname = group\r
385 if uid is not None:\r
386 tarinfo.uid = uid\r
387 tarinfo.uname = owner\r
388 return tarinfo\r
389\r
390 if not dry_run:\r
391 tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])\r
392 try:\r
393 tar.add(base_dir, filter=_set_uid_gid)\r
394 finally:\r
395 tar.close()\r
396\r
397 return archive_name\r
398\r
399def _call_external_zip(base_dir, zip_filename, verbose=False, dry_run=False):\r
400 # XXX see if we want to keep an external call here\r
401 if verbose:\r
402 zipoptions = "-r"\r
403 else:\r
404 zipoptions = "-rq"\r
405 from distutils.errors import DistutilsExecError\r
406 from distutils.spawn import spawn\r
407 try:\r
408 spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run)\r
409 except DistutilsExecError:\r
410 # XXX really should distinguish between "couldn't find\r
411 # external 'zip' command" and "zip failed".\r
412 raise ExecError, \\r
413 ("unable to create zip file '%s': "\r
414 "could neither import the 'zipfile' module nor "\r
415 "find a standalone zip utility") % zip_filename\r
416\r
417def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None):\r
418 """Create a zip file from all the files under 'base_dir'.\r
419\r
420 The output zip file will be named 'base_name' + ".zip". Uses either the\r
421 "zipfile" Python module (if available) or the InfoZIP "zip" utility\r
422 (if installed and found on the default search path). If neither tool is\r
423 available, raises ExecError. Returns the name of the output zip\r
424 file.\r
425 """\r
426 zip_filename = base_name + ".zip"\r
427 archive_dir = os.path.dirname(base_name)\r
428\r
429 if archive_dir and not os.path.exists(archive_dir):\r
430 if logger is not None:\r
431 logger.info("creating %s", archive_dir)\r
432 if not dry_run:\r
433 os.makedirs(archive_dir)\r
434\r
435 # If zipfile module is not available, try spawning an external 'zip'\r
436 # command.\r
437 try:\r
438 import zipfile\r
439 except ImportError:\r
440 zipfile = None\r
441\r
442 if zipfile is None:\r
443 _call_external_zip(base_dir, zip_filename, verbose, dry_run)\r
444 else:\r
445 if logger is not None:\r
446 logger.info("creating '%s' and adding '%s' to it",\r
447 zip_filename, base_dir)\r
448\r
449 if not dry_run:\r
450 with zipfile.ZipFile(zip_filename, "w",\r
451 compression=zipfile.ZIP_DEFLATED) as zf:\r
452 for dirpath, dirnames, filenames in os.walk(base_dir):\r
453 for name in filenames:\r
454 path = os.path.normpath(os.path.join(dirpath, name))\r
455 if os.path.isfile(path):\r
456 zf.write(path, path)\r
457 if logger is not None:\r
458 logger.info("adding '%s'", path)\r
459\r
460 return zip_filename\r
461\r
462_ARCHIVE_FORMATS = {\r
463 'gztar': (_make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),\r
464 'bztar': (_make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),\r
465 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"),\r
466 'zip': (_make_zipfile, [],"ZIP file")\r
467 }\r
468\r
469def get_archive_formats():\r
470 """Returns a list of supported formats for archiving and unarchiving.\r
471\r
472 Each element of the returned sequence is a tuple (name, description)\r
473 """\r
474 formats = [(name, registry[2]) for name, registry in\r
475 _ARCHIVE_FORMATS.items()]\r
476 formats.sort()\r
477 return formats\r
478\r
479def register_archive_format(name, function, extra_args=None, description=''):\r
480 """Registers an archive format.\r
481\r
482 name is the name of the format. function is the callable that will be\r
483 used to create archives. If provided, extra_args is a sequence of\r
484 (name, value) tuples that will be passed as arguments to the callable.\r
485 description can be provided to describe the format, and will be returned\r
486 by the get_archive_formats() function.\r
487 """\r
488 if extra_args is None:\r
489 extra_args = []\r
490 if not isinstance(function, collections.Callable):\r
491 raise TypeError('The %s object is not callable' % function)\r
492 if not isinstance(extra_args, (tuple, list)):\r
493 raise TypeError('extra_args needs to be a sequence')\r
494 for element in extra_args:\r
495 if not isinstance(element, (tuple, list)) or len(element) !=2 :\r
496 raise TypeError('extra_args elements are : (arg_name, value)')\r
497\r
498 _ARCHIVE_FORMATS[name] = (function, extra_args, description)\r
499\r
500def unregister_archive_format(name):\r
501 del _ARCHIVE_FORMATS[name]\r
502\r
503def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,\r
504 dry_run=0, owner=None, group=None, logger=None):\r
505 """Create an archive file (eg. zip or tar).\r
506\r
507 'base_name' is the name of the file to create, minus any format-specific\r
508 extension; 'format' is the archive format: one of "zip", "tar", "bztar"\r
509 or "gztar".\r
510\r
511 'root_dir' is a directory that will be the root directory of the\r
512 archive; ie. we typically chdir into 'root_dir' before creating the\r
513 archive. 'base_dir' is the directory where we start archiving from;\r
514 ie. 'base_dir' will be the common prefix of all files and\r
515 directories in the archive. 'root_dir' and 'base_dir' both default\r
516 to the current directory. Returns the name of the archive file.\r
517\r
518 'owner' and 'group' are used when creating a tar archive. By default,\r
519 uses the current owner and group.\r
520 """\r
521 save_cwd = os.getcwd()\r
522 if root_dir is not None:\r
523 if logger is not None:\r
524 logger.debug("changing into '%s'", root_dir)\r
525 base_name = os.path.abspath(base_name)\r
526 if not dry_run:\r
527 os.chdir(root_dir)\r
528\r
529 if base_dir is None:\r
530 base_dir = os.curdir\r
531\r
532 kwargs = {'dry_run': dry_run, 'logger': logger}\r
533\r
534 try:\r
535 format_info = _ARCHIVE_FORMATS[format]\r
536 except KeyError:\r
537 raise ValueError, "unknown archive format '%s'" % format\r
538\r
539 func = format_info[0]\r
540 for arg, val in format_info[1]:\r
541 kwargs[arg] = val\r
542\r
543 if format != 'zip':\r
544 kwargs['owner'] = owner\r
545 kwargs['group'] = group\r
546\r
547 try:\r
548 filename = func(base_name, base_dir, **kwargs)\r
549 finally:\r
550 if root_dir is not None:\r
551 if logger is not None:\r
552 logger.debug("changing back to '%s'", save_cwd)\r
553 os.chdir(save_cwd)\r
554\r
555 return filename\r