Skip to content

Commit ea9fc92

Browse files
committed
make a config parser
1 parent e9d1038 commit ea9fc92

1 file changed

Lines changed: 169 additions & 0 deletions

File tree

config_parser.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"""
2+
This is my own implementation of standard library ConfigParser module
3+
It parses .ini file and generates a configuration object
4+
"""
5+
import re, collections
6+
7+
FORMAT_RE = r'.*%\([^\)]+\)[sd].*'
8+
SECTION_RE = r'\[([^\[\]]+)\]'
9+
OPTION_RE = r'(.*)=(.*)'
10+
COMMENT_RE = r'^;.*'
11+
12+
class InvalidConfig(Exception):
13+
pass
14+
15+
def _is_format_str(s):
16+
regex = re.compile(FORMAT_RE)
17+
return regex.match(s)
18+
19+
def _parse_config_file(f):
20+
sections = {}
21+
current_section = None
22+
current_options = {}
23+
24+
section_re = re.compile(SECTION_RE)
25+
option_re = re.compile(OPTION_RE)
26+
comment_re = re.compile(COMMENT_RE)
27+
28+
for line in f:
29+
line = line.strip()
30+
31+
if not line: continue
32+
if comment_re.match(line): continue
33+
34+
mo = section_re.match(line)
35+
if mo:
36+
if current_section:
37+
sections[current_section] = current_options
38+
current_section = mo.group(1)
39+
current_options = {}
40+
else:
41+
mo1 = option_re.match(line)
42+
if mo1:
43+
if not current_section:
44+
raise InvalidConfig("Invalid config file")
45+
key, value = mo1.group(1).strip(), mo1.group(2).strip()
46+
current_options[key] = value
47+
else:
48+
raise InvalidConfig("Invalid config file")
49+
if current_section:
50+
sections[current_section] = current_options
51+
return sections
52+
53+
class ConfigParser(object):
54+
def __init__(self, defaults=None):
55+
"""
56+
create the parser and specify a dictionary of intrinsic defaults. The
57+
keys must be strings, the values must be appropriate for %()s string
58+
interpolation. Note that `__name__' is always an intrinsic default;
59+
its value is the section's name.
60+
"""
61+
self._defaults = defaults if defaults else {}
62+
self._sections = {}
63+
64+
65+
def sections(self):
66+
# return all the configuration section names
67+
return self._sections.keys()
68+
69+
def has_section(self, section):
70+
return section in self._sections
71+
72+
def has_option(self, section, option):
73+
return self.has_section(section) and option in self._sections[section]
74+
75+
def options(self, section):
76+
if not self.has_section(section):
77+
return []
78+
else:
79+
return self._sections[section].keys()
80+
81+
def read(self, filenames):
82+
"""
83+
read and parse the list of named configuration files, given by
84+
name. A single filename is also allowed. Non-existing files
85+
are ignored. Return list of successfully read files.
86+
"""
87+
if isinstance(filenames, basestring):
88+
f = open(filenames, 'r')
89+
self.readfp(f)
90+
elif isinstance(filenames, collections.Iterable):
91+
for fname in filenames:
92+
try:
93+
f = open(fname, 'r')
94+
self.readfp(f)
95+
except IOError:
96+
print "Fail to open file %s, ignored"%fname
97+
except InvalidConfig:
98+
print "Invalid config file %s, ignored"%fname
99+
100+
def readfp(self, fp, filename=None):
101+
"""
102+
read and parse one configuration file, given as a file object.
103+
The filename defaults to fp.name; it is only used in error
104+
messages (if fp has no `name' attribute, the string `<???>' is used).
105+
"""
106+
secs = _parse_config_file(fp)
107+
self._sections.update(secs)
108+
109+
def parse_item(self, section, key, val):
110+
# Now support only 1 level of interpolation
111+
if not _is_format_str(val):
112+
return val
113+
else:
114+
# first try default section
115+
try:
116+
newval = val%self._defaults
117+
except KeyError:
118+
try:
119+
newval = val%self._sections[section]
120+
except KeyError:
121+
newval = val
122+
return newval
123+
124+
def get(self, section, option, raw=False, vars=None):
125+
if not self.has_section(section):
126+
return None
127+
sec = self._sections[section]
128+
if option not in sec:
129+
return None
130+
return self.parse_item(section, option, sec[option])
131+
132+
def getint(self, section, option):
133+
return int(self.get(section, option))
134+
135+
def getfloat(self, section, option):
136+
return float(self.get(section, option))
137+
138+
def getboolean(self, section, option):
139+
val = self.get(section, option).strip()
140+
if val.lower() in ["0", "false", "no", "off"]:
141+
return False
142+
elif val.lower() in ["1", "true", "yes", "on"]:
143+
return True
144+
else:
145+
raise ValueError("Invalid boolean option")
146+
147+
def items(self, section, raw=False, vars=None):
148+
try:
149+
d = self._sections[section]
150+
except KeyError:
151+
d = {}
152+
d.update(self._defaults)
153+
return d.items()
154+
155+
def remove_section(self, section):
156+
if self.has_section(section):
157+
del self._sections[section]
158+
159+
def remove_option(self, section, option):
160+
if self.has_option(section, option):
161+
del self._sections[section][option]
162+
163+
def set(self, section, option, value):
164+
if self.has_section(section):
165+
self._sections[section][option] = value
166+
167+
def write(self, fp):
168+
#write the configuration state in .ini format
169+
pass

0 commit comments

Comments
 (0)