@@ -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
@@ -1911,7 +1922,7 @@ def discard(self):
19111922
19121923 def info (self ):
19131924 """File information about this path"""
1914- if "r" in self .mode :
1925+ if self .readable () :
19151926 return self .details
19161927 else :
19171928 raise ValueError ("Info not available while writing" )
@@ -1958,7 +1969,7 @@ def write(self, data):
19581969 data: bytes
19591970 Set of bytes to be written.
19601971 """
1961- if self . mode not in { "wb" , "ab" } :
1972+ if not self . writable () :
19621973 raise ValueError ("File not in write mode" )
19631974 if self .closed :
19641975 raise ValueError ("I/O operation on closed file." )
@@ -1991,7 +2002,7 @@ def flush(self, force=False):
19912002 if force :
19922003 self .forced = True
19932004
1994- if self .mode not in { "wb" , "ab" } :
2005+ if self .readable () :
19952006 # no-op to flush on read-mode
19962007 return
19972008
@@ -2140,29 +2151,46 @@ def close(self):
21402151 return
21412152 if self .closed :
21422153 return
2143- if self .mode == "rb" :
2144- self .cache = None
2145- else :
2146- if not self .forced :
2147- self .flush (force = True )
2148-
2149- if self .fs is not None :
2150- self .fs .invalidate_cache (self .path )
2151- self .fs .invalidate_cache (self .fs ._parent (self .path ))
2154+ try :
2155+ if self .mode == "rb" :
2156+ self .cache = None
2157+ else :
2158+ if not self .forced :
2159+ self .flush (force = True )
21522160
2153- self .closed = True
2161+ if self .fs is not None :
2162+ self .fs .invalidate_cache (self .path )
2163+ self .fs .invalidate_cache (self .fs ._parent (self .path ))
2164+ finally :
2165+ self .closed = True
21542166
21552167 def readable (self ):
21562168 """Whether opened for reading"""
2157- return self .mode == "rb" and not self .closed
2169+ return "r" in self .mode and not self .closed
21582170
21592171 def seekable (self ):
21602172 """Whether is seekable (only in read mode)"""
21612173 return self .readable ()
21622174
21632175 def writable (self ):
21642176 """Whether opened for writing"""
2165- return self .mode in {"wb" , "ab" } and not self .closed
2177+ return self .mode in {"wb" , "ab" , "xb" } and not self .closed
2178+
2179+ def __reduce__ (self ):
2180+ if self .mode != "rb" :
2181+ raise RuntimeError ("Pickling a writeable file is not supported" )
2182+
2183+ return reopen , (
2184+ self .fs ,
2185+ self .path ,
2186+ self .mode ,
2187+ self .blocksize ,
2188+ self .loc ,
2189+ self .size ,
2190+ self .autocommit ,
2191+ self .cache .name if self .cache else "none" ,
2192+ self .kwargs ,
2193+ )
21662194
21672195 def __del__ (self ):
21682196 if not self .closed :
@@ -2178,3 +2206,18 @@ def __enter__(self):
21782206
21792207 def __exit__ (self , * args ):
21802208 self .close ()
2209+
2210+
2211+ def reopen (fs , path , mode , blocksize , loc , size , autocommit , cache_type , kwargs ):
2212+ file = fs .open (
2213+ path ,
2214+ mode = mode ,
2215+ block_size = blocksize ,
2216+ autocommit = autocommit ,
2217+ cache_type = cache_type ,
2218+ size = size ,
2219+ ** kwargs ,
2220+ )
2221+ if loc > 0 :
2222+ file .seek (loc )
2223+ return file
0 commit comments