tatvastack-codeforge 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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TatvaStack
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 OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.4
2
+ Name: tatvastack-codeforge
3
+ Version: 0.1.0
4
+ Summary: Official CodeForge SDK: run Python, JavaScript, Java and C++ in isolated, network-free sandboxes with one API.
5
+ Author-email: TatvaStack <bhaskar@tatvastack.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://codeforge.tatvastack.com
8
+ Project-URL: Documentation, https://codeforge.tatvastack.com/docs/
9
+ Keywords: codeforge,code execution,code execution api,sandbox,compiler api,online compiler,code runner,judge
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Provides-Extra: certifi
20
+ Requires-Dist: certifi; extra == "certifi"
21
+ Dynamic: license-file
22
+
23
+ # CodeForge SDK for Python
24
+
25
+ Run Python, JavaScript, Java and C++ in isolated, network-free sandboxes. Python 3.8+, standard library only.
26
+
27
+ ```bash
28
+ pip install tatvastack-codeforge
29
+ ```
30
+
31
+ The package installs as `tatvastack-codeforge` and imports as `codeforge`.
32
+
33
+ ```python
34
+ import os
35
+ from codeforge import CodeForge
36
+
37
+ cf = CodeForge(api_key=os.environ["CODEFORGE_API_KEY"]) # keep keys server-side
38
+
39
+ result = cf.run("python", "print(input()[::-1])", stdin="hello")
40
+ print(result["status"], result["stdout"]) # SUCCEEDED olleh
41
+ ```
42
+
43
+ - `run(...)` submits and waits; `submit(...)` + `wait(id)` / `get(id)` for your own flow; `usage()` for quota.
44
+ - Every `submit` sends an `Idempotency-Key` (a UUID, or pass `idempotency_key=`). Network errors, 5xx and `CONCURRENCY_LIMIT` are retried with the **same** key, so a retry never runs code twice or charges twice.
45
+ - A compile error or non-zero exit is a normal result: check `result["status"]`. Exceptions are `CodeForgeError` with `code`, `status`, `request_id` and `retryable`.
46
+
47
+ ```python
48
+ from codeforge import CodeForgeError
49
+ try:
50
+ cf.run("cpp", source, time_ms=2000)
51
+ except CodeForgeError as e:
52
+ if e.code == "QUOTA_EXCEEDED":
53
+ print("Resets at", e.details["resetsAt"])
54
+ else:
55
+ raise
56
+ ```
57
+
58
+ **TLS on macOS:** python.org builds ship without CA certificates. If you see `TLS_ERROR`, run `pip install "tatvastack-codeforge[certifi]"` (the SDK uses it automatically) or *Install Certificates.command*. Behind a corporate proxy, pass `ssl_context=ssl.create_default_context(cafile=...)`.
59
+
60
+ Get an API key from the [CodeForge dashboard](https://codeforge.tatvastack.com/app/) (private beta). Full API reference: https://codeforge.tatvastack.com/docs/
61
+
62
+ MIT licensed. Made by [TatvaStack](https://tatvastack.com).
@@ -0,0 +1,40 @@
1
+ # CodeForge SDK for Python
2
+
3
+ Run Python, JavaScript, Java and C++ in isolated, network-free sandboxes. Python 3.8+, standard library only.
4
+
5
+ ```bash
6
+ pip install tatvastack-codeforge
7
+ ```
8
+
9
+ The package installs as `tatvastack-codeforge` and imports as `codeforge`.
10
+
11
+ ```python
12
+ import os
13
+ from codeforge import CodeForge
14
+
15
+ cf = CodeForge(api_key=os.environ["CODEFORGE_API_KEY"]) # keep keys server-side
16
+
17
+ result = cf.run("python", "print(input()[::-1])", stdin="hello")
18
+ print(result["status"], result["stdout"]) # SUCCEEDED olleh
19
+ ```
20
+
21
+ - `run(...)` submits and waits; `submit(...)` + `wait(id)` / `get(id)` for your own flow; `usage()` for quota.
22
+ - Every `submit` sends an `Idempotency-Key` (a UUID, or pass `idempotency_key=`). Network errors, 5xx and `CONCURRENCY_LIMIT` are retried with the **same** key, so a retry never runs code twice or charges twice.
23
+ - A compile error or non-zero exit is a normal result: check `result["status"]`. Exceptions are `CodeForgeError` with `code`, `status`, `request_id` and `retryable`.
24
+
25
+ ```python
26
+ from codeforge import CodeForgeError
27
+ try:
28
+ cf.run("cpp", source, time_ms=2000)
29
+ except CodeForgeError as e:
30
+ if e.code == "QUOTA_EXCEEDED":
31
+ print("Resets at", e.details["resetsAt"])
32
+ else:
33
+ raise
34
+ ```
35
+
36
+ **TLS on macOS:** python.org builds ship without CA certificates. If you see `TLS_ERROR`, run `pip install "tatvastack-codeforge[certifi]"` (the SDK uses it automatically) or *Install Certificates.command*. Behind a corporate proxy, pass `ssl_context=ssl.create_default_context(cafile=...)`.
37
+
38
+ Get an API key from the [CodeForge dashboard](https://codeforge.tatvastack.com/app/) (private beta). Full API reference: https://codeforge.tatvastack.com/docs/
39
+
40
+ MIT licensed. Made by [TatvaStack](https://tatvastack.com).
@@ -0,0 +1,169 @@
1
+ """CodeForge SDK for Python 3.8+. Standard library only.
2
+
3
+ from codeforge import CodeForge
4
+ cf = CodeForge(api_key=os.environ["CODEFORGE_API_KEY"])
5
+ result = cf.run("python", "print(6 * 7)")
6
+ print(result["status"], result["stdout"]) # SUCCEEDED 42
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import random
13
+ import ssl
14
+ import time
15
+ import urllib.error
16
+ import urllib.request
17
+ import uuid
18
+ from typing import Any, Dict, Optional
19
+
20
+ __all__ = ["CodeForge", "CodeForgeError", "DEFAULT_BASE_URL", "TERMINAL_STATUSES"]
21
+ __version__ = "0.1.0"
22
+
23
+ DEFAULT_BASE_URL = "https://api.tatvastack.com"
24
+ TERMINAL_STATUSES = frozenset({"SUCCEEDED", "COMPILE_ERROR", "RUNTIME_ERROR", "TIMED_OUT", "PLATFORM_ERROR"})
25
+
26
+
27
+ class CodeForgeError(Exception):
28
+ """An API error, or a client-side failure (NETWORK_ERROR, WAIT_TIMEOUT)."""
29
+
30
+ def __init__(self, message: str, code: str = "UNKNOWN", status: Optional[int] = None,
31
+ request_id: Optional[str] = None, retryable: bool = False, details: Optional[dict] = None):
32
+ super().__init__(message)
33
+ self.message = message
34
+ self.code = code
35
+ self.status = status
36
+ self.request_id = request_id
37
+ self.retryable = retryable
38
+ self.details = details or {}
39
+
40
+ def __repr__(self) -> str:
41
+ return f"CodeForgeError(code={self.code!r}, status={self.status}, request_id={self.request_id!r}, message={self.message!r})"
42
+
43
+
44
+ def _default_ssl_context() -> ssl.SSLContext:
45
+ """System CA store, or certifi's when installed (fixes python.org builds on macOS)."""
46
+ try:
47
+ import certifi # type: ignore
48
+ return ssl.create_default_context(cafile=certifi.where())
49
+ except ImportError:
50
+ return ssl.create_default_context()
51
+
52
+
53
+ _TLS_HINT = (
54
+ "TLS certificate verification failed. Your Python has no trusted CA certificates. "
55
+ "Fix: `pip install certifi` (the SDK uses it automatically), or on macOS run "
56
+ "'Install Certificates.command' from your Python folder, or pass ssl_context=..."
57
+ )
58
+
59
+
60
+ def _backoff(attempt: int) -> float:
61
+ return min(8.0, 0.3 * 2 ** (attempt - 1)) * (0.5 + random.random() / 2)
62
+
63
+
64
+ class CodeForge:
65
+ """Client for the CodeForge execution API.
66
+
67
+ Args:
68
+ api_key: Your ``cf_live_`` key. Keep it on your server.
69
+ base_url: API origin.
70
+ max_retries: Retries for network errors, 5xx responses and concurrency limits.
71
+ request_timeout: Seconds per HTTP request.
72
+ ssl_context: Custom TLS settings (e.g. a corporate CA bundle). Defaults to the system
73
+ store, or certifi's if installed.
74
+ """
75
+
76
+ def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL, max_retries: int = 3, request_timeout: float = 15.0,
77
+ ssl_context: Optional[ssl.SSLContext] = None):
78
+ if not api_key:
79
+ raise CodeForgeError("api_key is required", code="CONFIGURATION")
80
+ self.api_key = api_key
81
+ self.base_url = base_url.rstrip("/")
82
+ self.max_retries = max_retries
83
+ self.request_timeout = request_timeout
84
+ self.ssl_context = ssl_context or _default_ssl_context()
85
+
86
+ def _request(self, method: str, path: str, body: Optional[dict] = None, headers: Optional[Dict[str, str]] = None) -> Any:
87
+ data = json.dumps(body).encode() if body is not None else None
88
+ all_headers = {"Authorization": f"Bearer {self.api_key}", "User-Agent": f"codeforge-python/{__version__}", "Accept": "application/json"}
89
+ if data is not None:
90
+ all_headers["Content-Type"] = "application/json"
91
+ all_headers.update(headers or {})
92
+
93
+ attempt = 0
94
+ while True:
95
+ attempt += 1
96
+ req = urllib.request.Request(self.base_url + path, data=data, method=method, headers=all_headers)
97
+ try:
98
+ with urllib.request.urlopen(req, timeout=self.request_timeout, context=self.ssl_context) as res:
99
+ text = res.read().decode("utf-8")
100
+ return json.loads(text) if text else None
101
+ except urllib.error.HTTPError as http_err:
102
+ text = http_err.read().decode("utf-8", "replace")
103
+ try:
104
+ payload = json.loads(text) if text else {}
105
+ except ValueError:
106
+ payload = {}
107
+ e = payload.get("error", {}) if isinstance(payload, dict) else {}
108
+ error = CodeForgeError(
109
+ e.get("message") or f"HTTP {http_err.code}",
110
+ code=e.get("code") or f"HTTP_{http_err.code}",
111
+ status=http_err.code,
112
+ request_id=e.get("requestId") or http_err.headers.get("x-request-id"),
113
+ retryable=e.get("retryable", http_err.code >= 500),
114
+ details=e,
115
+ )
116
+ if error.retryable and attempt <= self.max_retries:
117
+ time.sleep(_backoff(attempt))
118
+ continue
119
+ raise error from None
120
+ except (urllib.error.URLError, TimeoutError, ConnectionError) as net_err:
121
+ reason = getattr(net_err, "reason", net_err)
122
+ if isinstance(reason, ssl.SSLCertVerificationError):
123
+ # Retrying can't fix a local trust-store problem.
124
+ raise CodeForgeError(_TLS_HINT, code="TLS_ERROR", retryable=False) from None
125
+ # Safe to retry: POSTs carry an Idempotency-Key, so a retry never runs code twice.
126
+ if attempt <= self.max_retries:
127
+ time.sleep(_backoff(attempt))
128
+ continue
129
+ raise CodeForgeError(f"Network error: {net_err}", code="NETWORK_ERROR", retryable=True) from None
130
+
131
+ def submit(self, language: str, source: str, stdin: Optional[str] = None, time_ms: Optional[int] = None,
132
+ idempotency_key: Optional[str] = None) -> dict:
133
+ """Queue an execution and return immediately: ``{"executionId", "status": "QUEUED", ...}``."""
134
+ body: Dict[str, Any] = {"language": language, "source": source}
135
+ if stdin is not None:
136
+ body["stdin"] = stdin
137
+ if time_ms is not None:
138
+ body["limits"] = {"timeMs": time_ms}
139
+ return self._request("POST", "/v1/executions", body, {"Idempotency-Key": idempotency_key or str(uuid.uuid4())})
140
+
141
+ def get(self, execution_id: str) -> dict:
142
+ """Fetch an execution's current state."""
143
+ return self._request("GET", f"/v1/executions/{urllib.request.quote(execution_id, safe='')}")
144
+
145
+ def wait(self, execution_id: str, poll_interval: float = 0.5, timeout: float = 120.0) -> dict:
146
+ """Poll until the execution finishes and return it."""
147
+ deadline = time.monotonic() + timeout
148
+ while True:
149
+ execution = self.get(execution_id)
150
+ if execution.get("status") in TERMINAL_STATUSES:
151
+ return execution
152
+ if time.monotonic() + poll_interval > deadline:
153
+ raise CodeForgeError(f"Execution {execution_id} still {execution.get('status')} after {timeout}s",
154
+ code="WAIT_TIMEOUT", retryable=True, details={"executionId": execution_id})
155
+ time.sleep(poll_interval)
156
+
157
+ def run(self, language: str, source: str, stdin: Optional[str] = None, time_ms: Optional[int] = None,
158
+ idempotency_key: Optional[str] = None, poll_interval: float = 0.5, timeout: float = 120.0) -> dict:
159
+ """Submit code and wait for the result.
160
+
161
+ A non-zero exit or compile error is a normal result (check ``status``), not an exception;
162
+ exceptions mean the request itself failed.
163
+ """
164
+ accepted = self.submit(language, source, stdin=stdin, time_ms=time_ms, idempotency_key=idempotency_key)
165
+ return self.wait(accepted["executionId"], poll_interval=poll_interval, timeout=timeout)
166
+
167
+ def usage(self) -> dict:
168
+ """This month's quota, current concurrency and 30-day daily usage."""
169
+ return self._request("GET", "/v1/usage")
File without changes
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ # PyPI's "codeforge" name belongs to an unrelated project; the import name is still `codeforge`.
7
+ name = "tatvastack-codeforge"
8
+ version = "0.1.0"
9
+ description = "Official CodeForge SDK: run Python, JavaScript, Java and C++ in isolated, network-free sandboxes with one API."
10
+ readme = "README.md"
11
+ requires-python = ">=3.8"
12
+ license = "MIT"
13
+ license-files = ["LICENSE"]
14
+ authors = [{ name = "TatvaStack", email = "bhaskar@tatvastack.com" }]
15
+ keywords = ["codeforge", "code execution", "code execution api", "sandbox", "compiler api", "online compiler", "code runner", "judge"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Software Development :: Libraries",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = []
25
+
26
+ [project.optional-dependencies]
27
+ # Fixes certificate errors on python.org builds for macOS; used automatically when installed.
28
+ certifi = ["certifi"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://codeforge.tatvastack.com"
32
+ Documentation = "https://codeforge.tatvastack.com/docs/"
33
+
34
+ [tool.setuptools]
35
+ packages = ["codeforge"]
36
+
37
+ [tool.setuptools.package-data]
38
+ codeforge = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.4
2
+ Name: tatvastack-codeforge
3
+ Version: 0.1.0
4
+ Summary: Official CodeForge SDK: run Python, JavaScript, Java and C++ in isolated, network-free sandboxes with one API.
5
+ Author-email: TatvaStack <bhaskar@tatvastack.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://codeforge.tatvastack.com
8
+ Project-URL: Documentation, https://codeforge.tatvastack.com/docs/
9
+ Keywords: codeforge,code execution,code execution api,sandbox,compiler api,online compiler,code runner,judge
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Provides-Extra: certifi
20
+ Requires-Dist: certifi; extra == "certifi"
21
+ Dynamic: license-file
22
+
23
+ # CodeForge SDK for Python
24
+
25
+ Run Python, JavaScript, Java and C++ in isolated, network-free sandboxes. Python 3.8+, standard library only.
26
+
27
+ ```bash
28
+ pip install tatvastack-codeforge
29
+ ```
30
+
31
+ The package installs as `tatvastack-codeforge` and imports as `codeforge`.
32
+
33
+ ```python
34
+ import os
35
+ from codeforge import CodeForge
36
+
37
+ cf = CodeForge(api_key=os.environ["CODEFORGE_API_KEY"]) # keep keys server-side
38
+
39
+ result = cf.run("python", "print(input()[::-1])", stdin="hello")
40
+ print(result["status"], result["stdout"]) # SUCCEEDED olleh
41
+ ```
42
+
43
+ - `run(...)` submits and waits; `submit(...)` + `wait(id)` / `get(id)` for your own flow; `usage()` for quota.
44
+ - Every `submit` sends an `Idempotency-Key` (a UUID, or pass `idempotency_key=`). Network errors, 5xx and `CONCURRENCY_LIMIT` are retried with the **same** key, so a retry never runs code twice or charges twice.
45
+ - A compile error or non-zero exit is a normal result: check `result["status"]`. Exceptions are `CodeForgeError` with `code`, `status`, `request_id` and `retryable`.
46
+
47
+ ```python
48
+ from codeforge import CodeForgeError
49
+ try:
50
+ cf.run("cpp", source, time_ms=2000)
51
+ except CodeForgeError as e:
52
+ if e.code == "QUOTA_EXCEEDED":
53
+ print("Resets at", e.details["resetsAt"])
54
+ else:
55
+ raise
56
+ ```
57
+
58
+ **TLS on macOS:** python.org builds ship without CA certificates. If you see `TLS_ERROR`, run `pip install "tatvastack-codeforge[certifi]"` (the SDK uses it automatically) or *Install Certificates.command*. Behind a corporate proxy, pass `ssl_context=ssl.create_default_context(cafile=...)`.
59
+
60
+ Get an API key from the [CodeForge dashboard](https://codeforge.tatvastack.com/app/) (private beta). Full API reference: https://codeforge.tatvastack.com/docs/
61
+
62
+ MIT licensed. Made by [TatvaStack](https://tatvastack.com).
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ codeforge/__init__.py
5
+ codeforge/py.typed
6
+ tatvastack_codeforge.egg-info/PKG-INFO
7
+ tatvastack_codeforge.egg-info/SOURCES.txt
8
+ tatvastack_codeforge.egg-info/dependency_links.txt
9
+ tatvastack_codeforge.egg-info/requires.txt
10
+ tatvastack_codeforge.egg-info/top_level.txt
11
+ tests/test_sdk.py
@@ -0,0 +1,3 @@
1
+
2
+ [certifi]
3
+ certifi
@@ -0,0 +1,126 @@
1
+ """Offline tests against a local fake API that behaves like CodeForge (no network, no AWS)."""
2
+
3
+ import json
4
+ import os
5
+ import sys
6
+ import threading
7
+ import unittest
8
+ from http.server import BaseHTTPRequestHandler, HTTPServer
9
+
10
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
11
+ from codeforge import CodeForge, CodeForgeError # noqa: E402
12
+
13
+
14
+ class FakeApi:
15
+ def __init__(self, handler):
16
+ self.calls = []
17
+ outer = self
18
+
19
+ class Handler(BaseHTTPRequestHandler):
20
+ def log_message(self, *args):
21
+ pass
22
+
23
+ def _serve(self):
24
+ length = int(self.headers.get("Content-Length") or 0)
25
+ body = json.loads(self.rfile.read(length)) if length else None
26
+ outer.calls.append({"method": self.command, "path": self.path, "headers": dict(self.headers), "body": body})
27
+ status, payload = handler(self.command, len(outer.calls), body)
28
+ data = json.dumps(payload).encode()
29
+ self.send_response(status)
30
+ self.send_header("Content-Type", "application/json")
31
+ self.send_header("Content-Length", str(len(data)))
32
+ self.end_headers()
33
+ self.wfile.write(data)
34
+
35
+ do_GET = do_POST = _serve
36
+
37
+ self.server = HTTPServer(("127.0.0.1", 0), Handler)
38
+ self.url = f"http://127.0.0.1:{self.server.server_address[1]}"
39
+ threading.Thread(target=self.server.serve_forever, daemon=True).start()
40
+
41
+ def close(self):
42
+ self.server.shutdown()
43
+
44
+
45
+ def err(code, retryable, **extra):
46
+ return {"error": {"code": code, "message": code, "requestId": "req_test", "retryable": retryable, **extra}}
47
+
48
+
49
+ class SdkTest(unittest.TestCase):
50
+ def test_run_submits_with_idempotency_key_and_polls(self):
51
+ state = {"polls": 0}
52
+
53
+ def handler(method, n, body):
54
+ if method == "POST":
55
+ return 202, {"executionId": "exe_1", "status": "QUEUED"}
56
+ state["polls"] += 1
57
+ return 200, {"executionId": "exe_1", "status": "RUNNING" if state["polls"] < 3 else "SUCCEEDED", "stdout": "42\n", "exitCode": 0}
58
+
59
+ api = FakeApi(handler)
60
+ try:
61
+ cf = CodeForge(api_key="cf_live_x", base_url=api.url)
62
+ result = cf.run("python", "print(42)", time_ms=2000, poll_interval=0.01)
63
+ self.assertEqual(result["status"], "SUCCEEDED")
64
+ self.assertEqual(result["stdout"], "42\n")
65
+ post = api.calls[0]
66
+ self.assertEqual(post["headers"]["Authorization"], "Bearer cf_live_x")
67
+ self.assertRegex(post["headers"]["Idempotency-Key"], r"^[0-9a-f-]{36}$")
68
+ self.assertEqual(post["body"], {"language": "python", "source": "print(42)", "limits": {"timeMs": 2000}})
69
+ self.assertEqual(len(api.calls), 4)
70
+ finally:
71
+ api.close()
72
+
73
+ def test_retries_reuse_the_same_idempotency_key(self):
74
+ def handler(method, n, body):
75
+ if n == 1:
76
+ return 500, err("INTERNAL_ERROR", True)
77
+ if n == 2:
78
+ return 429, err("CONCURRENCY_LIMIT", True)
79
+ return 202, {"executionId": "exe_2", "status": "QUEUED"}
80
+
81
+ api = FakeApi(handler)
82
+ try:
83
+ accepted = CodeForge(api_key="k", base_url=api.url).submit("cpp", "int main(){}")
84
+ self.assertEqual(accepted["executionId"], "exe_2")
85
+ self.assertEqual(len(api.calls), 3)
86
+ self.assertEqual(len({c["headers"]["Idempotency-Key"] for c in api.calls}), 1)
87
+ finally:
88
+ api.close()
89
+
90
+ def test_non_retryable_error_is_raised_with_details(self):
91
+ api = FakeApi(lambda m, n, b: (429, err("QUOTA_EXCEEDED", False, resetsAt="2026-12-01T00:00:00Z")))
92
+ try:
93
+ with self.assertRaises(CodeForgeError) as ctx:
94
+ CodeForge(api_key="k", base_url=api.url).submit("python", "x")
95
+ e = ctx.exception
96
+ self.assertEqual((e.code, e.status, e.request_id, e.retryable), ("QUOTA_EXCEEDED", 429, "req_test", False))
97
+ self.assertEqual(e.details["resetsAt"], "2026-12-01T00:00:00Z")
98
+ self.assertEqual(len(api.calls), 1)
99
+ finally:
100
+ api.close()
101
+
102
+ def test_wait_times_out(self):
103
+ api = FakeApi(lambda m, n, b: (200, {"executionId": "exe_3", "status": "QUEUED"}))
104
+ try:
105
+ with self.assertRaises(CodeForgeError) as ctx:
106
+ CodeForge(api_key="k", base_url=api.url).wait("exe_3", poll_interval=0.01, timeout=0.05)
107
+ self.assertEqual(ctx.exception.code, "WAIT_TIMEOUT")
108
+ finally:
109
+ api.close()
110
+
111
+ def test_custom_idempotency_key(self):
112
+ api = FakeApi(lambda m, n, b: (202, {"executionId": "exe_4", "status": "QUEUED"}))
113
+ try:
114
+ CodeForge(api_key="k", base_url=api.url).submit("java", "x", idempotency_key="order-123")
115
+ self.assertEqual(api.calls[0]["headers"]["Idempotency-Key"], "order-123")
116
+ finally:
117
+ api.close()
118
+
119
+ def test_network_error(self):
120
+ with self.assertRaises(CodeForgeError) as ctx:
121
+ CodeForge(api_key="k", base_url="http://127.0.0.1:1", max_retries=1).get("exe_x")
122
+ self.assertEqual(ctx.exception.code, "NETWORK_ERROR")
123
+
124
+
125
+ if __name__ == "__main__":
126
+ unittest.main()