summaryrefslogtreecommitdiff
path: root/lib/git/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/git/utils.py')
-rw-r--r--lib/git/utils.py144
1 files changed, 3 insertions, 141 deletions
diff --git a/lib/git/utils.py b/lib/git/utils.py
index 4e5f3b10..343338a9 100644
--- a/lib/git/utils.py
+++ b/lib/git/utils.py
@@ -11,7 +11,9 @@ import tempfile
from gitdb.util import (
stream_copy,
- make_sha
+ make_sha,
+ FDStreamWrapper,
+ LockedFD
)
@@ -271,146 +273,6 @@ class BlockingLockFile(LockFile):
break
# END endless loop
-
-class FDStreamWrapper(object):
- """A simple wrapper providing the most basic functions on a file descriptor
- with the fileobject interface. Cannot use os.fdopen as the resulting stream
- takes ownership"""
- __slots__ = ("_fd", '_pos')
- def __init__(self, fd):
- self._fd = fd
- self._pos = 0
-
- def write(self, data):
- self._pos += len(data)
- os.write(self._fd, data)
-
- def read(self, count=0):
- if count == 0:
- count = os.path.getsize(self._filepath)
- # END handle read everything
-
- bytes = os.read(self._fd, count)
- self._pos += len(bytes)
- return bytes
-
- def fileno(self):
- return self._fd
-
- def tell(self):
- return self._pos
-
-
-class LockedFD(LockFile):
- """This class facilitates a safe read and write operation to a file on disk.
- If we write to 'file', we obtain a lock file at 'file.lock' and write to
- that instead. If we succeed, the lock file will be renamed to overwrite
- the original file.
-
- When reading, we obtain a lock file, but to prevent other writers from
- succeeding while we are reading the file.
-
- This type handles error correctly in that it will assure a consistent state
- on destruction.
-
- :note: with this setup, parallel reading is not possible"""
- __slots__ = ("_filepath", '_fd', '_write')
-
- def __init__(self, filepath):
- """Initialize an instance with the givne filepath"""
- self._filepath = filepath
- self._fd = None
- self._write = None # if True, we write a file
-
- def __del__(self):
- # will do nothing if the file descriptor is already closed
- if self._fd is not None:
- self.rollback()
-
- def _lockfilepath(self):
- return "%s.lock" % self._filepath
-
- def open(self, write=False, stream=False):
- """Open the file descriptor for reading or writing, both in binary mode.
- :param write: if True, the file descriptor will be opened for writing. Other
- wise it will be opened read-only.
- :param stream: if True, the file descriptor will be wrapped into a simple stream
- object which supports only reading or writing
- :return: fd to read from or write to. It is still maintained by this instance
- and must not be closed directly
- :raise IOError: if the lock could not be retrieved
- :raise OSError: If the actual file could not be opened for reading
- :note: must only be called once"""
- if self._write is not None:
- raise AssertionError("Called %s multiple times" % self.open)
-
- self._write = write
-
- # try to open the lock file
- binary = getattr(os, 'O_BINARY', 0)
- lockmode = os.O_WRONLY | os.O_CREAT | os.O_EXCL | binary
- try:
- fd = os.open(self._lockfilepath(), lockmode)
- if not write:
- os.close(fd)
- else:
- self._fd = fd
- # END handle file descriptor
- except OSError:
- raise IOError("Lock at %r could not be obtained" % self._lockfilepath())
- # END handle lock retrieval
-
- # open actual file if required
- if self._fd is None:
- # we could specify exlusive here, as we obtained the lock anyway
- self._fd = os.open(self._filepath, os.O_RDONLY | binary)
- # END open descriptor for reading
-
- if stream:
- return FDStreamWrapper(self._fd)
- else:
- return self._fd
- # END handle stream
-
- def commit(self):
- """When done writing, call this function to commit your changes into the
- actual file.
- The file descriptor will be closed, and the lockfile handled.
- :note: can be called multiple times"""
- self._end_writing(successful=True)
-
- def rollback(self):
- """Abort your operation without any changes. The file descriptor will be
- closed, and the lock released.
- :note: can be called multiple times"""
- self._end_writing(successful=False)
-
- def _end_writing(self, successful=True):
- """Handle the lock according to the write mode """
- if self._write is None:
- raise AssertionError("Cannot end operation if it wasn't started yet")
-
- if self._fd is None:
- return
-
- os.close(self._fd)
- self._fd = None
-
- lockfile = self._lockfilepath()
- if self._write and successful:
- # on windows, rename does not silently overwrite the existing one
- if sys.platform == "win32":
- if os.path.isfile(self._filepath):
- os.remove(self._filepath)
- # END remove if exists
- # END win32 special handling
- os.rename(lockfile, self._filepath)
- else:
- # just delete the file so far, we failed
- os.remove(lockfile)
- # END successful handling
-
-
class LazyMixin(object):
"""