From c8a38b9c4f53a8aa7b1ec54afe1fbbe77bf5f8dd Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Wed, 19 Aug 2026 15:21:08 +0200 Subject: [PATCH] fix: tell the user a stopped tracker cannot be restarted start() after stop() used to log "Already started tracking" and silently do nothing, which is misleading: stop() has dropped the schedulers, released the lock and exited the output handlers, so the tracker is not tracking anything. Say so instead. Restarting is not supported and this does not add it. start() is wrapped in @suppress(Exception), so raising would be swallowed; the error is logged instead. Co-Authored-By: Claude Opus 5 (1M context) --- codecarbon/emissions_tracker.py | 10 ++++++++++ tests/test_emissions_tracker.py | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/codecarbon/emissions_tracker.py b/codecarbon/emissions_tracker.py index 96ed00c91..7915910d0 100644 --- a/codecarbon/emissions_tracker.py +++ b/codecarbon/emissions_tracker.py @@ -706,6 +706,16 @@ def start(self) -> None: ) return if self._start_time is not None: + if self._scheduler is None: + # stop() drops the schedulers, releases the lock and exits the + # output handlers, so a stopped tracker cannot be restarted. + # `start()` is wrapped in @suppress(Exception), so raising here + # would be swallowed: log instead of pretending it worked. + logger.error( + "This tracker was already stopped and cannot be restarted. " + "Create a new tracker instead." + ) + return logger.warning("Already started tracking") return diff --git a/tests/test_emissions_tracker.py b/tests/test_emissions_tracker.py index 8ab12e5d8..e2a330b4f 100644 --- a/tests/test_emissions_tracker.py +++ b/tests/test_emissions_tracker.py @@ -1108,3 +1108,20 @@ def test_cumulative_emissions_with_varying_intensity( # Verification: If it wasn't cumulative, it would be 3.0 kWh * 300 g/kWh = 0.9 kg self.assertLess(data3.emissions, 0.8) + + +class TestRestartAfterStop(unittest.TestCase): + def test_start_after_stop_is_refused(self): + tracker = OfflineEmissionsTracker( + country_iso_code="FRA", + save_to_file=False, + allow_multiple_runs=True, + measure_power_secs=10, + ) + tracker.start() + tracker.stop() + with self.assertLogs("codecarbon", level="ERROR") as logs: + tracker.start() + self.assertIn("cannot be restarted", "".join(logs.output)) + # Refused, not half-restarted: nothing was rebuilt. + self.assertIsNone(tracker._scheduler)