mrmd-python 0.3.3__tar.gz → 0.3.5__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.
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/PKG-INFO +1 -1
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/pyproject.toml +1 -1
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/src/mrmd_python/__init__.py +1 -1
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/src/mrmd_python/worker.py +91 -252
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/.gitignore +0 -0
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/README.md +0 -0
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/src/mrmd_python/cli.py +0 -0
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/src/mrmd_python/runtime_client.py +0 -0
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/src/mrmd_python/runtime_daemon.py +0 -0
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/src/mrmd_python/server.py +0 -0
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/src/mrmd_python/subprocess_manager.py +0 -0
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/src/mrmd_python/subprocess_worker.py +0 -0
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/src/mrmd_python/types.py +0 -0
- {mrmd_python-0.3.3 → mrmd_python-0.3.5}/uv.lock +0 -0
|
@@ -42,6 +42,7 @@ from .types import (
|
|
|
42
42
|
StdinRequest,
|
|
43
43
|
InputCancelledError,
|
|
44
44
|
)
|
|
45
|
+
from .subprocess_manager import SubprocessWorker
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
@dataclass
|
|
@@ -82,31 +83,25 @@ class IPythonWorker:
|
|
|
82
83
|
self._input_event: threading.Event | None = None
|
|
83
84
|
self._input_response: str | None = None
|
|
84
85
|
self._execution_count = 0
|
|
85
|
-
self.
|
|
86
|
+
self._subprocess_worker: SubprocessWorker | None = None # Persistent subprocess for different venv
|
|
86
87
|
|
|
87
88
|
def _get_venv_python(self) -> str | None:
|
|
88
89
|
"""Get the Python executable path for the configured venv."""
|
|
89
|
-
print(f"[IPythonWorker._get_venv_python] self.venv={self.venv}", flush=True)
|
|
90
90
|
if not self.venv:
|
|
91
|
-
print("[IPythonWorker._get_venv_python] -> None (no venv)", flush=True)
|
|
92
91
|
return None
|
|
93
92
|
venv_path = Path(self.venv)
|
|
94
93
|
if sys.platform == 'win32':
|
|
95
94
|
candidates = [venv_path / 'Scripts' / 'python.exe']
|
|
96
95
|
else:
|
|
97
|
-
# Try both 'python' and 'python3'
|
|
98
96
|
candidates = [
|
|
99
97
|
venv_path / 'bin' / 'python',
|
|
100
98
|
venv_path / 'bin' / 'python3',
|
|
101
99
|
]
|
|
102
100
|
|
|
103
101
|
for python_exe in candidates:
|
|
104
|
-
print(f"[IPythonWorker._get_venv_python] Checking {python_exe}...", flush=True)
|
|
105
102
|
if python_exe.exists():
|
|
106
|
-
print(f"[IPythonWorker._get_venv_python] -> {python_exe} (FOUND)", flush=True)
|
|
107
103
|
return str(python_exe)
|
|
108
104
|
|
|
109
|
-
print("[IPythonWorker._get_venv_python] -> None (no executable found)", flush=True)
|
|
110
105
|
return None
|
|
111
106
|
|
|
112
107
|
def _should_use_subprocess(self) -> bool:
|
|
@@ -117,33 +112,82 @@ class IPythonWorker:
|
|
|
117
112
|
- The venv differs from the current Python's prefix
|
|
118
113
|
- The venv's Python executable exists
|
|
119
114
|
"""
|
|
120
|
-
# DEBUG LOGGING
|
|
121
|
-
print(f"[IPythonWorker._should_use_subprocess] self.venv={self.venv}", flush=True)
|
|
122
|
-
print(f"[IPythonWorker._should_use_subprocess] sys.prefix={sys.prefix}", flush=True)
|
|
123
|
-
|
|
124
115
|
if not self.venv:
|
|
125
|
-
print("[IPythonWorker._should_use_subprocess] -> False (no venv configured)", flush=True)
|
|
126
116
|
return False
|
|
127
117
|
|
|
128
|
-
# Compare venv path to current Python's prefix
|
|
129
|
-
# Use realpath to handle symlinks
|
|
118
|
+
# Compare venv path to current Python's prefix (use realpath to handle symlinks)
|
|
130
119
|
current_prefix = os.path.realpath(sys.prefix)
|
|
131
120
|
target_venv = os.path.realpath(self.venv)
|
|
132
121
|
|
|
133
|
-
print(f"[IPythonWorker._should_use_subprocess] current_prefix={current_prefix}", flush=True)
|
|
134
|
-
print(f"[IPythonWorker._should_use_subprocess] target_venv={target_venv}", flush=True)
|
|
135
|
-
|
|
136
122
|
if current_prefix == target_venv:
|
|
137
|
-
print("[IPythonWorker._should_use_subprocess] -> False (same venv)", flush=True)
|
|
138
123
|
return False
|
|
139
124
|
|
|
140
|
-
#
|
|
125
|
+
# Verify the target Python exists
|
|
126
|
+
return self._get_venv_python() is not None
|
|
127
|
+
|
|
128
|
+
def _ensure_mrmd_python_in_venv(self) -> bool:
|
|
129
|
+
"""Ensure mrmd-python is installed in the target venv.
|
|
130
|
+
|
|
131
|
+
Uses uv pip install to add mrmd-python to the venv so that
|
|
132
|
+
subprocess_worker can be invoked with -m mrmd_python.subprocess_worker.
|
|
133
|
+
|
|
134
|
+
Returns True if successful.
|
|
135
|
+
"""
|
|
136
|
+
import subprocess as sp
|
|
137
|
+
|
|
141
138
|
python_exe = self._get_venv_python()
|
|
142
|
-
|
|
139
|
+
if not python_exe:
|
|
140
|
+
return False
|
|
143
141
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
# Check if mrmd_python is already importable
|
|
143
|
+
check_result = sp.run(
|
|
144
|
+
[python_exe, "-c", "import mrmd_python"],
|
|
145
|
+
capture_output=True,
|
|
146
|
+
text=True,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if check_result.returncode == 0:
|
|
150
|
+
# Already installed
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
# Install mrmd-python using uv pip
|
|
154
|
+
try:
|
|
155
|
+
install_result = sp.run(
|
|
156
|
+
["uv", "pip", "install", "--python", python_exe, "mrmd-python"],
|
|
157
|
+
capture_output=True,
|
|
158
|
+
text=True,
|
|
159
|
+
timeout=120,
|
|
160
|
+
)
|
|
161
|
+
return install_result.returncode == 0
|
|
162
|
+
except (FileNotFoundError, sp.TimeoutExpired):
|
|
163
|
+
# uv not available or timeout - try pip directly
|
|
164
|
+
try:
|
|
165
|
+
install_result = sp.run(
|
|
166
|
+
[python_exe, "-m", "pip", "install", "mrmd-python"],
|
|
167
|
+
capture_output=True,
|
|
168
|
+
text=True,
|
|
169
|
+
timeout=120,
|
|
170
|
+
)
|
|
171
|
+
return install_result.returncode == 0
|
|
172
|
+
except (FileNotFoundError, sp.TimeoutExpired):
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
def _get_subprocess_worker(self) -> SubprocessWorker:
|
|
176
|
+
"""Get or create the subprocess worker for different-venv execution."""
|
|
177
|
+
if self._subprocess_worker is None or not self._subprocess_worker.is_alive():
|
|
178
|
+
# Ensure mrmd-python is installed in the target venv
|
|
179
|
+
if not self._ensure_mrmd_python_in_venv():
|
|
180
|
+
raise RuntimeError(
|
|
181
|
+
f"Could not install mrmd-python in venv {self.venv}. "
|
|
182
|
+
"Please install it manually: uv pip install mrmd-python"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
self._subprocess_worker = SubprocessWorker(
|
|
186
|
+
venv=self.venv,
|
|
187
|
+
cwd=self.cwd,
|
|
188
|
+
assets_dir=self.assets_dir,
|
|
189
|
+
)
|
|
190
|
+
return self._subprocess_worker
|
|
147
191
|
|
|
148
192
|
def _ensure_initialized(self):
|
|
149
193
|
"""Lazy initialization of IPython shell."""
|
|
@@ -638,111 +682,18 @@ class IPythonWorker:
|
|
|
638
682
|
def _execute_subprocess(
|
|
639
683
|
self, code: str, exec_id: str | None = None
|
|
640
684
|
) -> ExecuteResult:
|
|
641
|
-
"""Execute code
|
|
642
|
-
import subprocess
|
|
643
|
-
import json
|
|
644
|
-
|
|
645
|
-
python_exe = self._get_venv_python()
|
|
646
|
-
if not python_exe:
|
|
647
|
-
return ExecuteResult(
|
|
648
|
-
success=False,
|
|
649
|
-
error=ExecuteError(
|
|
650
|
-
ename="VenvError",
|
|
651
|
-
evalue="Could not find Python executable in venv",
|
|
652
|
-
traceback=[]
|
|
653
|
-
)
|
|
654
|
-
)
|
|
655
|
-
|
|
656
|
-
self._execution_count += 1
|
|
657
|
-
start_time = time.time()
|
|
658
|
-
|
|
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
|
|
661
|
-
wrapper_code = '''
|
|
662
|
-
import sys as _sys
|
|
663
|
-
import ast as _ast
|
|
664
|
-
|
|
665
|
-
_code = """ ''' + code.replace('\\', '\\\\').replace('"""', '\\"\\"\\"') + ''' """
|
|
666
|
-
|
|
667
|
-
try:
|
|
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)
|
|
690
|
-
'''
|
|
691
|
-
|
|
685
|
+
"""Execute code using the persistent SubprocessWorker (IPython-based)."""
|
|
692
686
|
try:
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
capture_output=True,
|
|
696
|
-
text=True,
|
|
697
|
-
timeout=300, # 5 minute timeout
|
|
698
|
-
cwd=self.cwd,
|
|
699
|
-
)
|
|
700
|
-
|
|
701
|
-
duration = int((time.time() - start_time) * 1000)
|
|
702
|
-
|
|
703
|
-
if result.returncode == 0:
|
|
704
|
-
return ExecuteResult(
|
|
705
|
-
success=True,
|
|
706
|
-
stdout=result.stdout,
|
|
707
|
-
stderr=result.stderr,
|
|
708
|
-
executionCount=self._execution_count,
|
|
709
|
-
duration=duration,
|
|
710
|
-
)
|
|
711
|
-
else:
|
|
712
|
-
# Parse error from stderr if possible
|
|
713
|
-
error_lines = result.stderr.strip().split('\n') if result.stderr else []
|
|
714
|
-
return ExecuteResult(
|
|
715
|
-
success=False,
|
|
716
|
-
stdout=result.stdout,
|
|
717
|
-
stderr=result.stderr,
|
|
718
|
-
error=ExecuteError(
|
|
719
|
-
ename="ExecutionError",
|
|
720
|
-
evalue=error_lines[-1] if error_lines else "Unknown error",
|
|
721
|
-
traceback=error_lines
|
|
722
|
-
),
|
|
723
|
-
executionCount=self._execution_count,
|
|
724
|
-
duration=duration,
|
|
725
|
-
)
|
|
726
|
-
|
|
727
|
-
except subprocess.TimeoutExpired:
|
|
728
|
-
return ExecuteResult(
|
|
729
|
-
success=False,
|
|
730
|
-
error=ExecuteError(
|
|
731
|
-
ename="TimeoutError",
|
|
732
|
-
evalue="Execution timed out after 5 minutes",
|
|
733
|
-
traceback=[]
|
|
734
|
-
),
|
|
735
|
-
executionCount=self._execution_count,
|
|
736
|
-
)
|
|
687
|
+
worker = self._get_subprocess_worker()
|
|
688
|
+
return worker.execute(code, store_history=True, exec_id=exec_id)
|
|
737
689
|
except Exception as e:
|
|
738
690
|
return ExecuteResult(
|
|
739
691
|
success=False,
|
|
740
692
|
error=ExecuteError(
|
|
741
|
-
|
|
742
|
-
|
|
693
|
+
type=type(e).__name__,
|
|
694
|
+
message=str(e),
|
|
743
695
|
traceback=[]
|
|
744
696
|
),
|
|
745
|
-
executionCount=self._execution_count,
|
|
746
697
|
)
|
|
747
698
|
|
|
748
699
|
def _execute_subprocess_streaming(
|
|
@@ -751,149 +702,33 @@ except Exception as _e:
|
|
|
751
702
|
on_output: Callable[[str, str, str], None],
|
|
752
703
|
exec_id: str | None = None,
|
|
753
704
|
) -> ExecuteResult:
|
|
754
|
-
"""Execute code
|
|
755
|
-
import subprocess
|
|
756
|
-
|
|
757
|
-
print(f"[IPythonWorker._execute_subprocess_streaming] Starting subprocess execution", flush=True)
|
|
758
|
-
|
|
759
|
-
python_exe = self._get_venv_python()
|
|
760
|
-
print(f"[IPythonWorker._execute_subprocess_streaming] python_exe={python_exe}", flush=True)
|
|
761
|
-
|
|
762
|
-
if not python_exe:
|
|
763
|
-
return ExecuteResult(
|
|
764
|
-
success=False,
|
|
765
|
-
error=ExecuteError(
|
|
766
|
-
ename="VenvError",
|
|
767
|
-
evalue="Could not find Python executable in venv",
|
|
768
|
-
traceback=[]
|
|
769
|
-
)
|
|
770
|
-
)
|
|
771
|
-
|
|
772
|
-
self._execution_count += 1
|
|
773
|
-
start_time = time.time()
|
|
774
|
-
accumulated_stdout = ""
|
|
775
|
-
accumulated_stderr = ""
|
|
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
|
-
|
|
705
|
+
"""Execute code using the persistent SubprocessWorker with streaming."""
|
|
809
706
|
try:
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
cwd=self.cwd,
|
|
707
|
+
worker = self._get_subprocess_worker()
|
|
708
|
+
return worker.execute_streaming(
|
|
709
|
+
code,
|
|
710
|
+
on_output=on_output,
|
|
711
|
+
store_history=True,
|
|
712
|
+
exec_id=exec_id,
|
|
817
713
|
)
|
|
818
|
-
|
|
819
|
-
# Read output in real-time
|
|
820
|
-
import selectors
|
|
821
|
-
sel = selectors.DefaultSelector()
|
|
822
|
-
sel.register(process.stdout, selectors.EVENT_READ)
|
|
823
|
-
sel.register(process.stderr, selectors.EVENT_READ)
|
|
824
|
-
|
|
825
|
-
while process.poll() is None or sel.get_map():
|
|
826
|
-
for key, _ in sel.select(timeout=0.1):
|
|
827
|
-
data = key.fileobj.read(1)
|
|
828
|
-
if not data:
|
|
829
|
-
sel.unregister(key.fileobj)
|
|
830
|
-
continue
|
|
831
|
-
if key.fileobj == process.stdout:
|
|
832
|
-
accumulated_stdout += data
|
|
833
|
-
on_output("stdout", data, accumulated_stdout)
|
|
834
|
-
else:
|
|
835
|
-
accumulated_stderr += data
|
|
836
|
-
on_output("stderr", data, accumulated_stderr)
|
|
837
|
-
|
|
838
|
-
# Read any remaining output
|
|
839
|
-
remaining_stdout = process.stdout.read()
|
|
840
|
-
remaining_stderr = process.stderr.read()
|
|
841
|
-
if remaining_stdout:
|
|
842
|
-
accumulated_stdout += remaining_stdout
|
|
843
|
-
on_output("stdout", remaining_stdout, accumulated_stdout)
|
|
844
|
-
if remaining_stderr:
|
|
845
|
-
accumulated_stderr += remaining_stderr
|
|
846
|
-
on_output("stderr", remaining_stderr, accumulated_stderr)
|
|
847
|
-
|
|
848
|
-
duration = int((time.time() - start_time) * 1000)
|
|
849
|
-
success = process.returncode == 0
|
|
850
|
-
|
|
851
|
-
if not success:
|
|
852
|
-
error_lines = accumulated_stderr.strip().split('\n') if accumulated_stderr else []
|
|
853
|
-
return ExecuteResult(
|
|
854
|
-
success=False,
|
|
855
|
-
stdout=accumulated_stdout,
|
|
856
|
-
stderr=accumulated_stderr,
|
|
857
|
-
error=ExecuteError(
|
|
858
|
-
ename="ExecutionError",
|
|
859
|
-
evalue=error_lines[-1] if error_lines else "Unknown error",
|
|
860
|
-
traceback=error_lines
|
|
861
|
-
),
|
|
862
|
-
executionCount=self._execution_count,
|
|
863
|
-
duration=duration,
|
|
864
|
-
)
|
|
865
|
-
|
|
866
|
-
return ExecuteResult(
|
|
867
|
-
success=True,
|
|
868
|
-
stdout=accumulated_stdout,
|
|
869
|
-
stderr=accumulated_stderr,
|
|
870
|
-
executionCount=self._execution_count,
|
|
871
|
-
duration=duration,
|
|
872
|
-
)
|
|
873
|
-
|
|
874
714
|
except Exception as e:
|
|
875
715
|
return ExecuteResult(
|
|
876
716
|
success=False,
|
|
877
717
|
error=ExecuteError(
|
|
878
|
-
|
|
879
|
-
|
|
718
|
+
type=type(e).__name__,
|
|
719
|
+
message=str(e),
|
|
880
720
|
traceback=[]
|
|
881
721
|
),
|
|
882
|
-
executionCount=self._execution_count,
|
|
883
722
|
)
|
|
884
723
|
|
|
885
724
|
def execute(
|
|
886
725
|
self, code: str, store_history: bool = True, exec_id: str | None = None
|
|
887
726
|
) -> ExecuteResult:
|
|
888
727
|
"""Execute code and return result (non-streaming)."""
|
|
889
|
-
print(f"[IPythonWorker.execute] Called with code length={len(code)}", flush=True)
|
|
890
|
-
|
|
891
728
|
# Use subprocess execution if venv differs from current Python
|
|
892
729
|
if self._should_use_subprocess():
|
|
893
|
-
print("[IPythonWorker.execute] Using SUBPROCESS execution", flush=True)
|
|
894
730
|
return self._execute_subprocess(code, exec_id)
|
|
895
731
|
|
|
896
|
-
print("[IPythonWorker.execute] Using LOCAL execution", flush=True)
|
|
897
732
|
self._ensure_initialized()
|
|
898
733
|
self._captured_displays = []
|
|
899
734
|
self._current_exec_id = exec_id
|
|
@@ -977,14 +812,10 @@ except SyntaxError as _e:
|
|
|
977
812
|
Returns:
|
|
978
813
|
ExecuteResult with final result
|
|
979
814
|
"""
|
|
980
|
-
print(f"[IPythonWorker.execute_streaming] Called with code length={len(code)}", flush=True)
|
|
981
|
-
|
|
982
815
|
# Use subprocess execution if venv differs from current Python
|
|
983
816
|
if self._should_use_subprocess():
|
|
984
|
-
print("[IPythonWorker.execute_streaming] Using SUBPROCESS execution", flush=True)
|
|
985
817
|
return self._execute_subprocess_streaming(code, on_output, exec_id)
|
|
986
818
|
|
|
987
|
-
print("[IPythonWorker.execute_streaming] Using LOCAL execution", flush=True)
|
|
988
819
|
self._ensure_initialized()
|
|
989
820
|
self._captured_displays = []
|
|
990
821
|
self._current_exec_id = exec_id
|
|
@@ -1596,8 +1427,16 @@ except SyntaxError as _e:
|
|
|
1596
1427
|
|
|
1597
1428
|
def reset(self):
|
|
1598
1429
|
"""Reset the namespace."""
|
|
1599
|
-
self.
|
|
1600
|
-
|
|
1430
|
+
if self._subprocess_worker is not None:
|
|
1431
|
+
self._subprocess_worker.reset()
|
|
1432
|
+
elif self._initialized:
|
|
1433
|
+
self.shell.reset()
|
|
1434
|
+
|
|
1435
|
+
def shutdown(self):
|
|
1436
|
+
"""Shutdown the worker, cleaning up subprocess if needed."""
|
|
1437
|
+
if self._subprocess_worker is not None:
|
|
1438
|
+
self._subprocess_worker.shutdown()
|
|
1439
|
+
self._subprocess_worker = None
|
|
1601
1440
|
|
|
1602
1441
|
def get_info(self) -> dict:
|
|
1603
1442
|
"""Get info about this worker."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|