mrv.path
Covered: 348 lines
Missed: 554 lines
Skipped 338 lines
Percent: 38 %
   2
"""path.py - An object representing a path to a file or directory.
   4
Example:
   5
	>>> from path import path
   6
	>>> d = path('/home/guido/bin')
   7
	>>> for f in d.files('*.py'):
   8
	>>>		f.chmod(0755)
  10
This module requires Python 2.4 or later.
  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.
  17
   - Two people asked for path.chdir().	 This just seems wrong to me,
  18
	 I dunno.  chdir() is moderately evil anyway.
  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"
  31
__license__='Freeware'
  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")
  44
__version__ = '3.0'
  45
__all__ = ['Path', 'BasePath', 'make_path']
  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
  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
  70
try:
  71
	True, False
  72
except NameError:
  73
	True, False = 1, 0
  76
try:
  77
	basestring
  78
except NameError:
  79
	basestring = (str, unicode)
  82
_textmode = 'r'
  83
if hasattr(file, 'newlines'):
  84
	_textmode = 'U'
  88
_varprog = re.compile(r'\$(\w+|\{[^}]*\})')
  90
class TreeWalkWarning(Warning):
  91
	pass
  94
class Path( _base, iDagItem ):
  95
	""" Represents a filesystem path.
  97
	For documentation on individual methods, consult their
  98
	counterparts in os.path.
  99
	"""
 101
	sep = None
 102
	osep = None
 106
	def __repr__(self):
 107
		return '%s(%s)' % ( self.__class__.__name__, _base.__repr__(self) )
 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)
 119
	def __radd__(self, other):
 120
		if isinstance(other, basestring):
 121
			return self.__class__(other.__add__(self))
 122
		else:
 123
			return NotImplemented
 126
	def __div__(self, rel):
 127
		""" fp.__div__(rel) == fp / rel == fp.joinpath(rel)
 129
		Join two path components, adding a separator character if
 130
		needed.
 131
		"""
 132
		return self.__class__(os.path.join(self, rel))
 135
	__truediv__ = __div__
 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)))
 142
	def __ne__( self, other ):
 143
		return not self.__eq__( other )
 145
	def __hash__( self ):
 146
		"""Expanded hash method"""
 147
		return hash(unicode(self._expandvars(self)))
 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 "/"
 160
		global Path
 161
		if os.path.sep != cls.sep:
 162
			Path = ConversionPath
 163
		else:
 164
			Path = BasePath
 167
	@classmethod
 168
	def getcwd(cls):
 169
		""":return: the current working directory as a path object. """
 170
		return cls(_getcwd())
 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
 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()
 192
		return [ c for c in children if predicate( c ) ]
 198
	@classmethod
 199
	def _expandvars(cls, path):
 200
		"""Internal version returning a string only representing the non-recursively
 201
		expanded variable
 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
 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
 227
		return path	
 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)
 237
		return rval
 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
 249
	def expandvars_deep(self):
 250
		"""Expands all environment variables recursively"""
 251
		return type(self)(self._expandvars_deep(self))
 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
 261
	def expand(self):
 262
		""" Clean up a filename by calling expandvars() and expanduser()
 264
		This is commonly everything needed to clean up a filename
 265
		read from a configuration file, for example.
 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()
 272
	def containsvars( self ):
 273
		""":return: True if this path contains environment variables"""
 274
		return self.find( '$' ) != -1
 276
	def expand_or_raise(self):
 277
		""":return: Copy of self with all variables expanded ( using `expand` )
 278
		non-recursively !
 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
 287
	def namebase(self):
 288
		"""The same as path.basename(), but with one file extension stripped off.
 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
 295
	def ext(self):
 296
		""" The file extension, for example '.py'. """
 297
		f, ext = os.path.splitext(_base(self))
 298
		return ext
 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)
 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
 312
	def splitdrive(self):
 313
		""" p.splitdrive() -> Return (p.drive, <the rest of p>).
 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
 322
	def splitext(self):
 323
		""" p.splitext() -> Return (p.stripext(), p.ext).
 325
		Split the filename extension from this path and return
 326
		the two parts.	Either part may be empty.
 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
 335
	def stripext(self):
 336
		""" p.stripext() -> Remove one file extension from the path.
 338
		For example, path('/home/guido/python.tar.gz').stripext()
 339
		returns path('/home/guido/python.tar').
 340
		"""
 341
		return self.splitext()[0]
 343
	if hasattr(os.path, 'splitunc'):
 344
		def splitunc(self):
 345
			unc, rest = os.path.splitunc(self)
 346
			return self.__class__(unc), rest
 348
		def isunshared(self):
 349
			unc, r = os.path.splitunc(self)
 350
			return self.__class__(unc)
 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))
 358
	def splitall(self):
 359
		""" Return a list of the path components in this path.
 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.
 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
 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())
 386
	def relpathto(self, dest):
 387
		""" Return a relative path from self to dest.
 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
		"""
 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
 406
		start_list = os.path.abspath(dest).split(splitter)
 407
		path_list = os.path.abspath(self._expandvars(self)).split(splitter)
 410
		i = len(commonprefix([start_list, path_list]))
 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))
 417
	def relpathfrom(self, dest):
 418
		""" Return a relative path from dest to self"""
 419
		return self.__class__(dest).relpathto(self)
 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))
 426
	def tolinuxpath(self):
 427
		""":return: A path using only slashes as path separator"""
 428
		return self.__class__(self.replace("\\", "/"))
 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 /
 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 ) )
 446
	def listdir(self, pattern=None):
 447
		"""return list of items in this directory.
 449
		Use D.files() or D.dirs() instead if you want a listing
 450
		of just files or just subdirectories.
 452
		The elements of the list are path objects.
 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]
 462
	def dirs(self, pattern=None):
 463
		""" D.dirs() -> List of this directory's subdirectories.
 465
		The elements of the list are path objects.
 466
		This does not walk recursively into subdirectories
 467
		(but see path.walkdirs).
 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()]
 475
	def files(self, pattern=None):
 476
		""" D.files() -> List of the files in this directory.
 478
		The elements of the list are path objects.
 479
		This does not walk into subdirectories (see path.walkfiles).
 481
		With the optional ``pattern`` argument, this only lists files
 482
		whose names match the given pattern.  For example,
 483
		d.files("\*.pyc").
 484
		"""
 486
		return [p for p in self.listdir(pattern) if p.isfile()]
 488
	def walk(self, pattern=None, errors='strict', predicate=lambda p: True):
 489
		"""create iterator over files and subdirs, recursively.
 491
		The iterator yields path objects naming each child item of
 492
		this directory and its descendants.
 494
		It performs a depth-first traversal of the directory tree.
 495
		Each directory is returned just before all its children.
 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")
 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
 522
		for child in childList:
 523
			if ( pattern is None or child.fnmatch(pattern) ) and predicate(child):
 524
				yield child
 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
 541
			if not isdir:
 542
				continue
 544
			for item in child.walk(pattern, errors, predicate):
 545
				yield item
 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)
 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)
 561
	def fnmatch(self, pattern):
 562
		""" Return True if self.basename() matches the given pattern.
 564
		pattern - A filename pattern with wildcards,
 565
			for example "\*.py".
 566
		"""
 567
		pathexpanded = self.expandvars()
 568
		return fnmatch.fnmatch(pathexpanded.basename(), pattern)
 570
	def glob(self, pattern):
 571
		""" Return a list of path objects that match the pattern.
 573
		pattern - a path relative to this directory, with wildcards.
 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))]
 587
	def open(self, *args, **kwargs):
 588
		""" Open this file.	 Return a file object. """
 589
		return open(self._expandvars(self), *args, **kwargs)
 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()
 599
	def write_bytes(self, bytes, append=False):
 600
		""" Open this file and write the given bytes to it.
 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()
 616
		return self
 618
	def text(self, encoding=None, errors='strict'):
 619
		r""" Open this file, read it in, return the content as a string.
 621
		This uses "U" mode in Python 2.3 and later, so "\r\n" and "\r"
 622
		are automatically translated to '\n'.
 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
 634
		f = None
 635
		if encoding is None:
 636
			f = self.open(mode)
 637
		else:
 638
			f = codecs.open(self, 'r', encoding, errors)
 641
		try:
 642
			return f.read()
 643
		finally:
 644
			f.close()
 647
	def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
 648
		r""" Write the given text to this file.
 650
		The default behavior is to overwrite any existing file;
 651
		to append instead, use the 'append=True' keyword argument.
 653
		There are two differences between path.write_text() and
 654
		path.write_bytes(): newline handling and Unicode handling.
 655
		See below.
 657
		**Parameters**:
 658
		  - text - str/unicode - The text to be written.
 660
		  - encoding - str - The Unicode encoding that will be used.
 661
			This is ignored if 'text' isn't a Unicode string.
 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.
 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'.
 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.
 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").
 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".
 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.
 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.)
 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.
 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.
 707
		:return: self
 708
		"""
 709
		bytes = ""
 710
		if isinstance(text, unicode):
 711
			if linesep is not None:
 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:
 726
			assert encoding is None
 728
			if linesep is not None:
 729
				text = (text.replace('\r\n', '\n')
 730
							.replace('\r', '\n'))
 731
				bytes = text.replace('\n', linesep)
 733
		self.write_bytes(bytes, append)
 734
		return self
 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.
 740
		By default this overwrites any existing file at this path.
 742
		This puts a platform-specific newline sequence on every line.
 743
		See 'linesep' below.
 745
		lines - A list of strings.
 747
		encoding - A Unicode encoding to use.  This applies only if
 748
			'lines' contains any Unicode strings.
 750
		errors - How to handle errors in Unicode encoding.	This
 751
			also applies only to Unicode strings.
 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().
 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.
 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:
 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()
 802
		return self
 804
	def lines(self, encoding=None, errors='strict', retain=True):
 805
		r""" Open this file, read all lines, return them in a list.
 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.
 813
			 * errors: How to handle Unicode errors; see help(str.decode)
 814
				for the options.  Default is 'strict'
 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.
 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)
 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.
 836
		:note: This reads through the entire file.
 837
		"""
 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()
 850
		return hashobject.digest()
 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) )
 864
	if hasattr(os.path, 'samefile'):
 865
		samefile = lambda self, other: os.path.samefile( self._expandvars(self), other )
 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) )
 873
	if hasattr(os, 'access'):
 874
		def access(self, mode):
 875
			""" Return true if current user has access to this path.
 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)
 881
	def stat(self):
 882
		""" Perform a stat() system call on this path. """
 883
		return os.stat(self._expandvars(self))
 885
	def lstat(self):
 886
		""" Like path.stat(), but do not follow symbolic links. """
 887
		return os.lstat(self._expandvars(self))
 889
	def owner(self):
 890
		""" Return the name of the owner of this file or directory.
 892
		This follows symbolic links.
 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
 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))
 916
	if hasattr(os, 'pathconf'):
 917
		def pathconf(self, name):
 918
			"""see os.pathconf"""
 919
			return os.pathconf(self._expandvars(self), name)
 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
 926
		try:
 927
			fileobj = self.open( 'a' )
 928
		except:
 929
			return False
 930
		else:
 931
			fileobj.close()
 932
			return True
 940
	def setutime(self, times):
 941
		""" Set the access and modified times of this file.
 943
		:return: self"""
 944
		os.utime(self._expandvars(self), times)
 945
		return self
 947
	def chmod(self, mode):
 948
		"""Change file mode
 950
		:return: self"""
 951
		os.chmod(self._expandvars(self), mode)
 952
		return self
 954
	if hasattr(os, 'chown'):
 955
		def chown(self, uid, gid):
 956
			"""Change file ownership
 958
			:return: self"""
 959
			os.chown(self._expandvars(self), uid, gid)
 960
			return self
 962
	def rename(self, new):
 963
		"""os.rename
 965
		:return: Path to new file"""
 966
		os.rename(self._expandvars(self), new)
 967
		return type(self)(new)
 969
	def renames(self, new):
 970
		"""os.renames, super rename
 972
		:return: Path to new file"""
 973
		os.renames(self._expandvars(self), new)
 974
		return type(self)(new)
 980
	def mkdir(self, mode=0777):
 981
		"""Make this directory, fail if it already exists
 983
		:return: self"""
 984
		os.mkdir(self._expandvars(self), mode)
 985
		return self
 987
	def makedirs(self, mode=0777):
 988
		"""Smarter makedir, see os.makedirs
 990
		:return: self"""
 991
		os.makedirs(self._expandvars(self), mode)
 992
		return self
 994
	def rmdir(self):
 995
		"""Remove this empty directory
 997
		:return: self"""
 998
		os.rmdir(self._expandvars(self))
 999
		return self
1001
	def removedirs(self):
1002
		"""see os.removedirs
1004
		:return: self"""
1005
		os.removedirs(self._expandvars(self))
1006
		return self
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.
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
1023
	def remove(self):
1024
		"""Remove this file
1026
		:return: self"""
1027
		os.remove(self._expandvars(self))
1028
		return self
1030
	def unlink(self):
1031
		"""unlink this file
1033
		:return: self"""
1034
		os.unlink(self._expandvars(self))
1035
		return self
1041
	if hasattr(os, 'link'):
1042
		def link(self, newpath):
1043
			""" Create a hard link at 'newpath', pointing to this file. 
1045
			:return: Path to newpath"""
1046
			os.link(self._expandvars(self), newpath)
1047
			return type(self)(newpath)
1050
	if hasattr(os, 'symlink'):
1051
		def symlink(self, newlink):
1052
			""" Create a symbolic link at 'newlink', pointing here. 
1054
			:return: Path to newlink"""
1055
			os.symlink(self._expandvars(self), newlink)
1056
			return type(self)(newlink)
1058
	if hasattr(os, 'readlink'):
1059
		def readlink(self):
1060
			""" Return the path to which this symbolic link points.
1062
			The result may be an absolute or a relative path.
1063
			"""
1064
			return self.__class__(os.readlink(self._expandvars(self)))
1066
		def readlinkabs(self):
1067
			""" Return the path to which this symbolic link points.
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()
1081
	def copyfile(self, dest):
1082
		"""Copy self to dest
1084
		:return: Path to dest"""
1085
		shutil.copyfile( self._expandvars(self), dest )
1086
		return type(self)(dest)
1088
	def copymode(self, dest):
1089
		"""Copy our mode to dest
1091
		:return: Path to dest"""
1092
		shutil.copymode( self._expandvars(self), dest )
1093
		return type(self)(dest)
1095
	def copystat(self, dest):
1096
		"""Copy our stats to dest
1098
		:return: Path to dest"""
1099
		shutil.copystat( self._expandvars(self), dest )
1100
		return type(self)(dest)
1102
	def copy(self, dest):
1103
		"""Copy data and source bits to dest
1105
		:return: Path to dest"""
1106
		shutil.copy( self._expandvars(self), dest )
1107
		return type(self)(dest)
1109
	def copy2(self, dest):
1110
		"""Shutil.copy2 self to dest
1112
		:return: Path to dest"""
1113
		shutil.copy2( self._expandvars(self), dest )
1114
		return type(self)(dest)
1116
	def copytree(self, dest, **kwargs):
1117
		"""Deep copy this file or directory to destination
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)
1124
	if hasattr(shutil, 'move'):
1125
		def move(self, dest):
1126
			"""Move self to dest
1128
			:return: Path to dest"""
1129
			shutil.move( self._expandvars(self), dest )
1130
			return type(self)(dest)
1132
	def rmtree(self, **kwargs):
1133
		"""Remove self recursively
1135
		:param kwargs: passed to shutil.rmtree
1136
		:return: self"""
1137
		shutil.rmtree( self._expandvars(self),  **kwargs )
1138
		return self
1144
	if hasattr(os, 'chroot'):
1145
		def chroot(self):
1146
			"""Change the root directory path
1148
			:return: self"""
1149
			os.chroot(self._expandvars(self))
1150
			return self
1152
	if hasattr(os, 'startfile'):
1153
		def startfile(self):
1154
			"""see os.startfile
1156
			:return: self"""
1157
			os.startfile(self._expandvars(self))
1158
			return self
1162
_ossep = os.path.sep
1163
_oossep = (_ossep == "/" and "\\") or "/"
1165
def _to_os_path(path):
1166
	""":return: string being an os compatible path"""
1167
	return path.replace(_oossep, _ossep)
1172
BasePath = Path
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)
1181
	@classmethod
1182
	def _expandvars(cls, path):
1185
		return super(ConversionPath, cls)._expandvars(path).replace(cls.osep, cls.sep)
1187
	def _from_os_path(self, path):
1188
		""":return: path with separators matching to our configuration"""
1189
		return path.replace(self.osep, self.sep)
1191
	def abspath(self):
1192
		return self.__class__(self._from_os_path(_to_os_path(os.path.abspath(self))))
1194
	def normpath(self):
1195
		return self.__class__(self._from_os_path(os.path.normpath(self)))
1197
	def joinpath(self, *args):
1198
		return self.__class__(self._from_os_path(os.path.join(self, *args)))
1200
	def relpathto(self, dest):
1201
		rval = super(ConversionPath, self).relpathto(dest)
1202
		return type(self)(self._from_os_path(rval))
1204
	def dirname(self):
1205
		return self.__class__(self._from_os_path(os.path.dirname(_to_os_path(self))))
1207
	def basename(self):
1208
		return type(self)(self._from_os_path(os.path.basename(_to_os_path(self))))
1210
	def splitpath(self):
1211
		parent, child = os.path.split(_to_os_path(self))
1212
		return type(self)(self._from_os_path(parent)), child
1215
	if hasattr(os.path, 'splitunc'):
1216
		def splitunc(self):
1217
			return super(ConversionPath, self).splitunc()
1219
		def isunshared(self):
1220
			return super(ConversionPath, self).isunshared()
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)
1238
Path.set_separator(os.path.sep)