Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
112 changes: 112 additions & 0 deletions codecarbon/cli/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import signal
import subprocess
import sys
import time
from pathlib import Path
Expand Down Expand Up @@ -339,6 +340,117 @@ def config():
)


@codecarbon.command(
"run",
short_help="Run a command and track its emissions.",
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
)
def run(
ctx: typer.Context,
):
"""
Run a command and track its carbon emissions.

This command wraps any executable and measures the machine's total power
consumption during its execution. When the command completes, a summary
report is displayed and emissions data is saved to a CSV file.

Note: This tracks machine-level emissions (entire system), not just the
specific command. For process-specific tracking, the command must be
instrumented with the CodeCarbon Python library.

Examples:

# Run any shell command
codecarbon run -- ./benchmark.sh

# Commands with arguments (use single quotes for special chars)
codecarbon run -- python -c 'print("Hello World!")'

# Pipe the command output
codecarbon run -- npm run test > output.txt

The emissions data is appended to emissions.csv (default) in the current
directory. The file path is shown in the final report.
"""
# Suppress all CodeCarbon logs during execution
from codecarbon.external.logger import set_logger_level

set_logger_level("critical")

# Get the command from remaining args
command = ctx.args

if not command:
print(
"ERROR: No command provided. Use: codecarbon run -- <command>",
file=sys.stderr,
)
raise typer.Exit(1)

# Initialize tracker with minimal logging
tracker = EmissionsTracker(log_level="error", save_to_logger=False)

print("🌱 CodeCarbon: Starting emissions tracking...")
print(f" Command: {' '.join(command)}")
print()

tracker.start()

process = None
try:
# Run the command, streaming output to console
process = subprocess.Popen(
command,
stdout=sys.stdout,
stderr=sys.stderr,
text=True,
)

# Wait for completion
exit_code = process.wait()

except FileNotFoundError:
print(f"❌ Error: Command not found: {command[0]}", file=sys.stderr)
exit_code = 127
except KeyboardInterrupt:
print("\n⚠️ Interrupted by user", file=sys.stderr)
if process is not None:
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
exit_code = 130
except Exception as e:
print(f"❌ Error running command: {e}", file=sys.stderr)
exit_code = 1
finally:
emissions = tracker.stop()
print()
print("=" * 60)
print("🌱 CodeCarbon Emissions Report")
print("=" * 60)
print(f" Command: {' '.join(command)}")
if emissions is not None:
print(f" Emissions: {emissions * 1000:.4f} g CO2eq")
else:
print(" Emissions: N/A")

# Show where the data was saved
if hasattr(tracker, "_conf") and "output_file" in tracker._conf:
output_path = tracker._conf["output_file"]
# Make it absolute if it's relative
if not os.path.isabs(output_path):
output_path = os.path.abspath(output_path)
print(f" Saved to: {output_path}")

print(" ⚠️ Note: Measured entire machine (includes all system processes)")
print("=" * 60)

raise typer.Exit(exit_code)


@codecarbon.command("monitor", short_help="Monitor your machine's carbon emissions.")
def monitor(
measure_power_secs: Annotated[
Expand Down
10 changes: 9 additions & 1 deletion examples/command_line_tool.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
"""
This example demonstrates how to use CodeCarbon with command line tools.

Here we measure the emissions of an speech-to-text with WhisperX.
⚠️ IMPORTANT LIMITATION:
CodeCarbon tracks emissions at the MACHINE level when monitoring external commands
via subprocess. It measures total system power during the command execution, which
includes the command itself AND all other system processes.

For accurate process-level tracking, the tracking code must be embedded in the
application being measured (not possible with external binaries like WhisperX).

This example measures emissions during WhisperX execution, but cannot isolate
WhisperX's exact contribution from other system activity.
"""

import subprocess
Expand Down
Loading