comparison utils/util_scripts/path.py @ 0:4a0efb7baf70

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