Skip to content

Commit d97a8bb

Browse files
committed
config parsers as context mangers can now be reentered for locks
1 parent 0f4d5ce commit d97a8bb

File tree

2 files changed

+38
-15
lines changed

2 files changed

+38
-15
lines changed

‎git/config.py

+21-15
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,11 @@ def release(self):
133133
return self._config.release()
134134

135135
def __enter__(self):
136+
self._config.__enter__()
136137
return self
137138

138139
def __exit__(self, exception_type, exception_value, traceback):
139-
self.release()
140+
self._config.__exit__(exception_type, exception_value, traceback)
140141

141142

142143
class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, object)):
@@ -155,8 +156,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje
155156
:note:
156157
The config is case-sensitive even when queried, hence section and option names
157158
must match perfectly.
158-
If used as a context manager, will release the locked file. This parser cannot
159-
be used afterwards."""
159+
If used as a context manager, will release the locked file."""
160160

161161
#{ Configuration
162162
# The lock type determines the type of lock to use in new configuration readers.
@@ -206,18 +206,23 @@ def __init__(self, file_or_files, read_only=True, merge_includes=True):
206206
self._is_initialized = False
207207
self._merge_includes = merge_includes
208208
self._lock = None
209-
210-
if not read_only:
211-
if isinstance(file_or_files, (tuple, list)):
212-
raise ValueError(
213-
"Write-ConfigParsers can operate on a single file only, multiple files have been passed")
214-
# END single file check
215-
216-
if not isinstance(file_or_files, string_types):
217-
file_or_files = file_or_files.name
218-
# END get filename from handle/stream
219-
# initialize lock base - we want to write
220-
self._lock = self.t_lock(file_or_files)
209+
self._aquire_lock()
210+
211+
def _aquire_lock(self):
212+
if not self._read_only:
213+
if not self._lock:
214+
if isinstance(self._file_or_files, (tuple, list)):
215+
raise ValueError(
216+
"Write-ConfigParsers can operate on a single file only, multiple files have been passed")
217+
# END single file check
218+
219+
file_or_files = self._file_or_files
220+
if not isinstance(self._file_or_files, string_types):
221+
file_or_files = self._file_or_files.name
222+
# END get filename from handle/stream
223+
# initialize lock base - we want to write
224+
self._lock = self.t_lock(file_or_files)
225+
# END lock check
221226

222227
self._lock._obtain_lock()
223228
# END read-only check
@@ -228,6 +233,7 @@ def __del__(self):
228233
self.release()
229234

230235
def __enter__(self):
236+
self._aquire_lock()
231237
return self
232238

233239
def __exit__(self, exception_type, exception_value, traceback):

‎git/test/test_config.py

+17
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,23 @@ def test_read_write(self):
7272
w_config.release()
7373
# END for each filename
7474

75+
@with_rw_directory
76+
def test_lock_reentry(self, rw_dir):
77+
fpl = os.path.join(rw_dir, 'l')
78+
gcp = GitConfigParser(fpl, read_only=False)
79+
with gcp as cw:
80+
cw.set_value('include', 'some_value', 'a')
81+
# entering again locks the file again...
82+
with gcp as cw:
83+
cw.set_value('include', 'some_other_value', 'b')
84+
# ...so creating an additional config writer must fail due to exclusive access
85+
self.failUnlessRaises(IOError, GitConfigParser, fpl, read_only=False)
86+
# but work when the lock is removed
87+
with GitConfigParser(fpl, read_only=False):
88+
assert os.path.exists(fpl)
89+
# reentering with an existing lock must fail due to exclusive access
90+
self.failUnlessRaises(IOError, gcp.__enter__)
91+
7592
def test_multi_line_config(self):
7693
file_obj = self._to_memcache(fixture_path("git_config_with_comments"))
7794
config = GitConfigParser(file_obj, read_only=False)

0 commit comments

Comments
 (0)