• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cokelaer / easydev / 8869842052

28 Apr 2024 07:15PM UTC coverage: 82.609% (-0.1%) from 82.71%
8869842052

push

github

cokelaer
roll back the try/except on httplib (regression)

4 of 4 new or added lines in 1 file covered. (100.0%)

14 existing lines in 2 files now uncovered.

703 of 851 relevant lines covered (82.61%)

4.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.26
/easydev/config_tools.py
1
# -*- python -*-
2
# -*- coding: utf-8 -*-
3
#
4
#  This file is part of the easydev software
5
#
6
#  Copyright (c) 2011-2024
7
#
8
#  File author(s): Thomas Cokelaer <cokelaer@gmail.com>
9
#
10
#  Distributed under BSD3 license
11
#
12
#  Website: https://github.com/cokelaer/easydev
13
#  Documentation: http://packages.python.org/easydev
14
#
15
##############################################################################
16
import os
6✔
17
from configparser import ConfigParser
6✔
18

19
__all__ = ["CustomConfig", "DynamicConfigParser"]
6✔
20

21

22
class _DictSection:
6✔
23
    """Dictionary section.
24

25
    Reference: https://gist.github.com/dangoakachan/3855920
26

27
    """
28

29
    def __init__(self, config, section):
6✔
30
        object.__setattr__(self, "_config", config)
6✔
31
        object.__setattr__(self, "_section", section)
6✔
32

33
    def __getattr__(self, attr):
6✔
UNCOV
34
        return self.get(attr, None)
×
35

36
    __getitem__ = __getattr__
6✔
37

38
    def get(self, attr, default=None):
6✔
UNCOV
39
        if attr in self:
×
UNCOV
40
            return self._config.get(self._section, attr)
×
41
        else:  # pragma: no cover
42
            return default
43

44
    def __setattr__(self, attr, value):
6✔
45
        if attr.startswith("_"):
6✔
UNCOV
46
            object.__setattr__(self, attr, value)
×
47
        else:  # pragma: no cover
48
            self.__setitem__(attr, value)
49

50
    def __setitem__(self, attr, value):
6✔
51
        if self._section not in self._config:  # pragma: no cover
52
            self._config.add_section(self._section)
53

54
        self._config.set(self._section, attr, str(value))
6✔
55

56
    def __delattr__(self, attr):
6✔
57
        if attr in self:
6✔
58
            self._config.remove_option(self._section, attr)
6✔
59

60
    __delitem__ = __delattr__
6✔
61

62
    def __contains__(self, attr):
6✔
63
        config = self._config
6✔
64
        section = self._section
6✔
65

66
        return config.has_section(section) and config.has_option(section, attr)
6✔
67

68

69
class DynamicConfigParser(ConfigParser):
6✔
70
    """Enhanced version of Config Parser
71

72
    Provide some aliases to the original ConfigParser class and
73
    new methods such as :meth:`save` to save the config object in a file.
74

75
    .. code-block:: python
76

77
        >>> from easydev.config_tools import ConfigExample
78
        >>> standard_config_file = ConfigExample().config
79
        >>> c = DynamicConfigParser(standard_config_file)
80
        >>>
81
        >>> # then to get the sections, simply type as you would normally do with ConfigParser
82
        >>> c.sections()
83
        >>> # or for the options of a specific sections:
84
        >>> c.get_options('General')
85

86
    You can now also directly access to an option as follows::
87

88
        c.General.tag
89

90
    Then, you can add or remove sections (:meth:`remove_section`, :meth:`add_section`),
91
    or option from a section :meth:`remove_option`. You can save the instance into a file
92
    or print it::
93

94
        print(c)
95

96
    .. warning:: if you set options manually (e.G. self.GA.test =1 if GA is a
97
        section and test one of its options), then the save/write does not work
98
        at the moment even though if you typoe self.GA.test, it has the correct value
99

100

101
    Methods inherited from ConfigParser are available:
102

103
    ::
104

105
        # set value of an option in a section
106
        c.set(section, option, value=None)
107
        # but with this class, you can also use the attribute
108
        c.section.option = value
109

110
        # set value of an option in a section
111
        c.remove_option(section, option)
112
        c.remove_section(section)
113

114

115
    """
116

117
    def __init__(self, config_or_filename=None, *args, **kargs):
6✔
118

119
        object.__setattr__(self, "_filename", config_or_filename)
6✔
120
        # why not a super usage here ? Maybe there were issues related
121
        # to old style class ?
122
        ConfigParser.__init__(self, *args, **kargs)
6✔
123

124
        if isinstance(self._filename, str) and os.path.isfile(self._filename):
6✔
125
            self.read(self._filename)
6✔
126
        elif isinstance(config_or_filename, ConfigParser):
6✔
127
            self._replace_config(config_or_filename)
6✔
128
        elif config_or_filename == None:
6✔
129
            pass
6✔
130
        else:
131
            raise TypeError("config_or_filename must be a valid filename or valid ConfigParser instance")
6✔
132

133
    def read(self, filename):
6✔
134
        """Load a new config from a filename (remove all previous sections)"""
135
        if os.path.isfile(filename) == False:
6✔
136
            raise IOError("filename {0} not found".format(filename))
6✔
137

138
        config = ConfigParser()
6✔
139
        config.read(filename)
6✔
140

141
        self._replace_config(config)
6✔
142

143
    def _replace_config(self, config):
6✔
144
        """Remove all sections and add those from the input config file
145

146
        :param config:
147

148
        """
149
        for section in self.sections():
6✔
UNCOV
150
            self.remove_section(section)
×
151

152
        for section in config.sections():
6✔
153
            self.add_section(section)
6✔
154
            for option in config.options(section):
6✔
155
                data = config.get(section, option)
6✔
156
                self.set(section, option, data)
6✔
157

158
    def get_options(self, section):
6✔
159
        """Alias to get all options of a section in a dictionary
160

161
        One would normally need to extra each option manually::
162

163
            for option in config.options(section):
164
                config.get(section, option, raw=True)#
165

166
        then, populate a dictionary and finally take care of the types.
167

168
        .. warning:: types may be changed .For instance the string "True"
169
            is interpreted as a True boolean.
170

171
        ..  seealso:: internally, this method uses :meth:`section2dict`
172
        """
173
        return self.section2dict(section)
6✔
174

175
    def section2dict(self, section):
6✔
176
        """utility that extract options of a ConfigParser section into a dictionary
177

178
        :param ConfigParser config: a ConfigParser instance
179
        :param str section: the section to extract
180

181
        :returns: a dictionary where key/value contains all the
182
            options/values of the section required
183

184
        Let us build up  a standard config file:
185
        .. code-block:: python
186

187
            >>> # Python 3 code
188
            >>> from configparser import ConfigParser
189
            >>> c = ConfigParser()
190
            >>> c.add_section('general')
191
            >>> c.set('general', 'step', str(1))
192
            >>> c.set('general', 'verbose', 'True')
193

194
        To access to the step options, you would write::
195

196
            >>> c.get('general', 'step')
197

198
        this function returns a dictionary that may be manipulated as follows::
199

200
            >>> d_dict.general.step
201

202
        .. note:: a value (string) found to be True, Yes, true,
203
            yes is transformed to True
204
        .. note:: a value (string) found to be False, No, no,
205
            false is transformed to False
206
        .. note:: a value (string) found to be None; none,
207
            "" (empty string) is set to None
208
        .. note:: an integer is cast into an int
209
        """
210
        options = {}
6✔
211
        for option in self.options(section):  # pragma no cover
212
            data = self.get(section, option, raw=True)
213
            if data.lower() in ["true", "yes"]:
214
                options[option] = True
215
            elif data.lower() in ["false", "no"]:
216
                options[option] = False
217
            elif data in ["None", None, "none", ""]:
218
                options[option] = None
219
            else:
220
                try:  # numbers
221
                    try:
222
                        options[option] = self.getint(section, option)
223
                    except:
224
                        options[option] = self.getfloat(section, option)
225
                except:  # string
226
                    options[option] = self.get(section, option, raw=True)
227
        return options
6✔
228

229
    def save(self, filename):
6✔
230
        """Save all sections/options to a file.
231

232
        :param str filename: a valid filename
233

234
        ::
235

236
            config = ConfigParams('config.ini') #doctest: +SKIP
237
            config.save('config2.ini') #doctest: +SKIP
238

239
        """
240
        try:
6✔
241
            if os.path.exists(filename) == True:
6✔
242
                print("Warning: over-writing %s " % filename)
6✔
243
            fp = open(filename, "w")
6✔
244
        except Exception as err:  # pragma: no cover
245
            print(err)
246
            raise Exception("filename could not be opened")
247

248
        self.write(fp)
6✔
249
        fp.close()
6✔
250

251
    def add_option(self, section, option, value=None):
6✔
252
        """add an option to an existing section (with a value)
253

254
        .. code-block:: python
255

256
            >>> c = DynamicConfigParser()
257
            >>> c.add_section("general")
258
            >>> c.add_option("general", "verbose", True)
259
        """
260
        assert section in self.sections(), "unknown section"
6✔
261
        # TODO I had to cast to str with DictSection
262
        self.set(section, option, value=str(value))
6✔
263

264
    def __str__(self):
6✔
265
        str_ = ""
6✔
266
        for section in self.sections():
6✔
267
            str_ += "[" + section + "]\n"
6✔
268
            for option in self.options(section):
6✔
269
                data = self.get(section, option, raw=True)
6✔
270
                str_ += option + " = " + str(data) + "\n"
6✔
271
            str_ += "\n\n"
6✔
272

273
        return str_
6✔
274

275
    def __getattr__(self, key):
6✔
276
        return _DictSection(self, key)
6✔
277

278
    __getitem__ = __getattr__
6✔
279

280
    def __setattr__(self, attr, value):
6✔
281
        if attr.startswith("_") or attr:
6✔
282
            object.__setattr__(self, attr, value)
6✔
283
        else:  # pragma: no cover
284
            self.__setitem__(attr, value)
285

286
    # def __setitem__(self, attr, value):
287
    #    if isinstance(value, dict):
288
    #        section = self[attr]
289
    #        for k, v in value.items():
290
    #            section[k] = v
291
    #    else:
292
    #        raise TypeError("value must be a valid dictionary")
293

294
    def __delattr__(self, attr):
6✔
UNCOV
295
        if attr in self:
×
UNCOV
296
            self.remove_section(attr)
×
297

298
    def __contains__(self, attr):
6✔
299
        return self.has_section(attr)
6✔
300

301
    def __eq__(self, data):
6✔
302
        # FIXME if you read file, the string "True" is a string
303
        # but you may want it to be converted to a True boolean value
304
        if sorted(data.sections()) != sorted(self.sections()):
6✔
305
            print("Sections differ")
6✔
306
            return False
6✔
307
        for section in self.sections():
6✔
308

309
            for option in self.options(section):
6✔
310
                try:
6✔
311
                    if str(self.get(section, option, raw=True)) != str(data.get(section, option, raw=True)):
6✔
312
                        print("option %s in section %s differ" % (option, section))
6✔
313
                        return False
6✔
314
                except:  # pragma: no cover
315
                    return False
316
        return True
6✔
317

318

319
class CustomConfig(object):
6✔
320
    """Base class to manipulate a config directory"""
321

322
    def __init__(self, name, verbose=False):
6✔
323
        self.verbose = verbose
6✔
324
        from easydev import appdirs
6✔
325

326
        self.appdirs = appdirs.AppDirs(name)
6✔
327

328
    def init(self):
6✔
329
        sdir = self.appdirs.user_config_dir
6✔
330
        self._get_and_create(sdir)
6✔
331

332
    def _get_config_dir(self):
6✔
333
        sdir = self.appdirs.user_config_dir
6✔
334
        return self._get_and_create(sdir)
6✔
335

336
    user_config_dir = property(_get_config_dir, doc="return directory of this configuration file")
6✔
337

338
    def _get_and_create(self, sdir):
6✔
339
        if not os.path.exists(sdir):
6✔
340
            print("Creating directory %s " % sdir)
6✔
341
            try:
6✔
342
                self._mkdirs(sdir)
6✔
343
            except Exception:  # pragma: no cover
344
                print("Could not create the path %s " % sdir)
345
                return None
346
        return sdir
6✔
347

348
    def _mkdirs(self, newdir, mode=0o777):
6✔
349
        """See :func:`easydev.tools.mkdirs`"""
350
        from easydev.tools import mkdirs
6✔
351

352
        mkdirs(newdir, mode)
6✔
353

354
    def remove(self):
6✔
355
        try:
6✔
356
            sdir = self.appdirs.user_config_dir
6✔
357
            os.rmdir(sdir)
6✔
358
        except Exception as err:  # pragma: no cover
359
            raise Exception(err)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc