-
Notifications
You must be signed in to change notification settings - Fork 458
Expand file tree
/
Copy pathkwargs_test.py
More file actions
206 lines (178 loc) · 8.08 KB
/
Copy pathkwargs_test.py
File metadata and controls
206 lines (178 loc) · 8.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# kwargs_test.py - test for uncrecognized keywords
# RMM, 20 Mar 2022
#
# Allowing unrecognized keywords to be passed to a function without
# generating and error message can generate annoying bugs, since you
# sometimes think you are telling the function to do something and actually
# you have a misspelling or other error and your input is being ignored.
#
# This unit test looks through all functions in the package for any that
# allow kwargs as part of the function signature and makes sure that there
# is a unit test that checks for unrecognized keywords.
import inspect
import pytest
import warnings
import matplotlib.pyplot as plt
import control
import control.flatsys
# List of all of the test modules where kwarg unit tests are defined
import control.tests.flatsys_test as flatsys_test
import control.tests.frd_test as frd_test
import control.tests.interconnect_test as interconnect_test
import control.tests.statefbk_test as statefbk_test
import control.tests.trdata_test as trdata_test
@pytest.mark.parametrize("module, prefix", [
(control, ""), (control.flatsys, "flatsys.")
])
def test_kwarg_search(module, prefix):
# Look through every object in the package
for name, obj in inspect.getmembers(module):
# Skip anything that is outside of this module
if inspect.getmodule(obj) is not None and \
not inspect.getmodule(obj).__name__.startswith('control'):
# Skip anything that isn't part of the control package
continue
# Only look for functions with keyword arguments
if not inspect.isfunction(obj):
continue
# Get the signature for the function
sig = inspect.signature(obj)
# Skip anything that is inherited
if inspect.isclass(module) and obj.__name__ not in module.__dict__:
continue
# See if there is a variable keyword argument
for argname, par in sig.parameters.items():
if not par.kind == inspect.Parameter.VAR_KEYWORD:
continue
# Make sure there is a unit test defined
assert prefix + name in kwarg_unittest
# Make sure there is a unit test
if not hasattr(kwarg_unittest[prefix + name], '__call__'):
warnings.warn("No unit test defined for '%s'" % prefix + name)
source = None
else:
source = inspect.getsource(kwarg_unittest[prefix + name])
# Make sure the unit test looks for unrecognized keyword
if source and source.find('unrecognized keyword') < 0:
warnings.warn(
f"'unrecognized keyword' not found in unit test "
f"for {name}")
# Look for classes and then check member functions
if inspect.isclass(obj):
test_kwarg_search(obj, prefix + obj.__name__ + '.')
@pytest.mark.usefixtures('editsdefaults')
def test_unrecognized_kwargs():
# Create a SISO system for use in parameterized tests
sys = control.ss([[-1, 1], [0, -1]], [[0], [1]], [[1, 0]], 0, dt=None)
table = [
[control.dlqe, (sys, [[1]], [[1]]), {}],
[control.dlqr, (sys, [[1, 0], [0, 1]], [[1]]), {}],
[control.drss, (2, 1, 1), {}],
[control.input_output_response, (sys, [0, 1, 2], [1, 1, 1]), {}],
[control.lqe, (sys, [[1]], [[1]]), {}],
[control.lqr, (sys, [[1, 0], [0, 1]], [[1]]), {}],
[control.linearize, (sys, 0, 0), {}],
[control.pzmap, (sys,), {}],
[control.rlocus, (control.tf([1], [1, 1]), ), {}],
[control.root_locus, (control.tf([1], [1, 1]), ), {}],
[control.rss, (2, 1, 1), {}],
[control.set_defaults, ('control',), {'default_dt': True}],
[control.ss, (0, 0, 0, 0), {'dt': 1}],
[control.ss2io, (sys,), {}],
[control.ss2tf, (sys,), {}],
[control.summing_junction, (2,), {}],
[control.tf, ([1], [1, 1]), {}],
[control.tf2io, (control.tf([1], [1, 1]),), {}],
[control.tf2ss, (control.tf([1], [1, 1]),), {}],
[control.InputOutputSystem, (),
{'inputs': 1, 'outputs': 1, 'states': 1}],
[control.InputOutputSystem.linearize, (sys, 0, 0), {}],
[control.StateSpace, ([[-1, 0], [0, -1]], [[1], [1]], [[1, 1]], 0), {}],
[control.TransferFunction, ([1], [1, 1]), {}],
]
for function, args, kwargs in table:
# Call the function normally and make sure it works
function(*args, **kwargs)
# Now add an unrecognized keyword and make sure there is an error
with pytest.raises(TypeError, match="unrecognized keyword"):
function(*args, **kwargs, unknown=None)
# If we opened any figures, close them to avoid matplotlib warnings
if plt.gca():
plt.close('all')
def test_matplotlib_kwargs():
# Create a SISO system for use in parameterized tests
sys = control.ss([[-1, 1], [0, -1]], [[0], [1]], [[1, 0]], 0, dt=None)
ctl = control.ss([[-1, 1], [0, -1]], [[0], [1]], [[1, 0]], 0, dt=None)
table = [
[control.bode, (sys, ), {}],
[control.bode_plot, (sys, ), {}],
[control.describing_function_plot,
(sys, control.descfcn.saturation_nonlinearity(1), [1, 2, 3, 4]), {}],
[control.gangof4, (sys, ctl), {}],
[control.gangof4_plot, (sys, ctl), {}],
[control.nyquist, (sys, ), {}],
[control.nyquist_plot, (sys, ), {}],
[control.singular_values_plot, (sys, ), {}],
]
for function, args, kwargs in table:
# Call the function normally and make sure it works
function(*args, **kwargs)
# Now add an unrecognized keyword and make sure there is an error
with pytest.raises(AttributeError, match="has no property"):
function(*args, **kwargs, unknown=None)
# If we opened any figures, close them to avoid matplotlib warnings
if plt.gca():
plt.close('all')
#
# List of all unit tests that check for unrecognized keywords
#
# Every function that accepts variable keyword arguments (**kwargs) should
# have an entry in this table, to make sure that nothing is missing. This
# will also force people who add new functions to put in an appropriate unit
# test.
#
kwarg_unittest = {
'bode': test_matplotlib_kwargs,
'bode_plot': test_matplotlib_kwargs,
'describing_function_plot': test_matplotlib_kwargs,
'dlqe': test_unrecognized_kwargs,
'dlqr': test_unrecognized_kwargs,
'drss': test_unrecognized_kwargs,
'gangof4': test_matplotlib_kwargs,
'gangof4_plot': test_matplotlib_kwargs,
'input_output_response': test_unrecognized_kwargs,
'interconnect': interconnect_test.test_interconnect_exceptions,
'linearize': test_unrecognized_kwargs,
'lqe': test_unrecognized_kwargs,
'lqr': test_unrecognized_kwargs,
'nyquist': test_matplotlib_kwargs,
'nyquist_plot': test_matplotlib_kwargs,
'pzmap': test_unrecognized_kwargs,
'rlocus': test_unrecognized_kwargs,
'root_locus': test_unrecognized_kwargs,
'rss': test_unrecognized_kwargs,
'set_defaults': test_unrecognized_kwargs,
'singular_values_plot': test_matplotlib_kwargs,
'ss': test_unrecognized_kwargs,
'ss2io': test_unrecognized_kwargs,
'ss2tf': test_unrecognized_kwargs,
'summing_junction': interconnect_test.test_interconnect_exceptions,
'tf': test_unrecognized_kwargs,
'tf2io' : test_unrecognized_kwargs,
'tf2ss' : test_unrecognized_kwargs,
'flatsys.point_to_point':
flatsys_test.TestFlatSys.test_point_to_point_errors,
'FrequencyResponseData.__init__':
frd_test.TestFRD.test_unrecognized_keyword,
'InputOutputSystem.__init__': test_unrecognized_kwargs,
'InputOutputSystem.linearize': test_unrecognized_kwargs,
'InterconnectedSystem.__init__':
interconnect_test.test_interconnect_exceptions,
'LinearIOSystem.__init__':
interconnect_test.test_interconnect_exceptions,
'NonlinearIOSystem.__init__':
interconnect_test.test_interconnect_exceptions,
'StateSpace.__init__': test_unrecognized_kwargs,
'TimeResponseData.__call__': trdata_test.test_response_copy,
'TransferFunction.__init__': test_unrecognized_kwargs,
}