1179 def encodedopener(openerfn, fn): |
1179 def encodedopener(openerfn, fn): |
1180 def o(path, *args, **kw): |
1180 def o(path, *args, **kw): |
1181 return openerfn(fn(path), *args, **kw) |
1181 return openerfn(fn(path), *args, **kw) |
1182 return o |
1182 return o |
1183 |
1183 |
1184 def opener(base, audit=True): |
1184 def mktempcopy(name, emptyok=False): |
|
1185 """Create a temporary file with the same contents from name |
|
1186 |
|
1187 The permission bits are copied from the original file. |
|
1188 |
|
1189 If the temporary file is going to be truncated immediately, you |
|
1190 can use emptyok=True as an optimization. |
|
1191 |
|
1192 Returns the name of the temporary file. |
1185 """ |
1193 """ |
1186 return a function that opens files relative to base |
1194 d, fn = os.path.split(name) |
1187 |
1195 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d) |
1188 this function is used to hide the details of COW semantics and |
1196 os.close(fd) |
|
1197 # Temporary files are created with mode 0600, which is usually not |
|
1198 # what we want. If the original file already exists, just copy |
|
1199 # its mode. Otherwise, manually obey umask. |
|
1200 try: |
|
1201 st_mode = os.lstat(name).st_mode |
|
1202 except OSError, inst: |
|
1203 if inst.errno != errno.ENOENT: |
|
1204 raise |
|
1205 st_mode = 0666 & ~_umask |
|
1206 os.chmod(temp, st_mode) |
|
1207 if emptyok: |
|
1208 return temp |
|
1209 try: |
|
1210 try: |
|
1211 ifp = posixfile(name, "rb") |
|
1212 except IOError, inst: |
|
1213 if inst.errno == errno.ENOENT: |
|
1214 return temp |
|
1215 if not getattr(inst, 'filename', None): |
|
1216 inst.filename = name |
|
1217 raise |
|
1218 ofp = posixfile(temp, "wb") |
|
1219 for chunk in filechunkiter(ifp): |
|
1220 ofp.write(chunk) |
|
1221 ifp.close() |
|
1222 ofp.close() |
|
1223 except: |
|
1224 try: os.unlink(temp) |
|
1225 except: pass |
|
1226 raise |
|
1227 return temp |
|
1228 |
|
1229 class atomictempfile(posixfile): |
|
1230 """file-like object that atomically updates a file |
|
1231 |
|
1232 All writes will be redirected to a temporary copy of the original |
|
1233 file. When rename is called, the copy is renamed to the original |
|
1234 name, making the changes visible. |
|
1235 """ |
|
1236 def __init__(self, name, mode): |
|
1237 self.__name = name |
|
1238 self.temp = mktempcopy(name, emptyok=('w' in mode)) |
|
1239 posixfile.__init__(self, self.temp, mode) |
|
1240 |
|
1241 def rename(self): |
|
1242 if not self.closed: |
|
1243 posixfile.close(self) |
|
1244 rename(self.temp, localpath(self.__name)) |
|
1245 |
|
1246 def __del__(self): |
|
1247 if not self.closed: |
|
1248 try: |
|
1249 os.unlink(self.temp) |
|
1250 except: pass |
|
1251 posixfile.close(self) |
|
1252 |
|
1253 class opener(object): |
|
1254 """Open files relative to a base directory |
|
1255 |
|
1256 This class is used to hide the details of COW semantics and |
1189 remote file access from higher level code. |
1257 remote file access from higher level code. |
1190 """ |
1258 """ |
1191 def mktempcopy(name, emptyok=False): |
1259 def __init__(self, base, audit=True): |
1192 d, fn = os.path.split(name) |
1260 self.base = base |
1193 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d) |
1261 self.audit = audit |
1194 os.close(fd) |
1262 |
1195 # Temporary files are created with mode 0600, which is usually not |
1263 def __call__(self, path, mode="r", text=False, atomictemp=False): |
1196 # what we want. If the original file already exists, just copy |
1264 if self.audit: |
1197 # its mode. Otherwise, manually obey umask. |
|
1198 try: |
|
1199 st_mode = os.lstat(name).st_mode |
|
1200 except OSError, inst: |
|
1201 if inst.errno != errno.ENOENT: |
|
1202 raise |
|
1203 st_mode = 0666 & ~_umask |
|
1204 os.chmod(temp, st_mode) |
|
1205 if emptyok: |
|
1206 return temp |
|
1207 try: |
|
1208 try: |
|
1209 ifp = posixfile(name, "rb") |
|
1210 except IOError, inst: |
|
1211 if inst.errno == errno.ENOENT: |
|
1212 return temp |
|
1213 if not getattr(inst, 'filename', None): |
|
1214 inst.filename = name |
|
1215 raise |
|
1216 ofp = posixfile(temp, "wb") |
|
1217 for chunk in filechunkiter(ifp): |
|
1218 ofp.write(chunk) |
|
1219 ifp.close() |
|
1220 ofp.close() |
|
1221 except: |
|
1222 try: os.unlink(temp) |
|
1223 except: pass |
|
1224 raise |
|
1225 return temp |
|
1226 |
|
1227 class atomictempfile(posixfile): |
|
1228 """the file will only be copied when rename is called""" |
|
1229 def __init__(self, name, mode): |
|
1230 self.__name = name |
|
1231 self.temp = mktempcopy(name, emptyok=('w' in mode)) |
|
1232 posixfile.__init__(self, self.temp, mode) |
|
1233 def rename(self): |
|
1234 if not self.closed: |
|
1235 posixfile.close(self) |
|
1236 rename(self.temp, localpath(self.__name)) |
|
1237 def __del__(self): |
|
1238 if not self.closed: |
|
1239 try: |
|
1240 os.unlink(self.temp) |
|
1241 except: pass |
|
1242 posixfile.close(self) |
|
1243 |
|
1244 def o(path, mode="r", text=False, atomictemp=False): |
|
1245 if audit: |
|
1246 audit_path(path) |
1265 audit_path(path) |
1247 f = os.path.join(base, path) |
1266 f = os.path.join(self.base, path) |
1248 |
1267 |
1249 if not text and "b" not in mode: |
1268 if not text and "b" not in mode: |
1250 mode += "b" # for that other OS |
1269 mode += "b" # for that other OS |
1251 |
1270 |
1252 if mode[0] != "r": |
1271 if mode[0] != "r": |