From 2c7412aff2be2e42eb4557d1dc6fd93362a33714 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sat, 3 Oct 2026 13:24:14 +0200 Subject: [PATCH 1/5] Make __all__ a list of sorted strings --- slycot/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/slycot/__init__.py b/slycot/__init__.py index e9f7bf82..5e29bed0 100644 --- a/slycot/__init__.py +++ b/slycot/__init__.py @@ -72,15 +72,15 @@ __all__ = [ - ab01nd, ab04md, ab05md, ab05nd, ab07nd, ab08nd, ab08nz, - ab09ad, ab09ax, ab09bd, ab09md, ab09nd, ab13bd, ab13dd, - ab13ed, ab13fd, ab13md, ag08bd, mb02ed, mb03rd, mb03vd, - mb03vy, mb03wd, mb05md, mb05nd, mc01td, sb01bd, sb02md, - sb02mt, sb02od, sb03md, sb03md57, sb03od, sb04md, sb04qd, - sb10ad, sb10dd, sb10fd, sb10hd, sb10jd, sb10yd, sg02ad, - sg03ad, sg03bd, tb01id, tb01pd, tb03ad, tb04ad, tb05ad, - tc01od, tc04ad, td04ad, tf01md, tf01rd, tg01ad, tg01fd, - __version__ + '__version__', + 'ab01nd', 'ab04md', 'ab05md', 'ab05nd', 'ab07nd', 'ab08nd', 'ab08nz', + 'ab09ad', 'ab09ax', 'ab09bd', 'ab09md', 'ab09nd', 'ab13bd', 'ab13dd', + 'ab13ed', 'ab13fd', 'ab13md', 'ag08bd', 'mb02ed', 'mb03rd', 'mb03vd', + 'mb03vy', 'mb03wd', 'mb05md', 'mb05nd', 'mc01td', 'sb01bd', 'sb02md', + 'sb02mt', 'sb02od', 'sb03md', 'sb03md57', 'sb03od', 'sb04md', 'sb04qd', + 'sb10ad', 'sb10dd', 'sb10fd', 'sb10hd', 'sb10jd', 'sb10yd', 'sg02ad', + 'sg03ad', 'sg03bd', 'tb01id', 'tb01pd', 'tb03ad', 'tb04ad', 'tb05ad', + 'tc01od', 'tc04ad', 'td04ad', 'tf01md', 'tf01rd', 'tg01ad', 'tg01fd', ] def test(): From f6c159ac9a6e1334c2582bf412080f71bc6a9cb8 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sat, 3 Oct 2026 16:39:19 +0200 Subject: [PATCH 2/5] Make assert_docstring_parse checkvars argument non-optional All call-sites supply checkvars, and default was mutable. --- slycot/tests/test_exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slycot/tests/test_exceptions.py b/slycot/tests/test_exceptions.py index 28614ce5..e36b8770 100644 --- a/slycot/tests/test_exceptions.py +++ b/slycot/tests/test_exceptions.py @@ -29,7 +29,7 @@ SlycotWarning, raise_if_slycot_error) -def assert_docstring_parse(docstring, exception_class, erange, checkvars={}): +def assert_docstring_parse(docstring, exception_class, erange, checkvars): """To check that a docstring can be parsed into exceptions See also raise_if_slycot_error From 5ac8306591b9d4dbff90da50a69e7658869fde02 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sat, 3 Oct 2026 16:40:38 +0200 Subject: [PATCH 3/5] Suppress `ruff check` diagnostic messages In all cases suppression is as focused as possible. --- pyproject.toml | 4 +++- slycot/examples.py | 5 +++++ slycot/exceptions.py | 13 ++++++++++--- slycot/scipy-openblas-symbols/generate_def.py | 1 + slycot/synthesis.py | 2 ++ slycot/tests/test_ab08n.py | 1 + slycot/tests/test_ab09nd.py | 2 ++ slycot/tests/test_ab13md.py | 2 ++ slycot/tests/test_analysis.py | 1 + slycot/tests/test_examples.py | 1 + slycot/tests/test_exceptions.py | 3 +++ slycot/tests/test_mb.py | 3 +++ slycot/tests/test_mc.py | 2 ++ slycot/tests/test_sb.py | 1 + slycot/tests/test_sb10yd.py | 4 ++++ slycot/tests/test_sg02ad.py | 1 + slycot/tests/test_sg03ad.py | 3 +++ slycot/tests/test_tb05ad.py | 5 +++++ slycot/tests/test_td04ad.py | 4 ++++ slycot/transform.py | 5 +++++ 20 files changed, 59 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4a5a3f79..10699ed4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,9 @@ wheel.exclude = ["slycot/src/", "slycot/CMakeLists.txt", "slycot/slicot-source.c addopts = "--pyargs slycot" [tool.ruff.lint] -ignore = [ "E741" ] +ignore = [ + "I001", # import order & formatting +] [tool.cibuildwheel] # scipy-openblas32 version specified here diff --git a/slycot/examples.py b/slycot/examples.py index 9e41a256..dc000268 100644 --- a/slycot/examples.py +++ b/slycot/examples.py @@ -190,10 +190,13 @@ def tb03ad_example(): print('is the following:' ) print('index', out[4]) k_max = max(out[4]) + 1 + # ruff: disable[PIE808] for k in range(0,k_max): + # ruff: disable[UP031] print('P_%d =' %(k)) print(out[5][0:m,0:m,k]) for k in range(0,k_max): + # ruff: disable[UP031] print('Q_%d =' %(k)) print(out[6][0:m,0:p,k]) @@ -254,6 +257,7 @@ def tb05ad_example(): p = np.shape(C)[0] jw_s = [1j*11, 1j*15] + # ruff: disable[RUF059] at, bt, ct, g_1, hinvb,info = slycot.tb05ad(n, m, p, jw_s[0], A, B, C, job='NG') g_2, hinv2, info = slycot.tb05ad(n, m, p, jw_s[1], at, bt, ct, job='NH') @@ -261,6 +265,7 @@ def tb05ad_example(): print('Frequency response for (A, B, C)') print('-------------------------') print('Frequency | Response') + # ruff: disable[UP031] print('%s | %s '%(jw_s[0], g_1[0, 0])) print('%s | %s '%(jw_s[1], g_2[0, 0])) diff --git a/slycot/exceptions.py b/slycot/exceptions.py index f08bb3f6..44600936 100644 --- a/slycot/exceptions.py +++ b/slycot/exceptions.py @@ -27,6 +27,7 @@ class SlycotError(RuntimeError): """Slycot exception base class""" def __init__(self, message, info): + # ruff: disable[UP008] super(SlycotError, self).__init__(message) self.info = info @@ -37,13 +38,13 @@ class SlycotParameterError(SlycotError, ValueError): In case of a wrong input value, the SLICOT routines return a negative info parameter indicating which parameter was illegal. """ - + # ruff: disable[PIE790] pass class SlycotArithmeticError(SlycotError, ArithmeticError): """A Slycot computation failed""" - + # ruff: disable[PIE790] pass @@ -51,6 +52,7 @@ class SlycotWarning(UserWarning): """Slycot Warning""" def __init__(self, message, iwarn, info): + # ruff: disable[UP008] super(SlycotWarning, self).__init__(message) self.info = info self.iwarn = iwarn @@ -62,7 +64,7 @@ class SlycotResultWarning(SlycotWarning): A Slycot routine returned a nonzero info parameter that warns about the returned results, but the results might still be usable. """ - + # ruff: disable[PIE790] pass @@ -101,7 +103,9 @@ def _parse_docsection(section_name, docstring, checkvars): infospec_indent = len(imatch.group(1)) infospec = imatch.group(2) # Don't handle the standard case unless we have i + # ruff: disable[SIM102] if infospec == "info = -i": + # ruff: disable[SIM118] if 'i' not in checkvars.keys(): continue infospec_ = infospec.replace(" = ", " == ") @@ -242,18 +246,21 @@ def raise_if_slycot_error(info, arg_list=None, docstring=None, checkvars=None): return if info < 0 and arg_list: + # ruff: disable[UP032] message = ("The following argument had an illegal value: {}" "".format(arg_list[-info-1])) raise SlycotParameterError(message, info) # catch all if info > 0: + # ruff: disable[UP032] raise SlycotError("Caught unhandled nonzero INFO value {}" "".format(info), info) if iwarn is None and 'iwarn' in checkvars: iwarn = checkvars['iwarn'] if iwarn: + # ruff: disable[UP032] warn(SlycotWarning("Caught unhandled nonzero IWARN value {}" "".format(iwarn), iwarn, info)) diff --git a/slycot/scipy-openblas-symbols/generate_def.py b/slycot/scipy-openblas-symbols/generate_def.py index f7519de5..1ba429b0 100755 --- a/slycot/scipy-openblas-symbols/generate_def.py +++ b/slycot/scipy-openblas-symbols/generate_def.py @@ -95,6 +95,7 @@ def main(): ) with open("scipy-openblas-symbols.def", "wt") as outfile: + # ruff: disable[FURB122] for symbol in blas + lapack + own_symbols + lapack_exclusions: outfile.write(f"-D{symbol.upper()}=SCIPY_{symbol.upper()}\n") diff --git a/slycot/synthesis.py b/slycot/synthesis.py index 2b9fbf9b..3b6a8d8d 100644 --- a/slycot/synthesis.py +++ b/slycot/synthesis.py @@ -1087,6 +1087,7 @@ def sb04qd(n,m,A,B,C,ldwork=None): for the ({info}-{m})-th column of matrix X. """ hidden = ' (hidden by the wrapper)' + # ruff: disable[PLW0127, PLW0128] arg_list = arg_list = ['n', 'm', 'A', 'LDA' + hidden, 'B', 'LDB' + hidden, 'C', 'LDC' + hidden, 'Z', 'LDZ' + hidden, 'IWORK' + hidden, 'DWORK' + hidden, 'ldwork', 'INFO' + hidden] @@ -2378,6 +2379,7 @@ def sg02ad(dico,jobb,fact,uplo,jobl,scal,sort,acc,N,M,P,A,E,B,Q,R,L,ldwork=None, elif (jobb == 'B'): if (fact == 'N'): out = _wrapper.sg02ad_bn(dico,uplo,jobl,scal,sort,acc,N,M,A,E,B,Q,R,L,tol,ldwork) + #ruff: disable[SIM114] elif (fact == 'C'): out = _wrapper.sg02ad_bc(dico,jobl,scal,sort,acc,N,M,P,A,E,B,Q,R,L,tol,ldwork) elif (fact == 'D'): diff --git a/slycot/tests/test_ab08n.py b/slycot/tests/test_ab08n.py index c777fc07..67f37d2c 100644 --- a/slycot/tests/test_ab08n.py +++ b/slycot/tests/test_ab08n.py @@ -53,6 +53,7 @@ def ab08nX(self, ab08fun, A, B, C, D): assert_allclose(Af[:nu, :nu], np.array([[-4.]])) # Compute the structural invariants of the given system. out = ab08fun(n, m, p, A, B, C, D) + # ruff: disable[RUF059] nu, rank, dinfz, nkror, nkrol, infz, kronr, kronl, Af, Bf = out[:10] assert_equal(nu, 2) # Compute the invariant zeros of the given system. diff --git a/slycot/tests/test_ab09nd.py b/slycot/tests/test_ab09nd.py index dd08097a..22a3f848 100644 --- a/slycot/tests/test_ab09nd.py +++ b/slycot/tests/test_ab09nd.py @@ -38,6 +38,7 @@ def test_slicot_ref(): d = np.zeros((3,2)) + # ruff: disable[RUF059] nr, ar, br, cr, dr, ns, hsv = \ ab09nd(dico, job, equil, n, m, p, a, b, c, d, alpha, nr, tol1, tol2) @@ -85,6 +86,7 @@ def test_gh242_regression(): c = np.zeros((p, n)) d = np.array([[42.24]]) + # ruff: disable[RUF059] nr, ar, br, cr, dr, ns, hsv = \ ab09nd(dico='C', job='B', equil='S', n=a.shape[0], m=b.shape[1], p=c.shape[0], A=a, B=b, C=c, D=d) diff --git a/slycot/tests/test_ab13md.py b/slycot/tests/test_ab13md.py index ac933e1f..a0a199fb 100644 --- a/slycot/tests/test_ab13md.py +++ b/slycot/tests/test_ab13md.py @@ -40,6 +40,7 @@ def test_cached_inputoutput(): m = len(nblock) mr = np.count_nonzero(1==itype) + # ruff: disable[RUF059] mu0, d0, g0, x0 = ab13md(Z, nblock, itype) assert m+mr-1 == len(x0) @@ -112,6 +113,7 @@ def test_slicot(self): Z, nblock, itype = slicot_example() + # ruff: disable[RUF059] mu, d, g, x = ab13md(Z, nblock, itype) assert_allclose(mu, muref) diff --git a/slycot/tests/test_analysis.py b/slycot/tests/test_analysis.py index 0b899580..ef7ce066 100644 --- a/slycot/tests/test_analysis.py +++ b/slycot/tests/test_analysis.py @@ -10,6 +10,7 @@ from .test_exceptions import assert_docstring_parse +#ruff: disable[PT014] @pytest.mark.parametrize( 'fun, exception_class, erange, checkvars', ((analysis.ab05nd, SlycotArithmeticError, 1, {'p1': 1}), diff --git a/slycot/tests/test_examples.py b/slycot/tests/test_examples.py index c7d04943..4134c769 100644 --- a/slycot/tests/test_examples.py +++ b/slycot/tests/test_examples.py @@ -52,6 +52,7 @@ def test_example(examplefun, capsys, recwarn): (check_warn(recwarn, examplefun), "Example {} produced a warning.\n")] for failed, msgfmt in failconditions: if failed: + # ruff: disable[UP032] pytest.fail(msgfmt.format(examplefun.__name__) + "Captured output:\n{}\n" "Captured stderr:\n{}\n" diff --git a/slycot/tests/test_exceptions.py b/slycot/tests/test_exceptions.py index e36b8770..8ecf83a2 100644 --- a/slycot/tests/test_exceptions.py +++ b/slycot/tests/test_exceptions.py @@ -69,6 +69,7 @@ def assert_docstring_parse(docstring, exception_class, erange, checkvars): assert wm[0].message.iwarn == iwarn assert wm[0].message.info == info else: + # ruff: disable[TRY004] raise RuntimeError("Invalid test exception") @@ -83,6 +84,7 @@ def test_unhandled_info_iwarn(): with pytest.raises(SlycotError) as ex_info: raise_if_slycot_error(100, [], docstring="no valid docstring") assert ex_info.value.info == 100 + # ruff: disable[PT031] with pytest.warns(SlycotWarning) as wm: raise_if_slycot_error([101, 0], [], docstring="no valid docstring") raise_if_slycot_error(0, [], docstring="no valid docstring", @@ -162,6 +164,7 @@ def test_xerbla_override(): stderr=subprocess.STDOUT, universal_newlines=True) except subprocess.CalledProcessError as cpe: + # ruff: disable[UP032] raise RuntimeError("Trying to call _wrapper.ab08nd() failed with " "returncode {}.\n" "Captured STDOUT: \n {}\n" diff --git a/slycot/tests/test_mb.py b/slycot/tests/test_mb.py index 745d382a..15d8878f 100644 --- a/slycot/tests/test_mb.py +++ b/slycot/tests/test_mb.py @@ -206,6 +206,7 @@ def test_mb03rd(): A, X = schur(test1_A) Ah, Xh = np.copy(A), np.copy(X) # on this basis, get the transform + # ruff: disable[RUF059] Ar, Xr, blsize, W = mb03rd( test1_n, A, X, 'U', 'S', test1_pmax, test1_tol) # ensure X and A are unchanged @@ -235,6 +236,7 @@ def test_mb03rd_default(): X = Tschur.copy() + # ruff: disable[RUF059] Ar, Xr, blsize, W = mb03rd(Aschur.shape[0], Aschur, X, 'U', 'N', pmax=1.0, tol=0.0) Ar2, Xr2, blsize2, W2 = mb03rd(Aschur.shape[0], Aschur) @@ -428,6 +430,7 @@ def test_mb05md_warning(): match="\n" "Matrix A is defective, possibly " "due to rounding errors.") as record: + # ruff: disable[RUF059] (Ar, Vr, Yr, VAL) = mb05md(A, delta) assert record[0].message.info == 6 diff --git a/slycot/tests/test_mc.py b/slycot/tests/test_mc.py index e1650068..dfd9772a 100644 --- a/slycot/tests/test_mc.py +++ b/slycot/tests/test_mc.py @@ -32,6 +32,7 @@ def test_mc01td_D(): def test_mc01td_warnings(): """ test_mc01td_warnings: Test warnings """ + # ruff: disable[ISC004] T = [([0, 0], "Entry ``P(x)`` is the zero polynomial."), ([0, 1], "The polynomial ``P(x)`` is most probably unstable,\n" "although it may be stable with one or more zeros\n" @@ -43,4 +44,5 @@ def test_mc01td_warnings(): "for ``j = 0, 1,..., k-1`` and ``P(DB+1-k) <> 0.0``.")] for P, m in T: with pytest.warns(SlycotResultWarning, match=re.escape(m)): + # ruff: disable[RUF059] (dp, stable, nz) = mc01td('C', len(P)-1, P) diff --git a/slycot/tests/test_sb.py b/slycot/tests/test_sb.py index ceccad3b..17c9537a 100644 --- a/slycot/tests/test_sb.py +++ b/slycot/tests/test_sb.py @@ -38,6 +38,7 @@ def test_sb10ad(): nmeas = 1 gamma = 10 + # ruff: disable[RUF059] gamma_est, Ak, Bk, Ck, Dk, Ac, Bc, Cc, Dc, rcond = synthesis.sb10ad( n, m, np, ncon, nmeas, gamma, a, b, c, d) # from Octave, which also uses SB10AD: diff --git a/slycot/tests/test_sb10yd.py b/slycot/tests/test_sb10yd.py index 8574bd51..8b940e41 100644 --- a/slycot/tests/test_sb10yd.py +++ b/slycot/tests/test_sb10yd.py @@ -4,6 +4,7 @@ from slycot import synthesis +# ruff: disable[UP039] class Test_sb10yd(): # TODO: There might be better systems/filters to do these tests. @@ -75,6 +76,8 @@ def test_sb10yd_cont_allclose(self): n = 2 dico = 0 # 0 for continuous time flag = 0 # 0 for no constraints on the poles + + # ruff: disable[RUF059] n_id, A_id, B_id, C_id, D_id = synthesis.sb10yd( dico, flag, len(omega), real_H_resp, imag_H_resp, omega, n, tol=0) @@ -161,6 +164,7 @@ def test_sb10yd_disc_allclose(self): n = 2 dico = 1 # 0 for discrete time flag = 0 # 0 for no constraints on the poles + # ruff: disable[RUF059] n_id, A_id, B_id, C_id, D_id = synthesis.sb10yd( dico, flag, len(omega), real_H_resp, imag_H_resp, omega, n, tol=0) diff --git a/slycot/tests/test_sg02ad.py b/slycot/tests/test_sg02ad.py index 1576a8ec..468cd912 100644 --- a/slycot/tests/test_sg02ad.py +++ b/slycot/tests/test_sg02ad.py @@ -26,6 +26,7 @@ def test_sg02ad_case1(): [-1.94918951, -3.15480639, 5.24379117], [ 4.29133973, 8.10585767, -5.88895897]]) L = np.array(np.zeros((3,1))) + # ruff: disable[RUF059] rcondu, X, alphar, alphai, beta, S, T, U, iwarn = \ synthesis.sg02ad('D', 'B', 'N', 'U', 'Z', 'N', 'S', 'R', n, m, 1, diff --git a/slycot/tests/test_sg03ad.py b/slycot/tests/test_sg03ad.py index a5976e0d..044cbf41 100644 --- a/slycot/tests/test_sg03ad.py +++ b/slycot/tests/test_sg03ad.py @@ -23,6 +23,7 @@ def test_sg03ad_ex1c(): Y = A.T.dot(Xref).dot(E) + E.T.dot(Xref).dot(A) Q = np.zeros((n, n)) Z = np.zeros((n, n)) + # ruff: disable[RUF059] A, E, Q, Z, X, scale, sep, ferr, alphar, alphai, beta = \ synthesis.sg03ad('C', 'B', 'N', 'N', 'L', n, A, E, Q, Z, Y) assert_almost_equal(X, Xref) @@ -38,6 +39,7 @@ def test_sg03ad_ex1d(): Y = A.T.dot(Xref).dot(A) - E.T.dot(Xref).dot(E) Q = np.zeros((n, n)) Z = np.zeros((n, n)) + # ruff: disable[RUF059] A, E, Q, Z, X, scale, sep, ferr, alphar, alphai, beta = \ synthesis.sg03ad('D', 'B', 'N', 'N', 'L', n, A, E, Q, Z, Y) assert_almost_equal(X, Xref) @@ -59,6 +61,7 @@ def test_sg03ad_b1(): [0.0000, -1.0000, -3.0000]]) Q = np.zeros((3, 3)) Z = np.zeros((3, 3)) + # ruff: disable[RUF059] A, E, Q, Z, X, scale, sep, ferr, alphar, alphai, beta = \ synthesis.sg03ad('C', 'B', 'N', 'N', 'L', n, A, E, Q, Z, -Y) # print(A, E, Q, Z, X, scale, sep) diff --git a/slycot/tests/test_tb05ad.py b/slycot/tests/test_tb05ad.py index e940c45f..7325c4c6 100644 --- a/slycot/tests/test_tb05ad.py +++ b/slycot/tests/test_tb05ad.py @@ -40,6 +40,7 @@ def test_tb05ad_ng(): Test that tb05ad with job 'NG' computes the correct frequency response. """ + # ruff: disable[PLC0206] for key in CASES: sys = CASES[key] check_tb05ad_AG_NG(sys, 10*1j, 'NG') @@ -50,6 +51,7 @@ def test_tb05ad_ag(): Test that tb05ad with job 'AG' computes the correct frequency response. """ + # ruff: disable[PLC0206] for key in CASES: sys = CASES[key] check_tb05ad_AG_NG(sys, 10*1j, 'AG') @@ -64,6 +66,7 @@ def test_tb05ad_nh(): Subsequently, call tb05ad with job='NH' using this transformed system. """ jomega = 10*1j + # ruff: disable[PLC0206] for key in CASES: sys = CASES[key] sys_transformed = check_tb05ad_AG_NG(sys, jomega, 'NG') @@ -212,6 +215,7 @@ def test_tb05ad_balance(): Aeig = eig(A)[0] neig0 = np.sum(np.abs(Aeig) == 0) + # ruff: disable[RUF059] As, T = matrix_balance(A) nperm = np.sum(np.diag(T == 0)) nscale = n - np.sum(T == 1.0) @@ -224,6 +228,7 @@ def test_tb05ad_balance(): # do a run jomega = 1.0 + # ruff: disable[RUF059] At, Bt, Ct, rcond, g_jw, ev, hinvb, info = transform.tb05ad( 8, 4, 3, jomega, A, B, C, job='AG') diff --git a/slycot/tests/test_td04ad.py b/slycot/tests/test_td04ad.py index 57c557d0..1f8ae3c8 100644 --- a/slycot/tests/test_td04ad.py +++ b/slycot/tests/test_td04ad.py @@ -137,6 +137,7 @@ def test_staticgain(): assert C.shape == (3,0) np.testing.assert_array_almost_equal(D, Dc) + # ruff: disable[RUF059] n, A, B, C, D = transform.td04ad('R', 2, 3, idxr, denr, num) #print('A=\n', A, '\nB=\n', B, '\nC=\n', C, '\nD=\n', D) assert A.shape == (0,0) @@ -182,6 +183,7 @@ def test_mixfeedthrough(): denc = np.array([[1.0, 1.1], [1.0, 0.0]]) idxc = np.array([1, 0]) + # ruff: disable[RUF059] n, A, B, C, D = transform.td04ad('C', 2, 2, idxc, denc, numc) np.testing.assert_array_almost_equal(D, np.array([[0, 0],[-0.1, 0]])) @@ -198,6 +200,7 @@ def test_toandfrom(): num = tfout[6] den = tfout[5] idxc = np.array([1, 0]) + # ruff: disable[RUF059] n, At, Bt, Ct, Dt = transform.td04ad('R', 2, 2, idxc, den, num) np.testing.assert_array_almost_equal(D, Dt) np.testing.assert_array_almost_equal(A, At) @@ -214,6 +217,7 @@ def test_tfm2ss_6(): n, A, B, C, D = transform.td04ad('R', m, p, index, dcoeff, ucoeff) assert n == 0 np.testing.assert_array_almost_equal(D, np.array([[64]])) + # ruff: disable[RUF059] n, A, B, C, D = transform.td04ad('C', m, p, index, dcoeff, ucoeff) assert n == 0 np.testing.assert_array_almost_equal(D, np.array([[64]])) diff --git a/slycot/transform.py b/slycot/transform.py index 43762b5a..e6d44ce2 100644 --- a/slycot/transform.py +++ b/slycot/transform.py @@ -320,6 +320,7 @@ def tb03ad(n,m,p,A,B,C,D,leri,equil='N',tol=0.0,ldwork=None): "R": _wrapper.tb03ad_r} mp_ = {"L": p, "R": m} mp = mp_[leri] + # ruff: disable[SIM118] if leri not in wfun.keys(): raise SlycotParameterError('leri must be either L or R', -1) if ldwork is None: @@ -570,11 +571,13 @@ def tb05ad(n, m, p, jomega, A, B, C, job='NG'): "".format(*(A.shape + (n,))), -7) if B.shape != (n, m): + # ruff: disable[UP030] raise SlycotParameterError("The shape of B is ({0:}, {1:}), " "but expected ({2:}, {3:})" "".format(*(B.shape + (n, m))), -9) if C.shape != (p, n): + # ruff: disable[UP030] raise SlycotParameterError("The shape of C is ({0:}, {1:}), " "but expected ({2:}, {3:})" "".format(*(C.shape + (p, n))), @@ -604,6 +607,7 @@ def tb05ad(n, m, p, jomega, A, B, C, job='NG'): raise_if_slycot_error(info, arg_list, tb05ad.__doc__, locals()) return g_i, hinvb, info else: + # ruff: disable[UP032] raise SlycotParameterError("Unrecognized job. Expected job = 'AG' or " "job='NG' or job = 'NH' but received job={}" "".format(job), @@ -813,6 +817,7 @@ def tc04ad(m,p,index,pcoeff,qcoeff,leri,ldwork=None): ldwork = max(m, p)*(max(m, p)+4) n = sum(index) wfun = {"L": _wrapper.tc04ad_l, "R": _wrapper.tc04ad_r} + # ruff: disable[SIM118] if leri not in wfun.keys(): raise SlycotParameterError('leri must be either L or R', -1) out = wfun[leri](m, p, index, pcoeff, qcoeff, n) From b2283e68d3a043296f256424afcdb8809f45733d Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sat, 3 Oct 2026 16:54:44 +0200 Subject: [PATCH 4/5] Use scikit-build-core's tool.dynamic metadata for version --- pyproject.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 10699ed4..af007c00 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["scikit-build-core", "numpy"] +requires = ["scikit-build-core>=1.0", "numpy"] build-backend = "scikit_build_core.build" [project] @@ -43,8 +43,11 @@ test = ["pytest", "scipy"] [project.urls] homepage = "https://github.com/python-control/Slycot" +[[tool.dynamic-metadata]] +field = "version" +provider = "scikit_build_core.metadata.setuptools_scm" + [tool.scikit-build] -metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" wheel.exclude = ["slycot/src/", "slycot/CMakeLists.txt", "slycot/slicot-source.cmake", "slycot/scipy-openblas-symbols"] [tool.pytest.ini_options] From e937be370fa26df03aa7e86b948558b65a9181d7 Mon Sep 17 00:00:00 2001 From: Rory Yorke Date: Sat, 3 Oct 2026 16:58:24 +0200 Subject: [PATCH 5/5] Add checks and examples for BLA_VENDOR and in-place build --- BUILD.rst | 4 ++-- dev-tools/inplace-editable-build.bash | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/BUILD.rst b/BUILD.rst index 49a00e18..4396ab9d 100644 --- a/BUILD.rst +++ b/BUILD.rst @@ -62,9 +62,9 @@ The test command is the same as when building from the sdist:: Non-isolated, editable build ---------------------------- -When doing development one builds over and over; in that case it's faster to do a non-isolated editable build. Run or adapt developer script `inplace-editable-build.bash`_ for that. +When doing development one builds over and over; in that case it's faster to do a non-isolated editable build. Run or adapt developer script `inplace-editable-build.bash`_ for that. CMake variables can be set to configure the build; for example, in the command below, ``BLA_VENDOR`` is set to to tell CMake to use the Netlib BLAS/LAPACK libraries. This example is not merely illustrative: on Debian 13 *not* specifying ``BLA_VENDOR`` can cause test failures. -.. _`inplace-editable-build.bash`: ./dev-tools/inplace-editable-build.bash +.. _`inplace-editable-build.bash`: BLA_VENDOR=Generic ./dev-tools/inplace-editable-build.bash Customizing the build diff --git a/dev-tools/inplace-editable-build.bash b/dev-tools/inplace-editable-build.bash index 1f80040e..d816ca2d 100755 --- a/dev-tools/inplace-editable-build.bash +++ b/dev-tools/inplace-editable-build.bash @@ -20,6 +20,9 @@ # - BLAS and LAPACK libraries # # Tested on Debian 13. +# +# Select a BLAS/LAPACK provider with environment variable BLA_VENDOR; see +# https://cmake.org/cmake/help/latest/module/FindBLAS.html#blas-lapack-vendors set -euo pipefail @@ -28,6 +31,10 @@ if [ ! -f ./dev-tools/inplace-editable-build.bash ]; then exit 1; fi +if [ ! -v BLA_VENDOR ]; then + echo "BLA_VENDOR is not set. This is known to sometimes cause test failures"; +fi + echo "--Build" python -m venv venv-build source venv-build/bin/activate