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