1 """ path.py - An object representing a path to a file or directory.
2
3 Example:
4
5 from path import path
6 d = path('/home/guido/bin')
7 for f in d.files('*.py'):
8 f.chmod(0755)
9
10 This module requires Python 2.2 or later.
11
12
13 URL: http://www.jorendorff.com/articles/python/path
14 Author: Jason Orendorff <jason.orendorff\x40gmail\x2ecom> (and others - see the url!)
15 Date: 7 Mar 2004
16 """
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 from __future__ import generators
36
37 import sys, warnings, os, fnmatch, glob, shutil, codecs, md5
38
39 __version__ = '2.1'
40 __all__ = ['path']
41
42
43 if os.name == 'nt':
44 try:
45 import win32security
46 except ImportError:
47 win32security = None
48 else:
49 try:
50 import pwd
51 except ImportError:
52 pwd = None
53
54
55 if sys.version_info[:2] < (2,3):
56 _base = str
57 _getcwd = os.getcwd
58 else:
59 _base = unicode
60 _getcwd = os.getcwdu
61
62
63 try:
64 True, False
65 except NameError:
66 True, False = 1, 0
67
68
69 try:
70 basestring
71 except NameError:
72 basestring = (str, unicode)
73
74
75 _textmode = 'r'
76 if hasattr(file, 'newlines'):
77 _textmode = 'U'
78
79 import natural_sort
80
83
85 """ Represents a filesystem path.
86
87 For documentation on individual methods, consult their
88 counterparts in os.path.
89 """
90
91
92
93
94
95
96
98 try:
99 resultStr = _base.__add__(self, more)
100 except TypeError:
101 resultStr = NotImplemented
102 if resultStr is NotImplemented:
103 return resultStr
104 return self.__class__(resultStr)
105
107 if isinstance(other, basestring):
108 return self.__class__(other.__add__(self))
109 else:
110 return NotImplemented
111
112
114 """ fp.__div__(rel) == fp / rel == fp.joinpath(rel)
115
116 Join two path components, adding a separator character if
117 needed.
118 """
119 return self.__class__(os.path.join(self, rel))
120
121
122 __truediv__ = __div__
123
125 """ Return the current working directory as a path object. """
126 return cls(_getcwd())
127 getcwd = classmethod(getcwd)
128
129
130
131
132 isabs = os.path.isabs
140 basename = os.path.basename
141
143 """ Clean up a filename by calling expandvars(),
144 expanduser(), and normpath() on it.
145
146 This is commonly everything needed to clean up a filename
147 read from a configuration file, for example.
148 """
149 return self.expandvars().expanduser().normpath()
150
154
158
162
163 parent = property(
164 dirname, None, None,
165 """ This path's parent directory, as a new path object.
166
167 For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib')
168 """)
169
170 name = property(
171 basename, None, None,
172 """ The name of this file or directory without the full path.
173
174 For example, path('/usr/local/lib/libpython.so').name == 'libpython.so'
175 """)
176
177 namebase = property(
178 _get_namebase, None, None,
179 """ The same as path.name, but with one file extension stripped off.
180
181 For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz',
182 but path('/home/guido/python.tar.gz').namebase == 'python.tar'
183 """)
184
185 ext = property(
186 _get_ext, None, None,
187 """ The file extension, for example '.py'. """)
188
189 drive = property(
190 _get_drive, None, None,
191 """ The drive specifier, for example 'C:'.
192 This is always empty on systems that don't use drive specifiers.
193 """)
194
196 """ p.splitpath() -> Return (p.parent, p.name). """
197 parent, child = os.path.split(self)
198 return self.__class__(parent), child
199
201 """ p.splitdrive() -> Return (p.drive, <the rest of p>).
202
203 Split the drive specifier from this path. If there is
204 no drive specifier, p.drive is empty, so the return value
205 is simply (path(''), p). This is always the case on Unix.
206 """
207 drive, rel = os.path.splitdrive(self)
208 return self.__class__(drive), rel
209
210 - def splitext(self):
211 """ p.splitext() -> Return (p.stripext(), p.ext).
212
213 Split the filename extension from this path and return
214 the two parts. Either part may be empty.
215
216 The extension is everything from '.' to the end of the
217 last path segment. This has the property that if
218 (a, b) == p.splitext(), then a + b == p.
219 """
220 filename, ext = os.path.splitext(self)
221 return self.__class__(filename), ext
222
224 """ p.stripext() -> Remove one file extension from the path.
225
226 For example, path('/home/guido/python.tar.gz').stripext()
227 returns path('/home/guido/python.tar').
228 """
229 return self.splitext()[0]
230
231 if hasattr(os.path, 'splitunc'):
233 unc, rest = os.path.splitunc(self)
234 return self.__class__(unc), rest
235
237 unc, r = os.path.splitunc(self)
238 return self.__class__(unc)
239
240 uncshare = property(
241 _get_uncshare, None, None,
242 """ The UNC mount point for this path.
243 This is empty for paths on local drives. """)
244
246 """ Join two or more path components, adding a separator
247 character (os.sep) if needed. Returns a new path
248 object.
249 """
250 return self.__class__(os.path.join(self, *args))
251
253 r""" Return a list of the path components in this path.
254
255 The first item in the list will be a path. Its value will be
256 either os.curdir, os.pardir, empty, or the root directory of
257 this path (for example, '/' or 'C:\\'). The other items in
258 the list will be strings.
259
260 path.path.joinpath(*result) will yield the original path.
261 """
262 parts = []
263 loc = self
264 while loc != os.curdir and loc != os.pardir:
265 prev = loc
266 loc, child = prev.splitpath()
267 if loc == prev:
268 break
269 parts.append(child)
270 parts.append(loc)
271 parts.reverse()
272 return parts
273
275 """ Return this path as a relative path,
276 based from the current working directory.
277 """
278 cwd = self.__class__(os.getcwd())
279 return cwd.relpathto(self)
280
282 """ Return a relative path from self to dest.
283
284 If there is no relative path from self to dest, for example if
285 they reside on different drives in Windows, then this returns
286 dest.abspath().
287 """
288 origin = self.abspath()
289 dest = self.__class__(dest).abspath()
290
291 orig_list = origin.normcase().splitall()
292
293 dest_list = dest.splitall()
294
295 if orig_list[0] != os.path.normcase(dest_list[0]):
296
297 return dest
298
299
300 i = 0
301 for start_seg, dest_seg in zip(orig_list, dest_list):
302 if start_seg != os.path.normcase(dest_seg):
303 break
304 i += 1
305
306
307
308
309 segments = [os.pardir] * (len(orig_list) - i)
310
311 segments += dest_list[i:]
312 if len(segments) == 0:
313
314 relpath = os.curdir
315 else:
316 relpath = os.path.join(*segments)
317 return self.__class__(relpath)
318
319
320
322 """ D.listdir() -> List of items in this directory.
323
324 Use D.files() or D.dirs() instead if you want a listing
325 of just files or just subdirectories.
326
327 The elements of the list are path objects.
328
329 With the optional 'pattern' argument, this only lists
330 items whose names match the given pattern.
331 """
332 names = os.listdir(self)
333 if pattern is not None:
334 names = fnmatch.filter(names, pattern)
335
336
337
338
339
340 folders = []
341 files = []
342 for fname in names:
343 full_path = self / fname
344 if os.path.isdir(full_path):
345 folders.append(fname)
346 else:
347 files.append(fname)
348
349 key = lambda item: item.lower()
350 folders = natural_sort.obj_natsorted(folders,
351 natural_sort.natcasecmp,
352 key)
353 files = natural_sort.obj_natsorted(files,
354 natural_sort.natcasecmp,
355 key)
356
357 full_paths = [self / child for child in folders + files]
358 return full_paths
359
360 - def dirs(self, pattern=None):
361 """ D.dirs() -> List of this directory's subdirectories.
362
363 The elements of the list are path objects.
364 This does not walk recursively into subdirectories
365 (but see path.walkdirs).
366
367 With the optional 'pattern' argument, this only lists
368 directories whose names match the given pattern. For
369 example, d.dirs('build-*').
370 """
371 return [p for p in self.listdir(pattern) if p.isdir()]
372
373 - def files(self, pattern=None):
374 """ D.files() -> List of the files in this directory.
375
376 The elements of the list are path objects.
377 This does not walk into subdirectories (see path.walkfiles).
378
379 With the optional 'pattern' argument, this only lists files
380 whose names match the given pattern. For example,
381 d.files('*.pyc').
382 """
383
384 return [p for p in self.listdir(pattern) if p.isfile()]
385
386 - def walk(self, pattern=None, errors='strict'):
387 """ D.walk() -> iterator over files and subdirs, recursively.
388
389 The iterator yields path objects naming each child item of
390 this directory and its descendants. This requires that
391 D.isdir().
392
393 This performs a depth-first traversal of the directory tree.
394 Each directory is returned just before all its children.
395
396 The errors= keyword argument controls behavior when an
397 error occurs. The default is 'strict', which causes an
398 exception. The other allowed values are 'warn', which
399 reports the error via warnings.warn(), and 'ignore'.
400 """
401 if errors not in ('strict', 'warn', 'ignore'):
402 raise ValueError("invalid errors parameter")
403
404 try:
405 childList = self.listdir()
406 except Exception:
407 if errors == 'ignore':
408 return
409 elif errors == 'warn':
410 warnings.warn(
411 "Unable to list directory '%s': %s"
412 % (self, sys.exc_info()[1]),
413 TreeWalkWarning)
414 else:
415 raise
416
417 for child in childList:
418
419
420 if pattern:
421 if not child.fnmatch(pattern):
422 continue
423 else:
424 yield child
425 try:
426 isdir = child.isdir()
427 except Exception:
428 if errors == 'ignore':
429 isdir = False
430 elif errors == 'warn':
431 warnings.warn(
432 "Unable to access '%s': %s"
433 % (child, sys.exc_info()[1]),
434 TreeWalkWarning)
435 isdir = False
436 else:
437 raise
438
439 if isdir:
440 for item in child.walk(pattern, errors):
441 yield item
442
443 - def walkdirs(self, pattern=None, errors='strict'):
444 """ D.walkdirs() -> iterator over subdirs, recursively.
445
446 With the optional 'pattern' argument, this yields only
447 directories whose names match the given pattern. For
448 example, mydir.walkdirs('*test') yields only directories
449 with names ending in 'test'.
450
451 The errors= keyword argument controls behavior when an
452 error occurs. The default is 'strict', which causes an
453 exception. The other allowed values are 'warn', which
454 reports the error via warnings.warn(), and 'ignore'.
455 """
456 if errors not in ('strict', 'warn', 'ignore'):
457 raise ValueError("invalid errors parameter")
458
459 try:
460 dirs = self.dirs()
461 except Exception:
462 if errors == 'ignore':
463 return
464 elif errors == 'warn':
465 warnings.warn(
466 "Unable to list directory '%s': %s"
467 % (self, sys.exc_info()[1]),
468 TreeWalkWarning)
469 else:
470 raise
471
472 for child in dirs:
473 if pattern is None or child.fnmatch(pattern):
474 yield child
475 for subsubdir in child.walkdirs(pattern, errors):
476 yield subsubdir
477
478 - def walkfiles(self, pattern=None, errors='strict'):
479 """ D.walkfiles() -> iterator over files in D, recursively.
480
481 The optional argument, pattern, limits the results to files
482 with names that match the pattern. For example,
483 mydir.walkfiles('*.tmp') yields only files with the .tmp
484 extension.
485 """
486 if errors not in ('strict', 'warn', 'ignore'):
487 raise ValueError("invalid errors parameter")
488
489 try:
490 childList = self.listdir()
491 except Exception:
492 if errors == 'ignore':
493 return
494 elif errors == 'warn':
495 warnings.warn(
496 "Unable to list directory '%s': %s"
497 % (self, sys.exc_info()[1]),
498 TreeWalkWarning)
499 else:
500 raise
501
502 for child in childList:
503 try:
504 isfile = child.isfile()
505 isdir = not isfile and child.isdir()
506 except:
507 if errors == 'ignore':
508 return
509 elif errors == 'warn':
510 warnings.warn(
511 "Unable to access '%s': %s"
512 % (self, sys.exc_info()[1]),
513 TreeWalkWarning)
514 else:
515 raise
516
517 if isfile:
518 if pattern is None or child.fnmatch(pattern):
519 yield child
520 elif isdir:
521 for f in child.walkfiles(pattern, errors):
522 yield f
523
525 """ Return True if self.name matches the given pattern.
526
527 pattern - A filename pattern with wildcards,
528 for example '*.py'.
529 """
530 return fnmatch.fnmatch(self.name, pattern)
531
532 - def glob(self, pattern):
533 """ Return a list of path objects that match the pattern.
534
535 pattern - a path relative to this directory, with wildcards.
536
537 For example, path('/users').glob('*/bin/*') returns a list
538 of all the files users have in their bin directories.
539 """
540 cls = self.__class__
541 return [cls(s) for s in glob.glob(_base(self / pattern))]
542
543
544
545
546 - def open(self, mode='r'):
547 """ Open this file. Return a file object. """
548 return file(self, mode)
549
551 """ Open this file, read all bytes, return them as a string. """
552 f = self.open('rb')
553 try:
554 return f.read()
555 finally:
556 f.close()
557
559 """ Open this file and write the given bytes to it.
560
561 Default behavior is to overwrite any existing file.
562 Call p.write_bytes(bytes, append=True) to append instead.
563 """
564 if append:
565 mode = 'ab'
566 else:
567 mode = 'wb'
568 f = self.open(mode)
569 try:
570 f.write(bytes)
571 finally:
572 f.close()
573
574 - def text(self, encoding=None, errors='strict'):
575 r""" Open this file, read it in, return the content as a string.
576
577 This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r'
578 are automatically translated to '\n'.
579
580 Optional arguments:
581
582 encoding - The Unicode encoding (or character set) of
583 the file. If present, the content of the file is
584 decoded and returned as a unicode object; otherwise
585 it is returned as an 8-bit str.
586 errors - How to handle Unicode errors; see help(str.decode)
587 for the options. Default is 'strict'.
588 """
589 if encoding is None:
590
591 f = self.open(_textmode)
592 try:
593 return f.read()
594 finally:
595 f.close()
596 else:
597
598 f = codecs.open(self, 'r', encoding, errors)
599
600
601 try:
602 t = f.read()
603 finally:
604 f.close()
605 return (t.replace(u'\r\n', u'\n')
606 .replace(u'\r\x85', u'\n')
607 .replace(u'\r', u'\n')
608 .replace(u'\x85', u'\n')
609 .replace(u'\u2028', u'\n'))
610
611 - def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
612 r""" Write the given text to this file.
613
614 The default behavior is to overwrite any existing file;
615 to append instead, use the 'append=True' keyword argument.
616
617 There are two differences between path.write_text() and
618 path.write_bytes(): newline handling and Unicode handling.
619 See below.
620
621 Parameters:
622
623 - text - str/unicode - The text to be written.
624
625 - encoding - str - The Unicode encoding that will be used.
626 This is ignored if 'text' isn't a Unicode string.
627
628 - errors - str - How to handle Unicode encoding errors.
629 Default is 'strict'. See help(unicode.encode) for the
630 options. This is ignored if 'text' isn't a Unicode
631 string.
632
633 - linesep - keyword argument - str/unicode - The sequence of
634 characters to be used to mark end-of-line. The default is
635 os.linesep. You can also specify None; this means to
636 leave all newlines as they are in 'text'.
637
638 - append - keyword argument - bool - Specifies what to do if
639 the file already exists (True: append to the end of it;
640 False: overwrite it.) The default is False.
641
642
643 --- Newline handling.
644
645 write_text() converts all standard end-of-line sequences
646 ('\n', '\r', and '\r\n') to your platform's default end-of-line
647 sequence (see os.linesep; on Windows, for example, the
648 end-of-line marker is '\r\n').
649
650 If you don't like your platform's default, you can override it
651 using the 'linesep=' keyword argument. If you specifically want
652 write_text() to preserve the newlines as-is, use 'linesep=None'.
653
654 This applies to Unicode text the same as to 8-bit text, except
655 there are three additional standard Unicode end-of-line sequences:
656 u'\x85', u'\r\x85', and u'\u2028'.
657
658 (This is slightly different from when you open a file for
659 writing with fopen(filename, "w") in C or file(filename, 'w')
660 in Python.)
661
662
663 --- Unicode
664
665 If 'text' isn't Unicode, then apart from newline handling, the
666 bytes are written verbatim to the file. The 'encoding' and
667 'errors' arguments are not used and must be omitted.
668
669 If 'text' is Unicode, it is first converted to bytes using the
670 specified 'encoding' (or the default encoding if 'encoding'
671 isn't specified). The 'errors' argument applies only to this
672 conversion.
673
674 """
675 if isinstance(text, unicode):
676 if linesep is not None:
677
678
679 text = (text.replace(u'\r\n', u'\n')
680 .replace(u'\r\x85', u'\n')
681 .replace(u'\r', u'\n')
682 .replace(u'\x85', u'\n')
683 .replace(u'\u2028', u'\n'))
684 text = text.replace(u'\n', linesep)
685 if encoding is None:
686 encoding = sys.getdefaultencoding()
687 bytes = text.encode(encoding, errors)
688 else:
689
690
691 assert encoding is None
692
693 if linesep is not None:
694 text = (text.replace('\r\n', '\n')
695 .replace('\r', '\n'))
696 bytes = text.replace('\n', linesep)
697
698 self.write_bytes(bytes, append)
699
700 - def lines(self, encoding=None, errors='strict', retain=True):
701 r""" Open this file, read all lines, return them in a list.
702
703 Optional arguments:
704 encoding - The Unicode encoding (or character set) of
705 the file. The default is None, meaning the content
706 of the file is read as 8-bit characters and returned
707 as a list of (non-Unicode) str objects.
708 errors - How to handle Unicode errors; see help(str.decode)
709 for the options. Default is 'strict'
710 retain - If true, retain newline characters; but all newline
711 character combinations ('\r', '\n', '\r\n') are
712 translated to '\n'. If false, newline characters are
713 stripped off. Default is True.
714
715 This uses 'U' mode in Python 2.3 and later.
716 """
717 if encoding is None and retain:
718 f = self.open(_textmode)
719 try:
720 return f.readlines()
721 finally:
722 f.close()
723 else:
724 return self.text(encoding, errors).splitlines(retain)
725
726 - def write_lines(self, lines, encoding=None, errors='strict',
727 linesep=os.linesep, append=False):
728 r""" Write the given lines of text to this file.
729
730 By default this overwrites any existing file at this path.
731
732 This puts a platform-specific newline sequence on every line.
733 See 'linesep' below.
734
735 lines - A list of strings.
736
737 encoding - A Unicode encoding to use. This applies only if
738 'lines' contains any Unicode strings.
739
740 errors - How to handle errors in Unicode encoding. This
741 also applies only to Unicode strings.
742
743 linesep - The desired line-ending. This line-ending is
744 applied to every line. If a line already has any
745 standard line ending ('\r', '\n', '\r\n', u'\x85',
746 u'\r\x85', u'\u2028'), that will be stripped off and
747 this will be used instead. The default is os.linesep,
748 which is platform-dependent ('\r\n' on Windows, '\n' on
749 Unix, etc.) Specify None to write the lines as-is,
750 like file.writelines().
751
752 Use the keyword argument append=True to append lines to the
753 file. The default is to overwrite the file. Warning:
754 When you use this with Unicode data, if the encoding of the
755 existing data in the file is different from the encoding
756 you specify with the encoding= parameter, the result is
757 mixed-encoding data, which can really confuse someone trying
758 to read the file later.
759 """
760 if append:
761 mode = 'ab'
762 else:
763 mode = 'wb'
764 f = self.open(mode)
765 try:
766 for line in lines:
767 isUnicode = isinstance(line, unicode)
768 if linesep is not None:
769
770
771 if isUnicode:
772 if line[-2:] in (u'\r\n', u'\x0d\x85'):
773 line = line[:-2]
774 elif line[-1:] in (u'\r', u'\n',
775 u'\x85', u'\u2028'):
776 line = line[:-1]
777 else:
778 if line[-2:] == '\r\n':
779 line = line[:-2]
780 elif line[-1:] in ('\r', '\n'):
781 line = line[:-1]
782 line += linesep
783 if isUnicode:
784 if encoding is None:
785 encoding = sys.getdefaultencoding()
786 line = line.encode(encoding, errors)
787 f.write(line)
788 finally:
789 f.close()
790
792 """ Calculate the md5 hash for this file.
793
794 This reads through the entire file.
795 """
796 f = self.open('rb')
797 try:
798 m = md5.new()
799 while True:
800 d = f.read(8192)
801 if not d:
802 break
803 m.update(d)
804 finally:
805 f.close()
806 return m.digest()
807
808
809
810 exists = os.path.exists
811 isdir = os.path.isdir
812 isfile = os.path.isfile
813 islink = os.path.islink
814 ismount = os.path.ismount
815
816 if hasattr(os.path, 'samefile'):
817 samefile = os.path.samefile
818
819 getatime = os.path.getatime
820 atime = property(
821 getatime, None, None,
822 """ Last access time of the file. """)
823
824 getmtime = os.path.getmtime
825 mtime = property(
826 getmtime, None, None,
827 """ Last-modified time of the file. """)
828
829 if hasattr(os.path, 'getctime'):
830 getctime = os.path.getctime
831 ctime = property(
832 getctime, None, None,
833 """ Creation time of the file. """)
834
835 getsize = os.path.getsize
836 size = property(
837 getsize, None, None,
838 """ Size of the file, in bytes. """)
839
840 if hasattr(os, 'access'):
842 """ Return true if current user has access to this path.
843
844 mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
845 """
846 return os.access(self, mode)
847
849 """ Perform a stat() system call on this path. """
850 return os.stat(self)
851
853 """ Like path.stat(), but do not follow symbolic links. """
854 return os.lstat(self)
855
857 r""" Return the name of the owner of this file or directory.
858
859 This follows symbolic links.
860
861 On Windows, this returns a name of the form ur'DOMAIN\User Name'.
862 On Windows, a group can own a file or directory.
863 """
864 if os.name == 'nt':
865 if win32security is None:
866 raise Exception("path.owner requires win32all to be installed")
867 desc = win32security.GetFileSecurity(
868 self, win32security.OWNER_SECURITY_INFORMATION)
869 sid = desc.GetSecurityDescriptorOwner()
870 account, domain, typecode = win32security.LookupAccountSid(None, sid)
871 return domain + u'\\' + account
872 else:
873 if pwd is None:
874 raise NotImplementedError("path.owner is not implemented on this platform.")
875 st = self.stat()
876 return pwd.getpwuid(st.st_uid).pw_name
877
878 owner = property(
879 get_owner, None, None,
880 """ Name of the owner of this file or directory. """)
881
882 if hasattr(os, 'statvfs'):
884 """ Perform a statvfs() system call on this path. """
885 return os.statvfs(self)
886
887 if hasattr(os, 'pathconf'):
890
891
892
893
895 """ Set the access and modified times of this file. """
896 os.utime(self, times)
897
900
901 if hasattr(os, 'chown'):
902 - def chown(self, uid, gid):
903 os.chown(self, uid, gid)
904
907
910
911
912
913
914 - def mkdir(self, mode=0777):
916
919
922
925
926
927
928
930 """ Set the access/modified times of this file to the current time.
931 Create the file if it does not exist.
932 """
933 fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666)
934 os.close(fd)
935 os.utime(self, None)
936
939
942
943
944
945
946 if hasattr(os, 'link'):
947 - def link(self, newpath):
948 """ Create a hard link at 'newpath', pointing to this file. """
949 os.link(self, newpath)
950
951 if hasattr(os, 'symlink'):
953 """ Create a symbolic link at 'newlink', pointing here. """
954 os.symlink(self, newlink)
955
956 if hasattr(os, 'readlink'):
958 """ Return the path to which this symbolic link points.
959
960 The result may be an absolute or a relative path.
961 """
962 return self.__class__(os.readlink(self))
963
965 """ Return the path to which this symbolic link points.
966
967 The result is always an absolute path.
968 """
969 p = self.readlink()
970 if p.isabs():
971 return p
972 else:
973 return (self.parent / p).abspath()
974
975
976
977
978 copyfile = shutil.copyfile
979 copymode = shutil.copymode
980 copystat = shutil.copystat
981 copy = shutil.copy
982 copy2 = shutil.copy2
983 copytree = shutil.copytree
984 if hasattr(shutil, 'move'):
985 move = shutil.move
986 rmtree = shutil.rmtree
987
988
989
990
991 if hasattr(os, 'chroot'):
994
995 if hasattr(os, 'startfile'):
998