From 21ef29e53de87b30bf87a03ce1189cd546257fab Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 29 Apr 2021 22:06:45 +0200 Subject: [PATCH 1/3] add DefaultDict for deprecation handling --- control/config.py | 38 +++++++++++++++++++++++++++++++++++- control/freqplot.py | 8 ++++++++ control/tests/config_test.py | 37 +++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/control/config.py b/control/config.py index 99245dd2f..c720065ae 100644 --- a/control/config.py +++ b/control/config.py @@ -20,7 +20,43 @@ 'control.squeeze_time_response': None, 'forced_response.return_x': False, } -defaults = dict(_control_defaults) + + +class DefaultDict(dict): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def __getitem__(self, key): + return super().__getitem__(self._check_deprecation(key)) + + def __setitem__(self, key, value): + super().__setitem__(self._check_deprecation(key), value) + + def __missing__(self, key): + repl = self._check_deprecation(key) + if self.__contains__(repl): + return self[repl] + else: + raise KeyError + + def copy(self): + return DefaultDict(self) + + def get(self, key, default=None): + return super().get(self._check_deprecation(key), default) + + 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}'].", + DeprecationWarning) + 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..1e18504a0 100644 --- a/control/tests/config_test.py +++ b/control/tests/config_test.py @@ -49,6 +49,43 @@ def test_get_param(self): assert ct.config._get_param('config', 'test4', {'test4': 1}, None) == 1 + def test_default_deprecation(self): + ct.config.defaults['config.newkey'] = 1 + 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' + + with pytest.warns(DeprecationWarning, match=msgpattern): + assert ct.config.defaults['config.oldkey'] == 1 + with pytest.warns(DeprecationWarning, match=msgpattern): + ct.config.defaults['config.oldkey'] = 2 + with pytest.warns(DeprecationWarning, 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(DeprecationWarning, match=msgpattern): + assert ct.config._get_param('config', 'oldkey') == 3 + with pytest.warns(DeprecationWarning, match=msgpattern): + ct.config.set_defaults('config', oldkey=4) + with pytest.warns(DeprecationWarning, match=msgpattern): + assert ct.config.defaults['config.oldkey'] == 4 + assert ct.config.defaults['config.newkey'] == 4 + + with pytest.raises(KeyError): + with pytest.warns(DeprecationWarning, 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(DeprecationWarning, + 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() From c37df52ee23dc1eda96e5d272085ab052788fc7e Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Thu, 29 Apr 2021 22:38:38 +0200 Subject: [PATCH 2/3] remove __getitem__, covered by __missing__ --- control/config.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/control/config.py b/control/config.py index c720065ae..cdf723b47 100644 --- a/control/config.py +++ b/control/config.py @@ -26,9 +26,6 @@ class DefaultDict(dict): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - def __getitem__(self, key): - return super().__getitem__(self._check_deprecation(key)) - def __setitem__(self, key, value): super().__setitem__(self._check_deprecation(key), value) From e0dab934ce364fd4d59cac9ae9964f8ad054bee3 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Sat, 1 May 2021 15:15:42 +0200 Subject: [PATCH 3/3] use collections.UserDict for DefaultDict --- control/config.py | 23 ++++++++++++++--------- control/tests/config_test.py | 24 +++++++++++++++--------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/control/config.py b/control/config.py index cdf723b47..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', @@ -22,7 +24,14 @@ } -class DefaultDict(dict): +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) @@ -30,24 +39,20 @@ 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 - - def copy(self): - return DefaultDict(self) - - def get(self, key, default=None): - return super().get(self._check_deprecation(key), default) + 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}'].", - DeprecationWarning) + FutureWarning, stacklevel=3) return repl else: return key diff --git a/control/tests/config_test.py b/control/tests/config_test.py index 1e18504a0..e198254bf 100644 --- a/control/tests/config_test.py +++ b/control/tests/config_test.py @@ -50,38 +50,44 @@ def test_get_param(self): assert ct.config._get_param('config', 'test4', {'test4': 1}, None) == 1 def test_default_deprecation(self): - ct.config.defaults['config.newkey'] = 1 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' - with pytest.warns(DeprecationWarning, match=msgpattern): + ct.config.defaults['config.newkey'] = 1 + with pytest.warns(FutureWarning, match=msgpattern): assert ct.config.defaults['config.oldkey'] == 1 - with pytest.warns(DeprecationWarning, match=msgpattern): + with pytest.warns(FutureWarning, match=msgpattern): ct.config.defaults['config.oldkey'] = 2 - with pytest.warns(DeprecationWarning, match=msgpattern): + 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(DeprecationWarning, match=msgpattern): + with pytest.warns(FutureWarning, match=msgpattern): assert ct.config._get_param('config', 'oldkey') == 3 - with pytest.warns(DeprecationWarning, match=msgpattern): + with pytest.warns(FutureWarning, match=msgpattern): ct.config.set_defaults('config', oldkey=4) - with pytest.warns(DeprecationWarning, match=msgpattern): + 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(DeprecationWarning, match=msgpattern): + 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(DeprecationWarning, + with pytest.warns(FutureWarning, match='bode.* has been renamed to.*freqplot'): assert ct.config.defaults['bode.Hz'] \ == ct.config.defaults['freqplot.Hz'] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy