codecarbon 3.2.7__tar.gz → 3.2.9__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 (129) hide show
  1. {codecarbon-3.2.7 → codecarbon-3.2.9}/PKG-INFO +5 -4
  2. {codecarbon-3.2.7 → codecarbon-3.2.9}/README.md +2 -0
  3. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/__init__.py +7 -1
  4. codecarbon-3.2.9/codecarbon/_version.py +1 -0
  5. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/cli/auth.py +13 -6
  6. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/cli/main.py +34 -8
  7. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/cli/monitor.py +8 -6
  8. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/api_client.py +7 -5
  9. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/cpu.py +24 -10
  10. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/emissions.py +30 -19
  11. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/gpu.py +17 -0
  12. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/gpu_amd.py +7 -2
  13. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/gpu_device.py +14 -0
  14. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/gpu_nvidia.py +6 -1
  15. codecarbon-3.2.9/codecarbon/core/hardware_cache.py +249 -0
  16. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/powermetrics.py +15 -2
  17. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/resource_tracker.py +71 -25
  18. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/schemas.py +4 -0
  19. codecarbon-3.2.9/codecarbon/core/telemetry_client.py +61 -0
  20. codecarbon-3.2.9/codecarbon/core/telemetry_schemas.py +156 -0
  21. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/util.py +2 -1
  22. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/emissions_tracker.py +378 -219
  23. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/external/hardware.py +21 -5
  24. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/external/task.py +1 -1
  25. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/input.py +29 -12
  26. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output.py +1 -1
  27. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/base_output.py +27 -0
  28. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/file.py +15 -19
  29. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/http.py +13 -8
  30. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon.egg-info/PKG-INFO +5 -4
  31. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon.egg-info/SOURCES.txt +5 -0
  32. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon.egg-info/requires.txt +2 -4
  33. {codecarbon-3.2.7 → codecarbon-3.2.9}/pyproject.toml +4 -7
  34. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_api_call.py +9 -0
  35. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_config.py +4 -0
  36. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_cpu.py +78 -9
  37. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_cpu_load.py +5 -0
  38. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_custom_handler.py +4 -2
  39. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_emissions.py +72 -0
  40. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_emissions_tracker.py +320 -10
  41. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_emissions_tracker_constant.py +10 -15
  42. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_gpu.py +7 -0
  43. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_gpu_amd.py +73 -0
  44. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_gpu_nvidia.py +35 -0
  45. codecarbon-3.2.9/tests/test_hardware_cache.py +288 -0
  46. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_input.py +6 -3
  47. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_offline_emissions_tracker.py +11 -0
  48. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_powermetrics.py +83 -0
  49. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_resource_tracker.py +246 -2
  50. codecarbon-3.2.9/tests/test_telemetry_client.py +104 -0
  51. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/testdata.py +7 -0
  52. codecarbon-3.2.7/codecarbon/_version.py +0 -1
  53. {codecarbon-3.2.7 → codecarbon-3.2.9}/LICENSE +0 -0
  54. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/cli/__init__.py +0 -0
  55. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/cli/cli_utils.py +0 -0
  56. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/__init__.py +0 -0
  57. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/cloud.py +0 -0
  58. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/config.py +0 -0
  59. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/electricitymaps_api.py +0 -0
  60. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/measure.py +0 -0
  61. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/rapl.py +0 -0
  62. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/core/units.py +0 -0
  63. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/canada_provinces.geojson +0 -0
  64. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/cloud/impact.csv +0 -0
  65. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/hardware/cpu_dataset_builder/amd_cpu_scrapper.py +0 -0
  66. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/hardware/cpu_dataset_builder/intel_cpu_scrapper.py +0 -0
  67. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/hardware/cpu_dataset_builder/merge_scrapped_cpu_power.py +0 -0
  68. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/hardware/cpu_power.csv +0 -0
  69. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/private_infra/2016/usa_emissions.json +0 -0
  70. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/private_infra/2023/canada_energy_mix.json +0 -0
  71. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/private_infra/carbon_intensity_per_source.json +0 -0
  72. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/private_infra/global_energy_mix.json +0 -0
  73. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/data/private_infra/nordic_emissions.json +0 -0
  74. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/external/__init__.py +0 -0
  75. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/external/geography.py +0 -0
  76. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/external/logger.py +0 -0
  77. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/external/ram.py +0 -0
  78. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/external/scheduler.py +0 -0
  79. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/lock.py +0 -0
  80. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/__init__.py +0 -0
  81. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/boamps/__init__.py +0 -0
  82. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/boamps/mapper.py +0 -0
  83. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/boamps/models.py +0 -0
  84. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/boamps/output.py +0 -0
  85. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/emissions_data.py +0 -0
  86. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/logger.py +0 -0
  87. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/metrics/__init__.py +0 -0
  88. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/metrics/logfire.py +0 -0
  89. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/metrics/metric_docs.py +0 -0
  90. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/output_methods/metrics/prometheus.py +0 -0
  91. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/py.typed +0 -0
  92. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/__init__.py +0 -0
  93. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/assets/__init__.py +0 -0
  94. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/assets/car_icon.png +0 -0
  95. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/assets/house_icon.png +0 -0
  96. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/assets/tv_icon.png +0 -0
  97. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/carbonboard.py +0 -0
  98. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/carbonboard_on_api.py +0 -0
  99. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/components.py +0 -0
  100. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/data.py +0 -0
  101. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon/viz/units.py +0 -0
  102. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon.egg-info/dependency_links.txt +0 -0
  103. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon.egg-info/entry_points.txt +0 -0
  104. {codecarbon-3.2.7 → codecarbon-3.2.9}/codecarbon.egg-info/top_level.txt +0 -0
  105. {codecarbon-3.2.7 → codecarbon-3.2.9}/setup.cfg +0 -0
  106. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_boamps_output.py +0 -0
  107. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_cloud.py +0 -0
  108. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_core_util.py +0 -0
  109. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_docs_examples.py +0 -0
  110. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_electricitymaps_api.py +0 -0
  111. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_electricitymaps_backward_compatibility.py +0 -0
  112. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_electricitymaps_config_backward_compatibility.py +0 -0
  113. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_emissions_tracker_flush.py +0 -0
  114. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_energy.py +0 -0
  115. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_geography.py +0 -0
  116. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_lock.py +0 -0
  117. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_logger.py +0 -0
  118. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_logging_output.py +0 -0
  119. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_package_integrity.py +0 -0
  120. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_ram.py +0 -0
  121. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_rapl_mmio_scanning.py +0 -0
  122. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_rapl_parameters.py +0 -0
  123. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_rapl_permissions.py +0 -0
  124. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_tracking_inference.py +0 -0
  125. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_unsupported_gpu.py +0 -0
  126. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_utilization_tracking.py +0 -0
  127. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_viz_data.py +0 -0
  128. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/test_viz_units.py +0 -0
  129. {codecarbon-3.2.7 → codecarbon-3.2.9}/tests/testutils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecarbon
3
- Version: 3.2.7
3
+ Version: 3.2.9
4
4
  Author: Mila, DataForGood, BCG GAMMA, Comet.ml, Haverford College
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://codecarbon.io/
@@ -20,13 +20,14 @@ Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
21
  Requires-Dist: arrow
22
22
  Requires-Dist: authlib>=1.2.1
23
+ Requires-Dist: joserfc>=1.0.0
23
24
  Requires-Dist: click
24
25
  Requires-Dist: pandas>=2.3.3; python_version >= "3.14"
25
26
  Requires-Dist: pandas; python_version < "3.14"
26
27
  Requires-Dist: prometheus_client
27
28
  Requires-Dist: psutil>=6.0.0
28
29
  Requires-Dist: py-cpuinfo
29
- Requires-Dist: pydantic
30
+ Requires-Dist: pydantic<3.0.0,>=2.0.0
30
31
  Requires-Dist: nvidia-ml-py
31
32
  Requires-Dist: rapidfuzz
32
33
  Requires-Dist: requests
@@ -42,8 +43,6 @@ Provides-Extra: viz-legacy
42
43
  Requires-Dist: dash; extra == "viz-legacy"
43
44
  Requires-Dist: dash_bootstrap_components>1.0.0; extra == "viz-legacy"
44
45
  Requires-Dist: fire; extra == "viz-legacy"
45
- Provides-Extra: amdsmi
46
- Requires-Dist: amdsmi>=6.0.0; extra == "amdsmi"
47
46
  Dynamic: license-file
48
47
 
49
48
  ![banner](docs/images/banner.png)
