|
11 | 11 |
|
12 | 12 | fromgitdb.utilimport (
|
13 | 13 | stream_copy,
|
14 |
| -make_sha |
| 14 | +make_sha, |
| 15 | +FDStreamWrapper, |
| 16 | +LockedFD |
15 | 17 | )
|
16 | 18 |
|
17 | 19 |
|
@@ -271,146 +273,6 @@ def _obtain_lock(self):
|
271 | 273 | break
|
272 | 274 | # END endless loop
|
273 | 275 |
|
274 |
| - |
275 |
| -classFDStreamWrapper(object): |
276 |
| -"""A simple wrapper providing the most basic functions on a file descriptor |
277 |
| -with the fileobject interface. Cannot use os.fdopen as the resulting stream |
278 |
| -takes ownership""" |
279 |
| -__slots__= ("_fd",'_pos') |
280 |
| -def__init__(self,fd): |
281 |
| -self._fd=fd |
282 |
| -self._pos=0 |
283 |
| - |
284 |
| -defwrite(self,data): |
285 |
| -self._pos+=len(data) |
286 |
| -os.write(self._fd,data) |
287 |
| - |
288 |
| -defread(self,count=0): |
289 |
| -ifcount==0: |
290 |
| -count=os.path.getsize(self._filepath) |
291 |
| -# END handle read everything |
292 |
| - |
293 |
| -bytes=os.read(self._fd,count) |
294 |
| -self._pos+=len(bytes) |
295 |
| -returnbytes |
296 |
| - |
297 |
| -deffileno(self): |
298 |
| -returnself._fd |
299 |
| - |
300 |
| -deftell(self): |
301 |
| -returnself._pos |
302 |
| - |
303 |
| - |
304 |
| -classLockedFD(LockFile): |
305 |
| -"""This class facilitates a safe read and write operation to a file on disk. |
306 |
| -If we write to 'file', we obtain a lock file at 'file.lock' and write to |
307 |
| -that instead. If we succeed, the lock file will be renamed to overwrite |
308 |
| -the original file. |
309 |
| -
|
310 |
| -When reading, we obtain a lock file, but to prevent other writers from |
311 |
| -succeeding while we are reading the file. |
312 |
| -
|
313 |
| -This type handles error correctly in that it will assure a consistent state |
314 |
| -on destruction. |
315 |
| -
|
316 |
| -:note: with this setup, parallel reading is not possible""" |
317 |
| -__slots__= ("_filepath",'_fd','_write') |
318 |
| - |
319 |
| -def__init__(self,filepath): |
320 |
| -"""Initialize an instance with the givne filepath""" |
321 |
| -self._filepath=filepath |
322 |
| -self._fd=None |
323 |
| -self._write=None# if True, we write a file |
324 |
| - |
325 |
| -def__del__(self): |
326 |
| -# will do nothing if the file descriptor is already closed |
327 |
| -ifself._fdisnotNone: |
328 |
| -self.rollback() |
329 |
| - |
330 |
| -def_lockfilepath(self): |
331 |
| -return"%s.lock"%self._filepath |
332 |
| - |
333 |
| -defopen(self,write=False,stream=False): |
334 |
| -"""Open the file descriptor for reading or writing, both in binary mode. |
335 |
| -:param write: if True, the file descriptor will be opened for writing. Other |
336 |
| -wise it will be opened read-only. |
337 |
| -:param stream: if True, the file descriptor will be wrapped into a simple stream |
338 |
| -object which supports only reading or writing |
339 |
| -:return: fd to read from or write to. It is still maintained by this instance |
340 |
| -and must not be closed directly |
341 |
| -:raise IOError: if the lock could not be retrieved |
342 |
| -:raise OSError: If the actual file could not be opened for reading |
343 |
| -:note: must only be called once""" |
344 |
| -ifself._writeisnotNone: |
345 |
| -raiseAssertionError("Called %s multiple times"%self.open) |
346 |
| - |
347 |
| -self._write=write |
348 |
| - |
349 |
| -# try to open the lock file |
350 |
| -binary=getattr(os,'O_BINARY',0) |
351 |
| -lockmode=os.O_WRONLY|os.O_CREAT|os.O_EXCL|binary |
352 |
| -try: |
353 |
| -fd=os.open(self._lockfilepath(),lockmode) |
354 |
| -ifnotwrite: |
355 |
| -os.close(fd) |
356 |
| -else: |
357 |
| -self._fd=fd |
358 |
| -# END handle file descriptor |
359 |
| -exceptOSError: |
360 |
| -raiseIOError("Lock at %r could not be obtained"%self._lockfilepath()) |
361 |
| -# END handle lock retrieval |
362 |
| - |
363 |
| -# open actual file if required |
364 |
| -ifself._fdisNone: |
365 |
| -# we could specify exlusive here, as we obtained the lock anyway |
366 |
| -self._fd=os.open(self._filepath,os.O_RDONLY|binary) |
367 |
| -# END open descriptor for reading |
368 |
| - |
369 |
| -ifstream: |
370 |
| -returnFDStreamWrapper(self._fd) |
371 |
| -else: |
372 |
| -returnself._fd |
373 |
| -# END handle stream |
374 |
| - |
375 |
| -defcommit(self): |
376 |
| -"""When done writing, call this function to commit your changes into the |
377 |
| -actual file. |
378 |
| -The file descriptor will be closed, and the lockfile handled. |
379 |
| -:note: can be called multiple times""" |
380 |
| -self._end_writing(successful=True) |
381 |
| - |
382 |
| -defrollback(self): |
383 |
| -"""Abort your operation without any changes. The file descriptor will be |
384 |
| -closed, and the lock released. |
385 |
| -:note: can be called multiple times""" |
386 |
| -self._end_writing(successful=False) |
387 |
| - |
388 |
| -def_end_writing(self,successful=True): |
389 |
| -"""Handle the lock according to the write mode """ |
390 |
| -ifself._writeisNone: |
391 |
| -raiseAssertionError("Cannot end operation if it wasn't started yet") |
392 |
| - |
393 |
| -ifself._fdisNone: |
394 |
| -return |
395 |
| - |
396 |
| -os.close(self._fd) |
397 |
| -self._fd=None |
398 |
| - |
399 |
| -lockfile=self._lockfilepath() |
400 |
| -ifself._writeandsuccessful: |
401 |
| -# on windows, rename does not silently overwrite the existing one |
402 |
| -ifsys.platform=="win32": |
403 |
| -ifos.path.isfile(self._filepath): |
404 |
| -os.remove(self._filepath) |
405 |
| -# END remove if exists |
406 |
| -# END win32 special handling |
407 |
| -os.rename(lockfile,self._filepath) |
408 |
| -else: |
409 |
| -# just delete the file so far, we failed |
410 |
| -os.remove(lockfile) |
411 |
| -# END successful handling |
412 |
| - |
413 |
| - |
414 | 276 |
|
415 | 277 | classLazyMixin(object):
|
416 | 278 | """
|
|