codecarbon 3.1.0__tar.gz → 3.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. {codecarbon-3.1.0 → codecarbon-3.1.1}/PKG-INFO +7 -3
  2. {codecarbon-3.1.0 → codecarbon-3.1.1}/README.md +6 -2
  3. codecarbon-3.1.1/codecarbon/_version.py +1 -0
  4. codecarbon-3.1.0/codecarbon/core/co2_signal.py → codecarbon-3.1.1/codecarbon/core/electricitymaps_api.py +22 -15
  5. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/emissions.py +28 -6
  6. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/emissions_tracker.py +55 -12
  7. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/file.py +59 -7
  8. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon.egg-info/PKG-INFO +7 -3
  9. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon.egg-info/SOURCES.txt +4 -2
  10. {codecarbon-3.1.0 → codecarbon-3.1.1}/pyproject.toml +1 -1
  11. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_config.py +3 -2
  12. codecarbon-3.1.1/tests/test_electricitymaps_api.py +45 -0
  13. codecarbon-3.1.1/tests/test_electricitymaps_backward_compatibility.py +80 -0
  14. codecarbon-3.1.1/tests/test_electricitymaps_config_backward_compatibility.py +63 -0
  15. codecarbon-3.1.0/codecarbon/_version.py +0 -1
  16. codecarbon-3.1.0/tests/test_co2_signal.py +0 -47
  17. {codecarbon-3.1.0 → codecarbon-3.1.1}/LICENSE +0 -0
  18. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/__init__.py +0 -0
  19. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/cli/__init__.py +0 -0
  20. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/cli/cli_utils.py +0 -0
  21. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/cli/main.py +0 -0
  22. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/__init__.py +0 -0
  23. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/api_client.py +0 -0
  24. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/cloud.py +0 -0
  25. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/config.py +0 -0
  26. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/cpu.py +0 -0
  27. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/gpu.py +0 -0
  28. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/measure.py +0 -0
  29. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/powermetrics.py +0 -0
  30. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/rapl.py +0 -0
  31. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/resource_tracker.py +0 -0
  32. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/schemas.py +0 -0
  33. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/units.py +0 -0
  34. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/core/util.py +0 -0
  35. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/canada_provinces.geojson +0 -0
  36. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/cloud/impact.csv +0 -0
  37. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/hardware/cpu_dataset_builder/amd_cpu_scrapper.py +0 -0
  38. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/hardware/cpu_dataset_builder/intel_cpu_scrapper.py +0 -0
  39. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/hardware/cpu_dataset_builder/merge_scrapped_cpu_power.py +0 -0
  40. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/hardware/cpu_power.csv +0 -0
  41. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/private_infra/2016/usa_emissions.json +0 -0
  42. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/private_infra/2023/canada_energy_mix.json +0 -0
  43. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/private_infra/carbon_intensity_per_source.json +0 -0
  44. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/data/private_infra/global_energy_mix.json +0 -0
  45. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/external/__init__.py +0 -0
  46. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/external/geography.py +0 -0
  47. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/external/hardware.py +0 -0
  48. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/external/logger.py +0 -0
  49. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/external/ram.py +0 -0
  50. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/external/scheduler.py +0 -0
  51. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/external/task.py +0 -0
  52. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/input.py +0 -0
  53. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/lock.py +0 -0
  54. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output.py +0 -0
  55. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/__init__.py +0 -0
  56. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/base_output.py +0 -0
  57. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/emissions_data.py +0 -0
  58. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/http.py +0 -0
  59. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/logger.py +0 -0
  60. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/metrics/__init__.py +0 -0
  61. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/metrics/logfire.py +0 -0
  62. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/metrics/metric_docs.py +0 -0
  63. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/output_methods/metrics/prometheus.py +0 -0
  64. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/__init__.py +0 -0
  65. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/assets/__init__.py +0 -0
  66. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/assets/car_icon.png +0 -0
  67. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/assets/house_icon.png +0 -0
  68. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/assets/tv_icon.png +0 -0
  69. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/carbonboard.py +0 -0
  70. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/carbonboard_on_api.py +0 -0
  71. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/components.py +0 -0
  72. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/data.py +0 -0
  73. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon/viz/units.py +0 -0
  74. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon.egg-info/dependency_links.txt +0 -0
  75. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon.egg-info/entry_points.txt +0 -0
  76. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon.egg-info/requires.txt +0 -0
  77. {codecarbon-3.1.0 → codecarbon-3.1.1}/codecarbon.egg-info/top_level.txt +0 -0
  78. {codecarbon-3.1.0 → codecarbon-3.1.1}/setup.cfg +0 -0
  79. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_api_call.py +0 -0
  80. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_cli.py +0 -0
  81. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_cloud.py +0 -0
  82. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_core_util.py +0 -0
  83. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_cpu.py +0 -0
  84. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_cpu_load.py +0 -0
  85. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_custom_handler.py +0 -0
  86. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_emissions.py +0 -0
  87. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_emissions_tracker.py +0 -0
  88. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_emissions_tracker_constant.py +0 -0
  89. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_emissions_tracker_flush.py +0 -0
  90. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_energy.py +0 -0
  91. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_geography.py +0 -0
  92. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_gpu.py +0 -0
  93. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_lock.py +0 -0
  94. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_logging_output.py +0 -0
  95. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_offline_emissions_tracker.py +0 -0
  96. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_package_integrity.py +0 -0
  97. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_powermetrics.py +0 -0
  98. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_ram.py +0 -0
  99. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_rapl_mmio_scanning.py +0 -0
  100. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_rapl_parameters.py +0 -0
  101. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_rapl_permissions.py +0 -0
  102. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_tracking_inference.py +0 -0
  103. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_unsupported_gpu.py +0 -0
  104. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_viz_data.py +0 -0
  105. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/test_viz_units.py +0 -0
  106. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/testdata.py +0 -0
  107. {codecarbon-3.1.0 → codecarbon-3.1.1}/tests/testutils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecarbon
3
- Version: 3.1.0
3
+ Version: 3.1.1
4
4
  Author: Mila, DataForGood, BCG GAMMA, Comet.ml, Haverford College
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://codecarbon.io/
@@ -220,7 +220,7 @@ In order to contribute a change to our code base, please submit a pull request (
220
220
 
221
221
  Check out our [contribution guidelines :arrow_upper_right:](https://github.com/mlco2/codecarbon/blob/master/CONTRIBUTING.md)
222
222
 
223
- Contact [@vict0rsch](https://github.com/vict0rsch) to be added to our slack workspace if you want to contribute regularly!
223
+ Feel free to chat with us on [Discord](https://discord.gg/GS9js2XkJR).
224
224
 
225
225
  # How To Cite 📝
226
226
 
@@ -271,7 +271,11 @@ Here is a sample for BibTeX:
271
271
 
272
272
  # Contact 📝
273
273
 
274
- Maintainers are [@vict0rsch](https://github.com/vict0rsch) [@benoit-cty](https://github.com/benoit-cty) and [@SaboniAmine](https://github.com/saboniamine). Codecarbon is developed by volunteers from [**Mila**](http://mila.quebec) and the [**DataForGoodFR**](https://twitter.com/dataforgood_fr) community alongside donated professional time of engineers at [**Comet.ml**](https://comet.ml) and [**BCG GAMMA**](https://www.bcg.com/en-nl/beyond-consulting/bcg-gamma/default).
274
+ Feel free to chat with us on [Discord](https://discord.gg/GS9js2XkJR).
275
+
276
+ Codecarbon was formerly developed by volunteers from [**Mila**](http://mila.quebec) and the [**DataForGoodFR**](https://twitter.com/dataforgood_fr) community alongside donated professional time of engineers at [**Comet.ml**](https://comet.ml) and [**BCG GAMMA**](https://www.bcg.com/en-nl/beyond-consulting/bcg-gamma/default).
277
+
278
+ Now CodeCarbon is supported by [**Code Carbon**](https://www.helloasso.com/associations/code-carbon), a French non-profit organization whose mission is to accelerate the development and adoption of CodeCarbon.
275
279
 
276
280
  ## Star History
277
281
 
@@ -143,7 +143,7 @@ In order to contribute a change to our code base, please submit a pull request (
143
143
 
144
144
  Check out our [contribution guidelines :arrow_upper_right:](https://github.com/mlco2/codecarbon/blob/master/CONTRIBUTING.md)
145
145
 
146
- Contact [@vict0rsch](https://github.com/vict0rsch) to be added to our slack workspace if you want to contribute regularly!
146
+ Feel free to chat with us on [Discord](https://discord.gg/GS9js2XkJR).
147
147
 
148
148
  # How To Cite 📝
149
149
 
@@ -194,7 +194,11 @@ Here is a sample for BibTeX:
194
194
 
195
195
  # Contact 📝
196
196
 
197
- Maintainers are [@vict0rsch](https://github.com/vict0rsch) [@benoit-cty](https://github.com/benoit-cty) and [@SaboniAmine](https://github.com/saboniamine). Codecarbon is developed by volunteers from [**Mila**](http://mila.quebec) and the [**DataForGoodFR**](https://twitter.com/dataforgood_fr) community alongside donated professional time of engineers at [**Comet.ml**](https://comet.ml) and [**BCG GAMMA**](https://www.bcg.com/en-nl/beyond-consulting/bcg-gamma/default).
197
+ Feel free to chat with us on [Discord](https://discord.gg/GS9js2XkJR).
198
+
199
+ Codecarbon was formerly developed by volunteers from [**Mila**](http://mila.quebec) and the [**DataForGoodFR**](https://twitter.com/dataforgood_fr) community alongside donated professional time of engineers at [**Comet.ml**](https://comet.ml) and [**BCG GAMMA**](https://www.bcg.com/en-nl/beyond-consulting/bcg-gamma/default).
200
+
201
+ Now CodeCarbon is supported by [**Code Carbon**](https://www.helloasso.com/associations/code-carbon), a French non-profit organization whose mission is to accelerate the development and adoption of CodeCarbon.
198
202
 
199
203
  ## Star History
200
204
 
@@ -0,0 +1 @@
1
+ __version__ = "3.1.1"
@@ -5,19 +5,19 @@ import requests
5
5
  from codecarbon.core.units import EmissionsPerKWh, Energy
6
6
  from codecarbon.external.geography import GeoMetadata
7
7
 
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
10
10
 
11
11
 
12
12
  def get_emissions(
13
- energy: Energy, geo: GeoMetadata, co2_signal_api_token: str = ""
13
+ energy: Energy, geo: GeoMetadata, electricitymaps_api_token: str = ""
14
14
  ) -> float:
15
15
  """
16
16
  Calculate the CO2 emissions based on energy consumption and geographic location.
17
17
 
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.
21
21
 
22
22
  Args:
23
23
  energy (Energy):
@@ -25,8 +25,8 @@ def get_emissions(
25
25
  geo (GeoMetadata):
26
26
  Geographic metadata, including either latitude/longitude
27
27
  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).
30
30
 
31
31
  Returns:
32
32
  float:
@@ -34,8 +34,8 @@ def get_emissions(
34
34
  carbon intensity of the specified geographic location.
35
35
 
36
36
  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.
39
39
  """
40
40
  params: Dict[str, Any]
41
41
  if geo.latitude:
@@ -45,18 +45,25 @@ def get_emissions(
45
45
  resp = requests.get(
46
46
  URL,
47
47
  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,
50
50
  )
51
51
  if resp.status_code != 200:
52
52
  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
+
55
62
  emissions_per_kWh: EmissionsPerKWh = EmissionsPerKWh.from_g_per_kWh(
56
63
  carbon_intensity_g_per_kWh
57
64
  )
58
65
  return emissions_per_kWh.kgs_per_kWh * energy.kWh
59
66
 
60
67
 
61
- class CO2SignalAPIError(Exception):
68
+ class ElectricityMapsAPIError(Exception):
62
69
  pass
@@ -10,7 +10,7 @@ from typing import Dict, Optional
10
10
 
11
11
  import pandas as pd
12
12
 
13
- from codecarbon.core import co2_signal
13
+ from codecarbon.core import electricitymaps_api
14
14
  from codecarbon.core.units import EmissionsPerKWh, Energy
15
15
  from codecarbon.external.geography import CloudMetadata, GeoMetadata
16
16
  from codecarbon.external.logger import logger
@@ -19,10 +19,25 @@ from codecarbon.input import DataSource, DataSourceException
19
19
 
20
20
  class Emissions:
21
21
  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
23
28
  ):
24
29
  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
26
41
 
27
42
  def get_cloud_emissions(
28
43
  self, energy: Energy, cloud: CloudMetadata, geo: GeoMetadata = None
@@ -123,12 +138,19 @@ class Emissions:
123
138
  :param geo: Country and region metadata
124
139
  :return: CO2 emissions in kg
125
140
  """
126
- if self._co2_signal_api_token:
141
+ if self._electricitymaps_api_token:
127
142
  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
129
151
  except Exception as e:
130
152
  logger.error(
131
- "co2_signal.get_emissions: "
153
+ "electricitymaps_api.get_emissions: "
132
154
  + str(e)
133
155
  + " >>> Using CodeCarbon's data."
134
156
  )
@@ -171,7 +171,10 @@ class BaseEmissionsTracker(ABC):
171
171
  emissions_endpoint: Optional[str] = _sentinel,
172
172
  experiment_id: Optional[str] = _sentinel,
173
173
  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
175
178
  tracking_mode: Optional[str] = _sentinel,
176
179
  log_level: Optional[Union[int, str]] = _sentinel,
177
180
  on_csv_write: Optional[str] = _sentinel,
@@ -221,8 +224,9 @@ class BaseEmissionsTracker(ABC):
221
224
  data.
222
225
  :param experiment_id: Id of the experiment.
223
226
  :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.
226
230
  :param tracking_mode: One of "process" or "machine" in order to measure the
227
231
  power consumption due to the entire machine or to try and
228
232
  isolate the tracked processe's in isolation.
@@ -268,7 +272,31 @@ class BaseEmissionsTracker(ABC):
268
272
  self._set_from_conf(api_call_interval, "api_call_interval", 8, int)
269
273
  self._set_from_conf(api_endpoint, "api_endpoint", "https://api.codecarbon.io")
270
274
  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
+
272
300
  self._set_from_conf(emissions_endpoint, "emissions_endpoint")
273
301
  self._set_from_conf(experiment_name, "experiment_name", "base")
274
302
  self._set_from_conf(gpu_ids, "gpu_ids")
@@ -378,7 +406,7 @@ class BaseEmissionsTracker(ABC):
378
406
  self._conf["provider"] = cloud.provider
379
407
 
380
408
  self._emissions: Emissions = Emissions(
381
- self._data_source, self._co2_signal_api_token
409
+ self._data_source, self._electricitymaps_api_token
382
410
  )
383
411
  self._init_output_methods(api_key=self._api_key)
384
412
 
@@ -956,7 +984,7 @@ class OfflineEmissionsTracker(BaseEmissionsTracker):
956
984
  See https://github.com/mlco2/codecarbon/
957
985
  blob/master/codecarbon/data/cloud/impact.csv
958
986
  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.
960
988
  See http://api.electricitymap.org/v3/zones for
961
989
  a list of codes and their corresponding
962
990
  locations.
@@ -1095,7 +1123,10 @@ def track_emissions(
1095
1123
  emissions_endpoint: Optional[str] = _sentinel,
1096
1124
  experiment_id: Optional[str] = _sentinel,
1097
1125
  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
1099
1130
  tracking_mode: Optional[str] = _sentinel,
1100
1131
  log_level: Optional[Union[int, str]] = _sentinel,
1101
1132
  on_csv_write: Optional[str] = _sentinel,
@@ -1150,8 +1181,9 @@ def track_emissions(
1150
1181
  data.
1151
1182
  :param experiment_id: Id of the experiment.
1152
1183
  :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.
1155
1187
  :param tracking_mode: One of "process" or "machine" in order to measure the
1156
1188
  power consumption due to the entire machine or to try and
1157
1189
  isolate the tracked processe's in isolation.
@@ -1180,7 +1212,7 @@ def track_emissions(
1180
1212
  See https://github.com/mlco2/codecarbon/
1181
1213
  blob/master/codecarbon/data/cloud/impact.csv
1182
1214
  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.
1184
1216
  See http://api.electricitymap.org/v3/zones for
1185
1217
  a list of codes and their corresponding
1186
1218
  locations.
@@ -1199,6 +1231,17 @@ def track_emissions(
1199
1231
  @wraps(fn)
1200
1232
  def wrapped_fn(*args, **kwargs):
1201
1233
  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
+
1202
1245
  if offline and offline is not _sentinel:
1203
1246
  if (country_iso_code is None or country_iso_code is _sentinel) and (
1204
1247
  cloud_provider is None or cloud_provider is _sentinel
@@ -1217,7 +1260,7 @@ def track_emissions(
1217
1260
  prometheus_url=prometheus_url,
1218
1261
  output_handlers=output_handlers,
1219
1262
  gpu_ids=gpu_ids,
1220
- co2_signal_api_token=co2_signal_api_token,
1263
+ electricitymaps_api_token=_electricitymaps_token,
1221
1264
  tracking_mode=tracking_mode,
1222
1265
  log_level=log_level,
1223
1266
  on_csv_write=on_csv_write,
@@ -1256,7 +1299,7 @@ def track_emissions(
1256
1299
  emissions_endpoint=emissions_endpoint,
1257
1300
  experiment_id=experiment_id,
1258
1301
  experiment_name=experiment_name,
1259
- co2_signal_api_token=co2_signal_api_token,
1302
+ electricitymaps_api_token=_electricitymaps_token,
1260
1303
  tracking_mode=tracking_mode,
1261
1304
  log_level=log_level,
1262
1305
  on_csv_write=on_csv_write,
@@ -13,11 +13,29 @@ from codecarbon.output_methods.emissions_data import EmissionsData, TaskEmission
13
13
  class FileOutput(BaseOutput):
14
14
  """
15
15
  Saves experiment artifacts to a file
16
+
17
+ Attributes:
18
+ output_file_name: str, name of file to write to.
19
+ output_dir: str, path to directory to write to.
20
+ save_file_path: str, path to file to write to.
21
+ on_csv_write: str, "append" or "update", whether or not to append or overwrite a file if it exists.
16
22
  """
17
23
 
18
24
  def __init__(
19
25
  self, output_file_name: str, output_dir: str, on_csv_write: str = "append"
20
26
  ):
27
+ """
28
+ Initialize the FileOutput object.
29
+
30
+ Args:
31
+ output_file_name: name of file to write to.
32
+ output_dir: path to directory to write to.
33
+ on_csv_write: "append" or "update", whether or not to append or overwrite a file if it exists
34
+
35
+ Raises:
36
+ ValueError: If the on_csv_write value is invalid.
37
+ OSError: If the output directory does not exist.
38
+ """
21
39
  if on_csv_write not in {"append", "update"}:
22
40
  raise ValueError(
23
41
  f"Unknown `on_csv_write` value: {on_csv_write}"
@@ -33,7 +51,16 @@ class FileOutput(BaseOutput):
33
51
  f"Emissions data (if any) will be saved to file {os.path.abspath(self.save_file_path)}"
34
52
  )
35
53
 
36
- def has_valid_headers(self, data: EmissionsData):
54
+ def has_valid_headers(self, data: EmissionsData) -> bool:
55
+ """
56
+ Checks self.save_file_path has headers matching those from passed data.
57
+
58
+ Args:
59
+ data: EmissionsData object with valid headers.
60
+
61
+ Returns:
62
+ True if the file has valid headers, False otherwise.
63
+ """
37
64
  with open(self.save_file_path) as csv_file:
38
65
  csv_reader = csv.DictReader(csv_file)
39
66
  csv_entries_list = list(csv_reader)
@@ -44,11 +71,21 @@ class FileOutput(BaseOutput):
44
71
  list_of_column_names = list(dict_from_csv.keys())
45
72
  return list(data.values.keys()) == list_of_column_names
46
73
 
47
- def out(self, total: EmissionsData, delta: EmissionsData):
74
+ def out(self, total: EmissionsData, _: EmissionsData):
48
75
  """
49
- Save the emissions data to a CSV file.
50
- If the file already exists, append the new data to it.
51
- param `delta` is not used in this method.
76
+ Save the emissions data from a whole run to a CSV file.
77
+
78
+ * If the file does not exist, then create it.
79
+ * If the file already exists but has invalid headers, then back it up and replace with new data.
80
+ * If the file already exists and has valid headers:
81
+ * If it has no rows with a matching run ID, append the new data.
82
+ * If it has one row with a matching run ID, then replace that row with the new data.
83
+ * If it has > one row with a matching run ID, append the new data
84
+
85
+ Args:
86
+ total: data to save.
87
+
88
+
52
89
  """
53
90
  file_exists: bool = os.path.isfile(self.save_file_path)
54
91
  if file_exists and not self.has_valid_headers(total):
@@ -60,6 +97,10 @@ class FileOutput(BaseOutput):
60
97
  df = new_df
61
98
  elif self.on_csv_write == "append":
62
99
  df = pd.read_csv(self.save_file_path)
100
+ # Filter out empty or all-NA columns, to avoid warnings from Pandas,
101
+ # see https://github.com/pandas-dev/pandas/issues/55928
102
+ df = df.dropna(axis=1, how="all")
103
+ new_df = new_df.dropna(axis=1, how="all")
63
104
  df = pd.concat([df, new_df])
64
105
  else:
65
106
  df = pd.read_csv(self.save_file_path)
@@ -74,13 +115,22 @@ class FileOutput(BaseOutput):
74
115
  )
75
116
  df = pd.concat([df, new_df])
76
117
  else:
77
- df.at[df.run_id == total.run_id, total.values.keys()] = (
78
- total.values.values()
118
+ update_values = {}
119
+ for col, val in dict(total.values).items():
120
+ # Explicitly cast new values to prevent warnings about incompatible dtypes.
121
+ update_values[col] = df[col].dtype.type(val)
122
+ df.loc[df.run_id == total.run_id, update_values.keys()] = (
123
+ update_values.values()
79
124
  )
80
125
 
81
126
  df.to_csv(self.save_file_path, index=False)
82
127
 
83
128
  def task_out(self, data: List[TaskEmissionsData], experiment_name: str):
129
+ """
130
+ Save the emissions data from a single task in an experiment run to a CSV file.
131
+
132
+ Does not attempt to backup existing files or prevent ovewritting them.
133
+ """
84
134
  run_id = data[0].run_id
85
135
  save_task_file_path = os.path.join(
86
136
  self.output_dir, "emissions_" + experiment_name + "_" + run_id + ".csv"
@@ -90,6 +140,8 @@ class FileOutput(BaseOutput):
90
140
  [dict(data_point.values) for data_point in data]
91
141
  )
92
142
  # Filter out empty or all-NA columns, to avoid warnings from Pandas
143
+ # see https://github.com/pandas-dev/pandas/issues/55928
144
+ df = df.dropna(axis=1, how="all")
93
145
  new_df = new_df.dropna(axis=1, how="all")
94
146
  df = pd.concat([df, new_df], ignore_index=True)
95
147
  df.to_csv(save_task_file_path, index=False)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecarbon
3
- Version: 3.1.0
3
+ Version: 3.1.1
4
4
  Author: Mila, DataForGood, BCG GAMMA, Comet.ml, Haverford College
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://codecarbon.io/
@@ -220,7 +220,7 @@ In order to contribute a change to our code base, please submit a pull request (
220
220
 
221
221
  Check out our [contribution guidelines :arrow_upper_right:](https://github.com/mlco2/codecarbon/blob/master/CONTRIBUTING.md)
222
222
 
223
- Contact [@vict0rsch](https://github.com/vict0rsch) to be added to our slack workspace if you want to contribute regularly!
223
+ Feel free to chat with us on [Discord](https://discord.gg/GS9js2XkJR).
224
224
 
225
225
  # How To Cite 📝
226
226
 
@@ -271,7 +271,11 @@ Here is a sample for BibTeX:
271
271
 
272
272
  # Contact 📝
273
273
 
274
- Maintainers are [@vict0rsch](https://github.com/vict0rsch) [@benoit-cty](https://github.com/benoit-cty) and [@SaboniAmine](https://github.com/saboniamine). Codecarbon is developed by volunteers from [**Mila**](http://mila.quebec) and the [**DataForGoodFR**](https://twitter.com/dataforgood_fr) community alongside donated professional time of engineers at [**Comet.ml**](https://comet.ml) and [**BCG GAMMA**](https://www.bcg.com/en-nl/beyond-consulting/bcg-gamma/default).
274
+ Feel free to chat with us on [Discord](https://discord.gg/GS9js2XkJR).
275
+
276
+ Codecarbon was formerly developed by volunteers from [**Mila**](http://mila.quebec) and the [**DataForGoodFR**](https://twitter.com/dataforgood_fr) community alongside donated professional time of engineers at [**Comet.ml**](https://comet.ml) and [**BCG GAMMA**](https://www.bcg.com/en-nl/beyond-consulting/bcg-gamma/default).
277
+
278
+ Now CodeCarbon is supported by [**Code Carbon**](https://www.helloasso.com/associations/code-carbon), a French non-profit organization whose mission is to accelerate the development and adoption of CodeCarbon.
275
279
 
276
280
  ## Star History
277
281
 
@@ -19,9 +19,9 @@ codecarbon/cli/main.py
19
19
  codecarbon/core/__init__.py
20
20
  codecarbon/core/api_client.py
21
21
  codecarbon/core/cloud.py
22
- codecarbon/core/co2_signal.py
23
22
  codecarbon/core/config.py
24
23
  codecarbon/core/cpu.py
24
+ codecarbon/core/electricitymaps_api.py
25
25
  codecarbon/core/emissions.py
26
26
  codecarbon/core/gpu.py
27
27
  codecarbon/core/measure.py
@@ -71,12 +71,14 @@ codecarbon/viz/assets/tv_icon.png
71
71
  tests/test_api_call.py
72
72
  tests/test_cli.py
73
73
  tests/test_cloud.py
74
- tests/test_co2_signal.py
75
74
  tests/test_config.py
76
75
  tests/test_core_util.py
77
76
  tests/test_cpu.py
78
77
  tests/test_cpu_load.py
79
78
  tests/test_custom_handler.py
79
+ tests/test_electricitymaps_api.py
80
+ tests/test_electricitymaps_backward_compatibility.py
81
+ tests/test_electricitymaps_config_backward_compatibility.py
80
82
  tests/test_emissions.py
81
83
  tests/test_emissions_tracker.py
82
84
  tests/test_emissions_tracker_constant.py
@@ -165,7 +165,7 @@ docs = "cd docs/edit && make docs"
165
165
  carbonboard = "python codecarbon/viz/carbonboard.py"
166
166
 
167
167
  [tool.bumpver]
168
- current_version = "3.1.0"
168
+ current_version = "3.1.1"
169
169
  version_pattern = "MAJOR.MINOR.PATCH[_TAGNUM]"
170
170
 
171
171
  [tool.bumpver.file_patterns]
@@ -183,7 +183,8 @@ class TestConfig(unittest.TestCase):
183
183
  ):
184
184
  with patch("os.path.exists", return_value=True):
185
185
  tracker = EmissionsTracker(
186
- project_name="test-project", co2_signal_api_token="signal-token"
186
+ project_name="test-project",
187
+ electricitymaps_api_token="signal-token",
187
188
  )
188
189
  self.assertEqual(tracker._measure_power_secs, 10)
189
190
  self.assertEqual(tracker._force_cpu_power, None)
@@ -191,7 +192,7 @@ class TestConfig(unittest.TestCase):
191
192
  self.assertEqual(tracker._output_dir, "/success/overwritten")
192
193
  self.assertEqual(tracker._emissions_endpoint, "http://testhost:2000")
193
194
  self.assertEqual(tracker._gpu_ids, ["0", "1"])
194
- self.assertEqual(tracker._co2_signal_api_token, "signal-token")
195
+ self.assertEqual(tracker._electricitymaps_api_token, "signal-token")
195
196
  self.assertEqual(tracker._project_name, "test-project")
196
197
  self.assertTrue(tracker._save_to_file)
197
198
 
@@ -0,0 +1,45 @@
1
+ import unittest
2
+
3
+ import pytest
4
+ import responses
5
+
6
+ from codecarbon.core import electricitymaps_api
7
+ from codecarbon.core.units import Energy
8
+ from codecarbon.external.geography import GeoMetadata
9
+
10
+
11
+ class TestElectricityMapsAPI(unittest.TestCase):
12
+ def setUp(self) -> None:
13
+ # GIVEN
14
+ self._energy = Energy.from_energy(kWh=10)
15
+ self._geo = GeoMetadata(
16
+ country_iso_code="FRA",
17
+ country_name="France",
18
+ region=None,
19
+ country_2letter_iso_code="FR",
20
+ )
21
+
22
+ @responses.activate
23
+ def test_get_emissions_RUNS(self):
24
+ responses.add(
25
+ responses.GET,
26
+ electricitymaps_api.URL,
27
+ json={
28
+ "zone": "FR",
29
+ "carbonIntensity": 58.765447504659946,
30
+ "datetime": "2025-11-21T19:00:00.000Z",
31
+ "updatedAt": "2025-11-21T19:09:54.950Z",
32
+ },
33
+ status=200,
34
+ )
35
+ result = electricitymaps_api.get_emissions(self._energy, self._geo)
36
+ assert round(result, 5) == 0.58765
37
+
38
+ @pytest.mark.integ_test
39
+ @unittest.skip("Skip real API call in regular test runs")
40
+ def test_get_emissions_with_api_key(self):
41
+ """Test with real API call using provided API key"""
42
+ api_key = "YOUR_REAL_API_KEY"
43
+ result = electricitymaps_api.get_emissions(self._energy, self._geo, api_key)
44
+ # Should return a positive emissions value
45
+ assert result > 0
@@ -0,0 +1,80 @@
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
+ @patch("os.path.exists", return_value=True)
64
+ def test_tracker_new_parameter_takes_precedence(self, mock_exists):
65
+ """Test that new parameter takes precedence in EmissionsTracker."""
66
+
67
+ with self.assertLogs("codecarbon", level="WARNING") as log:
68
+ tracker = EmissionsTracker(
69
+ electricitymaps_api_token="new-token", co2_signal_api_token="old-token"
70
+ )
71
+
72
+ # New parameter should take precedence
73
+ self.assertEqual(tracker._electricitymaps_api_token, "new-token")
74
+
75
+ # Still should warn about using deprecated parameter
76
+ self.assertTrue(any("deprecated" in message.lower() for message in log.output))
77
+
78
+
79
+ if __name__ == "__main__":
80
+ unittest.main()
@@ -0,0 +1,63 @@
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
+ @patch("os.path.exists", return_value=True)
42
+ def test_new_config_parameter_takes_precedence(self, mock_exists):
43
+ """Test that new config parameter takes precedence over old one."""
44
+ config_with_both_names = dedent(
45
+ """\
46
+ [codecarbon]
47
+ electricitymaps_api_token=new-config-token
48
+ co2_signal_api_token=old-config-token
49
+ """
50
+ )
51
+
52
+ with patch(
53
+ "builtins.open",
54
+ new_callable=get_custom_mock_open(config_with_both_names, ""),
55
+ ):
56
+ tracker = EmissionsTracker()
57
+
58
+ # New parameter should take precedence
59
+ self.assertEqual(tracker._electricitymaps_api_token, "new-config-token")
60
+
61
+
62
+ if __name__ == "__main__":
63
+ unittest.main()
@@ -1 +0,0 @@
1
- __version__ = "3.1.0"
@@ -1,47 +0,0 @@
1
- import unittest
2
-
3
- import pytest
4
- import requests
5
- import responses
6
-
7
- from codecarbon.core import co2_signal
8
- from codecarbon.core.units import Energy
9
- from codecarbon.external.geography import GeoMetadata
10
-
11
-
12
- class TestCO2Signal(unittest.TestCase):
13
- def setUp(self) -> None:
14
- # GIVEN
15
- self._energy = Energy.from_energy(kWh=10)
16
- self._geo = GeoMetadata(
17
- country_iso_code="FRA",
18
- country_name="France",
19
- region=None,
20
- country_2letter_iso_code="FR",
21
- )
22
-
23
- @responses.activate
24
- def test_get_emissions_RUNS(self):
25
- responses.add(
26
- responses.GET,
27
- co2_signal.URL,
28
- json={
29
- "status": "ok",
30
- "countryCode": "FR",
31
- "data": {
32
- "carbonIntensity": 58.765447504659946,
33
- "fossilFuelPercentage": 8.464454194205029,
34
- },
35
- "units": {"carbonIntensity": "gCO2eq/kWh"},
36
- },
37
- status=200,
38
- )
39
- result = co2_signal.get_emissions(self._energy, self._geo)
40
- assert round(result, 5) == 0.58765
41
-
42
- @pytest.mark.integ_test
43
- def test_get_emissions_TIMEOUT(self):
44
- with self.assertRaises(
45
- (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout)
46
- ):
47
- co2_signal.get_emissions(self._energy, self._geo)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes