1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 # Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
29 import openerp.loglevels as loglevels
31 import openerp.release as release
33 class MyOption (optparse.Option, object):
34 """ optparse Option with two additional attributes.
36 The list of command line options (getopt.Option) is used to create the
37 list of the configuration file options. When reading the file, and then
38 reading the command line arguments, we don't want optparse.parse results
39 to override the configuration file values. But if we provide default
40 values to optparse, optparse will return them and we can't know if they
41 were really provided by the user or not. A solution is to not use
42 optparse's default attribute, but use a custom one (that will be copied
43 to create the default values of the configuration file).
46 def __init__(self, *opts, **attrs):
47 self.my_default = attrs.pop('my_default', None)
48 super(MyOption, self).__init__(*opts, **attrs)
50 #.apidoc title: Server Configuration Loader
54 from OpenSSL import SSL
57 return hasattr(socket, 'ssl') and hasattr(SSL, "Connection")
61 class configmanager(object):
62 def __init__(self, fname=None):
63 # Options not exposed on the command line. Command line options will be added
64 # from optparse's parser.
66 'admin_passwd': 'admin',
67 'csv_internal_sep': ',',
68 'login_message': False,
69 'publisher_warranty_url': 'http://services.openerp.com/publisher-warranty/',
74 # Not exposed in the configuration file.
75 self.blacklist_for_save = set(
76 ['publisher_warranty_url', 'load_language', 'root_path',
77 'init', 'save', 'config', 'update', 'stop_after_init'])
79 # dictionary mapping option destination (keys in self.options) to MyOptions.
83 self.config_file = fname
84 self.has_ssl = check_ssl()
86 self._LOGLEVELS = dict([(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'TEST', 'DEBUG', 'NOTSET')])
88 version = "%s %s" % (release.description, release.version)
89 self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
91 # Server startup config
92 group = optparse.OptionGroup(parser, "Common options")
93 group.add_option("-c", "--config", dest="config", help="specify alternate config file")
94 group.add_option("-s", "--save", action="store_true", dest="save", default=False,
95 help="save configuration to ~/.openerp_serverrc")
96 group.add_option("-i", "--init", dest="init", help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
97 group.add_option("-u", "--update", dest="update",
98 help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
99 group.add_option("--without-demo", dest="without_demo",
100 help="disable loading demo data for modules to be installed (comma-separated, use \"all\" for all modules). Requires -d and -i. Default is %default",
102 group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
103 help="Use this for big data importation, if it crashes you will be able to continue at the current state. Provide a filename to store intermediate importation states.")
104 group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
105 group.add_option("--addons-path", dest="addons_path",
106 help="specify additional addons paths (separated by commas).",
107 action="callback", callback=self._check_addons_path, nargs=1, type="string")
108 group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
109 parser.add_option_group(group)
112 group = optparse.OptionGroup(parser, "XML-RPC Configuration")
113 group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
114 help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
115 group.add_option("--xmlrpc-port", dest="xmlrpc_port", my_default=8069,
116 help="specify the TCP port for the XML-RPC protocol", type="int")
117 group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", my_default=True,
118 help="disable the XML-RPC protocol")
119 group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
120 help="Enable correct behavior when behind a reverse proxy")
121 parser.add_option_group(group)
124 title = "XML-RPC Secure Configuration"
126 title += " (disabled as ssl is unavailable)"
128 group = optparse.OptionGroup(parser, title)
129 group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
130 help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
131 group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
132 help="specify the TCP port for the XML-RPC Secure protocol", type="int")
133 group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
134 help="disable the XML-RPC Secure protocol")
135 group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
136 help="specify the certificate file for the SSL connection")
137 group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
138 help="specify the private key file for the SSL connection")
139 parser.add_option_group(group)
142 group = optparse.OptionGroup(parser, "NET-RPC Configuration")
143 group.add_option("--netrpc-interface", dest="netrpc_interface", my_default='',
144 help="specify the TCP IP address for the NETRPC protocol")
145 group.add_option("--netrpc-port", dest="netrpc_port", my_default=8070,
146 help="specify the TCP port for the NETRPC protocol", type="int")
147 group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=True,
148 help="disable the NETRPC protocol")
149 parser.add_option_group(group)
152 # TODO move to web addons after MetaOption merge
153 group = optparse.OptionGroup(parser, "Web interface Configuration")
154 group.add_option("--db-filter", dest="dbfilter", default='.*',
155 help="Filter listed database", metavar="REGEXP")
156 parser.add_option_group(group)
159 group = optparse.OptionGroup(parser, "Static HTTP service")
160 group.add_option("--static-http-enable", dest="static_http_enable", action="store_true", my_default=False, help="enable static HTTP service for serving plain HTML files")
161 group.add_option("--static-http-document-root", dest="static_http_document_root", help="specify the directory containing your static HTML files (e.g '/var/www/')")
162 group.add_option("--static-http-url-prefix", dest="static_http_url_prefix", help="specify the URL root prefix where you want web browsers to access your static HTML files (e.g '/')")
163 parser.add_option_group(group)
166 group = optparse.OptionGroup(parser, "Testing Configuration")
167 group.add_option("--test-file", dest="test_file", my_default=False,
168 help="Launch a YML test file.")
169 group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
170 help="If set, will save sample of all reports in this directory.")
171 group.add_option("--test-enable", action="store_true", dest="test_enable",
172 my_default=False, help="Enable YAML and unit tests.")
173 group.add_option("--test-commit", action="store_true", dest="test_commit",
174 my_default=False, help="Commit database changes performed by YAML or XML tests.")
175 parser.add_option_group(group)
178 group = optparse.OptionGroup(parser, "Logging Configuration")
179 group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
180 group.add_option("--no-logrotate", dest="logrotate", action="store_false", my_default=True, help="do not rotate the logfile")
181 group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
182 group.add_option('--log-handler', action="append", default=[':INFO'], my_default=[':INFO'], metavar="PREFIX:LEVEL", help='setup a handler at LEVEL for a given PREFIX. An empty PREFIX indicates the root logger. This option can be repeated. Example: "openerp.orm:DEBUG" or "werkzeug:CRITICAL" (default: ":INFO")')
183 group.add_option('--log-request', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.request:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.request:DEBUG')
184 group.add_option('--log-response', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.response:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.response:DEBUG')
185 group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.addons.web.common.http:DEBUG", help='shortcut for --log-handler=openerp.addons.web.common.http:DEBUG')
186 group.add_option('--log-sql', action="append_const", dest="log_handler", const="openerp.sql_db:DEBUG", help='shortcut for --log-handler=openerp.sql_db:DEBUG')
187 # For backward-compatibility, map the old log levels to something
189 levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
190 'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
191 group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
192 my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
194 parser.add_option_group(group)
197 group = optparse.OptionGroup(parser, "SMTP Configuration")
198 group.add_option('--email-from', dest='email_from', my_default=False,
199 help='specify the SMTP email address for sending email')
200 group.add_option('--smtp', dest='smtp_server', my_default='localhost',
201 help='specify the SMTP server for sending email')
202 group.add_option('--smtp-port', dest='smtp_port', my_default=25,
203 help='specify the SMTP port', type="int")
204 group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
205 help='specify the SMTP server support SSL or not')
206 group.add_option('--smtp-user', dest='smtp_user', my_default=False,
207 help='specify the SMTP username for sending email')
208 group.add_option('--smtp-password', dest='smtp_password', my_default=False,
209 help='specify the SMTP password for sending email')
210 parser.add_option_group(group)
212 group = optparse.OptionGroup(parser, "Database related options")
213 group.add_option("-d", "--database", dest="db_name", my_default=False,
214 help="specify the database name")
215 group.add_option("-r", "--db_user", dest="db_user", my_default=False,
216 help="specify the database user name")
217 group.add_option("-w", "--db_password", dest="db_password", my_default=False,
218 help="specify the database password")
219 group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
220 group.add_option("--db_host", dest="db_host", my_default=False,
221 help="specify the database host")
222 group.add_option("--db_port", dest="db_port", my_default=False,
223 help="specify the database port", type="int")
224 group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
225 help="specify the the maximum number of physical connections to posgresql")
226 group.add_option("--db-template", dest="db_template", my_default="template0",
227 help="specify a custom database template to create a new database")
228 parser.add_option_group(group)
230 group = optparse.OptionGroup(parser, "Internationalisation options",
231 "Use these options to translate OpenERP to another language."
232 "See i18n section of the user manual. Option '-d' is mandatory."
233 "Option '-l' is mandatory in case of importation"
235 group.add_option('--load-language', dest="load_language",
236 help="specifies the languages for the translations you want to be loaded")
237 group.add_option('-l', "--language", dest="language",
238 help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
239 group.add_option("--i18n-export", dest="translate_out",
240 help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
241 group.add_option("--i18n-import", dest="translate_in",
242 help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
243 group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
244 help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
245 group.add_option("--modules", dest="translate_modules",
246 help="specify modules to export. Use in combination with --i18n-export")
247 parser.add_option_group(group)
249 security = optparse.OptionGroup(parser, 'Security-related options')
250 security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
251 help="disable the ability to return the list of databases")
252 parser.add_option_group(security)
255 group = optparse.OptionGroup(parser, "Advanced options")
256 group.add_option("--cache-timeout", dest="cache_timeout", my_default=100000,
257 help="set the timeout for the cache system", type="int")
258 group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
259 group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
260 help="stop the server after its initialization")
261 group.add_option("-t", "--timezone", dest="timezone", my_default=False,
262 help="specify reference timezone for the server (e.g. Europe/Brussels")
263 group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
264 help="Force a limit on the maximum number of records kept in the virtual "
265 "osv_memory tables. The default is False, which means no count-based limit.",
267 group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
268 help="Force a limit on the maximum age of records kept in the virtual "
269 "osv_memory tables. This is a decimal value expressed in hours, "
270 "and the default is 1 hour.",
272 group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=4,
273 help="Maximum number of threads processing concurrently cron jobs.",
275 # TODO sensible default for the three following limits.
276 group.add_option("--virtual-memory-limit", dest="virtual_memory_limit", my_default=768 * 1024 * 1024,
277 help="Maximum allowed virtual memory per Gunicorn process. "
278 "When the limit is reached, any memory allocation will fail.",
280 group.add_option("--virtual-memory-reset", dest="virtual_memory_reset", my_default=640 * 1024 * 1024,
281 help="Maximum allowed virtual memory per Gunicorn process. "
282 "When the limit is reached, the worker will be reset after "
283 "the current request.",
285 group.add_option("--cpu-time-limit", dest="cpu_time_limit", my_default=60,
286 help="Maximum allowed CPU time per Gunicorn process. "
287 "When the limit is reached, an exception is raised.",
289 group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
290 help="Use the unaccent function provided by the database when available.")
292 parser.add_option_group(group)
294 # Copy all optparse options (i.e. MyOption) into self.options.
295 for group in parser.option_groups:
296 for option in group.option_list:
297 self.options[option.dest] = option.my_default
298 self.casts[option.dest] = option
300 self.parse_config(None, False)
302 def parse_config(self, args=None, complete=True):
303 """ Parse the configuration file (if any) and the command-line
306 This method initializes openerp.tools.config and openerp.conf (the
307 former should be removed in the furture) with library-wide
308 configuration values.
310 This method must be called before proper usage of this library can be
313 Typical usage of this method:
315 openerp.tools.config.parse_config(sys.argv[1:])
317 :param complete: this is a hack used in __init__(), leave it to True.
322 opt, args = self.parser.parse_args(args)
326 self.parser.error(msg)
328 # Ensures no illegitimate argument is silently discarded (avoids insidious "hyphen to dash" problem)
329 die(args, "unrecognized parameters: '%s'" % " ".join(args))
331 die(bool(opt.syslog) and bool(opt.logfile),
332 "the syslog and logfile options are exclusive")
334 die(opt.translate_in and (not opt.language or not opt.db_name),
335 "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
337 die(opt.overwrite_existing_translations and not (opt.translate_in or opt.update),
338 "the i18n-overwrite option cannot be used without the i18n-import option or without the update option")
340 die(opt.translate_out and (not opt.db_name),
341 "the i18n-export option cannot be used without the database (-d) option")
343 # Check if the config file exists (-c used, but not -s)
344 die(not opt.save and opt.config and not os.path.exists(opt.config),
345 "The config file '%s' selected with -c/--config doesn't exist, "\
346 "use -s/--save if you want to generate it"%(opt.config))
348 # place/search the config file on Win32 near the server installation
349 # (../etc from the server)
350 # if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write,
351 # else he won't be able to save the configurations, or even to start the server...
353 rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
355 rcfilepath = os.path.expanduser('~/.openerp_serverrc')
357 self.rcfile = os.path.abspath(
358 self.config_file or opt.config \
359 or os.environ.get('OPENERP_SERVER') or rcfilepath)
363 # Verify that we want to log or not, if not the output will go to stdout
364 if self.options['logfile'] in ('None', 'False'):
365 self.options['logfile'] = False
366 # the same for the pidfile
367 if self.options['pidfile'] in ('None', 'False'):
368 self.options['pidfile'] = False
370 # if defined dont take the configfile value even if the defined value is None
371 keys = ['xmlrpc_interface', 'xmlrpc_port', 'db_name', 'db_user', 'db_password', 'db_host',
372 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout',
373 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
374 'netrpc_interface', 'netrpc_port', 'db_maxconn', 'import_partial', 'addons_path',
375 'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',
376 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
377 'static_http_enable', 'static_http_document_root', 'static_http_url_prefix',
378 'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level'
382 # Copy the command-line argument...
383 if getattr(opt, arg):
384 self.options[arg] = getattr(opt, arg)
385 # ... or keep, but cast, the config file value.
386 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
387 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
389 # if defined but None take the configfile value
391 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
392 'debug_mode', 'smtp_ssl', 'load_language',
393 'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
394 'list_db', 'xmlrpcs', 'proxy_mode',
395 'test_file', 'test_enable', 'test_commit', 'test_report_directory',
396 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads',
397 'virtual_memory_limit', 'virtual_memory_reset', 'cpu_time_limit', 'unaccent',
401 # Copy the command-line argument...
402 if getattr(opt, arg) is not None:
403 self.options[arg] = getattr(opt, arg)
404 # ... or keep, but cast, the config file value.
405 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
406 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
408 self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
409 if not self.options['addons_path'] or self.options['addons_path']=='None':
410 self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
412 self.options['addons_path'] = ",".join(
413 os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
414 for x in self.options['addons_path'].split(','))
416 self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
417 self.options["demo"] = not opt.without_demo and self.options['init'] or {}
418 self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
419 self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
420 self.options['translate_modules'].sort()
422 # TODO checking the type of the parameters should be done for every
423 # parameters, not just the timezone.
424 # The call to get_server_timezone() sets the timezone; this should
425 # probably done here.
426 if self.options['timezone']:
427 # Prevent the timezone to be True. (The config file parsing changes
428 # the string 'True' to the boolean value True. It would be probably
429 # be better to remove that conversion.)
430 die(not isinstance(self.options['timezone'], basestring),
431 "Invalid timezone value in configuration or environment: %r.\n"
432 "Please fix this in your configuration." %(self.options['timezone']))
434 # If an explicit TZ was provided in the config, make sure it is known
437 pytz.timezone(self.options['timezone'])
438 except pytz.UnknownTimeZoneError:
439 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
441 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
445 self.options['pg_path'] = opt.pg_path
447 if self.options.get('language', False):
448 if len(self.options['language']) > 5:
449 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
451 if not self.options['db_user']:
454 self.options['db_user'] = getpass.getuser()
456 self.options['db_user'] = None
458 die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
460 if self.options['db_password']:
461 if sys.platform == 'win32' and not self.options['db_host']:
462 self.options['db_host'] = 'localhost'
463 #if self.options['db_host']:
464 # self._generate_pgpassfile()
469 openerp.conf.max_cron_threads = self.options['max_cron_threads']
471 openerp.conf.addons_paths = self.options['addons_path'].split(',')
472 if opt.server_wide_modules:
473 openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
475 openerp.conf.server_wide_modules = ['web']
477 openerp.modules.module.initialize_sys_path()
478 openerp.modules.loading.open_openerp_namespace()
480 def _generate_pgpassfile(self):
482 Generate the pgpass file with the parameters from the command line (db_host, db_user,
485 Used because pg_dump and pg_restore can not accept the password on the command line.
487 is_win32 = sys.platform == 'win32'
489 filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
491 filename = os.path.join(os.environ['HOME'], '.pgpass')
493 text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
495 if os.path.exists(filename):
496 content = [x.strip() for x in file(filename, 'r').readlines()]
497 if text_to_add in content:
500 fp = file(filename, 'a+')
501 fp.write(text_to_add + "\n")
509 x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
510 y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
511 _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
516 os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
518 def _is_addons_path(self, path):
519 for f in os.listdir(path):
520 modpath = os.path.join(path, f)
521 if os.path.isdir(modpath):
522 def hasfile(filename):
523 return os.path.isfile(os.path.join(modpath, filename))
524 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
528 def _check_addons_path(self, option, opt, value, parser):
530 for path in value.split(','):
532 res = os.path.abspath(os.path.expanduser(path))
533 if not os.path.isdir(res):
534 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
535 if not self._is_addons_path(res):
536 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
539 setattr(parser.values, option.dest, ",".join(ad_paths))
542 p = ConfigParser.ConfigParser()
544 p.read([self.rcfile])
545 for (name,value) in p.items('options'):
546 if value=='True' or value=='true':
548 if value=='False' or value=='false':
550 self.options[name] = value
551 #parse the other sections, as well
552 for sec in p.sections():
555 if not self.misc.has_key(sec):
557 for (name, value) in p.items(sec):
558 if value=='True' or value=='true':
560 if value=='False' or value=='false':
562 self.misc[sec][name] = value
565 except ConfigParser.NoSectionError:
569 p = ConfigParser.ConfigParser()
570 loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
571 p.add_section('options')
572 for opt in sorted(self.options.keys()):
573 if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
575 if opt in self.blacklist_for_save:
577 if opt in ('log_level',):
578 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
580 p.set('options', opt, self.options[opt])
582 for sec in sorted(self.misc.keys()):
584 for opt in sorted(self.misc[sec].keys()):
585 p.set(sec,opt,self.misc[sec][opt])
587 # try to create the directories and write the file
589 rc_exists = os.path.exists(self.rcfile)
590 if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
591 os.makedirs(os.path.dirname(self.rcfile))
593 p.write(file(self.rcfile, 'w'))
595 os.chmod(self.rcfile, 0600)
597 sys.stderr.write("ERROR: couldn't write the config file\n")
600 # what to do if impossible?
601 sys.stderr.write("ERROR: couldn't create the config directory\n")
603 def get(self, key, default=None):
604 return self.options.get(key, default)
606 def get_misc(self, sect, key, default=None):
607 return self.misc.get(sect,{}).get(key, default)
609 def __setitem__(self, key, value):
610 self.options[key] = value
612 def __getitem__(self, key):
613 return self.options[key]
615 config = configmanager()
618 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: