comparison mercurial/util.py @ 4827:89defeae88f3

turn util.opener into a class
author Alexis S. L. Carvalho <alexis@cecm.usp.br>
date Wed, 11 Jul 2007 17:40:41 -0300
parents 7549cd526b7f
children 41ad4105dde9
comparison
equal deleted inserted replaced
4826:15efc1d06143 4827:89defeae88f3
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":
1260 if atomictemp: 1279 if atomictemp:
1261 return atomictempfile(f, mode) 1280 return atomictempfile(f, mode)
1262 if nlink > 1: 1281 if nlink > 1:
1263 rename(mktempcopy(f), f) 1282 rename(mktempcopy(f), f)
1264 return posixfile(f, mode) 1283 return posixfile(f, mode)
1265
1266 return o
1267 1284
1268 class chunkbuffer(object): 1285 class chunkbuffer(object):
1269 """Allow arbitrary sized chunks of data to be efficiently read from an 1286 """Allow arbitrary sized chunks of data to be efficiently read from an
1270 iterator over chunks of arbitrary size.""" 1287 iterator over chunks of arbitrary size."""
1271 1288