scipy-doctest 1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ __all__ = ['func8']
2
+
3
+ def func8():
4
+ """Handle MPL stopwords.
5
+ >>> import matplotlib.pyplot as plt
6
+ >>> plt.show()
7
+
8
+ >>> plt.xlim([1, 2])
9
+ """
@@ -0,0 +1,170 @@
1
+ import pytest
2
+
3
+ from . import finder_cases
4
+ from ..util import get_all_list, get_public_objects
5
+ from ..impl import DTFinder, DTConfig
6
+ from ..frontend import find_doctests
7
+
8
+ def test_get_all_list():
9
+ items, depr, other = get_all_list(finder_cases)
10
+ assert sorted(items) == ['Klass', 'func']
11
+
12
+
13
+ def test_get_all_list_no_all():
14
+ # test get_all_list on a module which does not define all.
15
+ # Remove __all__, test, reload on exit to not depend on the test order.
16
+ try:
17
+ del finder_cases.__all__
18
+ items, depr, other = get_all_list(finder_cases)
19
+ assert items == []
20
+ finally:
21
+ from importlib import reload
22
+ reload(finder_cases)
23
+
24
+
25
+ def test_get_objects():
26
+ (items, names), failures = get_public_objects(finder_cases)
27
+ assert items == [finder_cases.func, finder_cases.Klass]
28
+ assert names == [obj.__name__ for obj in items]
29
+ assert failures == []
30
+
31
+
32
+ def test_get_objects_extra_items():
33
+ # test get_all_list on a module which defines an incorrect all.
34
+ # Patch __all__, test, reload on exit to not depend on the test order.
35
+ try:
36
+ finder_cases.__all__ += ['spurious']
37
+ (items, names), failures = get_public_objects(finder_cases)
38
+
39
+ assert items == [finder_cases.func, finder_cases.Klass]
40
+ assert len(failures) == 1
41
+
42
+ failed = failures[0]
43
+ assert failed[0].endswith(".spurious")
44
+ assert failed[2].startswith("Missing item")
45
+
46
+ finally:
47
+ from importlib import reload
48
+ reload(finder_cases)
49
+
50
+
51
+ def test_find_doctests_extra_items():
52
+ # test find_doctests on a module which defines an incorrect all.
53
+ # Patch __all__, test, reload on exit to not depend on the test order.
54
+ try:
55
+ finder_cases.__all__ += ['spurious', 'missing']
56
+ with pytest.raises(ValueError):
57
+ find_doctests(finder_cases, strategy='api')
58
+ finally:
59
+ from importlib import reload
60
+ reload(finder_cases)
61
+
62
+
63
+ class TestSkiplist:
64
+ """Test skipping items via skiplists."""
65
+ def test_get_objects_skiplist(self):
66
+ skips = [finder_cases.__name__ + '.' + 'func']
67
+ (items, name), failures = get_public_objects(finder_cases, skiplist=skips)
68
+
69
+ assert items == [finder_cases.Klass]
70
+ assert failures == []
71
+
72
+ def test_get_doctests_no_skiplist(self):
73
+ # strategy=None is equivalent to vanilla doctest module: finds all objects
74
+ tests = find_doctests(finder_cases)
75
+ names = [t.name for t in tests]
76
+
77
+ wanted_names = ['Klass', 'Klass.meth', 'Klass.meth_2', 'func',
78
+ 'private_func', '_underscored_private_func']
79
+ base = finder_cases.__name__
80
+ wanted_names = [base] + [base + '.' + n for n in wanted_names]
81
+
82
+ assert sorted(names) == sorted(wanted_names)
83
+
84
+ def test_get_doctests_strategy_None(self):
85
+ # Add a skiplist: strategy=None skips listed items
86
+ base = finder_cases.__name__
87
+ skips = [base + '.func', base + '.Klass.meth_2']
88
+ config = DTConfig(skiplist=skips)
89
+
90
+ tests = find_doctests(finder_cases, config=config)
91
+ names = [t.name for t in tests]
92
+
93
+ # note the lack of `func` and `Klass.meth_2`, as requested
94
+ wanted_names = ['Klass', 'Klass.meth',
95
+ 'private_func', '_underscored_private_func']
96
+ wanted_names = [base] + [base + '.' + n for n in wanted_names]
97
+
98
+ assert sorted(names) == sorted(wanted_names)
99
+
100
+ def test_get_doctests_strategy_api(self):
101
+ # Add a skiplist: strategy='api' skips listed items
102
+ base = finder_cases.__name__
103
+ skips = [base + '.func', base + '.Klass.meth_2']
104
+ config = DTConfig(skiplist=skips)
105
+
106
+ tests = find_doctests(finder_cases, strategy='api', config=config)
107
+ names = [t.name for t in tests]
108
+
109
+ # note the lack of
110
+ # - `func` and `Klass.meth_2`, as requested (via the skiplist)
111
+ # - *private* stuff, which is not in `__all__`
112
+ wanted_names = ['Klass', 'Klass.meth']
113
+ wanted_names = [base] + [base + '.' + n for n in wanted_names]
114
+
115
+ assert sorted(names) == sorted(wanted_names)
116
+
117
+ def test_get_doctests_strategy_list(self):
118
+ # Add a skiplist: strategy=<list> skips listed items
119
+ base = finder_cases.__name__
120
+ skips = [base + '.func', base + '.Klass.meth_2']
121
+ config = DTConfig(skiplist=skips)
122
+
123
+ tests = find_doctests(finder_cases,
124
+ strategy=[finder_cases.Klass], config=config)
125
+ names = [t.name for t in tests]
126
+
127
+ # note the lack of
128
+ # - `Klass.meth_2`, as requested (via the skiplist)
129
+ # - the 'base' module (via the strategy=<list>)
130
+ wanted_names = ['Klass', 'Klass.meth']
131
+ wanted_names = [base + '.' + n for n in wanted_names]
132
+
133
+ assert sorted(names) == sorted(wanted_names)
134
+
135
+
136
+ def test_explicit_object_list():
137
+ objs = [finder_cases.Klass]
138
+ tests = find_doctests(finder_cases, strategy=objs)
139
+
140
+ base = 'scipy_doctest.tests.finder_cases'
141
+ assert ([test.name for test in tests] ==
142
+ [base + '.Klass', base + '.Klass.meth', base + '.Klass.meth_2'])
143
+
144
+
145
+ def test_explicit_object_list_with_module():
146
+ # Module docstrings are examined literally, without looking into other objects
147
+ # in the module. These other objects need to be listed explicitly.
148
+ # In the `doctest`-speak: do not recurse.
149
+ objs = [finder_cases, finder_cases.Klass]
150
+ tests = find_doctests(finder_cases, strategy=objs)
151
+
152
+ base = 'scipy_doctest.tests.finder_cases'
153
+ assert ([test.name for test in tests] ==
154
+ [base, base + '.Klass', base + '.Klass.meth', base + '.Klass.meth_2'])
155
+
156
+
157
+ def test_find_doctests_api():
158
+ # Test that the module itself is included with strategy='api'
159
+ tests = find_doctests(finder_cases, strategy='api')
160
+
161
+ base = 'scipy_doctest.tests.finder_cases'
162
+ assert ([test.name for test in tests] ==
163
+ [base + '.func', base + '.Klass', base + '.Klass.meth',
164
+ base + '.Klass.meth_2', base])
165
+
166
+
167
+ def test_dtfinder_config():
168
+ config = DTConfig()
169
+ finder = DTFinder(config=config)
170
+ assert finder.config is config
@@ -0,0 +1,36 @@
1
+ from ..impl import DTConfig, DTParser
2
+
3
+
4
+ def test_parser_default_config():
5
+ # Test that parser adds the _ignore marker for stopwords
6
+ parser = DTParser()
7
+
8
+ string = "Text text \n >>> 1 + plt.xlim([1, 2])\n\n More text"
9
+ examples = parser.get_examples(string)
10
+
11
+ assert len(examples) == 1
12
+ assert examples[0].source == "1 + plt.xlim([1, 2])\n"
13
+ assert examples[0].want == " # _ignore\n"
14
+
15
+
16
+ def test_parser_vanilla_config():
17
+ # Test that no stopwords means no markers
18
+ config = DTConfig()
19
+ config.stopwords = set()
20
+ parser = DTParser(config)
21
+
22
+ string = "Text text \n >>> 1 + plt.xlim([1, 2])\n\n More text"
23
+ examples = parser.get_examples(string)
24
+
25
+ assert len(examples) == 1
26
+ assert examples[0].source == "1 + plt.xlim([1, 2])\n"
27
+ assert examples[0].want == ""
28
+
29
+
30
+ def test_config_nocopy():
31
+ config = DTConfig()
32
+ parser = DTParser(config)
33
+ assert parser.config is config
34
+
35
+
36
+
@@ -0,0 +1,95 @@
1
+ import pytest
2
+
3
+ try:
4
+ import matplotlib.pyplot as plt # noqa
5
+ HAVE_MATPLOTLIB = True
6
+ except Exception:
7
+ HAVE_MATPLOTLIB = False
8
+
9
+ try:
10
+ import scipy # noqa
11
+ HAVE_SCIPY = True
12
+ except Exception:
13
+ HAVE_SCIPY = False
14
+
15
+ from pathlib import Path
16
+
17
+ from . import module_cases, failure_cases, failure_cases_2, stopwords_cases, local_file_cases
18
+
19
+ # XXX: this is a bit hacky and repetetive. Can rework?
20
+
21
+
22
+ pytest_plugins = ['pytester']
23
+
24
+
25
+ @pytest.mark.skipif(not HAVE_SCIPY, reason='need scipy')
26
+ def test_module_cases(pytester):
27
+ """Test that pytest uses the DTChecker for doctests."""
28
+ path_str = module_cases.__file__
29
+ python_file = Path(path_str)
30
+ result = pytester.inline_run(python_file, "--doctest-modules")
31
+ assert result.ret == pytest.ExitCode.OK
32
+
33
+
34
+ def test_failure_cases(pytester):
35
+ file_list = [failure_cases, failure_cases_2]
36
+ for file in file_list:
37
+ path_str = file.__file__
38
+ python_file = Path(path_str)
39
+ result = pytester.inline_run(python_file, "--doctest-modules")
40
+ assert result.ret == pytest.ExitCode.TESTS_FAILED
41
+
42
+
43
+ @pytest.mark.skipif(not HAVE_MATPLOTLIB, reason='need matplotlib')
44
+ def test_stopword_cases(pytester):
45
+ """Test that pytest uses the DTParser for doctests."""
46
+ path_str = stopwords_cases.__file__
47
+ python_file = Path(path_str)
48
+ result = pytester.inline_run(python_file, "--doctest-modules")
49
+ assert result.ret == pytest.ExitCode.OK
50
+
51
+
52
+ @pytest.mark.xfail(reason="XXX: passes locally, fails on CI")
53
+ @pytest.mark.skipif(not HAVE_SCIPY, reason='need scipy')
54
+ def test_local_file_cases(pytester):
55
+ """Test that local files are found for use in doctests.
56
+ """
57
+ path_str = local_file_cases.__file__
58
+ python_file = Path(path_str)
59
+ result = pytester.inline_run(python_file, "--doctest-modules")
60
+ assert result.ret == pytest.ExitCode.OK
61
+
62
+
63
+ def test_alt_checker(pytester):
64
+ """Test an alternative Checker."""
65
+
66
+ # create a temporary conftest.py file
67
+ pytester.makeconftest(
68
+ """
69
+ import doctest
70
+ from scipy_doctest.conftest import dt_config
71
+
72
+ class Vanilla(doctest.OutputChecker):
73
+ def __init__(self, config):
74
+ pass
75
+
76
+ dt_config.CheckerKlass = Vanilla
77
+ """
78
+ )
79
+
80
+ # create a temporary pytest test file
81
+ f = pytester.makepyfile(
82
+ """
83
+ def func():
84
+ '''
85
+ >>> 2 / 3 # fails with vanilla doctest.OutputChecker
86
+ 0.667
87
+ '''
88
+ pass
89
+ """
90
+ )
91
+
92
+ # run all tests with pytest
93
+ result = pytester.inline_run(f, '--doctest-modules')
94
+ assert result.ret == pytest.ExitCode.TESTS_FAILED
95
+
@@ -0,0 +1,105 @@
1
+ import io
2
+
3
+ import doctest
4
+
5
+ import pytest
6
+
7
+ from . import (failure_cases as module,
8
+ finder_cases as finder_module,
9
+ module_cases)
10
+ from .. import DTFinder, DTRunner, DebugDTRunner, DTConfig
11
+
12
+
13
+ ### Smoke test DTRunner methods. Mainly to check that they are runnable.
14
+
15
+ def test_single_failure():
16
+ finder = DTFinder()
17
+ tests = finder.find(module.func9)
18
+ runner = DTRunner(verbose=False)
19
+ stream = io.StringIO()
20
+ for test in tests:
21
+ runner.run(test, out=stream.write)
22
+
23
+ stream.seek(0)
24
+ output = stream.read()
25
+ assert output.startswith('\n func9\n -----\n')
26
+
27
+
28
+ def test_exception():
29
+ finder = DTFinder()
30
+ tests = finder.find(module.func10)
31
+ runner = DTRunner(verbose=False)
32
+ stream = io.StringIO()
33
+ for test in tests:
34
+ runner.run(test, out=stream.write)
35
+
36
+ stream.seek(0)
37
+ output = stream.read()
38
+ assert output.startswith('\n func10\n ------\n')
39
+
40
+
41
+ def test_get_history():
42
+ finder = DTFinder()
43
+ tests = finder.find(finder_module)
44
+ runner = DTRunner(verbose=False)
45
+ for test in tests:
46
+ runner.run(test)
47
+
48
+ dct = runner.get_history()
49
+ assert len(dct) == 7
50
+
51
+
52
+ class TestDebugDTRunner:
53
+ def test_debug_runner_failure(self):
54
+ finder = DTFinder()
55
+ tests = finder.find(module.func9)
56
+ runner = DebugDTRunner(verbose=False)
57
+
58
+ with pytest.raises(doctest.DocTestFailure) as exc:
59
+ for t in tests:
60
+ runner.run(t)
61
+
62
+ # pytest wraps the original exception, unwrap it
63
+ orig_exception = exc.value
64
+
65
+ # DocTestFailure carries the doctest and the run result
66
+ assert orig_exception.test is tests[0]
67
+ assert orig_exception.test.name == 'func9'
68
+ assert orig_exception.got == 'array([1, 2, 3])\n'
69
+ assert orig_exception.example.want == 'array([2, 3, 4])\n'
70
+
71
+ def test_debug_runner_exception(self):
72
+ finder = DTFinder()
73
+ tests = finder.find(module.func10)
74
+ runner = DebugDTRunner(verbose=False)
75
+
76
+ with pytest.raises(doctest.UnexpectedException) as exc:
77
+ for t in tests:
78
+ runner.run(t)
79
+ orig_exception = exc.value
80
+
81
+ # exception carries the original test
82
+ assert orig_exception.test is tests[0]
83
+
84
+
85
+ class VanillaOutputChecker(doctest.OutputChecker):
86
+ """doctest.OutputChecker to drop in for DTChecker.
87
+
88
+ LSP break: OutputChecker does not have __init__,
89
+ here we add it to agree with DTChecker.
90
+ """
91
+ def __init__(self, config):
92
+ pass
93
+
94
+ class TestCheckerDropIn:
95
+ """Test DTChecker and vanilla doctest OutputChecker being drop-in replacements.
96
+ """
97
+ def test_vanilla_checker(self):
98
+ config = DTConfig(CheckerKlass=VanillaOutputChecker)
99
+ runner = DebugDTRunner(config=config)
100
+ tests = DTFinder().find(module_cases.func)
101
+
102
+ with pytest.raises(doctest.DocTestFailure):
103
+ for t in tests:
104
+ runner.run(t)
105
+
@@ -0,0 +1,202 @@
1
+ import doctest
2
+
3
+ try:
4
+ import matplotlib.pyplot as plt # noqa
5
+ HAVE_MATPLOTLIB = True
6
+ except Exception:
7
+ HAVE_MATPLOTLIB = False
8
+
9
+ import pytest
10
+
11
+ from ..impl import DTConfig, DTParser, DebugDTRunner
12
+
13
+
14
+ class TestSyntaxErrors:
15
+ """Syntax errors trigger a doctest failure *unless* marked.
16
+
17
+ Either mark it with +SKIP or as pseudocode.
18
+ """
19
+ @pytest.mark.skipif(not HAVE_MATPLOTLIB, reason='need matplotlib')
20
+ def test_invalid_python(self):
21
+ # This string raises
22
+ # TypeError("unsupported operand type(s) for +: 'int' and 'tuple'")
23
+ string = ">>> import matplotlib.pyplot as plt; 1 + plt.xlim([1, 2])\n"
24
+
25
+ config = DTConfig()
26
+ parser = DTParser(config)
27
+
28
+ test = parser.get_doctest(string,
29
+ globs=config.default_namespace,
30
+ name='none: plain',
31
+ filename='none',
32
+ lineno=0)
33
+ runner = DebugDTRunner()
34
+
35
+ with pytest.raises(doctest.UnexpectedException) as exc_info:
36
+ runner.run(test)
37
+
38
+ orig_error = exc_info.value.exc_info # unwrap pytest -> doctest -> example
39
+ kind, value, traceback = orig_error
40
+
41
+ assert kind is TypeError
42
+ assert "unsupported operand type(s)" in value.args[0]
43
+
44
+ def test_invalid_python_plus_skip(self):
45
+ # Adding a '# doctest: +SKIP' turns it into pseudocode:
46
+ # example NOT CHECKED, at all
47
+ string = ">>> import matplotlib.pyplot as plt; 1 + plt.xlim([1, 2])"
48
+ string += " # doctest: +SKIP"
49
+
50
+ config = DTConfig()
51
+ parser = DTParser(config)
52
+
53
+ test = parser.get_doctest(string,
54
+ globs=config.default_namespace,
55
+ name='none : +SKIP',
56
+ filename='none',
57
+ lineno=0)
58
+ runner = DebugDTRunner()
59
+ runner.run(test)
60
+ assert runner.get_history() == {'none : +SKIP': (0, 0)}
61
+
62
+ def test_invalid_python_pseudocode(self):
63
+ # Marking a test as pseudocode is equivalent to a +SKIP:
64
+ # example NOT CHECKED, at all
65
+ string = ">>> import matplotlib.pyplot as plt; 1 + plt.xlim([1, 2])"
66
+
67
+ config = DTConfig(pseudocode=['plt.xlim'])
68
+ parser = DTParser(config)
69
+
70
+ test = parser.get_doctest(string,
71
+ globs=config.default_namespace,
72
+ name='none : pseudocode',
73
+ filename='none',
74
+ lineno=0)
75
+ runner = DebugDTRunner()
76
+ runner.run(test)
77
+ assert runner.get_history() == {'none : pseudocode': (0, 0)}
78
+
79
+
80
+ class TestPseudocodeMarkers:
81
+ """Marking an example as pseudocode is exactly equivalent to a +SKIP."""
82
+
83
+ def test_pseudocode_markers(self):
84
+ # The first string has a +SKIP, the second one doesn't
85
+ string = ">>> oops #doctest: +SKIP\n>>> ouch\n"
86
+
87
+ parser = doctest.DocTestParser()
88
+ test = parser.get_doctest(string, globs={},
89
+ name='none', filename='none', lineno=0)
90
+
91
+ opts_when_skipped = {doctest.OPTIONFLAGS_BY_NAME['SKIP']: True}
92
+ assert len(test.examples) == 2
93
+ assert test.examples[0].options == opts_when_skipped
94
+ assert test.examples[1].options == {}
95
+
96
+ # Now mark the second example as pseudocode
97
+ config = DTConfig(pseudocode=['ouch'])
98
+ parser = DTParser(config=config)
99
+ test = parser.get_doctest(string, globs={},
100
+ name='none', filename='none', lineno=0)
101
+
102
+ assert len(test.examples) == 2
103
+ assert test.examples[0].options == opts_when_skipped
104
+ assert test.examples[1].options == opts_when_skipped
105
+
106
+
107
+ class TestStopwords:
108
+ """If an example contains a stopword, the example still needs to be a valid
109
+ python code, but the output is not checked.
110
+ """
111
+ @pytest.mark.skipif(not HAVE_MATPLOTLIB, reason='need matplotlib')
112
+ def test_bogus_output(self):
113
+ # 'plt.xlim(' is a stopword by default in the DTParser. This is because
114
+ # it returns a tuple, which we don't want to litter our docstrings with.
115
+ string = ">>> import matplotlib.pyplot as plt; plt.xlim([1, 2])\n"
116
+ string += "bogus, not what plt.xlim(..) returns\n"
117
+
118
+ parser = DTParser()
119
+ test = parser.get_doctest(string, globs={},
120
+ name='stopwords_bogus_output',
121
+ filename='none', lineno=0)
122
+ assert "bogus" in test.examples[0].want
123
+
124
+ runner = DebugDTRunner()
125
+ runner.run(test)
126
+
127
+ # one example tried, of which zero failed
128
+ assert runner.get_history() == {'stopwords_bogus_output': (0, 1)}
129
+
130
+
131
+ class TestMayVary:
132
+ """The '# may vary' markers are applied to the example output, not source.
133
+
134
+ Otherwise they are equivalent to declaring an example to have a stopword:
135
+ the source needs to be valid python code, but the output is not checked.
136
+ """
137
+ def test_may_vary(self):
138
+ string = ">>> 1 + 2\n"
139
+ string += "uhm, not sure # may vary\n"
140
+
141
+ parser = DTParser()
142
+ test = parser.get_doctest(string, globs={},
143
+ name='may_vary_markers',
144
+ filename='none', lineno=0)
145
+ assert "uhm" in test.examples[0].want
146
+
147
+ runner = DebugDTRunner()
148
+ runner.run(test)
149
+
150
+ # one example tried, of which zero failed
151
+ assert runner.get_history() == {'may_vary_markers': (0, 1)}
152
+
153
+ def test_may_vary_source(self):
154
+ # The marker needs to be added to the example output, not source.
155
+ string = ">>> 1 + 2 # may vary\n"
156
+ string += "uhm, can't say\n"
157
+
158
+ parser = DTParser()
159
+ test = parser.get_doctest(string, globs={},
160
+ name='may_vary_source',
161
+ filename='none', lineno=0)
162
+
163
+ runner = DebugDTRunner()
164
+ with pytest.raises(doctest.DocTestFailure):
165
+ runner.run(test)
166
+
167
+
168
+ string='''\
169
+
170
+ This is an example string with doctests and skipblocks. A block is a sequence
171
+ of examples (which start with a >>> marker) without an intervening text, like
172
+ below
173
+
174
+ >>> from some_module import some_function # doctest: +SKIPBLOCK
175
+ >>> some_function(42)
176
+
177
+ Note how the block above will fail doctesting unless the second line is
178
+ skipped. A standard solution is to add a +SKIP marker to every line, but this
179
+ is ugly and we skip the whole block instead.
180
+
181
+ Once the block is over, we get back to usual doctests, which are not skipped
182
+
183
+ >>> 1 + 2
184
+ 3
185
+
186
+ '''
187
+
188
+ def test_SKIPBLOCK():
189
+ parser = DTParser()
190
+ test = parser.get_doctest(string,
191
+ globs={},
192
+ name='SKIPBLOCK test',
193
+ filename='none',
194
+ lineno=0)
195
+
196
+ SKIP = doctest.OPTIONFLAGS_BY_NAME['SKIP']
197
+
198
+ assert len(test.examples) == 3
199
+ assert test.examples[0].options[SKIP] is True
200
+ assert test.examples[1].options[SKIP] is True
201
+ assert test.examples[2].options == {} # not skipped
202
+
@@ -0,0 +1,24 @@
1
+ from ..frontend import testfile as doctestfile
2
+ from ..impl import DTConfig
3
+
4
+ import pytest
5
+
6
+
7
+ @pytest.mark.xfail(True, reason="needs the scipy repo at a fixed location")
8
+ def test_one_scipy_tutorial():
9
+ # FIXME: HACK, will not work if scipy is not installed,
10
+ path = '/home/br/repos/scipy/scipy/doc/source/tutorial/ndimage.rst'
11
+
12
+ config = DTConfig()
13
+ config.stopwords = {}
14
+
15
+ doctestfile(path,
16
+ module_relative=False, verbose=2, raise_on_error=False,
17
+ config=config)
18
+
19
+
20
+ def test_linalg_clone():
21
+ # run on a clone of the scipy linalg tutorial
22
+ path = 'scipy_ndimage_tutorial_clone.rst'
23
+ doctestfile(path, package='scipy_doctest.tests', verbose=2,
24
+ raise_on_error=False)