Skip to content

Commit eb76fc1

Browse files
author
benoit-cty
committed
ElectricityMaps_Backward_compatibility
1 parent a5dd83e commit eb76fc1

5 files changed

Lines changed: 319 additions & 3 deletions

codecarbon/core/emissions.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,24 @@
1919

2020
class Emissions:
2121
def __init__(
22-
self, data_source: DataSource, electricitymaps_api_token: Optional[str] = None
22+
self,
23+
data_source: DataSource,
24+
electricitymaps_api_token: Optional[str] = None,
25+
co2_signal_api_token: Optional[
26+
str
27+
] = None, # Deprecated, for backward compatibility
2328
):
2429
self._data_source = data_source
30+
31+
# Handle backward compatibility
32+
if co2_signal_api_token is not None:
33+
logger.warning(
34+
"Parameter 'co2_signal_api_token' is deprecated and will be removed in a future version. "
35+
"Please use 'electricitymaps_api_token' instead."
36+
)
37+
if electricitymaps_api_token is None:
38+
electricitymaps_api_token = co2_signal_api_token
39+
2540
self._electricitymaps_api_token = electricitymaps_api_token
2641

2742
def get_cloud_emissions(

codecarbon/emissions_tracker.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ def __init__(
172172
experiment_id: Optional[str] = _sentinel,
173173
experiment_name: Optional[str] = _sentinel,
174174
electricitymaps_api_token: Optional[str] = _sentinel,
175+
co2_signal_api_token: Optional[
176+
str
177+
] = _sentinel, # Deprecated, use electricitymaps_api_token
175178
tracking_mode: Optional[str] = _sentinel,
176179
log_level: Optional[Union[int, str]] = _sentinel,
177180
on_csv_write: Optional[str] = _sentinel,
@@ -222,6 +225,8 @@ def __init__(
222225
:param experiment_id: Id of the experiment.
223226
:param experiment_name: Label of the experiment
224227
:param electricitymaps_api_token: API token for electricitymaps.com (formerly co2signal.com)
228+
:param co2_signal_api_token: [DEPRECATED] Use electricitymaps_api_token instead.
229+
Old parameter name for backward compatibility.
225230
:param tracking_mode: One of "process" or "machine" in order to measure the
226231
power consumption due to the entire machine or to try and
227232
isolate the tracked processe's in isolation.
@@ -267,7 +272,31 @@ def __init__(
267272
self._set_from_conf(api_call_interval, "api_call_interval", 8, int)
268273
self._set_from_conf(api_endpoint, "api_endpoint", "https://api.codecarbon.io")
269274
self._set_from_conf(api_key, "api_key", "api_key")
275+
276+
# Handle backward compatibility for co2_signal_api_token
277+
if co2_signal_api_token is not _sentinel:
278+
logger.warning(
279+
"Parameter 'co2_signal_api_token' is deprecated and will be removed in a future version. "
280+
"Please use 'electricitymaps_api_token' instead."
281+
)
282+
if electricitymaps_api_token is _sentinel:
283+
electricitymaps_api_token = co2_signal_api_token
284+
270285
self._set_from_conf(electricitymaps_api_token, "electricitymaps_api_token")
286+
# Also check for old config name for backward compatibility
287+
if (
288+
not hasattr(self, "_electricitymaps_api_token")
289+
or self._electricitymaps_api_token is None
290+
):
291+
self._set_from_conf(_sentinel, "co2_signal_api_token", prevent_setter=True)
292+
old_token = self._external_conf.get("co2_signal_api_token")
293+
if old_token:
294+
logger.warning(
295+
"Configuration parameter 'co2_signal_api_token' is deprecated. "
296+
"Please update your config to use 'electricitymaps_api_token' instead."
297+
)
298+
self._electricitymaps_api_token = old_token
299+
271300
self._set_from_conf(emissions_endpoint, "emissions_endpoint")
272301
self._set_from_conf(experiment_name, "experiment_name", "base")
273302
self._set_from_conf(gpu_ids, "gpu_ids")
@@ -1095,6 +1124,9 @@ def track_emissions(
10951124
experiment_id: Optional[str] = _sentinel,
10961125
experiment_name: Optional[str] = _sentinel,
10971126
electricitymaps_api_token: Optional[str] = _sentinel,
1127+
co2_signal_api_token: Optional[
1128+
str
1129+
] = _sentinel, # Deprecated, use electricitymaps_api_token
10981130
tracking_mode: Optional[str] = _sentinel,
10991131
log_level: Optional[Union[int, str]] = _sentinel,
11001132
on_csv_write: Optional[str] = _sentinel,
@@ -1150,6 +1182,8 @@ def track_emissions(
11501182
:param experiment_id: Id of the experiment.
11511183
:param experiment_name: Label of the experiment
11521184
:param electricitymaps_api_token: API token for electricitymaps.com (formerly co2signal.com)
1185+
:param co2_signal_api_token: [DEPRECATED] Use electricitymaps_api_token instead.
1186+
Old parameter name for backward compatibility.
11531187
:param tracking_mode: One of "process" or "machine" in order to measure the
11541188
power consumption due to the entire machine or to try and
11551189
isolate the tracked processe's in isolation.
@@ -1197,6 +1231,17 @@ def _decorate(fn: Callable):
11971231
@wraps(fn)
11981232
def wrapped_fn(*args, **kwargs):
11991233
fn_result = None
1234+
1235+
# Handle backward compatibility for co2_signal_api_token
1236+
_electricitymaps_token = electricitymaps_api_token
1237+
if co2_signal_api_token is not _sentinel:
1238+
logger.warning(
1239+
"Parameter 'co2_signal_api_token' is deprecated and will be removed in a future version. "
1240+
"Please use 'electricitymaps_api_token' instead."
1241+
)
1242+
if electricitymaps_api_token is _sentinel:
1243+
_electricitymaps_token = co2_signal_api_token
1244+
12001245
if offline and offline is not _sentinel:
12011246
if (country_iso_code is None or country_iso_code is _sentinel) and (
12021247
cloud_provider is None or cloud_provider is _sentinel
@@ -1215,7 +1260,7 @@ def wrapped_fn(*args, **kwargs):
12151260
prometheus_url=prometheus_url,
12161261
output_handlers=output_handlers,
12171262
gpu_ids=gpu_ids,
1218-
electricitymaps_api_token=electricitymaps_api_token,
1263+
electricitymaps_api_token=_electricitymaps_token,
12191264
tracking_mode=tracking_mode,
12201265
log_level=log_level,
12211266
on_csv_write=on_csv_write,
@@ -1254,7 +1299,7 @@ def wrapped_fn(*args, **kwargs):
12541299
emissions_endpoint=emissions_endpoint,
12551300
experiment_id=experiment_id,
12561301
experiment_name=experiment_name,
1257-
electricitymaps_api_token=electricitymaps_api_token,
1302+
electricitymaps_api_token=_electricitymaps_token,
12581303
tracking_mode=tracking_mode,
12591304
log_level=log_level,
12601305
on_csv_write=on_csv_write,
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Backward Compatibility for CO2 Signal → Electricity Maps Migration
2+
3+
## Summary
4+
5+
The API formerly known as CO2 Signal has been rebranded to Electricity Maps, and their API has been updated from v1 to v3. To maintain backward compatibility while adopting the new naming, we've implemented the following changes:
6+
7+
## Parameter Renaming
8+
9+
| Old Parameter Name | New Parameter Name | Status |
10+
|-------------------|-------------------|---------|
11+
| `co2_signal_api_token` | `electricitymaps_api_token` | Old name deprecated but still supported |
12+
| `CO2SignalAPIError` | `ElectricityMapsAPIError` | Old name removed |
13+
| `co2_signal` module | `electricitymaps_api` module | Old name removed |
14+
15+
## Backward Compatibility Features
16+
17+
### 1. Parameter Aliases
18+
19+
The old parameter name `co2_signal_api_token` is still accepted in all APIs:
20+
21+
```python
22+
# Both work, but the old name emits a deprecation warning
23+
tracker = EmissionsTracker(co2_signal_api_token="your-token") # Deprecated
24+
tracker = EmissionsTracker(electricitymaps_api_token="your-token") # Recommended
25+
```
26+
27+
### 2. Configuration File Support
28+
29+
Configuration files can use either the old or new parameter name:
30+
31+
```ini
32+
[codecarbon]
33+
# Old name (deprecated, but still works)
34+
co2_signal_api_token=your-token
35+
36+
# New name (recommended)
37+
electricitymaps_api_token=your-token
38+
```
39+
40+
### 3. Deprecation Warnings
41+
42+
When using the old parameter name, a warning is logged:
43+
44+
```
45+
WARNING: Parameter 'co2_signal_api_token' is deprecated and will be removed in a future version.
46+
Please use 'electricitymaps_api_token' instead.
47+
```
48+
49+
## Migration Guide
50+
51+
### For Users
52+
53+
**No immediate action required.** Your existing code will continue to work, but you'll see deprecation warnings.
54+
55+
To migrate:
56+
57+
1. Replace `co2_signal_api_token` with `electricitymaps_api_token` in your code
58+
2. Update your `.codecarbon.config` files to use the new parameter name
59+
3. Update your environment variables from `CODECARBON_CO2_SIGNAL_API_TOKEN` to `CODECARBON_ELECTRICITYMAPS_API_TOKEN`
60+
61+
Example migration:
62+
63+
```python
64+
# Before
65+
from codecarbon import EmissionsTracker
66+
67+
tracker = EmissionsTracker(
68+
co2_signal_api_token="your-token"
69+
)
70+
71+
# After
72+
from codecarbon import EmissionsTracker
73+
74+
tracker = EmissionsTracker(
75+
electricitymaps_api_token="your-token"
76+
)
77+
```
78+
79+
### For Developers
80+
81+
When both old and new parameters are provided:
82+
- The new parameter takes precedence
83+
- A deprecation warning is still emitted for the old parameter
84+
85+
Implementation details:
86+
- `BaseEmissionsTracker.__init__()` handles the parameter migration
87+
- `Emissions.__init__()` handles the parameter migration for the core class
88+
- `track_emissions()` decorator handles the parameter migration
89+
- Configuration file reading checks both parameter names
90+
91+
## Timeline
92+
93+
- **v3.1.0**: New parameter introduced, old parameter deprecated
94+
- **v4.0.0** (planned): Old parameter will be removed
95+
96+
## Testing
97+
98+
Backward compatibility is tested in:
99+
- `tests/test_backward_compatibility.py` - Tests parameter aliases
100+
- `tests/test_config_backward_compatibility.py` - Tests configuration file support
101+
102+
Run tests with:
103+
```bash
104+
uv run pytest tests/test_backward_compatibility.py tests/test_config_backward_compatibility.py -v
105+
```
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""
2+
Tests for backward compatibility with deprecated parameter names.
3+
"""
4+
5+
import unittest
6+
from unittest.mock import patch
7+
8+
from codecarbon.core.emissions import Emissions
9+
from codecarbon.emissions_tracker import EmissionsTracker
10+
from codecarbon.input import DataSource
11+
12+
13+
class TestElectricityMapsBackwardCompatibility(unittest.TestCase):
14+
"""Test that old parameter names still work with deprecation warnings."""
15+
16+
def test_emissions_co2_signal_api_token_deprecated(self):
17+
"""Test that co2_signal_api_token still works in Emissions class."""
18+
data_source = DataSource()
19+
20+
# Using the old parameter name should emit a warning
21+
with self.assertLogs("codecarbon", level="WARNING") as log:
22+
emissions = Emissions(data_source, co2_signal_api_token="test-token")
23+
24+
# Check that the token was set correctly
25+
self.assertEqual(emissions._electricitymaps_api_token, "test-token")
26+
27+
# Check that a deprecation warning was logged
28+
self.assertTrue(
29+
any("deprecated" in message.lower() for message in log.output),
30+
"Expected deprecation warning not found",
31+
)
32+
33+
def test_emissions_new_parameter_takes_precedence(self):
34+
"""Test that new parameter takes precedence over old one."""
35+
data_source = DataSource()
36+
37+
with self.assertLogs("codecarbon", level="WARNING"):
38+
emissions = Emissions(
39+
data_source,
40+
electricitymaps_api_token="new-token",
41+
co2_signal_api_token="old-token",
42+
)
43+
44+
# New parameter should take precedence
45+
self.assertEqual(emissions._electricitymaps_api_token, "new-token")
46+
47+
@patch("os.path.exists", return_value=True)
48+
def test_tracker_co2_signal_api_token_deprecated(self, mock_exists):
49+
"""Test that co2_signal_api_token parameter works in EmissionsTracker."""
50+
51+
with self.assertLogs("codecarbon", level="WARNING") as log:
52+
tracker = EmissionsTracker(co2_signal_api_token="test-token")
53+
54+
# Check that the token was set correctly
55+
self.assertEqual(tracker._electricitymaps_api_token, "test-token")
56+
57+
# Check that a deprecation warning was logged
58+
self.assertTrue(
59+
any("deprecated" in message.lower() for message in log.output),
60+
"Expected deprecation warning not found",
61+
)
62+
63+
tracker.stop()
64+
65+
@patch("os.path.exists", return_value=True)
66+
def test_tracker_new_parameter_takes_precedence(self, mock_exists):
67+
"""Test that new parameter takes precedence in EmissionsTracker."""
68+
69+
with self.assertLogs("codecarbon", level="WARNING") as log:
70+
tracker = EmissionsTracker(
71+
electricitymaps_api_token="new-token", co2_signal_api_token="old-token"
72+
)
73+
74+
# New parameter should take precedence
75+
self.assertEqual(tracker._electricitymaps_api_token, "new-token")
76+
77+
# Still should warn about using deprecated parameter
78+
self.assertTrue(any("deprecated" in message.lower() for message in log.output))
79+
80+
tracker.stop()
81+
82+
83+
if __name__ == "__main__":
84+
unittest.main()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
Test backward compatibility with configuration files using old parameter names.
3+
"""
4+
5+
import unittest
6+
from textwrap import dedent
7+
from unittest.mock import patch
8+
9+
from codecarbon.emissions_tracker import EmissionsTracker
10+
from tests.testutils import get_custom_mock_open
11+
12+
13+
class TestConfigBackwardCompatibility(unittest.TestCase):
14+
"""Test that old config parameter names still work."""
15+
16+
@patch("os.path.exists", return_value=True)
17+
def test_old_config_parameter_name(self, mock_exists):
18+
"""Test that co2_signal_api_token in config file still works."""
19+
config_with_old_name = dedent(
20+
"""\
21+
[codecarbon]
22+
co2_signal_api_token=old-config-token
23+
"""
24+
)
25+
26+
with patch(
27+
"builtins.open", new_callable=get_custom_mock_open(config_with_old_name, "")
28+
):
29+
with self.assertLogs("codecarbon", level="WARNING") as log:
30+
tracker = EmissionsTracker()
31+
32+
# Should use the token from config
33+
self.assertEqual(tracker._electricitymaps_api_token, "old-config-token")
34+
35+
# Should warn about deprecated config parameter
36+
self.assertTrue(
37+
any("deprecated" in message.lower() for message in log.output),
38+
"Expected deprecation warning for config parameter",
39+
)
40+
41+
tracker.stop()
42+
43+
@patch("os.path.exists", return_value=True)
44+
def test_new_config_parameter_takes_precedence(self, mock_exists):
45+
"""Test that new config parameter takes precedence over old one."""
46+
config_with_both_names = dedent(
47+
"""\
48+
[codecarbon]
49+
electricitymaps_api_token=new-config-token
50+
co2_signal_api_token=old-config-token
51+
"""
52+
)
53+
54+
with patch(
55+
"builtins.open",
56+
new_callable=get_custom_mock_open(config_with_both_names, ""),
57+
):
58+
tracker = EmissionsTracker()
59+
60+
# New parameter should take precedence
61+
self.assertEqual(tracker._electricitymaps_api_token, "new-config-token")
62+
63+
tracker.stop()
64+
65+
66+
if __name__ == "__main__":
67+
unittest.main()

0 commit comments

Comments
 (0)