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 group.add_option("--gevent", dest="gevent", action="store_true", my_default=False, help="Activate the GEvent mode, this also desactivate the cron.")
110 parser.add_option_group(group)
113 group = optparse.OptionGroup(parser, "XML-RPC Configuration")
114 group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
115 help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
116 group.add_option("--xmlrpc-port", dest="xmlrpc_port", my_default=8069,
117 help="specify the TCP port for the XML-RPC protocol", type="int")
118 group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", my_default=True,
119 help="disable the XML-RPC protocol")
120 group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
121 help="Enable correct behavior when behind a reverse proxy")
122 group.add_option("--longpolling-port", dest="longpolling_port", my_default=8072,
123 help="specify the TCP port for longpolling requests", type="int")
124 parser.add_option_group(group)
127 title = "XML-RPC Secure Configuration"
129 title += " (disabled as ssl is unavailable)"
131 group = optparse.OptionGroup(parser, title)
132 group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
133 help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
134 group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
135 help="specify the TCP port for the XML-RPC Secure protocol", type="int")
136 group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
137 help="disable the XML-RPC Secure protocol")
138 group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
139 help="specify the certificate file for the SSL connection")
140 group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
141 help="specify the private key file for the SSL connection")
142 parser.add_option_group(group)
145 # TODO move to web addons after MetaOption merge
146 group = optparse.OptionGroup(parser, "Web interface Configuration")
147 group.add_option("--db-filter", dest="dbfilter", default='.*',
148 help="Filter listed database", metavar="REGEXP")
149 parser.add_option_group(group)
152 group = optparse.OptionGroup(parser, "Static HTTP service")
153 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")
154 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/')")
155 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 '/')")
156 parser.add_option_group(group)
159 group = optparse.OptionGroup(parser, "Testing Configuration")
160 group.add_option("--test-file", dest="test_file", my_default=False,
161 help="Launch a YML test file.")
162 group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
163 help="If set, will save sample of all reports in this directory.")
164 group.add_option("--test-enable", action="store_true", dest="test_enable",
165 my_default=False, help="Enable YAML and unit tests.")
166 group.add_option("--test-commit", action="store_true", dest="test_commit",
167 my_default=False, help="Commit database changes performed by YAML or XML tests.")
168 parser.add_option_group(group)
171 group = optparse.OptionGroup(parser, "Logging Configuration")
172 group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
173 group.add_option("--logrotate", dest="logrotate", action="store_true", my_default=False, help="enable logfile rotation")
174 group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
175 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")')
176 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')
177 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')
178 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')
179 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')
180 # For backward-compatibility, map the old log levels to something
182 levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
183 'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
184 group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
185 my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
187 parser.add_option_group(group)
190 group = optparse.OptionGroup(parser, "SMTP Configuration")
191 group.add_option('--email-from', dest='email_from', my_default=False,
192 help='specify the SMTP email address for sending email')
193 group.add_option('--smtp', dest='smtp_server', my_default='localhost',
194 help='specify the SMTP server for sending email')
195 group.add_option('--smtp-port', dest='smtp_port', my_default=25,
196 help='specify the SMTP port', type="int")
197 group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
198 help='if passed, SMTP connections will be encrypted with SSL (STARTTLS)')
199 group.add_option('--smtp-user', dest='smtp_user', my_default=False,
200 help='specify the SMTP username for sending email')
201 group.add_option('--smtp-password', dest='smtp_password', my_default=False,
202 help='specify the SMTP password for sending email')
203 parser.add_option_group(group)
205 group = optparse.OptionGroup(parser, "Database related options")
206 group.add_option("-d", "--database", dest="db_name", my_default=False,
207 help="specify the database name")
208 group.add_option("-r", "--db_user", dest="db_user", my_default=False,
209 help="specify the database user name")
210 group.add_option("-w", "--db_password", dest="db_password", my_default=False,
211 help="specify the database password")
212 group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
213 group.add_option("--db_host", dest="db_host", my_default=False,
214 help="specify the database host")
215 group.add_option("--db_port", dest="db_port", my_default=False,
216 help="specify the database port", type="int")
217 group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
218 help="specify the the maximum number of physical connections to posgresql")
219 group.add_option("--db-template", dest="db_template", my_default="template1",
220 help="specify a custom database template to create a new database")
221 parser.add_option_group(group)
223 group = optparse.OptionGroup(parser, "Internationalisation options",
224 "Use these options to translate OpenERP to another language."
225 "See i18n section of the user manual. Option '-d' is mandatory."
226 "Option '-l' is mandatory in case of importation"
228 group.add_option('--load-language', dest="load_language",
229 help="specifies the languages for the translations you want to be loaded")
230 group.add_option('-l', "--language", dest="language",
231 help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
232 group.add_option("--i18n-export", dest="translate_out",
233 help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
234 group.add_option("--i18n-import", dest="translate_in",
235 help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
236 group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
237 help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
238 group.add_option("--modules", dest="translate_modules",
239 help="specify modules to export. Use in combination with --i18n-export")
240 parser.add_option_group(group)
242 security = optparse.OptionGroup(parser, 'Security-related options')
243 security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
244 help="disable the ability to return the list of databases")
245 parser.add_option_group(security)
248 group = optparse.OptionGroup(parser, "Advanced options")
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', 'gevent'
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: