File indexing completed on 2024-04-21 16:29:15

0001 # -*- coding: UTF-8 -*-
0002 
0003 """
0004 Access the user configuration of Pology.
0005 
0006 The location and the syntax of the configuration file is described
0007 in the user manual, at C{doc/user/common.docbook#sec-cmconfig}.
0008 
0009 At every place where the user configuration is sourced, the API documentation
0010 should state which sections and fields (with their types) are accessed,
0011 how they are used, and what is the behavior when they are not set.
0012 
0013 @author: Chusslove Illich (Часлав Илић) <caslav.ilic@gmx.net>
0014 @license: GPLv3
0015 """
0016 
0017 import codecs
0018 import os
0019 from configparser import ConfigParser
0020 
0021 from pology import _, n_
0022 from pology.report import error
0023 
0024 
0025 _config = ConfigParser()
0026 
0027 def _parse_config ():
0028 
0029     # Try to correctly resolve the config file location across systems.
0030     cfgbase = "pologyrc"
0031     if os.name=="nt":
0032         cfgpath = os.path.join(os.environ.get("APPDATA", ""), cfgbase)
0033     else:
0034         cfgbase = "." + cfgbase
0035         cfgpath = os.path.join(os.environ.get("HOME", ""), cfgbase)
0036 
0037     # Parse the config if available.
0038     if os.path.isfile(cfgpath):
0039         ifl = codecs.open(cfgpath, "r", "UTF-8")
0040         _config.read_file(ifl)
0041         ifl.close()
0042 
0043 # Parse configuration on first import.
0044 _parse_config()
0045 
0046 
0047 def has_section (name):
0048     """
0049     Check if the section of the configuration exists already.
0050 
0051     @param name: name of the section
0052     @type name: string
0053 
0054     @returns: C{True} if the section exists, C{False} otherwise
0055     @rtype: bool
0056     """
0057 
0058     return _config.has_section(name)
0059 
0060 
0061 class section:
0062     """
0063     Section of the configuration.
0064 
0065     All getter methods take the field name and the default value,
0066     which is returned when the field is not set.
0067     If the configuration field is set but cannot be converted into
0068     a value of requested type, execution aborts with an error message.
0069 
0070     @ivar name: name of the section
0071     @type name: string
0072     """
0073 
0074     def __init__ (self, name):
0075         """
0076         Retrieve a section of the configuration.
0077 
0078         Constructed section object is valid even when the configuration does
0079         not contain the requested section (in that case, all field queries
0080         on the section are going to return default values).
0081 
0082         @param name: name of the section
0083         @type name: string
0084         """
0085 
0086         self.name = name
0087 
0088 
0089     def fields (self):
0090         """
0091         Get all configuration field names in this section.
0092 
0093         @rtype: set(string)
0094         """
0095 
0096         if not _config.has_section(self.name):
0097             return set()
0098 
0099         return set(_config.options(self.name))
0100 
0101 
0102     def _value (self, typ, name, default=None, typename=None):
0103 
0104         if not _config.has_option(self.name, name):
0105             return default
0106 
0107         value = _config.get(self.name, name)
0108         if typ is not bool:
0109             try:
0110                 cvalue = typ(value)
0111             except:
0112                 cvalue = None
0113         else:
0114             cvalue = strbool(value)
0115 
0116         if cvalue is None:
0117             if typename:
0118                 error(_("@info",
0119                         "User configuration: value '%(val)s' "
0120                         "of field '%(field)s' in section '%(sec)s' "
0121                         "cannot be converted into '%(type)s' type.",
0122                         val=value, field=name, sec=self.name,
0123                         type=typename))
0124             else:
0125                 error(_("@info",
0126                         "User configuration: value '%(val)s' "
0127                         "of field '%(field)s' in section '%(sec)s' "
0128                         "cannot be converted into requested type.",
0129                         val=value, field=name, sec=self.name))
0130 
0131         return cvalue
0132 
0133 
0134     def string (self, name, default=None):
0135         """
0136         Get a configuration field as a string.
0137 
0138         @rtype: unicode or as C{default}
0139         """
0140 
0141         return self._value(str, name, default, "string")
0142 
0143 
0144     def integer (self, name, default=None):
0145         """
0146         Get a configuration field as an integer number.
0147 
0148         @rtype: int or as C{default}
0149         """
0150 
0151         return self._value(int, name, default, "integer")
0152 
0153 
0154     def real (self, name, default=None):
0155         """
0156         Get a configuration field as a real number.
0157 
0158         @rtype: float or as C{default}
0159         """
0160 
0161         return self._value(float, name, default, "real")
0162 
0163 
0164     def boolean (self, name, default=None):
0165         """
0166         Get a configuration field as a boolean.
0167 
0168         @rtype: bool or as C{default}
0169         """
0170 
0171         return self._value(bool, name, default, "boolean")
0172 
0173 
0174     def strslist (self, name, default=None, sep=","):
0175         """
0176         Get a configuration field as a list of separated strings.
0177 
0178         Separator character or string is used to split the field value
0179         into substrings::
0180 
0181             afield = foo, bar, baz
0182 
0183         Leading and trailing whitespace in list elements is stripped.
0184 
0185         If list elements should be able to contain any characters
0186         or whitespace is significant, use delimited list instead (L{strdlist}).
0187 
0188         @param sep: the separator
0189         @type sep: string
0190 
0191         @rtype: unicode or as C{default}
0192         """
0193 
0194         value = self._value(str, name, None, "string")
0195         if value is None:
0196             return default
0197         lst = value.split(sep)
0198         lst = [x.strip() for x in lst]
0199 
0200         return lst
0201 
0202 
0203     def strdlist (self, name, default=None):
0204         """
0205         Get a configuration field as a list of delimited strings.
0206 
0207         Delimiter is taken to be the non-alphanumeric character with
0208         which the field value starts. In this example::
0209 
0210             afield = /foo/bar/baz/
0211 
0212         the delimiter is C{/}.
0213 
0214         If the field value does not start with a non-alphanumeric,
0215         or it does not end with the delimiter, error is signalled.
0216 
0217         @rtype: unicode or as C{default}
0218         """
0219 
0220         value = self._value(str, name, None, "string")
0221         if value is None:
0222             return default
0223         value = value.strip()
0224 
0225         if len(value) < 2:
0226             error(_("@info",
0227                     "User configuration: value '%(val)s' of field '%(field)s' "
0228                     "in section '%(sec)s' is too short for a delimited list.",
0229                     val=value, field=name, sec=self.name))
0230         if value[0].isalnum():
0231             error(_("@info",
0232                     "User configuration: value '%(val)s' of field '%(field)s' "
0233                     "in section '%(sec)s' does not start with "
0234                     "a non-alphanumeric delimiter character.",
0235                     val=value, field=name, sec=self.name))
0236 
0237         delim = value[0]
0238 
0239         if value[-1] != delim:
0240             error(_("@info",
0241                     "User configuration: value '%(val)s' of field '%(field)s' "
0242                     "in section '%(sec)s' does not end with "
0243                     "the delimiter character with which it starts.",
0244                     val=value, field=name, sec=self.name))
0245 
0246         lst = value[1:-1].split(delim)
0247 
0248         return lst
0249 
0250 
0251 def strbool (value):
0252     """
0253     Parse the string specification of a boolean value.
0254 
0255     Values considered C{false} are: C{"0"}, C{"no"}, C{"false"}, C{"off"};
0256     and C{True}: C{1}, C{"yes"}, C{"true"}, C{"on"}.
0257     String is stripped of leading and trailing whitespace and lowercased
0258     before matching.
0259 
0260     If the string matches none of the expected logical specifiers,
0261     C{None} is returned.
0262 
0263     @param value: string to parse
0264     @type value: string
0265 
0266     @return: parsed boolean
0267     @rtype: bool
0268     """
0269 
0270     value = value.strip().lower()
0271     if value in ("0", "no", "false", "off"):
0272         return False
0273     elif value in ("1", "yes", "true", "on"):
0274         return True
0275     else:
0276         return None
0277