diff options
Diffstat (limited to 'lib/dodai/config')
-rw-r--r-- | lib/dodai/config/__init__.py | 76 | ||||
-rw-r--r-- | lib/dodai/config/databases/__init__.py | 285 | ||||
-rw-r--r-- | lib/dodai/config/databases/sa.py | 90 | ||||
-rw-r--r-- | lib/dodai/config/files.py | 106 | ||||
-rw-r--r-- | lib/dodai/config/log.py | 150 | ||||
-rw-r--r-- | lib/dodai/config/option.py | 61 | ||||
-rw-r--r-- | lib/dodai/config/sections.py | 117 |
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 | |||
19 | class 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 | |||
75 | class 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 | |||
19 | from dodai.exception import DatabaseEmptyOptionException | ||
20 | from dodai.exception import DatabasePortException | ||
21 | from dodai.exception import DatabaseHostnameException | ||
22 | from dodai.exception import DatabaseProtocolException | ||
23 | from dodai.exception import DatabaseConnectionException | ||
24 | from dodai.exception import UnknownDatabaseConnectionException | ||
25 | from dodai.tools import list_to_english | ||
26 | |||
27 | class 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 | |||
102 | class 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 | |||
128 | class 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 | |||
186 | class 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 | |||
238 | class 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 | |||
273 | class 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 | |||
18 | class 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 | |||
18 | import os | ||
19 | import ConfigParser | ||
20 | from dodai.config.sections import ConfigSections | ||
21 | from dodai.exception import InvalidConfigParser | ||
22 | from dodai.exception import FileDoesNotExist | ||
23 | from dodai.exception import FileIsDirectory | ||
24 | |||
25 | class 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 | |||
19 | import logging | ||
20 | import logging.handlers | ||
21 | import os | ||
22 | |||
23 | class 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 | |||
141 | class NoDirectoryExistException(Exception): | ||
142 | pass | ||
143 | |||
144 | |||
145 | class DirectoryNotSetException(Exception): | ||
146 | pass | ||
147 | |||
148 | |||
149 | class 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 | |||
19 | from optparse import OptionParser | ||
20 | |||
21 | class 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 | |||
19 | class 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) | ||