diff --git a/control/config.py b/control/config.py index 99245dd2f..afd7615ca 100644 --- a/control/config.py +++ b/control/config.py @@ -7,6 +7,8 @@ # files. For now, you can just choose between MATLAB and FBS default # values + tweak a few other things. + +import collections import warnings __all__ = ['defaults', 'set_defaults', 'reset_defaults', @@ -20,7 +22,43 @@ 'control.squeeze_time_response': None, 'forced_response.return_x': False, } -defaults = dict(_control_defaults) + + +class DefaultDict(collections.UserDict): + """Map names for settings from older version to their renamed ones. + + If a user wants to write to an old setting, issue a warning and write to + the renamed setting instead. Accessing the old setting returns the value + from the new name. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def __setitem__(self, key, value): + super().__setitem__(self._check_deprecation(key), value) + + def __missing__(self, key): + # An old key should never have been set. If it is being accessed + # through __getitem__, return the value from the new name. + repl = self._check_deprecation(key) + if self.__contains__(repl): + return self[repl] + else: + raise KeyError(key) + + def _check_deprecation(self, key): + if self.__contains__(f"deprecated.{key}"): + repl = self[f"deprecated.{key}"] + warnings.warn(f"config.defaults['{key}'] has been renamed to " + f"config.defaults['{repl}'].", + FutureWarning, stacklevel=3) + return repl + else: + return key + + +defaults = DefaultDict(_control_defaults) def set_defaults(module, **keywords): diff --git a/control/freqplot.py b/control/freqplot.py index f6e995bee..8dbf998d3 100644 --- a/control/freqplot.py +++ b/control/freqplot.py @@ -68,8 +68,16 @@ 'freqplot.Hz': False, # Plot frequency in Hertz 'freqplot.grid': True, # Turn on grid for gain and phase 'freqplot.wrap_phase': False, # Wrap the phase plot at a given value + + # deprecations + 'deprecated.bode.dB': 'freqplot.dB', + 'deprecated.bode.deg': 'freqplot.deg', + 'deprecated.bode.Hz': 'freqplot.Hz', + 'deprecated.bode.grid': 'freqplot.grid', + 'deprecated.bode.wrap_phase': 'freqplot.wrap_phase', } + # # Main plotting functions # diff --git a/control/tests/config_test.py b/control/tests/config_test.py index 45fd8de22..e198254bf 100644 --- a/control/tests/config_test.py +++ b/control/tests/config_test.py @@ -49,6 +49,49 @@ def test_get_param(self): assert ct.config._get_param('config', 'test4', {'test4': 1}, None) == 1 + def test_default_deprecation(self): + ct.config.defaults['deprecated.config.oldkey'] = 'config.newkey' + ct.config.defaults['deprecated.config.oldmiss'] = 'config.newmiss' + + msgpattern = r'config\.oldkey.* has been renamed to .*config\.newkey' + + ct.config.defaults['config.newkey'] = 1 + with pytest.warns(FutureWarning, match=msgpattern): + assert ct.config.defaults['config.oldkey'] == 1 + with pytest.warns(FutureWarning, match=msgpattern): + ct.config.defaults['config.oldkey'] = 2 + with pytest.warns(FutureWarning, match=msgpattern): + assert ct.config.defaults['config.oldkey'] == 2 + assert ct.config.defaults['config.newkey'] == 2 + + ct.config.set_defaults('config', newkey=3) + with pytest.warns(FutureWarning, match=msgpattern): + assert ct.config._get_param('config', 'oldkey') == 3 + with pytest.warns(FutureWarning, match=msgpattern): + ct.config.set_defaults('config', oldkey=4) + with pytest.warns(FutureWarning, match=msgpattern): + assert ct.config.defaults['config.oldkey'] == 4 + assert ct.config.defaults['config.newkey'] == 4 + + ct.config.defaults.update({'config.newkey': 5}) + with pytest.warns(FutureWarning, match=msgpattern): + ct.config.defaults.update({'config.oldkey': 6}) + with pytest.warns(FutureWarning, match=msgpattern): + assert ct.config.defaults.get('config.oldkey') == 6 + + with pytest.raises(KeyError): + with pytest.warns(FutureWarning, match=msgpattern): + ct.config.defaults['config.oldmiss'] + with pytest.raises(KeyError): + ct.config.defaults['config.neverdefined'] + + # assert that reset defaults keeps the custom type + ct.config.reset_defaults() + with pytest.warns(FutureWarning, + match='bode.* has been renamed to.*freqplot'): + assert ct.config.defaults['bode.Hz'] \ + == ct.config.defaults['freqplot.Hz'] + @mplcleanup def test_fbs_bode(self): ct.use_fbs_defaults()
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: