modal-cuda-runner 0.1.0__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.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Corbin Platti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN THE
21
+ SOFTWARE.</content>
22
+ </invoke>
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: modal-cuda-runner
3
+ Version: 0.1.0
4
+ Summary: Run CUDA files on Modal from your local machine
5
+ Author-email: Corbin Platti <csplatti@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/csplatti/modal-cuda-runner
8
+ Project-URL: Repository, https://github.com/csplatti/modal-cuda-runner
9
+ Project-URL: Issues, https://github.com/csplatti/modal-cuda-runner/issues
10
+ Keywords: cuda,modal,gpu,nvcc,cli
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Environment :: GPU :: NVIDIA CUDA
13
+ Classifier: Topic :: Software Development :: Compilers
14
+ Classifier: Topic :: System :: Distributed Computing
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: modal>=1.0
19
+ Requires-Dist: rich>=13.0
20
+ Dynamic: license-file
21
+
22
+ # mcr — Modal CUDA Runner
23
+
24
+ Run a CUDA `.cu` file on an NVIDIA GPU from Modal, from anywhere on your machine, in one command. No local GPU required.
25
+
26
+ ## Install
27
+
28
+ ```
29
+ pip install git+https://github.com/<your-handle>/modal-cuda-runner
30
+ modal setup # one-time Modal authentication
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```
36
+ $ mcr test.cu
37
+ → Running test.cu on T4
38
+ GPU: Tesla T4
39
+ Vector length: 1048576
40
+ Errors: 0
41
+ Sample: c[0]=3.0 c[1048575]=3.0
42
+ ✓ Finished executing on Modal (3.42s)
43
+ ```
44
+
45
+ Pick a different GPU with `--gpu`:
46
+
47
+ ```
48
+ mcr test.cu --gpu H100
49
+ ```
50
+
51
+ Supported: `T4` (default), `L4`, `A10G`, `A100`, `A100-80GB`, `H100`.
52
+
53
+ The CUDA program's stdout is forwarded to your stdout and its exit code becomes `mcr`'s exit code, so `mcr` composes cleanly with shell pipes and CI.
54
+
55
+ ## How it works
56
+
57
+ `mcr` reads your `.cu` file locally and sends the **source as a function argument** to a Modal function running an `nvidia/cuda:12.9.0-devel-ubuntu22.04` image. Because the image never changes across invocations, Modal caches it forever — no per-run image rebuild.
58
+
59
+ On the remote container: `nvcc` compiles the source, then the resulting binary runs on the requested GPU. stdout streams back as it's produced.
60
+
61
+ - First run: slower — container cold start on Modal's side.
62
+ - Subsequent runs: typically a few seconds for small kernels.
63
+
64
+ ## Not yet supported
65
+
66
+ Intentionally scoped small for v1:
67
+ - Multi-file projects / `.cuh` headers
68
+ - Passing CLI args or stdin to the CUDA binary
69
+ - Custom `nvcc` flags
@@ -0,0 +1,48 @@
1
+ # mcr — Modal CUDA Runner
2
+
3
+ Run a CUDA `.cu` file on an NVIDIA GPU from Modal, from anywhere on your machine, in one command. No local GPU required.
4
+
5
+ ## Install
6
+
7
+ ```
8
+ pip install git+https://github.com/<your-handle>/modal-cuda-runner
9
+ modal setup # one-time Modal authentication
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```
15
+ $ mcr test.cu
16
+ → Running test.cu on T4
17
+ GPU: Tesla T4
18
+ Vector length: 1048576
19
+ Errors: 0
20
+ Sample: c[0]=3.0 c[1048575]=3.0
21
+ ✓ Finished executing on Modal (3.42s)
22
+ ```
23
+
24
+ Pick a different GPU with `--gpu`:
25
+
26
+ ```
27
+ mcr test.cu --gpu H100
28
+ ```
29
+
30
+ Supported: `T4` (default), `L4`, `A10G`, `A100`, `A100-80GB`, `H100`.
31
+
32
+ The CUDA program's stdout is forwarded to your stdout and its exit code becomes `mcr`'s exit code, so `mcr` composes cleanly with shell pipes and CI.
33
+
34
+ ## How it works
35
+
36
+ `mcr` reads your `.cu` file locally and sends the **source as a function argument** to a Modal function running an `nvidia/cuda:12.9.0-devel-ubuntu22.04` image. Because the image never changes across invocations, Modal caches it forever — no per-run image rebuild.
37
+
38
+ On the remote container: `nvcc` compiles the source, then the resulting binary runs on the requested GPU. stdout streams back as it's produced.
39
+
40
+ - First run: slower — container cold start on Modal's side.
41
+ - Subsequent runs: typically a few seconds for small kernels.
42
+
43
+ ## Not yet supported
44
+
45
+ Intentionally scoped small for v1:
46
+ - Multi-file projects / `.cuh` headers
47
+ - Passing CLI args or stdin to the CUDA binary
48
+ - Custom `nvcc` flags
File without changes
@@ -0,0 +1,99 @@
1
+ import argparse
2
+ import pathlib
3
+ import sys
4
+ import time
5
+ import modal
6
+
7
+ # Relative import (`from .remote`) instead of `from mcr.remote`
8
+ from .remote import app, run_cuda
9
+ from rich.console import Console
10
+ from rich.progress import Progress, ProgressColumn, SpinnerColumn, TextColumn
11
+ from rich.text import Text
12
+
13
+ # `Console(stderr=True)` sends all cosmetic output to stderr so the CUDA
14
+ # program's own stdout stays clean
15
+ console = Console(stderr=True)
16
+
17
+ class _SecondsElapsedColumn(ProgressColumn):
18
+ def render(self, task) -> Text:
19
+ elapsed = task.elapsed
20
+ if elapsed is None:
21
+ return Text("--", style="cyan")
22
+ return Text(f"{elapsed:.2f}s", style="cyan")
23
+
24
+ # Hardcoded GPU list. Modal doesn't expose a "valid GPUs" API, so typos would
25
+ # otherwise fail at Modal call time — after you've already waited on a container.
26
+ GPU_CHOICES = ["T4", "L4", "A10G", "A100", "A100-80GB", "H100"]
27
+
28
+ def main() -> None:
29
+ args = receive_args().parse_args()
30
+
31
+ path = pathlib.Path(args.file)
32
+ if not path.is_file():
33
+ print(f"mcr: {path}: no such file", file=sys.stderr)
34
+ sys.exit(1)
35
+
36
+ source = path.read_text()
37
+
38
+ fn = run_cuda if args.gpu == "T4" else run_cuda.with_options(gpu=args.gpu)
39
+
40
+ try:
41
+ result, elapsed = run_on_modal(args, path, source, fn)
42
+ print_oneline_summary(result, elapsed)
43
+
44
+
45
+ sys.stdout.write(result["stdout"])
46
+ sys.stderr.write(result["stderr"])
47
+ sys.exit(result["returncode"])
48
+ except modal.exception.AuthError:
49
+ console.print("[red]✗[/red] Not logged in to Modal. Run [cyan]modal setup[/cyan] to authenticate.")
50
+ sys.exit(1)
51
+
52
+
53
+ def run_on_modal(args, path, source, fn):
54
+ console.print(
55
+ f"[dim]→[/dim] Running [cyan]{path.name}[/cyan] on [magenta]{args.gpu}[/magenta]"
56
+ )
57
+
58
+ start = time.monotonic()
59
+ with Progress(
60
+ SpinnerColumn(),
61
+ TextColumn("[cyan]Executing on Modal..."),
62
+ _SecondsElapsedColumn(),
63
+ console=console,
64
+ transient=True,
65
+ ) as progress:
66
+ progress.add_task("", total=None)
67
+ with app.run():
68
+ result = fn.remote(source, filename=path.name)
69
+ elapsed = time.monotonic() - start
70
+ return result, elapsed
71
+
72
+ def receive_args():
73
+ parser = argparse.ArgumentParser(
74
+ prog="mcr",
75
+ description="Compile and run a CUDA source file on Modal.",
76
+ )
77
+ parser.add_argument("file", help="path to a .cu source file")
78
+ parser.add_argument(
79
+ "--gpu",
80
+ default="T4",
81
+ choices=GPU_CHOICES,
82
+ help="GPU to run on (default: T4)",
83
+ )
84
+
85
+ return parser
86
+
87
+ def print_oneline_summary(result, elapsed):
88
+ if result["stage"] == "compile" and result["returncode"] != 0:
89
+ console.print(
90
+ f"[red]✗[/red] Compile failed (exit {result['returncode']}) ({elapsed:.2f}s)"
91
+ )
92
+ elif result["returncode"] != 0:
93
+ console.print(
94
+ f"[yellow]✗[/yellow] Exited with code {result['returncode']} ({elapsed:.2f}s)"
95
+ )
96
+ else:
97
+ console.print(
98
+ f"[green]✓[/green] Finished executing on Modal ({elapsed:.2f}s)"
99
+ )
@@ -0,0 +1,61 @@
1
+ import modal
2
+ import pathlib
3
+ import subprocess
4
+ import tempfile
5
+
6
+ cuda_image = (
7
+ modal.Image.from_registry(
8
+ "nvidia/cuda:12.9.0-devel-ubuntu22.04",
9
+ add_python="3.11",
10
+ )
11
+ .run_commands("rm -rf /opt/nvidia/entrypoint.d")
12
+ )
13
+
14
+ app = modal.App("mcr")
15
+
16
+
17
+ @app.function(image=cuda_image, gpu="T4")
18
+
19
+ def run_cuda(source: str, filename: str = "kernel.cu") -> dict:
20
+ with tempfile.TemporaryDirectory() as tmpdir:
21
+ tmp = pathlib.Path(tmpdir)
22
+ src_path = tmp / filename
23
+ bin_path = tmp / "a.out"
24
+ src_path.write_text(source)
25
+
26
+ compile_proc = subprocess.run(
27
+ ["nvcc", str(src_path), "-o", str(bin_path)],
28
+ capture_output=True,
29
+ text=True,
30
+ )
31
+
32
+ if compile_proc.returncode != 0:
33
+ return {
34
+ "stage": "compile",
35
+ "stdout": compile_proc.stdout,
36
+ "stderr": compile_proc.stderr,
37
+ "returncode": compile_proc.returncode,
38
+ }
39
+
40
+ run_proc = subprocess.Popen(
41
+ [str(bin_path)],
42
+ stdout=subprocess.PIPE,
43
+ stderr=subprocess.STDOUT,
44
+ text=True,
45
+ bufsize=1,
46
+ errors="replace",
47
+ )
48
+ PREVIEW_ON = "\033[2;36m"
49
+ PREVIEW_OFF = "\033[0m"
50
+ captured = []
51
+ for line in run_proc.stdout:
52
+ captured.append(line)
53
+ print(f"{PREVIEW_ON} {line.rstrip()}{PREVIEW_OFF}", flush=True)
54
+ run_proc.wait()
55
+
56
+ return {
57
+ "stage": "run",
58
+ "stdout": "".join(captured),
59
+ "stderr": "",
60
+ "returncode": run_proc.returncode,
61
+ }
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: modal-cuda-runner
3
+ Version: 0.1.0
4
+ Summary: Run CUDA files on Modal from your local machine
5
+ Author-email: Corbin Platti <csplatti@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/csplatti/modal-cuda-runner
8
+ Project-URL: Repository, https://github.com/csplatti/modal-cuda-runner
9
+ Project-URL: Issues, https://github.com/csplatti/modal-cuda-runner/issues
10
+ Keywords: cuda,modal,gpu,nvcc,cli
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Environment :: GPU :: NVIDIA CUDA
13
+ Classifier: Topic :: Software Development :: Compilers
14
+ Classifier: Topic :: System :: Distributed Computing
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: modal>=1.0
19
+ Requires-Dist: rich>=13.0
20
+ Dynamic: license-file
21
+
22
+ # mcr — Modal CUDA Runner
23
+
24
+ Run a CUDA `.cu` file on an NVIDIA GPU from Modal, from anywhere on your machine, in one command. No local GPU required.
25
+
26
+ ## Install
27
+
28
+ ```
29
+ pip install git+https://github.com/<your-handle>/modal-cuda-runner
30
+ modal setup # one-time Modal authentication
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```
36
+ $ mcr test.cu
37
+ → Running test.cu on T4
38
+ GPU: Tesla T4
39
+ Vector length: 1048576
40
+ Errors: 0
41
+ Sample: c[0]=3.0 c[1048575]=3.0
42
+ ✓ Finished executing on Modal (3.42s)
43
+ ```
44
+
45
+ Pick a different GPU with `--gpu`:
46
+
47
+ ```
48
+ mcr test.cu --gpu H100
49
+ ```
50
+
51
+ Supported: `T4` (default), `L4`, `A10G`, `A100`, `A100-80GB`, `H100`.
52
+
53
+ The CUDA program's stdout is forwarded to your stdout and its exit code becomes `mcr`'s exit code, so `mcr` composes cleanly with shell pipes and CI.
54
+
55
+ ## How it works
56
+
57
+ `mcr` reads your `.cu` file locally and sends the **source as a function argument** to a Modal function running an `nvidia/cuda:12.9.0-devel-ubuntu22.04` image. Because the image never changes across invocations, Modal caches it forever — no per-run image rebuild.
58
+
59
+ On the remote container: `nvcc` compiles the source, then the resulting binary runs on the requested GPU. stdout streams back as it's produced.
60
+
61
+ - First run: slower — container cold start on Modal's side.
62
+ - Subsequent runs: typically a few seconds for small kernels.
63
+
64
+ ## Not yet supported
65
+
66
+ Intentionally scoped small for v1:
67
+ - Multi-file projects / `.cuh` headers
68
+ - Passing CLI args or stdin to the CUDA binary
69
+ - Custom `nvcc` flags
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ mcr/__init__.py
5
+ mcr/cli.py
6
+ mcr/remote.py
7
+ modal_cuda_runner.egg-info/PKG-INFO
8
+ modal_cuda_runner.egg-info/SOURCES.txt
9
+ modal_cuda_runner.egg-info/dependency_links.txt
10
+ modal_cuda_runner.egg-info/entry_points.txt
11
+ modal_cuda_runner.egg-info/requires.txt
12
+ modal_cuda_runner.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mcr = mcr.cli:main
@@ -0,0 +1,2 @@
1
+ modal>=1.0
2
+ rich>=13.0
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "modal-cuda-runner"
7
+ version = "0.1.0"
8
+ description = "Run CUDA files on Modal from your local machine"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ authors = [{name = "Corbin Platti", email = "csplatti@gmail.com"}]
12
+ requires-python = ">=3.9"
13
+ dependencies = ["modal>=1.0", "rich>=13.0"]
14
+ keywords = ["cuda", "modal", "gpu", "nvcc", "cli"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Environment :: GPU :: NVIDIA CUDA",
18
+ "Topic :: Software Development :: Compilers",
19
+ "Topic :: System :: Distributed Computing",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/csplatti/modal-cuda-runner"
24
+ Repository = "https://github.com/csplatti/modal-cuda-runner"
25
+ Issues = "https://github.com/csplatti/modal-cuda-runner/issues"
26
+
27
+ [project.scripts]
28
+ mcr = "mcr.cli:main"
29
+
30
+ [tool.setuptools.packages.find]
31
+ include = ["mcr*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+