Skip to content

Commit 9f27293

Browse files
authored
Merge pull request #619 from bnavigator/defaults-deprecation
add DefaultDict for deprecation handling
2 parents db174b7 + e0dab93 commit 9f27293

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

control/config.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
# files. For now, you can just choose between MATLAB and FBS default
88
# values + tweak a few other things.
99

10+
11+
import collections
1012
import warnings
1113

1214
__all__ = ['defaults', 'set_defaults', 'reset_defaults',
@@ -20,7 +22,43 @@
2022
'control.squeeze_time_response': None,
2123
'forced_response.return_x': False,
2224
}
23-
defaults = dict(_control_defaults)
25+
26+
27+
class DefaultDict(collections.UserDict):
28+
"""Map names for settings from older version to their renamed ones.
29+
30+
If a user wants to write to an old setting, issue a warning and write to
31+
the renamed setting instead. Accessing the old setting returns the value
32+
from the new name.
33+
"""
34+
35+
def __init__(self, *args, **kwargs):
36+
super().__init__(*args, **kwargs)
37+
38+
def __setitem__(self, key, value):
39+
super().__setitem__(self._check_deprecation(key), value)
40+
41+
def __missing__(self, key):
42+
# An old key should never have been set. If it is being accessed
43+
# through __getitem__, return the value from the new name.
44+
repl = self._check_deprecation(key)
45+
if self.__contains__(repl):
46+
return self[repl]
47+
else:
48+
raise KeyError(key)
49+
50+
def _check_deprecation(self, key):
51+
if self.__contains__(f"deprecated.{key}"):
52+
repl = self[f"deprecated.{key}"]
53+
warnings.warn(f"config.defaults['{key}'] has been renamed to "
54+
f"config.defaults['{repl}'].",
55+
FutureWarning, stacklevel=3)
56+
return repl
57+
else:
58+
return key
59+
60+
61+
defaults = DefaultDict(_control_defaults)
2462

2563

2664
def set_defaults(module, **keywords):

control/freqplot.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,16 @@
6868
'freqplot.Hz': False, # Plot frequency in Hertz
6969
'freqplot.grid': True, # Turn on grid for gain and phase
7070
'freqplot.wrap_phase': False, # Wrap the phase plot at a given value
71+
72+
# deprecations
73+
'deprecated.bode.dB': 'freqplot.dB',
74+
'deprecated.bode.deg': 'freqplot.deg',
75+
'deprecated.bode.Hz': 'freqplot.Hz',
76+
'deprecated.bode.grid': 'freqplot.grid',
77+
'deprecated.bode.wrap_phase': 'freqplot.wrap_phase',
7178
}
7279

80+
7381
#
7482
# Main plotting functions
7583
#

control/tests/config_test.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,49 @@ def test_get_param(self):
4949

5050
assert ct.config._get_param('config', 'test4', {'test4': 1}, None) == 1
5151

52+
def test_default_deprecation(self):
53+
ct.config.defaults['deprecated.config.oldkey'] = 'config.newkey'
54+
ct.config.defaults['deprecated.config.oldmiss'] = 'config.newmiss'
55+
56+
msgpattern = r'config\.oldkey.* has been renamed to .*config\.newkey'
57+
58+
ct.config.defaults['config.newkey'] = 1
59+
with pytest.warns(FutureWarning, match=msgpattern):
60+
assert ct.config.defaults['config.oldkey'] == 1
61+
with pytest.warns(FutureWarning, match=msgpattern):
62+
ct.config.defaults['config.oldkey'] = 2
63+
with pytest.warns(FutureWarning, match=msgpattern):
64+
assert ct.config.defaults['config.oldkey'] == 2
65+
assert ct.config.defaults['config.newkey'] == 2
66+
67+
ct.config.set_defaults('config', newkey=3)
68+
with pytest.warns(FutureWarning, match=msgpattern):
69+
assert ct.config._get_param('config', 'oldkey') == 3
70+
with pytest.warns(FutureWarning, match=msgpattern):
71+
ct.config.set_defaults('config', oldkey=4)
72+
with pytest.warns(FutureWarning, match=msgpattern):
73+
assert ct.config.defaults['config.oldkey'] == 4
74+
assert ct.config.defaults['config.newkey'] == 4
75+
76+
ct.config.defaults.update({'config.newkey': 5})
77+
with pytest.warns(FutureWarning, match=msgpattern):
78+
ct.config.defaults.update({'config.oldkey': 6})
79+
with pytest.warns(FutureWarning, match=msgpattern):
80+
assert ct.config.defaults.get('config.oldkey') == 6
81+
82+
with pytest.raises(KeyError):
83+
with pytest.warns(FutureWarning, match=msgpattern):
84+
ct.config.defaults['config.oldmiss']
85+
with pytest.raises(KeyError):
86+
ct.config.defaults['config.neverdefined']
87+
88+
# assert that reset defaults keeps the custom type
89+
ct.config.reset_defaults()
90+
with pytest.warns(FutureWarning,
91+
match='bode.* has been renamed to.*freqplot'):
92+
assert ct.config.defaults['bode.Hz'] \
93+
== ct.config.defaults['freqplot.Hz']
94+
5295
@mplcleanup
5396
def test_fbs_bode(self):
5497
ct.use_fbs_defaults()

0 commit comments

Comments
 (0)