From 7d1fe192566fe814b4f00d97a7da433747c3ae58 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 20:09:39 +0000 Subject: [PATCH] maint: close search.log handler + four CLI-noise warning fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five mechanical fixes from the 2026-08-06 full-suite -W all audit (#1495): - configure_handler now closes the search.log FileHandler beside the removeHandler in its finally block, ending the per-search ResourceWarning (44/run) and descriptor leak. - Fit.arrays/Fit.hdus carry the overlaps= annotations SQLAlchemy's SAWarning suggests: HDU extends Array by joined-table inheritance, so those relationships intentionally share the array.fit_id column. - Nautilus samples_info_from reads the log_z property instead of the deprecated evidence() method (which just returns log_z). - LBFGS no longer forwards the disp/iprint options scipy 1.15 deprecated for L-BFGS-B (removal slated for 1.18); the constructor still accepts both, and disp remains live for plain BFGS. - test_fork_context.py filters the fork-in-a-multi-threaded-process DeprecationWarning (CPython 3.12+) and JAX's equivalent RuntimeWarning module-locally — the fork-pinned pool is the module's subject. Full suite: 1884 passed, 3 skipped. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_018WXzGhe8dt8ANs4xaXHcuD --- autofit/database/model/fit.py | 7 +++++++ autofit/non_linear/search/abstract_search.py | 1 + autofit/non_linear/search/mle/bfgs/search.py | 12 ++++++++++++ autofit/non_linear/search/nest/nautilus/search.py | 2 +- .../non_linear/search/optimize/test_lbfgs.py | 15 +++++++++++---- test_autofit/non_linear/test_fork_context.py | 10 ++++++++++ 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/autofit/database/model/fit.py b/autofit/database/model/fit.py index 8b10a3fe9..86762062c 100644 --- a/autofit/database/model/fit.py +++ b/autofit/database/model/fit.py @@ -327,15 +327,22 @@ def model(self, model: AbstractPriorModel): lazy="joined", foreign_keys=[JSON.fit_id], ) + # ``overlaps=``: ``HDU`` extends ``Array`` by joined-table inheritance, so + # ``Fit.arrays``/``Array.fit`` and ``Fit.hdus``/``HDU.fit`` all write the + # same ``array.fit_id`` column. The overlap is intentional (an HDU is an + # Array); the annotations carry exactly the names SQLAlchemy's SAWarning + # from ``configure_mappers()`` suggests. arrays: Mapped[List[Array]] = sa.orm.relationship( "Array", lazy="joined", foreign_keys=[Array.fit_id], + overlaps="fit", ) hdus: Mapped[List[HDU]] = sa.orm.relationship( "HDU", lazy="joined", foreign_keys=[HDU.fit_id], + overlaps="arrays,fit", ) fits: Mapped[List[Fits]] = sa.orm.relationship( "Fits", diff --git a/autofit/non_linear/search/abstract_search.py b/autofit/non_linear/search/abstract_search.py index 54237cfbc..92569c3c2 100644 --- a/autofit/non_linear/search/abstract_search.py +++ b/autofit/non_linear/search/abstract_search.py @@ -134,6 +134,7 @@ def decorated(self, *args, **kwargs): return func(self, *args, **kwargs) finally: root_logger.removeHandler(handler) + handler.close() return decorated diff --git a/autofit/non_linear/search/mle/bfgs/search.py b/autofit/non_linear/search/mle/bfgs/search.py index 98168534c..4bfe4c365 100644 --- a/autofit/non_linear/search/mle/bfgs/search.py +++ b/autofit/non_linear/search/mle/bfgs/search.py @@ -405,3 +405,15 @@ class LBFGS(AbstractBFGS): """ method = "L-BFGS-B" + + # SciPy 1.15 deprecated the L-BFGS-B ``disp`` / ``iprint`` options (removal + # slated for 1.18) — the solver no longer emits its Fortran-side verbose + # output, so passing them buys nothing but a DeprecationWarning per + # ``minimize`` call. The constructor still accepts both for API stability; + # they simply never reach scipy for this method. + @property + def options(self): + options = dict(super().options) + del options["disp"] + del options["iprint"] + return options diff --git a/autofit/non_linear/search/nest/nautilus/search.py b/autofit/non_linear/search/nest/nautilus/search.py index 2cb9dce56..92e905264 100644 --- a/autofit/non_linear/search/nest/nautilus/search.py +++ b/autofit/non_linear/search/nest/nautilus/search.py @@ -532,7 +532,7 @@ def output_search_internal(self, search_internal): def samples_info_from(self, search_internal=None): return { - "log_evidence": search_internal.evidence(), + "log_evidence": search_internal.log_z, "total_samples": int(search_internal.n_like), "total_accepted_samples": int(search_internal.n_like), "time": self.timer.time if self.timer else None, diff --git a/test_autofit/non_linear/search/optimize/test_lbfgs.py b/test_autofit/non_linear/search/optimize/test_lbfgs.py index e7b77950b..7e1168efe 100644 --- a/test_autofit/non_linear/search/optimize/test_lbfgs.py +++ b/test_autofit/non_linear/search/optimize/test_lbfgs.py @@ -28,9 +28,14 @@ def test__explicit_params(): assert search.options["eps"] == 4. assert search.options["maxfun"] == 25000 assert search.options["maxiter"] == 26000 - assert search.options["iprint"] == -2 assert search.options["maxls"] == 21 - assert search.options["disp"] is True + + # Accepted for API stability but never forwarded: scipy deprecated the + # L-BFGS-B ``disp`` / ``iprint`` options in 1.15. + assert search.iprint == -2 + assert search.disp is True + assert "iprint" not in search.options + assert "disp" not in search.options assert isinstance(search.initializer, af.InitializerBall) assert search.initializer.lower_limit == 0.2 assert search.initializer.upper_limit == 0.8 @@ -46,7 +51,9 @@ def test__explicit_params(): assert search.options["eps"] == 1e-08 assert search.options["maxfun"] == 15000 assert search.options["maxiter"] == 15000 - assert search.options["iprint"] == -1 assert search.options["maxls"] == 20 - assert search.options["disp"] is False + assert search.iprint == -1 + assert search.disp is False + assert "iprint" not in search.options + assert "disp" not in search.options assert isinstance(search.initializer, af.InitializerBall) diff --git a/test_autofit/non_linear/test_fork_context.py b/test_autofit/non_linear/test_fork_context.py index c276f0512..5857cb205 100644 --- a/test_autofit/non_linear/test_fork_context.py +++ b/test_autofit/non_linear/test_fork_context.py @@ -13,6 +13,16 @@ prior_transform, ) +# pytest's own machinery keeps a background thread alive, so CPython 3.12+ +# flags every os.fork() here as fork-in-a-multi-threaded-process, and JAX +# (itself multithreaded once imported) registers an equivalent RuntimeWarning +# hook. The tests exercise the fork-pinned pool deliberately; the deadlock +# caveat does not apply to these short-lived, likelihood-only workers. +pytestmark = [ + pytest.mark.filterwarnings("ignore:This process:DeprecationWarning"), + pytest.mark.filterwarnings(r"ignore:os\.fork\(\) was called:RuntimeWarning"), +] + pins_fork = ( sys.platform != "darwin" and "fork" in multiprocessing.get_all_start_methods()