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