diff --git a/control/tests/conftest.py b/control/tests/conftest.py index b67ef3674..ac35748f3 100644 --- a/control/tests/conftest.py +++ b/control/tests/conftest.py @@ -1,14 +1,13 @@ """conftest.py - pytest local plugins and fixtures""" -from contextlib import contextmanager -from distutils.version import StrictVersion import os import sys +from contextlib import contextmanager import matplotlib as mpl import numpy as np -import scipy as sp import pytest +import scipy as sp import control @@ -18,10 +17,6 @@ # pytest.param(marks=) slycotonly = pytest.mark.skipif(not control.exception.slycot_check(), reason="slycot not installed") -noscipy0 = pytest.mark.skipif(StrictVersion(sp.__version__) < "1.0", - reason="requires SciPy 1.0 or greater") -nopython2 = pytest.mark.skipif(sys.version_info < (3, 0), - reason="requires Python 3+") matrixfilter = pytest.mark.filterwarnings("ignore:.*matrix subclass:" "PendingDeprecationWarning") matrixerrorfilter = pytest.mark.filterwarnings("error:.*matrix subclass:" @@ -43,6 +38,7 @@ def control_defaults(): # assert that nothing changed it without reverting assert control.config.defaults == the_defaults + @pytest.fixture(scope="function", autouse=TEST_MATRIX_AND_ARRAY, params=[pytest.param("arrayout", marks=matrixerrorfilter), pytest.param("matrixout", marks=matrixfilter)]) @@ -110,10 +106,10 @@ def editsdefaults(): @pytest.fixture(scope="function") def mplcleanup(): - """Workaround for python2 + """Clean up any plots and changes a test may have made to matplotlib. - python 2 does not like to mix the original mpl decorator with pytest - fixtures. So we roll our own. + compare matplotlib.testing.decorators.cleanup() but as a fixture instead + of a decorator. """ save = mpl.units.registry.copy() try: diff --git a/control/tests/flatsys_test.py b/control/tests/flatsys_test.py index a12852759..e3584d459 100644 --- a/control/tests/flatsys_test.py +++ b/control/tests/flatsys_test.py @@ -8,8 +8,6 @@ created for that purpose. """ -from distutils.version import StrictVersion - import numpy as np import pytest import scipy as sp @@ -118,11 +116,10 @@ def test_kinematic_car(self, vehicle_flat, poly): resp = ct.input_output_response(vehicle_flat, T, ud, x0) np.testing.assert_array_almost_equal(resp.states, xd, decimal=2) - # For SciPy 1.0+, integrate equations and compare to desired - if StrictVersion(sp.__version__) >= "1.0": - t, y, x = ct.input_output_response( - vehicle_flat, T, ud, x0, return_x=True) - np.testing.assert_allclose(x, xd, atol=0.01, rtol=0.01) + # integrate equations and compare to desired + t, y, x = ct.input_output_response( + vehicle_flat, T, ud, x0, return_x=True) + np.testing.assert_allclose(x, xd, atol=0.01, rtol=0.01) def test_flat_default_output(self, vehicle_flat): # Construct a flat system with the default outputs diff --git a/control/tests/iosys_test.py b/control/tests/iosys_test.py index 825cec5c5..693be979e 100644 --- a/control/tests/iosys_test.py +++ b/control/tests/iosys_test.py @@ -9,13 +9,15 @@ """ import re +import warnings import numpy as np import pytest import control as ct from control import iosys as ios -from control.tests.conftest import noscipy0, matrixfilter +from control.tests.conftest import matrixfilter + class TestIOSys: @@ -46,7 +48,6 @@ class TSys: return T - @noscipy0 def test_linear_iosys(self, tsys): # Create an input/output system from the linear system linsys = tsys.siso_linsys @@ -65,7 +66,6 @@ def test_linear_iosys(self, tsys): np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_allclose(lti_y, ios_y, atol=0.002, rtol=0.) - @noscipy0 def test_tf2io(self, tsys): # Create a transfer function from the state space system linsys = tsys.siso_linsys @@ -129,7 +129,6 @@ def test_iosys_print(self, tsys, capsys): ios_linearized = ios.linearize(ios_unspecified, [0, 0], [0]) print(ios_linearized) - @noscipy0 @pytest.mark.parametrize("ss", [ios.NonlinearIOSystem, ct.ss]) def test_nonlinear_iosys(self, tsys, ss): # Create a simple nonlinear I/O system @@ -236,7 +235,6 @@ def test_linearize_named_signals(self, kincar): assert lin_nocopy.find_output('x') is None assert lin_nocopy.find_state('x') is None - @noscipy0 def test_connect(self, tsys): # Define a couple of (linear) systems to interconnection linsys1 = tsys.siso_linsys @@ -294,7 +292,6 @@ def test_connect(self, tsys): np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_allclose(lti_y, ios_y,atol=0.002,rtol=0.) - @noscipy0 @pytest.mark.parametrize( "connections, inplist, outlist", [pytest.param([[(1, 0), (0, 0, 1)]], [[(0, 0, 1)]], [[(1, 0, 1)]], @@ -338,7 +335,6 @@ def test_connect_spec_variants(self, tsys, connections, inplist, outlist): np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_allclose(lti_y, ios_y, atol=0.002, rtol=0.) - @noscipy0 @pytest.mark.parametrize( "connections, inplist, outlist", [pytest.param([['sys2.u[0]', 'sys1.y[0]']], @@ -375,7 +371,6 @@ def test_connect_spec_warnings(self, tsys, connections, inplist, outlist): np.testing.assert_array_almost_equal(lti_t, ios_t) np.testing.assert_allclose(lti_y, ios_y, atol=0.002, rtol=0.) - @noscipy0 def test_static_nonlinearity(self, tsys): # Linear dynamical system linsys = tsys.siso_linsys @@ -400,7 +395,6 @@ def test_static_nonlinearity(self, tsys): np.testing.assert_array_almost_equal(lti_y, ios_y, decimal=2) - @noscipy0 @pytest.mark.filterwarnings("ignore:Duplicate name::control.iosys") def test_algebraic_loop(self, tsys): # Create some linear and nonlinear systems to play with @@ -470,7 +464,6 @@ def test_algebraic_loop(self, tsys): with pytest.raises(RuntimeError): ios.input_output_response(*args) - @noscipy0 def test_summer(self, tsys): # Construct a MIMO system for testing linsys = tsys.mimo_linsys1 @@ -489,7 +482,6 @@ def test_summer(self, tsys): ios_t, ios_y = ios.input_output_response(iosys_parallel, T, U, X0) np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) - @noscipy0 def test_rmul(self, tsys): # Test right multiplication # TODO: replace with better tests when conversions are implemented @@ -510,7 +502,6 @@ def test_rmul(self, tsys): lti_t, lti_y = ct.forced_response(ioslin, T, U*U, X0) np.testing.assert_array_almost_equal(ios_y, lti_y*lti_y, decimal=3) - @noscipy0 def test_neg(self, tsys): """Test negation of a system""" @@ -533,7 +524,6 @@ def test_neg(self, tsys): lti_t, lti_y = ct.forced_response(ioslin, T, U*U, X0) np.testing.assert_array_almost_equal(ios_y, -lti_y, decimal=3) - @noscipy0 def test_feedback(self, tsys): # Set up parameters for simulation T, U, X0 = tsys.T, tsys.U, tsys.X0 @@ -549,7 +539,6 @@ def test_feedback(self, tsys): lti_t, lti_y = ct.forced_response(linsys, T, U, X0) np.testing.assert_allclose(ios_y, lti_y,atol=0.002,rtol=0.) - @noscipy0 def test_bdalg_functions(self, tsys): """Test block diagram functions algebra on I/O systems""" # Set up parameters for simulation @@ -596,7 +585,6 @@ def test_bdalg_functions(self, tsys): ios_t, ios_y = ios.input_output_response(iosys_feedback, T, U, X0) np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) - @noscipy0 def test_algebraic_functions(self, tsys): """Test algebraic operations on I/O systems""" # Set up parameters for simulation @@ -648,7 +636,6 @@ def test_algebraic_functions(self, tsys): ios_t, ios_y = ios.input_output_response(iosys_negate, T, U, X0) np.testing.assert_allclose(ios_y, lin_y,atol=0.002,rtol=0.) - @noscipy0 def test_nonsquare_bdalg(self, tsys): # Set up parameters for simulation T = tsys.T @@ -699,7 +686,6 @@ def test_nonsquare_bdalg(self, tsys): with pytest.raises(ValueError): ct.series(*args) - @noscipy0 def test_discrete(self, tsys): """Test discrete time functionality""" # Create some linear and nonlinear systems to play with @@ -868,7 +854,6 @@ def test_find_eqpts(self, tsys): assert xeq is None assert ueq is None - @noscipy0 def test_params(self, tsys): # Start with the default set of parameters ios_secord_default = ios.NonlinearIOSystem( @@ -1433,11 +1418,10 @@ def test_duplicates(self, tsys): nlios2 = ios.NonlinearIOSystem(None, lambda t, x, u, params: u * u, inputs=1, outputs=1, name="nlios2") - with pytest.warns(None) as record: + with warnings.catch_warnings(): + warnings.simplefilter("error") ct.InterconnectedSystem([nlios1, iosys_siso, nlios2], inputs=0, outputs=0, states=0) - if record: - pytest.fail("Warning not expected: " + record[0].message) def test_linear_interconnection(): @@ -1527,7 +1511,7 @@ def predprey(t, x, u, params={}): def pvtol(t, x, u, params={}): """Reduced planar vertical takeoff and landing dynamics""" - from math import sin, cos + from math import cos, sin m = params.get('m', 4.) # kg, system mass J = params.get('J', 0.0475) # kg m^2, system inertia r = params.get('r', 0.25) # m, thrust offset @@ -1543,7 +1527,7 @@ def pvtol(t, x, u, params={}): def pvtol_full(t, x, u, params={}): - from math import sin, cos + from math import cos, sin m = params.get('m', 4.) # kg, system mass J = params.get('J', 0.0475) # kg m^2, system inertia r = params.get('r', 0.25) # m, thrust offset @@ -1596,8 +1580,12 @@ def test_interconnect_unused_input(): inputs=['r'], outputs=['y']) - with pytest.warns(None) as record: + with warnings.catch_warnings(): # no warning if output explicitly ignored, various argument forms + warnings.simplefilter("error") + # strip out matrix warnings + warnings.filterwarnings("ignore", "the matrix subclass", + category=PendingDeprecationWarning) h = ct.interconnect([g,s,k], inputs=['r'], outputs=['y'], @@ -1612,14 +1600,6 @@ def test_interconnect_unused_input(): h = ct.interconnect([g,s,k], connections=False) - #https://docs.pytest.org/en/6.2.x/warnings.html#recwarn - for r in record: - # strip out matrix warnings - if re.match(r'.*matrix subclass', str(r.message)): - continue - print(r.message) - pytest.fail(f'Unexpected warning: {r.message}') - # warn if explicity ignored input in fact used with pytest.warns( @@ -1674,7 +1654,11 @@ def test_interconnect_unused_output(): # no warning if output explicitly ignored - with pytest.warns(None) as record: + with warnings.catch_warnings(): + warnings.simplefilter("error") + # strip out matrix warnings + warnings.filterwarnings("ignore", "the matrix subclass", + category=PendingDeprecationWarning) h = ct.interconnect([g,s,k], inputs=['r'], outputs=['y'], @@ -1689,14 +1673,6 @@ def test_interconnect_unused_output(): h = ct.interconnect([g,s,k], connections=False) - #https://docs.pytest.org/en/6.2.x/warnings.html#recwarn - for r in record: - # strip out matrix warnings - if re.match(r'.*matrix subclass', str(r.message)): - continue - print(r.message) - pytest.fail(f'Unexpected warning: {r.message}') - # warn if explicity ignored output in fact used with pytest.warns( UserWarning, diff --git a/control/tests/namedio_test.py b/control/tests/namedio_test.py index 3a96203a8..4925e9790 100644 --- a/control/tests/namedio_test.py +++ b/control/tests/namedio_test.py @@ -10,6 +10,7 @@ import re from copy import copy +import warnings import numpy as np import control as ct @@ -243,9 +244,12 @@ def test_duplicate_sysname(): sys = ct.rss(4, 1, 1) # No warnings should be generated if we reuse an an unnamed system - with pytest.warns(None) as record: + with warnings.catch_warnings(): + warnings.simplefilter("error") + # strip out matrix warnings + warnings.filterwarnings("ignore", "the matrix subclass", + category=PendingDeprecationWarning) res = sys * sys - assert not any([type(msg) == UserWarning for msg in record]) # Generate a warning if the system is named sys = ct.rss(4, 1, 1, name='sys') diff --git a/control/tests/nyquist_test.py b/control/tests/nyquist_test.py index b1aa00577..ddc69e7bb 100644 --- a/control/tests/nyquist_test.py +++ b/control/tests/nyquist_test.py @@ -8,6 +8,8 @@ """ +import warnings + import pytest import numpy as np import matplotlib.pyplot as plt @@ -225,10 +227,11 @@ def test_nyquist_encirclements(): sys = 1 / (s**2 + s + 1) with pytest.warns(UserWarning, match="encirclements was a non-integer"): count = ct.nyquist_plot(sys, omega_limits=[0.5, 1e3]) - with pytest.warns(None) as records: + with warnings.catch_warnings(): + warnings.simplefilter("error") + # strip out matrix warnings count = ct.nyquist_plot( sys, omega_limits=[0.5, 1e3], encirclement_threshold=0.2) - assert len(records) == 0 plt.title("Non-integer number of encirclements [%g]" % count) diff --git a/control/tests/optimal_test.py b/control/tests/optimal_test.py index 1aa307b60..027b55c75 100644 --- a/control/tests/optimal_test.py +++ b/control/tests/optimal_test.py @@ -366,7 +366,7 @@ def test_optimal_logging(capsys): x0 = [-1, 1] # Solve it, with logging turned on (with warning due to mixed constraints) - with pytest.warns(sp.optimize.optimize.OptimizeWarning, + with pytest.warns(sp.optimize.OptimizeWarning, match="Equality and inequality .* same element"): res = opt.solve_ocp( sys, time, x0, cost, input_constraint, terminal_cost=cost, diff --git a/control/tests/timeresp_test.py b/control/tests/timeresp_test.py index fe73ab4a9..ff712f849 100644 --- a/control/tests/timeresp_test.py +++ b/control/tests/timeresp_test.py @@ -1,7 +1,6 @@ """timeresp_test.py - test time response functions""" from copy import copy -from distutils.version import StrictVersion import numpy as np import pytest @@ -521,8 +520,6 @@ def test_impulse_response_mimo(self, tsystem): _t, yy = impulse_response(sys, T=t, input=0) np.testing.assert_array_almost_equal(yy[:,0,:], yref_notrim, decimal=4) - @pytest.mark.skipif(StrictVersion(sp.__version__) < "1.3", - reason="requires SciPy 1.3 or greater") @pytest.mark.parametrize("tsystem", ["siso_tf1"], indirect=True) def test_discrete_time_impulse(self, tsystem): # discrete time impulse sampled version should match cont time @@ -998,9 +995,6 @@ def test_time_series_data_convention_2D(self, tsystem): [6, 2, 2, False, (2, 2, 8), (2, 8)], ]) def test_squeeze(self, fcn, nstate, nout, ninp, squeeze, shape1, shape2): - # Figure out if we have SciPy 1+ - scipy0 = StrictVersion(sp.__version__) < '1.0' - # Define the system if fcn == ct.tf and (nout > 1 or ninp > 1) and not slycot_check(): pytest.skip("Conversion of MIMO systems to transfer functions " @@ -1077,7 +1071,7 @@ def test_squeeze(self, fcn, nstate, nout, ninp, squeeze, shape1, shape2): assert yvec.shape == (8, ) # For InputOutputSystems, also test input/output response - if isinstance(sys, ct.InputOutputSystem) and not scipy0: + if isinstance(sys, ct.InputOutputSystem): _, yvec = ct.input_output_response(sys, tvec, uvec, squeeze=squeeze) assert yvec.shape == shape2 @@ -1108,7 +1102,7 @@ def test_squeeze(self, fcn, nstate, nout, ninp, squeeze, shape1, shape2): assert yvec.shape == (sys.noutputs, 8) # For InputOutputSystems, also test input_output_response - if isinstance(sys, ct.InputOutputSystem) and not scipy0: + if isinstance(sys, ct.InputOutputSystem): _, yvec = ct.input_output_response(sys, tvec, uvec) if squeeze is not True or sys.noutputs > 1: assert yvec.shape == (sys.noutputs, 8) diff --git a/control/tests/xferfcn_test.py b/control/tests/xferfcn_test.py index 79273f31b..894da5594 100644 --- a/control/tests/xferfcn_test.py +++ b/control/tests/xferfcn_test.py @@ -12,7 +12,7 @@ from control import isctime, isdtime, sample_system, defaults from control.statesp import _convert_to_statespace from control.xferfcn import _convert_to_transfer_function -from control.tests.conftest import slycotonly, nopython2, matrixfilter +from control.tests.conftest import slycotonly, matrixfilter class TestXferFcn: @@ -448,7 +448,6 @@ def test_call_siso(self, dt, omega, resp): np.testing.assert_allclose(sys.evalfr(omega), resp, atol=1e-3) - @nopython2 def test_call_dtime(self): sys = TransferFunction([1., 3., 5], [1., 6., 2., -1], 0.1) np.testing.assert_array_almost_equal(sys(1j), -0.5 - 0.5j) @@ -741,8 +740,7 @@ def test_indexing(self): "matarrayin", [pytest.param(np.array, id="arrayin", - marks=[nopython2, - pytest.mark.skip(".__matmul__ not implemented")]), + marks=[pytest.mark.skip(".__matmul__ not implemented")]), pytest.param(np.matrix, id="matrixin", marks=matrixfilter)], diff --git a/doc/intro.rst b/doc/intro.rst index 01fe81bd0..ce01aca15 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -36,7 +36,7 @@ Installation ============ The `python-control` package can be installed using pip, conda or the -standard distutils/setuptools mechanisms. The package requires `numpy`_ and +standard setuptools mechanisms. The package requires `numpy`_ and `scipy`_, and the plotting routines require `matplotlib `_. In addition, some routines require the `slycot `_ library in order to implement diff --git a/setup.py b/setup.py index a8609148e..df3f8519d 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ packages=find_packages(exclude=['benchmarks']), classifiers=[f for f in CLASSIFIERS.split('\n') if f], install_requires=['numpy', - 'scipy', + 'scipy>=1.3', 'matplotlib'], extras_require={ 'test': ['pytest', 'pytest-timeout'], 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