Merge lp:~canonical-platform-qa/uci-config/decreased-complexity into lp:~canonical-ci-engineering/uci-config/trunk
- decreased-complexity
- Merge into trunk
Proposed by Federico Gimenez
Status: | Work in progress |
---|---|
Proposed branch: | lp:~canonical-platform-qa/uci-config/decreased-complexity |
Merge into: | lp:~canonical-ci-engineering/uci-config/trunk |
Prerequisite: | lp:~canonical-platform-qa/uci-config/py3-packaging |
Diff against target: | 295 lines (+83/-125) 4 files modified setup.py (+3/-3) uciconfig/options.py (+3/-12) uciconfig/parsers.py (+59/-93) uciconfig/stacks.py (+18/-17) |
To merge this branch: | bzr merge lp:~canonical-platform-qa/uci-config/decreased-complexity |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Canonical CI Engineering | Pending | ||
Review via email: |
Commit message
mccabe complexity reduction
Description of the change
There are methods reported to be too complex by the McCabe script. This is attempt to reduce the reported complexity, while keeping the api interface and the test suite passing.
To post a comment you must log in.
- 67. By Federico Gimenez
-
some tests fixed
- 68. By Federico Gimenez
-
only 4 tests red
- 69. By Federico Gimenez
-
3 to go
Unmerged revisions
- 69. By Federico Gimenez
-
3 to go
- 68. By Federico Gimenez
-
only 4 tests red
- 67. By Federico Gimenez
-
some tests fixed
- 66. By Federico Gimenez
-
reported complexity reduced to 10
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'setup.py' |
2 | --- setup.py 2014-04-01 07:41:42 +0000 |
3 | +++ setup.py 2015-03-03 15:56:20 +0000 |
4 | @@ -3,16 +3,16 @@ |
5 | # This file is part of Ubuntu Continuous Integration configuration framework. |
6 | # |
7 | # Copyright 2013, 2014 Canonical Ltd. |
8 | -# |
9 | +# |
10 | # This program is free software: you can redistribute it and/or modify it under |
11 | # the terms of the GNU General Public License version 3, as published by the |
12 | # Free Software Foundation. |
13 | -# |
14 | +# |
15 | # This program is distributed in the hope that it will be useful, but WITHOUT |
16 | # ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
17 | # SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | # General Public License for more details. |
19 | -# |
20 | +# |
21 | # You should have received a copy of the GNU General Public License along with |
22 | # this program. If not, see <http://www.gnu.org/licenses/>. |
23 | |
24 | |
25 | === modified file 'uciconfig/options.py' |
26 | --- uciconfig/options.py 2014-02-15 16:49:46 +0000 |
27 | +++ uciconfig/options.py 2015-03-03 15:56:20 +0000 |
28 | @@ -77,19 +77,12 @@ |
29 | safely unquote them. It is provided so daughter classes can handle |
30 | the quoting themselves. |
31 | """ |
32 | - if override_from_env is None: |
33 | - override_from_env = [] |
34 | - if default_from_env is None: |
35 | - default_from_env = [] |
36 | self.name = name |
37 | self._help = help_string |
38 | - self.override_from_env = override_from_env |
39 | + self.override_from_env = override_from_env or [] |
40 | # Convert the default value to a unicode string so all values are |
41 | # strings internally before conversion (via from_unicode) is attempted. |
42 | - if default is None: |
43 | - self.default = None |
44 | - elif default is MANDATORY: |
45 | - # Special case, no further check needed |
46 | + if default is None or default is MANDATORY or callable(default): |
47 | self.default = default |
48 | elif isinstance(default, list): |
49 | # Only the empty list is supported |
50 | @@ -100,13 +93,11 @@ |
51 | elif isinstance(default, (unicode, bool, int, float)): |
52 | # Rely on python to convert strings, booleans, floats and integers |
53 | self.default = '{}'.format(default) |
54 | - elif callable(default): |
55 | - self.default = default |
56 | else: |
57 | # other python objects are not expected |
58 | raise AssertionError( |
59 | '{!r} is not supported as a default value'.format(default)) |
60 | - self.default_from_env = default_from_env |
61 | + self.default_from_env = default_from_env or [] |
62 | self.from_unicode = from_unicode |
63 | self.unquote = unquote |
64 | if invalid and invalid not in ('warning', 'error'): |
65 | |
66 | === modified file 'uciconfig/parsers.py' |
67 | --- uciconfig/parsers.py 2015-01-29 10:17:25 +0000 |
68 | +++ uciconfig/parsers.py 2015-03-03 15:56:20 +0000 |
69 | @@ -174,109 +174,75 @@ |
70 | pre = None |
71 | while remaining: |
72 | pre_comment = self.comment_re.match(remaining) |
73 | - if pre_comment: |
74 | - # Buffer the comment lines until a section or an option collect |
75 | - # it |
76 | - if pre is None: |
77 | - pre = pre_comment.group(1) |
78 | - else: |
79 | - pre = pre + pre_comment.group(1) |
80 | - remaining = remaining[pre_comment.end():] |
81 | - self.line += 1 |
82 | - continue |
83 | - |
84 | section = self.section_re.match(remaining) |
85 | - if section: |
86 | - # A new section |
87 | - name = section.group(1) |
88 | - if name == '': |
89 | - raise errors.SectionEmptyName(self.path, self.line) |
90 | - remaining = remaining[section.end():] |
91 | - post_comment = self.comment_re.match(remaining) |
92 | - if post_comment: |
93 | - post = post_comment.group(1) |
94 | - remaining = remaining[post_comment.end():] |
95 | - else: |
96 | - post = None |
97 | - tokens.append(SectionDefinition(name, pre, post)) |
98 | - pre = None |
99 | - if post is not None: |
100 | - self.line += 1 |
101 | - post = None |
102 | - continue |
103 | - |
104 | key_matches = self.option_re.match(remaining) |
105 | - if key_matches: |
106 | - if not tokens: |
107 | - # First option definition without a previous section, it's |
108 | - # the None section. |
109 | - tokens.append(SectionDefinition(None)) |
110 | - key = key_matches.group(1) |
111 | - remaining = remaining[key_matches.end():] |
112 | - # the option value is still in 'remaining' which can be empty |
113 | - # or start with a newline if no value is defined for the |
114 | - # option. |
115 | - value, post, remaining = self.parse_value(remaining) |
116 | - tokens.append(OptionDefinition(key, value, pre, post)) |
117 | - pre = None |
118 | - continue |
119 | - |
120 | - if remaining.startswith('\n'): |
121 | + if pre_comment: |
122 | + remaining, pre = self._parse_precomment( |
123 | + pre_comment, remaining, pre) |
124 | + elif section: |
125 | + tokens, remaining, pre = self._parse_section( |
126 | + section, tokens, remaining, pre) |
127 | + elif key_matches: |
128 | + tokens, remaining, pre = self._parse_key( |
129 | + key_matches, tokens, remaining, pre) |
130 | + elif remaining.startswith('\n'): |
131 | # Keep track of the current line |
132 | self.line += 1 |
133 | remaining = remaining[1:] |
134 | - continue |
135 | - |
136 | - if remaining.startswith(' '): |
137 | + elif remaining.startswith(' '): |
138 | # Consume spaces to get rid of empty lines |
139 | remaining = remaining[1:] |
140 | - continue |
141 | - |
142 | - raise errors.InvalidSyntax(self.path, self.line) |
143 | + else: |
144 | + raise errors.InvalidSyntax(self.path, self.line) |
145 | return tokens |
146 | |
147 | - def parse_value(self, text): |
148 | - """Parse an option value. |
149 | - |
150 | - :param text: The unicode string to parse containing the value. |
151 | - |
152 | - :return: A (value, post, remaining) tuple where 'value' is the string |
153 | - representing the value, 'post' the associated comment (can be None) |
154 | - and 'remaining' the rest of text after parsing. |
155 | - """ |
156 | - value = '' |
157 | - post = '' |
158 | - cur = 0 |
159 | - post_last = len(text) |
160 | - # ignore leading spaces |
161 | - while cur < post_last and text[cur] == ' ': |
162 | - cur += 1 |
163 | - # build value until we encounter end of line or a comment |
164 | - while cur < post_last and text[cur] not in ('#', '\n'): |
165 | - value += text[cur] |
166 | - cur += 1 |
167 | - # remove trailing spaces |
168 | - spaces = '' |
169 | - while value.endswith(' '): |
170 | - spaces += value[-1] |
171 | - value = value[:-1] |
172 | - # comment (if present) ends at end of line |
173 | - if cur < post_last and text[cur] == '#': |
174 | - post = spaces |
175 | - while cur < post_last and text[cur] != '\n': |
176 | - post += text[cur] |
177 | - cur += 1 |
178 | - if post == '': |
179 | + def _parse_precomment(self, pre_comment, remaining, pre): |
180 | + # Buffer the comment lines until a section or an option collect |
181 | + # it |
182 | + if pre is None: |
183 | + pre = pre_comment.group(1) |
184 | + else: |
185 | + pre = pre + pre_comment.group(1) |
186 | + remaining = remaining[pre_comment.end():] |
187 | + self.line += 1 |
188 | + return (remaining, pre) |
189 | + |
190 | + def _parse_section(self, section, tokens, remaining, pre): |
191 | + name = section.group(1) |
192 | + if name == '': |
193 | + raise errors.SectionEmptyName(self.path, self.line) |
194 | + remaining = remaining[section.end():] |
195 | + post_comment = self.comment_re.match(remaining) |
196 | + if post_comment: |
197 | + post = post_comment.group(1) |
198 | + remaining = remaining[post_comment.end():] |
199 | + else: |
200 | post = None |
201 | - # Consume end of line if present |
202 | - if cur < post_last and text[cur] == '\n': |
203 | - if post is not None: |
204 | - # If there is a post comment, it needs to include the '\n', |
205 | - # yet, the current line shouldn't be updated yet, so we leave |
206 | - # it in remaining. |
207 | - post += '\n' |
208 | - remaining = text[cur:] |
209 | - return value, post, remaining |
210 | + tokens.append(SectionDefinition(name, pre, post)) |
211 | + pre = None |
212 | + if post is not None: |
213 | + self.line += 1 |
214 | + |
215 | + return (tokens, remaining, pre) |
216 | + |
217 | + def _parse_key(self, key_matches, tokens, remaining, pre): |
218 | + if not tokens: |
219 | + # First option definition without a previous section, it's |
220 | + # the None section. |
221 | + tokens.append(SectionDefinition(None)) |
222 | + key = key_matches.group(1) |
223 | + remaining = remaining[key_matches.end():] |
224 | + result = re.match( |
225 | + r'\ *([\\,\{\}\w /\-\._]*)?([ \t\r\f\v]*#.*\n)?(.*)', |
226 | + remaining, |
227 | + re.DOTALL) |
228 | + value, post, remaining = ( |
229 | + result.group(1).strip(), result.group(2), result.group(3)) |
230 | + tokens.append(OptionDefinition(key, value, pre, post)) |
231 | + if post is not None: |
232 | + self.line += 1 |
233 | + pre = None |
234 | + return (tokens, remaining, pre) |
235 | |
236 | def make_sections(self, tokens): |
237 | """Yields the sections built from the received tokens. |
238 | |
239 | === modified file 'uciconfig/stacks.py' |
240 | --- uciconfig/stacks.py 2014-03-26 23:22:19 +0000 |
241 | +++ uciconfig/stacks.py 2015-03-03 15:56:20 +0000 |
242 | @@ -225,36 +225,37 @@ |
243 | # Not registered |
244 | opt = None |
245 | |
246 | - def expand_and_convert(val): |
247 | - # This may need to be called in different contexts if the value is |
248 | - # None or ends up being None during expansion or conversion. |
249 | - if val is not None: |
250 | - if expand: |
251 | - val = self._expand_options_in_string(val) |
252 | - if opt is None: |
253 | - val = found_store.unquote(val) |
254 | - elif convert: |
255 | - val = opt.convert_from_unicode(found_store, val) |
256 | - return val |
257 | - |
258 | # First of all, check if the environment can override the configuration |
259 | # value |
260 | if opt is not None and opt.override_from_env: |
261 | - value = opt.get_override() |
262 | - value = expand_and_convert(value) |
263 | + value = self.expand_and_convert( |
264 | + opt.get_override(), expand, opt, found_store, convert) |
265 | if value is None: |
266 | for store, section in self.iter_sections(): |
267 | value = section.get(name) |
268 | if value is not None: |
269 | found_store = store |
270 | break |
271 | - value = expand_and_convert(value) |
272 | + value = self.expand_and_convert( |
273 | + value, expand, opt, found_store, convert) |
274 | if opt is not None and value is None: |
275 | # If the option is registered, it may provide a default value |
276 | - value = opt.get_default() |
277 | - value = expand_and_convert(value) |
278 | + value = self.expand_and_convert( |
279 | + opt.get_default(), expand, opt, found_store, convert) |
280 | return value |
281 | |
282 | + def expand_and_convert(self, val, expand, opt, found_store, convert): |
283 | + # This may need to be called in different contexts if the value is |
284 | + # None or ends up being None during expansion or conversion. |
285 | + if val is not None: |
286 | + if expand: |
287 | + val = self._expand_options_in_string(val) |
288 | + if opt is None: |
289 | + val = found_store.unquote(val) |
290 | + elif convert: |
291 | + val = opt.convert_from_unicode(found_store, val) |
292 | + return val |
293 | + |
294 | def expand_options(self, string, env=None): |
295 | """Expand option references in the string in the configuration context. |
296 |