##// END OF EJS Templates
upgraded to path.py v2.1
vivainio -
Show More
@@ -11,23 +11,18 b' This module requires Python 2.2 or later.'
11
11
12
12
13 URL: http://www.jorendorff.com/articles/python/path
13 URL: http://www.jorendorff.com/articles/python/path
14 Author: Jason Orendorff <jason@jorendorff.com> (and others - see the url!)
14 Author: Jason Orendorff <jason.orendorff\x40gmail\x2ecom> (and others - see the url!)
15 Date: 7 Mar 2004
15 Date: 7 Mar 2004
16 """
16 """
17
17
18 # Original license statement:
19 #License: You may use path.py for whatever you wish, at your own risk. (For
20 #example, you may modify, relicense, and redistribute it.) It is provided
21 #without any guarantee or warranty of any kind, not even for merchantability or
22 #fitness for any purpose.
23
24 # IPython license note:
25 # For the sake of convenience, IPython includes this module
26 # in its directory structure in unmodified form, apart from
27 # these license statements. The same license still applies.
28
29
18
30 # TODO
19 # TODO
20 # - Tree-walking functions don't avoid symlink loops. Matt Harrison sent me a patch for this.
21 # - Tree-walking functions can't ignore errors. Matt Harrison asked for this.
22 #
23 # - Two people asked for path.chdir(). This just seems wrong to me,
24 # I dunno. chdir() is moderately evil anyway.
25 #
31 # - Bug in write_text(). It doesn't support Universal newline mode.
26 # - Bug in write_text(). It doesn't support Universal newline mode.
32 # - Better error message in listdir() when self isn't a
27 # - Better error message in listdir() when self isn't a
33 # directory. (On Windows, the error message really sucks.)
28 # directory. (On Windows, the error message really sucks.)
@@ -36,25 +31,42 b' Date: 7 Mar 2004'
36 # - guess_content_type() method?
31 # - guess_content_type() method?
37 # - Perhaps support arguments to touch().
32 # - Perhaps support arguments to touch().
38 # - Could add split() and join() methods that generate warnings.
33 # - Could add split() and join() methods that generate warnings.
39 # - Note: __add__() technically has a bug, I think, where
40 # it doesn't play nice with other types that implement
41 # __radd__(). Test this.
42
34
43 from __future__ import generators
35 from __future__ import generators
44
36
45 import sys, os, fnmatch, glob, shutil, codecs
37 import sys, warnings, os, fnmatch, glob, shutil, codecs, md5
46
38
47 __version__ = '2.0.4'
39 __version__ = '2.1'
48 __all__ = ['path']
40 __all__ = ['path']
49
41
42 # Platform-specific support for path.owner
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
50 # Pre-2.3 support. Are unicode filenames supported?
54 # Pre-2.3 support. Are unicode filenames supported?
51 _base = str
55 _base = str
56 _getcwd = os.getcwd
52 try:
57 try:
53 if os.path.supports_unicode_filenames:
58 if os.path.supports_unicode_filenames:
54 _base = unicode
59 _base = unicode
60 _getcwd = os.getcwdu
55 except AttributeError:
61 except AttributeError:
56 pass
62 pass
57
63
64 # Pre-2.3 workaround for booleans
65 try:
66 True, False
67 except NameError:
68 True, False = 1, 0
69
58 # Pre-2.3 workaround for basestring.
70 # Pre-2.3 workaround for basestring.
59 try:
71 try:
60 basestring
72 basestring
@@ -67,6 +79,9 b" if hasattr(file, 'newlines'):"
67 _textmode = 'U'
79 _textmode = 'U'
68
80
69
81
82 class TreeWalkWarning(Warning):
83 pass
84
70 class path(_base):
85 class path(_base):
71 """ Represents a filesystem path.
86 """ Represents a filesystem path.
72
87
@@ -81,10 +96,19 b' class path(_base):'
81
96
82 # Adding a path and a string yields a path.
97 # Adding a path and a string yields a path.
83 def __add__(self, more):
98 def __add__(self, more):
84 return path(_base(self) + more)
99 try:
100 resultStr = _base.__add__(self, more)
101 except TypeError: #Python bug
102 resultStr = NotImplemented
103 if resultStr is NotImplemented:
104 return resultStr
105 return self.__class__(resultStr)
85
106
86 def __radd__(self, other):
107 def __radd__(self, other):
87 return path(other + _base(self))
108 if isinstance(other, basestring):
109 return self.__class__(other.__add__(self))
110 else:
111 return NotImplemented
88
112
89 # The / operator joins paths.
113 # The / operator joins paths.
90 def __div__(self, rel):
114 def __div__(self, rel):
@@ -93,26 +117,27 b' class path(_base):'
93 Join two path components, adding a separator character if
117 Join two path components, adding a separator character if
94 needed.
118 needed.
95 """
119 """
96 return path(os.path.join(self, rel))
120 return self.__class__(os.path.join(self, rel))
97
121
98 # Make the / operator work even when true division is enabled.
122 # Make the / operator work even when true division is enabled.
99 __truediv__ = __div__
123 __truediv__ = __div__
100
124
101 def getcwd():
125 def getcwd(cls):
102 """ Return the current working directory as a path object. """
126 """ Return the current working directory as a path object. """
103 return path(os.getcwd())
127 return cls(_getcwd())
104 getcwd = staticmethod(getcwd)
128 getcwd = classmethod(getcwd)
105
129
106
130
107 # --- Operations on path strings.
131 # --- Operations on path strings.
108
132
109 def abspath(self): return path(os.path.abspath(self))
133 isabs = os.path.isabs
110 def normcase(self): return path(os.path.normcase(self))
134 def abspath(self): return self.__class__(os.path.abspath(self))
111 def normpath(self): return path(os.path.normpath(self))
135 def normcase(self): return self.__class__(os.path.normcase(self))
112 def realpath(self): return path(os.path.realpath(self))
136 def normpath(self): return self.__class__(os.path.normpath(self))
113 def expanduser(self): return path(os.path.expanduser(self))
137 def realpath(self): return self.__class__(os.path.realpath(self))
114 def expandvars(self): return path(os.path.expandvars(self))
138 def expanduser(self): return self.__class__(os.path.expanduser(self))
115 def dirname(self): return path(os.path.dirname(self))
139 def expandvars(self): return self.__class__(os.path.expandvars(self))
140 def dirname(self): return self.__class__(os.path.dirname(self))
116 basename = os.path.basename
141 basename = os.path.basename
117
142
118 def expand(self):
143 def expand(self):
@@ -134,7 +159,7 b' class path(_base):'
134
159
135 def _get_drive(self):
160 def _get_drive(self):
136 drive, r = os.path.splitdrive(self)
161 drive, r = os.path.splitdrive(self)
137 return path(drive)
162 return self.__class__(drive)
138
163
139 parent = property(
164 parent = property(
140 dirname, None, None,
165 dirname, None, None,
@@ -171,7 +196,7 b' class path(_base):'
171 def splitpath(self):
196 def splitpath(self):
172 """ p.splitpath() -> Return (p.parent, p.name). """
197 """ p.splitpath() -> Return (p.parent, p.name). """
173 parent, child = os.path.split(self)
198 parent, child = os.path.split(self)
174 return path(parent), child
199 return self.__class__(parent), child
175
200
176 def splitdrive(self):
201 def splitdrive(self):
177 """ p.splitdrive() -> Return (p.drive, <the rest of p>).
202 """ p.splitdrive() -> Return (p.drive, <the rest of p>).
@@ -181,7 +206,7 b' class path(_base):'
181 is simply (path(''), p). This is always the case on Unix.
206 is simply (path(''), p). This is always the case on Unix.
182 """
207 """
183 drive, rel = os.path.splitdrive(self)
208 drive, rel = os.path.splitdrive(self)
184 return path(drive), rel
209 return self.__class__(drive), rel
185
210
186 def splitext(self):
211 def splitext(self):
187 """ p.splitext() -> Return (p.stripext(), p.ext).
212 """ p.splitext() -> Return (p.stripext(), p.ext).
@@ -194,7 +219,7 b' class path(_base):'
194 (a, b) == p.splitext(), then a + b == p.
219 (a, b) == p.splitext(), then a + b == p.
195 """
220 """
196 filename, ext = os.path.splitext(self)
221 filename, ext = os.path.splitext(self)
197 return path(filename), ext
222 return self.__class__(filename), ext
198
223
199 def stripext(self):
224 def stripext(self):
200 """ p.stripext() -> Remove one file extension from the path.
225 """ p.stripext() -> Remove one file extension from the path.
@@ -207,11 +232,11 b' class path(_base):'
207 if hasattr(os.path, 'splitunc'):
232 if hasattr(os.path, 'splitunc'):
208 def splitunc(self):
233 def splitunc(self):
209 unc, rest = os.path.splitunc(self)
234 unc, rest = os.path.splitunc(self)
210 return path(unc), rest
235 return self.__class__(unc), rest
211
236
212 def _get_uncshare(self):
237 def _get_uncshare(self):
213 unc, r = os.path.splitunc(self)
238 unc, r = os.path.splitunc(self)
214 return path(unc)
239 return self.__class__(unc)
215
240
216 uncshare = property(
241 uncshare = property(
217 _get_uncshare, None, None,
242 _get_uncshare, None, None,
@@ -223,10 +248,10 b' class path(_base):'
223 character (os.sep) if needed. Returns a new path
248 character (os.sep) if needed. Returns a new path
224 object.
249 object.
225 """
250 """
226 return path(os.path.join(self, *args))
251 return self.__class__(os.path.join(self, *args))
227
252
228 def splitall(self):
253 def splitall(self):
229 """ Return a list of the path components in this path.
254 r""" Return a list of the path components in this path.
230
255
231 The first item in the list will be a path. Its value will be
256 The first item in the list will be a path. Its value will be
232 either os.curdir, os.pardir, empty, or the root directory of
257 either os.curdir, os.pardir, empty, or the root directory of
@@ -251,7 +276,7 b' class path(_base):'
251 """ Return this path as a relative path,
276 """ Return this path as a relative path,
252 based from the current working directory.
277 based from the current working directory.
253 """
278 """
254 cwd = path(os.getcwd())
279 cwd = self.__class__(os.getcwd())
255 return cwd.relpathto(self)
280 return cwd.relpathto(self)
256
281
257 def relpathto(self, dest):
282 def relpathto(self, dest):
@@ -262,7 +287,7 b' class path(_base):'
262 dest.abspath().
287 dest.abspath().
263 """
288 """
264 origin = self.abspath()
289 origin = self.abspath()
265 dest = path(dest).abspath()
290 dest = self.__class__(dest).abspath()
266
291
267 orig_list = origin.normcase().splitall()
292 orig_list = origin.normcase().splitall()
268 # Don't normcase dest! We want to preserve the case.
293 # Don't normcase dest! We want to preserve the case.
@@ -287,10 +312,10 b' class path(_base):'
287 segments += dest_list[i:]
312 segments += dest_list[i:]
288 if len(segments) == 0:
313 if len(segments) == 0:
289 # If they happen to be identical, use os.curdir.
314 # If they happen to be identical, use os.curdir.
290 return path(os.curdir)
315 relpath = os.curdir
291 else:
316 else:
292 return path(os.path.join(*segments))
317 relpath = os.path.join(*segments)
293
318 return self.__class__(relpath)
294
319
295 # --- Listing, searching, walking, and matching
320 # --- Listing, searching, walking, and matching
296
321
@@ -336,7 +361,7 b' class path(_base):'
336
361
337 return [p for p in self.listdir(pattern) if p.isfile()]
362 return [p for p in self.listdir(pattern) if p.isfile()]
338
363
339 def walk(self, pattern=None):
364 def walk(self, pattern=None, errors='strict'):
340 """ D.walk() -> iterator over files and subdirs, recursively.
365 """ D.walk() -> iterator over files and subdirs, recursively.
341
366
342 The iterator yields path objects naming each child item of
367 The iterator yields path objects naming each child item of
@@ -345,29 +370,85 b' class path(_base):'
345
370
346 This performs a depth-first traversal of the directory tree.
371 This performs a depth-first traversal of the directory tree.
347 Each directory is returned just before all its children.
372 Each directory is returned just before all its children.
373
374 The errors= keyword argument controls behavior when an
375 error occurs. The default is 'strict', which causes an
376 exception. The other allowed values are 'warn', which
377 reports the error via warnings.warn(), and 'ignore'.
348 """
378 """
349 for child in self.listdir():
379 if errors not in ('strict', 'warn', 'ignore'):
380 raise ValueError("invalid errors parameter")
381
382 try:
383 childList = self.listdir()
384 except Exception:
385 if errors == 'ignore':
386 return
387 elif errors == 'warn':
388 warnings.warn(
389 "Unable to list directory '%s': %s"
390 % (self, sys.exc_info()[1]),
391 TreeWalkWarning)
392 else:
393 raise
394
395 for child in childList:
350 if pattern is None or child.fnmatch(pattern):
396 if pattern is None or child.fnmatch(pattern):
351 yield child
397 yield child
352 if child.isdir():
398 try:
353 for item in child.walk(pattern):
399 isdir = child.isdir()
400 except Exception:
401 if errors == 'ignore':
402 isdir = False
403 elif errors == 'warn':
404 warnings.warn(
405 "Unable to access '%s': %s"
406 % (child, sys.exc_info()[1]),
407 TreeWalkWarning)
408 isdir = False
409 else:
410 raise
411
412 if isdir:
413 for item in child.walk(pattern, errors):
354 yield item
414 yield item
355
415
356 def walkdirs(self, pattern=None):
416 def walkdirs(self, pattern=None, errors='strict'):
357 """ D.walkdirs() -> iterator over subdirs, recursively.
417 """ D.walkdirs() -> iterator over subdirs, recursively.
358
418
359 With the optional 'pattern' argument, this yields only
419 With the optional 'pattern' argument, this yields only
360 directories whose names match the given pattern. For
420 directories whose names match the given pattern. For
361 example, mydir.walkdirs('*test') yields only directories
421 example, mydir.walkdirs('*test') yields only directories
362 with names ending in 'test'.
422 with names ending in 'test'.
423
424 The errors= keyword argument controls behavior when an
425 error occurs. The default is 'strict', which causes an
426 exception. The other allowed values are 'warn', which
427 reports the error via warnings.warn(), and 'ignore'.
363 """
428 """
364 for child in self.dirs():
429 if errors not in ('strict', 'warn', 'ignore'):
430 raise ValueError("invalid errors parameter")
431
432 try:
433 dirs = self.dirs()
434 except Exception:
435 if errors == 'ignore':
436 return
437 elif errors == 'warn':
438 warnings.warn(
439 "Unable to list directory '%s': %s"
440 % (self, sys.exc_info()[1]),
441 TreeWalkWarning)
442 else:
443 raise
444
445 for child in dirs:
365 if pattern is None or child.fnmatch(pattern):
446 if pattern is None or child.fnmatch(pattern):
366 yield child
447 yield child
367 for subsubdir in child.walkdirs(pattern):
448 for subsubdir in child.walkdirs(pattern, errors):
368 yield subsubdir
449 yield subsubdir
369
450
370 def walkfiles(self, pattern=None):
451 def walkfiles(self, pattern=None, errors='strict'):
371 """ D.walkfiles() -> iterator over files in D, recursively.
452 """ D.walkfiles() -> iterator over files in D, recursively.
372
453
373 The optional argument, pattern, limits the results to files
454 The optional argument, pattern, limits the results to files
@@ -375,12 +456,42 b' class path(_base):'
375 mydir.walkfiles('*.tmp') yields only files with the .tmp
456 mydir.walkfiles('*.tmp') yields only files with the .tmp
376 extension.
457 extension.
377 """
458 """
378 for child in self.listdir():
459 if errors not in ('strict', 'warn', 'ignore'):
379 if child.isfile():
460 raise ValueError("invalid errors parameter")
461
462 try:
463 childList = self.listdir()
464 except Exception:
465 if errors == 'ignore':
466 return
467 elif errors == 'warn':
468 warnings.warn(
469 "Unable to list directory '%s': %s"
470 % (self, sys.exc_info()[1]),
471 TreeWalkWarning)
472 else:
473 raise
474
475 for child in childList:
476 try:
477 isfile = child.isfile()
478 isdir = not isfile and child.isdir()
479 except:
480 if errors == 'ignore':
481 return
482 elif errors == 'warn':
483 warnings.warn(
484 "Unable to access '%s': %s"
485 % (self, sys.exc_info()[1]),
486 TreeWalkWarning)
487 else:
488 raise
489
490 if isfile:
380 if pattern is None or child.fnmatch(pattern):
491 if pattern is None or child.fnmatch(pattern):
381 yield child
492 yield child
382 elif child.isdir():
493 elif isdir:
383 for f in child.walkfiles(pattern):
494 for f in child.walkfiles(pattern, errors):
384 yield f
495 yield f
385
496
386 def fnmatch(self, pattern):
497 def fnmatch(self, pattern):
@@ -399,7 +510,8 b' class path(_base):'
399 For example, path('/users').glob('*/bin/*') returns a list
510 For example, path('/users').glob('*/bin/*') returns a list
400 of all the files users have in their bin directories.
511 of all the files users have in their bin directories.
401 """
512 """
402 return map(path, glob.glob(_base(self / pattern)))
513 cls = self.__class__
514 return [cls(s) for s in glob.glob(_base(self / pattern))]
403
515
404
516
405 # --- Reading or writing an entire file at once.
517 # --- Reading or writing an entire file at once.
@@ -420,7 +532,7 b' class path(_base):'
420 """ Open this file and write the given bytes to it.
532 """ Open this file and write the given bytes to it.
421
533
422 Default behavior is to overwrite any existing file.
534 Default behavior is to overwrite any existing file.
423 Call this with write_bytes(bytes, append=True) to append instead.
535 Call p.write_bytes(bytes, append=True) to append instead.
424 """
536 """
425 if append:
537 if append:
426 mode = 'ab'
538 mode = 'ab'
@@ -433,7 +545,7 b' class path(_base):'
433 f.close()
545 f.close()
434
546
435 def text(self, encoding=None, errors='strict'):
547 def text(self, encoding=None, errors='strict'):
436 """ Open this file, read it in, return the content as a string.
548 r""" Open this file, read it in, return the content as a string.
437
549
438 This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r'
550 This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r'
439 are automatically translated to '\n'.
551 are automatically translated to '\n'.
@@ -470,7 +582,7 b' class path(_base):'
470 .replace(u'\u2028', u'\n'))
582 .replace(u'\u2028', u'\n'))
471
583
472 def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
584 def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
473 """ Write the given text to this file.
585 r""" Write the given text to this file.
474
586
475 The default behavior is to overwrite any existing file;
587 The default behavior is to overwrite any existing file;
476 to append instead, use the 'append=True' keyword argument.
588 to append instead, use the 'append=True' keyword argument.
@@ -559,7 +671,7 b' class path(_base):'
559 self.write_bytes(bytes, append)
671 self.write_bytes(bytes, append)
560
672
561 def lines(self, encoding=None, errors='strict', retain=True):
673 def lines(self, encoding=None, errors='strict', retain=True):
562 """ Open this file, read all lines, return them in a list.
674 r""" Open this file, read all lines, return them in a list.
563
675
564 Optional arguments:
676 Optional arguments:
565 encoding - The Unicode encoding (or character set) of
677 encoding - The Unicode encoding (or character set) of
@@ -586,7 +698,7 b' class path(_base):'
586
698
587 def write_lines(self, lines, encoding=None, errors='strict',
699 def write_lines(self, lines, encoding=None, errors='strict',
588 linesep=os.linesep, append=False):
700 linesep=os.linesep, append=False):
589 """ Write the given lines of text to this file.
701 r""" Write the given lines of text to this file.
590
702
591 By default this overwrites any existing file at this path.
703 By default this overwrites any existing file at this path.
592
704
@@ -649,11 +761,26 b' class path(_base):'
649 finally:
761 finally:
650 f.close()
762 f.close()
651
763
764 def read_md5(self):
765 """ Calculate the md5 hash for this file.
766
767 This reads through the entire file.
768 """
769 f = self.open('rb')
770 try:
771 m = md5.new()
772 while True:
773 d = f.read(8192)
774 if not d:
775 break
776 m.update(d)
777 finally:
778 f.close()
779 return m.digest()
652
780
653 # --- Methods for querying the filesystem.
781 # --- Methods for querying the filesystem.
654
782
655 exists = os.path.exists
783 exists = os.path.exists
656 isabs = os.path.isabs
657 isdir = os.path.isdir
784 isdir = os.path.isdir
658 isfile = os.path.isfile
785 isfile = os.path.isfile
659 islink = os.path.islink
786 islink = os.path.islink
@@ -699,6 +826,32 b' class path(_base):'
699 """ Like path.stat(), but do not follow symbolic links. """
826 """ Like path.stat(), but do not follow symbolic links. """
700 return os.lstat(self)
827 return os.lstat(self)
701
828
829 def get_owner(self):
830 r""" Return the name of the owner of this file or directory.
831
832 This follows symbolic links.
833
834 On Windows, this returns a name of the form ur'DOMAIN\User Name'.
835 On Windows, a group can own a file or directory.
836 """
837 if os.name == 'nt':
838 if win32security is None:
839 raise Exception("path.owner requires win32all to be installed")
840 desc = win32security.GetFileSecurity(
841 self, win32security.OWNER_SECURITY_INFORMATION)
842 sid = desc.GetSecurityDescriptorOwner()
843 account, domain, typecode = win32security.LookupAccountSid(None, sid)
844 return domain + u'\\' + account
845 else:
846 if pwd is None:
847 raise NotImplementedError("path.owner is not implemented on this platform.")
848 st = self.stat()
849 return pwd.getpwuid(st.st_uid).pw_name
850
851 owner = property(
852 get_owner, None, None,
853 """ Name of the owner of this file or directory. """)
854
702 if hasattr(os, 'statvfs'):
855 if hasattr(os, 'statvfs'):
703 def statvfs(self):
856 def statvfs(self):
704 """ Perform a statvfs() system call on this path. """
857 """ Perform a statvfs() system call on this path. """
@@ -779,7 +932,7 b' class path(_base):'
779
932
780 The result may be an absolute or a relative path.
933 The result may be an absolute or a relative path.
781 """
934 """
782 return path(os.readlink(self))
935 return self.__class__(os.readlink(self))
783
936
784 def readlinkabs(self):
937 def readlinkabs(self):
785 """ Return the path to which this symbolic link points.
938 """ Return the path to which this symbolic link points.
@@ -19,6 +19,10 b''
19 interesting (stored / manually defined) aliases last
19 interesting (stored / manually defined) aliases last
20 where they catch the eye w/o scrolling.
20 where they catch the eye w/o scrolling.
21
21
22 * Magic.py (%rehashx), ext_rehashdir.py: files with
23 'py' extension are always considered executable, even
24 when not in PATHEXT environment variable.
25
22 2006-10-12 Ville Vainio <vivainio@gmail.com>
26 2006-10-12 Ville Vainio <vivainio@gmail.com>
23
27
24 * jobctrl.py: Add new "jobctrl" extension for spawning background
28 * jobctrl.py: Add new "jobctrl" extension for spawning background
General Comments 0
You need to be logged in to leave comments. Login now