Skip to content

CLI can be called in offline mode without and API endpoint #782

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 15, 2025
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 49 additions & 34 deletions codecarbon/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import time
from pathlib import Path
from typing import Optional
import signal

import questionary
import requests
Expand All @@ -23,7 +24,7 @@
)
from codecarbon.core.api_client import ApiClient, get_datetime_with_timezone
from codecarbon.core.schemas import ExperimentCreate, OrganizationCreate, ProjectCreate
from codecarbon.emissions_tracker import EmissionsTracker
from codecarbon.emissions_tracker import EmissionsTracker, OfflineEmissionsTracker

AUTH_CLIENT_ID = os.environ.get(
"AUTH_CLIENT_ID",
Expand Down Expand Up @@ -327,46 +328,60 @@ def config():

@codecarbon.command("monitor", short_help="Monitor your machine's carbon emissions.")
def monitor(
measure_power_secs: Annotated[
int, typer.Argument(help="Interval between two measures.")
] = 10,
api_call_interval: Annotated[
int, typer.Argument(help="Number of measures between API calls.")
] = 30,
api: Annotated[
bool, typer.Option(help="Choose to call Code Carbon API or not")
] = True,
measure_power_secs: Annotated[int, typer.Argument(help="Interval between two measures.")] = 10,
api_call_interval: Annotated[int, typer.Argument(help="Number of measures between API calls.")] = 30,
api: Annotated[bool, typer.Option(help="Choose to call Code Carbon API or not")] = True,
offline: Annotated[bool, typer.Option(help="Run in offline mode")] = False,
country_iso_code: Annotated[str, typer.Option(help="3-letter country ISO code for offline mode")] = None,
region: Annotated[str, typer.Option(help="Region/province for offline mode")] = None,
):
"""Monitor your machine's carbon emissions.
"""Monitor your machine's carbon emissions."""
if offline:
if not country_iso_code:
print("ERROR: country_iso_code is required for offline mode", file=sys.stderr)
raise typer.Exit(1)

tracker = OfflineEmissionsTracker(
measure_power_secs=measure_power_secs,
country_iso_code=country_iso_code,
region=region,
)
else:
experiment_id = get_existing_local_exp_id()
if api and experiment_id is None:
print("ERROR: No experiment id, call 'codecarbon config' first.", file=sys.stderr)
raise typer.Exit(1)

tracker = EmissionsTracker(
measure_power_secs=measure_power_secs,
api_call_interval=api_call_interval,
save_to_api=api,
)

def signal_handler(signum, frame):
print("\nReceived signal to stop. Saving emissions data...")
tracker.stop()
sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)


Args:
measure_power_secs (Annotated[int, typer.Argument, optional): Interval between two measures. Defaults to 10.
api_call_interval (Annotated[int, typer.Argument, optional): Number of measures before calling API. Defaults to 30.
api (Annotated[bool, typer.Option, optional): Choose to call Code Carbon API or not. Defaults to True.
"""
experiment_id = get_existing_local_exp_id()
if api:
if experiment_id is None:
print(
"ERROR: No experiment id, call 'codecarbon config' first.",
file=sys.stderr,
)
print("CodeCarbon is going in an infinite loop to monitor this machine.")
with EmissionsTracker(
measure_power_secs=measure_power_secs,
api_call_interval=api_call_interval,
save_to_api=api,
) as tracker:
# Infinite loop
print("Press Ctrl+C to stop and save emissions data.")

tracker.start()
try:
while True:
if (
hasattr(tracker, "_another_instance_already_running")
and tracker._another_instance_already_running
):
if (hasattr(tracker, "_another_instance_already_running")
and tracker._another_instance_already_running):
print("Another instance of CodeCarbon is already running. Exiting.")
break
time.sleep(300)

except Exception as e:
print(f"\nError occurred: {e}")
tracker.stop()
raise e

def questionary_prompt(prompt, list_options, default):
value = questionary.select(
Expand Down
Loading