@@ -124,6 +123,8 @@ Configuration precedence and examples: [configuration guide](https://docs.codeca
124
123
 
125
124
  We created a Python package that estimates your hardware electricity power consumption (GPU + CPU + RAM) and we apply to it the carbon intensity of the region where the computing is done.
126
125
 
126
+ CodeCarbon focuses on the main compute components it can measure or estimate directly: CPU, GPU, and RAM. It does not separately model disk I/O, network transfers, displays, cooling, or other peripherals because those sources are usually much smaller for local code-level experiments and are not exposed through the same low-overhead measurement interfaces.
127
+
127
128
  ![calculation Summary](docs/images/calculation.png)
128
129
 
129
130
  We explain more about this calculation in the [**Methodology**](https://docs.codecarbon.io/latest/explanation/methodology/) section of the documentation.
@@ -76,6 +76,8 @@ Configuration precedence and examples: [configuration guide](https://docs.codeca
76
76
 
77
77
  We created a Python package that estimates your hardware electricity power consumption (GPU + CPU + RAM) and we apply to it the carbon intensity of the region where the computing is done.
78
78
 
79
+ CodeCarbon focuses on the main compute components it can measure or estimate directly: CPU, GPU, and RAM. It does not separately model disk I/O, network transfers, displays, cooling, or other peripherals because those sources are usually much smaller for local code-level experiments and are not exposed through the same low-overhead measurement interfaces.
80
+
79
81
  ![calculation Summary](docs/images/calculation.png)
80
82
 
81
83
  We explain more about this calculation in the [**Methodology**](https://docs.codecarbon.io/latest/explanation/methodology/) section of the documentation.
@@ -8,6 +8,12 @@ from .emissions_tracker import (
8
8
  OfflineEmissionsTracker,
9
9
  track_emissions,
10
10
  )
11
+ from .output_methods.base_output import OutputMethod
11
12
 
12
- __all__ = ["EmissionsTracker", "OfflineEmissionsTracker", "track_emissions"]
13
+ __all__ = [
14
+ "EmissionsTracker",
15
+ "OfflineEmissionsTracker",
16
+ "OutputMethod",
17
+ "track_emissions",
18
+ ]
13
19
  __app_name__ = "codecarbon"
@@ -0,0 +1 @@
1
+ __version__ = "3.2.9"
@@ -6,6 +6,7 @@ PKCE), credential storage, JWKS validation, and transparent refresh.
6
6
  """
7
7
 
8
8
  import json
9
+ import logging
9
10
  import os
10
11
  import webbrowser
11
12
  from http.server import BaseHTTPRequestHandler, HTTPServer
@@ -15,9 +16,9 @@ from urllib.parse import parse_qs, urlparse
15
16
  import requests
16
17
  from authlib.common.security import generate_token
17
18
  from authlib.integrations.requests_client import OAuth2Session
18
- from authlib.jose import JsonWebKey
19
- from authlib.jose import jwt as jose_jwt
20
19
  from authlib.oauth2.rfc7636 import create_s256_code_challenge
20
+ from joserfc import jwt as jose_jwt
21
+ from joserfc.jwk import KeySet
21
22
 
22
23
  AUTH_CLIENT_ID = os.environ.get(
23
24
  "AUTH_CLIENT_ID",
@@ -31,6 +32,7 @@ AUTH_SERVER_WELL_KNOWN = os.environ.get(
31
32
  _REDIRECT_PORT = 8090
32
33
  _REDIRECT_URI = f"http://localhost:{_REDIRECT_PORT}/callback"
33
34
  _CREDENTIALS_FILE = Path("./credentials.json")
35
+ logger = logging.getLogger(__name__)
34
36
 
35
37
 
36
38
  # ── OAuth callback server ───────────────────────────────────────────
@@ -108,11 +110,16 @@ def _validate_access_token(access_token: str) -> bool:
108
110
  discovery = _discover_endpoints()
109
111
  jwks_resp = requests.get(discovery["jwks_uri"])
110
112
  jwks_resp.raise_for_status()
111
- keyset = JsonWebKey.import_key_set(jwks_resp.json())
112
- claims = jose_jwt.decode(access_token, keyset)
113
- claims.validate()
113
+ keyset = KeySet.import_key_set(jwks_resp.json())
114
+ token = jose_jwt.decode(access_token, keyset)
115
+ jose_jwt.JWTClaimsRegistry().validate(token.claims)
114
116
  return True
115
- except requests.RequestException:
117
+ except requests.RequestException as exc:
118
+ logger.warning(
119
+ "Skipping local access token validation because the auth server is "
120
+ "unreachable; the API will validate the token. Error: %s",
121
+ exc,
122
+ )
116
123
  return True # Can't reach auth server — let the API handle it
117
124
  except Exception:
118
125
  return False
@@ -5,15 +5,12 @@ import time
5
5
  from pathlib import Path
6
6
  from typing import Optional
7
7
 
8
- import questionary
9
- import requests
10
8
  import typer
11
9
  from rich import print
12
10
  from rich.prompt import Confirm
13
11
  from typing_extensions import Annotated
14
12
 
15
13
  from codecarbon import __app_name__, __version__
16
- from codecarbon.cli.auth import authorize, get_access_token
17
14
  from codecarbon.cli.cli_utils import (
18
15
  create_new_config_file,
19
16
  get_api_endpoint,
@@ -21,10 +18,6 @@ from codecarbon.cli.cli_utils import (
21
18
  get_existing_exp_id,
22
19
  overwrite_local_config,
23
20
  )
24
- from codecarbon.cli.monitor import run_and_monitor
25
- from codecarbon.core.api_client import ApiClient, get_datetime_with_timezone
26
- from codecarbon.core.schemas import ExperimentCreate, OrganizationCreate, ProjectCreate
27
- from codecarbon.emissions_tracker import EmissionsTracker, OfflineEmissionsTracker
28
21
 
29
22
  API_URL = os.environ.get("API_URL", "https://dashboard.codecarbon.io/api")
30
23
 
@@ -68,6 +61,9 @@ def version(
68
61
 
69
62
 
70
63
  def show_config(path: Path = Path("./.codecarbon.config")) -> None:
64
+ from codecarbon.cli.auth import get_access_token
65
+ from codecarbon.core.api_client import ApiClient
66
+
71
67
  d = get_config(path)
72
68
  print("Current configuration : \n")
73
69
  print("Config file content : ")
@@ -114,6 +110,9 @@ def api_get():
114
110
  """
115
111
  ex: test-api
116
112
  """
113
+ from codecarbon.cli.auth import get_access_token
114
+ from codecarbon.core.api_client import ApiClient
115
+
117
116
  api_endpoint = get_api_endpoint()
118
117
  api = ApiClient(endpoint_url=api_endpoint)
119
118
  api.set_access_token(get_access_token())
@@ -123,6 +122,9 @@ def api_get():
123
122
 
124
123
  @codecarbon.command("login", short_help="Login to CodeCarbon")
125
124
  def login():
125
+ from codecarbon.cli.auth import authorize, get_access_token
126
+ from codecarbon.core.api_client import ApiClient
127
+
126
128
  authorize()
127
129
  api_endpoint = get_api_endpoint()
128
130
  api = ApiClient(endpoint_url=api_endpoint)
@@ -132,6 +134,10 @@ def login():
132
134
 
133
135
 
134
136
  def get_api_key(project_id: str):
137
+ import requests
138
+
139
+ from codecarbon.cli.auth import get_access_token
140
+
135
141
  api_endpoint = get_api_endpoint()
136
142
  api_endpoint = api_endpoint.rstrip("/")
137
143
  req = requests.post(
@@ -161,6 +167,13 @@ def config():
161
167
  """
162
168
  Initialize CodeCarbon, this will prompt you for configuration of Organisation/Team/Project/Experiment.
163
169
  """
170
+ from codecarbon.cli.auth import get_access_token
171
+ from codecarbon.core.api_client import ApiClient, get_datetime_with_timezone
172
+ from codecarbon.core.schemas import (
173
+ ExperimentCreate,
174
+ OrganizationCreate,
175
+ ProjectCreate,
176
+ )
164
177
 
165
178
  print("Welcome to CodeCarbon configuration wizard")
166
179
  home = Path.home()
@@ -342,6 +355,10 @@ def monitor(
342
355
  str,
343
356
  typer.Option(help="Region/province for offline mode"),
344
357
  ] = None,
358
+ log_level: Annotated[
359
+ str,
360
+ typer.Option(help="Log level (critical, error, warning, info, debug)"),
361
+ ] = "error",
345
362
  ):
346
363
  """Monitor your machine's carbon emissions."""
347
364
 
@@ -349,6 +366,7 @@ def monitor(
349
366
  tracker_args = {
350
367
  "measure_power_secs": measure_power_secs,
351
368
  "api_call_interval": api_call_interval,
369
+ "log_level": log_level,
352
370
  }
353
371
  # Set up the tracker arguments based on mode (offline vs online) and validate required args for each mode
354
372
  if offline:
@@ -375,9 +393,13 @@ def monitor(
375
393
 
376
394
  tracker_args = {**tracker_args, "save_to_api": api}
377
395
 
396
+ from codecarbon.emissions_tracker import EmissionsTracker, OfflineEmissionsTracker
397
+
378
398
  # If extra args are provided (e.g. `codecarbon monitor -- my_script.py`), delegate to `run_and_monitor`
379
399
  if getattr(ctx, "args", None):
380
- return run_and_monitor(ctx, **tracker_args)
400
+ from codecarbon.cli.monitor import run_and_monitor
401
+
402
+ return run_and_monitor(ctx, offline=offline, **tracker_args)
381
403
 
382
404
  # Instantiate the tracker
383
405
  if offline:
@@ -417,6 +439,8 @@ def detect():
417
439
  """
418
440
  Detects hardware and prints information without running any measurements.
419
441
  """
442
+ from codecarbon.emissions_tracker import EmissionsTracker
443
+
420
444
  print("Detecting hardware...")
421
445
  tracker = EmissionsTracker(save_to_file=False)
422
446
  hardware_info = tracker.get_detected_hardware()
@@ -438,6 +462,8 @@ def detect():
438
462
 
439
463
 
440
464
  def questionary_prompt(prompt, list_options, default):
465
+ import questionary
466
+
441
467
  value = questionary.select(
442
468
  prompt,
443
469
  list_options,
@@ -8,8 +8,6 @@ import typer
8
8
  from rich import print
9
9
  from typing_extensions import Annotated
10
10
 
11
- from codecarbon.emissions_tracker import EmissionsTracker
12
-
13
11
 
14
12
  def run_and_monitor(
15
13
  ctx: typer.Context,
@@ -17,6 +15,7 @@ def run_and_monitor(
17
15
  str,
18
16
  typer.Option(help="Log level (critical, error, warning, info, debug)"),
19
17
  ] = "error",
18
+ offline: bool = False,
20
19
  **tracker_args,
21
20
  ):
22
21
  """
@@ -49,12 +48,15 @@ def run_and_monitor(
49
48
  directory. The file path is shown in the final report.
50
49
  """
51
50
  # Suppress all CodeCarbon logs during execution
51
+ from codecarbon.emissions_tracker import EmissionsTracker, OfflineEmissionsTracker
52
52
  from codecarbon.external.logger import set_logger_level
53
53
 
54
54
  set_logger_level(log_level)
55
55
 
56
- # Get the command from remaining args
57
- command = ctx.args
56
+ # Get the command from remaining args (strip nested subcommand / `--` leftovers)
57
+ command = list(getattr(ctx, "args", None) or [])
58
+ while command and command[0] in ("monitor", "--"):
59
+ command.pop(0)
58
60
 
59
61
  if not command:
60
62
  print(
@@ -63,8 +65,8 @@ def run_and_monitor(
63
65
  )
64
66
  raise typer.Exit(1)
65
67
 
66
- # Initialize tracker with specified logging level and shared args
67
- tracker = EmissionsTracker(
68
+ tracker_cls = OfflineEmissionsTracker if offline else EmissionsTracker
69
+ tracker = tracker_cls(
68
70
  log_level=log_level,
69
71
  save_to_logger=False,
70
72
  tracking_mode="process",
@@ -10,7 +10,6 @@ import dataclasses
10
10
  import json
11
11
  from datetime import timedelta, tzinfo
12
12
 
13
- import arrow
14
13
  import requests
15
14
 
16
15
  from codecarbon.core.schemas import (
@@ -22,12 +21,11 @@ from codecarbon.core.schemas import (
22
21
  )
23
22
  from codecarbon.external.logger import logger
24
23
 
25
- # from codecarbon.output import EmissionsData
26
-
27
24
 
28
25
  def get_datetime_with_timezone():
29
- timestamp = str(arrow.now().isoformat())
30
- return timestamp
26
+ import arrow
27
+
28
+ return str(arrow.now().isoformat())
31
29
 
32
30
 
33
31
  class ApiClient: # (AsyncClient)
@@ -227,6 +225,10 @@ class ApiClient: # (AsyncClient)
227
225
  gpu_energy=carbon_emission["gpu_energy"],
228
226
  ram_energy=carbon_emission["ram_energy"],
229
227
  energy_consumed=carbon_emission["energy_consumed"],
228
+ cpu_utilization_percent=carbon_emission.get("cpu_utilization_percent"),
229
+ gpu_utilization_percent=carbon_emission.get("gpu_utilization_percent"),
230
+ ram_utilization_percent=carbon_emission.get("ram_utilization_percent"),
231
+ wue=carbon_emission.get("wue", 0),
230
232
  )
231
233
  try:
232
234
  payload = dataclasses.asdict(emission)
@@ -4,14 +4,16 @@ using Intel Power Gadget
4
4
  https://software.intel.com/content/www/us/en/develop/articles/intel-power-gadget.html
5
5
  """
6
6
 
7
+ from __future__ import annotations
8
+
7
9
  import os
8
10
  import re
9
11
  import shutil
10
12
  import subprocess
11
13
  import sys
12
- from typing import Dict, Optional, Tuple
14
+ from functools import lru_cache
15
+ from typing import TYPE_CHECKING, Dict, Optional, Tuple
13
16
 
14
- import pandas as pd
15
17
  import psutil
16
18
  from rapidfuzz import fuzz, process, utils
17
19
 
@@ -19,12 +21,15 @@ from codecarbon.core.rapl import RAPLFile
19
21
  from codecarbon.core.units import Time
20
22
  from codecarbon.core.util import count_cpus, detect_cpu_model
21
23
  from codecarbon.external.logger import logger
22
- from codecarbon.input import DataSource
24
+
25
+ if TYPE_CHECKING:
26
+ import pandas as pd
23
27
 
24
28
  # default W value per core for a CPU if no model is found in the ref csv
25
29
  DEFAULT_POWER_PER_CORE = 4
26
30
 
27
31
 
32
+ @lru_cache(maxsize=1)
28
33
  def is_powergadget_available() -> bool:
29
34
  """
30
35
  Checks if Intel Power Gadget is available on the system.
@@ -44,6 +49,10 @@ def is_powergadget_available() -> bool:
44
49
  return False
45
50
 
46
51
 
52
+ def clear_powergadget_cache() -> None:
53
+ is_powergadget_available.cache_clear()
54
+
55
+
47
56
  def _get_candidate_bases(rapl_dir: str) -> list:
48
57
  """Get list of directories to scan for RAPL files."""
49
58
  default_rapl_dir = "/sys/class/powercap/intel-rapl/subsystem"
@@ -217,19 +226,19 @@ def is_psutil_available():
217
226
  return True
218
227
  else:
219
228
  logger.debug(
220
- f"is_psutil_available(): psutil.cpu_times().nice is too small: {nice}"
229
+ f"is_psutil_available(): psutil.cpu_times().nice is too small: {nice}; "
230
+ "using fallback check."
221
231
  )
222
- return False
223
232
 
224
233
  else:
225
- # Fallback: check if psutil works by calling cpu_percent
226
234
  logger.debug(
227
235
  "is_psutil_available(): no 'nice' attribute, using fallback check."
228
236
  )
229
237
 
230
- # check CPU utilization usable
231
- psutil.cpu_percent(interval=0.0, percpu=False)
232
- return True
238
+ # Fallback: check if psutil CPU utilization is usable. This covers
239
+ # platforms like Windows and macOS where cpu_times().nice is absent or 0.
240
+ psutil.cpu_percent(interval=0.0, percpu=False)
241
+ return True
233
242
 
234
243
  except Exception as e:
235
244
  logger.debug(
@@ -366,6 +375,8 @@ class IntelPowerGadget:
366
375
  self._log_values()
367
376
  cpu_details = {}
368
377
  try:
378
+ import pandas as pd
379
+
369
380
  cpu_data = pd.read_csv(self._log_file_path).dropna()
370
381
  for col_name in cpu_data.columns:
371
382
  if col_name in ["System Time", "Elapsed Time (sec)", "RDTSC"]:
@@ -828,7 +839,8 @@ class IntelRAPL:
828
839
  """
829
840
  cpu_details = {}
830
841
  try:
831
- list(map(lambda rapl_file: rapl_file.delta(duration), self._rapl_files))
842
+ for rapl_file in self._rapl_files:
843
+ rapl_file.delta(duration)
832
844
 
833
845
  for rapl_file in self._rapl_files:
834
846
  logger.debug(rapl_file)
@@ -891,6 +903,8 @@ class TDP:
891
903
  return float(cpu_power_df[cpu_power_df["Name"] == match]["TDP"].values[0])
892
904
 
893
905
  def _get_cpu_power_from_registry(self, cpu_model_raw: str) -> Optional[int]:
906
+ from codecarbon.input import DataSource
907
+
894
908
  cpu_power_df = DataSource().get_cpu_power_data()
895
909
  cpu_matching = self._get_matching_cpu(cpu_model_raw, cpu_power_df)
896
910
  if cpu_matching:
@@ -6,9 +6,7 @@ https://github.com/mlco2/impact
6
6
  https://github.com/responsibleproblemsolving/energy-usage
7
7
  """
8
8
 
9
- from typing import Dict, Optional
10
-
11
- import pandas as pd
9
+ from typing import TYPE_CHECKING, Dict, Optional
12
10
 
13
11
  from codecarbon.core import electricitymaps_api
14
12
  from codecarbon.core.units import EmissionsPerKWh, Energy
@@ -16,6 +14,15 @@ from codecarbon.external.geography import CloudMetadata, GeoMetadata
16
14
  from codecarbon.external.logger import logger
17
15
  from codecarbon.input import DataSource, DataSourceException
18
16
 
17
+ if TYPE_CHECKING:
18
+ import pandas as pd
19
+
20
+ _NORDIC_REGIONS_BY_COUNTRY = {
21
+ "SWE": {"SE1", "SE2", "SE3", "SE4"},
22
+ "NOR": {"NO1", "NO2", "NO3", "NO4", "NO5"},
23
+ "FIN": {"FI"},
24
+ }
25
+
19
26
 
20
27
  class Emissions:
21
28
  def __init__(
@@ -169,8 +176,11 @@ class Emissions:
169
176
  + " >>> Using CodeCarbon's data."
170
177
  )
171
178
 
179
+ country_iso_code = (
180
+ geo.country_iso_code.upper() if geo.country_iso_code is not None else None
181
+ )
172
182
  compute_with_regional_data: bool = (geo.region is not None) and (
173
- geo.country_iso_code.upper() in ["USA", "CAN", "SWE", "NOR", "FIN"]
183
+ country_iso_code in ["USA", "CAN"] or self._is_supported_nordic_region(geo)
174
184
  )
175
185
 
176
186
  if compute_with_regional_data:
@@ -187,24 +197,10 @@ class Emissions:
187
197
  def _try_get_nordic_region_emissions(
188
198
  self, energy: Energy, geo: GeoMetadata
189
199
  ) -> Optional[float]:
190
- nordic_regions = {
191
- "SE1",
192
- "SE2",
193
- "SE3",
194
- "SE4",
195
- "NO1",
196
- "NO2",
197
- "NO3",
198
- "NO4",
199
- "NO5",
200
- "FI",
201
- }
202
- if geo.region is None:
200
+ if not self._is_supported_nordic_region(geo):
203
201
  return None
204
202
 
205
203
  region_upper = geo.region.upper()
206
- if region_upper not in nordic_regions:
207
- return None
208
204
 
209
205
  try:
210
206
  nordic_data = self._data_source.get_nordic_country_energy_mix_data()
@@ -225,6 +221,16 @@ class Emissions:
225
221
  )
226
222
  return None
227
223
 
224
+ @staticmethod
225
+ def _is_supported_nordic_region(geo: GeoMetadata) -> bool:
226
+ if geo.country_iso_code is None or geo.region is None:
227
+ return False
228
+
229
+ country_regions = _NORDIC_REGIONS_BY_COUNTRY.get(
230
+ geo.country_iso_code.upper(), set()
231
+ )
232
+ return geo.region.upper() in country_regions
233
+
228
234
  def get_region_emissions(self, energy: Energy, geo: GeoMetadata) -> float:
229
235
  """
230
236
  Computes emissions for a region on private infra.
@@ -248,6 +254,11 @@ class Emissions:
248
254
  nordic_emissions = self._try_get_nordic_region_emissions(energy, geo)
249
255
  if nordic_emissions is not None:
250
256
  return nordic_emissions
257
+ if (
258
+ geo.country_iso_code is not None
259
+ and geo.country_iso_code.upper() in _NORDIC_REGIONS_BY_COUNTRY
260
+ ):
261
+ return self.get_country_emissions(energy, geo)
251
262
 
252
263
  # Handle USA and Canada regional data
253
264
  try:
@@ -137,6 +137,23 @@ class AllGPUDevices:
137
137
  logger.warning("Failed to retrieve gpu information", exc_info=True)
138
138
  return []
139
139
 
140
+ def get_gpu_utilization_list(self) -> List:
141
+ """Lightweight alternative to :meth:`get_gpu_details` for the 1s
142
+ monitoring hot path. Returns only ``gpu_index`` and
143
+ ``gpu_utilization`` per device, skipping heavyweight queries
144
+ (memory, temperature, compute mode, process lists).
145
+
146
+ >>> get_gpu_utilization_list()
147
+ [
148
+ {"gpu_index": 0, "gpu_utilization": 0},
149
+ ]
150
+ """
151
+ try:
152
+ return [d.get_gpu_utilization_lightweight() for d in self.devices]
153
+ except Exception:
154
+ logger.warning("Failed to retrieve gpu utilization", exc_info=True)
155
+ return []
156
+
140
157
  def get_delta(self, last_duration: Time) -> List:
141
158
  """Get difference since last time this function was called
142
159
  >>> get_delta()
@@ -1,21 +1,26 @@
1
1
  import subprocess
2
2
  from collections import namedtuple
3
+ from functools import lru_cache
3
4
  from typing import Callable
4
5
 
5
6
  from codecarbon.core.gpu_device import GPUDevice
6
7
  from codecarbon.external.logger import logger
7
8
 
8
9
 
10
+ @lru_cache(maxsize=1)
9
11
  def is_rocm_system():
10
12
  """Returns True if the system has an rocm-smi interface."""
11
13
  try:
12
- # Check if rocm-smi is available
13
14
  subprocess.check_output(["rocm-smi", "--help"])
14
15
  return True
15
16
  except (subprocess.CalledProcessError, OSError):
16
17
  return False
17
18
 
18
19
 
20
+ def clear_rocm_system_cache() -> None:
21
+ is_rocm_system.cache_clear()
22
+
23
+
19
24
  try:
20
25
  import amdsmi
21
26
 
@@ -28,7 +33,7 @@ except ImportError:
28
33
  "Please install amdsmi to get GPU metrics."
29
34
  )
30
35
  AMDSMI_AVAILABLE = False
31
- except AttributeError as e:
36
+ except (AttributeError, OSError, KeyError) as e:
32
37
  amdsmi = None
33
38
  # In some environments, amdsmi may be present but not properly configured, leading to AttributeError when importing
34
39
  logger.warning(
@@ -101,6 +101,20 @@ class GPUDevice:
101
101
  }
102
102
  return device_details
103
103
 
104
+ def get_gpu_utilization_lightweight(self) -> Dict[str, Any]:
105
+ """
106
+ Lightweight alternative to :meth:`get_gpu_details` for the hot path
107
+ (``_monitor_power`` which runs every 1s).
108
+
109
+ Only queries the GPU utilization — avoids heavyweight calls like
110
+ memory info, temperature, compute mode, and process lists which are
111
+ not consumed by the tracker's monitoring loop.
112
+ """
113
+ return {
114
+ "gpu_index": self.gpu_index,
115
+ "gpu_utilization": self._get_gpu_utilization(),
116
+ }
117
+
104
118
  def _to_utf8(self, str_or_bytes) -> Any:
105
119
  if hasattr(str_or_bytes, "decode"):
106
120
  return str_or_bytes.decode("utf-8", errors="replace")
@@ -1,21 +1,26 @@
1
1
  import subprocess
2
2
  from dataclasses import dataclass
3
+ from functools import lru_cache
3
4
  from typing import Any, Union
4
5
 
5
6
  from codecarbon.core.gpu_device import GPUDevice
6
7
  from codecarbon.external.logger import logger
7
8
 
8
9
 
10
+ @lru_cache(maxsize=1)
9
11
  def is_nvidia_system():
10
12
  """Returns True if the system has an nvidia-smi interface."""
11
13
  try:
12
- # Check if nvidia-smi is available
13
14
  subprocess.check_output(["nvidia-smi", "--help"])
14
15
  return True
15
16
  except Exception:
16
17
  return False
17
18
 
18
19
 
20
+ def clear_nvidia_system_cache() -> None:
21
+ is_nvidia_system.cache_clear()
22
+
23
+
19
24
  try:
20
25
  import pynvml
21
26