test_result_of_config_scope_contains_keys()   F
last analyzed

Complexity

Conditions 15

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
c 0
b 0
f 0
dl 0
loc 19
rs 2.9998

How to fix   Complexity   

Complexity

Complex classes like test_result_of_config_scope_contains_keys() 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
#!/usr/bin/env python
2
# coding=utf-8
3
from __future__ import division, print_function, unicode_literals
4
5
import pytest
6
import sacred.optional as opt
7
from sacred.config.config_scope import (ConfigScope, dedent_function_body,
8
                                        dedent_line, get_function_body,
9
                                        is_empty_or_comment)
10
from sacred.config.custom_containers import DogmaticDict, DogmaticList
11
12
13
@pytest.fixture
14
def conf_scope():
15
    @ConfigScope
16
    def cfg():
17
        # description for a
18
        a = 1
19
        # description for b and c
20
        b, c = 2.0, True
21
        # d and dd are both strings
22
        d = dd = 'string'
23
        e = [1, 2, 3]  # inline description for e
24
        f = {'a': 'b', 'c': 'd'}
25
        composit1 = a + b
26
        # pylint: this comment is filtered out
27
        composit2 = f['c'] + "ada"
28
29
        func1 = lambda: 23
30
31
        deriv = func1()
32
33
        def func2(a):
34
            return 'Nothing to report' + a
35
36
        some_type = int
37
38
    cfg()
39
    return cfg
40
41
42
def test_result_of_config_scope_is_dict(conf_scope):
43
    cfg = conf_scope()
44
    assert isinstance(cfg, dict)
45
46
47
def test_result_of_config_scope_contains_keys(conf_scope):
48
    cfg = conf_scope()
49
    assert set(cfg.keys()) == {'a', 'b', 'c', 'd', 'dd', 'e', 'f',
50
                               'composit1', 'composit2', 'deriv', 'func1',
51
                               'func2', 'some_type'}
52
53
    assert cfg['a'] == 1
54
    assert cfg['b'] == 2.0
55
    assert cfg['c']
56
    assert cfg['d'] == 'string'
57
    assert cfg['dd'] == 'string'
58
    assert cfg['e'] == [1, 2, 3]
59
    assert cfg['f'] == {'a': 'b', 'c': 'd'}
60
    assert cfg['composit1'] == 3.0
61
    assert cfg['composit2'] == 'dada'
62
    assert cfg['func1']() == 23
63
    assert cfg['func2'](', sir!') == 'Nothing to report, sir!'
64
    assert cfg['some_type'] == int
65
    assert cfg['deriv'] == 23
66
67
68
def test_fixing_values(conf_scope):
69
    cfg = conf_scope({'a': 100})
70
    assert cfg['a'] == 100
71
    assert cfg['composit1'] == 102.0
72
73
74
def test_fixing_nested_dicts(conf_scope):
75
    cfg = conf_scope({'f': {'c': 't'}})
76
    assert cfg['f']['a'] == 'b'
77
    assert cfg['f']['c'] == 't'
78
    assert cfg['composit2'] == 'tada'
79
80
81
def test_adding_values(conf_scope):
82
    cfg = conf_scope({'g': 23, 'h': {'i': 10}})
83
    assert cfg['g'] == 23
84
    assert cfg['h'] == {'i': 10}
85
    assert cfg.added == {'g', 'h', 'h.i'}
86
87
88
def test_typechange(conf_scope):
89
    cfg = conf_scope({'a': 'bar', 'b': 'foo', 'c': 1})
90
    assert cfg.typechanged == {'a': (int, type('bar')),
91
                               'b': (float, type('foo')),
92
                               'c': (bool, int)}
93
94
95
def test_nested_typechange(conf_scope):
96
    cfg = conf_scope({'f': {'a': 10}})
97
    assert cfg.typechanged == {'f.a': (type('a'), int)}
98
99
100
def test_config_docs(conf_scope):
101
    cfg = conf_scope()
102
    assert cfg.docs == {
103
        'a': 'description for a',
104
        'b': 'description for b and c',
105
        'c': 'description for b and c',
106
        'd': 'd and dd are both strings',
107
        'dd': 'd and dd are both strings',
108
        'e': 'inline description for e',
109
        'seed': 'the random seed for this experiment'
110
    }
111
112
113
def is_dogmatic(a):
114
    if isinstance(a, (DogmaticDict, DogmaticList)):
115
        return True
116
    elif isinstance(a, dict):
117
        return any(is_dogmatic(v) for v in a.values())
118
    elif isinstance(a, (list, tuple)):
119
        return any(is_dogmatic(v) for v in a)
120
121
122
def test_conf_scope_is_not_dogmatic(conf_scope):
123
    assert not is_dogmatic(conf_scope({'e': [1, 1, 1]}))
124
125
126
@pytest.mark.skipif(not opt.has_numpy, reason="requires numpy")
127
def test_conf_scope_handles_numpy_bools():
128
    @ConfigScope
129
    def conf_scope():
130
        a = opt.np.bool_(1)
131
132
    cfg = conf_scope()
133
    assert 'a' in cfg
134
    assert cfg['a']
135
136
137
def test_conf_scope_can_access_preset():
138
    @ConfigScope
139
    def conf_scope(a):
140
        answer = 2 * a
141
142
    cfg = conf_scope(preset={'a': 21})
143
    assert cfg['answer'] == 42
144
145
146
def test_conf_scope_contains_presets():
147
    @ConfigScope
148
    def conf_scope(a):
149
        answer = 2 * a
150
151
    cfg = conf_scope(preset={'a': 21, 'unrelated': True})
152
    assert set(cfg.keys()) == {'a', 'answer', 'unrelated'}
153
    assert cfg['a'] == 21
154
    assert cfg['answer'] == 42
155
    assert cfg['unrelated'] is True
156
157
158
def test_conf_scope_cannot_access_undeclared_presets():
159
    @ConfigScope
160
    def conf_scope():
161
        answer = 2 * a
162
163
    with pytest.raises(NameError):
164
        conf_scope(preset={'a': 21})
165
166
167
def test_conf_scope_can_access_fallback():
168
    @ConfigScope
169
    def conf_scope(a):
170
        answer = 2 * a
171
172
    cfg = conf_scope(fallback={'a': 21})
173
    assert cfg['answer'] == 42
174
175
176
def test_conf_scope_does_not_contain_fallback():
177
    @ConfigScope
178
    def conf_scope(a):
179
        answer = 2 * a
180
181
    cfg = conf_scope(fallback={'a': 21, 'b': 10})
182
    assert set(cfg.keys()) == {'answer'}
183
184
185
def test_conf_scope_cannot_access_undeclared_fallback():
186
    @ConfigScope
187
    def conf_scope():
188
        answer = 2 * a
189
190
    with pytest.raises(NameError):
191
        conf_scope(fallback={'a': 21})
192
193
194
def test_conf_scope_can_access_fallback_and_preset():
195
    @ConfigScope
196
    def conf_scope(a, b):
197
        answer = a + b
198
199
    cfg = conf_scope(preset={'b': 40}, fallback={'a': 2})
200
    assert cfg['answer'] == 42
201
202
203
def test_conf_raises_for_unaccessible_arguments():
204
    @ConfigScope
205
    def conf_scope(a, b, c):
206
        answer = 42
207
208
    with pytest.raises(KeyError):
209
        conf_scope(preset={'a': 1}, fallback={'b': 2})
210
211
212
def test_can_access_globals_from_original_scope():
213
    from .enclosed_config_scope import cfg as conf_scope
214
    cfg = conf_scope()
215
    assert set(cfg.keys()) == {'answer'}
216
    assert cfg['answer'] == 42
217
218
219
SEVEN = 7
220
221
222
def test_cannot_access_globals_from_calling_scope():
223
    from .enclosed_config_scope import cfg2 as conf_scope
224
    with pytest.raises(NameError):
225
        conf_scope()  # would require SEVEN
226
227
228 View Code Duplication
def test_fixed_subentry_of_preset():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
229
    @ConfigScope
230
    def conf_scope():
231
        pass
232
233
    cfg = conf_scope(preset={'d': {'a': 1, 'b': 2}}, fixed={'d': {'a': 10}})
234
235
    assert set(cfg.keys()) == {'d'}
236
    assert set(cfg['d'].keys()) == {'a', 'b'}
237
    assert cfg['d']['a'] == 10
238
    assert cfg['d']['b'] == 2
239
240
241
@pytest.mark.parametrize("line,indent,expected", [
242
    ('    a=5', '    ', 'a=5'),
243
    ('  a=5',   '    ', 'a=5'),
244
    ('a=5',     '    ', 'a=5'),
245
    ('    a=5', '  ',   '  a=5'),
246
    ('    a=5', '',     '    a=5'),
247
    ('    a=5', '\t',   '    a=5'),
248
    ('  a=5', '      ', 'a=5'),
249
    ('    a=5', '  \t', '  a=5')
250
])
251
def test_dedent_line(line, indent, expected):
252
    assert dedent_line(line, indent) == expected
253
254
255
@pytest.mark.parametrize("line,expected", [
256
    ('', True),
257
    ('  ', True),
258
    ('\n', True),
259
    ('    \n', True),
260
    ('  \t \n', True),
261
    ('#comment', True),
262
    ('   #comment', True),
263
    ('  a=5 # not comment', False),
264
    ('a=5', False),
265
    ('"""', False),
266
    ("'''", False)
267
268
])
269
def test_is_empty_or_comment(line, expected):
270
    assert is_empty_or_comment(line) == expected
271
272
273
def evil_indentation_func(a,
274
                                    b,
275
c, d):
276
# Lets do the most evil things with indentation
277
  # 1
278
    # 2
279
     # ran
280
        """ and also in the docstring
281
             atrne
282
    uiaeue
283
utdr
284
    """
285
        alpha = 0.1
286
        d = ('even', 'more',
287
    'evilness')
288
        wat = """ multi
289
    line
290
strings
291
    """
292
# another comment
293
        # this one is ok
294
    # madness
295
        foo=12
296
297
        def subfunc():
298
            return 23
299
300
body = '''# Lets do the most evil things with indentation
301
  # 1
302
    # 2
303
     # ran
304
        """ and also in the docstring
305
             atrne
306
    uiaeue
307
utdr
308
    """
309
        alpha = 0.1
310
        d = ('even', 'more',
311
    'evilness')
312
        wat = """ multi
313
    line
314
strings
315
    """
316
# another comment
317
        # this one is ok
318
    # madness
319
        foo=12
320
321
        def subfunc():
322
            return 23
323
'''
324
325
dedented_body = '''# Lets do the most evil things with indentation
326
# 1
327
# 2
328
# ran
329
""" and also in the docstring
330
     atrne
331
uiaeue
332
utdr
333
"""
334
alpha = 0.1
335
d = ('even', 'more',
336
'evilness')
337
wat = """ multi
338
line
339
strings
340
"""
341
# another comment
342
# this one is ok
343
# madness
344
foo=12
345
346
def subfunc():
347
    return 23
348
'''
349
350
351
def test_dedent_body():
352
    assert dedent_function_body(body) == dedented_body
353
354
355
def test_get_function_body():
356
    func_body, line_offset = get_function_body(evil_indentation_func)
357
    assert func_body == body
358
359
360
def test_config_scope_can_deal_with_indentation_madness():
361
    # assert_no_raise:
362
    ConfigScope(evil_indentation_func)
363