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