aboutsummaryrefslogtreecommitdiff
path: root/lib/dodai/config
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dodai/config')
-rw-r--r--lib/dodai/config/__init__.py76
-rw-r--r--lib/dodai/config/databases/__init__.py285
-rw-r--r--lib/dodai/config/databases/sa.py90
-rw-r--r--lib/dodai/config/files.py106
-rw-r--r--lib/dodai/config/log.py150
-rw-r--r--lib/dodai/config/option.py61
-rw-r--r--lib/dodai/config/sections.py117
7 files changed, 885 insertions, 0 deletions
diff --git a/lib/dodai/config/__init__.py b/lib/dodai/config/__init__.py
new file mode 100644
index 0000000..e849f8e
--- /dev/null
+++ b/lib/dodai/config/__init__.py
@@ -0,0 +1,76 @@
1# Copyright (C) 2010 Leonard Thomas
2#
3# This file is part of Dodai.
4#
5# Dodai is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Dodai is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Dodai. If not, see <http://www.gnu.org/licenses/>.
17
18
19class Config(object):
20
21 def __call__(self):
22 obj = ConfigResults()
23 if hasattr(self, 'vars'):
24 for key, val in self.vars.items():
25 setattr(obj, key, val)
26 return obj
27
28 @property
29 def files(self):
30 if not hasattr(self, '_files'):
31 from dodai.config.files import ConfigFiles
32 from dodai.tools.odict import OrderedDict
33 from dodai.tools import Section
34 from dodai.tools.himo import String2Himo
35 from ConfigParser import ConfigParser
36 s2h = String2Himo()
37 self._files = ConfigFiles(OrderedDict, Section, s2h)
38 self._files.register_parser_object(ConfigParser)
39 return self._files
40
41 @property
42 def options(self):
43 if not hasattr(self, '_options'):
44 from dodai.config.option import ConfigOption
45 self._options = ConfigOption()
46 return self._options
47
48 @property
49 def logs(self):
50 if not hasattr(self, '_logs'):
51 from dodai.config.log import ConfigLog
52 self._logs = ConfigLog()
53 return self._logs
54
55 @property
56 def databases(self):
57 if not hasattr(self, '_databases'):
58 # Wire up the sqlalchemy objects to use in the sa object
59 # which will be used as the default database handler
60 from dodai.config.databases import ConfigDatabases
61 from dodai.config.databases.sa import Sa
62 from sqlalchemy.orm import sessionmaker
63 from sqlalchemy import create_engine
64 from dodai.db import Db
65 sa = Sa(create_engine, sessionmaker, Db)
66 self._databases = ConfigDatabases(sa, 'sa')
67 return self._databases
68
69 def set(self, key, val):
70 if not hasattr(self, 'vars'):
71 self.vars = {}
72 self.vars[key] = val
73
74
75class ConfigResults(object):
76 pass
diff --git a/lib/dodai/config/databases/__init__.py b/lib/dodai/config/databases/__init__.py
new file mode 100644
index 0000000..c91e77f
--- /dev/null
+++ b/lib/dodai/config/databases/__init__.py
@@ -0,0 +1,285 @@
1# Copyright (C) 2010 Leonard Thomas
2#
3# This file is part of Dodai.
4#
5# Dodai is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Dodai is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Dodai. If not, see <http://www.gnu.org/licenses/>.
17
18
19from dodai.exception import DatabaseEmptyOptionException
20from dodai.exception import DatabasePortException
21from dodai.exception import DatabaseHostnameException
22from dodai.exception import DatabaseProtocolException
23from dodai.exception import DatabaseConnectionException
24from dodai.exception import UnknownDatabaseConnectionException
25from dodai.tools import list_to_english
26
27class ConfigDatabases(object):
28 """An object that is used for creating database connection objects
29
30 """
31 def __init__(self, default_handler, default_name):
32 self.connections = {}
33 self._handlers = {}
34 DatabaseConnection.DEFAULT_HANDLER = default_name
35 self.add_handler(default_name, default_handler)
36
37 def add(self, sections):
38 """Adds a dictionary of sections to this object that could
39 be used for creating database connection objects. The dictionary
40 should be formatted as follows:
41
42 sections[name] = {option: value, option: value, ....}
43
44 The 'name' is the section name. It is an identifier that is used
45 to load the database:
46
47 The options and values can have the following:
48
49 protocol: The database engine (postgresql, mysql, sqlite,
50 oracle, mssql etc..)
51
52 hostname: The hostname or ip address of the database server
53
54 port: The port that should be used to connect to the
55 database server should be a number between 1
56 and 65535
57
58 username: The username used to connect to the database server
59
60 password: The password used to connect to the database server
61
62 database: The database to connect to once connected to the
63 server
64
65 schema: The schema that will be used. This option does
66 not do anything but will exist in the db object
67 for use
68
69 filename: The file path to the database file (sqlite)
70
71 protocol_extra: Used to tell the handler which python db engine
72 to use. For example one might want to tell
73 the sqlalchemy handler to use the psycopg2
74 python object.
75
76 handler: The handler used to build the orm object. Dodai
77 only works with sqlalchemy so the default 'sa'
78 is used.
79
80 """
81 for name, section in sections.items():
82 self.connections[name] = DatabaseConnection(section, name,
83 self._handlers)
84
85 def load(self, section_name):
86 """Returns a database connection object of the given section_name.
87 Throws Exceptions for any type of configuration errors or missing
88 configuration data
89
90 """
91 if section_name in self.connections:
92 return self.connections[section_name].load()
93 else:
94 raise UnknownDatabaseConnectionException(section_name)
95
96 def add_handler(self, name, handler):
97 """Adds the given handler and name to this objects handlers
98
99 """
100 self._handlers[name] = handler
101
102class DatabaseConnection(object):
103
104 DEFAULT_HANDLER = None
105
106 def __init__(self, section, name, handlers):
107 self.section = section
108 self.name = name
109 self.handlers = handlers
110 self.validate = DatabaseConnectionValidator.load(section, name)
111 self.obj = None
112
113 def load(self):
114 if not self.obj:
115 self.validate()
116 handler = self._get_handler()
117 self.obj = handler(self.section, self.name)
118 return self.obj
119
120 def _get_handler(self):
121 if self.section.has_key('handler'):
122 name = self.section['handler']
123 if name in self.handlers:
124 return self.handlers[name]
125 return self.handlers[self.DEFAULT_HANDLER]
126
127
128class DatabaseConnectionValidator(object):
129
130 def __init__(self, section, name, validators=[]):
131 self.section = section
132 self.name = name
133 self.validators = validators
134
135 def __call__(self):
136 return self.validate()
137
138 def validate(self):
139 """Validates the connection. Returns true if valid. Throws
140 DodaiDatabaseConnectionConfigurationError on any errors
141
142 """
143 raise NotImplementedError()
144
145 def _validate_option(self, name):
146 try:
147 value = self.section[name]
148 except KeyError:
149 value = None
150
151 if not value:
152 raise DatabaseEmptyOptionException(self.name, name)
153 return True
154
155 def _validate_protocol(self):
156
157 self._validate_option('protocol')
158
159 if self.section['protocol'] not in self.PROTOCOLS:
160 raise DatabaseProtocolException(self.name,
161 self.section['protocol'], self.DB_TYPE,
162 self.PROTOCOLS)
163 return True
164
165 @staticmethod
166 def load(section, name):
167 """Static method (factory) that loads the correct database
168 validation class.
169
170 Attributes:
171 section: Dictionary of key val connection information
172 name: String name of the section
173
174 """
175 action = None
176 validators = [DatabaseConnectionValidatorServer,
177 DatabaseConnectionValidatorFile]
178
179 for validator in validators:
180 obj = validator.load(section, name)
181 if obj:
182 return obj
183
184 return DatabaseConnectionValidatorUnknown(section, name, validators)
185
186class DatabaseConnectionValidatorServer(DatabaseConnectionValidator):
187
188 DB_TYPE = 'server'
189 REQUIRED = ['protocol', 'hostname', 'port', 'username', 'password',
190 'database']
191 PROTOCOLS = ['postgresql', 'mysql', 'mssql', 'oracle']
192
193 def _validate_port(self):
194 self._validate_option('port')
195
196 try:
197 port = int(self.section['port'])
198 except ValueError:
199 port = self.section['port']
200 else:
201 if port >=1 and port <=65535:
202 return True
203 raise DatabasePortException(self.name, port)
204
205 def validate(self):
206 """Validates the connection. Returns true if valid. Throws
207 DodaiDatabaseConnectionConfigurationError on any errors
208
209 """
210 self._validate_protocol()
211 self._validate_port()
212 self._validate_option('hostname')
213 self._validate_option('username')
214 self._validate_option('password')
215 self._validate_option('database')
216 return True
217
218 @classmethod
219 def load(cls, section, name):
220 """Return this validation class if it is possible that the
221 given connection information contains enough data to make
222 a database server connection.
223
224 Attributes:
225 section: Dictionary of key val connection information
226 name: String name of the section
227
228 """
229 if section.has_key('protocol'):
230 if section['protocol'] in cls.PROTOCOLS:
231 return cls(section, name)
232 keys = section.keys()
233 for key in keys:
234 if key in ['hostname', 'port']:
235 return cls(section, name)
236
237
238class DatabaseConnectionValidatorFile(DatabaseConnectionValidator):
239
240 DB_TYPE = 'file'
241 REQUIRED = ['protocol', 'filename']
242 PROTOCOLS = ['sqlite']
243
244 def validate(self):
245 """Validates the connection. Returns true if valid. Throws
246 DodaiDatabaseConnectionConfigurationError on any errors
247
248 """
249 self._validate_protocol()
250 self._validate_option('filename')
251 return True
252
253 @classmethod
254 def load(cls, section, name):
255 """Return this validation class if it is possible that the
256 given connection information contains enough data to make
257 a database file connection.
258
259 Attributes:
260 section: Dictionary of key val connection information
261 name: String name of the section
262
263 """
264 if section.has_key('protocol'):
265 if section['protocol'] in cls.PROTOCOLS:
266 return cls(section, name)
267 keys = section.keys()
268 for key in keys:
269 if key in ['filename']:
270 return cls(section, name)
271
272
273class DatabaseConnectionValidatorUnknown(DatabaseConnectionValidator):
274
275 DB_TYPE = 'unkonwn'
276 REQUIRED = []
277 PROTOCOLS = []
278
279 def validate(self):
280 """Validates the connection. Returns true if valid. Throws
281 DodaiDatabaseConnectionConfigurationError on any errors
282
283 """
284
285 raise DatabaseConnectionException(self.name, self.validators)
diff --git a/lib/dodai/config/databases/sa.py b/lib/dodai/config/databases/sa.py
new file mode 100644
index 0000000..eaf35ea
--- /dev/null
+++ b/lib/dodai/config/databases/sa.py
@@ -0,0 +1,90 @@
1# Copyright (C) 2010 Leonard Thomas
2#
3# This file is part of Dodai.
4#
5# Dodai is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Dodai is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Dodai. If not, see <http://www.gnu.org/licenses/>.
17
18class Sa(object):
19 """Callable object that will wire up sqlalchemy engine and
20 session objects
21
22 Attributes:
23 create_engine: The sqlalchemy create_engine object
24 session_maker: The sqlalchemy session_maker object
25 result_object: The object that will be populated with
26 all the connection information along with
27 the sqlalchemy objects
28
29 """
30 def __init__(self, create_engine, session_maker, result_object):
31 self._create_engine = create_engine
32 self._session_maker = session_maker
33 self._result_object = result_object
34
35 def __call__(self, section, name):
36 db = self._result_object()
37 db.name = name
38 db.protocol = section['protocol']
39
40 if section.has_key('protocol_extra') and section['protocol_extra']:
41 db.protocol_extra = section['protocol_extra']
42
43 if section.has_key('filename') and section['filename']:
44 db.filename = section['filename']
45
46 if section.has_key('port') and section['port']:
47 db.port = int(section['port'])
48
49 if section.has_key('database') and section['database']:
50 db.database = section['database']
51
52 if section.has_key('schema') and section['schema']:
53 db.schema = section['schema']
54
55 if section.has_key('username') and section['username']:
56 db.username = section['username']
57
58 if section.has_key('hostname') and section['hostname']:
59 db.hostname = section['hostname']
60
61 db.engine = self._build_engine(section)
62 db.session = self._build_session(db.engine)
63 return db
64
65 def _build_session(self, engine):
66 session = self._session_maker(bind=engine)
67 return session()
68
69 def _build_connection_string(self, section):
70 out = []
71 out.append('{section[protocol]}')
72 if section.has_key('protocol_extra') and section['protocol_extra']:
73 out.append('+{section[protocol_extra]}')
74 out.append('://')
75 if section.has_key('filename') and section['filename']:
76 out.append('/{section[filename]}')
77 else:
78 out.append('{section[username]}:{section[password]}@')
79 out.append('{section[hostname]}')
80 if section.has_key('port') and section['port']:
81 out.append(':{section[port]}')
82 out.append('/{section[database]}')
83 out = ''.join(out)
84 out = out.format(section=section)
85 return out
86
87 def _build_engine(self, section):
88 connection_string = self._build_connection_string(section)
89 db_obj = self._create_engine(connection_string)
90 return db_obj
diff --git a/lib/dodai/config/files.py b/lib/dodai/config/files.py
new file mode 100644
index 0000000..a27e4a2
--- /dev/null
+++ b/lib/dodai/config/files.py
@@ -0,0 +1,106 @@
1# Copyright (C) 2010 Leonard Thomas
2#
3# This file is part of Dodai.
4#
5# Dodai is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Dodai is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Dodai. If not, see <http://www.gnu.org/licenses/>.
17
18import os
19import ConfigParser
20from dodai.config.sections import ConfigSections
21from dodai.exception import InvalidConfigParser
22from dodai.exception import FileDoesNotExist
23from dodai.exception import FileIsDirectory
24
25class ConfigFiles(object):
26
27 REQUIRED_METHODS = ['read', 'sections', 'options', 'get']
28
29 def __init__(self, ordered_dict_object, section_object, string_object):
30
31 self._ordered_dict_object = ordered_dict_object
32 self._section_object = section_object
33 self._string_object = string_object
34 self.parser_objects = []
35 self.files = self._ordered_dict_object()
36 self.files_read = []
37
38 def register_parser_object(self, parser_object):
39 """Registers a config file parser with this object. Raises
40 InvalidConfigParser error if the parser does not have the 'read',
41 'sections', 'options' and 'get' methods.
42
43 """
44 if self.check_parser_object(parser_object):
45 self.parser_objects.append(parser_object)
46
47 def check_parser_object(self, parser_object):
48 """Checks the given parser object to insure it has all of the required
49 methods needed to parse files.
50
51 """
52 for name in self.REQUIRED_METHODS:
53 if not hasattr(parser_object, name):
54 raise InvalidConfigParser(self.REQUIRED_METHODS,
55 parser_object.__name__)
56 return True
57
58 def add(self, filename, encoding=None):
59 """Adds the given filename to this object to be parsed.
60
61 """
62 if os.path.exists(filename):
63 if os.path.isdir(filename):
64 raise FileIsDirectory(filename)
65 else:
66 filename = os.path.realpath(filename)
67 self.files[filename] = encoding
68 else:
69 raise FileDoesNotExist(filename)
70
71 def _read(self):
72 self.files_read = []
73 out = self._ordered_dict_object()
74 for filename, encoding in self.files.items():
75 error = False
76 for parser_obj in self.parser_objects:
77 try:
78 parser = parser_obj()
79 parser.read(filename)
80 except Exception, e:
81 error = True
82 else:
83 out[filename] = self._ordered_dict_object()
84 out[filename]['encoding'] = encoding
85 out[filename]['parser'] = parser
86 self.files_read.append(filename)
87 error = False
88 break
89 if error:
90 raise e
91 return out
92
93
94 def load(self):
95 """Returns a ConfigSections object, which acts like a dictionary,
96 that contains all parsed data.
97
98 """
99 files = self._read()
100 sections = ConfigSections(self._ordered_dict_object,
101 self._section_object, self._string_object)
102 for filename, data in files.items():
103 encoding = data['encoding']
104 parser = data['parser']
105 sections(parser, encoding)
106 return sections
diff --git a/lib/dodai/config/log.py b/lib/dodai/config/log.py
new file mode 100644
index 0000000..fdb5c93
--- /dev/null
+++ b/lib/dodai/config/log.py
@@ -0,0 +1,150 @@
1# Copyright (C) 2010 Leonard Thomas
2#
3# This file is part of Dodai.
4#
5# Dodai is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Dodai is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Dodai. If not, see <http://www.gnu.org/licenses/>.
17
18
19import logging
20import logging.handlers
21import os
22
23class ConfigLog(object):
24
25 LEVELS = {
26 logging.CRITICAL: [
27 'critical',
28 "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
29 "%(message)s"],
30 logging.ERROR: [
31 'error',
32 "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
33 "%(message)s"],
34 logging.WARNING: [
35 'warning',
36 "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
37 "%(message)s"],
38 logging.INFO: [
39 'info',
40 "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
41 "%(message)s"],
42 logging.DEBUG: [
43 'debug',
44 "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
45 "%(message)s"]
46 }
47
48 MAX_BYTES = 10485760
49 BACKUP_COUNT = 5
50 FILE_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
51 STDOUT_FORMAT = "%(message)s"
52
53 def __init__(self):
54 self.log_level = logging.CRITICAL
55 self.directory = None
56 self._levels = {}
57
58 def set_log_level(self, level):
59 try:
60 level = self._fetch_log_level(level)
61 except InvalidLevelException:
62 pass
63 else:
64 self.log_level = level
65
66 def set_directory(self, directory):
67 if os.path.isdir(directory):
68 self.directory = directory
69 else:
70 raise NoDirectoryExistException(directory)
71
72 def get_file_message_format(self, level):
73 if not self._levels:
74 self._levels = self.LEVELS
75 level = self._fetch_log_level(level)
76 return self._levels[level][1]
77
78 def get_screen_message_format(self, level):
79 if not self._levels:
80 self._levels = self.LEVELS
81 level = self._fetch_log_level(level)
82 return self._levels[level][2]
83
84 def _fetch_log_level(self, level):
85 out = None
86 if isinstance(level, str):
87 level = level.strip().lower()
88 if level in self.LEVELS:
89 out = level
90 else:
91 for key, items in self.LEVELS.items():
92 if level == items[0]:
93 out = key
94 if not out:
95 raise InvalidLevelException(level)
96 else:
97 return out
98
99 def _build_filepath(self, data):
100 data = os.path.normpath(data)
101 if data.startswith(os.path.sep):
102 dir = os.path.dirname(data)
103 if not os.path.isdir(dir):
104 raise NoDirectoryExistException(dir)
105 else:
106 if not self.directory:
107 raise DirectoryNotSetException()
108 else:
109 data = os.path.join(self.directory, data)
110 return data
111
112 def load(self, name):
113 log =logging.getLogger(name)
114 log.setLevel(self.log_level)
115 return log
116
117 def attach_file_handler(self, log, filename):
118 filepath = self._build_filepath(filename)
119 handler = logging.handlers.RotatingFileHandler(
120 filepath, maxBytes = self.MAX_BYTES,
121 backupCount=self.BACKUP_COUNT)
122 file_format = self.get_file_message_format(self.log_level)
123 format_obj = logging.Formatter(file_format)
124 handler.setFormatter(format_obj)
125 handler.setLevel(self.log_level)
126 log.addHandler(handler)
127
128 def attach_screen_handler(self, log, level=None):
129 if level:
130 level = self._fetch_log_level(level)
131 else:
132 level = self.log_level
133 message_format = self.get_screen_message_format(level)
134 handler = logging.StreamHandler()
135 handler.setLevel(level)
136 format_obj = logging.Formatter(message_format)
137 handler.setFormatter(format_obj)
138 log.addHandler(handler)
139
140
141class NoDirectoryExistException(Exception):
142 pass
143
144
145class DirectoryNotSetException(Exception):
146 pass
147
148
149class InvalidLevelException(Exception):
150 pass
diff --git a/lib/dodai/config/option.py b/lib/dodai/config/option.py
new file mode 100644
index 0000000..0561881
--- /dev/null
+++ b/lib/dodai/config/option.py
@@ -0,0 +1,61 @@
1# Copyright (C) 2010 Leonard Thomas
2#
3# This file is part of Dodai.
4#
5# Dodai is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Dodai is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Dodai. If not, see <http://www.gnu.org/licenses/>.
17
18
19from optparse import OptionParser
20
21class ConfigOption(object):
22
23 def __init__(self):
24
25 self.parser = OptionParser()
26 self._options = None
27 self._args = []
28
29 def get_args(self):
30 self._parse_args()
31 return self._args
32
33 def get_options(self):
34 self._parse_args()
35 return self._options
36
37 def _parse_args(self):
38 options, args = self.parser.parse_args()
39 self._options = options
40 self._args = args
41
42 def add_quiet(self):
43
44 self.parser.add_option("-q", "--quiet", dest="verbose", default=True,
45 action="store_false",
46 help="Don't print status messages to the screen")
47
48 def add_verbose(self):
49 self.parser.add_option("-v", "--verbose", dest="verbose",
50 action="store_true",
51 default=False, help="Print status messages to the screen")
52
53 def add_log_level(self, default='critical'):
54 self.parser.add_option("-l", "--log-level", dest="log_level",
55 default=default, help="Sets the log level")
56
57 def add_setup(self):
58 self.parser.add_option('', "--setup", dest="setup",
59 action="store_true", default=False,
60 help="run the setup which builds the config "\
61 "files.")
diff --git a/lib/dodai/config/sections.py b/lib/dodai/config/sections.py
new file mode 100644
index 0000000..d789a24
--- /dev/null
+++ b/lib/dodai/config/sections.py
@@ -0,0 +1,117 @@
1# Copyright (C) 2010 Leonard Thomas
2#
3# This file is part of Dodai.
4#
5# Dodai is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Dodai is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Dodai. If not, see <http://www.gnu.org/licenses/>.
17
18
19class ConfigSections(object):
20 """
21 An iterable object that contains ConfigSection objects
22
23 """
24
25 def __init__(self, ordered_dict_object, section_object,
26 string_object = None,):
27 """
28 Iterable object that handles the conversion of a config
29 parser object to a list of section objects.
30
31
32 string_object: This is an object (non instantiated or
33 callable) that the results of the config's
34 sections, and options will be stored in.
35 This enables you to store your values as a
36 custom object. A good object to use is the
37 dodai.tools.himo Himo object. If the
38 string_object is not given the default python
39 str() object will be used. The callable of
40 this object must also allow for encoding
41 to be passed in string_object(data, 'UTF-8')
42
43
44 """
45 self._string_object = string_object or ''
46 self._ordered_dict_object = ordered_dict_object
47 self._section_object = section_object
48 self._sections = self._ordered_dict_object()
49
50 def __call__(self, parser, encoding=None):
51 """
52 Parses the given parser object into this object's sections.
53
54 parser: The actual parser object that is used to
55 get the sections. This object must have
56 the sections(), options() and get()
57 methods. A good object to use is the native
58 ConfigParse object. However, you can create
59 your own
60
61 """
62 self._build_sections(parser, encoding)
63
64 def _build_sections(self, parser, encoding):
65 # Builds ConfigSection objects from the parser object
66
67 for section_name in parser.sections():
68 section = self.get_section(section_name, encoding)
69 self._build_options(parser, section_name, section, encoding)
70
71 def _build_options(self, parser, section_name, section, encoding):
72 # Adds the options to the section object
73
74 for key in parser.options(section_name):
75 value = self._build_string_object(parser.get(section_name, key),
76 encoding)
77 key = self._build_string_object(key, encoding)
78 section[key] = value
79
80 def _build_string_object(self, data, encoding=None):
81 if self._string_object:
82 return self._string_object(data, encoding)
83 else:
84 return data
85
86 def get_section(self, section_name, encoding=None):
87 """
88 Returns a Section (aka dict) object from this object's section
89 dictionary or creates a new ConfigSection object, which is
90 stored int this object's section dictionary then is returned
91
92 """
93 section_name = self._build_string_object(section_name, encoding)
94 if section_name in self._sections:
95 return self._sections[section_name]
96 else:
97 section = self._section_object(section_name)
98 self._sections[section_name] = section
99 return section
100
101 def __getitem__(self, key):
102 return self._sections[key]
103
104 def __getattr__(self, key):
105 try:
106 out = self._sections[key]
107 except KeyError:
108 return getattr(self._sections, key)
109 else:
110 return out
111
112 def __iter__(self, *args, **kargs):
113 return self._sections.__iter__(*args, **kargs)
114
115
116 def __len__(self):
117 return len(self._sections)