diff options
Diffstat (limited to 'dodai/config')
-rw-r--r-- | dodai/config/__init__.py | 64 | ||||
-rw-r--r-- | dodai/config/db/__init__.py | 179 | ||||
-rw-r--r-- | dodai/config/db/sa.py | 64 | ||||
-rw-r--r-- | dodai/config/file.py | 116 | ||||
-rw-r--r-- | dodai/config/log.py | 150 | ||||
-rw-r--r-- | dodai/config/option.py | 61 | ||||
-rw-r--r-- | dodai/config/sections.py | 172 |
7 files changed, 806 insertions, 0 deletions
diff --git a/dodai/config/__init__.py b/dodai/config/__init__.py new file mode 100644 index 0000000..aac9be7 --- /dev/null +++ b/dodai/config/__init__.py | |||
@@ -0,0 +1,64 @@ | |||
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 | |||
20 | class Config(object): | ||
21 | |||
22 | def __call__(self): | ||
23 | obj = ConfigResults() | ||
24 | if hasattr(self, 'vars'): | ||
25 | for key, val in self.vars.items(): | ||
26 | setattr(obj, key, val) | ||
27 | return obj | ||
28 | |||
29 | def set(self, key, val): | ||
30 | if not hasattr(self, 'vars'): | ||
31 | self.vars = {} | ||
32 | self.vars[key] = val | ||
33 | |||
34 | def options(self): | ||
35 | if not hasattr(self, '_options'): | ||
36 | from dodai.config.option import ConfigOption | ||
37 | self._options = ConfigOption() | ||
38 | return self._options | ||
39 | |||
40 | def files(self): | ||
41 | if not hasattr(self, '_files'): | ||
42 | from dodai.config.file import ConfigFile | ||
43 | self._files = ConfigFile() | ||
44 | return self._files | ||
45 | |||
46 | def logs(self): | ||
47 | if not hasattr(self, '_logs'): | ||
48 | from dodai.config.log import ConfigLog | ||
49 | self._logs = ConfigLog() | ||
50 | return self._logs | ||
51 | |||
52 | def dbs(self): | ||
53 | if not hasattr(self, '_dbs'): | ||
54 | from dodai.config.db import ConfigDb | ||
55 | self._dbs = ConfigDb() | ||
56 | return self._dbs | ||
57 | |||
58 | |||
59 | |||
60 | |||
61 | |||
62 | |||
63 | class ConfigResults(object): | ||
64 | pass | ||
diff --git a/dodai/config/db/__init__.py b/dodai/config/db/__init__.py new file mode 100644 index 0000000..fa510ac --- /dev/null +++ b/dodai/config/db/__init__.py | |||
@@ -0,0 +1,179 @@ | |||
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 ConfigDb(object): | ||
19 | |||
20 | def __init__(self): | ||
21 | self.connections = {} | ||
22 | self._handlers = {} | ||
23 | from dodai.config.db.sa import Sa | ||
24 | self.register_handler('sa', Sa) | ||
25 | |||
26 | def register_handler(self, name, obj): | ||
27 | self._handlers[name] = [obj, None] | ||
28 | |||
29 | def add_config(self, config_parser=None): | ||
30 | if config_parser: | ||
31 | if hasattr(config_parser, 'sections') and \ | ||
32 | hasattr(config_parser, 'options'): | ||
33 | config_obj = ConfigDbFile(config_parser) | ||
34 | self._add_connections(config_obj) | ||
35 | else: | ||
36 | raise NotConfigParserObject() | ||
37 | |||
38 | def _add_connections(self, config_obj): | ||
39 | connections = config_obj() | ||
40 | for name, obj in connections.items(): | ||
41 | self.connections[name] = obj | ||
42 | |||
43 | def load(self, name): | ||
44 | if name in self.connections: | ||
45 | connection = self.connections[name] | ||
46 | if connection.db_obj: | ||
47 | return connection.db_obj | ||
48 | else: | ||
49 | handler = self._load_handler(connection.handler) | ||
50 | db_obj = handler.load(connection) | ||
51 | self.connections[name].db_obj = db_obj | ||
52 | return db_obj | ||
53 | |||
54 | def _load_handler(self, name): | ||
55 | if name in self._handlers: | ||
56 | handler = self._handlers[name] | ||
57 | cls = handler[0] | ||
58 | obj = handler[1] | ||
59 | if not obj: | ||
60 | obj = cls() | ||
61 | self._handlers[name] = [cls, obj] | ||
62 | return obj | ||
63 | raise UnknownHandlerException(name) | ||
64 | |||
65 | |||
66 | |||
67 | class ConfigDbFile(object): | ||
68 | |||
69 | OPTIONS_REQUIRED = [ | ||
70 | ['protocol', 'hostname', 'port', 'username', 'password','database'], | ||
71 | ['protocol', 'filename'] | ||
72 | ] | ||
73 | OPTIONS_EXTRA = ['protocol_extra', 'handler'] | ||
74 | DEFAULT_HANDLER = 'sa' | ||
75 | |||
76 | def __init__(self, config_parser): | ||
77 | self.parser = config_parser | ||
78 | self._options = self._all_options() | ||
79 | self.connections = {} | ||
80 | |||
81 | def __call__(self): | ||
82 | if not self.connections: | ||
83 | for section in self.parser.sections(): | ||
84 | if self._is_valid(section): | ||
85 | obj = self._build_connection(section) | ||
86 | self.connections[obj.name] = obj | ||
87 | return self.connections | ||
88 | |||
89 | def _all_options(self): | ||
90 | out = [] | ||
91 | for option_group in self.OPTIONS_REQUIRED: | ||
92 | for option in option_group: | ||
93 | if option not in out: | ||
94 | out.append(option) | ||
95 | for option in self.OPTIONS_EXTRA: | ||
96 | if option not in out: | ||
97 | out.append(option) | ||
98 | return out | ||
99 | |||
100 | def _is_valid(self, section): | ||
101 | for option_group in self.OPTIONS_REQUIRED: | ||
102 | total = len(option_group) | ||
103 | count = 0 | ||
104 | for option in option_group: | ||
105 | if option in self.parser.options(section): | ||
106 | value = self.parser.get(section, option) | ||
107 | if value: | ||
108 | count += 1 | ||
109 | if count >= total: | ||
110 | return True | ||
111 | return False | ||
112 | |||
113 | def _build_connection(self, section): | ||
114 | obj = ConfigDbConnection() | ||
115 | for option in self._options: | ||
116 | obj.name = section | ||
117 | if self.parser.has_option(section, option): | ||
118 | value = self.parser.get(section, option) | ||
119 | setattr(obj, option, value) | ||
120 | if not hasattr(obj, 'handler') or not obj.handler: | ||
121 | obj.handler = self.DEFAULT_HANDLER | ||
122 | return obj | ||
123 | |||
124 | |||
125 | class BaseConfigDb(object): | ||
126 | |||
127 | PROTOCOLS = ['postgresql', 'mysql', 'sqlite', 'mssql', 'oracle'] | ||
128 | |||
129 | def _clean(self, obj): | ||
130 | obj.protocol = self._clean_protocol(obj.protocol) | ||
131 | if hasattr(obj, 'port'): | ||
132 | obj.port = self._clean_port(obj.port) | ||
133 | |||
134 | def _clean_protocol(self, data): | ||
135 | data = data.lower() | ||
136 | if data in ('postgres', 'postgre'): | ||
137 | data = 'postgresql' | ||
138 | if data not in self.PROTOCOLS: | ||
139 | raise InvalidProtocolException(data) | ||
140 | else: | ||
141 | return data | ||
142 | |||
143 | def _clean_port(self, data): | ||
144 | try: | ||
145 | data = int(data) | ||
146 | except ValueError: | ||
147 | data = None | ||
148 | except TypeError: | ||
149 | data = None | ||
150 | if data: | ||
151 | if data <1 or data > 65535: | ||
152 | raise InvalidPortException(data) | ||
153 | return data | ||
154 | |||
155 | |||
156 | class ConfigDbConnection(object): | ||
157 | |||
158 | def __init__(self): | ||
159 | self.db_obj = None | ||
160 | |||
161 | |||
162 | class NotConfigParserObject(Exception): | ||
163 | pass | ||
164 | |||
165 | |||
166 | class InvalidProtocolException(Exception): | ||
167 | pass | ||
168 | |||
169 | |||
170 | class InvalidPortException(Exception): | ||
171 | pass | ||
172 | |||
173 | |||
174 | class UnknownHandlerException(Exception): | ||
175 | pass | ||
176 | |||
177 | |||
178 | class UnknownConnectionException(Exception): | ||
179 | pass | ||
diff --git a/dodai/config/db/sa.py b/dodai/config/db/sa.py new file mode 100644 index 0000000..06adf9c --- /dev/null +++ b/dodai/config/db/sa.py | |||
@@ -0,0 +1,64 @@ | |||
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 | from dodai.config.db import BaseConfigDb | ||
19 | from dodai.db import Db | ||
20 | |||
21 | class Sa(BaseConfigDb): | ||
22 | |||
23 | |||
24 | def load(self, obj): | ||
25 | from sqlalchemy.orm import sessionmaker | ||
26 | self._clean(obj) | ||
27 | db = Db() | ||
28 | db.engine = self._build_engine(obj) | ||
29 | Session = sessionmaker(bind=db.engine) | ||
30 | db.session = Session() | ||
31 | db.name = obj.name | ||
32 | db.protocol = obj.protocol | ||
33 | if hasattr(obj, 'filename') and obj.filename: | ||
34 | db.filename = obj.filename | ||
35 | else: | ||
36 | db.hostname = obj.hostname | ||
37 | if hasattr(obj, 'port') and obj.port: | ||
38 | db.port = obj.port | ||
39 | db.database = obj.database | ||
40 | return db | ||
41 | |||
42 | def _build_connection_string(self, obj): | ||
43 | out = [] | ||
44 | out.append('{db.protocol}') | ||
45 | if hasattr(obj, 'protocol_extra') and obj.protocol_extra: | ||
46 | out.append('+{db.protocol_extra}') | ||
47 | out.append('://') | ||
48 | if hasattr(obj, 'filename') and obj.filename: | ||
49 | out.append('{db.filename}') | ||
50 | else: | ||
51 | out.append('{db.username}:{db.password}@') | ||
52 | out.append('{db.hostname}') | ||
53 | if hasattr(obj, 'port') and obj.port: | ||
54 | out.append(':{db.port}') | ||
55 | out.append('/{db.database}') | ||
56 | out = ''.join(out) | ||
57 | out = out.format(db=obj) | ||
58 | return out | ||
59 | |||
60 | def _build_engine(self, obj): | ||
61 | from sqlalchemy import create_engine | ||
62 | connection_string = self._build_connection_string(obj) | ||
63 | db_obj = create_engine(connection_string) | ||
64 | return db_obj | ||
diff --git a/dodai/config/file.py b/dodai/config/file.py new file mode 100644 index 0000000..5cab6f8 --- /dev/null +++ b/dodai/config/file.py | |||
@@ -0,0 +1,116 @@ | |||
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 os | ||
20 | import ConfigParser | ||
21 | |||
22 | class ConfigFile(object): | ||
23 | |||
24 | def __init__(self): | ||
25 | self._files = [] | ||
26 | self._parser = None | ||
27 | self.files_loaded = [] | ||
28 | self._dir = None | ||
29 | |||
30 | def set_directory(self, path): | ||
31 | """ Sets the direcory where files will be looked for | ||
32 | raises: InvalidDirectoryException or DirectoryDoesNotExistException | ||
33 | """ | ||
34 | if os.path.isdir(path): | ||
35 | self._dir = path | ||
36 | else: | ||
37 | if os.path.isfile(path): | ||
38 | raise InvalidDirectoryException(path) | ||
39 | else: | ||
40 | raise DirectoryDoesNotExistException(path) | ||
41 | |||
42 | def get_directory(self): | ||
43 | """ Returns the directory where files will be looked for | ||
44 | """ | ||
45 | return self._dir | ||
46 | |||
47 | def add_file(self, path): | ||
48 | """ Adds a full file path with the given path (list or string) | ||
49 | raises: FileDoesNotExistException | ||
50 | """ | ||
51 | if isinstance(path, list): | ||
52 | for file_ in path: | ||
53 | self._add_file(file_) | ||
54 | else: | ||
55 | if path not in self._files: | ||
56 | self._add_file(path) | ||
57 | |||
58 | def _add_file(self, path): | ||
59 | """ Adds the given file path file to the object if the filepath | ||
60 | doesn't already exist | ||
61 | """ | ||
62 | if os.path.isfile(path): | ||
63 | if path not in self._files: | ||
64 | self._files.append(path) | ||
65 | else: | ||
66 | raise FileDoesNotExistException(path) | ||
67 | |||
68 | def get_files(self): | ||
69 | """ Returns a list of files that were added to this object | ||
70 | """ | ||
71 | return self._files | ||
72 | |||
73 | def parser(self): | ||
74 | """ Returns a ConfigParser.ConfigParser object with files loaded | ||
75 | raises: NoFilesToLoadException | ||
76 | """ | ||
77 | self._reset_parser() | ||
78 | if not self._parser: | ||
79 | if not self._files: | ||
80 | raise NoFilesToLoadException() | ||
81 | self._parser = ConfigParser.ConfigParser() | ||
82 | self.files_loaded = self._parser.read(self._files) | ||
83 | return self._parser | ||
84 | |||
85 | def load(self, name): | ||
86 | """ Takes the given name and merges it with the object's directory | ||
87 | then adds the path to the object | ||
88 | """ | ||
89 | if not self._dir: | ||
90 | raise DirectoryNotSetException() | ||
91 | else: | ||
92 | path = os.path.join(self._dir, name) | ||
93 | self.add_file(path) | ||
94 | |||
95 | def _reset_parser(self): | ||
96 | """ Resets the _parser property if the files_loaded does not equal | ||
97 | the files assigned to this object | ||
98 | """ | ||
99 | if self._parser: | ||
100 | if self.files_loaded != self._files: | ||
101 | self._parser = None | ||
102 | |||
103 | class NoFilesToLoadException(Exception): | ||
104 | pass | ||
105 | |||
106 | class DirectoryNotSetException(Exception): | ||
107 | pass | ||
108 | |||
109 | class InvalidDirectoryException(Exception): | ||
110 | pass | ||
111 | |||
112 | class DirectoryDoesNotExistException(Exception): | ||
113 | pass | ||
114 | |||
115 | class FileDoesNotExistException(Exception): | ||
116 | pass | ||
diff --git a/dodai/config/log.py b/dodai/config/log.py new file mode 100644 index 0000000..fdb5c93 --- /dev/null +++ b/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/dodai/config/option.py b/dodai/config/option.py new file mode 100644 index 0000000..0561881 --- /dev/null +++ b/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/dodai/config/sections.py b/dodai/config/sections.py new file mode 100644 index 0000000..feb59be --- /dev/null +++ b/dodai/config/sections.py | |||
@@ -0,0 +1,172 @@ | |||
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 unicodedata | ||
19 | |||
20 | class ConfigSections(object): | ||
21 | """ | ||
22 | An iterable object that contains ConfigSection objects | ||
23 | |||
24 | """ | ||
25 | |||
26 | def __init__(self, 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. | ||
40 | |||
41 | """ | ||
42 | self._string_object = string_object or None | ||
43 | self._sections = {} | ||
44 | |||
45 | def __call__(self, parser): | ||
46 | """ | ||
47 | Parses the given parser object into this object's sections. | ||
48 | |||
49 | parser: The actual parser object that is used to | ||
50 | get the sections. This object must have | ||
51 | the sections(), options() and get() | ||
52 | methods. A good object to use is the native | ||
53 | ConfigParse object. However, you can create | ||
54 | your own | ||
55 | |||
56 | """ | ||
57 | self._build_sections(parser) | ||
58 | |||
59 | def _build_sections(self, parser): | ||
60 | # Builds ConfigSection objects from the parser object | ||
61 | |||
62 | for section_name in parser.sections(): | ||
63 | section = self.get_section(section_name) | ||
64 | self._build_options(parser, section_name, section) | ||
65 | |||
66 | def _build_options(self, parser, section_name, section): | ||
67 | # Adds the options to the section object | ||
68 | |||
69 | for key in parser.options(section_name): | ||
70 | key = unicode(self._build_string_object(key)) | ||
71 | value = self._build_string_object(parser.get(section_name, key)) | ||
72 | setattr(section, key, value) | ||
73 | |||
74 | def _build_string_object(self, data): | ||
75 | if self._string_object: | ||
76 | return self._string_object(data) | ||
77 | else: | ||
78 | return data | ||
79 | |||
80 | def get_section(self, section_name): | ||
81 | """ | ||
82 | Returns a ConfigSection object from this object's section | ||
83 | dictionary or creates a new ConfigSection object, which is | ||
84 | stored int this object's section dictionary then is returned | ||
85 | |||
86 | """ | ||
87 | section_name = unicode(self._build_string_object(section_name)) | ||
88 | if section_name in self._sections: | ||
89 | return self._sections[section_name] | ||
90 | else: | ||
91 | section = ConfigSection(section_name) | ||
92 | self._sections[section_name] = section | ||
93 | return section | ||
94 | |||
95 | def __getitem__(self, key): | ||
96 | key = normalize_key(key) | ||
97 | return self._sections[key] | ||
98 | |||
99 | def __getattr__(self, key): | ||
100 | key = normalize_key(key) | ||
101 | try: | ||
102 | out = self._sections[key] | ||
103 | except KeyError: | ||
104 | return getattr(self._sections, key) | ||
105 | else: | ||
106 | return out | ||
107 | |||
108 | def __iter__(self, *args, **kargs): | ||
109 | return self._sections.__iter__(*args, **kargs) | ||
110 | |||
111 | |||
112 | def __len__(self): | ||
113 | return len(self._sections) | ||
114 | |||
115 | class ConfigSection(object): | ||
116 | """ | ||
117 | A generic object to hold keys and values primarily from a config file | ||
118 | |||
119 | """ | ||
120 | def __init__(self, title): | ||
121 | """ | ||
122 | Holds keys and values primarily from a section of a config file | ||
123 | |||
124 | title: The title of the section of the config file | ||
125 | |||
126 | """ | ||
127 | self.___title___ = title | ||
128 | self.___options___ = {} | ||
129 | |||
130 | |||
131 | def get_title(self): | ||
132 | """ | ||
133 | Returns the title of the section | ||
134 | |||
135 | """ | ||
136 | return self.___title___ | ||
137 | |||
138 | |||
139 | def __setattr__(self, key, value): | ||
140 | if key.startswith('___') and key.endswith('___'): | ||
141 | object.__setattr__(self, key, value) | ||
142 | else: | ||
143 | key = normalize_key(key) | ||
144 | if self.___options___.has_key(key): | ||
145 | self.___options___[key] = value | ||
146 | else: | ||
147 | dict.__setitem__(self.___options___, key, value) | ||
148 | |||
149 | def __getattr__(self, key): | ||
150 | if key.startswith('___') and key.endswith('___'): | ||
151 | return self.__dict__[key] | ||
152 | else: | ||
153 | key = normalize_key(key) | ||
154 | try: | ||
155 | out = self.___options___[key] | ||
156 | except KeyError: | ||
157 | return getattr(self.___options___, key) | ||
158 | else: | ||
159 | return out | ||
160 | |||
161 | def __getitem__(self, key): | ||
162 | key = normalize_key(key) | ||
163 | return self.___options___[key] | ||
164 | |||
165 | def __iter__(self, *args, **kargs): | ||
166 | return self.___options___.__iter__(*args, **kargs) | ||
167 | |||
168 | |||
169 | def normalize_key(key): | ||
170 | key = unicode(key) | ||
171 | key = unicodedata.normalize('NFC', key) | ||
172 | return key | ||