1 #openerp.loggers.handlers. -*- 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 home = os.path.expanduser('~')
65 if os.path.exists(home):
66 func = appdirs.user_data_dir
68 if sys.platform in ['win32', 'darwin']:
69 func = appdirs.site_data_dir
71 func = lambda **kwarg: "/var/lib/%s" % kwarg['appname'].lower()
72 # No "version" kwarg as session and filestore paths are shared against series
73 return func(appname=release.product_name, appauthor=release.author)
75 class configmanager(object):
76 def __init__(self, fname=None):
77 # Options not exposed on the command line. Command line options will be added
78 # from optparse's parser.
80 'admin_passwd': 'admin',
81 'csv_internal_sep': ',',
82 'publisher_warranty_url': 'http://services.openerp.com/publisher-warranty/',
87 # Not exposed in the configuration file.
88 self.blacklist_for_save = set([
89 'publisher_warranty_url', 'load_language', 'root_path',
90 'init', 'save', 'config', 'update', 'stop_after_init'
93 # dictionary mapping option destination (keys in self.options) to MyOptions.
97 self.config_file = fname
98 self.has_ssl = check_ssl()
100 self._LOGLEVELS = dict([
101 (getattr(loglevels, 'LOG_%s' % x), getattr(logging, x))
102 for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET')
105 version = "%s %s" % (release.description, release.version)
106 self.parser = parser = optparse.OptionParser(version=version, option_class=MyOption)
108 # Server startup config
109 group = optparse.OptionGroup(parser, "Common options")
110 group.add_option("-c", "--config", dest="config", help="specify alternate config file")
111 group.add_option("-s", "--save", action="store_true", dest="save", default=False,
112 help="save configuration to ~/.openerp_serverrc")
113 group.add_option("-i", "--init", dest="init", help="install one or more modules (comma-separated list, use \"all\" for all modules), requires -d")
114 group.add_option("-u", "--update", dest="update",
115 help="update one or more modules (comma-separated list, use \"all\" for all modules). Requires -d.")
116 group.add_option("--without-demo", dest="without_demo",
117 help="disable loading demo data for modules to be installed (comma-separated, use \"all\" for all modules). Requires -d and -i. Default is %default",
119 group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
120 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.")
121 group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
122 group.add_option("--addons-path", dest="addons_path",
123 help="specify additional addons paths (separated by commas).",
124 action="callback", callback=self._check_addons_path, nargs=1, type="string")
125 group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
127 group.add_option("-D", "--data-dir", dest="data_dir", my_default=_get_default_datadir(),
128 help="Directory where to store Odoo data")
129 parser.add_option_group(group)
132 group = optparse.OptionGroup(parser, "XML-RPC Configuration")
133 group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
134 help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
135 group.add_option("--xmlrpc-port", dest="xmlrpc_port", my_default=8069,
136 help="specify the TCP port for the XML-RPC protocol", type="int")
137 group.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", my_default=True,
138 help="disable the XML-RPC protocol")
139 group.add_option("--proxy-mode", dest="proxy_mode", action="store_true", my_default=False,
140 help="Enable correct behavior when behind a reverse proxy")
141 group.add_option("--longpolling-port", dest="longpolling_port", my_default=8072,
142 help="specify the TCP port for longpolling requests", type="int")
143 parser.add_option_group(group)
146 title = "XML-RPC Secure Configuration"
148 title += " (disabled as ssl is unavailable)"
150 group = optparse.OptionGroup(parser, title)
151 group.add_option("--xmlrpcs-interface", dest="xmlrpcs_interface", my_default='',
152 help="Specify the TCP IP address for the XML-RPC Secure protocol. The empty string binds to all interfaces.")
153 group.add_option("--xmlrpcs-port", dest="xmlrpcs_port", my_default=8071,
154 help="specify the TCP port for the XML-RPC Secure protocol", type="int")
155 group.add_option("--no-xmlrpcs", dest="xmlrpcs", action="store_false", my_default=True,
156 help="disable the XML-RPC Secure protocol")
157 group.add_option("--cert-file", dest="secure_cert_file", my_default='server.cert',
158 help="specify the certificate file for the SSL connection")
159 group.add_option("--pkey-file", dest="secure_pkey_file", my_default='server.pkey',
160 help="specify the private key file for the SSL connection")
161 parser.add_option_group(group)
164 group = optparse.OptionGroup(parser, "Web interface Configuration")
165 group.add_option("--db-filter", dest="dbfilter", my_default='.*',
166 help="Filter listed database", metavar="REGEXP")
167 parser.add_option_group(group)
170 group = optparse.OptionGroup(parser, "Testing Configuration")
171 group.add_option("--test-file", dest="test_file", my_default=False,
172 help="Launch a python or YML test file.")
173 group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
174 help="If set, will save sample of all reports in this directory.")
175 group.add_option("--test-enable", action="store_true", dest="test_enable",
176 my_default=False, help="Enable YAML and unit tests.")
177 group.add_option("--test-commit", action="store_true", dest="test_commit",
178 my_default=False, help="Commit database changes performed by YAML or XML tests.")
179 parser.add_option_group(group)
182 group = optparse.OptionGroup(parser, "Logging Configuration")
183 group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
184 group.add_option("--logrotate", dest="logrotate", action="store_true", my_default=False, help="enable logfile rotation")
185 group.add_option("--syslog", action="store_true", dest="syslog", my_default=False, help="Send the log to the syslog server")
186 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")')
187 group.add_option('--log-request', action="append_const", dest="log_handler", const="openerp.http.rpc.request:DEBUG", help='shortcut for --log-handler=openerp.http.rpc.request:DEBUG')
188 group.add_option('--log-response', action="append_const", dest="log_handler", const="openerp.http.rpc.response:DEBUG", help='shortcut for --log-handler=openerp.http.rpc.response:DEBUG')
189 group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.http:DEBUG", help='shortcut for --log-handler=openerp.http:DEBUG')
190 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')
191 group.add_option('--log-db', dest='log_db', help="Logging database", my_default=False)
192 # For backward-compatibility, map the old log levels to something
195 'info', 'debug_rpc', 'warn', 'test', 'critical',
196 'debug_sql', 'error', 'debug', 'debug_rpc_answer', 'notset'
198 group.add_option('--log-level', dest='log_level', type='choice',
199 choices=levels, my_default='info',
200 help='specify the level of the logging. Accepted values: %s (deprecated option).' % (levels,))
202 parser.add_option_group(group)
205 group = optparse.OptionGroup(parser, "SMTP Configuration")
206 group.add_option('--email-from', dest='email_from', my_default=False,
207 help='specify the SMTP email address for sending email')
208 group.add_option('--smtp', dest='smtp_server', my_default='localhost',
209 help='specify the SMTP server for sending email')
210 group.add_option('--smtp-port', dest='smtp_port', my_default=25,
211 help='specify the SMTP port', type="int")
212 group.add_option('--smtp-ssl', dest='smtp_ssl', action='store_true', my_default=False,
213 help='if passed, SMTP connections will be encrypted with SSL (STARTTLS)')
214 group.add_option('--smtp-user', dest='smtp_user', my_default=False,
215 help='specify the SMTP username for sending email')
216 group.add_option('--smtp-password', dest='smtp_password', my_default=False,
217 help='specify the SMTP password for sending email')
218 parser.add_option_group(group)
220 group = optparse.OptionGroup(parser, "Database related options")
221 group.add_option("-d", "--database", dest="db_name", my_default=False,
222 help="specify the database name")
223 group.add_option("-r", "--db_user", dest="db_user", my_default=False,
224 help="specify the database user name")
225 group.add_option("-w", "--db_password", dest="db_password", my_default=False,
226 help="specify the database password")
227 group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path")
228 group.add_option("--db_host", dest="db_host", my_default=False,
229 help="specify the database host")
230 group.add_option("--db_port", dest="db_port", my_default=False,
231 help="specify the database port", type="int")
232 group.add_option("--db_maxconn", dest="db_maxconn", type='int', my_default=64,
233 help="specify the the maximum number of physical connections to posgresql")
234 group.add_option("--db-template", dest="db_template", my_default="template1",
235 help="specify a custom database template to create a new database")
236 parser.add_option_group(group)
238 group = optparse.OptionGroup(parser, "Internationalisation options",
239 "Use these options to translate Odoo to another language."
240 "See i18n section of the user manual. Option '-d' is mandatory."
241 "Option '-l' is mandatory in case of importation"
243 group.add_option('--load-language', dest="load_language",
244 help="specifies the languages for the translations you want to be loaded")
245 group.add_option('-l', "--language", dest="language",
246 help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
247 group.add_option("--i18n-export", dest="translate_out",
248 help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
249 group.add_option("--i18n-import", dest="translate_in",
250 help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
251 group.add_option("--i18n-overwrite", dest="overwrite_existing_translations", action="store_true", my_default=False,
252 help="overwrites existing translation terms on updating a module or importing a CSV or a PO file.")
253 group.add_option("--modules", dest="translate_modules",
254 help="specify modules to export. Use in combination with --i18n-export")
255 parser.add_option_group(group)
257 security = optparse.OptionGroup(parser, 'Security-related options')
258 security.add_option('--no-database-list', action="store_false", dest='list_db', my_default=True,
259 help="disable the ability to return the list of databases")
260 parser.add_option_group(security)
263 group = optparse.OptionGroup(parser, "Advanced options")
264 if os.name == 'posix':
265 group.add_option('--auto-reload', dest='auto_reload', action='store_true', my_default=False, help='enable auto reload')
266 group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
267 group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
268 help="stop the server after its initialization")
269 group.add_option("-t", "--timezone", dest="timezone", my_default=False,
270 help="specify reference timezone for the server (e.g. Europe/Brussels")
271 group.add_option("--osv-memory-count-limit", dest="osv_memory_count_limit", my_default=False,
272 help="Force a limit on the maximum number of records kept in the virtual "
273 "osv_memory tables. The default is False, which means no count-based limit.",
275 group.add_option("--osv-memory-age-limit", dest="osv_memory_age_limit", my_default=1.0,
276 help="Force a limit on the maximum age of records kept in the virtual "
277 "osv_memory tables. This is a decimal value expressed in hours, "
278 "and the default is 1 hour.",
280 group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=2,
281 help="Maximum number of threads processing concurrently cron jobs (default 2).",
283 group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
284 help="Use the unaccent function provided by the database when available.")
285 group.add_option("--geoip-db", dest="geoip_database", my_default='/usr/share/GeoIP/GeoLiteCity.dat',
286 help="Absolute path to the GeoIP database file.")
287 parser.add_option_group(group)
289 if os.name == 'posix':
290 group = optparse.OptionGroup(parser, "Multiprocessing options")
291 # TODO sensible default for the three following limits.
292 group.add_option("--workers", dest="workers", my_default=0,
293 help="Specify the number of workers, 0 disable prefork mode.",
295 group.add_option("--limit-memory-soft", dest="limit_memory_soft", my_default=2048 * 1024 * 1024,
296 help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request (default 671088640 aka 640MB).",
298 group.add_option("--limit-memory-hard", dest="limit_memory_hard", my_default=2560 * 1024 * 1024,
299 help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail (default 805306368 aka 768MB).",
301 group.add_option("--limit-time-cpu", dest="limit_time_cpu", my_default=60,
302 help="Maximum allowed CPU time per request (default 60).",
304 group.add_option("--limit-time-real", dest="limit_time_real", my_default=120,
305 help="Maximum allowed Real time per request (default 120).",
307 group.add_option("--limit-request", dest="limit_request", my_default=8192,
308 help="Maximum number of request to be processed per worker (default 8192).",
310 parser.add_option_group(group)
312 # Copy all optparse options (i.e. MyOption) into self.options.
313 for group in parser.option_groups:
314 for option in group.option_list:
315 if option.dest not in self.options:
316 self.options[option.dest] = option.my_default
317 self.casts[option.dest] = option
319 # generate default config
322 def parse_config(self, args=None):
323 """ Parse the configuration file (if any) and the command-line
326 This method initializes openerp.tools.config and openerp.conf (the
327 former should be removed in the furture) with library-wide
328 configuration values.
330 This method must be called before proper usage of this library can be
333 Typical usage of this method:
335 openerp.tools.config.parse_config(sys.argv[1:])
337 self._parse_config(args)
338 openerp.netsvc.init_logger()
339 openerp.modules.module.initialize_sys_path()
341 def _parse_config(self, args=None):
344 opt, args = self.parser.parse_args(args)
348 self.parser.error(msg)
350 # Ensures no illegitimate argument is silently discarded (avoids insidious "hyphen to dash" problem)
351 die(args, "unrecognized parameters: '%s'" % " ".join(args))
353 die(bool(opt.syslog) and bool(opt.logfile),
354 "the syslog and logfile options are exclusive")
356 die(opt.translate_in and (not opt.language or not opt.db_name),
357 "the i18n-import option cannot be used without the language (-l) and the database (-d) options")
359 die(opt.overwrite_existing_translations and not (opt.translate_in or opt.update),
360 "the i18n-overwrite option cannot be used without the i18n-import option or without the update option")
362 die(opt.translate_out and (not opt.db_name),
363 "the i18n-export option cannot be used without the database (-d) option")
365 # Check if the config file exists (-c used, but not -s)
366 die(not opt.save and opt.config and not os.access(opt.config, os.R_OK),
367 "The config file '%s' selected with -c/--config doesn't exist or is not readable, "\
368 "use -s/--save if you want to generate it"% opt.config)
370 # place/search the config file on Win32 near the server installation
371 # (../etc from the server)
372 # 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,
373 # else he won't be able to save the configurations, or even to start the server...
376 rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
378 rcfilepath = os.path.expanduser('~/.openerp_serverrc')
380 self.rcfile = os.path.abspath(
381 self.config_file or opt.config \
382 or os.environ.get('OPENERP_SERVER') or rcfilepath)
385 # Verify that we want to log or not, if not the output will go to stdout
386 if self.options['logfile'] in ('None', 'False'):
387 self.options['logfile'] = False
388 # the same for the pidfile
389 if self.options['pidfile'] in ('None', 'False'):
390 self.options['pidfile'] = False
392 # if defined dont take the configfile value even if the defined value is None
393 keys = ['xmlrpc_interface', 'xmlrpc_port', 'longpolling_port',
394 'db_name', 'db_user', 'db_password', 'db_host',
395 'db_port', 'db_template', 'logfile', 'pidfile', 'smtp_port',
396 'email_from', 'smtp_server', 'smtp_user', 'smtp_password',
397 'db_maxconn', 'import_partial', 'addons_path',
398 'xmlrpc', 'syslog', 'without_demo', 'timezone',
399 'xmlrpcs_interface', 'xmlrpcs_port', 'xmlrpcs',
400 'secure_cert_file', 'secure_pkey_file', 'dbfilter', 'log_handler', 'log_level', 'log_db',
405 # Copy the command-line argument (except the special case for log_handler, due to
406 # action=append requiring a real default, so we cannot use the my_default workaround)
407 if getattr(opt, arg) and getattr(opt, arg) != DEFAULT_LOG_HANDLER:
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 if isinstance(self.options['log_handler'], basestring):
414 self.options['log_handler'] = self.options['log_handler'].split(',')
416 # if defined but None take the configfile value
418 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
419 'debug_mode', 'smtp_ssl', 'load_language',
420 'stop_after_init', 'logrotate', 'without_demo', 'xmlrpc', 'syslog',
421 'list_db', 'xmlrpcs', 'proxy_mode',
422 'test_file', 'test_enable', 'test_commit', 'test_report_directory',
423 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent',
428 'auto_reload', 'workers',
429 'limit_memory_hard', 'limit_memory_soft',
430 'limit_time_cpu', 'limit_time_real', 'limit_request',
433 if os.name == 'posix':
436 self.options.update(dict.fromkeys(posix_keys, None))
438 # Copy the command-line arguments...
440 if getattr(opt, arg) is not None:
441 self.options[arg] = getattr(opt, arg)
442 # ... or keep, but cast, the config file value.
443 elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
444 self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
446 self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
447 if not self.options['addons_path'] or self.options['addons_path']=='None':
448 base_addons = os.path.join(self.options['root_path'], 'addons')
449 main_addons = os.path.abspath(os.path.join(self.options['root_path'], '../addons'))
450 self.options['addons_path'] = '%s,%s' % (base_addons, main_addons)
452 self.options['addons_path'] = ",".join(
453 os.path.abspath(os.path.expanduser(os.path.expandvars(x)))
454 for x in self.options['addons_path'].split(','))
456 self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
457 self.options["demo"] = not opt.without_demo and self.options['init'] or {}
458 self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
459 self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
460 self.options['translate_modules'].sort()
462 # TODO checking the type of the parameters should be done for every
463 # parameters, not just the timezone.
464 # The call to get_server_timezone() sets the timezone; this should
465 # probably done here.
466 if self.options['timezone']:
467 # Prevent the timezone to be True. (The config file parsing changes
468 # the string 'True' to the boolean value True. It would be probably
469 # be better to remove that conversion.)
470 die(not isinstance(self.options['timezone'], basestring),
471 "Invalid timezone value in configuration or environment: %r.\n"
472 "Please fix this in your configuration." %(self.options['timezone']))
474 # If an explicit TZ was provided in the config, make sure it is known
477 pytz.timezone(self.options['timezone'])
478 except pytz.UnknownTimeZoneError:
479 die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
481 # If pytz is missing, don't check the provided TZ, it will be ignored anyway.
485 self.options['pg_path'] = opt.pg_path
487 if self.options.get('language', False):
488 if len(self.options['language']) > 5:
489 raise Exception('ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE')
491 if not self.options['db_user']:
494 self.options['db_user'] = getpass.getuser()
496 self.options['db_user'] = None
498 die(not self.options['db_user'], 'ERROR: No user specified for the connection to the database')
500 if self.options['db_password']:
501 if sys.platform == 'win32' and not self.options['db_host']:
502 self.options['db_host'] = 'localhost'
503 #if self.options['db_host']:
504 # self._generate_pgpassfile()
509 openerp.conf.addons_paths = self.options['addons_path'].split(',')
510 if opt.server_wide_modules:
511 openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
513 openerp.conf.server_wide_modules = ['web','web_kanban']
515 def _generate_pgpassfile(self):
517 Generate the pgpass file with the parameters from the command line (db_host, db_user,
520 Used because pg_dump and pg_restore can not accept the password on the command line.
522 is_win32 = sys.platform == 'win32'
524 filename = os.path.join(os.environ['APPDATA'], 'pgpass.conf')
526 filename = os.path.join(os.environ['HOME'], '.pgpass')
528 text_to_add = "%(db_host)s:*:*:%(db_user)s:%(db_password)s" % self.options
530 if os.path.exists(filename):
531 content = [x.strip() for x in file(filename, 'r').readlines()]
532 if text_to_add in content:
535 fp = file(filename, 'a+')
536 fp.write(text_to_add + "\n")
544 x=_winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
545 y = _winreg.OpenKey(x, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0,_winreg.KEY_ALL_ACCESS)
546 _winreg.SetValueEx(y,"PGPASSFILE", 0, _winreg.REG_EXPAND_SZ, filename )
551 os.chmod(filename, stat.S_IRUSR + stat.S_IWUSR)
553 def _is_addons_path(self, path):
554 for f in os.listdir(path):
555 modpath = os.path.join(path, f)
556 if os.path.isdir(modpath):
557 def hasfile(filename):
558 return os.path.isfile(os.path.join(modpath, filename))
559 if hasfile('__init__.py') and (hasfile('__openerp__.py') or hasfile('__terp__.py')):
563 def _check_addons_path(self, option, opt, value, parser):
565 for path in value.split(','):
567 res = os.path.abspath(os.path.expanduser(path))
568 if not os.path.isdir(res):
569 raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, path))
570 if not self._is_addons_path(res):
571 raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, path))
574 setattr(parser.values, option.dest, ",".join(ad_paths))
577 p = ConfigParser.ConfigParser()
579 p.read([self.rcfile])
580 for (name,value) in p.items('options'):
581 if value=='True' or value=='true':
583 if value=='False' or value=='false':
585 self.options[name] = value
586 #parse the other sections, as well
587 for sec in p.sections():
590 if not self.misc.has_key(sec):
592 for (name, value) in p.items(sec):
593 if value=='True' or value=='true':
595 if value=='False' or value=='false':
597 self.misc[sec][name] = value
600 except ConfigParser.NoSectionError:
604 p = ConfigParser.ConfigParser()
605 loglevelnames = dict(zip(self._LOGLEVELS.values(), self._LOGLEVELS.keys()))
606 p.add_section('options')
607 for opt in sorted(self.options.keys()):
608 if opt in ('version', 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', 'init', 'update'):
610 if opt in self.blacklist_for_save:
612 if opt in ('log_level',):
613 p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
615 p.set('options', opt, self.options[opt])
617 for sec in sorted(self.misc.keys()):
619 for opt in sorted(self.misc[sec].keys()):
620 p.set(sec,opt,self.misc[sec][opt])
622 # try to create the directories and write the file
624 rc_exists = os.path.exists(self.rcfile)
625 if not rc_exists and not os.path.exists(os.path.dirname(self.rcfile)):
626 os.makedirs(os.path.dirname(self.rcfile))
628 p.write(file(self.rcfile, 'w'))
630 os.chmod(self.rcfile, 0600)
632 sys.stderr.write("ERROR: couldn't write the config file\n")
635 # what to do if impossible?
636 sys.stderr.write("ERROR: couldn't create the config directory\n")
638 def get(self, key, default=None):
639 return self.options.get(key, default)
641 def get_misc(self, sect, key, default=None):
642 return self.misc.get(sect,{}).get(key, default)
644 def __setitem__(self, key, value):
645 self.options[key] = value
646 if key in self.options and isinstance(self.options[key], basestring) and \
647 key in self.casts and self.casts[key].type in optparse.Option.TYPE_CHECKER:
648 self.options[key] = optparse.Option.TYPE_CHECKER[self.casts[key].type](self.casts[key], key, self.options[key])
650 def __getitem__(self, key):
651 return self.options[key]
654 def addons_data_dir(self):
655 d = os.path.join(self['data_dir'], 'addons', release.series)
656 if not os.path.exists(d):
663 def session_dir(self):
664 d = os.path.join(self['data_dir'], 'sessions')
665 if not os.path.exists(d):
671 def filestore(self, dbname):
672 return os.path.join(self['data_dir'], 'filestore', dbname)
674 config = configmanager()
677 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: