eric6/DebugClients/Python/coverage/tomlconfig.py

changeset 7427
362cd1b6f81a
equal deleted inserted replaced
7426:dc171b1d8261 7427:362cd1b6f81a
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2 # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
3
4 """TOML configuration support for coverage.py"""
5
6 import io
7 import os
8 import re
9
10 from coverage import env
11 from coverage.backward import configparser, path_types
12 from coverage.misc import CoverageException, substitute_variables
13
14
15 class TomlDecodeError(Exception):
16 """An exception class that exists even when toml isn't installed."""
17 pass
18
19
20 class TomlConfigParser:
21 """TOML file reading with the interface of HandyConfigParser."""
22
23 # This class has the same interface as config.HandyConfigParser, no
24 # need for docstrings.
25 # pylint: disable=missing-function-docstring
26
27 def __init__(self, our_file):
28 self.our_file = our_file
29 self.data = None
30
31 def read(self, filenames):
32 from coverage.optional import toml
33
34 # RawConfigParser takes a filename or list of filenames, but we only
35 # ever call this with a single filename.
36 assert isinstance(filenames, path_types)
37 filename = filenames
38 if env.PYVERSION >= (3, 6):
39 filename = os.fspath(filename)
40
41 try:
42 with io.open(filename, encoding='utf-8') as fp:
43 toml_text = fp.read()
44 except IOError:
45 return []
46 if toml:
47 toml_text = substitute_variables(toml_text, os.environ)
48 try:
49 self.data = toml.loads(toml_text)
50 except toml.TomlDecodeError as err:
51 raise TomlDecodeError(*err.args)
52 return [filename]
53 else:
54 has_toml = re.search(r"^\[tool\.coverage\.", toml_text, flags=re.MULTILINE)
55 if self.our_file or has_toml:
56 # Looks like they meant to read TOML, but we can't read it.
57 msg = "Can't read {!r} without TOML support. Install with [toml] extra"
58 raise CoverageException(msg.format(filename))
59 return []
60
61 def _get_section(self, section):
62 """Get a section from the data.
63
64 Arguments:
65 section (str): A section name, which can be dotted.
66
67 Returns:
68 name (str): the actual name of the section that was found, if any,
69 or None.
70 data (str): the dict of data in the section, or None if not found.
71
72 """
73 prefixes = ["tool.coverage."]
74 if self.our_file:
75 prefixes.append("")
76 for prefix in prefixes:
77 real_section = prefix + section
78 parts = real_section.split(".")
79 try:
80 data = self.data[parts[0]]
81 for part in parts[1:]:
82 data = data[part]
83 except KeyError:
84 continue
85 break
86 else:
87 return None, None
88 return real_section, data
89
90 def _get(self, section, option):
91 """Like .get, but returns the real section name and the value."""
92 name, data = self._get_section(section)
93 if data is None:
94 raise configparser.NoSectionError(section)
95 try:
96 return name, data[option]
97 except KeyError:
98 raise configparser.NoOptionError(option, name)
99
100 def has_option(self, section, option):
101 _, data = self._get_section(section)
102 if data is None:
103 return False
104 return option in data
105
106 def has_section(self, section):
107 name, _ = self._get_section(section)
108 return name
109
110 def options(self, section):
111 _, data = self._get_section(section)
112 if data is None:
113 raise configparser.NoSectionError(section)
114 return list(data.keys())
115
116 def get_section(self, section):
117 _, data = self._get_section(section)
118 return data
119
120 def get(self, section, option):
121 _, value = self._get(section, option)
122 return value
123
124 def _check_type(self, section, option, value, type_, type_desc):
125 if not isinstance(value, type_):
126 raise ValueError(
127 'Option {!r} in section {!r} is not {}: {!r}'
128 .format(option, section, type_desc, value)
129 )
130
131 def getboolean(self, section, option):
132 name, value = self._get(section, option)
133 self._check_type(name, option, value, bool, "a boolean")
134 return value
135
136 def getlist(self, section, option):
137 name, values = self._get(section, option)
138 self._check_type(name, option, values, list, "a list")
139 return values
140
141 def getregexlist(self, section, option):
142 name, values = self._get(section, option)
143 self._check_type(name, option, values, list, "a list")
144 for value in values:
145 value = value.strip()
146 try:
147 re.compile(value)
148 except re.error as e:
149 raise CoverageException(
150 "Invalid [%s].%s value %r: %s" % (name, option, value, e)
151 )
152 return values
153
154 def getint(self, section, option):
155 name, value = self._get(section, option)
156 self._check_type(name, option, value, int, "an integer")
157 return value
158
159 def getfloat(self, section, option):
160 name, value = self._get(section, option)
161 if isinstance(value, int):
162 value = float(value)
163 self._check_type(name, option, value, float, "a float")
164 return value

eric ide

mercurial