|
11 | 11 |
|
12 | 12 | from gitdb.util import (
|
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 |
| -class FDStreamWrapper(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 |
| - def write(self, data): |
285 |
| - self._pos += len(data) |
286 |
| - os.write(self._fd, data) |
287 |
| - |
288 |
| - def read(self, count=0): |
289 |
| - if count == 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 |
| - return bytes |
296 |
| - |
297 |
| - def fileno(self): |
298 |
| - return self._fd |
299 |
| - |
300 |
| - def tell(self): |
301 |
| - return self._pos |
302 |
| - |
303 |
| - |
304 |
| -class LockedFD(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 |
| - if self._fd is not None: |
328 |
| - self.rollback() |
329 |
| - |
330 |
| - def _lockfilepath(self): |
331 |
| - return "%s.lock" % self._filepath |
332 |
| - |
333 |
| - def open(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 |
| - if self._write is not None: |
345 |
| - raise AssertionError("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 |
| - if not write: |
355 |
| - os.close(fd) |
356 |
| - else: |
357 |
| - self._fd = fd |
358 |
| - # END handle file descriptor |
359 |
| - except OSError: |
360 |
| - raise IOError("Lock at %r could not be obtained" % self._lockfilepath()) |
361 |
| - # END handle lock retrieval |
362 |
| - |
363 |
| - # open actual file if required |
364 |
| - if self._fd is None: |
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 |
| - if stream: |
370 |
| - return FDStreamWrapper(self._fd) |
371 |
| - else: |
372 |
| - return self._fd |
373 |
| - # END handle stream |
374 |
| - |
375 |
| - def commit(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 |
| - def rollback(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 |
| - if self._write is None: |
391 |
| - raise AssertionError("Cannot end operation if it wasn't started yet") |
392 |
| - |
393 |
| - if self._fd is None: |
394 |
| - return |
395 |
| - |
396 |
| - os.close(self._fd) |
397 |
| - self._fd = None |
398 |
| - |
399 |
| - lockfile = self._lockfilepath() |
400 |
| - if self._write and successful: |
401 |
| - # on windows, rename does not silently overwrite the existing one |
402 |
| - if sys.platform == "win32": |
403 |
| - if os.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 | class LazyMixin(object):
|
416 | 278 | """
|
|
0 commit comments