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 'login_message': False,
70 'publisher_warranty_url': 'http://services.openerp.com/publisher-warranty/',
75 # Not exposed in the configuration file.
76 self.blacklist_for_save = set(
77 ['publisher_warranty_url', 'load_language', 'root_path',
78 'init', 'save', 'config', 'update', 'stop_after_init'])
80 # dictionary mapping option destination (keys in self.options) to MyOptions.
84 self.config_file = fname
85 self.has_ssl = check_ssl()
87 self._LOGLEVELS = dict([(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'TEST', 'DEBUG', 'NOTSET')])
89 version = "%s %s" % (release.description, release.version)
90 self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
92 # Server startup config
93 group = optparse.OptionGroup(parser, "Common options")
94 group.add_option("-c", "--config", dest="config", help="specify alternate config file")
95 group.add_option("-s", "--save", action="store_true", dest="save", default=False,
96 help="save configuration to ~/.openerp_serverrc")
97 group.add_option("-i", "--init", dest="init", help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
98 group.add_option("-u", "--update", dest="update",
99 help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
100 group.add_option("--without-demo", dest="without_demo",
101 help="disable loading demo data for modules to be installed (comma-separated, use \"all\" for all modules). Requires -d and -i. Default is %default",
103 group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
104 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.")
105 group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
106 group.add_option("--addons-path", dest="addons_path",
107 help="specify additional addons paths (separated by commas).",
108 action="callback", callback=self._check_addons_path, nargs=1, type="string")
109 group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
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 parser.add_option_group(group)
125 title = "XML-RPC Secure Configuration"
127 title += " (disabled as ssl is unavailable)"
129 group = optparse.OptionGroup(parser, title)
130 group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
131 help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
132 group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
133 help="specify the TCP port for the XML-RPC Secure protocol", type="int")
134 group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
135 help="disable the XML-RPC Secure protocol")
136 group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
137 help="specify the certificate file for the SSL connection")
138 group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
139 help="specify the private key file for the SSL connection")
140 parser.add_option_group(group)
143 group = optparse.OptionGroup(parser, "NET-RPC Configuration")
144 group.add_option("--netrpc-interface", dest="netrpc_interface", my_default='',
145 help="specify the TCP IP address for the NETRPC protocol")
146 group.add_option("--netrpc-port", dest="netrpc_port", my_default=8070,
147 help="specify the TCP port for the NETRPC protocol", type="int")
148 # Needed a few day for runbot and saas
149 group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=False, help="disable the NETRPC protocol")
150 group.add_option("--netrpc", dest="netrpc", action="store_true", my_default=False, help="enable 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-enable", action="store_true", dest="test_enable",
174 my_default=False, help="Enable YAML and unit tests.")
175 group.add_option("--test-commit", action="store_true", dest="test_commit",
176 my_default=False, help="Commit database changes performed by YAML or XML tests.")
177 parser.add_option_group(group)
180 group = optparse.OptionGroup(parser, "Logging Configuration")
181 group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
182 group.add_option("--no-logrotate", dest="logrotate", action="store_false", my_default=True, help="do not rotate the logfile")
183 group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
184 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")')
185 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')
186 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')
187 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')
188 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')
189 # For backward-compatibility, map the old log levels to something
191 levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
192 'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
193 group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
194 my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
196 parser.add_option_group(group)
199 group = optparse.OptionGroup(parser, "SMTP Configuration")
200 group.add_option('--email-from', dest='email_from', my_default=False,
201 help='specify the SMTP email address for sending email')
202 group.add_option('--smtp', dest='smtp_server', my_default='localhost',
203 help='specify the SMTP server for sending email')
204 group.add_option('--smtp-port', dest='smtp_port', my_default=25,
205 help='specify the SMTP port', type="int")
206 group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
207 help='if passed, SMTP connections will be encrypted with SSL (STARTTLS)')
208 group.add_option('--smtp-user', dest='smtp_user', my_default=False,
209 help='specify the SMTP username for sending email')
210 group.add_option('--smtp-password', dest='smtp_password', my_default=False,
211 help='specify the SMTP password for sending email')
212 parser.add_option_group(group)
214 group = optparse.OptionGroup(parser, "Database related options")
215 group.add_option("-d", "--database", dest="db_name", my_default=False,
216 help="specify the database name")
217 group.add_option("-r", "--db_user", dest="db_user", my_default=False,
218 help="specify the database user name")
219 group.add_option("-w", "--db_password", dest="db_password", my_default=False,
220 help="specify the database password")
221 group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
222 group.add_option("--db_host", dest="db_host", my_default=False,
223 help="specify the database host")
224 group.add_option("--db_port", dest="db_port", my_default=False,
225 help="specify the database port", type="int")
226 group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
227 help="specify the the maximum number of physical connections to posgresql")
228 group.add_option("--db-template", dest="db_template", my_default="template1",
229 help="specify a custom database template to create a new database")
230 parser.add_option_group(group)
232 group = optparse.OptionGroup(parser, "Internationalisation options",
233 "Use these options to translate OpenERP to another language."
234 "See i18n section of the user manual. Option '-d' is mandatory."
235 "Option '-l' is mandatory in case of importation"
237 group.add_option('--load-language', dest="load_language",
238 help="specifies the languages for the translations you want to be loaded")
239 group.add_option('-l', "--language", dest="language",
240 help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
241 group.add_option("--i18n-export", dest="translate_out",
242 help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
243 group.add_option("--i18n-import", dest="translate_in",
244 help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
245 group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
246 help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
247 group.add_option("--modules", dest="translate_modules",
248 help="specify modules to export. Use in combination with --i18n-export")
249 parser.add_option_group(group)
251 security = optparse.OptionGroup(parser, 'Security-related options')
252 security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
253 help="disable the ability to return the list of databases")
254 parser.add_option_group(security)
257 group = optparse.OptionGroup(parser, "Advanced options")
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=2,
273 help="Maximum number of threads processing concurrently cron jobs (default 2).",
275 group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
276 help="Use the unaccent function provided by the database when available.")
277 parser.add_option_group(group)
279 group = optparse.OptionGroup(parser, "Multiprocessing options")
280 # TODO sensible default for the three following limits.
281 group.add_option("--workers", dest="workers", my_default=0,
282 help="Specify the number of workers, 0 disable prefork mode.",
284 group.add_option("--limit-memory-soft", dest="limit_memory_soft", my_default=640 * 1024 * 1024,
285 help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request (default 671088640 aka 640MB).",
287 group.add_option("--limit-memory-hard", dest="limit_memory_hard", my_default=768 * 1024 * 1024,
288 help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail (default 805306368 aka 768MB).",
290 group.add_option("--limit-time-cpu", dest="limit_time_cpu", my_default=60,
291 help="Maximum allowed CPU time per request (default 60).",
293 group.add_option("--limit-time-real", dest="limit_time_real", my_default=120,
294 help="Maximum allowed Real time per request (default 120).",
296 group.add_option("--limit-request", dest="limit_request", my_default=8192,
297 help="Maximum number of request to be processed per worker (default 8192).",
299 parser.add_option_group(group)
301 # Copy all optparse options (i.e. MyOption) into self.options.
302 for group in parser.option_groups:
303 for option in group.option_list:
304 if option.dest not in self.options:
305 self.options[option.dest] = option.my_default
306 self.casts[option.dest] = option
308 self.parse_config(None, False)
310 def parse_config(self, args=None, complete=True):
311 """ Parse the configuration file (if any) and the command-line
314 This method initializes openerp.tools.config and openerp.conf (the
315 former should be removed in the furture) with library-wide
316 configuration values.
318 This method must be called before proper usage of this library can be
321 Typical usage of this method:
323 openerp.tools.config.parse_config(sys.argv[1:])
325 :param complete: this is a hack used in __init__(), leave it to True.
330 opt, args = self.parser.parse_args(args)
334 self.parser.error(msg)
336 # Ensures no illegitimate argument is silently discarded (avoids insidious "hyphen to dash" problem)
337 die(args, "unrecognized parameters: '%s'" % " ".join(args))
339 die(bool(opt.syslog) and bool(opt.logfile),
340 "the syslog and logfile options are exclusive")
342 die(opt.translate_in and (not opt.language or not opt.db_name),
343 "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
345 die(opt.overwrite_existing_translations and not (opt.translate_in or opt.update),
346 "the i18n-overwrite option cannot be used without the i18n-import option or without the update option")
348 die(opt.translate_out and (not opt.db_name),
349 "the i18n-export option cannot be used without the database (-d) option")
351 # Check if the config file exists (-c used, but not -s)
352 die(not opt.save and opt.config and not os.path.exists(opt.config),
353 "The config file '%s' selected with -c/--config doesn't exist, "\
354 "use -s/--save if you want to generate it"% opt.config)
356 # place/search the config file on Win32 near the server installation
357 # (../etc from the server)
358 # 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,
359 # else he won't be able to save the configurations, or even to start the server...
361 rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
363 rcfilepath = os.path.expanduser('~/.openerp_serverrc')
365 self.rcfile = os.path.abspath(
366 self.config_file or opt.config \
367 or os.environ.get('OPENERP_SERVER') or rcfilepath)
371 # Verify that we want to log or not, if not the output will go to stdout
372 if self.options['logfile'] in ('None', 'False'):
373 self.options['logfile'] = False
374 # the same for the pidfile
375 if self.options['pidfile'] in ('None', 'False'):
376 self.options['pidfile'] = False
378 # if defined dont take the configfile value even if the defined value is None
379 keys = ['xmlrpc_interface', 'xmlrpc_port', 'db_name', 'db_user', 'db_password', 'db_host',
380 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port',
381 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
382 'netrpc_interface', 'netrpc_port', 'db_maxconn', 'import_partial', 'addons_path',
383 'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',
384 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
385 'static_http_enable', 'static_http_document_root', 'static_http_url_prefix',
386 'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level'
390 # Copy the command-line argument (except the special case for log_handler, due to
391 # action=append requiring a real default, so we cannot use the my_default workaround)
392 if getattr(opt, arg) and getattr(opt, arg) != DEFAULT_LOG_HANDLER:
393 self.options[arg] = getattr(opt, arg)
394 # ... or keep, but cast, the config file value.
395 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
396 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
399 if isinstance(self.options['log_handler'], basestring):
400 self.options['log_handler'] = self.options['log_handler'].split(',')
402 # if defined but None take the configfile value
404 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
405 'debug_mode', 'smtp_ssl', 'load_language',
406 'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
407 'list_db', 'xmlrpcs', 'proxy_mode',
408 'test_file', 'test_enable', 'test_commit', 'test_report_directory',
409 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent',
410 'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request'
414 # Copy the command-line argument...
415 if getattr(opt, arg) is not None:
416 self.options[arg] = getattr(opt, arg)
417 # ... or keep, but cast, the config file value.
418 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
419 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
421 self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
422 if not self.options['addons_path'] or self.options['addons_path']=='None':
423 self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
425 self.options['addons_path'] = ",".join(
426 os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
427 for x in self.options['addons_path'].split(','))
429 self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
430 self.options["demo"] = not opt.without_demo and self.options['init'] or {}
431 self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
432 self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
433 self.options['translate_modules'].sort()
435 # TODO checking the type of the parameters should be done for every
436 # parameters, not just the timezone.
437 # The call to get_server_timezone() sets the timezone; this should
438 # probably done here.
439 if self.options['timezone']:
440 # Prevent the timezone to be True. (The config file parsing changes
441 # the string 'True' to the boolean value True. It would be probably
442 # be better to remove that conversion.)
443 die(not isinstance(self.options['timezone'], basestring),
444 "Invalid timezone value in configuration or environment: %r.\n"
445 "Please fix this in your configuration." %(self.options['timezone']))
447 # If an explicit TZ was provided in the config, make sure it is known
450 pytz.timezone(self.options['timezone'])
451 except pytz.UnknownTimeZoneError:
452 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
454 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
458 self.options['pg_path'] = opt.pg_path
460 if self.options.get('language', False):
461 if len(self.options['language']) > 5:
462 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
464 if not self.options['db_user']:
467 self.options['db_user'] = getpass.getuser()
469 self.options['db_user'] = None
471 die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
473 if self.options['db_password']:
474 if sys.platform == 'win32' and not self.options['db_host']:
475 self.options['db_host'] = 'localhost'
476 #if self.options['db_host']:
477 # self._generate_pgpassfile()
482 openerp.conf.addons_paths = self.options['addons_path'].split(',')
483 if opt.server_wide_modules:
484 openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
486 openerp.conf.server_wide_modules = ['web','web_kanban']
488 openerp.modules.module.initialize_sys_path()
490 def _generate_pgpassfile(self):
492 Generate the pgpass file with the parameters from the command line (db_host, db_user,
495 Used because pg_dump and pg_restore can not accept the password on the command line.
497 is_win32 = sys.platform == 'win32'
499 filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
501 filename = os.path.join(os.environ['HOME'], '.pgpass')
503 text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
505 if os.path.exists(filename):
506 content = [x.strip() for x in file(filename, 'r').readlines()]
507 if text_to_add in content:
510 fp = file(filename, 'a+')
511 fp.write(text_to_add + "\n")
519 x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
520 y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
521 _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
526 os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
528 def _is_addons_path(self, path):
529 for f in os.listdir(path):
530 modpath = os.path.join(path, f)
531 if os.path.isdir(modpath):
532 def hasfile(filename):
533 return os.path.isfile(os.path.join(modpath, filename))
534 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
538 def _check_addons_path(self, option, opt, value, parser):
540 for path in value.split(','):
542 res = os.path.abspath(os.path.expanduser(path))
543 if not os.path.isdir(res):
544 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
545 if not self._is_addons_path(res):
546 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
549 setattr(parser.values, option.dest, ",".join(ad_paths))
552 p = ConfigParser.ConfigParser()
554 p.read([self.rcfile])
555 for (name,value) in p.items('options'):
556 if value=='True' or value=='true':
558 if value=='False' or value=='false':
560 self.options[name] = value
561 #parse the other sections, as well
562 for sec in p.sections():
565 if not self.misc.has_key(sec):
567 for (name, value) in p.items(sec):
568 if value=='True' or value=='true':
570 if value=='False' or value=='false':
572 self.misc[sec][name] = value
575 except ConfigParser.NoSectionError:
579 p = ConfigParser.ConfigParser()
580 loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
581 p.add_section('options')
582 for opt in sorted(self.options.keys()):
583 if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
585 if opt in self.blacklist_for_save:
587 if opt in ('log_level',):
588 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
590 p.set('options', opt, self.options[opt])
592 for sec in sorted(self.misc.keys()):
594 for opt in sorted(self.misc[sec].keys()):
595 p.set(sec,opt,self.misc[sec][opt])
597 # try to create the directories and write the file
599 rc_exists = os.path.exists(self.rcfile)
600 if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
601 os.makedirs(os.path.dirname(self.rcfile))
603 p.write(file(self.rcfile, 'w'))
605 os.chmod(self.rcfile, 0600)
607 sys.stderr.write("ERROR: couldn't write the config file\n")
610 # what to do if impossible?
611 sys.stderr.write("ERROR: couldn't create the config directory\n")
613 def get(self, key, default=None):
614 return self.options.get(key, default)
616 def get_misc(self, sect, key, default=None):
617 return self.misc.get(sect,{}).get(key, default)
619 def __setitem__(self, key, value):
620 self.options[key] = value
621 if key in self.options and isinstance(self.options[key], basestring) and \
622 key in self.casts and self.casts[key].type in optparse.Option.TYPE_CHECKER:
623 self.options[key] = optparse.Option.TYPE_CHECKER[self.casts[key].type](self.casts[key], key, self.options[key])
625 def __getitem__(self, key):
626 return self.options[key]
628 config = configmanager()
631 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: