-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathconfig.py
233 lines (183 loc) · 7.68 KB
/
config.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/usr/bin/python
# vim: set fileencoding=utf-8 :
"""
options.py
-----------
This module cares about the configuration of pyalpmm and if you want to: its
applications. PyChmViewerConfig at the bottom is the actual configuration
handler class. It defines all configuration options explicitly, describing its
properties.
All ConfigItem instances and derivates take two arguments: at first
the 'section' the option belongs to and then the default-value, which
should be used (optional).
After these definitions, the __init__ has to construct the filename for the
config file and then just call the super().__init__() and the ConfigMapper
instance gets populated with the data from the input file.
"""
from StringIO import StringIO
from ConfigParser import RawConfigParser
class ConfigError(Exception):
pass
class ConfigItem(object):
"""The baseclass for all *ConfigItem instances. One ConfigItem represents
one option and its value inside the ConfigMapper class.
As long as used as an object attribute it behaves like a simple data type,
but actually is a descriptor with also holds additional data for this
option.
Each derived class has to set those two class-attributes:
- converter: a callable which converts the input (str) into the
representation of the wanted data type
- default: a default value, which is taken if neither the instance defined
a default nor the config file has an entry for this option
"""
# this method is called for data that is read
inconv = lambda s, v: v
# and this one is called for data to be written
outconv = lambda s, v: v
default = None
def __init__(self, section, default_value=None):
self.section = section
self.value = default_value or self.default
# this is set inside ConfigMapper.__init__
self.name = None
def __get__(self, obj, owner):
return self.value
def __set__(self, obj, val):
self.value = val
def __repr__(self):
return "<{0} name={1} val=\"{2}\" section=\"{3}\">".format(
self.__class__.__name__,
self.name, self.value, self.section
)
class StringConfigItem(ConfigItem):
"""Holds a string of config data"""
inconv = lambda s, v: str(v)
outconv = lambda s, v: str(v)
default = ""
class IntegerConfigItem(ConfigItem):
"""Holds an integer of config data"""
inconv = lambda s, v: int(v)
outconv = lambda s, v: str(v)
default = 0
class ListConfigItem(ConfigItem):
"""Holds a list of config data"""
inconv = lambda s, v: [x.strip() for x in
(v.split(",") if "," in v else v.split(" "))]
outconv = lambda s, v: ",".join(v)
default = []
def __iter__(self):
for item in self.value:
yield item
def __getitem__(self, key):
return self.value[key]
def __len__(self):
return len(self.value)
class YesNoConfigItem(ConfigItem):
"""Is either True or False"""
inconv = lambda s, v: v.lower() == "yes" if v.lower() in ["no", "yes"] \
else bool(v)
outconv = lambda s, v: "yes" if v else "no"
default = False
class CommandlineItem(ConfigItem):
"""A special ConfigItem, which is passed through the commandline"""
default = False
def __init__(self, default_value=None):
super(CommandlineItem, self).__init__(None, default_value)
class ConfigMapper(object):
"""The baseclass for a ConfigMapper class.
The idea is to define your configuration options as precise as possible
and the let the ConfigMapper do the rest, including r/w a configfile,
convert into the needed data types and provide the right default values,
if needed.
You just define attributes in your CustomConfigMapper class like this:
class CustomConfigMapper(ConfigMapper):
path = StringConfigItem("general")
other_path = StringConfigItem("foo", "my_default_value")
alist = ListConfigItem("foo", [1,2,3,4])
.
.
special_easter_egg = CommandlineItem(False)
.
Then call it with a 'stream' (means .read() must be available) and a
options object from 'optparse', or something that behaves like it. You will
get a fully populated CustomConfigMapper object already up-to-date with
your config file.
"""
config_items = {}
# if strict is True, _all_ config options MUST be set in the config
strict = False
def __init__(self, stream=None):
# all ConfigurationItems in class
all_confs = ((name, attr) \
for name, attr in self.__class__.__dict__.items() \
if isinstance(attr, ConfigItem))
# as an attribute doesn't know his own name automaticly, set it!
# further append all Items to their appropriate lists:
# - config_items for ConfigItems
for name, attr in all_confs:
attr.name = name
self.config_items[name] = attr
self.stream = stream or StringIO()
self.confobj = RawConfigParser()
self.confobj.readfp(self.stream)
# actually read the data from the file
self.read_from_file()
def __getitem__(self, key):
if key in self:
return self.config_items[key]
raise KeyError("'{0}' is not an existing config key".format(key))
def __contains__(self, key):
return key in self.cmdline_items.keys()
def __iter__(self):
for k, v in self.config_items.items():
yield (k, v)
def read_from_file(self):
"""Read configuration from file into the object attributes"""
for item in self.config_items.values():
if self.confobj.has_option(item.section, item.name):
item.value = item.inconv(self.confobj.get(item.section, item.name).strip())
elif self.strict:
raise ConfigError(
"Didn't find section: %s with option: %s" % (
item.section,
item.name
))
def save_into_file(self, path):
"""Write the default config settings to a file"""
conf_obj = RawConfigParser()
written_sections = []
for k, v in self.config_items.items():
if v.section not in written_sections:
written_sections.append(v.section)
conf_obj.add_section(v.section)
conf_obj.set(v.section, k, v.outconv(v.value))
with open(path, "w") as stream:
conf_obj.write(stream)
class PyChmViewerConfig(ConfigMapper):
"""The through the whole pyalpmm library used config class, usually there
should be an instance of it around as attribute from a Session instance
"""
defaultEncoding = StringConfigItem("userconfig", "gb18030")
sessionRestore = YesNoConfigItem("userconfig", True)
openRemoteURL = YesNoConfigItem("userconfig", True)
fontfamily = StringConfigItem("userconfig", "")
fontsize = IntegerConfigItem("userconfig", 10)
lastdir = StringConfigItem("userdata", ".")
def __init__(self, path):
try:
stream = open(path)
except IOError:
stream = None
#with open(path) as stream:
super(PyChmViewerConfig, self).__init__( stream )
self.path = path
def save_into_file(self, path=None):
path = path or self.path
super(PyChmViewerConfig, self).save_into_file(path)
def __str__(self):
"""Showing all config options"""
reports = "Showing all Configuration options:\n"
reports += "--------------------------------\n"
for key, value in self.config_items.items():
reports += "{0:20} = {1}\n".format(key, value)
return reports