diff --git a/CHANGELOG.md b/CHANGELOG.md index cf61b7e0..21887dd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,60 @@ other release, fixes included, the minor. ## [Unreleased] +## [0.13] - 2026-10-09 + +Simpler, faster and harder to misuse. Each computation now has one +implementation (#204): a simulation's random numbers are counter-based, a +function of the seed, the stream, the simulation and the draw alone +(#209); every timeline is recorded by the event loop (#205); numba's engine +takes every level's events in one loop (#206) and keeps whether each level +works up to date without truth tables (#255, #254); a decision diagram has +one search and one replay (#207); and a run's changes are put in time order +by numpy alone (#208). The exact curves are built several times as fast, +an MTTF takes a few hundred evaluations where it took thousands (#229), +and capacity runs, timelines and conditional runs spend less time around +the compiled loop (#246, #247, #248). Common-cause groups are worked out +module by module, linear in the groups rather than doubling per group, in +fault trees and non-repairable diagrams (#219) and in repairable ones +(#218), whose members' tests and repairs may now take time (#220); limited +repair crews refuse common-cause groups where they dropped them (#251, +#252); and `availability_rate` is exact just after a scheduled maintenance +(#240). From the 0.12 persona check (#213): a mixture as a repairable life +(#227), times first in the measures over time (#224), replacement intervals +from a calendar (#230), discounted costs from new and endless horizons +(#231), chunks of a plain run (#236), unavailability over time to its own +precision (#237), `to_dict()` on every result (#235), public levers +(#244), simulations of one diagram from several threads (#216), a shared +fitted model drawn once in parameter uncertainty (#214), messages that say +what to do and inputs checked where they are given (#232, #233), and docs +brought up to date (#238). What 0.12 deprecated is gone. + +Behaviour changes: a `RepairableRBD` simulation's seeded results differ +from 0.12's, within their standard errors, as its random numbers are +counter-based (#209); exact curves move in their seventh or eighth +significant figure (a grid of 1,000 steps); a cost result's `mean`, +`cost_rate` and breakdowns, and the new `mean_availability`, are the run's +estimate (exact or conditional by default), the simulations' own being +`sample_mean` (#223); a model given for the input or output node is +refused (#217); costs, intervals and times given as text or booleans, +surpyval's distribution class in place of a model, and seeds that are not +whole numbers or lists of them are refused (#233, #232), as are unknown +nodes in `allowed=`, `offset_shares=` and `with_intervals` (#222) and a +component that fails and is repaired at once; a fixed-probability node's +simulated lifetime is 0 or infinite, so seeded draws of such a diagram +change; `compare(quantity="cost")` counts acquisition, and `cost()` with +only an acquisition cost gives a result (#234); a test offset within a +billionth of the interval is 0 (#237); a capacity run's probabilities can +differ from 0.12's in their last digits (#246); what 0.12 deprecated is +removed: calling `SparesDemand.mean()` or `std()` raises `TypeError` (they +are properties), as do `StandbyModel`'s and `LoadSharingModel`'s +`mc_samples`, `lower` and `seed`, and `StandbyModel`'s +`switching_probability` and `dormancy_factor` are given by name; and +surpyval 0.23 is required. Deprecated, to go in 0.14: +`optimal_inspection_intervals(offsets=)` (now `offset_shares=`), calling +`CapacityDistribution.mean()`, and the simulation options of an exact +`mean`. + ### Added - **A surpyval `MixtureModel` as a repairable component's life (#227).** @@ -367,6 +421,7 @@ other release, fixes included, the minor. bound method printed when `()` is left off.) - **Warnings point at the line that called the package (#232)**, where several pointed inside it. + ### Deprecated - **`optimal_inspection_intervals(offsets=)` is renamed `offset_shares=` @@ -388,6 +443,17 @@ other release, fixes included, the minor. `_calendar_lever` and `_as_spec`, stay for 0.13 (its `Lever` tuple with a `bounds` field at its end), and may change or go in 0.14: use `levers()`, `Lever` (its `calendar` and `value`) and `with_levers`. + +### Removed + +- **What 0.12 deprecated.** `SparesDemand.mean` and `std` are properties + alone: calling them, `mean()`, raises `TypeError` (#184). `StandbyModel` + and `LoadSharingModel` no longer take `mc_samples`, `lower` or `seed`, + which set a fit to simulated lifetimes that 0.12 removed (#149): + passing them raises `TypeError`. `StandbyModel`'s `switching_probability` + and `dormancy_factor` came after them, so they are now given by name, and + a call that gave them by position is refused rather than misread. + ### Fixed - **`availability_rate` just after a scheduled maintenance** (#240). A diff --git a/CLAUDE.md b/CLAUDE.md index 5418f523..fbc4489c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -338,20 +338,19 @@ non-parametric node, and a standby or load-sharing model with no exact or numerical reliability refuses one (`is_simulated`), where a fit to simulated lifetimes stood in, and is left to the simulations. What 0.12 - deprecates goes in 0.13 (`NEXT_REMOVAL`): calling - `SparesDemand.mean()`/`std()`, now properties (#184, through - `deprecation.called`), and those models' `mc_samples`, `lower` and - `seed`, which set the fit (through `deprecation.ignored`). - `test_the_calls_go_in_the_release_after_next` fails once the version - reaches it. What 0.13 deprecates goes in 0.14 (`REMOVAL_AFTER_NEXT`): - `optimal_inspection_intervals(offsets=)`, renamed `offset_shares=` - (#222, through `deprecation.renamed`); calling + deprecated went in 0.13, and `test_removed_in_0_13.py` keeps it gone: + calling `SparesDemand.mean()`/`std()` (#184) and those models' + `mc_samples`, `lower` and `seed` raise `TypeError`, and `StandbyModel`'s + options after them are given by name. What 0.13 deprecates goes in 0.14 + (`NEXT_REMOVAL`): `optimal_inspection_intervals(offsets=)`, renamed + `offset_shares=` (#222, through `deprecation.renamed`); calling `CapacityDistribution.mean()`, now a property (#235, through `deprecation.called`); and `mc_samples` and `seed` given to the exact `mean` of a `StandbyModel`, `LoadSharingModel` or `DegradingNode`, which ignores them (#233, through `deprecation.ignored` in `standby_node.drawn_mean`), all of which - `test_what_0_13_deprecates_goes_in_0_14` holds to. + `test_what_0_13_deprecates_goes_in_0_14` holds to: it fails once the + version reaches `NEXT_REMOVAL`. - **Exact by default, simulation on request.** Where an analysis can be computed exactly or numerically, that is the default, and the Monte-Carlo estimate is a `method="simulate"` away (as for `NonRepairableRBD.mean`). diff --git a/docs/guide/redundancy-models.md b/docs/guide/redundancy-models.md index 0f5f1c2b..2e7e5d82 100644 --- a/docs/guide/redundancy-models.md +++ b/docs/guide/redundancy-models.md @@ -95,8 +95,8 @@ reliability, naming the simulations that take it: `random`, `NonRepairableRBD`, or `availability` and `cost` of a `RepairableRBD`. Until 0.12 such an arrangement was given a Kaplan–Meier fit to simulated lifetimes, which carried their Monte-Carlo error into every exact analysis -of the system; the `mc_samples`, `lower` and `seed` that set the fit are -now ignored, and warn until 0.13 refuses them. +of the system; the `mc_samples`, `lower` and `seed` that set the fit went +in 0.13. `mean()` and `random(size, seed=None)` give the arrangement's mean lifetime and draw lifetimes; `cs(x, X)` is its conditional survival. A standby node diff --git a/docs/guide/saving.md b/docs/guide/saving.md index 280d7939..1e2bf6f3 100644 --- a/docs/guide/saving.md +++ b/docs/guide/saving.md @@ -34,7 +34,7 @@ path = Path(tempfile.mkdtemp()) / "plant.json" rbd.to_json(path) # written; returns None NonRepairableRBD.from_json(path).sf(30) == rbd.sf(30) # True type(RBD.from_dict(data)).__name__ # 'NonRepairableRBD': the base class dispatches on type -data["type"], data["repyability_version"] # ('NonRepairableRBD', '0.12') +data["type"], data["repyability_version"] # ('NonRepairableRBD', '0.13') ``` What is saved: diff --git a/repyability/_version.py b/repyability/_version.py index df19ad71..639c5fa1 100644 --- a/repyability/_version.py +++ b/repyability/_version.py @@ -5,4 +5,4 @@ dependencies) via ``[tool.setuptools.dynamic]`` in ``pyproject.toml``. """ -__version__ = "0.12" +__version__ = "0.13" diff --git a/repyability/rbd/load_sharing_node.py b/repyability/rbd/load_sharing_node.py index f178188e..89a6074d 100644 --- a/repyability/rbd/load_sharing_node.py +++ b/repyability/rbd/load_sharing_node.py @@ -37,7 +37,7 @@ import numpy as np from surpyval import Hypoexponential -from repyability.utils.deprecation import ignored, refuse_removed_names +from repyability.utils.deprecation import refuse_removed_names from repyability.utils.wrappers import conditional_survival, numpy_seed from ._dependent_lifetimes import LoadSharingSurvival @@ -143,14 +143,6 @@ class LoadSharingModel: k : int, optional The minimum number of surviving units for the group to work, from 1 to ``len(models)``, by default 1. - mc_samples : int, optional - Deprecated and unused: the number of simulated lifetimes the - reliability of different units was fitted to until 0.12. Passing - it warns, and 0.13 will refuse it. - lower : float, optional - Deprecated and unused, as ``mc_samples`` is: that fit's lower limit. - seed : int or None, optional - Deprecated and unused, as ``mc_samples`` is: that fit's seed. Attributes ---------- @@ -209,15 +201,7 @@ def __init__( models, load, k=1, - mc_samples=None, - lower=None, - seed=None, ): - ignored( - "LoadSharingModel()", - "its reliability is no longer fitted to simulated lifetimes.", - {"mc_samples": mc_samples, "lower": lower, "seed": seed}, - ) models = list(models) if len(models) == 0: raise ValueError("LoadSharingModel needs at least one unit.") diff --git a/repyability/rbd/results.py b/repyability/rbd/results.py index 8d25ebe4..de7747e8 100644 --- a/repyability/rbd/results.py +++ b/repyability/rbd/results.py @@ -33,7 +33,7 @@ from scipy.special import ndtri from repyability.rbd import _montecarlo as montecarlo -from repyability.utils.deprecation import REMOVAL_AFTER_NEXT, called +from repyability.utils.deprecation import called from repyability.utils.wrappers import outside_level @@ -1995,7 +1995,6 @@ def mean(self) -> Any: return called( self._per_time(np.sum(parts, axis=0)), "CapacityDistribution.mean", - REMOVAL_AFTER_NEXT, ) def delivered_fraction(self, demand: float) -> Any: @@ -2724,21 +2723,15 @@ def __repr__(self) -> str: @property def mean(self) -> float: - """The expected number of spares used. - - A property, as the other results' values are (#184): calling it, - ``mean()``, as before 0.12, still gives it, with a - ``FutureWarning``, until 0.13. + """The expected number of spares used (a property, as the other + results' values are, #184). Returns ------- float The mean number of replacements. """ - return called( - float(self.probabilities @ np.arange(len(self.probabilities))), - "SparesDemand.mean", - ) + return float(self.probabilities @ np.arange(len(self.probabilities))) @property def std(self) -> float: @@ -2751,11 +2744,7 @@ def std(self) -> float: The standard deviation of the number of replacements. """ counts = np.arange(len(self.probabilities)) - mean = float(self.mean) - return called( - float(np.sqrt(self.probabilities @ (counts - mean) ** 2)), - "SparesDemand.std", - ) + return float(np.sqrt(self.probabilities @ (counts - self.mean) ** 2)) def covered(self, stock: int) -> float: """The probability that ``stock`` spares cover the horizon's diff --git a/repyability/rbd/standby_node.py b/repyability/rbd/standby_node.py index 05ed9ae8..0ea35755 100644 --- a/repyability/rbd/standby_node.py +++ b/repyability/rbd/standby_node.py @@ -4,11 +4,7 @@ from surpyval import Hypoexponential from repyability.utils.checks import simulation_options, whole_number -from repyability.utils.deprecation import ( - REMOVAL_AFTER_NEXT, - ignored, - refuse_removed_names, -) +from repyability.utils.deprecation import ignored, refuse_removed_names from repyability.utils.wrappers import numpy_seed from ._dependent_lifetimes import ( @@ -56,7 +52,6 @@ def drawn_mean(model, name: str, exact, mc_samples, seed, method) -> float: "the mean is worked out exactly (or numerically), so nothing is " "drawn; method='simulate' estimates it from draws instead.", options, - REMOVAL_AFTER_NEXT, ) return value @@ -189,21 +184,13 @@ class StandbyModel: k : int, optional The number of units that must operate for the arrangement to work, from 1 to ``len(reliabilities)``, by default 1. - mc_samples : int, optional - Deprecated and unused: the number of simulated lifetimes the - reliability was fitted to until 0.12. Passing it warns, and 0.13 - will refuse it. - lower : float, optional - Deprecated and unused, as ``mc_samples`` is: that fit's lower limit. switching_probability : float or sequence of float, optional The probability, in ``[0, 1]``, that switching onto the next spare succeeds: a scalar for every switch, or one value per spare (length ``len(reliabilities) - k``). A failed switch ends the arrangement's life when the unit it should replace fails. By default 1.0 (perfect switching). Imperfect switching is supported - for cold standby only. - seed : int or None, optional - Deprecated and unused, as ``mc_samples`` is: that fit's seed. + for cold standby only. Given by name, as ``dormancy_factor`` is. dormancy_factor : float, optional The dormant-to-operating aging ratio, in ``[0, 1]``: 0 is cold, 1 is hot and anything between is warm. By default 0.0. @@ -286,17 +273,10 @@ def __init__( self, reliabilities, k=1, - mc_samples=None, - lower=None, + *, switching_probability=1.0, - seed=None, dormancy_factor=0.0, ): - ignored( - "StandbyModel()", - "its reliability is no longer fitted to simulated lifetimes.", - {"mc_samples": mc_samples, "lower": lower, "seed": seed}, - ) k = whole_number(k, "k (how many units must operate)") if k > len(reliabilities): raise ValueError( diff --git a/repyability/tests/test_api_consistency.py b/repyability/tests/test_api_consistency.py index 3b4987f5..14807d41 100644 --- a/repyability/tests/test_api_consistency.py +++ b/repyability/tests/test_api_consistency.py @@ -33,7 +33,7 @@ from repyability._version import __version__ from repyability.rbd.ccf import with_parameters from repyability.rbd.uncertainty import draw_ccf_models -from repyability.utils.deprecation import NEXT_REMOVAL, REMOVAL_AFTER_NEXT +from repyability.utils.deprecation import NEXT_REMOVAL W, E = surv.Weibull.from_params, surv.Exponential.from_params F = FixedEventProbability.from_params @@ -258,16 +258,6 @@ def test_the_spares_demand_values_are_properties(): demand = rbd.spares_demand(1000.0)["pump"] assert demand.mean == pytest.approx(10.0, rel=1e-5) assert demand.std == pytest.approx(np.sqrt(10.0), rel=1e-4) - with pytest.warns(FutureWarning, match=r"write mean, not mean\(\)"): - assert demand.mean() == demand.mean - with pytest.warns(FutureWarning, match="0.13"): - assert demand.std() == demand.std - - -def test_the_calls_go_in_the_release_after_next(): - # When the version reaches NEXT_REMOVAL, what 0.12 deprecates must go. - version = tuple(map(int, __version__.split("."))) - assert version < tuple(map(int, NEXT_REMOVAL.split("."))) def test_what_0_13_deprecates_goes_in_0_14(): @@ -278,7 +268,7 @@ def test_what_0_13_deprecates_goes_in_0_14(): # names callers used before the levers were public, kept as they were # for 0.13: rbd._sensitivity's levers and Lever (#244). version = tuple(map(int, __version__.split("."))) - assert version < tuple(map(int, REMOVAL_AFTER_NEXT.split("."))) + assert version < tuple(map(int, NEXT_REMOVAL.split("."))) def test_an_uncertainty_result_shows_a_summary(): diff --git a/repyability/tests/test_messages.py b/repyability/tests/test_messages.py index 18cbfb63..92e03922 100644 --- a/repyability/tests/test_messages.py +++ b/repyability/tests/test_messages.py @@ -60,7 +60,9 @@ def crow(): "max_samples", ), ( - lambda: StandbyModel([W([100, 2])] * 2, n_sims=100), + lambda: StandbyModel([W([100, 2])] * 2).mean( + method="simulate", n_sims=100 + ), "n_sims", "mc_samples", ), diff --git a/repyability/tests/test_removed_in_0_12.py b/repyability/tests/test_removed_in_0_12.py index 7f1f2a45..dd03067c 100644 --- a/repyability/tests/test_removed_in_0_12.py +++ b/repyability/tests/test_removed_in_0_12.py @@ -3,7 +3,7 @@ and the standby and load-sharing models with no exact or numerical reliability refuse it, rather than fitting one to simulated lifetimes, and are left to the simulations. Their constructors' simulation settings, which -set that fit, warn until 0.13 (``NEXT_REMOVAL``).""" +set that fit, went in 0.13 (``test_removed_in_0_13``).""" import warnings @@ -257,25 +257,6 @@ def test_a_repairable_component_with_one_is_simulated(build, case): assert 0.0 < float(np.mean(result.availability)) <= 1.0 -@pytest.mark.parametrize("build, case", NO_RELIABILITY, ids=IDS) -def test_the_fits_settings_warn_until_0_13(build, case): - from repyability.utils.deprecation import NEXT_REMOVAL - - model = quiet(build) - kind = type(model) - with pytest.warns( - FutureWarning, match=rf"mc_samples, seed.*{NEXT_REMOVAL}" - ): - if kind is StandbyModel: - StandbyModel( - model.reliabilities, k=model.k, mc_samples=100, seed=1 - ) - else: - LoadSharingModel( - model.models, load=model.load, mc_samples=100, seed=1 - ) - - def test_exact_and_numerical_models_are_unchanged(): for build in ( lambda: StandbyModel([W([100, 2])] * 2), diff --git a/repyability/tests/test_removed_in_0_13.py b/repyability/tests/test_removed_in_0_13.py new file mode 100644 index 00000000..bb6f178d --- /dev/null +++ b/repyability/tests/test_removed_in_0_13.py @@ -0,0 +1,52 @@ +"""What 0.12 deprecated is gone in 0.13: calling ``SparesDemand.mean()`` +and ``std()``, properties since 0.12 (#184), and ``StandbyModel``'s and +``LoadSharingModel``'s ``mc_samples``, ``lower`` and ``seed``, which set a +fit to simulated lifetimes that 0.12 removed (#149).""" + +import numpy as np +import pytest +import surpyval as surv + +from repyability import LoadSharingModel, RepairableRBD, StandbyModel + +W, E = surv.Weibull.from_params, surv.Exponential.from_params + + +def aft(): + rng = np.random.default_rng(0) + load = rng.uniform(0.5, 2.0, size=400) + x = rng.exponential(100.0 / np.exp(0.6 * (load - 1.0)), size=400) + return surv.ExponentialAFT.fit(x + 1e-3, Z=load.reshape(-1, 1)) + + +def test_the_spares_demand_values_are_not_called(): + rbd = RepairableRBD( + [("s", "pump"), ("pump", "t")], + {"pump": {"reliability": E([0.01]), "repairability": "instant"}}, + ) + demand = rbd.spares_demand(1000.0)["pump"] + assert type(demand.mean) is float and type(demand.std) is float + for value in (demand.mean, demand.std): + with pytest.raises(TypeError, match="not callable"): + value() + + +@pytest.mark.parametrize("name", ["mc_samples", "lower", "seed"]) +def test_the_fits_settings_are_refused(name): + with pytest.raises(TypeError, match=name): + StandbyModel([W([100, 2])] * 2, **{name: 1}) + with pytest.raises(TypeError, match=name): + LoadSharingModel([aft()] * 2, load=2.0, **{name: 1}) + + +def test_a_standby_models_options_are_given_by_name(): + # A switching probability or dormancy factor given by position was + # after the removed settings, so it is refused rather than misread. + units = [E([0.01])] * 2 + with pytest.raises(TypeError): + StandbyModel(units, 1, None, None, 0.9) + with pytest.raises(TypeError): + StandbyModel(units, 1, 0.9) + given = StandbyModel(units, 1, switching_probability=0.9) + assert given.switching_probability == 0.9 + assert float(given.mean()) == pytest.approx(100.0 + 0.9 * 100.0) diff --git a/repyability/utils/deprecation.py b/repyability/utils/deprecation.py index 27dd55b4..6d5c744f 100644 --- a/repyability/utils/deprecation.py +++ b/repyability/utils/deprecation.py @@ -1,24 +1,23 @@ """Deprecated names, arguments and calls. -A deprecation gives one minor release's notice: what 0.12 deprecates goes -in 0.13 (``NEXT_REMOVAL``). Each warns with a ``FutureWarning``, which +A deprecation gives one minor release's notice: what 0.13 deprecates goes +in 0.14 (``NEXT_REMOVAL``). Each warns with a ``FutureWarning``, which Python always shows, as the notice is short: -- calling a result's value that is now a property, such as - ``SparesDemand.mean()`` (``called``, #184); -- ``StandbyModel``'s and ``LoadSharingModel``'s ``mc_samples``, ``lower`` - and ``seed``, which set a fit to simulated lifetimes that 0.12 removed - (``ignored``, #149). - -What 0.13 deprecates goes in 0.14 (``REMOVAL_AFTER_NEXT``): the -search's ``offsets`` of ``optimal_inspection_intervals``, renamed -``offset_shares`` (``renamed``, #222); ``CapacityDistribution.mean()``, -now a property (``called``, #235); and the simulation options of a -``StandbyModel``'s, ``LoadSharingModel``'s or ``DegradingNode``'s exact -``mean`` (``ignored``, #233). - -What 0.11 deprecated went in 0.12 (#149): its old names are refused with -the names that took their place (``refuses_removed_names``, #232). +- the search's ``offsets`` of ``optimal_inspection_intervals``, renamed + ``offset_shares`` (``renamed``, #222); +- calling ``CapacityDistribution.mean()``, now a property (``called``, + #235); +- the simulation options of a ``StandbyModel``'s, ``LoadSharingModel``'s + or ``DegradingNode``'s exact ``mean``, which it ignores (``ignored``, + #233). + +What 0.12 deprecated went in 0.13: calling ``SparesDemand.mean()`` and +``std()``, properties since 0.12 (#184), and ``StandbyModel``'s and +``LoadSharingModel``'s ``mc_samples``, ``lower`` and ``seed``, which set a +fit to simulated lifetimes that 0.12 removed (#149). What 0.11 deprecated +went in 0.12 (#149): its old names are refused with the names that took +their place (``refuses_removed_names``, #232). """ import functools @@ -30,10 +29,8 @@ from repyability.utils.wrappers import outside_level -#: The release that removes what 0.12 deprecates. -NEXT_REMOVAL = "0.13" #: The release that removes what 0.13 deprecates. -REMOVAL_AFTER_NEXT = "0.14" +NEXT_REMOVAL = "0.14" def _called_warning(name: str, removal: str) -> None: @@ -77,7 +74,7 @@ class CalledArray(np.ndarray): of the array, with a ``FutureWarning``.""" _name: str = "" - _removal: str = REMOVAL_AFTER_NEXT + _removal: str = NEXT_REMOVAL def __call__(self) -> np.ndarray: _called_warning(self._name, self._removal) @@ -90,7 +87,7 @@ def __reduce__(self): def called(value: Any, name: str, removal: str = NEXT_REMOVAL) -> Any: """``value``, a result's property ``name`` (``"Class.attribute"``) that used to be a method: still callable, with a ``FutureWarning``, until - ``removal`` (``NEXT_REMOVAL`` for what 0.12 deprecated). A number + ``removal`` (``NEXT_REMOVAL`` by default). A number becomes a ``CalledValue``, an array a ``CalledArray``.""" if np.ndim(value) == 0: return CalledValue(float(value), name, removal) @@ -107,7 +104,7 @@ def ignored( ) -> None: """Warn that the arguments in ``given`` that were passed (not None or False) are ignored by ``method``, for the reason ``why``: refused in - ``removal`` (``NEXT_REMOVAL`` for what 0.12 deprecated).""" + ``removal`` (``NEXT_REMOVAL`` by default).""" passed = [ name for name, value in given.items() @@ -126,11 +123,10 @@ def ignored( def renamed(method: str, old: str, new: str, why: str) -> None: """Warn that ``method``'s argument ``old`` is renamed ``new``, for the - reason ``why``: deprecated in 0.13, it is refused in - ``REMOVAL_AFTER_NEXT``.""" + reason ``why``: deprecated in 0.13, it is refused in ``NEXT_REMOVAL``.""" warnings.warn( f"{method}'s {old}= is renamed {new}=, as {why}. Passing {old}= is " - f"deprecated, and {REMOVAL_AFTER_NEXT} will refuse it.", + f"deprecated, and {NEXT_REMOVAL} will refuse it.", FutureWarning, stacklevel=outside_level(), )