Skip to content

Commit dc9278f

Browse files
committed
enable config parsers to be used as context managers
if used as context managers, the parsers will automatically release their file locks.
1 parent 121f6af commit dc9278f

File tree

4 files changed

+53
-42
lines changed

4 files changed

+53
-42
lines changed

Diff for: git/config.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ class SectionConstraint(object):
9595
"""Constrains a ConfigParser to only option commands which are constrained to
9696
always use the section we have been initialized with.
9797
98-
It supports all ConfigParser methods that operate on an option"""
98+
It supports all ConfigParser methods that operate on an option.
99+
100+
:note:
101+
If used as a context manager, will release the wrapped ConfigParser."""
99102
__slots__ = ("_config", "_section_name")
100103
_valid_attrs_ = ("get_value", "set_value", "get", "set", "getint", "getfloat", "getboolean", "has_option",
101104
"remove_section", "remove_option", "options")
@@ -129,6 +132,12 @@ def release(self):
129132
"""Equivalent to GitConfigParser.release(), which is called on our underlying parser instance"""
130133
return self._config.release()
131134

135+
def __enter__(self):
136+
return self
137+
138+
def __exit__(self, exception_type, exception_value, traceback):
139+
self.release()
140+
132141

133142
class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, object)):
134143

@@ -145,7 +154,9 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje
145154
146155
:note:
147156
The config is case-sensitive even when queried, hence section and option names
148-
must match perfectly."""
157+
must match perfectly.
158+
If used as a context manager, will release the locked file. This parser cannot
159+
be used afterwards."""
149160

150161
#{ Configuration
151162
# The lock type determines the type of lock to use in new configuration readers.
@@ -216,6 +227,12 @@ def __del__(self):
216227
# NOTE: only consistent in PY2
217228
self.release()
218229

230+
def __enter__(self):
231+
return self
232+
233+
def __exit__(self, exception_type, exception_value, traceback):
234+
self.release()
235+
219236
def release(self):
220237
"""Flush changes and release the configuration write lock. This instance must not be used anymore afterwards.
221238
In Python 3, it's required to explicitly release locks and flush changes, as __del__ is not called

Diff for: git/repo/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ def config_writer(self, config_level="repository"):
403403
"""
404404
:return:
405405
GitConfigParser allowing to write values of the specified configuration file level.
406-
Config writers should be retrieved, used to change the configuration ,and written
406+
Config writers should be retrieved, used to change the configuration, and written
407407
right away as they will lock the configuration file in question and prevent other's
408408
to write it.
409409

Diff for: git/test/test_config.py

+31-39
Original file line numberDiff line numberDiff line change
@@ -139,57 +139,49 @@ def check_test_value(cr, value):
139139

140140
# PREPARE CONFIG FILE A
141141
fpa = os.path.join(rw_dir, 'a')
142-
cw = GitConfigParser(fpa, read_only=False)
143-
write_test_value(cw, 'a')
144-
145-
fpb = os.path.join(rw_dir, 'b')
146-
fpc = os.path.join(rw_dir, 'c')
147-
cw.set_value('include', 'relative_path_b', 'b')
148-
cw.set_value('include', 'doesntexist', 'foobar')
149-
cw.set_value('include', 'relative_cycle_a_a', 'a')
150-
cw.set_value('include', 'absolute_cycle_a_a', fpa)
151-
cw.release()
142+
with GitConfigParser(fpa, read_only=False) as cw:
143+
write_test_value(cw, 'a')
144+
145+
fpb = os.path.join(rw_dir, 'b')
146+
fpc = os.path.join(rw_dir, 'c')
147+
cw.set_value('include', 'relative_path_b', 'b')
148+
cw.set_value('include', 'doesntexist', 'foobar')
149+
cw.set_value('include', 'relative_cycle_a_a', 'a')
150+
cw.set_value('include', 'absolute_cycle_a_a', fpa)
152151
assert os.path.exists(fpa)
153152

154153
# PREPARE CONFIG FILE B
155-
cw = GitConfigParser(fpb, read_only=False)
156-
write_test_value(cw, 'b')
157-
cw.set_value('include', 'relative_cycle_b_a', 'a')
158-
cw.set_value('include', 'absolute_cycle_b_a', fpa)
159-
cw.set_value('include', 'relative_path_c', 'c')
160-
cw.set_value('include', 'absolute_path_c', fpc)
161-
cw.release()
154+
with GitConfigParser(fpb, read_only=False) as cw:
155+
write_test_value(cw, 'b')
156+
cw.set_value('include', 'relative_cycle_b_a', 'a')
157+
cw.set_value('include', 'absolute_cycle_b_a', fpa)
158+
cw.set_value('include', 'relative_path_c', 'c')
159+
cw.set_value('include', 'absolute_path_c', fpc)
162160

163161
# PREPARE CONFIG FILE C
164-
cw = GitConfigParser(fpc, read_only=False)
165-
write_test_value(cw, 'c')
166-
cw.release()
162+
with GitConfigParser(fpc, read_only=False) as cw:
163+
write_test_value(cw, 'c')
167164

168-
cr = GitConfigParser(fpa, read_only=True)
169-
for tv in ('a', 'b', 'c'):
170-
check_test_value(cr, tv)
171-
# end for each test to verify
172-
assert len(cr.items('include')) == 8, "Expected all include sections to be merged"
173-
cr.release()
165+
with GitConfigParser(fpa, read_only=True) as cr:
166+
for tv in ('a', 'b', 'c'):
167+
check_test_value(cr, tv)
168+
# end for each test to verify
169+
assert len(cr.items('include')) == 8, "Expected all include sections to be merged"
174170

175171
# test writable config writers - assure write-back doesn't involve includes
176-
cw = GitConfigParser(fpa, read_only=False, merge_includes=True)
177-
tv = 'x'
178-
write_test_value(cw, tv)
179-
cw.release()
172+
with GitConfigParser(fpa, read_only=False, merge_includes=True) as cw:
173+
tv = 'x'
174+
write_test_value(cw, tv)
180175

181-
cr = GitConfigParser(fpa, read_only=True)
182-
self.failUnlessRaises(cp.NoSectionError, check_test_value, cr, tv)
183-
cr.release()
176+
with GitConfigParser(fpa, read_only=True) as cr:
177+
self.failUnlessRaises(cp.NoSectionError, check_test_value, cr, tv)
184178

185179
# But can make it skip includes alltogether, and thus allow write-backs
186-
cw = GitConfigParser(fpa, read_only=False, merge_includes=False)
187-
write_test_value(cw, tv)
188-
cw.release()
180+
with GitConfigParser(fpa, read_only=False, merge_includes=False) as cw:
181+
write_test_value(cw, tv)
189182

190-
cr = GitConfigParser(fpa, read_only=True)
191-
check_test_value(cr, tv)
192-
cr.release()
183+
with GitConfigParser(fpa, read_only=True) as cr:
184+
check_test_value(cr, tv)
193185

194186
def test_rename(self):
195187
file_obj = self._to_memcache(fixture_path('git_config'))

Diff for: test-requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
-r requirements.txt
2+
13
coverage
24
flake8
35
nose

0 commit comments

Comments
 (0)