Skip to content

Commit f44672f

Browse files
authored
Merge pull request #980 from mlco2/fix/979_electricitymaps_api
Switch from CO2Signal to ElectricityMaps
2 parents 3e9fc9f + c5f70d7 commit f44672f

38 files changed

+530
-289
lines changed

codecarbon/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "3.1.0"
1+
__version__ = "3.1.1"
Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,37 @@
55
from codecarbon.core.units import EmissionsPerKWh, Energy
66
from codecarbon.external.geography import GeoMetadata
77

8-
URL: str = "https://api.co2signal.com/v1/latest"
9-
CO2_SIGNAL_API_TIMEOUT: int = 30
8+
URL: str = "https://api.electricitymaps.com/v3/carbon-intensity/latest"
9+
ELECTRICITYMAPS_API_TIMEOUT: int = 30
1010

1111

1212
def get_emissions(
13-
energy: Energy, geo: GeoMetadata, co2_signal_api_token: str = ""
13+
energy: Energy, geo: GeoMetadata, electricitymaps_api_token: str = ""
1414
) -> float:
1515
"""
1616
Calculate the CO2 emissions based on energy consumption and geographic location.
1717
18-
This function retrieves the carbon intensity (in grams of CO2 per kWh) from the CO2
19-
Signal API based on the geographic location provided. It then calculates the total
20-
CO2 emissions for a given amount of energy consumption.
18+
This function retrieves the carbon intensity (in grams of CO2 per kWh) from the
19+
Electricity Maps API (formerly CO2 Signal) based on the geographic location provided.
20+
It then calculates the total CO2 emissions for a given amount of energy consumption.
2121
2222
Args:
2323
energy (Energy):
2424
An object representing the energy consumption in kilowatt-hours (kWh).
2525
geo (GeoMetadata):
2626
Geographic metadata, including either latitude/longitude
2727
or a country code.
28-
co2_signal_api_token (str, optional):
29-
The API token for authenticating with the CO2 Signal API (default is an empty string).
28+
electricitymaps_api_token (str, optional):
29+
The API token for authenticating with the Electricity Maps API (default is an empty string).
3030
3131
Returns:
3232
float:
3333
The total CO2 emissions in kilograms based on the provided energy consumption and
3434
carbon intensity of the specified geographic location.
3535
3636
Raises:
37-
CO2SignalAPIError:
38-
If the CO2 Signal API request fails or returns an error.
37+
ElectricityMapsAPIError:
38+
If the Electricity Maps API request fails or returns an error.
3939
"""
4040
params: Dict[str, Any]
4141
if geo.latitude:
@@ -45,18 +45,25 @@ def get_emissions(
4545
resp = requests.get(
4646
URL,
4747
params=params,
48-
headers={"auth-token": co2_signal_api_token},
49-
timeout=CO2_SIGNAL_API_TIMEOUT,
48+
headers={"auth-token": electricitymaps_api_token},
49+
timeout=ELECTRICITYMAPS_API_TIMEOUT,
5050
)
5151
if resp.status_code != 200:
5252
message = resp.json().get("error") or resp.json().get("message")
53-
raise CO2SignalAPIError(message)
54-
carbon_intensity_g_per_kWh = resp.json()["data"]["carbonIntensity"]
53+
raise ElectricityMapsAPIError(message)
54+
55+
# API v3 response structure: carbonIntensity is at the root level
56+
response_data = resp.json()
57+
carbon_intensity_g_per_kWh = response_data.get("carbonIntensity")
58+
59+
if carbon_intensity_g_per_kWh is None:
60+
raise ElectricityMapsAPIError("No carbonIntensity data in response")
61+
5562
emissions_per_kWh: EmissionsPerKWh = EmissionsPerKWh.from_g_per_kWh(
5663
carbon_intensity_g_per_kWh
5764
)
5865
return emissions_per_kWh.kgs_per_kWh * energy.kWh
5966

6067

61-
class CO2SignalAPIError(Exception):
68+
class ElectricityMapsAPIError(Exception):
6269
pass

codecarbon/core/emissions.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
import pandas as pd
1212

13-
from codecarbon.core import co2_signal
13+
from codecarbon.core import electricitymaps_api
1414
from codecarbon.core.units import EmissionsPerKWh, Energy
1515
from codecarbon.external.geography import CloudMetadata, GeoMetadata
1616
from codecarbon.external.logger import logger
@@ -19,10 +19,25 @@
1919

2020
class Emissions:
2121
def __init__(
22-
self, data_source: DataSource, co2_signal_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
25-
self._co2_signal_api_token = co2_signal_api_token
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+
40+
self._electricitymaps_api_token = electricitymaps_api_token
2641

2742
def get_cloud_emissions(
2843
self, energy: Energy, cloud: CloudMetadata, geo: GeoMetadata = None
@@ -123,12 +138,19 @@ def get_private_infra_emissions(self, energy: Energy, geo: GeoMetadata) -> float
123138
:param geo: Country and region metadata
124139
:return: CO2 emissions in kg
125140
"""
126-
if self._co2_signal_api_token:
141+
if self._electricitymaps_api_token:
127142
try:
128-
return co2_signal.get_emissions(energy, geo, self._co2_signal_api_token)
143+
emissions = electricitymaps_api.get_emissions(
144+
energy, geo, self._electricitymaps_api_token
145+
)
146+
logger.debug(
147+
"electricitymaps_api.get_emissions: "
148+
+ f"Retrieved emissions for {geo.country_name} using Electricity Maps API :{emissions * 1000} g CO2eq"
149+
)
150+
return emissions
129151
except Exception as e:
130152
logger.error(
131-
"co2_signal.get_emissions: "
153+
"electricitymaps_api.get_emissions: "
132154
+ str(e)
133155
+ " >>> Using CodeCarbon's data."
134156
)

codecarbon/emissions_tracker.py

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,10 @@ def __init__(
171171
emissions_endpoint: Optional[str] = _sentinel,
172172
experiment_id: Optional[str] = _sentinel,
173173
experiment_name: Optional[str] = _sentinel,
174-
co2_signal_api_token: Optional[str] = _sentinel,
174+
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,
@@ -221,8 +224,9 @@ def __init__(
221224
data.
222225
:param experiment_id: Id of the experiment.
223226
:param experiment_name: Label of the experiment
224-
:param co2_signal_api_token: API token for co2signal.com (requires sign-up for
225-
free beta)
227+
: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.
226230
:param tracking_mode: One of "process" or "machine" in order to measure the
227231
power consumption due to the entire machine or to try and
228232
isolate the tracked processe's in isolation.
@@ -268,7 +272,31 @@ def __init__(
268272
self._set_from_conf(api_call_interval, "api_call_interval", 8, int)
269273
self._set_from_conf(api_endpoint, "api_endpoint", "https://api.codecarbon.io")
270274
self._set_from_conf(api_key, "api_key", "api_key")
271-
self._set_from_conf(co2_signal_api_token, "co2_signal_api_token")
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+
285+
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+
272300
self._set_from_conf(emissions_endpoint, "emissions_endpoint")
273301
self._set_from_conf(experiment_name, "experiment_name", "base")
274302
self._set_from_conf(gpu_ids, "gpu_ids")
@@ -378,7 +406,7 @@ def __init__(
378406
self._conf["provider"] = cloud.provider
379407

380408
self._emissions: Emissions = Emissions(
381-
self._data_source, self._co2_signal_api_token
409+
self._data_source, self._electricitymaps_api_token
382410
)
383411
self._init_output_methods(api_key=self._api_key)
384412

@@ -956,7 +984,7 @@ def __init__(
956984
See https://github.com/mlco2/codecarbon/
957985
blob/master/codecarbon/data/cloud/impact.csv
958986
for a list of cloud regions.
959-
:param country_2letter_iso_code: For use with the CO2Signal emissions API.
987+
:param country_2letter_iso_code: For use with the Electricity Maps emissions API.
960988
See http://api.electricitymap.org/v3/zones for
961989
a list of codes and their corresponding
962990
locations.
@@ -1095,7 +1123,10 @@ def track_emissions(
10951123
emissions_endpoint: Optional[str] = _sentinel,
10961124
experiment_id: Optional[str] = _sentinel,
10971125
experiment_name: Optional[str] = _sentinel,
1098-
co2_signal_api_token: Optional[str] = _sentinel,
1126+
electricitymaps_api_token: Optional[str] = _sentinel,
1127+
co2_signal_api_token: Optional[
1128+
str
1129+
] = _sentinel, # Deprecated, use electricitymaps_api_token
10991130
tracking_mode: Optional[str] = _sentinel,
11001131
log_level: Optional[Union[int, str]] = _sentinel,
11011132
on_csv_write: Optional[str] = _sentinel,
@@ -1150,8 +1181,9 @@ def track_emissions(
11501181
data.
11511182
:param experiment_id: Id of the experiment.
11521183
:param experiment_name: Label of the experiment
1153-
:param co2_signal_api_token: API token for co2signal.com (requires sign-up for
1154-
free beta)
1184+
: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.
11551187
:param tracking_mode: One of "process" or "machine" in order to measure the
11561188
power consumption due to the entire machine or to try and
11571189
isolate the tracked processe's in isolation.
@@ -1180,7 +1212,7 @@ def track_emissions(
11801212
See https://github.com/mlco2/codecarbon/
11811213
blob/master/codecarbon/data/cloud/impact.csv
11821214
for a list of cloud regions.
1183-
:param country_2letter_iso_code: For use with the CO2Signal emissions API.
1215+
:param country_2letter_iso_code: For use with the Electricity Maps emissions API.
11841216
See http://api.electricitymap.org/v3/zones for
11851217
a list of codes and their corresponding
11861218
locations.
@@ -1199,6 +1231,17 @@ def _decorate(fn: Callable):
11991231
@wraps(fn)
12001232
def wrapped_fn(*args, **kwargs):
12011233
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+
12021245
if offline and offline is not _sentinel:
12031246
if (country_iso_code is None or country_iso_code is _sentinel) and (
12041247
cloud_provider is None or cloud_provider is _sentinel
@@ -1217,7 +1260,7 @@ def wrapped_fn(*args, **kwargs):
12171260
prometheus_url=prometheus_url,
12181261
output_handlers=output_handlers,
12191262
gpu_ids=gpu_ids,
1220-
co2_signal_api_token=co2_signal_api_token,
1263+
electricitymaps_api_token=_electricitymaps_token,
12211264
tracking_mode=tracking_mode,
12221265
log_level=log_level,
12231266
on_csv_write=on_csv_write,
@@ -1256,7 +1299,7 @@ def wrapped_fn(*args, **kwargs):
12561299
emissions_endpoint=emissions_endpoint,
12571300
experiment_id=experiment_id,
12581301
experiment_name=experiment_name,
1259-
co2_signal_api_token=co2_signal_api_token,
1302+
electricitymaps_api_token=_electricitymaps_token,
12601303
tracking_mode=tracking_mode,
12611304
log_level=log_level,
12621305
on_csv_write=on_csv_write,

docs/.buildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Sphinx build info version 1
22
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
3-
config: 5444747ce17d7942890303a4e6aa98ce
3+
config: 1179cfac2a69f75fdb97976cfa9a706e
44
tags: 645f666f9bcd5a90fca523b33c5a78b7

docs/_sources/parameters.rst.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ Input Parameters
3131
* - log_level
3232
- | Global codecarbon log level (by order of verbosity): "debug", "info" (default),
3333
| "warning", "error", or "critical"
34-
* - co2_signal_api_token
35-
- | API token for co2signal.com (requires sign-up for free beta)
34+
* - electricitymaps_api_token
35+
- | API token for electricitymaps.com (formerly co2signal.com)
3636
* - pue
3737
- | PUE (Power Usage Effectiveness) of the data center
3838
| where the experiment is being run.

docs/_sources/usage.rst.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ For instance:
215215
[codecarbon]
216216
save_to_file = true
217217
output_dir = /Users/victor/emissions
218-
co2_signal_api_token=script-overwrite
218+
electricitymaps_api_token=script-overwrite
219219
experiment_id = 235b1da5-aaaa-aaaa-aaaa-893681599d2c
220220
log_level = DEBUG
221221
tracking_mode = process
@@ -235,7 +235,7 @@ For instance:
235235
EmissionsTracker(
236236
api_call_interval=4,
237237
save_to_api=True,
238-
co2_signal_api_token="some-token")
238+
electricitymaps_api_token="some-token")
239239
240240
Yields attributes:
241241

@@ -251,7 +251,7 @@ Yields attributes:
251251
"tracking_mode": "process", # from ./.codecarbon.config
252252
"emissions_endpoint": "localhost:7777", # from ~/.codecarbon.config
253253
"output_dir": "/Users/victor/emissions", # from ./.codecarbon.config
254-
"co2_signal_api_token": "some-token", # from script (override ./.codecarbon.config)
254+
"electricitymaps_api_token": "some-token", # from script (override ./.codecarbon.config)
255255
"gpu_ids": [0, 1], # from environment variable
256256
}
257257

docs/_static/documentation_options.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const DOCUMENTATION_OPTIONS = {
2-
VERSION: '3.1.0',
2+
VERSION: '3.1.1',
33
LANGUAGE: 'en',
44
COLLAPSE_INDEX: false,
55
BUILDER: 'html',

docs/advanced_installation.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
77

88
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
9-
<title>Advanced Installation &mdash; CodeCarbon 3.1.0 documentation</title>
9+
<title>Advanced Installation &mdash; CodeCarbon 3.1.1 documentation</title>
1010
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=03e43079" />
1111
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=e59714d7" />
1212

1313

1414
<script src="_static/jquery.js?v=5d32c60e"></script>
1515
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
16-
<script src="_static/documentation_options.js?v=dd1205ac"></script>
16+
<script src="_static/documentation_options.js?v=796a81b5"></script>
1717
<script src="_static/doctools.js?v=9bcbadda"></script>
1818
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
1919
<script src="_static/js/theme.js"></script>

docs/api.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
77

88
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
9-
<title>CodeCarbon API &mdash; CodeCarbon 3.1.0 documentation</title>
9+
<title>CodeCarbon API &mdash; CodeCarbon 3.1.1 documentation</title>
1010
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=03e43079" />
1111
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=e59714d7" />
1212

1313

1414
<script src="_static/jquery.js?v=5d32c60e"></script>
1515
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
16-
<script src="_static/documentation_options.js?v=dd1205ac"></script>
16+
<script src="_static/documentation_options.js?v=796a81b5"></script>
1717
<script src="_static/doctools.js?v=9bcbadda"></script>
1818
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
1919
<script src="_static/js/theme.js"></script>

0 commit comments

Comments
 (0)