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 DEFAULT_LOG_HANDLER = [':INFO']
63 class configmanager(object):
64 def __init__(self, fname=None):
65 # Options not exposed on the command line. Command line options will be added
66 # from optparse's parser.
68 'admin_passwd': 'admin',
69 'csv_internal_sep': ',',
70 'login_message': False,
71 'publisher_warranty_url': 'http://services.openerp.com/publisher-warranty/',
76 # Not exposed in the configuration file.
77 self.blacklist_for_save = set(
78 ['publisher_warranty_url', 'load_language', 'root_path',
79 'init', 'save', 'config', 'update', 'stop_after_init'])
81 # dictionary mapping option destination (keys in self.options) to MyOptions.
85 self.config_file = fname
86 self.has_ssl = check_ssl()
88 self._LOGLEVELS = dict([(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'TEST', 'DEBUG', 'NOTSET')])
90 version = "%s %s" % (release.description, release.version)
91 self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
93 # Server startup config
94 group = optparse.OptionGroup(parser, "Common options")
95 group.add_option("-c", "--config", dest="config", help="specify alternate config file")
96 group.add_option("-s", "--save", action="store_true", dest="save", default=False,
97 help="save configuration to ~/.openerp_serverrc")
98 group.add_option("-i", "--init", dest="init", help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
99 group.add_option("-u", "--update", dest="update",
100 help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
101 group.add_option("--without-demo", dest="without_demo",
102 help="disable loading demo data for modules to be installed (comma-separated, use \"all\" for all modules). Requires -d and -i. Default is %default",
104 group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
105 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.")
106 group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
107 group.add_option("--addons-path", dest="addons_path",
108 help="specify additional addons paths (separated by commas).",
109 action="callback", callback=self._check_addons_path, nargs=1, type="string")
110 group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
111 parser.add_option_group(group)
114 group = optparse.OptionGroup(parser, "XML-RPC Configuration")
115 group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
116 help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
117 group.add_option("--xmlrpc-port", dest="xmlrpc_port", my_default=8069,
118 help="specify the TCP port for the XML-RPC protocol", type="int")
119 group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", my_default=True,
120 help="disable the XML-RPC protocol")
121 group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
122 help="Enable correct behavior when behind a reverse proxy")
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 group = optparse.OptionGroup(parser, "NET-RPC Configuration")
145 group.add_option("--netrpc-interface", dest="netrpc_interface", my_default='',
146 help="specify the TCP IP address for the NETRPC protocol")
147 group.add_option("--netrpc-port", dest="netrpc_port", my_default=8070,
148 help="specify the TCP port for the NETRPC protocol", type="int")
149 group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=True,
150 help="disable the NETRPC protocol")
151 parser.add_option_group(group)
154 # TODO move to web addons after MetaOption merge
155 group = optparse.OptionGroup(parser, "Web interface Configuration")
156 group.add_option("--db-filter", dest="dbfilter", default='.*',
157 help="Filter listed database", metavar="REGEXP")
158 parser.add_option_group(group)
161 group = optparse.OptionGroup(parser, "Static HTTP service")
162 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")
163 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/')")
164 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 '/')")
165 parser.add_option_group(group)
168 group = optparse.OptionGroup(parser, "Testing Configuration")
169 group.add_option("--test-file", dest="test_file", my_default=False,
170 help="Launch a YML test file.")
171 group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
172 help="If set, will save sample of all reports in this directory.")
173 group.add_option("--test-disable", action="store_true", dest="test_disable",
174 my_default=False, help="Disable loading test files.")
175 group.add_option("--test-commit", action="store_true", dest="test_commit",
176 my_default=False, help="Commit database changes performed by tests.")
177 group.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=self._LOGLEVELS.keys(),
179 help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (self._LOGLEVELS.keys(),))
180 parser.add_option_group(group)
183 group = optparse.OptionGroup(parser, "Logging Configuration")
184 group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
185 group.add_option("--no-logrotate", dest="logrotate", action="store_false", my_default=True, help="do not rotate the logfile")
186 group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
187 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")')
188 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')
189 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')
190 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')
191 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')
192 # For backward-compatibility, map the old log levels to something
194 levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
195 'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
196 group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
197 my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
199 parser.add_option_group(group)
202 group = optparse.OptionGroup(parser, "SMTP Configuration")
203 group.add_option('--email-from', dest='email_from', my_default=False,
204 help='specify the SMTP email address for sending email')
205 group.add_option('--smtp', dest='smtp_server', my_default='localhost',
206 help='specify the SMTP server for sending email')
207 group.add_option('--smtp-port', dest='smtp_port', my_default=25,
208 help='specify the SMTP port', type="int")
209 group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
210 help='specify the SMTP server support SSL or not')
211 group.add_option('--smtp-user', dest='smtp_user', my_default=False,
212 help='specify the SMTP username for sending email')
213 group.add_option('--smtp-password', dest='smtp_password', my_default=False,
214 help='specify the SMTP password for sending email')
215 parser.add_option_group(group)
217 group = optparse.OptionGroup(parser, "Database related options")
218 group.add_option("-d", "--database", dest="db_name", my_default=False,
219 help="specify the database name")
220 group.add_option("-r", "--db_user", dest="db_user", my_default=False,
221 help="specify the database user name")
222 group.add_option("-w", "--db_password", dest="db_password", my_default=False,
223 help="specify the database password")
224 group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
225 group.add_option("--db_host", dest="db_host", my_default=False,
226 help="specify the database host")
227 group.add_option("--db_port", dest="db_port", my_default=False,
228 help="specify the database port", type="int")
229 group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
230 help="specify the the maximum number of physical connections to posgresql")
231 group.add_option("--db-template", dest="db_template", my_default="template0",
232 help="specify a custom database template to create a new database")
233 parser.add_option_group(group)
235 group = optparse.OptionGroup(parser, "Internationalisation options",
236 "Use these options to translate OpenERP to another language."
237 "See i18n section of the user manual. Option '-d' is mandatory."
238 "Option '-l' is mandatory in case of importation"
240 group.add_option('--load-language', dest="load_language",
241 help="specifies the languages for the translations you want to be loaded")
242 group.add_option('-l', "--language", dest="language",
243 help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
244 group.add_option("--i18n-export", dest="translate_out",
245 help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
246 group.add_option("--i18n-import", dest="translate_in",
247 help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
248 group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
249 help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
250 group.add_option("--modules", dest="translate_modules",
251 help="specify modules to export. Use in combination with --i18n-export")
252 parser.add_option_group(group)
254 security = optparse.OptionGroup(parser, 'Security-related options')
255 security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
256 help="disable the ability to return the list of databases")
257 parser.add_option_group(security)
260 group = optparse.OptionGroup(parser, "Advanced options")
261 group.add_option("--cache-timeout", dest="cache_timeout", my_default=100000,
262 help="set the timeout for the cache system", type="int")
263 group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
264 group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
265 help="stop the server after its initialization")
266 group.add_option("-t", "--timezone", dest="timezone", my_default=False,
267 help="specify reference timezone for the server (e.g. Europe/Brussels")
268 group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
269 help="Force a limit on the maximum number of records kept in the virtual "
270 "osv_memory tables. The default is False, which means no count-based limit.",
272 group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
273 help="Force a limit on the maximum age of records kept in the virtual "
274 "osv_memory tables. This is a decimal value expressed in hours, "
275 "and the default is 1 hour.",
277 group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=4,
278 help="Maximum number of threads processing concurrently cron jobs.",
280 # TODO sensible default for the three following limits.
281 group.add_option("--virtual-memory-limit", dest="virtual_memory_limit", my_default=768 * 1024 * 1024,
282 help="Maximum allowed virtual memory per Gunicorn process. "
283 "When the limit is reached, any memory allocation will fail.",
285 group.add_option("--virtual-memory-reset", dest="virtual_memory_reset", my_default=640 * 1024 * 1024,
286 help="Maximum allowed virtual memory per Gunicorn process. "
287 "When the limit is reached, the worker will be reset after "
288 "the current request.",
290 group.add_option("--cpu-time-limit", dest="cpu_time_limit", my_default=60,
291 help="Maximum allowed CPU time per Gunicorn process. "
292 "When the limit is reached, an exception is raised.",
294 group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
295 help="Use the unaccent function provided by the database when available.")
297 parser.add_option_group(group)
299 # Copy all optparse options (i.e. MyOption) into self.options.
300 for group in parser.option_groups:
301 for option in group.option_list:
302 if option.dest not in self.options:
303 self.options[option.dest] = option.my_default
304 self.casts[option.dest] = option
306 self.parse_config(None, False)
308 def parse_config(self, args=None, complete=True):
309 """ Parse the configuration file (if any) and the command-line
312 This method initializes openerp.tools.config and openerp.conf (the
313 former should be removed in the furture) with library-wide
314 configuration values.
316 This method must be called before proper usage of this library can be
319 Typical usage of this method:
321 openerp.tools.config.parse_config(sys.argv[1:])
323 :param complete: this is a hack used in __init__(), leave it to True.
328 opt, args = self.parser.parse_args(args)
332 self.parser.error(msg)
334 # Ensures no illegitimate argument is silently discarded (avoids insidious "hyphen to dash" problem)
335 die(args, "unrecognized parameters: '%s'" % " ".join(args))
337 die(bool(opt.syslog) and bool(opt.logfile),
338 "the syslog and logfile options are exclusive")
340 die(opt.translate_in and (not opt.language or not opt.db_name),
341 "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
343 die(opt.overwrite_existing_translations and not (opt.translate_in or opt.update),
344 "the i18n-overwrite option cannot be used without the i18n-import option or without the update option")
346 die(opt.translate_out and (not opt.db_name),
347 "the i18n-export option cannot be used without the database (-d) option")
349 # Check if the config file exists (-c used, but not -s)
350 die(not opt.save and opt.config and not os.path.exists(opt.config),
351 "The config file '%s' selected with -c/--config doesn't exist, "\
352 "use -s/--save if you want to generate it"%(opt.config))
354 # place/search the config file on Win32 near the server installation
355 # (../etc from the server)
356 # 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,
357 # else he won't be able to save the configurations, or even to start the server...
359 rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
361 rcfilepath = os.path.expanduser('~/.openerp_serverrc')
363 self.rcfile = os.path.abspath(
364 self.config_file or opt.config \
365 or os.environ.get('OPENERP_SERVER') or rcfilepath)
369 # Verify that we want to log or not, if not the output will go to stdout
370 if self.options['logfile'] in ('None', 'False'):
371 self.options['logfile'] = False
372 # the same for the pidfile
373 if self.options['pidfile'] in ('None', 'False'):
374 self.options['pidfile'] = False
376 # if defined dont take the configfile value even if the defined value is None
377 keys = ['xmlrpc_interface', 'xmlrpc_port', 'db_name', 'db_user', 'db_password', 'db_host',
378 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout',
379 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
380 'netrpc_interface', 'netrpc_port', 'db_maxconn', 'import_partial', 'addons_path',
381 'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',
382 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
383 'static_http_enable', 'static_http_document_root', 'static_http_url_prefix',
384 'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level'
388 # Copy the command-line argument (except the special case for log_handler, due to
389 # action=append requiring a real default, so we cannot use the my_default workaround)
390 if getattr(opt, arg) and getattr(opt, arg) != DEFAULT_LOG_HANDLER:
391 self.options[arg] = getattr(opt, arg)
392 # ... or keep, but cast, the config file value.
393 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
394 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
397 if isinstance(self.options['log_handler'], basestring):
398 self.options['log_handler'] = self.options['log_handler'].split(',')
400 # if defined but None take the configfile value
402 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
403 'debug_mode', 'smtp_ssl', 'load_language',
404 'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
405 'list_db', 'xmlrpcs', 'proxy_mode',
406 'test_file', 'test_disable', 'test_commit', 'test_report_directory',
407 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads',
408 'virtual_memory_limit', 'virtual_memory_reset', 'cpu_time_limit', 'unaccent',
412 # Copy the command-line argument...
413 if getattr(opt, arg) is not None:
414 self.options[arg] = getattr(opt, arg)
415 # ... or keep, but cast, the config file value.
416 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
417 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
419 if opt.assert_exit_level:
420 self.options['assert_exit_level'] = self._LOGLEVELS[opt.assert_exit_level]
422 self.options['assert_exit_level'] = self._LOGLEVELS.get(self.options['assert_exit_level']) or int(self.options['assert_exit_level'])
424 self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
425 if not self.options['addons_path'] or self.options['addons_path']=='None':
426 self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
428 self.options['addons_path'] = ",".join(
429 os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
430 for x in self.options['addons_path'].split(','))
432 self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
433 self.options["demo"] = not opt.without_demo and self.options['init'] or {}
434 self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
435 self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
436 self.options['translate_modules'].sort()
438 # TODO checking the type of the parameters should be done for every
439 # parameters, not just the timezone.
440 # The call to get_server_timezone() sets the timezone; this should
441 # probably done here.
442 if self.options['timezone']:
443 # Prevent the timezone to be True. (The config file parsing changes
444 # the string 'True' to the boolean value True. It would be probably
445 # be better to remove that conversion.)
446 die(not isinstance(self.options['timezone'], basestring),
447 "Invalid timezone value in configuration or environment: %r.\n"
448 "Please fix this in your configuration." %(self.options['timezone']))
450 # If an explicit TZ was provided in the config, make sure it is known
453 pytz.timezone(self.options['timezone'])
454 except pytz.UnknownTimeZoneError:
455 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
457 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
461 self.options['pg_path'] = opt.pg_path
463 if self.options.get('language', False):
464 if len(self.options['language']) > 5:
465 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
467 if not self.options['db_user']:
470 self.options['db_user'] = getpass.getuser()
472 self.options['db_user'] = None
474 die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
476 if self.options['db_password']:
477 if sys.platform == 'win32' and not self.options['db_host']:
478 self.options['db_host'] = 'localhost'
479 #if self.options['db_host']:
480 # self._generate_pgpassfile()
485 openerp.conf.max_cron_threads = self.options['max_cron_threads']
487 openerp.conf.addons_paths = self.options['addons_path'].split(',')
488 if opt.server_wide_modules:
489 openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
491 openerp.conf.server_wide_modules = ['web']
493 openerp.modules.module.initialize_sys_path()
494 openerp.modules.loading.open_openerp_namespace()
496 def _generate_pgpassfile(self):
498 Generate the pgpass file with the parameters from the command line (db_host, db_user,
501 Used because pg_dump and pg_restore can not accept the password on the command line.
503 is_win32 = sys.platform == 'win32'
505 filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
507 filename = os.path.join(os.environ['HOME'], '.pgpass')
509 text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
511 if os.path.exists(filename):
512 content = [x.strip() for x in file(filename, 'r').readlines()]
513 if text_to_add in content:
516 fp = file(filename, 'a+')
517 fp.write(text_to_add + "\n")
525 x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
526 y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
527 _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
532 os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
534 def _is_addons_path(self, path):
535 for f in os.listdir(path):
536 modpath = os.path.join(path, f)
537 if os.path.isdir(modpath):
538 def hasfile(filename):
539 return os.path.isfile(os.path.join(modpath, filename))
540 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
544 def _check_addons_path(self, option, opt, value, parser):
546 for path in value.split(','):
548 res = os.path.abspath(os.path.expanduser(path))
549 if not os.path.isdir(res):
550 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
551 if not self._is_addons_path(res):
552 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
555 setattr(parser.values, option.dest, ",".join(ad_paths))
558 p = ConfigParser.ConfigParser()
560 p.read([self.rcfile])
561 for (name,value) in p.items('options'):
562 if value=='True' or value=='true':
564 if value=='False' or value=='false':
566 self.options[name] = value
567 #parse the other sections, as well
568 for sec in p.sections():
571 if not self.misc.has_key(sec):
573 for (name, value) in p.items(sec):
574 if value=='True' or value=='true':
576 if value=='False' or value=='false':
578 self.misc[sec][name] = value
581 except ConfigParser.NoSectionError:
585 p = ConfigParser.ConfigParser()
586 loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
587 p.add_section('options')
588 for opt in sorted(self.options.keys()):
589 if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
591 if opt in self.blacklist_for_save:
593 if opt in ('log_level', 'assert_exit_level'):
594 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
596 p.set('options', opt, self.options[opt])
598 for sec in sorted(self.misc.keys()):
600 for opt in sorted(self.misc[sec].keys()):
601 p.set(sec,opt,self.misc[sec][opt])
603 # try to create the directories and write the file
605 rc_exists = os.path.exists(self.rcfile)
606 if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
607 os.makedirs(os.path.dirname(self.rcfile))
609 p.write(file(self.rcfile, 'w'))
611 os.chmod(self.rcfile, 0600)
613 sys.stderr.write("ERROR: couldn't write the config file\n")
616 # what to do if impossible?
617 sys.stderr.write("ERROR: couldn't create the config directory\n")
619 def get(self, key, default=None):
620 return self.options.get(key, default)
622 def get_misc(self, sect, key, default=None):
623 return self.misc.get(sect,{}).get(key, default)
625 def __setitem__(self, key, value):
626 self.options[key] = value
627 if key in self.options and isinstance(self.options[key], basestring) and \
628 key in self.casts and self.casts[key].type in optparse.Option.TYPE_CHECKER:
629 self.options[key] = optparse.Option.TYPE_CHECKER[self.casts[key].type](self.casts[key], key, self.options[key])
631 def __getitem__(self, key):
632 return self.options[key]
634 config = configmanager()
637 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: