2
"""path.py - An object representing a path to a file or directory.
5
>>> from path import path
6
>>> d = path('/home/guido/bin')
7
>>> for f in d.files('*.py'):
10
This module requires Python 2.4 or later.
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.
28
from __future__ import generators
29
__docformat__ = "restructuredtext"
41
from interface import iDagItem
42
log = logging.getLogger("mrv.path")
45
__all__ = ['Path', 'BasePath', 'make_path']
47
# Platform-specific support for path.owner
59
# Pre-2.3 support. Are unicode filenames supported?
63
if os.path.supports_unicode_filenames:
69
# Pre-2.3 workaround for booleans
75
# Pre-2.3 workaround for basestring.
79
basestring = (str, unicode)
81
# Universal newline support
83
if hasattr(file, 'newlines'):
87
# cache used for path expansion
88
_varprog = re.compile(r'\$(\w+|\{[^}]*\})')
90
class TreeWalkWarning(Warning):
94
class Path( _base, iDagItem ):
95
""" Represents a filesystem path.
97
For documentation on individual methods, consult their
98
counterparts in os.path.
104
#{ Special Python methods
107
return '%s(%s)' % ( self.__class__.__name__, _base.__repr__(self) )
109
# Adding a path and a string yields a path.
110
def __add__(self, more):
112
resultStr = _base.__add__(self, more)
113
except TypeError: #Python bug
114
resultStr = NotImplemented
115
if resultStr is NotImplemented:
117
return self.__class__(resultStr)
119
def __radd__(self, other):
120
if isinstance(other, basestring):
121
return self.__class__(other.__add__(self))
123
return NotImplemented
125
# The / operator joins paths.
126
def __div__(self, rel):
127
""" fp.__div__(rel) == fp / rel == fp.joinpath(rel)
129
Join two path components, adding a separator character if
132
return self.__class__(os.path.join(self, rel))
134
# Make the / operator work even when true division is enabled.
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)))
149
#} END Special Python methods
152
def set_separator(cls, sep):
153
"""Set this type to support the given separator as general path separator"""
155
raise ValueError("Invalid path separator", sep)
157
cls.osep = (sep == '/' and '\\') or "/"
159
# setup path conversion as necessary
161
if os.path.sep != cls.sep:
162
Path = ConversionPath
165
# END handle Path type
169
""":return: the current working directory as a path object. """
170
return cls(_getcwd())
172
#{ iDagItem Implementation
175
""":return: the parent directory of this Path or None if this is the root"""
176
parent = self.dirname()
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
188
children = self.listdir( pattern )
192
return [ c for c in children if predicate( c ) ]
194
#} END idagitem implementation
196
#{ Operations on path strings.
199
def _expandvars(cls, path):
200
"""Internal version returning a string only representing the non-recursively
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 )"""
211
m = _varprog.search(path, i)
216
if name.startswith('{') and name.endswith('}'):
218
if name in os.environ:
220
path = path[:i] + os.environ[name]
225
# END handle variable exists in environ
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):
235
rval = cls._expandvars(path)
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))
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.
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` )
280
:raise ValueError: If we could not expand all environment variables as
281
their values where missing in the environment"""
283
if str(rval) == str(self) and rval.containsvars():
284
raise ValueError("Failed to expand all environment variables in %r, got %r" % (self, rval))
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())
296
""" The file extension, for example '.py'. """
297
f, ext = os.path.splitext(_base(self))
301
""" The drive specifier, for example 'C:'.
302
This is always empty on systems that don't use drive specifiers.
304
drive, r = os.path.splitdrive(self)
305
return self.__class__(drive)
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.
319
drive, rel = os.path.splitdrive(self)
320
return self.__class__(drive), rel
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.
332
filename, ext = os.path.splitext(self)
333
return self.__class__(filename), ext
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').
341
return self.splitext()[0]
343
if hasattr(os.path, 'splitunc'):
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
356
return self.__class__(os.path.join(self, *args))
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
370
while loc != os.curdir and loc != os.pardir:
372
loc, child = prev.splitpath()
381
""" Return this path as a relative path,
382
originating from the current working directory.
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
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
400
for i, c in enumerate(s1):
406
start_list = os.path.abspath(dest).split(splitter)
407
path_list = os.path.abspath(self._expandvars(self)).split(splitter)
409
# Work out how much of the filepath is shared by start and path.
410
i = len(commonprefix([start_list, path_list]))
412
rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:]
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"""
437
if sys.platform.startswith( "win" ):
440
return Path( self.replace( s, d ) )
442
#} END Operations on path strings
444
#{ Listing, searching, walking, and matching
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.
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-\*").
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,
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
504
if errors not in ('strict', 'warn', 'ignore'):
505
raise ValueError("invalid errors parameter")
508
childList = self.listdir()
510
if errors == 'ignore':
512
elif errors == 'warn':
514
"Unable to list directory '%s': %s"
515
% (self, sys.exc_info()[1]))
519
# END handle errors value
520
# END listdir exception handling
522
for child in childList:
523
if ( pattern is None or child.fnmatch(pattern) ) and predicate(child):
527
isdir = child.isdir()
530
if errors == 'ignore':
532
elif errors == 'warn':
534
"Unable to access '%s': %s"
535
% (child, sys.exc_info()[1]))
538
# END handle errors value
539
# END directory access exception handling
544
for item in child.walk(pattern, errors, predicate):
547
# END for each child in childlist
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,
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.
579
pathexpanded = self.expandvars()
580
return [cls(s) for s in glob.glob(_base(pathexpanded / pattern))]
582
#} END Listing, searching, walking and watching
585
#{ Reading or writing an entire file at once
587
def open(self, *args, **kwargs):
588
""" Open this file. Return a file object. """
589
return open(self._expandvars(self), *args, **kwargs)
592
""" Open this file, read all bytes, return them as a string. """
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.
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'.
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'.
632
mode = 'U' # we are in python 2.4 at least
638
f = codecs.open(self, 'r', encoding, errors)
639
# END handle encoding
645
# END handle file read
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.
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
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")
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
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)
721
encoding = sys.getdefaultencoding()
722
bytes = text.encode(encoding, errors)
724
# It is an error to specify an encoding if 'text' is
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)
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.
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.
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.
783
if line[-2:] in (u'\r\n', u'\x0d\x85'):
785
elif line[-1:] in (u'\r', u'\n',
789
if line[-2:] == '\r\n':
791
elif line[-1:] in ('\r', '\n'):
796
encoding = sys.getdefaultencoding()
797
line = line.encode(encoding, errors)
804
def lines(self, encoding=None, errors='strict', retain=True):
805
r""" Open this file, read all lines, return them in a list.
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.
823
if encoding is None and retain:
824
f = self.open(_textmode)
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.
848
# END assure file gets closed
850
return hashobject.digest()
852
#} END Reading or writing an enitre file at once
854
#{ Methods for querying the filesystem
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
879
return os.access(self._expandvars(self), mode)
882
""" Perform a stat() system call on this path. """
883
return os.stat(self._expandvars(self))
886
""" Like path.stat(), but do not follow symbolic links. """
887
return os.lstat(self._expandvars(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.
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
907
raise NotImplementedError("path.owner is not implemented on this platform.")
909
return pwd.getpwuid(st.st_uid).pw_name
911
if hasattr(os, 'statvfs'):
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
927
fileobj = self.open( 'a' )
933
# END handle file open
936
#} END Methods for querying the filesystem
938
#{ Modifying operations on files and directories
940
def setutime(self, times):
941
""" Set the access and modified times of this file.
944
os.utime(self._expandvars(self), times)
947
def chmod(self, mode):
951
os.chmod(self._expandvars(self), mode)
954
if hasattr(os, 'chown'):
955
def chown(self, uid, gid):
956
"""Change file ownership
959
os.chown(self._expandvars(self), uid, gid)
962
def rename(self, new):
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)
976
#} END Modifying operations on files and directories
978
#{ Create/delete operations on directories
980
def mkdir(self, mode=0777):
981
"""Make this directory, fail if it already exists
984
os.mkdir(self._expandvars(self), mode)
987
def makedirs(self, mode=0777):
988
"""Smarter makedir, see os.makedirs
991
os.makedirs(self._expandvars(self), mode)
995
"""Remove this empty directory
998
os.rmdir(self._expandvars(self))
1001
def removedirs(self):
1002
"""see os.removedirs
1005
os.removedirs(self._expandvars(self))
1008
#} END Create/delete operations on directories
1010
#{ Modifying operations on files
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.
1018
fd = os.open(self._expandvars(self), flags, mode)
1020
os.utime(self._expandvars(self), None)
1027
os.remove(self._expandvars(self))
1034
os.unlink(self._expandvars(self))
1037
#} END Modifying operations on files
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'):
1060
""" Return the path to which this symbolic link points.
1062
The result may be an absolute or a relative path.
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.
1075
return (self.parent() / p).abspath()
1079
#{ High-level functions from shutil
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
1137
shutil.rmtree( self._expandvars(self), **kwargs )
1143
#{ Special stuff from os
1144
if hasattr(os, 'chroot'):
1146
"""Change the root directory path
1149
os.chroot(self._expandvars(self))
1152
if hasattr(os, 'startfile'):
1153
def startfile(self):
1157
os.startfile(self._expandvars(self))
1159
#} END Special stuff from os
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)
1171
# backup original class
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)
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)
1187
def _from_os_path(self, path):
1188
""":return: path with separators matching to our configuration"""
1189
return path.replace(self.osep, self.sep)
1192
return self.__class__(self._from_os_path(_to_os_path(os.path.abspath(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))
1205
return self.__class__(self._from_os_path(os.path.dirname(_to_os_path(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'):
1217
return super(ConversionPath, self).splitunc()
1219
def isunshared(self):
1220
return super(ConversionPath, self).isunshared()
1221
# } END special methods
1223
# END handle backslashes
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"""
1236
# assure separator is set
1237
################################
1238
Path.set_separator(os.path.sep)
1239
################################