@@ -778,8 +778,12 @@ def cat_file(self, path, start=None, end=None, **kwargs):
778778 return f .read (end - f .tell ())
779779 return f .read ()
780780
781- def pipe_file (self , path , value , ** kwargs ):
781+ def pipe_file (self , path , value , mode = "overwrite" , ** kwargs ):
782782 """Set the bytes of given file"""
783+ if mode == "create" and self .exists (path ):
784+ # non-atomic but simple way; or could use "xb" in open(), which is likely
785+ # not as well supported
786+ raise FileExistsError
783787 with self .open (path , "wb" , ** kwargs ) as f :
784788 f .write (value )
785789
@@ -971,8 +975,12 @@ def get(
971975 with callback .branched (rpath , lpath ) as child :
972976 self .get_file (rpath , lpath , callback = child , ** kwargs )
973977
974- def put_file (self , lpath , rpath , callback = DEFAULT_CALLBACK , ** kwargs ):
978+ def put_file (
979+ self , lpath , rpath , callback = DEFAULT_CALLBACK , mode = "overwrite" , ** kwargs
980+ ):
975981 """Copy single file to remote"""
982+ if mode == "create" and self .exists (rpath ):
983+ raise FileExistsError
976984 if os .path .isdir (lpath ):
977985 self .makedirs (rpath , exist_ok = True )
978986 return None
@@ -1262,6 +1270,9 @@ def open(
12621270 Target file
12631271 mode: str like 'rb', 'w'
12641272 See builtin ``open()``
1273+ Mode "x" (exclusive write) may be implemented by the backend. Even if
1274+ it is, whether it is checked up front or on commit, and whether it is
1275+ atomic is implementation-dependent.
12651276 block_size: int
12661277 Some indication of buffering - this is a value in bytes
12671278 cache_options : dict, optional
@@ -1795,7 +1806,7 @@ def discard(self):
17951806
17961807 def info (self ):
17971808 """File information about this path"""
1798- if "r" in self .mode :
1809+ if self .readable () :
17991810 return self .details
18001811 else :
18011812 raise ValueError ("Info not available while writing" )
@@ -1842,7 +1853,7 @@ def write(self, data):
18421853 data: bytes
18431854 Set of bytes to be written.
18441855 """
1845- if self . mode not in { "wb" , "ab" } :
1856+ if not self . writable () :
18461857 raise ValueError ("File not in write mode" )
18471858 if self .closed :
18481859 raise ValueError ("I/O operation on closed file." )
@@ -1875,7 +1886,7 @@ def flush(self, force=False):
18751886 if force :
18761887 self .forced = True
18771888
1878- if self .mode not in { "wb" , "ab" } :
1889+ if self .readable () :
18791890 # no-op to flush on read-mode
18801891 return
18811892
@@ -2024,29 +2035,30 @@ def close(self):
20242035 return
20252036 if self .closed :
20262037 return
2027- if self .mode == "rb" :
2028- self .cache = None
2029- else :
2030- if not self .forced :
2031- self .flush (force = True )
2032-
2033- if self .fs is not None :
2034- self .fs .invalidate_cache (self .path )
2035- self .fs .invalidate_cache (self .fs ._parent (self .path ))
2038+ try :
2039+ if self .mode == "rb" :
2040+ self .cache = None
2041+ else :
2042+ if not self .forced :
2043+ self .flush (force = True )
20362044
2037- self .closed = True
2045+ if self .fs is not None :
2046+ self .fs .invalidate_cache (self .path )
2047+ self .fs .invalidate_cache (self .fs ._parent (self .path ))
2048+ finally :
2049+ self .closed = True
20382050
20392051 def readable (self ):
20402052 """Whether opened for reading"""
2041- return self .mode == "rb" and not self .closed
2053+ return "r" in self .mode and not self .closed
20422054
20432055 def seekable (self ):
20442056 """Whether is seekable (only in read mode)"""
20452057 return self .readable ()
20462058
20472059 def writable (self ):
20482060 """Whether opened for writing"""
2049- return self .mode in {"wb" , "ab" } and not self .closed
2061+ return self .mode in {"wb" , "ab" , "xb" } and not self .closed
20502062
20512063 def __del__ (self ):
20522064 if not self .closed :
0 commit comments