Package mrv :: Module path
[hide private]
[frames] | no frames]

Source Code for Module mrv.path

   1  # -*- coding: utf-8 -*-
 
   2  """path.py - An object representing a path to a file or directory.
 
   3  
 
   4  Example:
 
   5          >>> from path import path
 
   6          >>> d = path('/home/guido/bin')
 
   7          >>> for f in d.files('*.py'):
 
   8          >>>             f.chmod(0755)
 
   9  
 
  10  This module requires Python 2.4 or later.
 
  11  
 
  12  TODO
 
  13  ----
 
  14     - Tree-walking functions don't avoid symlink loops.  Matt Harrison sent me a patch for this.
 
  15     - Tree-walking functions can't ignore errors.  Matt Harrison asked for this.
 
  16  
 
  17     - Two people asked for path.chdir().  This just seems wrong to me,
 
  18           I dunno.  chdir() is moderately evil anyway.
 
  19  
 
  20     - Bug in write_text().  It doesn't support Universal newline mode.
 
  21     - Better error message in listdir() when self isn't a
 
  22           directory. (On Windows, the error message really sucks.)
 
  23     - Make sure everything has a good docstringc.
 
  24     - Add methods for regex find and replace.
 
  25     - guess_content_type() method?
 
  26     - Could add split() and join() methods that generate warnings.
 
  27  """ 
  28  from __future__ import generators 
  29  __docformat__ = "restructuredtext" 
  30  
 
  31  __license__='Freeware' 
  32  
 
  33  import sys 
  34  import logging 
  35  import os 
  36  import fnmatch 
  37  import glob 
  38  import shutil 
  39  import codecs 
  40  import re 
  41  from interface import iDagItem 
  42  log = logging.getLogger("mrv.path") 
  43  
 
  44  __version__ = '3.0' 
  45  __all__ = ['Path', 'BasePath', 'make_path'] 
  46  
 
  47  # Platform-specific support for path.owner
 
  48  if os.name == 'nt': 
  49          try: 
  50                  import win32security 
  51          except ImportError: 
  52                  win32security = None 
  53  else: 
  54          try: 
  55                  import pwd 
  56          except ImportError: 
  57                  pwd = None 
  58  
 
  59  # Pre-2.3 support.      Are unicode filenames supported?
 
  60  _base = str 
  61  _getcwd = os.getcwd 
  62  try: 
  63          if os.path.supports_unicode_filenames: 
  64                  _base = unicode 
  65                  _getcwd = os.getcwdu 
  66  except AttributeError: 
  67          pass 
  68  
 
  69  # Pre-2.3 workaround for booleans
 
  70  try: 
  71          True, False 
  72  except NameError: 
  73          True, False = 1, 0 
  74  
 
  75  # Pre-2.3 workaround for basestring.
 
  76  try: 
  77          basestring 
  78  except NameError: 
  79          basestring = (str, unicode) 
  80  
 
  81  # Universal newline support
 
  82  _textmode = 'r' 
  83  if hasattr(file, 'newlines'): 
  84          _textmode = 'U' 
  85  
 
  86  
 
  87  # cache used for path expansion
 
  88  _varprog = re.compile(r'\$(\w+|\{[^}]*\})') 
89 90 -class TreeWalkWarning(Warning):
91 pass
92
93 94 -class Path( _base, iDagItem ):
95 """ Represents a filesystem path. 96 97 For documentation on individual methods, consult their 98 counterparts in os.path. 99 """ 100 # Configuration 101 sep = None 102 osep = None 103 104 #{ Special Python methods 105
106 - def __repr__(self):
107 return '%s(%s)' % ( self.__class__.__name__, _base.__repr__(self) )
108 109 # Adding a path and a string yields a path.
110 - def __add__(self, more):
111 try: 112 resultStr = _base.__add__(self, more) 113 except TypeError: #Python bug 114 resultStr = NotImplemented 115 if resultStr is NotImplemented: 116 return resultStr 117 return self.__class__(resultStr)
118
119 - def __radd__(self, other):
120 if isinstance(other, basestring): 121 return self.__class__(other.__add__(self)) 122 else: 123 return NotImplemented
124 125 # The / operator joins paths.
126 - def __div__(self, rel):
127 """ fp.__div__(rel) == fp / rel == fp.joinpath(rel) 128 129 Join two path components, adding a separator character if 130 needed. 131 """ 132 return self.__class__(os.path.join(self, rel))
133 134 # Make the / operator work even when true division is enabled. 135 __truediv__ = __div__ 136
137 - def __eq__( self, other ):
138 """Comparison method with expanded variables, just to assure 139 the comparison yields the results we would expect""" 140 return unicode(self._expandvars(self)) == unicode(self._expandvars(unicode(other)))
141
142 - def __ne__( self, other ):
143 return not self.__eq__( other )
144
145 - def __hash__( self ):
146 """Expanded hash method""" 147 return hash(unicode(self._expandvars(self)))
148 149 #} END Special Python methods 150 151 @classmethod
152 - def set_separator(cls, sep):
153 """Set this type to support the given separator as general path separator""" 154 if sep not in "/\\": 155 raise ValueError("Invalid path separator", sep) 156 cls.sep = sep 157 cls.osep = (sep == '/' and '\\') or "/" 158 159 # setup path conversion as necessary 160 global Path 161 if os.path.sep != cls.sep: 162 Path = ConversionPath 163 else: 164 Path = BasePath
165 # END handle Path type 166 167 @classmethod
168 - def getcwd(cls):
169 """:return: the current working directory as a path object. """ 170 return cls(_getcwd())
171 172 #{ iDagItem Implementation 173
174 - def parent( self ):
175 """:return: the parent directory of this Path or None if this is the root""" 176 parent = self.dirname() 177 if parent == self: 178 return None 179 return parent
180
181 - def children( self, predicate = lambda p: True, pattern = None ):
182 """:return: child paths as retrieved by queryiing the file system. 183 :note: files cannot have children, and willl return an empty array accordingly 184 :param predicate: return p if predicate( p ) returns True 185 :param pattern: list only elements that match the given simple pattern 186 i.e. *.*""" 187 try: 188 children = self.listdir( pattern ) 189 except OSError: 190 return list() 191 192 return [ c for c in children if predicate( c ) ]
193 194 #} END idagitem implementation 195 196 #{ Operations on path strings. 197 198 @classmethod
199 - def _expandvars(cls, path):
200 """Internal version returning a string only representing the non-recursively 201 expanded variable 202 203 :note: It is a slightly changed copy of the version in posixfile 204 as the windows version was implemented differently ( it expands 205 variables to an empty space which is undesireable )""" 206 if '$' not in path: 207 return path 208 209 i = 0 210 while True: 211 m = _varprog.search(path, i) 212 if not m: 213 break 214 i, j = m.span(0) 215 name = m.group(1) 216 if name.startswith('{') and name.endswith('}'): 217 name = name[1:-1] 218 if name in os.environ: 219 tail = path[j:] 220 path = path[:i] + os.environ[name] 221 i = len(path) 222 path += tail 223 else: 224 i = j 225 # END handle variable exists in environ 226 # END loop forever 227 return path
228 229 @classmethod
230 - def _expandvars_deep(cls, path):
231 """As above, but recursively expands as many variables as possible""" 232 rval = cls._expandvars(path) 233 while str(rval) != str(path): 234 path = rval 235 rval = cls._expandvars(path) 236 # END expansion loop 237 return rval
238 239 isabs = os.path.isabs
240 - def abspath(self): return self.__class__(os.path.abspath(self._expandvars(self)))
241 - def normcase(self): return self.__class__(os.path.normcase(self))
242 - def normpath(self): return self.__class__(os.path.normpath(self))
243 - def realpath(self): return self.__class__(os.path.realpath(self._expandvars(self)))
244 - def expanduser(self): return self.__class__(os.path.expanduser(self))
245 - def expandvars(self): return self.__class__(self._expandvars(self))
246 - def dirname(self): return self.__class__(os.path.dirname(self))
247 basename = os.path.basename 248
249 - def expandvars_deep(self):
250 """Expands all environment variables recursively""" 251 return type(self)(self._expandvars_deep(self))
252
253 - def expandvars_deep_or_raise(self):
254 """Expands all environment variables recursively, and raises ValueError 255 if the path still contains variables afterwards""" 256 rval = self.expandvars_deep() 257 if rval.containsvars(): 258 raise ValueError("Failed to expand all environment variables in %r, got %r" % (self, rval)) 259 return rval
260
261 - def expand(self):
262 """ Clean up a filename by calling expandvars() and expanduser() 263 264 This is commonly everything needed to clean up a filename 265 read from a configuration file, for example. 266 267 If you are not interested in trailing slashes, you should call 268 normpath() on the resulting Path as well. 269 """ 270 return self.expandvars().expanduser()
271
272 - def containsvars( self ):
273 """:return: True if this path contains environment variables""" 274 return self.find( '$' ) != -1
275
276 - def expand_or_raise(self):
277 """:return: Copy of self with all variables expanded ( using `expand` ) 278 non-recursively ! 279 280 :raise ValueError: If we could not expand all environment variables as 281 their values where missing in the environment""" 282 rval = self.expand() 283 if str(rval) == str(self) and rval.containsvars(): 284 raise ValueError("Failed to expand all environment variables in %r, got %r" % (self, rval)) 285 return rval
286
287 - def namebase(self):
288 """The same as path.basename(), but with one file extension stripped off. 289 290 For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz', 291 but path('/home/guido/python.tar.gz').namebase == 'python.tar'""" 292 base, ext = os.path.splitext(self.basename()) 293 return base
294
295 - def ext(self):
296 """ The file extension, for example '.py'. """ 297 f, ext = os.path.splitext(_base(self)) 298 return ext
299
300 - def drive(self):
301 """ The drive specifier, for example 'C:'. 302 This is always empty on systems that don't use drive specifiers. 303 """ 304 drive, r = os.path.splitdrive(self) 305 return self.__class__(drive)
306
307 - def splitpath(self):
308 """ p.splitpath() -> Return (p.parent(), p.basename()). """ 309 parent, child = os.path.split(self) 310 return self.__class__(parent), child
311
312 - def splitdrive(self):
313 """ p.splitdrive() -> Return (p.drive, <the rest of p>). 314 315 Split the drive specifier from this path. If there is 316 no drive specifier, p.drive is empty, so the return value 317 is simply (path(''), p). This is always the case on Unix. 318 """ 319 drive, rel = os.path.splitdrive(self) 320 return self.__class__(drive), rel
321
322 - def splitext(self):
323 """ p.splitext() -> Return (p.stripext(), p.ext). 324 325 Split the filename extension from this path and return 326 the two parts. Either part may be empty. 327 328 The extension is everything from '.' to the end of the 329 last path segment. This has the property that if 330 (a, b) == p.splitext(), then a + b == p. 331 """ 332 filename, ext = os.path.splitext(self) 333 return self.__class__(filename), ext
334
335 - def stripext(self):
336 """ p.stripext() -> Remove one file extension from the path. 337 338 For example, path('/home/guido/python.tar.gz').stripext() 339 returns path('/home/guido/python.tar'). 340 """ 341 return self.splitext()[0]
342 343 if hasattr(os.path, 'splitunc'):
344 - def splitunc(self):
345 unc, rest = os.path.splitunc(self) 346 return self.__class__(unc), rest
347
348 - def isunshared(self):
349 unc, r = os.path.splitunc(self) 350 return self.__class__(unc)
351
352 - def joinpath(self, *args):
353 """ Join two or more path components, adding a separator 354 character (os.sep) if needed. Returns a new path 355 object.""" 356 return self.__class__(os.path.join(self, *args))
357
358 - def splitall(self):
359 """ Return a list of the path components in this path. 360 361 The first item in the list will be a path. Its value will be 362 either os.curdir, os.pardir, empty, or the root directory of 363 this path (for example, '/' or 'C:\\'). The other items in 364 the list will be strings. 365 366 path.path.joinpath(\*result) can possibly yield the original path, depending 367 on the input.""" 368 parts = list() 369 loc = self 370 while loc != os.curdir and loc != os.pardir: 371 prev = loc 372 loc, child = prev.splitpath() 373 if loc == prev: 374 break 375 parts.append(child) 376 parts.append(loc) 377 parts.reverse() 378 return parts
379
380 - def relpath(self):
381 """ Return this path as a relative path, 382 originating from the current working directory. 383 """ 384 return self.relpathto(os.getcwd())
385
386 - def relpathto(self, dest):
387 """ Return a relative path from self to dest. 388 389 If there is no relative path from self to dest, for example if 390 they reside on different drives in Windows, then this returns 391 dest.abspath(). 392 """ 393 # on windows, abspath returns \\ paths even if / paths are given. 394 # On linux this is not the case ... thanks 395 splitter = (os.name == 'nt' and _ossep) or self.sep 396 def commonprefix(m): 397 if not m: return '' 398 s1 = min(m) 399 s2 = max(m) 400 for i, c in enumerate(s1): 401 if c != s2[i]: 402 return s1[:i] 403 return s1
404 # END common prefix 405 406 start_list = os.path.abspath(dest).split(splitter) 407 path_list = os.path.abspath(self._expandvars(self)).split(splitter) 408 409 # Work out how much of the filepath is shared by start and path. 410 i = len(commonprefix([start_list, path_list])) 411 412 rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] 413 if not rel_list: 414 return os.curdir 415 return self.__class__(os.path.join(*rel_list))
416
417 - def relpathfrom(self, dest):
418 """ Return a relative path from dest to self""" 419 return self.__class__(dest).relpathto(self)
420
421 - def convert_separators(self):
422 """:return: Version of self with all separators set to be 'sep'. The difference 423 to normpath is that it does not cut trailing separators""" 424 return self.__class__(self.replace(self.osep, self.sep))
425
426 - def tolinuxpath(self):
427 """:return: A path using only slashes as path separator""" 428 return self.__class__(self.replace("\\", "/"))
429
430 - def tonative( self ):
431 r"""Convert the path separator to the type required by the current operating 432 system - on windows / becomes \ and on linux \ becomes / 433 434 :return: native version of self""" 435 s = "\\" 436 d = "/" 437 if sys.platform.startswith( "win" ): 438 s = "/" 439 d = "\\" 440 return Path( self.replace( s, d ) )
441 442 #} END Operations on path strings 443 444 #{ Listing, searching, walking, and matching 445
446 - def listdir(self, pattern=None):
447 """return list of items in this directory. 448 449 Use D.files() or D.dirs() instead if you want a listing 450 of just files or just subdirectories. 451 452 The elements of the list are path objects. 453 454 With the optional 'pattern' argument, this only lists 455 items whose names match the given pattern. 456 """ 457 names = os.listdir(self._expandvars(self)) 458 if pattern is not None: 459 names = fnmatch.filter(names, pattern) 460 return [self / child for child in names]
461
462 - def dirs(self, pattern=None):
463 """ D.dirs() -> List of this directory's subdirectories. 464 465 The elements of the list are path objects. 466 This does not walk recursively into subdirectories 467 (but see path.walkdirs). 468 469 With the optional ``pattern`` argument, this only lists 470 directories whose names match the given pattern. For 471 example, d.dirs("build-\*"). 472 """ 473 return [p for p in self.listdir(pattern) if p.isdir()]
474
475 - def files(self, pattern=None):
476 """ D.files() -> List of the files in this directory. 477 478 The elements of the list are path objects. 479 This does not walk into subdirectories (see path.walkfiles). 480 481 With the optional ``pattern`` argument, this only lists files 482 whose names match the given pattern. For example, 483 d.files("\*.pyc"). 484 """ 485 486 return [p for p in self.listdir(pattern) if p.isfile()]
487
488 - def walk(self, pattern=None, errors='strict', predicate=lambda p: True):
489 """create iterator over files and subdirs, recursively. 490 491 The iterator yields path objects naming each child item of 492 this directory and its descendants. 493 494 It performs a depth-first traversal of the directory tree. 495 Each directory is returned just before all its children. 496 497 :param pattern: fnmatch compatible pattern or None 498 :param errors: controls behavior when an 499 error occurs. The default is 'strict', which causes an 500 exception. The other allowed values are 'warn', which 501 reports the error via log.warn(), and 'ignore'. 502 :param predicate: returns True for each Path p to be yielded by iterator 503 """ 504 if errors not in ('strict', 'warn', 'ignore'): 505 raise ValueError("invalid errors parameter") 506 507 try: 508 childList = self.listdir() 509 except Exception: 510 if errors == 'ignore': 511 return 512 elif errors == 'warn': 513 log.warn( 514 "Unable to list directory '%s': %s" 515 % (self, sys.exc_info()[1])) 516 childList = list() 517 else: 518 raise 519 # END handle errors value 520 # END listdir exception handling 521 522 for child in childList: 523 if ( pattern is None or child.fnmatch(pattern) ) and predicate(child): 524 yield child 525 526 try: 527 isdir = child.isdir() 528 except Exception: 529 isdir = False 530 if errors == 'ignore': 531 pass 532 elif errors == 'warn': 533 log.warn( 534 "Unable to access '%s': %s" 535 % (child, sys.exc_info()[1])) 536 else: 537 raise 538 # END handle errors value 539 # END directory access exception handling 540 541 if not isdir: 542 continue 543 544 for item in child.walk(pattern, errors, predicate): 545 yield item
546 # END for each item 547 # END for each child in childlist 548
549 - def walkdirs(self, pattern=None, errors='strict', predicate=lambda p: True):
550 """ D.walkdirs() -> iterator over subdirs, recursively. 551 see `walk` for a parameter description """ 552 pred = lambda p: p.isdir() and predicate(p) 553 return self.walk(pattern, errors, pred)
554
555 - def walkfiles(self, pattern=None, errors='strict', predicate=lambda p: True):
556 """ D.walkfiles() -> iterator over files in D, recursively. 557 see `walk` for a parameter description""" 558 pred = lambda p: p.isfile() and predicate(p) 559 return self.walk(pattern, errors, pred)
560
561 - def fnmatch(self, pattern):
562 """ Return True if self.basename() matches the given pattern. 563 564 pattern - A filename pattern with wildcards, 565 for example "\*.py". 566 """ 567 pathexpanded = self.expandvars() 568 return fnmatch.fnmatch(pathexpanded.basename(), pattern)
569
570 - def glob(self, pattern):
571 """ Return a list of path objects that match the pattern. 572 573 pattern - a path relative to this directory, with wildcards. 574 575 For example, path('/users').glob('*/bin/*') returns a list 576 of all the files users have in their bin directories. 577 """ 578 cls = self.__class__ 579 pathexpanded = self.expandvars() 580 return [cls(s) for s in glob.glob(_base(pathexpanded / pattern))]
581 582 #} END Listing, searching, walking and watching 583 584 585 #{ Reading or writing an entire file at once 586
587 - def open(self, *args, **kwargs):
588 """ Open this file. Return a file object. """ 589 return open(self._expandvars(self), *args, **kwargs)
590
591 - def bytes(self):
592 """ Open this file, read all bytes, return them as a string. """ 593 f = self.open('rb') 594 try: 595 return f.read() 596 finally: 597 f.close()
598
599 - def write_bytes(self, bytes, append=False):
600 """ Open this file and write the given bytes to it. 601 602 Default behavior is to overwrite any existing file. 603 Call p.write_bytes(bytes, append=True) to append instead. 604 :return: self 605 """ 606 if append: 607 mode = 'ab' 608 else: 609 mode = 'wb' 610 f = self.open(mode) 611 try: 612 f.write(bytes) 613 finally: 614 f.close() 615 616 return self
617
618 - def text(self, encoding=None, errors='strict'):
619 r""" Open this file, read it in, return the content as a string. 620 621 This uses "U" mode in Python 2.3 and later, so "\r\n" and "\r" 622 are automatically translated to '\n'. 623 624 Optional arguments: 625 * encoding - The Unicode encoding (or character set) of 626 the file. If present, the content of the file is 627 decoded and returned as a unicode object; otherwise 628 it is returned as an 8-bit str. 629 * errors - How to handle Unicode errors; see help(str.decode) 630 for the options. Default is 'strict'. 631 """ 632 mode = 'U' # we are in python 2.4 at least 633 634 f = None 635 if encoding is None: 636 f = self.open(mode) 637 else: 638 f = codecs.open(self, 'r', encoding, errors) 639 # END handle encoding 640 641 try: 642 return f.read() 643 finally: 644 f.close()
645 # END handle file read 646
647 - def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
648 r""" Write the given text to this file. 649 650 The default behavior is to overwrite any existing file; 651 to append instead, use the 'append=True' keyword argument. 652 653 There are two differences between path.write_text() and 654 path.write_bytes(): newline handling and Unicode handling. 655 See below. 656 657 **Parameters**: 658 - text - str/unicode - The text to be written. 659 660 - encoding - str - The Unicode encoding that will be used. 661 This is ignored if 'text' isn't a Unicode string. 662 663 - errors - str - How to handle Unicode encoding errors. 664 Default is 'strict'. See help(unicode.encode) for the 665 options. This is ignored if 'text' isn't a Unicode 666 string. 667 668 - linesep - keyword argument - str/unicode - The sequence of 669 characters to be used to mark end-of-line. The default is 670 os.linesep. You can also specify None; this means to 671 leave all newlines as they are in 'text'. 672 673 - append - keyword argument - bool - Specifies what to do if 674 the file already exists (True: append to the end of it; 675 False: overwrite it.) The default is False. 676 677 678 **Newline handling**: 679 - write_text() converts all standard end-of-line sequences 680 ("\n", "\r", and "\r\n") to your platforms default end-of-line 681 sequence (see os.linesep; on Windows, for example, the 682 end-of-line marker is "\r\n"). 683 684 - If you don't like your platform's default, you can override it 685 using the "linesep=" keyword argument. If you specifically want 686 write_text() to preserve the newlines as-is, use "linesep=None". 687 688 - This applies to Unicode text the same as to 8-bit text, except 689 there are additional standard Unicode end-of-line sequences, check 690 the code to see them. 691 692 - (This is slightly different from when you open a file for 693 writing with fopen(filename, "w") in C or file(filename, "w") 694 in Python.) 695 696 697 **Unicode**: 698 If "text" isn't Unicode, then apart from newline handling, the 699 bytes are written verbatim to the file. The "encoding" and 700 'errors' arguments are not used and must be omitted. 701 702 If 'text' is Unicode, it is first converted to bytes using the 703 specified 'encoding' (or the default encoding if 'encoding' 704 isn't specified). The 'errors' argument applies only to this 705 conversion. 706 707 :return: self 708 """ 709 bytes = "" 710 if isinstance(text, unicode): 711 if linesep is not None: 712 # Convert all standard end-of-line sequences to 713 # ordinary newline characters. 714 text = (text.replace(u'\r\n', u'\n') 715 .replace(u'\r\x85', u'\n') 716 .replace(u'\r', u'\n') 717 .replace(u'\x85', u'\n') 718 .replace(u'\u2028', u'\n')) 719 text = text.replace(u'\n', linesep) 720 if encoding is None: 721 encoding = sys.getdefaultencoding() 722 bytes = text.encode(encoding, errors) 723 else: 724 # It is an error to specify an encoding if 'text' is 725 # an 8-bit string. 726 assert encoding is None 727 728 if linesep is not None: 729 text = (text.replace('\r\n', '\n') 730 .replace('\r', '\n')) 731 bytes = text.replace('\n', linesep) 732 733 self.write_bytes(bytes, append) 734 return self
735
736 - def write_lines(self, lines, encoding=None, errors='strict', 737 linesep=os.linesep, append=False):
738 r""" Write the given lines of text to this file. 739 740 By default this overwrites any existing file at this path. 741 742 This puts a platform-specific newline sequence on every line. 743 See 'linesep' below. 744 745 lines - A list of strings. 746 747 encoding - A Unicode encoding to use. This applies only if 748 'lines' contains any Unicode strings. 749 750 errors - How to handle errors in Unicode encoding. This 751 also applies only to Unicode strings. 752 753 linesep - The desired line-ending. This line-ending is 754 applied to every line. If a line already has any 755 standard line ending, that will be stripped off and 756 this will be used instead. The default is os.linesep, 757 which is platform-dependent ('\r\n' on Windows, '\n' on 758 Unix, etc.) Specify None to write the lines as-is, 759 like file.writelines(). 760 761 Use the keyword argument append=True to append lines to the 762 file. The default is to overwrite the file. Warning: 763 When you use this with Unicode data, if the encoding of the 764 existing data in the file is different from the encoding 765 you specify with the encoding= parameter, the result is 766 mixed-encoding data, which can really confuse someone trying 767 to read the file later. 768 769 :return: self 770 """ 771 if append: 772 mode = 'ab' 773 else: 774 mode = 'wb' 775 f = self.open(mode) 776 try: 777 for line in lines: 778 isUnicode = isinstance(line, unicode) 779 if linesep is not None: 780 # Strip off any existing line-end and add the 781 # specified linesep string. 782 if isUnicode: 783 if line[-2:] in (u'\r\n', u'\x0d\x85'): 784 line = line[:-2] 785 elif line[-1:] in (u'\r', u'\n', 786 u'\x85', u'\u2028'): 787 line = line[:-1] 788 else: 789 if line[-2:] == '\r\n': 790 line = line[:-2] 791 elif line[-1:] in ('\r', '\n'): 792 line = line[:-1] 793 line += linesep 794 if isUnicode: 795 if encoding is None: 796 encoding = sys.getdefaultencoding() 797 line = line.encode(encoding, errors) 798 f.write(line) 799 finally: 800 f.close() 801 802 return self
803
804 - def lines(self, encoding=None, errors='strict', retain=True):
805 r""" Open this file, read all lines, return them in a list. 806 807 Optional arguments: 808 * encoding: The Unicode encoding (or character set) of 809 the file. The default is None, meaning the content 810 of the file is read as 8-bit characters and returned 811 as a list of (non-Unicode) str objects. 812 813 * errors: How to handle Unicode errors; see help(str.decode) 814 for the options. Default is 'strict' 815 816 * retain: If true, retain newline characters; but all newline 817 character combinations ("\r", "\n", "\r\n") are 818 translated to "\n". If false, newline characters are 819 stripped off. Default is True. 820 821 This uses "U" mode in Python 2.3 and later. 822 """ 823 if encoding is None and retain: 824 f = self.open(_textmode) 825 try: 826 return f.readlines() 827 finally: 828 f.close() 829 else: 830 return self.text(encoding, errors).splitlines(retain)
831
832 - def digest(self, hashobject):
833 """ Calculate the hash for this file using the given hashobject. It must 834 support the 'update' and 'digest' methods. 835 836 :note: This reads through the entire file. 837 """ 838 839 f = self.open('rb') 840 try: 841 while True: 842 d = f.read(8192) 843 if not d: 844 break 845 hashobject.update(d) 846 finally: 847 f.close() 848 # END assure file gets closed 849 850 return hashobject.digest()
851 852 #} END Reading or writing an enitre file at once 853 854 #{ Methods for querying the filesystem 855 856 exists = lambda self: os.path.exists( self._expandvars(self) ) 857 if hasattr(os.path, 'lexists'): 858 lexists = lambda self: os.path.lexists( self._expandvars(self) ) 859 isdir = lambda self: os.path.isdir( self._expandvars(self) ) 860 isfile = lambda self: os.path.isfile( self._expandvars(self) ) 861 islink = lambda self: os.path.islink( self._expandvars(self) ) 862 ismount = lambda self: os.path.ismount( self._expandvars(self) ) 863 864 if hasattr(os.path, 'samefile'): 865 samefile = lambda self, other: os.path.samefile( self._expandvars(self), other ) 866 867 atime = lambda self: os.path.getatime( self._expandvars(self) ) 868 mtime = lambda self: os.path.getmtime( self._expandvars(self) ) 869 if hasattr(os.path, 'getctime'): 870 ctime = lambda self: os.path.getctime( self._expandvars(self) ) 871 size = lambda self: os.path.getsize( self._expandvars(self) ) 872 873 if hasattr(os, 'access'):
874 - def access(self, mode):
875 """ Return true if current user has access to this path. 876 877 mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK 878 """ 879 return os.access(self._expandvars(self), mode)
880
881 - def stat(self):
882 """ Perform a stat() system call on this path. """ 883 return os.stat(self._expandvars(self))
884
885 - def lstat(self):
886 """ Like path.stat(), but do not follow symbolic links. """ 887 return os.lstat(self._expandvars(self))
888
889 - def owner(self):
890 """ Return the name of the owner of this file or directory. 891 892 This follows symbolic links. 893 894 On Windows, this returns a name of the form ur'DOMAIN\User Name'. 895 On Windows, a group can own a file or directory. 896 """ 897 if os.name == 'nt': 898 if win32security is None: 899 raise Exception("path.owner requires win32all to be installed") 900 desc = win32security.GetFileSecurity( 901 self, win32security.OWNER_SECURITY_INFORMATION) 902 sid = desc.GetSecurityDescriptorOwner() 903 account, domain, typecode = win32security.LookupAccountSid(None, sid) 904 return domain + u'\\' + account 905 else: 906 if pwd is None: 907 raise NotImplementedError("path.owner is not implemented on this platform.") 908 st = self.stat() 909 return pwd.getpwuid(st.st_uid).pw_name
910 911 if hasattr(os, 'statvfs'):
912 - def statvfs(self):
913 """ Perform a statvfs() system call on this path. """ 914 return os.statvfs(self._expandvars(self))
915 916 if hasattr(os, 'pathconf'):
917 - def pathconf(self, name):
918 """see os.pathconf""" 919 return os.pathconf(self._expandvars(self), name)
920
921 - def isWritable( self ):
922 """:return: true if the file can be written to""" 923 if not self.exists(): 924 return False # assure we do not create anything not already there 925 926 try: 927 fileobj = self.open( 'a' ) 928 except: 929 return False 930 else: 931 fileobj.close() 932 return True
933 # END handle file open 934 935 936 #} END Methods for querying the filesystem 937 938 #{ Modifying operations on files and directories 939
940 - def setutime(self, times):
941 """ Set the access and modified times of this file. 942 943 :return: self""" 944 os.utime(self._expandvars(self), times) 945 return self
946
947 - def chmod(self, mode):
948 """Change file mode 949 950 :return: self""" 951 os.chmod(self._expandvars(self), mode) 952 return self
953 954 if hasattr(os, 'chown'):
955 - def chown(self, uid, gid):
956 """Change file ownership 957 958 :return: self""" 959 os.chown(self._expandvars(self), uid, gid) 960 return self
961
962 - def rename(self, new):
963 """os.rename 964 965 :return: Path to new file""" 966 os.rename(self._expandvars(self), new) 967 return type(self)(new)
968
969 - def renames(self, new):
970 """os.renames, super rename 971 972 :return: Path to new file""" 973 os.renames(self._expandvars(self), new) 974 return type(self)(new)
975 976 #} END Modifying operations on files and directories 977 978 #{ Create/delete operations on directories 979
980 - def mkdir(self, mode=0777):
981 """Make this directory, fail if it already exists 982 983 :return: self""" 984 os.mkdir(self._expandvars(self), mode) 985 return self
986
987 - def makedirs(self, mode=0777):
988 """Smarter makedir, see os.makedirs 989 990 :return: self""" 991 os.makedirs(self._expandvars(self), mode) 992 return self
993
994 - def rmdir(self):
995 """Remove this empty directory 996 997 :return: self""" 998 os.rmdir(self._expandvars(self)) 999 return self
1000
1001 - def removedirs(self):
1002 """see os.removedirs 1003 1004 :return: self""" 1005 os.removedirs(self._expandvars(self)) 1006 return self
1007 1008 #} END Create/delete operations on directories 1009 1010 #{ Modifying operations on files 1011
1012 - def touch(self, flags = os.O_WRONLY | os.O_CREAT, mode = 0666):
1013 """ Set the access/modified times of this file to the current time. 1014 Create the file if it does not exist. 1015 1016 :return: self 1017 """ 1018 fd = os.open(self._expandvars(self), flags, mode) 1019 os.close(fd) 1020 os.utime(self._expandvars(self), None) 1021 return self
1022
1023 - def remove(self):
1024 """Remove this file 1025 1026 :return: self""" 1027 os.remove(self._expandvars(self)) 1028 return self
1029 1036 1037 #} END Modifying operations on files 1038 1039 #{ Links 1040 1041 if hasattr(os, 'link'): 1048 1049 1050 if hasattr(os, 'symlink'): 1057 1058 if hasattr(os, 'readlink'): 1065
1066 - def readlinkabs(self):
1067 """ Return the path to which this symbolic link points. 1068 1069 The result is always an absolute path. 1070 """ 1071 p = self.readlink() 1072 if p.isabs(): 1073 return p 1074 else: 1075 return (self.parent() / p).abspath()
1076 1077 #} END Links 1078 1079 #{ High-level functions from shutil 1080
1081 - def copyfile(self, dest):
1082 """Copy self to dest 1083 1084 :return: Path to dest""" 1085 shutil.copyfile( self._expandvars(self), dest ) 1086 return type(self)(dest)
1087
1088 - def copymode(self, dest):
1089 """Copy our mode to dest 1090 1091 :return: Path to dest""" 1092 shutil.copymode( self._expandvars(self), dest ) 1093 return type(self)(dest)
1094
1095 - def copystat(self, dest):
1096 """Copy our stats to dest 1097 1098 :return: Path to dest""" 1099 shutil.copystat( self._expandvars(self), dest ) 1100 return type(self)(dest)
1101
1102 - def copy(self, dest):
1103 """Copy data and source bits to dest 1104 1105 :return: Path to dest""" 1106 shutil.copy( self._expandvars(self), dest ) 1107 return type(self)(dest)
1108
1109 - def copy2(self, dest):
1110 """Shutil.copy2 self to dest 1111 1112 :return: Path to dest""" 1113 shutil.copy2( self._expandvars(self), dest ) 1114 return type(self)(dest)
1115
1116 - def copytree(self, dest, **kwargs):
1117 """Deep copy this file or directory to destination 1118 1119 :param kwargs: passed to shutil.copytree 1120 :return: Path to dest""" 1121 shutil.copytree( self._expandvars(self), dest, **kwargs ) 1122 return type(self)(dest)
1123 1124 if hasattr(shutil, 'move'):
1125 - def move(self, dest):
1126 """Move self to dest 1127 1128 :return: Path to dest""" 1129 shutil.move( self._expandvars(self), dest ) 1130 return type(self)(dest)
1131
1132 - def rmtree(self, **kwargs):
1133 """Remove self recursively 1134 1135 :param kwargs: passed to shutil.rmtree 1136 :return: self""" 1137 shutil.rmtree( self._expandvars(self), **kwargs ) 1138 return self
1139 1140 #} END High-Level 1141 1142 1143 #{ Special stuff from os 1144 if hasattr(os, 'chroot'):
1145 - def chroot(self):
1146 """Change the root directory path 1147 1148 :return: self""" 1149 os.chroot(self._expandvars(self)) 1150 return self
1151 1152 if hasattr(os, 'startfile'):
1153 - def startfile(self):
1154 """see os.startfile 1155 1156 :return: self""" 1157 os.startfile(self._expandvars(self)) 1158 return self
1159 #} END Special stuff from os 1160 1161 #{ utilities 1162 _ossep = os.path.sep 1163 _oossep = (_ossep == "/" and "\\") or "/"
1164 1165 -def _to_os_path(path):
1166 """:return: string being an os compatible path""" 1167 return path.replace(_oossep, _ossep)
1168 1169 #} END utilities 1170 1171 # backup original class 1172 BasePath = Path
1173 1174 -class ConversionPath(BasePath):
1175 """On windows, python represents paths with backslashes, within maya though, 1176 these are slashes We want to keep the original representation, but allow 1177 the methods to work nonetheless."""
1178 - def __div__(self, rel):
1179 return self.joinpath(rel)
1180 1181 @classmethod
1182 - def _expandvars(cls, path):
1183 # when expanding, we might get operating system separators into the path, which 1184 # have to be replaced to our actual one 1185 return super(ConversionPath, cls)._expandvars(path).replace(cls.osep, cls.sep)
1186
1187 - def _from_os_path(self, path):
1188 """:return: path with separators matching to our configuration""" 1189 return path.replace(self.osep, self.sep)
1190
1191 - def abspath(self):
1192 return self.__class__(self._from_os_path(_to_os_path(os.path.abspath(self))))
1193
1194 - def normpath(self):
1195 return self.__class__(self._from_os_path(os.path.normpath(self)))
1196
1197 - def joinpath(self, *args):
1198 return self.__class__(self._from_os_path(os.path.join(self, *args)))
1199
1200 - def relpathto(self, dest):
1201 rval = super(ConversionPath, self).relpathto(dest) 1202 return type(self)(self._from_os_path(rval))
1203
1204 - def dirname(self):
1205 return self.__class__(self._from_os_path(os.path.dirname(_to_os_path(self))))
1206
1207 - def basename(self):
1208 return type(self)(self._from_os_path(os.path.basename(_to_os_path(self))))
1209
1210 - def splitpath(self):
1211 parent, child = os.path.split(_to_os_path(self)) 1212 return type(self)(self._from_os_path(parent)), child
1213 1214 # { Special Methods 1215 if hasattr(os.path, 'splitunc'):
1216 - def splitunc(self):
1217 return super(ConversionPath, self).splitunc()
1218
1219 - def isunshared(self):
1220 return super(ConversionPath, self).isunshared()
1221 # } END special methods
1222 1223 # END handle backslashes 1224 1225 1226 #{ Utilities 1227 -def make_path(path):
1228 """:return: A path instance of the correct type 1229 :note: use this constructor if you use the Path.set_separator method at runtime 1230 to assure you will always create instances of the actual type, and not only 1231 of the type you imported last""" 1232 return Path(path)
1233 1234 #} END utilities 1235 1236 # assure separator is set 1237 ################################ 1238 Path.set_separator(os.path.sep) 1239 ################################ 1240