view configmix/ini.py @ 37:2a4dbbb42f43 v0.4

+++++ v0.4
author Franz Glasner <hg@dom66.de>
date Thu, 31 Mar 2016 19:19:24 +0200
parents 7c7955da42ab
children aa8345dae995
line wrap: on
line source

# -*- coding: utf-8 -*-
r"""Read INI-style configuration files.

"""

from __future__ import division, absolute_import, print_function

import sys
import os
import io
import locale
try:
    from configparser import SafeConfigParser, NoSectionError, NoOptionError
except ImportError:
    from ConfigParser import SafeConfigParser, NoSectionError, NoOptionError
try:
    from collections import OrderedDict as DictImpl
except ImportError:
    try:
        from ordereddict import OrderedDict as DictImpl
    except ImportError:
        DictImpl = dict

from .compat import PY2, u


__all__ = ["INIConfigParser", "NoSectionError", "NoOptionError",
           "load"]


class INIConfigParser(SafeConfigParser):

    """A case sensitive config parser that returns all-unicode string
    values.

    """

    def __init__(self, filename, executable=None, encoding=None):
        SafeConfigParser.__init__(self)
        if executable is None:
            executable = sys.argv[0]
        if PY2:
            if isinstance(filename, str):
                filename = filename.decode(locale.getpreferredencoding())
            if isinstance(executable, str):
                executable = executable.decode(locale.getpreferredencoding())
        self.executable = os.path.normpath(os.path.abspath(executable))
        if encoding is None:
            encoding = locale.getpreferredencoding()
        self.encoding = encoding
        with io.open(filename, mode="rt", encoding=self.encoding) as cf:
            self.readfp(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):
        raise NotImplementedError("use `readfp()' instead")

    def readfp(self, fp, filename):
        if hasattr(self, "filename"):
            raise RuntimeError("already initialized")
        filename = os.path.normpath(os.path.abspath(filename))
        if PY2:
            if isinstance(filename, str):
                filename = filename.decode(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))
        SafeConfigParser.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 `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 `self.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 `self.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"]):
    """Load a single INI file and read/interpolate the sections given in
    `extract`.

    Flattens the given sections into the resulting dictionary.

    """
    conf = DictImpl()
    ini = INIConfigParser(filename)
    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
    return conf