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