143 lines
4.5 KiB
Python
143 lines
4.5 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Read from configuration files easily without hurting performances
|
|
|
|
USAGE:
|
|
During development you can load a config file either in .ini or .json
|
|
format (by default app/private/appconfig.ini or app/private/appconfig.json)
|
|
The result is a dict holding the configured values. Passing reload=True
|
|
is meant only for development: in production, leave reload to False and all
|
|
values will be cached
|
|
|
|
from gluon.contrib.appconfig import AppConfig
|
|
myconfig = AppConfig(path_to_configfile, reload=False)
|
|
|
|
print myconfig['db']['uri']
|
|
|
|
The returned dict can walk with "dot notation" an arbitrarely nested dict
|
|
|
|
print myconfig.take('db.uri')
|
|
|
|
You can even pass a cast function, i.e.
|
|
|
|
print myconfig.take('auth.expiration', cast=int)
|
|
|
|
Once the value has been fetched (and casted) it won't change until the process
|
|
is restarted (or reload=True is passed).
|
|
|
|
"""
|
|
import os
|
|
import json
|
|
from gluon._compat import thread, configparser
|
|
from gluon.globals import current
|
|
|
|
locker = thread.allocate_lock()
|
|
|
|
def AppConfig(*args, **vars):
|
|
|
|
locker.acquire()
|
|
reload_ = vars.pop('reload', False)
|
|
try:
|
|
instance_name = 'AppConfig_' + current.request.application
|
|
if reload_ or not hasattr(AppConfig, instance_name):
|
|
setattr(AppConfig, instance_name, AppConfigLoader(*args, **vars))
|
|
return getattr(AppConfig, instance_name).settings
|
|
finally:
|
|
locker.release()
|
|
|
|
|
|
class AppConfigDict(dict):
|
|
"""
|
|
dict that has a .take() method to fetch nested values and puts
|
|
them into cache
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
dict.__init__(self, *args, **kwargs)
|
|
self.int_cache = {}
|
|
|
|
def get(self, path, default=None):
|
|
try:
|
|
value = self.take(path).strip()
|
|
if value.lower() in ('none','null',''):
|
|
return None
|
|
elif value.lower() == 'true':
|
|
return True
|
|
elif value.lower() == 'false':
|
|
return False
|
|
elif value.isdigit() or (value[0]=='-' and value[1:].isdigit()):
|
|
return int(value)
|
|
elif ',' in value:
|
|
return map(lambda x:x.strip(),value.split(','))
|
|
else:
|
|
try:
|
|
return float(value)
|
|
except:
|
|
return value
|
|
except:
|
|
return default
|
|
|
|
def take(self, path, cast=None):
|
|
parts = path.split('.')
|
|
if path in self.int_cache:
|
|
return self.int_cache[path]
|
|
value = self
|
|
walking = []
|
|
for part in parts:
|
|
if part not in value:
|
|
raise BaseException("%s not in config [%s]" %
|
|
(part, '-->'.join(walking)))
|
|
value = value[part]
|
|
walking.append(part)
|
|
if cast is None:
|
|
self.int_cache[path] = value
|
|
else:
|
|
try:
|
|
value = cast(value)
|
|
self.int_cache[path] = value
|
|
except (ValueError, TypeError):
|
|
raise BaseException("%s can't be converted to %s" %
|
|
(value, cast))
|
|
return value
|
|
|
|
|
|
class AppConfigLoader(object):
|
|
|
|
def __init__(self, configfile=None):
|
|
if not configfile:
|
|
priv_folder = os.path.join(current.request.folder, 'private')
|
|
configfile = os.path.join(priv_folder, 'appconfig.ini')
|
|
if not os.path.isfile(configfile):
|
|
configfile = os.path.join(priv_folder, 'appconfig.json')
|
|
if not os.path.isfile(configfile):
|
|
configfile = None
|
|
if not configfile or not os.path.isfile(configfile):
|
|
raise BaseException("Config file not found")
|
|
self.file = configfile
|
|
self.ctype = os.path.splitext(configfile)[1][1:]
|
|
self.settings = None
|
|
self.read_config()
|
|
|
|
def read_config_ini(self):
|
|
config = configparser.SafeConfigParser()
|
|
config.read(self.file)
|
|
settings = {}
|
|
for section in config.sections():
|
|
settings[section] = {}
|
|
for option in config.options(section):
|
|
settings[section][option] = config.get(section, option)
|
|
self.settings = AppConfigDict(settings)
|
|
|
|
def read_config_json(self):
|
|
with open(self.file, 'r') as c:
|
|
self.settings = AppConfigDict(json.load(c))
|
|
|
|
def read_config(self):
|
|
if self.settings is None:
|
|
try:
|
|
getattr(self, 'read_config_' + self.ctype)()
|
|
except AttributeError:
|
|
raise BaseException("Unsupported config file format")
|
|
return self.settings
|