Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3fd3723

Browse files
committed
implemented config class as far as necessary, one check is still failing
Added odict module to get an OrderedDict to be used in the config parser, assuring the order of sections and options does not change
1 parent9513aa0 commit3fd3723

File tree

4 files changed

+1710
-21
lines changed

4 files changed

+1710
-21
lines changed

‎lib/git/config.py

Lines changed: 236 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,67 @@
99
"""
1010

1111
importre
12-
fromConfigParserimportRawConfigParser
12+
importos
13+
importConfigParserascp
14+
fromgit.odictimportOrderedDict
15+
importinspect
1316

1417
class_MetaParserBuilder(type):
1518
"""
16-
Utlity class wrapping methods into decorators that assure read-only properties
19+
Utlity class wrappingbase-classmethods into decorators that assure read-only properties
1720
"""
21+
def__new__(metacls,name,bases,clsdict):
22+
"""
23+
Equip all base-class methods with a _needs_values decorator, and all non-const methods
24+
with a _set_dirty_and_flush_changes decorator in addition to that.
25+
"""
26+
new_type=super(_MetaParserBuilder,metacls).__new__(metacls,name,bases,clsdict)
27+
28+
mutating_methods=clsdict['_mutating_methods_']
29+
forbaseinbases:
30+
methods= (tfortininspect.getmembers(base,inspect.ismethod)ifnott[0].startswith("_") )
31+
forname,methodinmethods:
32+
ifnameinclsdict:
33+
continue
34+
method_with_values=_needs_values(method)
35+
ifnameinmutating_methods:
36+
method_with_values=_set_dirty_and_flush_changes(method_with_values)
37+
# END mutating methods handling
38+
clsdict[name]=method_with_values
39+
# END for each base
40+
41+
returnnew_type
42+
43+
1844

1945
def_needs_values(func):
20-
"""Returns method assuring we read values (on demand) before we try to access them"""
21-
returnfunc
46+
"""
47+
Returns method assuring we read values (on demand) before we try to access them
48+
"""
49+
defassure_data_present(self,*args,**kwargs):
50+
self.read()
51+
returnfunc(self,*args,**kwargs)
52+
# END wrapper method
53+
assure_data_present.__name__=func.__name__
54+
returnassure_data_present
2255

23-
def_ensure_writable(non_const_func):
24-
"""Return method that checks whether given non constant function may be called.
25-
If so, the instance will be set dirty"""
56+
def_set_dirty_and_flush_changes(non_const_func):
57+
"""
58+
Return method that checks whether given non constant function may be called.
59+
If so, the instance will be set dirty.
60+
Additionally, we flush the changes right to disk
61+
"""
62+
defflush_changes(self,*args,**kwargs):
63+
rval=non_const_func(self,*args,**kwargs)
64+
self.write()
65+
returnrval
66+
# END wrapper method
67+
flush_changes.__name__=non_const_func.__name__
68+
returnflush_changes
2669

2770

2871

29-
classGitConfigParser(RawConfigParser,object):
72+
classGitConfigParser(cp.RawConfigParser,object):
3073
"""
3174
Implements specifics required to read git style configuration files.
3275
@@ -38,6 +81,10 @@ class GitConfigParser(RawConfigParser, object):
3881
3982
The configuration file will be locked if you intend to change values preventing other
4083
instances to write concurrently.
84+
85+
NOTE
86+
The config is case-sensitive even when queried, hence section and option names
87+
must match perfectly.
4188
"""
4289
__metaclass__=_MetaParserBuilder
4390

@@ -51,45 +98,214 @@ class GitConfigParser(RawConfigParser, object):
5198
)
5299

53100
# list of RawConfigParser methods able to change the instance
54-
_mutating_methods_=tuple()
55-
101+
_mutating_methods_= ("remove_section","remove_option","set")
56102

57103
def__init__(self,file_or_files,read_only=True):
58104
"""
59105
Initialize a configuration reader to read the given file_or_files and to
60-
possibly allow changes to it by setting read_only False
106+
possibly allow changes to it by setting read_only False
107+
108+
``file_or_files``
109+
A single file path or file objects or multiple of these
110+
111+
``read_only``
112+
If True, the ConfigParser may only read the data , but not change it.
113+
If False, only a single file path or file object may be given.
61114
"""
115+
# initialize base with ordered dictionaries to be sure we write the same
116+
# file back
117+
self._sections=OrderedDict()
118+
self._defaults=OrderedDict()
119+
62120
self._file_or_files=file_or_files
63121
self._read_only=read_only
122+
self._owns_lock=False
64123
self._is_initialized=False
65-
self._is_dirty=False
124+
125+
126+
ifnotread_only:
127+
ifisinstance(file_or_files, (tuple,list)):
128+
raiseValueError("Write-ConfigParsers can operate on a single file only, multiple files have been passed")
129+
# END single file check
130+
131+
self._file_name=file_or_files
132+
ifnotisinstance(self._file_name,basestring):
133+
self._file_name=file_or_files.name
134+
# END get filename
135+
136+
self._obtain_lock_or_raise()
137+
# END read-only check
138+
66139

67140
def__del__(self):
68141
"""
69142
Write pending changes if required and release locks
70143
"""
144+
ifself.read_only:
145+
return
146+
147+
try:
148+
try:
149+
self.write()
150+
exceptIOError,e:
151+
print"Exception during destruction of GitConfigParser: %s"%str(e)
152+
finally:
153+
self._release_lock()
154+
155+
def_lock_file_path(self):
156+
"""
157+
Return
158+
Path to lockfile
159+
"""
160+
return"%s.lock"% (self._file_name)
161+
162+
def_has_lock(self):
163+
"""
164+
Return
165+
True if we have a lock and if the lockfile still exists
166+
167+
Raise
168+
AssertionError if our lock-file does not exist
169+
"""
170+
ifnotself._owns_lock:
171+
returnFalse
172+
173+
lock_file=self._lock_file_path()
174+
try:
175+
fp=open(lock_file,"r")
176+
pid=int(fp.read())
177+
fp.close()
178+
exceptIOError:
179+
raiseAssertionError("GitConfigParser has a lock but the lock file at %s could not be read"%lock_file)
180+
181+
ifpid!=os.getpid():
182+
raiseAssertionError("We claim to own the lock at %s, but it was not owned by our process: %i"% (lock_file,os.getpid()))
183+
184+
returnTrue
185+
186+
def_obtain_lock_or_raise(self):
187+
"""
188+
Create a lock file as flag for other instances, mark our instance as lock-holder
189+
190+
Raise
191+
IOError if a lock was already present or a lock file could not be written
192+
"""
193+
ifself._has_lock():
194+
return
195+
196+
lock_file=self._lock_file_path()
197+
ifos.path.exists(lock_file):
198+
raiseIOError("Lock for file %r did already exist, delete %r in case the lock is illegal"% (self._file_name,lock_file))
199+
200+
fp=open(lock_file,"w")
201+
fp.write(str(os.getpid()))
202+
fp.close()
203+
204+
self._owns_lock=True
205+
206+
def_release_lock(self):
207+
"""
208+
Release our lock if we have one
209+
"""
210+
ifnotself._has_lock():
211+
return
212+
213+
os.remove(self._lock_file_path())
214+
self._owns_lock=False
215+
216+
defoptionxform(self,optionstr):
217+
"""
218+
Do not transform options in any way when writing
219+
"""
220+
returnoptionstr
71221

72222
defread(self):
73223
"""
74-
Read configuration information from our file or files
224+
Reads the data stored in the files we have been initialized with
225+
226+
Returns
227+
Nothing
228+
229+
Raises
230+
IOError if not all files could be read
75231
"""
76232
ifself._is_initialized:
77-
return
233+
return
234+
235+
236+
files_to_read=self._file_or_files
237+
ifnotisinstance(files_to_read, (tuple,list)):
238+
files_to_read= [files_to_read ]
78239

240+
forfile_objectinfiles_to_read:
241+
fp=file_object
242+
close_fp=False
243+
# assume a path if it is not a file-object
244+
ifnothasattr(file_object,"seek"):
245+
fp=open(file_object,"w")
246+
close_fp=True
247+
# END fp handling
248+
249+
try:
250+
self._read(fp,fp.name)
251+
finally:
252+
ifclose_fp:
253+
fp.close()
254+
# END read-handling
255+
# END for each file object to read
79256
self._is_initialized=True
80257

81-
@_ensure_writable
258+
def_write(self,fp):
259+
"""Write an .ini-format representation of the configuration state in
260+
git compatible format"""
261+
defwrite_section(name,section_dict):
262+
fp.write("[%s]\n"%name)
263+
for (key,value)insection_dict.items():
264+
ifkey!="__name__":
265+
fp.write("\t%s = %s\n"% (key,str(value).replace('\n','\n\t')))
266+
# END if key is not __name__
267+
# END section writing
268+
269+
ifself._defaults:
270+
write_section(cp.DEFAULTSECT,self._defaults)
271+
map(lambdat:write_section(t[0],t[1]),self._sections.items())
272+
273+
82274
defwrite(self):
83275
"""
84-
Writeourchanges to our file
276+
Write changes to our file, if there are changes at all
85277
86278
Raise
87-
AssertionError if this is a read-only writer instance
279+
IOError if this is a read-only writer instance or if we could not obtain
280+
a file lock
88281
"""
89-
ifnotself._is_dirty:
90-
return
282+
self._assure_writable("write")
283+
self._obtain_lock_or_raise()
284+
285+
286+
fp=self._file_or_files
287+
close_fp=False
288+
289+
ifnothasattr(fp,"seek"):
290+
fp=open(self._file_or_files,"w")
291+
close_fp=True
292+
else:
293+
fp.seek(0)
294+
295+
# WRITE DATA
296+
try:
297+
self._write(fp)
298+
finally:
299+
ifclose_fp:
300+
fp.close()
301+
# END data writing
302+
303+
# we do not release the lock - it will be done automatically once the
304+
# instance vanishes
91305

92-
self._is_dirty=False
306+
def_assure_writable(self,method_name):
307+
ifself.read_only:
308+
raiseIOError("Cannot execute non-constant method %s.%s"% (self,method_name))
93309

94310
@property
95311
defread_only(self):

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp