Mercurial > hgrepos > Python > libs > ConfigMix
view configmix/ini.py @ 760:1514e7d2f691
FIX: add forgotten test configuration data file
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Thu, 07 Dec 2023 08:40:46 +0100 |
| parents | 301cf2337fde |
| children |
line wrap: on
line source
# -*- coding: utf-8 -*- # :- # :Copyright: (c) 2015-2022, Franz Glasner. All rights reserved. # :License: BSD-3-Clause. See LICENSE.txt for details. # :- """Read INI-style configuration files. """ from __future__ import division, absolute_import, print_function __all__ = ["INIConfigParser", "NoSectionError", "NoOptionError", "load"] import sys import os import io import locale try: from configparser import ConfigParser, NoSectionError, NoOptionError # SafeConfigParser is deprecated in Python 3.2+ (together with "readfp()") if hasattr(ConfigParser, "read_file"): _ConfigParserBase = ConfigParser else: from configparser import SafeConfigParser _ConfigParserBase = SafeConfigParser except ImportError: from ConfigParser import SafeConfigParser, NoSectionError, NoOptionError _ConfigParserBase = SafeConfigParser try: from collections import OrderedDict as DictImpl except ImportError: try: from ordereddict import OrderedDict as DictImpl except ImportError: DictImpl = dict from .compat import u, u2fs class INIConfigParser(_ConfigParserBase): """A case sensitive config parser that returns all-unicode string values. """ def __init__(self, filename, executable=None, encoding=None): _ConfigParserBase.__init__(self) if executable is None: executable = sys.argv[0] filename = u(filename, locale.getpreferredencoding()) executable = u(executable, locale.getpreferredencoding()) self.executable = os.path.normpath(os.path.abspath(executable)) if encoding is None: encoding = locale.getpreferredencoding() self.encoding = encoding with io.open(u2fs(filename), mode="rt", encoding=self.encoding) as cf: self.read_file(cf, filename) def optionxform(self, option): return option def get_path_list(self, section, option): v = self.get(section, option) return v.split(os.pathsep) def read(self, filenames): """Not implemented. Use :meth:`read_file` instead.""" raise NotImplementedError("use `read_file()' instead") def readfp(self, fp, filename): """Compatibility for older Python versions. Use :meth:`.read_file` instead. """ return self.read_file(fp, filename) def read_file(self, fp, filename): """Read from a file-like object `fp`. The `fp` argument must be iterable (Python 3.2+) or have a `readline()` method (Python 2, <3.2). """ if hasattr(self, "filename"): raise RuntimeError("already initialized") filename = os.path.normpath(os.path.abspath(filename)) filename = u(filename, locale.getpreferredencoding()) # self.set(None, u("self"), filename) # self.set(None, u("here"), os.path.dirname(filename)) # self.set(None, u("root"), os.path.dirname(self.executable)) if hasattr(_ConfigParserBase, "read_file"): _ConfigParserBase.read_file(self, fp, source=filename) else: _ConfigParserBase.readfp(self, fp, filename=filename) self.filename = filename # self.root = os.path.dirname(self.executable) def getx(self, section, option): """Extended `get()` with some automatic type conversion support. Default: Fetch as string (like :meth:`get`). If annotated with ``:bool:`` fetch as bool, if annotated with ``:int:`` fetch as int, if annotated with ``:float:`` fetch as float. """ v = self.get(section, option) if v.startswith(u(":bool:")): v = v[6:].lower() if v not in self._BOOL_CVT: raise ValueError("Not a boolean: %r" % v) return self._BOOL_CVT[v] elif v.startswith(u(":int:")): return int(v[5:], 0) elif v.startswith(u(":float:")): return float(v[7:]) else: return v _BOOL_CVT = {u('1'): True, u('yes'): True, u('true'): True, u('on'): True, u('0'): False, u('no'): False, u('false'): False, u('off'): False} def itemsx(self, section, options): """Get all the options given in `options` of section `section`. Fetch them with :meth:`getx` in the order given. Return a list of ``(name, value)`` pairs for each option in `options` in the given `section`. """ d = [] for option in options: try: val = self.getx(section, option) except (NoSectionError, NoOptionError): pass else: d.append((option, val, )) return d def items_as_dictx(self, section, options): """Similar to :meth:`itemsx` but return a (possibly ordered) dict instead of a list of key-value pairs. """ return DictImpl(self.itemsx(section, options)) def load(filename, extract=["config"], encoding="utf-8"): """Load a single INI file and read/interpolate the sections given in `extract`. Flattens the given sections into the resulting dictionary. Then build a tree out of sections which start with any of the `extract` content value and a dot ``.``. The encoding of the file is given in `encoding`. """ conf = DictImpl() ini = INIConfigParser(filename, encoding=encoding) for sect in extract: try: cfg = ini.options(sect) except NoSectionError: pass else: for option in cfg: value = ini.getx(sect, option) conf[option] = value # try to read "<extract>.xxx" sections as tree for treemarker in [e + '.' for e in extract]: sections = list(ini.sections()) sections.sort() for section in sections: cur_cfg = conf if section.startswith(treemarker): treestr = section[len(treemarker):] for treepart in treestr.split('.'): cur_cfg = cur_cfg.setdefault(treepart, DictImpl()) for option in ini.options(section): value = ini.getx(section, option) cur_cfg[option] = value return conf
