mrmd-python 0.3.2__tar.gz → 0.3.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mrmd-python
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Python runtime server implementing the MRMD Runtime Protocol (MRP)
5
5
  Author: mrmd contributors
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mrmd-python"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "Python runtime server implementing the MRMD Runtime Protocol (MRP)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -657,24 +657,36 @@ class IPythonWorker:
657
657
  start_time = time.time()
658
658
 
659
659
  # Create a wrapper script that executes the code and captures output
660
+ # Uses AST to detect trailing expressions and print their results like IPython
660
661
  wrapper_code = '''
661
- import sys
662
- import json
662
+ import sys as _sys
663
+ import ast as _ast
663
664
 
664
- # Execute the user's code
665
- _mrmd_result = None
666
- _mrmd_error = None
667
- _mrmd_stdout = ""
665
+ _code = """ ''' + code.replace('\\', '\\\\').replace('"""', '\\"\\"\\"') + ''' """
668
666
 
669
667
  try:
670
- exec(compile("""''' + code.replace('\\', '\\\\').replace('"""', '\\"\\"\\"') + '''""", "<cell>", "exec"))
671
- except Exception as e:
672
- import traceback
673
- _mrmd_error = {
674
- "ename": type(e).__name__,
675
- "evalue": str(e),
676
- "traceback": traceback.format_exception(type(e), e, e.__traceback__)
677
- }
668
+ _tree = _ast.parse(_code)
669
+ if _tree.body and isinstance(_tree.body[-1], _ast.Expr):
670
+ # Last statement is an expression - capture its value
671
+ if len(_tree.body) > 1:
672
+ # Execute all but last statement
673
+ _exec_code = _ast.Module(body=_tree.body[:-1], type_ignores=[])
674
+ exec(compile(_exec_code, "<cell>", "exec"))
675
+ # Evaluate and print last expression
676
+ _expr_code = _ast.Expression(body=_tree.body[-1].value)
677
+ _result = eval(compile(_expr_code, "<cell>", "eval"))
678
+ if _result is not None:
679
+ print(f"Out[''' + str(self._execution_count) + ''']: " + repr(_result))
680
+ else:
681
+ # No trailing expression, just exec everything
682
+ exec(compile(_code, "<cell>", "exec"))
683
+ except SyntaxError:
684
+ # Fall back to simple exec
685
+ exec(compile(_code, "<cell>", "exec"))
686
+ except Exception as _e:
687
+ import traceback as _tb
688
+ print("".join(_tb.format_exception(type(_e), _e, _e.__traceback__)), file=_sys.stderr)
689
+ _sys.exit(1)
678
690
  '''
679
691
 
680
692
  try:
@@ -742,7 +754,11 @@ except Exception as e:
742
754
  """Execute code in subprocess with streaming output."""
743
755
  import subprocess
744
756
 
757
+ print(f"[IPythonWorker._execute_subprocess_streaming] Starting subprocess execution", flush=True)
758
+
745
759
  python_exe = self._get_venv_python()
760
+ print(f"[IPythonWorker._execute_subprocess_streaming] python_exe={python_exe}", flush=True)
761
+
746
762
  if not python_exe:
747
763
  return ExecuteResult(
748
764
  success=False,
@@ -758,10 +774,42 @@ except Exception as e:
758
774
  accumulated_stdout = ""
759
775
  accumulated_stderr = ""
760
776
 
777
+ # Wrap code to capture expression results like IPython does
778
+ # This handles cases like "import sys; sys.executable" which need to print
779
+ wrapper_code = '''
780
+ import sys as _sys
781
+ import ast as _ast
782
+
783
+ _code = """ ''' + code.replace('\\', '\\\\').replace('"""', '\\"\\"\\"') + ''' """
784
+
785
+ # Parse to check if last statement is an expression
786
+ try:
787
+ _tree = _ast.parse(_code)
788
+ if _tree.body and isinstance(_tree.body[-1], _ast.Expr):
789
+ # Last statement is an expression - capture its value
790
+ if len(_tree.body) > 1:
791
+ # Execute all but last statement
792
+ _exec_code = _ast.Module(body=_tree.body[:-1], type_ignores=[])
793
+ exec(compile(_exec_code, "<cell>", "exec"))
794
+ # Evaluate and print last expression
795
+ _expr_code = _ast.Expression(body=_tree.body[-1].value)
796
+ _result = eval(compile(_expr_code, "<cell>", "eval"))
797
+ if _result is not None:
798
+ print(f"Out[{''' + str(self._execution_count) + '''}]: " + repr(_result))
799
+ else:
800
+ # No trailing expression, just exec everything
801
+ exec(compile(_code, "<cell>", "exec"))
802
+ except SyntaxError as _e:
803
+ # Fall back to simple exec for syntax errors in wrapper
804
+ exec(compile(_code, "<cell>", "exec"))
805
+ '''
806
+
807
+ print(f"[IPythonWorker._execute_subprocess_streaming] Running: {python_exe} -c ...", flush=True)
808
+
761
809
  try:
762
810
  # Start subprocess
763
811
  process = subprocess.Popen(
764
- [python_exe, "-c", code],
812
+ [python_exe, "-c", wrapper_code],
765
813
  stdout=subprocess.PIPE,
766
814
  stderr=subprocess.PIPE,
767
815
  text=True,
File without changes
File without changes
File without changes