Skip to content

Commit f9ccbfa

Browse files
committed
fix: rename to codecarbon monitor
1 parent bbbaa9c commit f9ccbfa

3 files changed

Lines changed: 168 additions & 140 deletions

File tree

codecarbon/cli/main.py

Lines changed: 29 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
from codecarbon.cli.monitor import run_and_monitor
12
import os
23
import signal
3-
import subprocess
44
import sys
55
import time
66
from pathlib import Path
@@ -341,132 +341,17 @@ def config():
341341

342342

343343
@codecarbon.command(
344-
"run",
345-
short_help="Run a command and track its emissions.",
344+
"monitor",
345+
short_help="Monitor your machine's carbon emissions.",
346346
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
347347
)
348-
def run(
349-
ctx: typer.Context,
350-
log_level: Annotated[
351-
str, typer.Option(help="Log level (critical, error, warning, info, debug)")
352-
] = "error",
353-
):
354-
"""
355-
Run a command and track its carbon emissions.
356-
357-
This command wraps any executable and measures the process's total power
358-
consumption during its execution. When the command completes, a summary
359-
report is displayed and emissions data is saved to a CSV file.
360-
361-
Note: This tracks process-level emissions (only the specific command), not the
362-
entire machine. For machine-level tracking, use the `monitor` command.
363-
364-
Examples:
365-
366-
Do not use quotes around the command. Use -- to separate CodeCarbon args.
367-
368-
# Run any shell command:
369-
codecarbon run -- ./benchmark.sh
370-
371-
# Commands with arguments (use single quotes for special chars):
372-
codecarbon run -- python -c 'print("Hello World!")'
373-
374-
# Pipe the command output:
375-
codecarbon run -- npm run test > output.txt
376-
377-
# Display the CodeCarbon detailed logs:
378-
codecarbon run --log-level debug -- python --version
379-
380-
The emissions data is appended to emissions.csv (default) in the current
381-
directory. The file path is shown in the final report.
382-
"""
383-
# Suppress all CodeCarbon logs during execution
384-
from codecarbon.external.logger import set_logger_level
385-
386-
set_logger_level(log_level)
387-
388-
# Get the command from remaining args
389-
command = ctx.args
390-
391-
if not command:
392-
print(
393-
"ERROR: No command provided. Use: codecarbon run -- <command>",
394-
file=sys.stderr,
395-
)
396-
raise typer.Exit(1)
397-
398-
# Initialize tracker with specified logging level
399-
tracker = EmissionsTracker(
400-
log_level=log_level, save_to_logger=False, tracking_mode="process"
401-
)
402-
403-
print("🌱 CodeCarbon: Starting emissions tracking...")
404-
print(f" Command: {' '.join(command)}")
405-
print()
406-
407-
tracker.start()
408-
409-
process = None
410-
try:
411-
# Run the command, streaming output to console
412-
process = subprocess.Popen(
413-
command,
414-
stdout=sys.stdout,
415-
stderr=sys.stderr,
416-
text=True,
417-
)
418-
419-
# Wait for completion
420-
exit_code = process.wait()
421-
422-
except FileNotFoundError:
423-
print(f"❌ Error: Command not found: {command[0]}", file=sys.stderr)
424-
exit_code = 127
425-
except KeyboardInterrupt:
426-
print("\n⚠️ Interrupted by user", file=sys.stderr)
427-
if process is not None:
428-
process.terminate()
429-
try:
430-
process.wait(timeout=5)
431-
except subprocess.TimeoutExpired:
432-
process.kill()
433-
exit_code = 130
434-
except Exception as e:
435-
print(f"❌ Error running command: {e}", file=sys.stderr)
436-
exit_code = 1
437-
finally:
438-
emissions = tracker.stop()
439-
print()
440-
print("=" * 60)
441-
print("🌱 CodeCarbon Emissions Report")
442-
print("=" * 60)
443-
print(f" Command: {' '.join(command)}")
444-
if emissions is not None:
445-
print(f" Emissions: {emissions * 1000:.4f} g CO2eq")
446-
else:
447-
print(" Emissions: N/A")
448-
449-
# Show where the data was saved
450-
if hasattr(tracker, "_conf") and "output_file" in tracker._conf:
451-
output_path = tracker._conf["output_file"]
452-
# Make it absolute if it's relative
453-
if not os.path.isabs(output_path):
454-
output_path = os.path.abspath(output_path)
455-
print(f" Saved to: {output_path}")
456-
457-
print(" ⚠️ Note: Tracked the command process and its children")
458-
print("=" * 60)
459-
460-
raise typer.Exit(exit_code)
461-
462-
463-
@codecarbon.command("monitor", short_help="Monitor your machine's carbon emissions.")
464348
def monitor(
349+
ctx: typer.Context,
465350
measure_power_secs: Annotated[
466-
int, typer.Argument(help="Interval between two measures.")
351+
int, typer.Option(help="Interval between two measures.")
467352
] = 10,
468353
api_call_interval: Annotated[
469-
int, typer.Argument(help="Number of measures between API calls.")
354+
int, typer.Option(help="Number of measures between API calls.")
470355
] = 30,
471356
api: Annotated[
472357
bool, typer.Option(help="Choose to call Code Carbon API or not")
@@ -480,18 +365,25 @@ def monitor(
480365
] = None,
481366
):
482367
"""Monitor your machine's carbon emissions."""
368+
369+
# Shared tracker args so monitor and run_and_monitor behave the same
370+
tracker_args = {
371+
"measure_power_secs": measure_power_secs,
372+
"api_call_interval": api_call_interval,
373+
}
374+
# Set up the tracker arguments based on mode (offline vs online) and validate required args for each mode
483375
if offline:
484376
if not country_iso_code:
485377
print(
486378
"ERROR: country_iso_code is required for offline mode", file=sys.stderr
487379
)
488380
raise typer.Exit(1)
489381

490-
tracker = OfflineEmissionsTracker(
491-
measure_power_secs=measure_power_secs,
492-
country_iso_code=country_iso_code,
493-
region=region,
494-
)
382+
tracker_args = {
383+
**tracker_args,
384+
"country_iso_code": country_iso_code,
385+
"region": region,
386+
}
495387
else:
496388
experiment_id = get_existing_local_exp_id()
497389
if api and experiment_id is None:
@@ -501,11 +393,17 @@ def monitor(
501393
)
502394
raise typer.Exit(1)
503395

504-
tracker = EmissionsTracker(
505-
measure_power_secs=measure_power_secs,
506-
api_call_interval=api_call_interval,
507-
save_to_api=api,
508-
)
396+
tracker_args = {**tracker_args, "save_to_api": api}
397+
398+
# If extra args are provided (e.g. `codecarbon monitor -- my_script.py`), delegate to `run_and_monitor`
399+
if getattr(ctx, "args", None):
400+
return run_and_monitor(ctx, **tracker_args)
401+
402+
# Instantiate the tracker
403+
if offline:
404+
tracker = OfflineEmissionsTracker(**tracker_args)
405+
else:
406+
tracker = EmissionsTracker(**tracker_args)
509407

510408
def signal_handler(signum, frame):
511409
print("\nReceived signal to stop. Saving emissions data...")

codecarbon/cli/monitor.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""CodeCarbon CLI - Monitor Command"""
2+
3+
import os
4+
import subprocess
5+
import sys
6+
7+
import typer
8+
from rich import print
9+
from typing_extensions import Annotated
10+
11+
from codecarbon.emissions_tracker import EmissionsTracker
12+
13+
14+
def run_and_monitor(
15+
ctx: typer.Context,
16+
log_level: Annotated[
17+
str, typer.Option(help="Log level (critical, error, warning, info, debug)")
18+
] = "error",
19+
**tracker_args,
20+
):
21+
"""
22+
Run a command and track its carbon emissions.
23+
24+
This command wraps any executable and measures the process's total power
25+
consumption during its execution. When the command completes, a summary
26+
report is displayed and emissions data is saved to a CSV file.
27+
28+
Note: This tracks process-level emissions (only the specific command), not the
29+
entire machine. For machine-level tracking, use the `monitor` command.
30+
31+
Examples:
32+
33+
Do not use quotes around the command. Use -- to separate CodeCarbon args.
34+
35+
# Run any shell command:
36+
codecarbon monitor -- ./benchmark.sh
37+
38+
# Commands with arguments (use single quotes for special chars):
39+
codecarbon monitor -- python -c 'print("Hello World!")'
40+
41+
# Pipe the command output:
42+
codecarbon monitor -- npm run test > output.txt
43+
44+
# Display the CodeCarbon detailed logs:
45+
codecarbon monitor --log-level debug -- python --version
46+
47+
The emissions data is appended to emissions.csv (default) in the current
48+
directory. The file path is shown in the final report.
49+
"""
50+
# Suppress all CodeCarbon logs during execution
51+
from codecarbon.external.logger import set_logger_level
52+
53+
set_logger_level(log_level)
54+
55+
# Get the command from remaining args
56+
command = ctx.args
57+
58+
if not command:
59+
print(
60+
"ERROR: No command provided. Use: codecarbon monitor -- <command>",
61+
file=sys.stderr,
62+
)
63+
raise typer.Exit(1)
64+
65+
# Initialize tracker with specified logging level and shared args
66+
tracker = EmissionsTracker(
67+
log_level=log_level,
68+
save_to_logger=False,
69+
tracking_mode="process",
70+
**tracker_args,
71+
)
72+
73+
print("🌱 CodeCarbon: Starting emissions tracking...")
74+
print(f" Command: {' '.join(command)}")
75+
print()
76+
77+
tracker.start()
78+
79+
process = None
80+
try:
81+
# Run the command, streaming output to console
82+
process = subprocess.Popen(
83+
command,
84+
stdout=sys.stdout,
85+
stderr=sys.stderr,
86+
text=True,
87+
)
88+
89+
# Wait for completion
90+
exit_code = process.wait()
91+
92+
except FileNotFoundError:
93+
print(f"❌ Error: Command not found: {command[0]}", file=sys.stderr)
94+
exit_code = 127
95+
except KeyboardInterrupt:
96+
print("\n⚠️ Interrupted by user", file=sys.stderr)
97+
if process is not None:
98+
process.terminate()
99+
try:
100+
process.wait(timeout=5)
101+
except subprocess.TimeoutExpired:
102+
process.kill()
103+
exit_code = 130
104+
except Exception as e:
105+
print(f"❌ Error running command: {e}", file=sys.stderr)
106+
exit_code = 1
107+
finally:
108+
emissions = tracker.stop()
109+
print()
110+
print("=" * 60)
111+
print("🌱 CodeCarbon Emissions Report")
112+
print("=" * 60)
113+
print(f" Command: {' '.join(command)}")
114+
if emissions is not None:
115+
print(f" Emissions: {emissions * 1000:.4f} g CO2eq")
116+
else:
117+
print(" Emissions: N/A")
118+
119+
# Show where the data was saved
120+
if hasattr(tracker, "_conf") and "output_file" in tracker._conf:
121+
output_path = tracker._conf["output_file"]
122+
# Make it absolute if it's relative
123+
if not os.path.isabs(output_path):
124+
output_path = os.path.abspath(output_path)
125+
print(f" Saved to: {output_path}")
126+
127+
print(" ⚠️ Note: Tracked the command process and its children")
128+
print("=" * 60)
129+
130+
raise typer.Exit(exit_code)

docs/edit/usage.rst

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ The command line could also works without internet by providing the country code
7070
Running Any Command with CodeCarbon
7171
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7272

73-
If you want to track emissions while running any command or program (not just Python scripts), you can use the ``codecarbon run`` command.
73+
If you want to track emissions while running any command or program (not just Python scripts), you can use the ``codecarbon monitor --`` command.
7474
This allows non-Python users to measure machine emissions during the execution of any command:
7575

7676
.. code-block:: console
7777
78-
codecarbon run -- <your_command>
78+
codecarbon monitor -- <your_command>
7979
8080
Do not surround ``<your_command>`` with quotes. The double hyphen ``--`` indicates the end of CodeCarbon options and the beginning of the command to run.
8181

@@ -84,22 +84,22 @@ Do not surround ``<your_command>`` with quotes. The double hyphen ``--`` indicat
8484
.. code-block:: console
8585
8686
# Run a shell script
87-
codecarbon run -- ./benchmark.sh
87+
codecarbon monitor -- ./benchmark.sh
8888
8989
# Run a command with arguments (use quotes for special characters)
90-
codecarbon run -- bash -c 'echo "Processing..."; sleep 30; echo "Done!"'
90+
codecarbon monitor -- bash -c 'echo "Processing..."; sleep 30; echo "Done!"'
9191
9292
# Run Python scripts
93-
codecarbon run -- python train_model.py
93+
codecarbon monitor -- python train_model.py
9494
9595
# Run Node.js applications
96-
codecarbon run -- node app.js
96+
codecarbon monitor -- node app.js
9797
9898
# Run tests with output redirection
99-
codecarbon run -- npm run test > output.txt
99+
codecarbon monitor -- npm run test > output.txt
100100
101101
# Display the CodeCarbon detailed logs
102-
codecarbon run --log-level debug -- python --version
102+
codecarbon monitor --log-level debug -- python --version
103103
104104
**Output:**
105105

@@ -123,7 +123,7 @@ When the command completes, CodeCarbon displays a summary report and saves the e
123123
============================================================
124124
125125
.. note::
126-
The ``codecarbon run`` command tracks process-level emissions (only the specific command), not the
126+
The ``codecarbon monitor --`` command tracks process-level emissions (only the specific command), not the
127127
entire machine. For machine-level tracking, use the ``codecarbon monitor`` command.
128128

129129
For more fine-grained tracking, implementing CodeCarbon in your code allows you to track the emissions of a specific block of code.

0 commit comments

Comments
 (0)