agentic-python-coder 3.3.0__py3-none-any.whl → 3.4.0__py3-none-any.whl
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.
- agentic_python_coder/kernel.py +25 -1
- agentic_python_coder/mcp_server.py +74 -1
- {agentic_python_coder-3.3.0.dist-info → agentic_python_coder-3.4.0.dist-info}/METADATA +1 -1
- {agentic_python_coder-3.3.0.dist-info → agentic_python_coder-3.4.0.dist-info}/RECORD +7 -7
- {agentic_python_coder-3.3.0.dist-info → agentic_python_coder-3.4.0.dist-info}/WHEEL +0 -0
- {agentic_python_coder-3.3.0.dist-info → agentic_python_coder-3.4.0.dist-info}/entry_points.txt +0 -0
- {agentic_python_coder-3.3.0.dist-info → agentic_python_coder-3.4.0.dist-info}/licenses/LICENSE +0 -0
agentic_python_coder/kernel.py
CHANGED
|
@@ -10,6 +10,7 @@ import logging
|
|
|
10
10
|
import os
|
|
11
11
|
import re
|
|
12
12
|
import shutil
|
|
13
|
+
import signal
|
|
13
14
|
import threading
|
|
14
15
|
import time
|
|
15
16
|
import uuid
|
|
@@ -311,7 +312,21 @@ except ImportError:
|
|
|
311
312
|
return self._execute_raw(code, deadline_timeout)
|
|
312
313
|
|
|
313
314
|
def shutdown(self):
|
|
314
|
-
"""Shutdown the kernel and clean up.
|
|
315
|
+
"""Shutdown the kernel and clean up.
|
|
316
|
+
|
|
317
|
+
Kernels run in their own process group (jupyter_client launches with
|
|
318
|
+
start_new_session=True). After the regular shutdown, defensively kill
|
|
319
|
+
the whole group: uv-wrapped kernels are a tree (uv parent + python
|
|
320
|
+
child) and a surviving member would be orphaned.
|
|
321
|
+
"""
|
|
322
|
+
# Capture the kernel's process group before shutdown clears it
|
|
323
|
+
pgid = None
|
|
324
|
+
try:
|
|
325
|
+
if hasattr(self, "km") and self.km:
|
|
326
|
+
pgid = getattr(getattr(self.km, "provisioner", None), "pgid", None)
|
|
327
|
+
except Exception:
|
|
328
|
+
pass
|
|
329
|
+
|
|
315
330
|
try:
|
|
316
331
|
if hasattr(self, "kc") and self.kc:
|
|
317
332
|
try:
|
|
@@ -330,6 +345,15 @@ except ImportError:
|
|
|
330
345
|
# Ignore errors during shutdown
|
|
331
346
|
pass
|
|
332
347
|
|
|
348
|
+
# Defensive: ensure no process-tree member survived. Never signal our
|
|
349
|
+
# own group (pgid could be bogus if start_new_session did not apply).
|
|
350
|
+
if pgid and pgid > 0:
|
|
351
|
+
try:
|
|
352
|
+
if pgid != os.getpgid(0):
|
|
353
|
+
os.killpg(pgid, signal.SIGKILL)
|
|
354
|
+
except (ProcessLookupError, PermissionError, OSError):
|
|
355
|
+
pass
|
|
356
|
+
|
|
333
357
|
|
|
334
358
|
# =============================================================================
|
|
335
359
|
# Core Registry Functions
|
|
@@ -7,6 +7,9 @@ import ast
|
|
|
7
7
|
import asyncio
|
|
8
8
|
import json
|
|
9
9
|
import logging
|
|
10
|
+
import os
|
|
11
|
+
import signal
|
|
12
|
+
import threading
|
|
10
13
|
|
|
11
14
|
from mcp.server import Server
|
|
12
15
|
from mcp.server.stdio import stdio_server
|
|
@@ -20,6 +23,7 @@ from agentic_python_coder.kernel import (
|
|
|
20
23
|
kernel_exists,
|
|
21
24
|
list_kernels,
|
|
22
25
|
restart_kernel,
|
|
26
|
+
shutdown_all_kernels,
|
|
23
27
|
)
|
|
24
28
|
|
|
25
29
|
# Configure logging
|
|
@@ -240,6 +244,30 @@ No side effects - safe to call anytime.""",
|
|
|
240
244
|
},
|
|
241
245
|
},
|
|
242
246
|
),
|
|
247
|
+
Tool(
|
|
248
|
+
name="submit_code",
|
|
249
|
+
description="""Submit your final, verified, self-contained Python program.
|
|
250
|
+
|
|
251
|
+
Call this ONCE at the end of a solve, only after you have executed the program
|
|
252
|
+
via python_exec and your verification passed. The submission must be a
|
|
253
|
+
standalone program: all imports included, no reliance on session state.
|
|
254
|
+
|
|
255
|
+
The code is syntax-checked with compile():
|
|
256
|
+
- {"ok": true} if it parses
|
|
257
|
+
- {"ok": false, "error": "<detail>"} on a syntax error — fix and resubmit
|
|
258
|
+
|
|
259
|
+
The code is NOT executed and NOT written to disk; the client persists it.""",
|
|
260
|
+
inputSchema={
|
|
261
|
+
"type": "object",
|
|
262
|
+
"properties": {
|
|
263
|
+
"code": {
|
|
264
|
+
"type": "string",
|
|
265
|
+
"description": "Complete standalone Python program (final answer).",
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
"required": ["code"],
|
|
269
|
+
},
|
|
270
|
+
),
|
|
243
271
|
Tool(
|
|
244
272
|
name="python_interrupt",
|
|
245
273
|
description="""Interrupt running code in the session.
|
|
@@ -391,6 +419,21 @@ versions
|
|
|
391
419
|
result = await execute_with_timeout(code, timeout, kernel_id)
|
|
392
420
|
return [TextContent(type="text", text=json.dumps(result))]
|
|
393
421
|
|
|
422
|
+
elif name == "submit_code":
|
|
423
|
+
code = arguments.get("code", "")
|
|
424
|
+
if not code.strip():
|
|
425
|
+
payload = {"ok": False, "error": "Empty submission"}
|
|
426
|
+
else:
|
|
427
|
+
try:
|
|
428
|
+
compile(code, "<submission>", "exec")
|
|
429
|
+
payload = {"ok": True}
|
|
430
|
+
except SyntaxError as e:
|
|
431
|
+
payload = {
|
|
432
|
+
"ok": False,
|
|
433
|
+
"error": f"Syntax error at line {e.lineno}: {e.msg}",
|
|
434
|
+
}
|
|
435
|
+
return [TextContent(type="text", text=json.dumps(payload))]
|
|
436
|
+
|
|
394
437
|
elif name == "python_status":
|
|
395
438
|
kernel_id = get_kernel_id(arguments)
|
|
396
439
|
is_active = kernel_exists(kernel_id)
|
|
@@ -521,9 +564,39 @@ async def run_server():
|
|
|
521
564
|
await server.run(read, write, server.create_initialization_options())
|
|
522
565
|
|
|
523
566
|
|
|
567
|
+
# Hard-exit fallback if kernel cleanup hangs after a termination signal
|
|
568
|
+
_TERMINATION_CLEANUP_TIMEOUT = 10.0
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _handle_termination(signum, frame):
|
|
572
|
+
"""Shut down all kernels on SIGTERM/SIGINT, then exit.
|
|
573
|
+
|
|
574
|
+
The cleanup runs in a thread (not in signal-handler context) so it can
|
|
575
|
+
safely take the kernel-registry locks; a timer hard-exits if it hangs.
|
|
576
|
+
"""
|
|
577
|
+
|
|
578
|
+
def _cleanup_and_exit():
|
|
579
|
+
try:
|
|
580
|
+
shutdown_all_kernels()
|
|
581
|
+
finally:
|
|
582
|
+
os._exit(128 + signum)
|
|
583
|
+
|
|
584
|
+
threading.Timer(
|
|
585
|
+
_TERMINATION_CLEANUP_TIMEOUT, lambda: os._exit(128 + signum)
|
|
586
|
+
).start()
|
|
587
|
+
threading.Thread(target=_cleanup_and_exit, daemon=True).start()
|
|
588
|
+
|
|
589
|
+
|
|
524
590
|
def main():
|
|
525
591
|
"""Entry point for coder-mcp command."""
|
|
526
|
-
|
|
592
|
+
signal.signal(signal.SIGTERM, _handle_termination)
|
|
593
|
+
signal.signal(signal.SIGINT, _handle_termination)
|
|
594
|
+
try:
|
|
595
|
+
asyncio.run(run_server())
|
|
596
|
+
finally:
|
|
597
|
+
# stdin EOF / transport close / normal exit: deterministic cleanup
|
|
598
|
+
# (atexit also runs this, but only on clean interpreter shutdown)
|
|
599
|
+
shutdown_all_kernels()
|
|
527
600
|
|
|
528
601
|
|
|
529
602
|
if __name__ == "__main__":
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
agentic_python_coder/__init__.py,sha256=e6o-k__u5xNIiQAkZbHtbZC2-NwpeOdi4MdQtAqJBjE,2112
|
|
2
2
|
agentic_python_coder/agent.py,sha256=gzDFf4n7J1JdWawYPzWQFVwcHZ2rsRHUzVZVcGYjQWo,14451
|
|
3
3
|
agentic_python_coder/cli.py,sha256=HS9nlqsiWZAvg0Hc8ExoVT8eEcy2t3Bux6UGukERWqQ,14663
|
|
4
|
-
agentic_python_coder/kernel.py,sha256=
|
|
4
|
+
agentic_python_coder/kernel.py,sha256=JFN3gtZp3S-bGbQf2B-iQAE3AF8Q1TyQg4wrsx4Fdok,22261
|
|
5
5
|
agentic_python_coder/llm.py,sha256=zjsk4-Kdx92hFSxywPGIkLv8zflWnE2cMEcpeXu6ttw,6998
|
|
6
|
-
agentic_python_coder/mcp_server.py,sha256=
|
|
6
|
+
agentic_python_coder/mcp_server.py,sha256=7Qs5kJ9_W238vIywN5OIMG01vuPzfQ1cy1xFR8C7nGM,21054
|
|
7
7
|
agentic_python_coder/project_md.py,sha256=4xkQHlWb4cDPEkvegw1YwcfaTJ3aFbrl0CYcC_xxABY,3249
|
|
8
8
|
agentic_python_coder/runner.py,sha256=Kbw1ceYJ4MVfUKgSIkiuBhMb8ZtbZuJwbBeTUsvPRts,13045
|
|
9
9
|
agentic_python_coder/tools.py,sha256=tjhQbsF3n3bFIegnEOtE8czjzbtMSXy6B9gYeb-YAJ4,12088
|
|
@@ -41,8 +41,8 @@ agentic_python_coder/models/sonnet46.json,sha256=Ops1FpBoyx9PBGRhIXG8u-YGBgIui9D
|
|
|
41
41
|
agentic_python_coder/models/sonnet5.json,sha256=kf6DIkQlcGVZ40IzRv3GXFtNncZF_gKqQeGzZAxE_KM,87
|
|
42
42
|
agentic_python_coder/prompts/system.md,sha256=hREDVAhJXwCBAgJ3AE9JD6j-JRHREUPlkRSvThOvM3A,3807
|
|
43
43
|
agentic_python_coder/prompts/system_todo.md,sha256=TuNeeUNS9KqyF62Da6t2mlUCYhKsgzwGe5ic_vFPDqw,5054
|
|
44
|
-
agentic_python_coder-3.
|
|
45
|
-
agentic_python_coder-3.
|
|
46
|
-
agentic_python_coder-3.
|
|
47
|
-
agentic_python_coder-3.
|
|
48
|
-
agentic_python_coder-3.
|
|
44
|
+
agentic_python_coder-3.4.0.dist-info/METADATA,sha256=A0vE8oBRtMdgx3kwnaVwZSzhCgTi-VXBRCxcJotjXY0,12872
|
|
45
|
+
agentic_python_coder-3.4.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
46
|
+
agentic_python_coder-3.4.0.dist-info/entry_points.txt,sha256=XIW3nZzL4kJ-Igohilj_mTvBcCyHBouDq2jmGzDILrk,107
|
|
47
|
+
agentic_python_coder-3.4.0.dist-info/licenses/LICENSE,sha256=R9-M_V-SPdN71IphOVzM7kCNpUYs_hwj7rtroTwDhs0,11362
|
|
48
|
+
agentic_python_coder-3.4.0.dist-info/RECORD,,
|
|
File without changes
|
{agentic_python_coder-3.3.0.dist-info → agentic_python_coder-3.4.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{agentic_python_coder-3.3.0.dist-info → agentic_python_coder-3.4.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|