Completed
Push — master ( 4f74b5...35b5fc )
by Satoru
01:05
created

_load()   F

Complexity

Conditions 11

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 2 Features 0
Metric Value
cc 11
c 2
b 2
f 0
dl 0
loc 44
rs 3.1764

How to fix   Complexity   

Complexity

Complex classes like _load() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#
2
# Copyright (C) 2011 - 2015 Satoru SATOH <ssato @ redhat.com>
3
# License: MIT
4
#
5
#  pylint: disable=unused-argument
6
"""INI or INI like config files backend.
7
8
.. versionchanged:: 0.3
9
   Introduce 'ac_parse_value' keyword option to switch behaviors, same as
10
   original configparser and rich backend parsing each parameter values.
11
12
- Format to support: INI or INI like ones
13
- Requirements: It should be available always.
14
15
  - ConfigParser in python 2 standard library:
16
    https://docs.python.org/2.7/library/configparser.html
17
18
  - configparser in python 3 standard library:
19
    https://docs.python.org/3/library/configparser.html
20
21
- Limitations: None obvious
22
- Special options:
23
24
  - Use 'ac_parse_value' boolean keyword option if you want to parse values by
25
    custom parser, anyconfig.backend.ini._parse.
26
"""
27
from __future__ import absolute_import
28
29
import anyconfig.backend.base
30
import anyconfig.parser as P
31
import anyconfig.utils
32
33
from anyconfig.compat import configparser, iteritems, OrderedDict
34
from anyconfig.backend.base import mk_opt_args
35
36
37
_SEP = ','
38
39
40
def _noop(val, *args, **kwargs):
41
    """
42
    Parser does nothing.
43
    """
44
    # It means nothing but can suppress 'Unused argument' pylint warns.
45
    # (val, args, kwargs)[0]
46
    return val
47
48
49
def _parse(val_s, sep=_SEP):
50
    """
51
    FIXME: May be too naive implementation.
52
53
    :param val_s: A string represents some value to parse
54
    :param sep: separator between values
55
56
    >>> _parse(r'"foo string"')
57
    'foo string'
58
    >>> _parse("a, b, c")
59
    ['a', 'b', 'c']
60
    >>> _parse("aaa")
61
    'aaa'
62
    """
63
    if (val_s.startswith('"') and val_s.endswith('"')) or \
64
            (val_s.startswith("'") and val_s.endswith("'")):
65
        return val_s[1:-1]
66
    elif sep in val_s:
67
        return [P.parse(x) for x in P.parse_list(val_s)]
68
    else:
69
        return P.parse(val_s)
70
71
72
def _to_s(val, sep=", "):
73
    """Convert any to string.
74
75
    :param val: An object
76
    :param sep: separator between values
77
78
    >>> _to_s([1, 2, 3])
79
    '1, 2, 3'
80
    >>> _to_s("aaa")
81
    'aaa'
82
    """
83
    if anyconfig.utils.is_iterable(val):
84
        return sep.join(str(x) for x in val)
85
    else:
86
        return str(val)
87
88
89
def _load(stream, to_container=dict, sep=_SEP, **kwargs):
90
    """
91
    :param stream: File or file-like object provides ini-style conf
92
    :param to_container: any callable to make container
93
    :param sep: Seprator string
94
95
    :return: Dict or dict-like object represents config values
96
    """
97
    _parse_val = _parse if kwargs.get("ac_parse_value", False) else _noop
98
99
    if kwargs.get("ac_ordered", False) or kwargs.get("dict_type", False):
100
        kwargs["dict_type"] = to_container = OrderedDict
101
    if "dict_type" not in kwargs and not kwargs.get("ac_ordered", True):
102
        kwargs["dict_type"] = to_container
103
104
    # Optional arguements for configparser.SafeConfigParser{,readfp}
105
    kwargs_0 = mk_opt_args(("defaults", "dict_type", "allow_no_value"), kwargs)
106
    kwargs_1 = mk_opt_args(("filename", ), kwargs)
107
108
    try:
109
        parser = configparser.SafeConfigParser(**kwargs_0)
110
    except TypeError:
111
        # .. note::
112
        #    It seems ConfigPaser.*ConfigParser in python 2.6 does not support
113
        #    'allow_no_value' option parameter, and TypeError will be thrown.
114
        kwargs_0 = mk_opt_args(("defaults", "dict_type"), kwargs)
115
        parser = configparser.SafeConfigParser(**kwargs_0)
116
117
    cnf = to_container()
118
    parser.readfp(stream, **kwargs_1)
119
120
    # .. note:: Process DEFAULT config parameters as special ones.
121
    defaults = parser.defaults()
122
    if defaults:
123
        cnf["DEFAULT"] = to_container()
124
        for key, val in iteritems(defaults):
125
            cnf["DEFAULT"][key] = _parse_val(val, sep)
126
127
    for sect in parser.sections():
128
        cnf[sect] = to_container()
129
        for key, val in parser.items(sect):
130
            cnf[sect][key] = _parse_val(val, sep)
131
132
    return cnf
133
134
135
def _dumps_itr(cnf):
136
    """
137
    :param cnf: Configuration data to dump
138
    """
139
    dkey = "DEFAULT"
140
    for sect, params in iteritems(cnf):
141
        yield "[%s]" % sect
142
143
        for key, val in iteritems(params):
144
            if sect != dkey and dkey in cnf and cnf[dkey].get(key) == val:
145
                continue  # It should be in [DEFAULT] section.
146
147
            yield "%s = %s" % (key, _to_s(val))
148
149
        yield ''  # it will be a separator between each sections.
150
151
152
def _dumps(cnf, **kwargs):
153
    """
154
    :param cnf: Configuration data to dump
155
    :param kwargs: optional keyword parameters to be sanitized :: dict
156
157
    :return: String representation of `cnf` object in INI format
158
    """
159
    return '\n'.join(l for l in _dumps_itr(cnf))
160
161
162
class Parser(anyconfig.backend.base.FromStreamLoader,
163
             anyconfig.backend.base.ToStringDumper):
164
    """
165
    Ini config files parser.
166
    """
167
    _type = "ini"
168
    _extensions = ["ini"]
169
    _load_opts = ["defaults", "dict_type", "allow_no_value", "filename",
170
                  "ac_parse_value"]
171
172
    dump_to_string = anyconfig.backend.base.to_method(_dumps)
173
174
    def load_from_stream(self, stream, to_container, **options):
175
        """
176
        Load config from given file like object `stream`.
177
178
        :param stream:  Config file or file like object
179
        :param to_container: callble to make a container object
180
        :param options: optional keyword arguments
181
182
        :return: Dict-like object holding config parameters
183
        """
184
        return _load(stream, to_container=to_container, **options)
185
186
# vim:sw=4:ts=4:et:
187