##// END OF EJS Templates
Update path.py to version 2.2.
walter.doerwald -
Show More

The requested changes are too big and content was truncated. Show full diff

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