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)
53 from OpenSSL import SSL
56 return hasattr(socket, 'ssl') and hasattr(SSL, "Connection")
60 DEFAULT_LOG_HANDLER = [':INFO']
62 class configmanager(object):
63 def __init__(self, fname=None):
64 # Options not exposed on the command line. Command line options will be added
65 # from optparse's parser.
67 'admin_passwd': 'admin',
68 'csv_internal_sep': ',',
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', '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 group.add_option("--longpolling-port", dest="longpolling_port", my_default=8072,
122 help="specify the TCP port for longpolling requests", type="int")
123 parser.add_option_group(group)
126 title = "XML-RPC Secure Configuration"
128 title += " (disabled as ssl is unavailable)"
130 group = optparse.OptionGroup(parser, title)
131 group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
132 help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
133 group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
134 help="specify the TCP port for the XML-RPC Secure protocol", type="int")
135 group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
136 help="disable the XML-RPC Secure protocol")
137 group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
138 help="specify the certificate file for the SSL connection")
139 group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
140 help="specify the private key file for the SSL connection")
141 parser.add_option_group(group)
144 # TODO move to web addons after MetaOption merge
145 group = optparse.OptionGroup(parser, "Web interface Configuration")
146 group.add_option("--db-filter", dest="dbfilter", default='.*',
147 help="Filter listed database", metavar="REGEXP")
148 parser.add_option_group(group)
151 group = optparse.OptionGroup(parser, "Static HTTP service")
152 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")
153 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/')")
154 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 '/')")
155 parser.add_option_group(group)
158 group = optparse.OptionGroup(parser, "Testing Configuration")
159 group.add_option("--test-file", dest="test_file", my_default=False,
160 help="Launch a YML test file.")
161 group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
162 help="If set, will save sample of all reports in this directory.")
163 group.add_option("--test-enable", action="store_true", dest="test_enable",
164 my_default=False, help="Enable YAML and unit tests.")
165 group.add_option("--test-commit", action="store_true", dest="test_commit",
166 my_default=False, help="Commit database changes performed by YAML or XML tests.")
167 parser.add_option_group(group)
170 group = optparse.OptionGroup(parser, "Logging Configuration")
171 group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
172 group.add_option("--logrotate", dest="logrotate", action="store_true", my_default=False, help="enable logfile rotation")
173 group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
174 group.add_option('--log-handler', action="append", default=DEFAULT_LOG_HANDLER, my_default=DEFAULT_LOG_HANDLER, 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")')
175 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')
176 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')
177 group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.addons.web.http:DEBUG", help='shortcut for --log-handler=openerp.addons.web.http:DEBUG')
178 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')
179 # For backward-compatibility, map the old log levels to something
181 levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
182 'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
183 group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
184 my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
186 parser.add_option_group(group)
189 group = optparse.OptionGroup(parser, "SMTP Configuration")
190 group.add_option('--email-from', dest='email_from', my_default=False,
191 help='specify the SMTP email address for sending email')
192 group.add_option('--smtp', dest='smtp_server', my_default='localhost',
193 help='specify the SMTP server for sending email')
194 group.add_option('--smtp-port', dest='smtp_port', my_default=25,
195 help='specify the SMTP port', type="int")
196 group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
197 help='if passed, SMTP connections will be encrypted with SSL (STARTTLS)')
198 group.add_option('--smtp-user', dest='smtp_user', my_default=False,
199 help='specify the SMTP username for sending email')
200 group.add_option('--smtp-password', dest='smtp_password', my_default=False,
201 help='specify the SMTP password for sending email')
202 parser.add_option_group(group)
204 group = optparse.OptionGroup(parser, "Database related options")
205 group.add_option("-d", "--database", dest="db_name", my_default=False,
206 help="specify the database name")
207 group.add_option("-r", "--db_user", dest="db_user", my_default=False,
208 help="specify the database user name")
209 group.add_option("-w", "--db_password", dest="db_password", my_default=False,
210 help="specify the database password")
211 group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
212 group.add_option("--db_host", dest="db_host", my_default=False,
213 help="specify the database host")
214 group.add_option("--db_port", dest="db_port", my_default=False,
215 help="specify the database port", type="int")
216 group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
217 help="specify the the maximum number of physical connections to posgresql")
218 group.add_option("--db-template", dest="db_template", my_default="template1",
219 help="specify a custom database template to create a new database")
220 parser.add_option_group(group)
222 group = optparse.OptionGroup(parser, "Internationalisation options",
223 "Use these options to translate OpenERP to another language."
224 "See i18n section of the user manual. Option '-d' is mandatory."
225 "Option '-l' is mandatory in case of importation"
227 group.add_option('--load-language', dest="load_language",
228 help="specifies the languages for the translations you want to be loaded")
229 group.add_option('-l', "--language", dest="language",
230 help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
231 group.add_option("--i18n-export", dest="translate_out",
232 help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
233 group.add_option("--i18n-import", dest="translate_in",
234 help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
235 group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
236 help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
237 group.add_option("--modules", dest="translate_modules",
238 help="specify modules to export. Use in combination with --i18n-export")
239 parser.add_option_group(group)
241 security = optparse.OptionGroup(parser, 'Security-related options')
242 security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
243 help="disable the ability to return the list of databases")
244 parser.add_option_group(security)
247 group = optparse.OptionGroup(parser, "Advanced options")
248 group.add_option('--auto-reload', dest='auto_reload', action='store_true', my_default=False, help='enable auto reload')
249 group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
250 group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
251 help="stop the server after its initialization")
252 group.add_option("-t", "--timezone", dest="timezone", my_default=False,
253 help="specify reference timezone for the server (e.g. Europe/Brussels")
254 group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
255 help="Force a limit on the maximum number of records kept in the virtual "
256 "osv_memory tables. The default is False, which means no count-based limit.",
258 group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
259 help="Force a limit on the maximum age of records kept in the virtual "
260 "osv_memory tables. This is a decimal value expressed in hours, "
261 "and the default is 1 hour.",
263 group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=2,
264 help="Maximum number of threads processing concurrently cron jobs (default 2).",
266 group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
267 help="Use the unaccent function provided by the database when available.")
268 parser.add_option_group(group)
270 group = optparse.OptionGroup(parser, "Multiprocessing options")
271 # TODO sensible default for the three following limits.
272 group.add_option("--workers", dest="workers", my_default=0,
273 help="Specify the number of workers, 0 disable prefork mode.",
275 group.add_option("--limit-memory-soft", dest="limit_memory_soft", my_default=640 * 1024 * 1024,
276 help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request (default 671088640 aka 640MB).",
278 group.add_option("--limit-memory-hard", dest="limit_memory_hard", my_default=768 * 1024 * 1024,
279 help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail (default 805306368 aka 768MB).",
281 group.add_option("--limit-time-cpu", dest="limit_time_cpu", my_default=60,
282 help="Maximum allowed CPU time per request (default 60).",
284 group.add_option("--limit-time-real", dest="limit_time_real", my_default=120,
285 help="Maximum allowed Real time per request (default 120).",
287 group.add_option("--limit-request", dest="limit_request", my_default=8192,
288 help="Maximum number of request to be processed per worker (default 8192).",
290 parser.add_option_group(group)
292 # Copy all optparse options (i.e. MyOption) into self.options.
293 for group in parser.option_groups:
294 for option in group.option_list:
295 if option.dest not in self.options:
296 self.options[option.dest] = option.my_default
297 self.casts[option.dest] = option
299 self.parse_config(None, False)
301 def parse_config(self, args=None, complete=True):
302 """ Parse the configuration file (if any) and the command-line
305 This method initializes openerp.tools.config and openerp.conf (the
306 former should be removed in the furture) with library-wide
307 configuration values.
309 This method must be called before proper usage of this library can be
312 Typical usage of this method:
314 openerp.tools.config.parse_config(sys.argv[1:])
316 :param complete: this is a hack used in __init__(), leave it to True.
321 opt, args = self.parser.parse_args(args)
325 self.parser.error(msg)
327 # Ensures no illegitimate argument is silently discarded (avoids insidious "hyphen to dash" problem)
328 die(args, "unrecognized parameters: '%s'" % " ".join(args))
330 die(bool(opt.syslog) and bool(opt.logfile),
331 "the syslog and logfile options are exclusive")
333 die(opt.translate_in and (not opt.language or not opt.db_name),
334 "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
336 die(opt.overwrite_existing_translations and not (opt.translate_in or opt.update),
337 "the i18n-overwrite option cannot be used without the i18n-import option or without the update option")
339 die(opt.translate_out and (not opt.db_name),
340 "the i18n-export option cannot be used without the database (-d) option")
342 # Check if the config file exists (-c used, but not -s)
343 die(not opt.save and opt.config and not os.path.exists(opt.config),
344 "The config file '%s' selected with -c/--config doesn't exist, "\
345 "use -s/--save if you want to generate it"% opt.config)
347 # place/search the config file on Win32 near the server installation
348 # (../etc from the server)
349 # 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,
350 # else he won't be able to save the configurations, or even to start the server...
352 rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
354 rcfilepath = os.path.expanduser('~/.openerp_serverrc')
356 self.rcfile = os.path.abspath(
357 self.config_file or opt.config \
358 or os.environ.get('OPENERP_SERVER') or rcfilepath)
362 # Verify that we want to log or not, if not the output will go to stdout
363 if self.options['logfile'] in ('None', 'False'):
364 self.options['logfile'] = False
365 # the same for the pidfile
366 if self.options['pidfile'] in ('None', 'False'):
367 self.options['pidfile'] = False
369 # if defined dont take the configfile value even if the defined value is None
370 keys = ['xmlrpc_interface', 'xmlrpc_port', 'longpolling_port',
371 'db_name', 'db_user', 'db_password', 'db_host',
372 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port',
373 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
374 'db_maxconn', 'import_partial', 'addons_path',
375 '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 (except the special case for log_handler, due to
383 # action=append requiring a real default, so we cannot use the my_default workaround)
384 if getattr(opt, arg) and getattr(opt, arg) != DEFAULT_LOG_HANDLER:
385 self.options[arg] = getattr(opt, arg)
386 # ... or keep, but cast, the config file value.
387 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
388 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
391 if isinstance(self.options['log_handler'], basestring):
392 self.options['log_handler'] = self.options['log_handler'].split(',')
394 # if defined but None take the configfile value
396 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
397 'debug_mode', 'smtp_ssl', 'load_language',
398 'stop_after_init', 'logrotate', 'without_demo', 'xmlrpc', 'syslog',
399 'list_db', 'xmlrpcs', 'proxy_mode',
400 'test_file', 'test_enable', 'test_commit', 'test_report_directory',
401 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent',
402 'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request', 'auto_reload'
406 # Copy the command-line argument...
407 if getattr(opt, arg) is not None:
408 self.options[arg] = getattr(opt, arg)
409 # ... or keep, but cast, the config file value.
410 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
411 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
413 self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
414 if not self.options['addons_path'] or self.options['addons_path']=='None':
415 self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
417 self.options['addons_path'] = ",".join(
418 os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
419 for x in self.options['addons_path'].split(','))
421 self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
422 self.options["demo"] = not opt.without_demo and self.options['init'] or {}
423 self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
424 self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
425 self.options['translate_modules'].sort()
427 # TODO checking the type of the parameters should be done for every
428 # parameters, not just the timezone.
429 # The call to get_server_timezone() sets the timezone; this should
430 # probably done here.
431 if self.options['timezone']:
432 # Prevent the timezone to be True. (The config file parsing changes
433 # the string 'True' to the boolean value True. It would be probably
434 # be better to remove that conversion.)
435 die(not isinstance(self.options['timezone'], basestring),
436 "Invalid timezone value in configuration or environment: %r.\n"
437 "Please fix this in your configuration." %(self.options['timezone']))
439 # If an explicit TZ was provided in the config, make sure it is known
442 pytz.timezone(self.options['timezone'])
443 except pytz.UnknownTimeZoneError:
444 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
446 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
450 self.options['pg_path'] = opt.pg_path
452 if self.options.get('language', False):
453 if len(self.options['language']) > 5:
454 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
456 if not self.options['db_user']:
459 self.options['db_user'] = getpass.getuser()
461 self.options['db_user'] = None
463 die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
465 if self.options['db_password']:
466 if sys.platform == 'win32' and not self.options['db_host']:
467 self.options['db_host'] = 'localhost'
468 #if self.options['db_host']:
469 # self._generate_pgpassfile()
474 openerp.conf.addons_paths = self.options['addons_path'].split(',')
475 if opt.server_wide_modules:
476 openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
478 openerp.conf.server_wide_modules = ['web','web_kanban']
480 openerp.modules.module.initialize_sys_path()
482 def _generate_pgpassfile(self):
484 Generate the pgpass file with the parameters from the command line (db_host, db_user,
487 Used because pg_dump and pg_restore can not accept the password on the command line.
489 is_win32 = sys.platform == 'win32'
491 filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
493 filename = os.path.join(os.environ['HOME'], '.pgpass')
495 text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
497 if os.path.exists(filename):
498 content = [x.strip() for x in file(filename, 'r').readlines()]
499 if text_to_add in content:
502 fp = file(filename, 'a+')
503 fp.write(text_to_add + "\n")
511 x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
512 y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
513 _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
518 os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
520 def _is_addons_path(self, path):
521 for f in os.listdir(path):
522 modpath = os.path.join(path, f)
523 if os.path.isdir(modpath):
524 def hasfile(filename):
525 return os.path.isfile(os.path.join(modpath, filename))
526 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
530 def _check_addons_path(self, option, opt, value, parser):
532 for path in value.split(','):
534 res = os.path.abspath(os.path.expanduser(path))
535 if not os.path.isdir(res):
536 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
537 if not self._is_addons_path(res):
538 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
541 setattr(parser.values, option.dest, ",".join(ad_paths))
544 p = ConfigParser.ConfigParser()
546 p.read([self.rcfile])
547 for (name,value) in p.items('options'):
548 if value=='True' or value=='true':
550 if value=='False' or value=='false':
552 self.options[name] = value
553 #parse the other sections, as well
554 for sec in p.sections():
557 if not self.misc.has_key(sec):
559 for (name, value) in p.items(sec):
560 if value=='True' or value=='true':
562 if value=='False' or value=='false':
564 self.misc[sec][name] = value
567 except ConfigParser.NoSectionError:
571 p = ConfigParser.ConfigParser()
572 loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
573 p.add_section('options')
574 for opt in sorted(self.options.keys()):
575 if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
577 if opt in self.blacklist_for_save:
579 if opt in ('log_level',):
580 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
582 p.set('options', opt, self.options[opt])
584 for sec in sorted(self.misc.keys()):
586 for opt in sorted(self.misc[sec].keys()):
587 p.set(sec,opt,self.misc[sec][opt])
589 # try to create the directories and write the file
591 rc_exists = os.path.exists(self.rcfile)
592 if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
593 os.makedirs(os.path.dirname(self.rcfile))
595 p.write(file(self.rcfile, 'w'))
597 os.chmod(self.rcfile, 0600)
599 sys.stderr.write("ERROR: couldn't write the config file\n")
602 # what to do if impossible?
603 sys.stderr.write("ERROR: couldn't create the config directory\n")
605 def get(self, key, default=None):
606 return self.options.get(key, default)
608 def get_misc(self, sect, key, default=None):
609 return self.misc.get(sect,{}).get(key, default)
611 def __setitem__(self, key, value):
612 self.options[key] = value
613 if key in self.options and isinstance(self.options[key], basestring) and \
614 key in self.casts and self.casts[key].type in optparse.Option.TYPE_CHECKER:
615 self.options[key] = optparse.Option.TYPE_CHECKER[self.casts[key].type](self.casts[key], key, self.options[key])
617 def __getitem__(self, key):
618 return self.options[key]
620 config = configmanager()
623 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: