mrmd-python 0.3.4__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.4 → mrmd_python-0.3.5}/PKG-INFO +1 -1
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/pyproject.toml +1 -1
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/src/mrmd_python/__init__.py +1 -1
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/src/mrmd_python/worker.py +87 -250
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/.gitignore +0 -0
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/README.md +0 -0
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/src/mrmd_python/cli.py +0 -0
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/src/mrmd_python/runtime_client.py +0 -0
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/src/mrmd_python/runtime_daemon.py +0 -0
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/src/mrmd_python/server.py +0 -0
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/src/mrmd_python/subprocess_manager.py +0 -0
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/src/mrmd_python/subprocess_worker.py +0 -0
- {mrmd_python-0.3.4 → mrmd_python-0.3.5}/src/mrmd_python/types.py +0 -0
- {mrmd_python-0.3.4 → 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,103 +682,10 @@ 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
|
-
type="VenvError",
|
|
651
|
-
message="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
|
-
# Use .strip() to remove any leading/trailing whitespace from the embedded code
|
|
662
|
-
wrapper_code = '''
|
|
663
|
-
import sys as _sys
|
|
664
|
-
import ast as _ast
|
|
665
|
-
|
|
666
|
-
_code = """''' + code.replace('\\', '\\\\').replace('"""', '\\"\\"\\"') + '''""".strip()
|
|
667
|
-
|
|
668
|
-
try:
|
|
669
|
-
_tree = _ast.parse(_code)
|
|
670
|
-
if _tree.body and isinstance(_tree.body[-1], _ast.Expr):
|
|
671
|
-
# Last statement is an expression - capture its value
|
|
672
|
-
if len(_tree.body) > 1:
|
|
673
|
-
# Execute all but last statement
|
|
674
|
-
_exec_code = _ast.Module(body=_tree.body[:-1], type_ignores=[])
|
|
675
|
-
exec(compile(_exec_code, "<cell>", "exec"))
|
|
676
|
-
# Evaluate and print last expression
|
|
677
|
-
_expr_code = _ast.Expression(body=_tree.body[-1].value)
|
|
678
|
-
_result = eval(compile(_expr_code, "<cell>", "eval"))
|
|
679
|
-
if _result is not None:
|
|
680
|
-
print(f"Out[''' + str(self._execution_count) + ''']: " + repr(_result))
|
|
681
|
-
else:
|
|
682
|
-
# No trailing expression, just exec everything
|
|
683
|
-
exec(compile(_code, "<cell>", "exec"))
|
|
684
|
-
except SyntaxError:
|
|
685
|
-
# Fall back to simple exec
|
|
686
|
-
exec(compile(_code, "<cell>", "exec"))
|
|
687
|
-
except Exception as _e:
|
|
688
|
-
import traceback as _tb
|
|
689
|
-
print("".join(_tb.format_exception(type(_e), _e, _e.__traceback__)), file=_sys.stderr)
|
|
690
|
-
_sys.exit(1)
|
|
691
|
-
'''
|
|
692
|
-
|
|
685
|
+
"""Execute code using the persistent SubprocessWorker (IPython-based)."""
|
|
693
686
|
try:
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
capture_output=True,
|
|
697
|
-
text=True,
|
|
698
|
-
timeout=300, # 5 minute timeout
|
|
699
|
-
cwd=self.cwd,
|
|
700
|
-
)
|
|
701
|
-
|
|
702
|
-
duration = int((time.time() - start_time) * 1000)
|
|
703
|
-
|
|
704
|
-
if result.returncode == 0:
|
|
705
|
-
return ExecuteResult(
|
|
706
|
-
success=True,
|
|
707
|
-
stdout=result.stdout,
|
|
708
|
-
stderr=result.stderr,
|
|
709
|
-
executionCount=self._execution_count,
|
|
710
|
-
duration=duration,
|
|
711
|
-
)
|
|
712
|
-
else:
|
|
713
|
-
# Parse error from stderr if possible
|
|
714
|
-
error_lines = result.stderr.strip().split('\n') if result.stderr else []
|
|
715
|
-
return ExecuteResult(
|
|
716
|
-
success=False,
|
|
717
|
-
stdout=result.stdout,
|
|
718
|
-
stderr=result.stderr,
|
|
719
|
-
error=ExecuteError(
|
|
720
|
-
type="ExecutionError",
|
|
721
|
-
message=error_lines[-1] if error_lines else "Unknown error",
|
|
722
|
-
traceback=error_lines
|
|
723
|
-
),
|
|
724
|
-
executionCount=self._execution_count,
|
|
725
|
-
duration=duration,
|
|
726
|
-
)
|
|
727
|
-
|
|
728
|
-
except subprocess.TimeoutExpired:
|
|
729
|
-
return ExecuteResult(
|
|
730
|
-
success=False,
|
|
731
|
-
error=ExecuteError(
|
|
732
|
-
type="TimeoutError",
|
|
733
|
-
message="Execution timed out after 5 minutes",
|
|
734
|
-
traceback=[]
|
|
735
|
-
),
|
|
736
|
-
executionCount=self._execution_count,
|
|
737
|
-
)
|
|
687
|
+
worker = self._get_subprocess_worker()
|
|
688
|
+
return worker.execute(code, store_history=True, exec_id=exec_id)
|
|
738
689
|
except Exception as e:
|
|
739
690
|
return ExecuteResult(
|
|
740
691
|
success=False,
|
|
@@ -743,7 +694,6 @@ except Exception as _e:
|
|
|
743
694
|
message=str(e),
|
|
744
695
|
traceback=[]
|
|
745
696
|
),
|
|
746
|
-
executionCount=self._execution_count,
|
|
747
697
|
)
|
|
748
698
|
|
|
749
699
|
def _execute_subprocess_streaming(
|
|
@@ -752,127 +702,15 @@ except Exception as _e:
|
|
|
752
702
|
on_output: Callable[[str, str, str], None],
|
|
753
703
|
exec_id: str | None = None,
|
|
754
704
|
) -> ExecuteResult:
|
|
755
|
-
"""Execute code
|
|
756
|
-
import subprocess
|
|
757
|
-
|
|
758
|
-
print(f"[IPythonWorker._execute_subprocess_streaming] Starting subprocess execution", flush=True)
|
|
759
|
-
|
|
760
|
-
python_exe = self._get_venv_python()
|
|
761
|
-
print(f"[IPythonWorker._execute_subprocess_streaming] python_exe={python_exe}", flush=True)
|
|
762
|
-
|
|
763
|
-
if not python_exe:
|
|
764
|
-
return ExecuteResult(
|
|
765
|
-
success=False,
|
|
766
|
-
error=ExecuteError(
|
|
767
|
-
type="VenvError",
|
|
768
|
-
message="Could not find Python executable in venv",
|
|
769
|
-
traceback=[]
|
|
770
|
-
)
|
|
771
|
-
)
|
|
772
|
-
|
|
773
|
-
self._execution_count += 1
|
|
774
|
-
start_time = time.time()
|
|
775
|
-
accumulated_stdout = ""
|
|
776
|
-
accumulated_stderr = ""
|
|
777
|
-
|
|
778
|
-
# Wrap code to capture expression results like IPython does
|
|
779
|
-
# This handles cases like "import sys; sys.executable" which need to print
|
|
780
|
-
# Use .strip() to remove any leading/trailing whitespace from the embedded code
|
|
781
|
-
wrapper_code = '''
|
|
782
|
-
import sys as _sys
|
|
783
|
-
import ast as _ast
|
|
784
|
-
|
|
785
|
-
_code = """''' + code.replace('\\', '\\\\').replace('"""', '\\"\\"\\"') + '''""".strip()
|
|
786
|
-
|
|
787
|
-
# Parse to check if last statement is an expression
|
|
788
|
-
try:
|
|
789
|
-
_tree = _ast.parse(_code)
|
|
790
|
-
if _tree.body and isinstance(_tree.body[-1], _ast.Expr):
|
|
791
|
-
# Last statement is an expression - capture its value
|
|
792
|
-
if len(_tree.body) > 1:
|
|
793
|
-
# Execute all but last statement
|
|
794
|
-
_exec_code = _ast.Module(body=_tree.body[:-1], type_ignores=[])
|
|
795
|
-
exec(compile(_exec_code, "<cell>", "exec"))
|
|
796
|
-
# Evaluate and print last expression
|
|
797
|
-
_expr_code = _ast.Expression(body=_tree.body[-1].value)
|
|
798
|
-
_result = eval(compile(_expr_code, "<cell>", "eval"))
|
|
799
|
-
if _result is not None:
|
|
800
|
-
print(f"Out[{''' + str(self._execution_count) + '''}]: " + repr(_result))
|
|
801
|
-
else:
|
|
802
|
-
# No trailing expression, just exec everything
|
|
803
|
-
exec(compile(_code, "<cell>", "exec"))
|
|
804
|
-
except SyntaxError as _e:
|
|
805
|
-
# Fall back to simple exec for syntax errors in wrapper
|
|
806
|
-
exec(compile(_code, "<cell>", "exec"))
|
|
807
|
-
'''
|
|
808
|
-
|
|
809
|
-
print(f"[IPythonWorker._execute_subprocess_streaming] Running: {python_exe} -c ...", flush=True)
|
|
810
|
-
|
|
705
|
+
"""Execute code using the persistent SubprocessWorker with streaming."""
|
|
811
706
|
try:
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
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,
|
|
819
713
|
)
|
|
820
|
-
|
|
821
|
-
# Read output in real-time
|
|
822
|
-
import selectors
|
|
823
|
-
sel = selectors.DefaultSelector()
|
|
824
|
-
sel.register(process.stdout, selectors.EVENT_READ)
|
|
825
|
-
sel.register(process.stderr, selectors.EVENT_READ)
|
|
826
|
-
|
|
827
|
-
while process.poll() is None or sel.get_map():
|
|
828
|
-
for key, _ in sel.select(timeout=0.1):
|
|
829
|
-
data = key.fileobj.read(1)
|
|
830
|
-
if not data:
|
|
831
|
-
sel.unregister(key.fileobj)
|
|
832
|
-
continue
|
|
833
|
-
if key.fileobj == process.stdout:
|
|
834
|
-
accumulated_stdout += data
|
|
835
|
-
on_output("stdout", data, accumulated_stdout)
|
|
836
|
-
else:
|
|
837
|
-
accumulated_stderr += data
|
|
838
|
-
on_output("stderr", data, accumulated_stderr)
|
|
839
|
-
|
|
840
|
-
# Read any remaining output
|
|
841
|
-
remaining_stdout = process.stdout.read()
|
|
842
|
-
remaining_stderr = process.stderr.read()
|
|
843
|
-
if remaining_stdout:
|
|
844
|
-
accumulated_stdout += remaining_stdout
|
|
845
|
-
on_output("stdout", remaining_stdout, accumulated_stdout)
|
|
846
|
-
if remaining_stderr:
|
|
847
|
-
accumulated_stderr += remaining_stderr
|
|
848
|
-
on_output("stderr", remaining_stderr, accumulated_stderr)
|
|
849
|
-
|
|
850
|
-
duration = int((time.time() - start_time) * 1000)
|
|
851
|
-
success = process.returncode == 0
|
|
852
|
-
|
|
853
|
-
if not success:
|
|
854
|
-
error_lines = accumulated_stderr.strip().split('\n') if accumulated_stderr else []
|
|
855
|
-
return ExecuteResult(
|
|
856
|
-
success=False,
|
|
857
|
-
stdout=accumulated_stdout,
|
|
858
|
-
stderr=accumulated_stderr,
|
|
859
|
-
error=ExecuteError(
|
|
860
|
-
type="ExecutionError",
|
|
861
|
-
message=error_lines[-1] if error_lines else "Unknown error",
|
|
862
|
-
traceback=error_lines
|
|
863
|
-
),
|
|
864
|
-
executionCount=self._execution_count,
|
|
865
|
-
duration=duration,
|
|
866
|
-
)
|
|
867
|
-
|
|
868
|
-
return ExecuteResult(
|
|
869
|
-
success=True,
|
|
870
|
-
stdout=accumulated_stdout,
|
|
871
|
-
stderr=accumulated_stderr,
|
|
872
|
-
executionCount=self._execution_count,
|
|
873
|
-
duration=duration,
|
|
874
|
-
)
|
|
875
|
-
|
|
876
714
|
except Exception as e:
|
|
877
715
|
return ExecuteResult(
|
|
878
716
|
success=False,
|
|
@@ -881,21 +719,16 @@ except SyntaxError as _e:
|
|
|
881
719
|
message=str(e),
|
|
882
720
|
traceback=[]
|
|
883
721
|
),
|
|
884
|
-
executionCount=self._execution_count,
|
|
885
722
|
)
|
|
886
723
|
|
|
887
724
|
def execute(
|
|
888
725
|
self, code: str, store_history: bool = True, exec_id: str | None = None
|
|
889
726
|
) -> ExecuteResult:
|
|
890
727
|
"""Execute code and return result (non-streaming)."""
|
|
891
|
-
print(f"[IPythonWorker.execute] Called with code length={len(code)}", flush=True)
|
|
892
|
-
|
|
893
728
|
# Use subprocess execution if venv differs from current Python
|
|
894
729
|
if self._should_use_subprocess():
|
|
895
|
-
print("[IPythonWorker.execute] Using SUBPROCESS execution", flush=True)
|
|
896
730
|
return self._execute_subprocess(code, exec_id)
|
|
897
731
|
|
|
898
|
-
print("[IPythonWorker.execute] Using LOCAL execution", flush=True)
|
|
899
732
|
self._ensure_initialized()
|
|
900
733
|
self._captured_displays = []
|
|
901
734
|
self._current_exec_id = exec_id
|
|
@@ -979,14 +812,10 @@ except SyntaxError as _e:
|
|
|
979
812
|
Returns:
|
|
980
813
|
ExecuteResult with final result
|
|
981
814
|
"""
|
|
982
|
-
print(f"[IPythonWorker.execute_streaming] Called with code length={len(code)}", flush=True)
|
|
983
|
-
|
|
984
815
|
# Use subprocess execution if venv differs from current Python
|
|
985
816
|
if self._should_use_subprocess():
|
|
986
|
-
print("[IPythonWorker.execute_streaming] Using SUBPROCESS execution", flush=True)
|
|
987
817
|
return self._execute_subprocess_streaming(code, on_output, exec_id)
|
|
988
818
|
|
|
989
|
-
print("[IPythonWorker.execute_streaming] Using LOCAL execution", flush=True)
|
|
990
819
|
self._ensure_initialized()
|
|
991
820
|
self._captured_displays = []
|
|
992
821
|
self._current_exec_id = exec_id
|
|
@@ -1598,8 +1427,16 @@ except SyntaxError as _e:
|
|
|
1598
1427
|
|
|
1599
1428
|
def reset(self):
|
|
1600
1429
|
"""Reset the namespace."""
|
|
1601
|
-
self.
|
|
1602
|
-
|
|
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
|
|
1603
1440
|
|
|
1604
1441
|
def get_info(self) -> dict:
|
|
1605
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
|