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