From 61fae0205d62a5b23d00cb005255546a728918c1 Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Wed, 12 Aug 2026 16:56:11 +0200 Subject: [PATCH 1/2] fix: raise on offline tracker config errors `@suppress(Exception)` on `OfflineEmissionsTracker.__init__` swallowed configuration errors such as a missing `output_dir`, returning a half-built object with no `_start_time`, `_hardware` or `_scheduler`. `start()`/`stop()` then failed silently and no emissions were recorded. Construction now raises, matching `EmissionsTracker`. The suppression on `start`/`flush`/`stop` is kept, so runtime measurement errors still cannot crash a user's job. Co-Authored-By: Claude Opus 5 (1M context) --- codecarbon/emissions_tracker.py | 1 - tests/test_offline_emissions_tracker.py | 14 +++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/codecarbon/emissions_tracker.py b/codecarbon/emissions_tracker.py index 96ed00c91..4eb146a46 100644 --- a/codecarbon/emissions_tracker.py +++ b/codecarbon/emissions_tracker.py @@ -1309,7 +1309,6 @@ class OfflineEmissionsTracker(BaseEmissionsTracker): _country_iso_code = None _country_name, _region, country_2letter_iso_code = None, None, None - @suppress(Exception) def __init__( self, *args, diff --git a/tests/test_offline_emissions_tracker.py b/tests/test_offline_emissions_tracker.py index 36447409d..0f5246237 100644 --- a/tests/test_offline_emissions_tracker.py +++ b/tests/test_offline_emissions_tracker.py @@ -6,7 +6,7 @@ import pandas as pd -from codecarbon.emissions_tracker import OfflineEmissionsTracker +from codecarbon.emissions_tracker import EmissionsTracker, OfflineEmissionsTracker from tests.testutils import get_custom_mock_open, get_test_data_source @@ -68,6 +68,18 @@ def test_offline_tracker_task(self): self.assertGreater(task_emission_data.emissions, 0.0) self.assertEqual(task_emission_data.country_name, None) + def test_offline_tracker_raises_on_invalid_output_dir(self): + # Configuration errors must not be swallowed by the constructor, + # otherwise the user gets a half-built tracker that silently does + # nothing. Same semantics as the online EmissionsTracker. + with self.assertRaises(OSError): + OfflineEmissionsTracker( + country_iso_code="FRA", + output_dir=str(self.temp_path / "does_not_exist"), + ) + with self.assertRaises(OSError): + EmissionsTracker(output_dir=str(self.temp_path / "does_not_exist")) + def test_resolve_offline_country_name_logs_on_invalid_iso(self): tracker = OfflineEmissionsTracker( country_iso_code="INVALID", From 85eedc84a6ea23742cadcf636a7fa00e5136e0ad Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Wed, 12 Aug 2026 19:47:28 +0200 Subject: [PATCH 2/2] docs: note that offline tracker constructor errors now raise --- docs/reference/api.md | 7 +++++++ tests/test_offline_emissions_tracker.py | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/docs/reference/api.md b/docs/reference/api.md index 15117b123..f8561acb3 100644 --- a/docs/reference/api.md +++ b/docs/reference/api.md @@ -8,6 +8,13 @@ Parameters can be set via `EmissionsTracker()`, `OfflineEmissionsTracker()`, the PUE is a multiplication factor provided by the user. Old datacenters have PUE up to 2.2, new greener ones as low as 1.1. +!!! warning "Constructor errors (changed in v3.4.0)" + `OfflineEmissionsTracker(...)` used to swallow every exception raised while + building the tracker, returning a half-initialised object that silently + recorded nothing. It now propagates those errors, like `EmissionsTracker` + always did — for example an `output_dir` that does not exist raises instead of + logging. Code that relied on the constructor never raising needs a `try`/`except`. + !!! note "GPU selection" If you use `CUDA_VISIBLE_DEVICES` or `ROCR_VISIBLE_DEVICES` to set GPUs, CodeCarbon will automatically populate `gpu_ids`. Manual `gpu_ids` overrides this. diff --git a/tests/test_offline_emissions_tracker.py b/tests/test_offline_emissions_tracker.py index 0f5246237..c13c1c61d 100644 --- a/tests/test_offline_emissions_tracker.py +++ b/tests/test_offline_emissions_tracker.py @@ -80,6 +80,12 @@ def test_offline_tracker_raises_on_invalid_output_dir(self): with self.assertRaises(OSError): EmissionsTracker(output_dir=str(self.temp_path / "does_not_exist")) + def test_offline_tracker_raises_on_invalid_region(self): + # A second, offline-specific constructor path: the region check runs + # before `super().__init__`, so it also has to reach the caller. + with self.assertRaises(AssertionError): + OfflineEmissionsTracker(country_iso_code="FRA", region=123) + def test_resolve_offline_country_name_logs_on_invalid_iso(self): tracker = OfflineEmissionsTracker( country_iso_code="INVALID",