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-2014 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
34 class MyOption (optparse.Option, object):
35 """ optparse Option with two additional attributes.
37 The list of command line options (getopt.Option) is used to create the
38 list of the configuration file options. When reading the file, and then
39 reading the command line arguments, we don't want optparse.parse results
40 to override the configuration file values. But if we provide default
41 values to optparse, optparse will return them and we can't know if they
42 were really provided by the user or not. A solution is to not use
43 optparse's default attribute, but use a custom one (that will be copied
44 to create the default values of the configuration file).
47 def __init__(self, *opts, **attrs):
48 self.my_default = attrs.pop('my_default', None)
49 super(MyOption, self).__init__(*opts, **attrs)
54 from OpenSSL import SSL
57 return hasattr(socket, 'ssl') and hasattr(SSL, "Connection")
61 DEFAULT_LOG_HANDLER = [':INFO']
63 def _get_default_datadir():
64 return appdirs.user_data_dir(appname='OpenERP', appauthor=release.author)
66 class configmanager(object):
67 def __init__(self, fname=None):
68 # Options not exposed on the command line. Command line options will be added
69 # from optparse's parser.
71 'admin_passwd': 'admin',
72 'csv_internal_sep': ',',
73 'publisher_warranty_url': 'http://services.openerp.com/publisher-warranty/',
78 # Not exposed in the configuration file.
79 self.blacklist_for_save = set(
80 ['publisher_warranty_url', 'load_language', 'root_path',
81 'init', 'save', 'config', 'update', 'stop_after_init'])
83 # dictionary mapping option destination (keys in self.options) to MyOptions.
87 self.config_file = fname
88 self.has_ssl = check_ssl()
90 self._LOGLEVELS = dict([(getattr(loglevels, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET')])
92 version = "%s %s" % (release.description, release.version)
93 self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
95 # Server startup config
96 group = optparse.OptionGroup(parser, "Common options")
97 group.add_option("-c", "--config", dest="config", help="specify alternate config file")
98 group.add_option("-s", "--save", action="store_true", dest="save", default=False,
99 help="save configuration to ~/.openerp_serverrc")
100 group.add_option("-i", "--init", dest="init", help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
101 group.add_option("-u", "--update", dest="update",
102 help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
103 group.add_option("--without-demo", dest="without_demo",
104 help="disable loading demo data for modules to be installed (comma-separated, use \"all\" for all modules). Requires -d and -i. Default is %default",
106 group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
107 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.")
108 group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
109 group.add_option("--addons-path", dest="addons_path",
110 help="specify additional addons paths (separated by commas).",
111 action="callback", callback=self._check_addons_path, nargs=1, type="string")
112 group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
114 group.add_option("-D", "--data-dir", dest="data_dir", my_default=_get_default_datadir(),
115 help="Directory where to store OpenERP data")
116 parser.add_option_group(group)
119 group = optparse.OptionGroup(parser, "XML-RPC Configuration")
120 group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
121 help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
122 group.add_option("--xmlrpc-port", dest="xmlrpc_port", my_default=8069,
123 help="specify the TCP port for the XML-RPC protocol", type="int")
124 group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", my_default=True,
125 help="disable the XML-RPC protocol")
126 group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
127 help="Enable correct behavior when behind a reverse proxy")
128 group.add_option("--longpolling-port", dest="longpolling_port", my_default=8072,
129 help="specify the TCP port for longpolling requests", type="int")
130 parser.add_option_group(group)
133 title = "XML-RPC Secure Configuration"
135 title += " (disabled as ssl is unavailable)"
137 group = optparse.OptionGroup(parser, title)
138 group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
139 help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
140 group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
141 help="specify the TCP port for the XML-RPC Secure protocol", type="int")
142 group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
143 help="disable the XML-RPC Secure protocol")
144 group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
145 help="specify the certificate file for the SSL connection")
146 group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
147 help="specify the private key file for the SSL connection")
148 parser.add_option_group(group)
151 # TODO move to web addons after MetaOption merge
152 group = optparse.OptionGroup(parser, "Web interface Configuration")
153 group.add_option("--db-filter", dest="dbfilter", default='.*',
154 help="Filter listed database", metavar="REGEXP")
155 parser.add_option_group(group)
158 group = optparse.OptionGroup(parser, "Static HTTP service")
159 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")
160 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/')")
161 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 '/')")
162 parser.add_option_group(group)
165 group = optparse.OptionGroup(parser, "Testing Configuration")
166 group.add_option("--test-file", dest="test_file", my_default=False,
167 help="Launch a python or YML test file.")
168 group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
169 help="If set, will save sample of all reports in this directory.")
170 group.add_option("--test-enable", action="store_true", dest="test_enable",
171 my_default=False, help="Enable YAML and unit tests.")
172 group.add_option("--test-commit", action="store_true", dest="test_commit",
173 my_default=False, help="Commit database changes performed by YAML or XML tests.")
174 parser.add_option_group(group)
177 group = optparse.OptionGroup(parser, "Logging Configuration")
178 group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
179 group.add_option("--logrotate", dest="logrotate", action="store_true", my_default=False, help="enable logfile rotation")
180 group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
181 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")')
182 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')
183 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')
184 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')
185 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')
186 # For backward-compatibility, map the old log levels to something
188 levels = ['info', 'debug_rpc', 'warn', 'test', 'critical',
189 'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset']
190 group.add_option('--log-level', dest='log_level', type='choice', choices=levels,
191 my_default='info', help='specify the level of the logging. Accepted values: ' + str(levels) + ' (deprecated option).')
193 parser.add_option_group(group)
196 group = optparse.OptionGroup(parser, "SMTP Configuration")
197 group.add_option('--email-from', dest='email_from', my_default=False,
198 help='specify the SMTP email address for sending email')
199 group.add_option('--smtp', dest='smtp_server', my_default='localhost',
200 help='specify the SMTP server for sending email')
201 group.add_option('--smtp-port', dest='smtp_port', my_default=25,
202 help='specify the SMTP port', type="int")
203 group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
204 help='if passed, SMTP connections will be encrypted with SSL (STARTTLS)')
205 group.add_option('--smtp-user', dest='smtp_user', my_default=False,
206 help='specify the SMTP username for sending email')
207 group.add_option('--smtp-password', dest='smtp_password', my_default=False,
208 help='specify the SMTP password for sending email')
209 parser.add_option_group(group)
211 group = optparse.OptionGroup(parser, "Database related options")
212 group.add_option("-d", "--database", dest="db_name", my_default=False,
213 help="specify the database name")
214 group.add_option("-r", "--db_user", dest="db_user", my_default=False,
215 help="specify the database user name")
216 group.add_option("-w", "--db_password", dest="db_password", my_default=False,
217 help="specify the database password")
218 group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
219 group.add_option("--db_host", dest="db_host", my_default=False,
220 help="specify the database host")
221 group.add_option("--db_port", dest="db_port", my_default=False,
222 help="specify the database port", type="int")
223 group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
224 help="specify the the maximum number of physical connections to posgresql")
225 group.add_option("--db-template", dest="db_template", my_default="template1",
226 help="specify a custom database template to create a new database")
227 parser.add_option_group(group)
229 group = optparse.OptionGroup(parser, "Internationalisation options",
230 "Use these options to translate OpenERP to another language."
231 "See i18n section of the user manual. Option '-d' is mandatory."
232 "Option '-l' is mandatory in case of importation"
234 group.add_option('--load-language', dest="load_language",
235 help="specifies the languages for the translations you want to be loaded")
236 group.add_option('-l', "--language", dest="language",
237 help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
238 group.add_option("--i18n-export", dest="translate_out",
239 help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
240 group.add_option("--i18n-import", dest="translate_in",
241 help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
242 group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
243 help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
244 group.add_option("--modules", dest="translate_modules",
245 help="specify modules to export. Use in combination with --i18n-export")
246 parser.add_option_group(group)
248 security = optparse.OptionGroup(parser, 'Security-related options')
249 security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
250 help="disable the ability to return the list of databases")
251 parser.add_option_group(security)
254 group = optparse.OptionGroup(parser, "Advanced options")
255 if os.name == 'posix':
256 group.add_option('--auto-reload', dest='auto_reload', action='store_true', my_default=False, help='enable auto reload')
257 group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
258 group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
259 help="stop the server after its initialization")
260 group.add_option("-t", "--timezone", dest="timezone", my_default=False,
261 help="specify reference timezone for the server (e.g. Europe/Brussels")
262 group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
263 help="Force a limit on the maximum number of records kept in the virtual "
264 "osv_memory tables. The default is False, which means no count-based limit.",
266 group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
267 help="Force a limit on the maximum age of records kept in the virtual "
268 "osv_memory tables. This is a decimal value expressed in hours, "
269 "and the default is 1 hour.",
271 group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=2,
272 help="Maximum number of threads processing concurrently cron jobs (default 2).",
274 group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
275 help="Use the unaccent function provided by the database when available.")
276 parser.add_option_group(group)
278 if os.name == 'posix':
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=2048 * 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=2560 * 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...
362 rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
364 rcfilepath = os.path.expanduser('~/.openerp_serverrc')
366 self.rcfile = os.path.abspath(
367 self.config_file or opt.config \
368 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', 'longpolling_port',
380 'db_name', 'db_user', 'db_password', 'db_host',
381 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port',
382 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
383 'db_maxconn', 'import_partial', 'addons_path',
384 'xmlrpc', 'syslog', 'without_demo', 'timezone',
385 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
386 'static_http_enable', 'static_http_document_root', 'static_http_url_prefix',
387 'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level'
391 # Copy the command-line argument (except the special case for log_handler, due to
392 # action=append requiring a real default, so we cannot use the my_default workaround)
393 if getattr(opt, arg) and getattr(opt, arg) != DEFAULT_LOG_HANDLER:
394 self.options[arg] = getattr(opt, arg)
395 # ... or keep, but cast, the config file value.
396 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
397 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', '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',
414 'auto_reload', 'workers',
415 'limit_memory_hard', 'limit_memory_soft',
416 'limit_time_cpu', 'limit_time_real', 'limit_request',
419 if os.name == 'posix':
422 self.options.update(dict.fromkeys(posix_keys, None))
424 # Copy the command-line arguments...
426 if getattr(opt, arg) is not None:
427 self.options[arg] = getattr(opt, arg)
428 # ... or keep, but cast, the config file value.
429 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
430 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
432 self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
433 if not self.options['addons_path'] or self.options['addons_path']=='None':
434 self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
436 self.options['addons_path'] = ",".join(
437 os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
438 for x in self.options['addons_path'].split(','))
440 self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
441 self.options["demo"] = not opt.without_demo and self.options['init'] or {}
442 self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
443 self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
444 self.options['translate_modules'].sort()
446 # TODO checking the type of the parameters should be done for every
447 # parameters, not just the timezone.
448 # The call to get_server_timezone() sets the timezone; this should
449 # probably done here.
450 if self.options['timezone']:
451 # Prevent the timezone to be True. (The config file parsing changes
452 # the string 'True' to the boolean value True. It would be probably
453 # be better to remove that conversion.)
454 die(not isinstance(self.options['timezone'], basestring),
455 "Invalid timezone value in configuration or environment: %r.\n"
456 "Please fix this in your configuration." %(self.options['timezone']))
458 # If an explicit TZ was provided in the config, make sure it is known
461 pytz.timezone(self.options['timezone'])
462 except pytz.UnknownTimeZoneError:
463 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
465 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
469 self.options['pg_path'] = opt.pg_path
471 if self.options.get('language', False):
472 if len(self.options['language']) > 5:
473 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
475 if not self.options['db_user']:
478 self.options['db_user'] = getpass.getuser()
480 self.options['db_user'] = None
482 die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
484 if self.options['db_password']:
485 if sys.platform == 'win32' and not self.options['db_host']:
486 self.options['db_host'] = 'localhost'
487 #if self.options['db_host']:
488 # self._generate_pgpassfile()
493 openerp.conf.addons_paths = self.options['addons_path'].split(',')
494 if opt.server_wide_modules:
495 openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
497 openerp.conf.server_wide_modules = ['web','web_kanban']
499 openerp.modules.module.initialize_sys_path()
501 def _generate_pgpassfile(self):
503 Generate the pgpass file with the parameters from the command line (db_host, db_user,
506 Used because pg_dump and pg_restore can not accept the password on the command line.
508 is_win32 = sys.platform == 'win32'
510 filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
512 filename = os.path.join(os.environ['HOME'], '.pgpass')
514 text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
516 if os.path.exists(filename):
517 content = [x.strip() for x in file(filename, 'r').readlines()]
518 if text_to_add in content:
521 fp = file(filename, 'a+')
522 fp.write(text_to_add + "\n")
530 x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
531 y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
532 _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
537 os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
539 def _is_addons_path(self, path):
540 for f in os.listdir(path):
541 modpath = os.path.join(path, f)
542 if os.path.isdir(modpath):
543 def hasfile(filename):
544 return os.path.isfile(os.path.join(modpath, filename))
545 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
549 def _check_addons_path(self, option, opt, value, parser):
551 for path in value.split(','):
553 res = os.path.abspath(os.path.expanduser(path))
554 if not os.path.isdir(res):
555 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
556 if not self._is_addons_path(res):
557 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
560 setattr(parser.values, option.dest, ",".join(ad_paths))
563 p = ConfigParser.ConfigParser()
565 p.read([self.rcfile])
566 for (name,value) in p.items('options'):
567 if value=='True' or value=='true':
569 if value=='False' or value=='false':
571 self.options[name] = value
572 #parse the other sections, as well
573 for sec in p.sections():
576 if not self.misc.has_key(sec):
578 for (name, value) in p.items(sec):
579 if value=='True' or value=='true':
581 if value=='False' or value=='false':
583 self.misc[sec][name] = value
586 except ConfigParser.NoSectionError:
590 p = ConfigParser.ConfigParser()
591 loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
592 p.add_section('options')
593 for opt in sorted(self.options.keys()):
594 if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
596 if opt in self.blacklist_for_save:
598 if opt in ('log_level',):
599 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
601 p.set('options', opt, self.options[opt])
603 for sec in sorted(self.misc.keys()):
605 for opt in sorted(self.misc[sec].keys()):
606 p.set(sec,opt,self.misc[sec][opt])
608 # try to create the directories and write the file
610 rc_exists = os.path.exists(self.rcfile)
611 if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
612 os.makedirs(os.path.dirname(self.rcfile))
614 p.write(file(self.rcfile, 'w'))
616 os.chmod(self.rcfile, 0600)
618 sys.stderr.write("ERROR: couldn't write the config file\n")
621 # what to do if impossible?
622 sys.stderr.write("ERROR: couldn't create the config directory\n")
624 def get(self, key, default=None):
625 return self.options.get(key, default)
627 def get_misc(self, sect, key, default=None):
628 return self.misc.get(sect,{}).get(key, default)
630 def __setitem__(self, key, value):
631 self.options[key] = value
632 if key in self.options and isinstance(self.options[key], basestring) and \
633 key in self.casts and self.casts[key].type in optparse.Option.TYPE_CHECKER:
634 self.options[key] = optparse.Option.TYPE_CHECKER[self.casts[key].type](self.casts[key], key, self.options[key])
636 def __getitem__(self, key):
637 return self.options[key]
640 def addons_data_dir(self):
641 d = os.path.join(self['data_dir'], 'addons', release.series)
642 if not os.path.exists(d):
649 def session_dir(self):
650 d = os.path.join(self['data_dir'], 'sessions', release.series)
651 if not os.path.exists(d):
657 config = configmanager()
660 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: