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