1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2013 LEAP
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 Implements the abstract base class for configuration
27 from abc import ABCMeta, abstractmethod
29 from leap.common.check import leap_assert
30 from leap.common.files import mkdir_p
31 from leap.common.config.pluggableconfig import PluggableConfig
32 from leap.common.config.prefixers import get_platform_prefixer
34 logger = logging.getLogger(__name__)
39 Abstract base class for any JSON based configuration.
42 __metaclass__ = ABCMeta
45 Standalone is a class wide parameter.
47 :param standalone: if True it will return the prefix for a
48 standalone application. Otherwise, it will
50 default for configuration storage.
51 :type standalone: bool
57 self._config_checker = None
62 Returns the spec object for the specific configuration.
66 def _safe_get_value(self, key):
68 Tries to return a value only if the config has already been loaded.
70 @rtype: depends on the config structure, dict, str, array, int
71 @return: returns the value for the specified key in the config
73 leap_assert(self._config_checker, "Load the config first")
74 return self._config_checker.config.get(key, None)
76 def get_path_prefix(self):
78 Returns the platform dependant path prefixer
80 return get_platform_prefixer().get_path_prefix(
81 standalone=self.standalone)
85 Returns True if the configuration has been already
86 loaded. False otherwise
88 return self._config_checker is not None
90 def save(self, path_list):
92 Saves the current configuration to disk.
94 :param path_list: list of components that form the relative
95 path to configuration. The absolute path
96 will be calculated depending on the platform.
99 :return: True if saved to disk correctly, False otherwise
101 config_path = os.path.join(self.get_path_prefix(), *(path_list[:-1]))
105 self._config_checker.serialize(os.path.join(config_path,
107 except Exception as e:
108 logger.warning("%s" % (e,))
112 def load(self, path="", data=None, mtime=None, relative=True):
114 Loads the configuration from disk.
116 :param path: if relative=True, this is a relative path
117 to configuration. The absolute path
118 will be calculated depending on the platform
121 :param relative: if True, path is relative. If False, it's absolute.
124 :return: True if loaded from disk correctly, False otherwise
129 config_path = os.path.join(
130 self.get_path_prefix(), path)
134 self._config_checker = PluggableConfig(format="json")
135 self._config_checker.options = copy.deepcopy(self._get_spec())
139 self._config_checker.load(fromfile=config_path, mtime=mtime)
141 self._config_checker.load(data, mtime=mtime)
142 except Exception as e:
143 logger.error("Something went wrong while loading " +
144 "the config from %s\n%s" % (config_path, e))
145 self._config_checker = None
150 class LocalizedKey(object):
152 Decorator used for keys that are localized in a configuration.
155 def __init__(self, func, **kwargs):
158 def __call__(self, instance, lang="en"):
160 Tries to return the string for the specified language, otherwise
161 informs the problem and returns an empty string.
163 :param lang: language code
166 :return: localized value from the possible values returned by
169 descriptions = self._func(instance)
170 description_lang = ""
172 for key in descriptions.keys():
173 if lang.startswith(key):
177 description_lang = descriptions[config_lang]
178 return description_lang
180 def __get__(self, instance, instancetype):
182 Implement the descriptor protocol to make decorating instance
185 # Return a partial function with the first argument is the instance
186 # of the class decorated.
187 return functools.partial(self.__call__, instance)
189 if __name__ == "__main__":
191 config = BaseConfig() # should throw TypeError for _get_spec
192 except Exception as e:
193 assert isinstance(e, TypeError), "Something went wrong"
194 print "Abstract BaseConfig class is working as expected"