root/trunk/config.py

Revision 264, 10.8 kB (checked in by marc, 1 year ago)

Code cleanup

  • Property svn:keywords set to Id Rev
Line 
1 #! /usr/bin/env python
2 # -*- coding: utf8 -*-
3 #
4 # Itaka is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # any later version.
8 #
9 # Itaka is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with Itaka; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 #
18 # Copyright 2003-2007 Marc E.
19 # http://itaka.jardinpresente.com.ar
20 #
21 # $Id$
22
23 """ Itaka configuration engine """
24
25 __version__ = '1.0'
26 __revision__ = '$Rev$'
27
28 import os
29 import sys
30 import ConfigParser
31 import shutil
32 import traceback
33
34 #: Availability of libnotify
35 notify_available = False
36
37 try:
38     import pynotify
39     notify_available = True
40
41     if not pynotify.init('Itaka'):
42         print_warning(_('Pynotify module is failing, disabling notifications'))
43         notify_available = False
44 except ImportError:
45     print_warning(_('Pynotify module is missing, disabling notifications'))
46     notify_available = False
47
48 config_instance = ConfigParser.ConfigParser()
49 image_dir = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'share/images/')
50 system = os.name
51
52 #: To be changed on install to specify where the installed files actually are
53 image_prefix = '/usr/share/itaka/images/'
54 if os.path.exists(image_prefix):
55     image_dir = image_prefix
56
57 if not os.path.exists(image_dir):
58     print_error(_('Could not find images directory %s' % (image_dir)))
59     sys.exit(1)
60
61 #: Save path for screenshots (system-specific specified later on)
62 save_path = os.getcwd()
63
64 """ Console output verbosity
65 'normal' is for all normal operation mesages and warnings (not including errors)
66 'debug' is for all messages through self.console.debug
67 'quiet' is to quiet all errors and warnings. (totally quiet is in conjunction
68 with 'normal' = False, which quiets normal messages too)
69 """
70 console_verbosity = {'normal': False, 'debug': False, 'quiet': False}
71
72 #: Globally acessable configuration values
73 configuration_values = {}
74
75 if os.environ.get('HOME'):
76     save_path = os.path.join(os.environ.get('HOME'), '.itaka')
77 else:
78     save_path = os.environ.get('TMP') or os.environ.get('TEMP')
79
80 #: Default HTML headers and footers for the server.
81 # Putting <meta http-equiv="Refresh" content="5; url=http://localhost:8000"> is very useful for debugging
82 head_html = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
83 <html>
84 <head>
85 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
86 <link rel="icon" href="/favicon.ico" type="image/x-icon">
87 <title>Itaka</title>
88 </head>
89 <body>
90 <div id="main">
91 '''
92
93 footer_html = '''
94 </div>
95 </body>
96 </html>'''
97
98 class ConfigParser:
99     """
100     Itaka configuration engine
101     """
102
103     def __init__(self, arguments=1):
104         """
105         Configuration engine constructor. It also handles whether the
106         L{console_verbosity} setting is set to debug.
107
108         @type arguments: tuple
109         @param arguments: A tuple of sys.argv
110         """
111
112         if len(arguments) > 1 and arguments[-1] in ('-d', '--debug'):
113             global console_verbosity
114             console_verbosity = {'normal': True, 'debug': True, 'quiet': False}
115             print_m(_('Initializing in debug mode'))
116
117         #: Default configuration sections and values
118         self.default_options = (
119                 {'server': (
120                     ('port', 8000), ('authentication', False),
121                     ('username', 'user'), ('password', 'password'),
122                     ('notify', notify_available)
123                 )},
124
125                 {'screenshot': (
126                     ('format', 'jpeg'), ('quality', 30), ('path', save_path),
127                     ('currentwindow', False), ('scale', False),
128                     ('scalepercent', 100)
129                 )},
130                
131                 {'html': (
132                     ('html', '<img src="screenshot" alt="If you are seeing this message it means there was an error in Itaka or you are using a text-only browser.">'),
133                     ('authfailure', '<p><strong>Sorry, but you cannot access this resource without authorization.</strong></p>')
134                 )}
135         )
136
137     def load(self):
138         """
139         Set up and load configuration
140
141         @rtype: dict
142         @return: Dictionary of configuration values.
143         """
144
145         self.config_file = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "itaka.conf")
146
147         global configuration_values
148         configuration_values = {}
149
150         # Check routine
151         if system in ('posix'):
152             if not (os.path.exists(os.path.join(os.environ['HOME'], '.itaka/itaka.conf'))):
153                 self.create(os.path.join(os.environ['HOME'], '.itaka/itaka.conf'))
154             else:
155                 self.config_file = os.path.join(os.environ['HOME'], '.itaka/itaka.conf')
156         elif (system == 'nt'):
157             if not (os.path.exists(os.path.join(os.environ['APPDATA'], 'itaka/itaka.ini'))):
158                 self.create(os.path.join(os.environ['APPDATA'], 'itaka/itaka.ini'))
159             else:
160                 self.config_file = os.path.join(os.environ['APPDATA'], 'itaka/itaka.ini')
161         else:
162             # Generic path
163             if not os.path.exists(self.config_file):
164                 self.create(self.config_file)
165
166         # Read and assign values from the configuration file
167         try:
168             config_instance.read(self.config_file)
169             if console_verbosity['normal']:
170                 print_m(_('Loaded configuration %s' % (self.config_file)))
171
172         except:
173             if console_verbosity['normal']: print_error(_('Could not read configuration file (%s)' % (self.config_file)))
174             if console_verbosity['debug']: traceback.print_exc()
175
176         # Get values as a dict and return it
177         for section in config_instance.sections():
178             configuration_values[section] = dict(config_instance.items(section))
179             # Convert 'False' and 'True' into booleans, and numbers into ints
180             # Add config options that are not there
181             for option, value in configuration_values[section].iteritems():
182                 if value.strip() == 'True':
183                     configuration_values[section][option] = True
184                 elif value.strip() == 'False':
185                     configuration_values[section][option] = False
186                 elif value.isdigit():
187                     configuration_values[section][option] = int(value)
188
189         # Compare it to our default configuration set, to see if there is anything missing
190         # This is useful for updates, and corrupted files.
191         # NOTE: The setting of values[section][key] here is purely pragmatical, so we
192         # dont have to reload
193         brokenwarning = False
194         for configdict in self.default_options:
195             for section in configdict:
196                 if not configuration_values.has_key(section):
197                     if not console_verbosity['quiet'] and not brokenwarning:
198                         print_warning(_('Detected old or broken configuration file. Fixing'))
199                         brokenwarning = True
200                     config_instance.add_section(section)
201                     configuration_values[section] = {}
202                     for keyset in configdict[section]:
203                         key, val = keyset
204                         self.update(section, key, val)
205                         configuration_values[section][key] = val
206                 else:
207                     # Check if all the key:vals are in the section
208                     for keyset in configdict[section]:
209                         key, val = keyset
210                         if not configuration_values[section].has_key(key):
211                             if not console_verbosity['quiet'] and not brokenwarning:
212                                 print_warning(_('Detected old or broken configuration file. Fixing'))
213                             self.update(section, key, val)
214                             configuration_values[section][key] = val
215                             brokenwarning = True
216         return configuration_values
217
218     def save(self, valuesdict):
219         """
220         Saves a dictionary containing the configuration
221
222         @type valuesdict: dict
223         @param valuesdict: Dictionary of configuration
224         """
225
226         # Unpack the dict into section, option, value
227         for section in valuesdict.keys():
228             for key, value in valuesdict[section].items():
229                 config_instance.set(section, key, value)
230
231         # Save
232         try:
233             config_instance.write(open(self.config_file, 'w'))
234             if console_verbosity['normal']: print_m(_('Saving configuration')) 
235         except:         
236             if not console_verbosity['quiet']: print_error(_('Could not write configuration file %s' % (self.config_file)))
237             if console_verbosity['debug']: traceback.print_exc()
238
239     def update(self, section, key, value):
240         """
241         Update a specific key's value
242
243         @type section: str
244         @param section: String of the section of the key to update
245         @type key: str
246         @param key: String of the key to update
247         @type value: str/int/bool
248         @param value: Value of the key to update
249         """     
250
251         config_instance.set(section, key, value)
252
253         try:
254             config_instance.write(open(self.config_file, 'w'))
255             if console_verbosity['debug']: print_m(_('Updating configuration key %s to %s' % (key, value)))     
256         except:
257             if not console_verbosity['quiet']: print_error(_('Could not write configuration file %s' % (self.config_file)))
258             if console_verbosity['debug']: traceback.print_exc()
259
260     def create(self, path):
261         """
262         Create a configuration file from default values
263
264         @type path: str
265         @param path: Path to the configuration file
266         """
267
268         if console_verbosity['normal']: print_m(_('Creating default configuration'))
269
270         # Set default sections and options
271         for configdict in self.default_options:
272             for section in configdict:
273                 config_instance.add_section(section)
274                 for keyset in configdict[section]:
275                     key, val = keyset
276                     config_instance.set(section, key, val)
277
278         if not (os.path.exists(os.path.dirname(path))):
279             shutil.os.mkdir(os.path.dirname(path))
280
281         try:
282             config_instance.write(open(path, 'w'))
283         except:
284             if not console_verbosity['quiet']: print_error(_('Could not write configuration file %s' % (path)))
285             if console_verbosity['debug']: traceback.print_exc()
286
287         self.config_file = path
288
Note: See TracBrowser for help on using the browser.