isolate 0.12.15__tar.gz → 0.12.16__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.
Potentially problematic release.
This version of isolate might be problematic. Click here for more details.
- {isolate-0.12.15 → isolate-0.12.16}/PKG-INFO +1 -1
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/_isolate_version.py +2 -2
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/common.py +5 -2
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/conda.py +1 -1
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/pyenv.py +2 -2
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/virtualenv.py +1 -1
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/_local/_base.py +15 -6
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/_base.py +6 -2
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/agent.py +31 -36
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/ipc/_base.py +4 -15
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/ipc/agent.py +29 -10
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate.egg-info/PKG-INFO +1 -1
- {isolate-0.12.15 → isolate-0.12.16}/.github/workflows/release.yml +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/.github/workflows/test.yml +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/.gitignore +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/.pre-commit-config.yaml +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/LICENSE +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/README.md +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/pyproject.toml +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/setup.cfg +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/_version.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/_base.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/container.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/local.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/remote.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/backends/settings.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/common/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/common/timestamp.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/_local/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/_local/agent_startup.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/common.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/configuration.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/agent.proto +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/agent_pb2.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/agent_pb2.pyi +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/agent_pb2_grpc.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/common.proto +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/common_pb2.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/common_pb2.pyi +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/common_pb2_grpc.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/interface.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/ipc/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/logs.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/py.typed +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/registry.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/definitions/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/definitions/server.proto +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/definitions/server_pb2.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/definitions/server_pb2.pyi +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/definitions/server_pb2_grpc.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/health/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/health/health.proto +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/health/health_pb2.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/health/health_pb2.pyi +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/health/health_pb2_grpc.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/health_server.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/interface.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate/server/server.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate.egg-info/SOURCES.txt +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate.egg-info/dependency_links.txt +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate.egg-info/entry_points.txt +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate.egg-info/requires.txt +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/src/isolate.egg-info/top_level.txt +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tests/__init__.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tests/conftest.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tests/test_backends.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tests/test_concurrency.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tests/test_connections.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tests/test_isolate.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tests/test_log.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tests/test_serialization.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tests/test_server.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tools/Dockerfile +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tools/agent_requirements.txt +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tools/protobuf-requirements.txt +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tools/regen_grpc.py +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tools/requirements.txt +0 -0
- {isolate-0.12.15 → isolate-0.12.16}/tools/test_agent_requirements.txt +0 -0
|
@@ -171,23 +171,26 @@ def _unblocked_pipe() -> tuple[int, int]:
|
|
|
171
171
|
def logged_io(
|
|
172
172
|
stdout_hook: HookT,
|
|
173
173
|
stderr_hook: HookT | None = None,
|
|
174
|
-
|
|
174
|
+
log_hook: HookT | None = None,
|
|
175
|
+
) -> Iterator[tuple[int, int, int]]:
|
|
175
176
|
"""Open two new streams (for stdout and stderr, respectively) and start relaying all
|
|
176
177
|
the output from them to the given hooks."""
|
|
177
178
|
|
|
178
179
|
stdout_reader_fd, stdout_writer_fd = _unblocked_pipe()
|
|
179
180
|
stderr_reader_fd, stderr_writer_fd = _unblocked_pipe()
|
|
181
|
+
log_reader_fd, log_writer_fd = _unblocked_pipe()
|
|
180
182
|
|
|
181
183
|
termination_event = threading.Event()
|
|
182
184
|
io_observer = _io_observer(
|
|
183
185
|
hooks={
|
|
184
186
|
stdout_reader_fd: stdout_hook,
|
|
185
187
|
stderr_reader_fd: stderr_hook or stdout_hook,
|
|
188
|
+
log_reader_fd: log_hook or stdout_hook,
|
|
186
189
|
},
|
|
187
190
|
termination_event=termination_event,
|
|
188
191
|
)
|
|
189
192
|
try:
|
|
190
|
-
yield stdout_writer_fd, stderr_writer_fd
|
|
193
|
+
yield stdout_writer_fd, stderr_writer_fd, log_writer_fd
|
|
191
194
|
finally:
|
|
192
195
|
termination_event.set()
|
|
193
196
|
try:
|
|
@@ -171,7 +171,7 @@ class CondaEnvironment(BaseEnvironment[Path]):
|
|
|
171
171
|
|
|
172
172
|
def _run_conda(self, *args: Any) -> None:
|
|
173
173
|
conda_executable = get_executable(self._exec_command, self._exec_home)
|
|
174
|
-
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr):
|
|
174
|
+
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr, _):
|
|
175
175
|
subprocess.check_call(
|
|
176
176
|
[conda_executable, *args],
|
|
177
177
|
stdout=stdout,
|
|
@@ -80,7 +80,7 @@ class PyenvEnvironment(BaseEnvironment[Path]):
|
|
|
80
80
|
return Path(prefix.strip())
|
|
81
81
|
|
|
82
82
|
def _install_python(self, pyenv: Path, root_path: Path) -> None:
|
|
83
|
-
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr):
|
|
83
|
+
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr, _):
|
|
84
84
|
try:
|
|
85
85
|
subprocess.check_call(
|
|
86
86
|
[pyenv, "install", "--skip-existing", self.python_version],
|
|
@@ -102,7 +102,7 @@ class PyenvEnvironment(BaseEnvironment[Path]):
|
|
|
102
102
|
return None
|
|
103
103
|
|
|
104
104
|
pyenv_root = connection_key.parent.parent
|
|
105
|
-
with logged_io(self.log) as (stdout, stderr):
|
|
105
|
+
with logged_io(self.log) as (stdout, stderr, _):
|
|
106
106
|
subprocess.check_call(
|
|
107
107
|
[pyenv, "uninstall", "-f", connection_key.name],
|
|
108
108
|
env={**os.environ, "PYENV_ROOT": str(pyenv_root)},
|
|
@@ -110,7 +110,7 @@ class VirtualPythonEnvironment(BaseEnvironment[Path]):
|
|
|
110
110
|
for extra_index_url in self.extra_index_urls:
|
|
111
111
|
pip_cmd.extend(["--extra-index-url", extra_index_url])
|
|
112
112
|
|
|
113
|
-
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr):
|
|
113
|
+
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr, _):
|
|
114
114
|
try:
|
|
115
115
|
subprocess.check_call(
|
|
116
116
|
pip_cmd,
|
|
@@ -17,7 +17,7 @@ from typing import (
|
|
|
17
17
|
|
|
18
18
|
from isolate.backends.common import get_executable_path, logged_io
|
|
19
19
|
from isolate.connections.common import AGENT_SIGNATURE
|
|
20
|
-
from isolate.logs import LogLevel
|
|
20
|
+
from isolate.logs import LogLevel, LogSource
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
23
|
from isolate.backends import BaseEnvironment
|
|
@@ -113,14 +113,22 @@ class PythonExecutionBase(Generic[ConnectionType]):
|
|
|
113
113
|
|
|
114
114
|
python_executable = get_executable_path(self.environment_path, "python")
|
|
115
115
|
with logged_io(
|
|
116
|
-
partial(
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
partial(
|
|
117
|
+
self.handle_agent_log, source=LogSource.USER, level=LogLevel.STDOUT
|
|
118
|
+
),
|
|
119
|
+
partial(
|
|
120
|
+
self.handle_agent_log, source=LogSource.USER, level=LogLevel.STDERR
|
|
121
|
+
),
|
|
122
|
+
partial(
|
|
123
|
+
self.handle_agent_log, source=LogSource.BRIDGE, level=LogLevel.TRACE
|
|
124
|
+
),
|
|
125
|
+
) as (stdout, stderr, log_fd):
|
|
119
126
|
yield subprocess.Popen(
|
|
120
|
-
self.get_python_cmd(python_executable, connection),
|
|
127
|
+
self.get_python_cmd(python_executable, connection, log_fd),
|
|
121
128
|
env=self.get_env_vars(),
|
|
122
129
|
stdout=stdout,
|
|
123
130
|
stderr=stderr,
|
|
131
|
+
pass_fds=(log_fd,),
|
|
124
132
|
text=True,
|
|
125
133
|
)
|
|
126
134
|
|
|
@@ -158,11 +166,12 @@ class PythonExecutionBase(Generic[ConnectionType]):
|
|
|
158
166
|
self,
|
|
159
167
|
executable: Path,
|
|
160
168
|
connection: ConnectionType,
|
|
169
|
+
log_fd: int,
|
|
161
170
|
) -> list[str | Path]:
|
|
162
171
|
"""Return the command to run the agent process with."""
|
|
163
172
|
raise NotImplementedError
|
|
164
173
|
|
|
165
|
-
def handle_agent_log(self, line: str, level: LogLevel) -> None:
|
|
174
|
+
def handle_agent_log(self, line: str, level: LogLevel, source: LogSource) -> None:
|
|
166
175
|
"""Handle a log line emitted by the agent process. The level will be either
|
|
167
176
|
STDOUT or STDERR."""
|
|
168
177
|
raise NotImplementedError
|
|
@@ -136,13 +136,17 @@ class LocalPythonGRPC(PythonExecutionBase[str], GRPCExecutionBase):
|
|
|
136
136
|
self,
|
|
137
137
|
executable: Path,
|
|
138
138
|
connection: str,
|
|
139
|
+
log_fd: int,
|
|
139
140
|
) -> List[Union[str, Path]]:
|
|
140
141
|
return [
|
|
141
142
|
executable,
|
|
142
143
|
agent_startup.__file__,
|
|
143
144
|
agent.__file__,
|
|
144
145
|
connection,
|
|
146
|
+
"--log-fd",
|
|
147
|
+
str(log_fd),
|
|
145
148
|
]
|
|
146
149
|
|
|
147
|
-
def handle_agent_log(self, line: str, level: LogLevel) -> None:
|
|
148
|
-
|
|
150
|
+
def handle_agent_log(self, line: str, level: LogLevel, source: LogSource) -> None:
|
|
151
|
+
print(f"[{source}] [{level}] {line}")
|
|
152
|
+
self.log(line, level=level, source=source)
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
5
7
|
import traceback
|
|
6
8
|
from argparse import ArgumentParser
|
|
7
9
|
from concurrent import futures
|
|
8
|
-
from dataclasses import dataclass
|
|
10
|
+
from dataclasses import dataclass
|
|
9
11
|
from typing import (
|
|
10
12
|
Any,
|
|
11
|
-
Generator,
|
|
12
13
|
Iterable,
|
|
13
14
|
Iterator,
|
|
14
|
-
cast,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
import grpc
|
|
@@ -21,8 +21,7 @@ from isolate.backends.common import sha256_digest_of
|
|
|
21
21
|
from isolate.connections.common import SerializationError, serialize_object
|
|
22
22
|
from isolate.connections.grpc import definitions
|
|
23
23
|
from isolate.connections.grpc.configuration import get_default_options
|
|
24
|
-
from isolate.connections.grpc.interface import from_grpc
|
|
25
|
-
from isolate.logs import Log, LogLevel, LogSource
|
|
24
|
+
from isolate.connections.grpc.interface import from_grpc
|
|
26
25
|
|
|
27
26
|
|
|
28
27
|
@dataclass
|
|
@@ -30,16 +29,19 @@ class AbortException(Exception):
|
|
|
30
29
|
message: str
|
|
31
30
|
|
|
32
31
|
|
|
33
|
-
@dataclass
|
|
34
32
|
class AgentServicer(definitions.AgentServicer):
|
|
35
|
-
|
|
33
|
+
def __init__(self, log_fd: int | None = None):
|
|
34
|
+
super().__init__()
|
|
35
|
+
|
|
36
|
+
self._run_cache: dict[str, Any] = {}
|
|
37
|
+
self._log = sys.stdout if log_fd is None else os.fdopen(log_fd, "w")
|
|
36
38
|
|
|
37
39
|
def Run(
|
|
38
40
|
self,
|
|
39
41
|
request: definitions.FunctionCall,
|
|
40
42
|
context: ServicerContext,
|
|
41
43
|
) -> Iterator[definitions.PartialRunResult]:
|
|
42
|
-
|
|
44
|
+
self.log(f"A connection has been established: {context.peer()}!")
|
|
43
45
|
|
|
44
46
|
extra_args = []
|
|
45
47
|
if request.HasField("setup_func"):
|
|
@@ -53,16 +55,16 @@ class AgentServicer(definitions.AgentServicer):
|
|
|
53
55
|
result,
|
|
54
56
|
was_it_raised,
|
|
55
57
|
stringized_tb,
|
|
56
|
-
) =
|
|
58
|
+
) = self.execute_function(
|
|
57
59
|
request.setup_func,
|
|
58
60
|
"setup",
|
|
59
61
|
)
|
|
60
62
|
|
|
61
63
|
if was_it_raised:
|
|
62
|
-
|
|
64
|
+
self.log(
|
|
63
65
|
"The setup function has thrown an error. Aborting the run."
|
|
64
66
|
)
|
|
65
|
-
yield
|
|
67
|
+
yield self.send_object(
|
|
66
68
|
request.setup_func.method,
|
|
67
69
|
result,
|
|
68
70
|
was_it_raised,
|
|
@@ -78,12 +80,12 @@ class AgentServicer(definitions.AgentServicer):
|
|
|
78
80
|
extra_args.append(self._run_cache[cache_key])
|
|
79
81
|
|
|
80
82
|
try:
|
|
81
|
-
result, was_it_raised, stringized_tb =
|
|
83
|
+
result, was_it_raised, stringized_tb = self.execute_function(
|
|
82
84
|
request.function,
|
|
83
85
|
"function",
|
|
84
86
|
extra_args=extra_args,
|
|
85
87
|
)
|
|
86
|
-
yield
|
|
88
|
+
yield self.send_object(
|
|
87
89
|
request.function.method,
|
|
88
90
|
result,
|
|
89
91
|
was_it_raised,
|
|
@@ -98,7 +100,7 @@ class AgentServicer(definitions.AgentServicer):
|
|
|
98
100
|
function_kind: str,
|
|
99
101
|
*,
|
|
100
102
|
extra_args: Iterable[Any] = (),
|
|
101
|
-
) ->
|
|
103
|
+
) -> tuple[Any, bool, str | None]:
|
|
102
104
|
if function.was_it_raised:
|
|
103
105
|
raise AbortException(
|
|
104
106
|
f"The {function_kind} function must be callable, "
|
|
@@ -110,7 +112,7 @@ class AgentServicer(definitions.AgentServicer):
|
|
|
110
112
|
# depickling is basically involves code execution from the *user*.
|
|
111
113
|
function = from_grpc(function)
|
|
112
114
|
except SerializationError:
|
|
113
|
-
|
|
115
|
+
self.log(traceback.format_exc())
|
|
114
116
|
raise AbortException(
|
|
115
117
|
f"The {function_kind} function could not be deserialized."
|
|
116
118
|
)
|
|
@@ -121,7 +123,7 @@ class AgentServicer(definitions.AgentServicer):
|
|
|
121
123
|
f"not {type(function).__name__}."
|
|
122
124
|
)
|
|
123
125
|
|
|
124
|
-
|
|
126
|
+
self.log(f"Starting the execution of the {function_kind} function.")
|
|
125
127
|
|
|
126
128
|
was_it_raised = False
|
|
127
129
|
stringized_tb = None
|
|
@@ -133,7 +135,7 @@ class AgentServicer(definitions.AgentServicer):
|
|
|
133
135
|
num_frames = len(traceback.extract_stack()[:-5])
|
|
134
136
|
stringized_tb = "".join(traceback.format_exc(limit=-num_frames))
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
self.log(f"Completed the execution of the {function_kind} function.")
|
|
137
139
|
return result, was_it_raised, stringized_tb
|
|
138
140
|
|
|
139
141
|
def send_object(
|
|
@@ -142,46 +144,38 @@ class AgentServicer(definitions.AgentServicer):
|
|
|
142
144
|
result: object,
|
|
143
145
|
was_it_raised: bool,
|
|
144
146
|
stringized_tb: str | None,
|
|
145
|
-
) ->
|
|
147
|
+
) -> definitions.PartialRunResult:
|
|
146
148
|
try:
|
|
147
149
|
definition = serialize_object(serialization_method, result)
|
|
148
150
|
except SerializationError:
|
|
149
151
|
if stringized_tb:
|
|
150
|
-
|
|
151
|
-
stringized_tb, source=LogSource.USER, level=LogLevel.STDERR
|
|
152
|
-
)
|
|
152
|
+
print(stringized_tb, file=sys.stderr)
|
|
153
153
|
raise AbortException(
|
|
154
154
|
"Error while serializing the execution result "
|
|
155
155
|
f"(object of type {type(result)})."
|
|
156
156
|
)
|
|
157
157
|
except BaseException:
|
|
158
|
-
|
|
158
|
+
self.log(traceback.format_exc())
|
|
159
159
|
raise AbortException(
|
|
160
160
|
"An unexpected error occurred while serializing the result."
|
|
161
161
|
)
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
self.log("Sending the result.")
|
|
164
164
|
serialized_obj = definitions.SerializedObject(
|
|
165
165
|
method=serialization_method,
|
|
166
166
|
definition=definition,
|
|
167
167
|
was_it_raised=was_it_raised,
|
|
168
168
|
stringized_traceback=stringized_tb,
|
|
169
169
|
)
|
|
170
|
-
|
|
170
|
+
return definitions.PartialRunResult(
|
|
171
171
|
result=serialized_obj,
|
|
172
172
|
is_complete=True,
|
|
173
173
|
logs=[],
|
|
174
174
|
)
|
|
175
175
|
|
|
176
|
-
def log(
|
|
177
|
-
self
|
|
178
|
-
|
|
179
|
-
level: LogLevel = LogLevel.TRACE,
|
|
180
|
-
source: LogSource = LogSource.BRIDGE,
|
|
181
|
-
) -> Iterator[definitions.PartialRunResult]:
|
|
182
|
-
log = to_grpc(Log(message, level=level, source=source))
|
|
183
|
-
log = cast(definitions.Log, log)
|
|
184
|
-
yield definitions.PartialRunResult(result=None, is_complete=False, logs=[log])
|
|
176
|
+
def log(self, message: str) -> None:
|
|
177
|
+
self._log.write(message)
|
|
178
|
+
self._log.flush()
|
|
185
179
|
|
|
186
180
|
def abort_with_msg(
|
|
187
181
|
self,
|
|
@@ -211,10 +205,10 @@ def create_server(address: str) -> grpc.Server:
|
|
|
211
205
|
return server
|
|
212
206
|
|
|
213
207
|
|
|
214
|
-
def run_agent(address: str) -> int:
|
|
208
|
+
def run_agent(address: str, log_fd: int | None = None) -> int:
|
|
215
209
|
"""Run the agent servicer on the given address."""
|
|
216
210
|
server = create_server(address)
|
|
217
|
-
servicer = AgentServicer()
|
|
211
|
+
servicer = AgentServicer(log_fd=log_fd)
|
|
218
212
|
|
|
219
213
|
# This function just calls some methods on the server
|
|
220
214
|
# and register a generic handler for the bridge. It does
|
|
@@ -229,9 +223,10 @@ def run_agent(address: str) -> int:
|
|
|
229
223
|
def main() -> int:
|
|
230
224
|
parser = ArgumentParser()
|
|
231
225
|
parser.add_argument("address", type=str)
|
|
226
|
+
parser.add_argument("--log-fd", type=int)
|
|
232
227
|
|
|
233
228
|
options = parser.parse_args()
|
|
234
|
-
return run_agent(options.address)
|
|
229
|
+
return run_agent(options.address, log_fd=options.log_fd)
|
|
235
230
|
|
|
236
231
|
|
|
237
232
|
if __name__ == "__main__":
|
|
@@ -202,6 +202,7 @@ class PythonIPC(PythonExecutionBase[AgentListener], IsolatedProcessConnection):
|
|
|
202
202
|
self,
|
|
203
203
|
executable: Path,
|
|
204
204
|
connection: AgentListener,
|
|
205
|
+
log_fd: int,
|
|
205
206
|
) -> list[str | Path]:
|
|
206
207
|
assert isinstance(connection.address, tuple)
|
|
207
208
|
return [
|
|
@@ -214,21 +215,9 @@ class PythonIPC(PythonExecutionBase[AgentListener], IsolatedProcessConnection):
|
|
|
214
215
|
# the connection with the bridge.
|
|
215
216
|
"--serialization-backend",
|
|
216
217
|
self.environment.settings.serialization_method,
|
|
218
|
+
"--log-fd",
|
|
219
|
+
str(log_fd),
|
|
217
220
|
]
|
|
218
221
|
|
|
219
|
-
def handle_agent_log(self, line: str, level: LogLevel) -> None:
|
|
220
|
-
# TODO: we probably should create a new fd and pass it as
|
|
221
|
-
# one of the the arguments to the child process. Then everything
|
|
222
|
-
# from that fd can be automatically logged as originating from the
|
|
223
|
-
# bridge.
|
|
224
|
-
|
|
225
|
-
# Agent can produce [trace] messages, so change the log
|
|
226
|
-
# level to it if this does not originate from the user.
|
|
227
|
-
if line.startswith("[trace]"):
|
|
228
|
-
line = line.replace("[trace]", "", 1)
|
|
229
|
-
level = LogLevel.TRACE
|
|
230
|
-
source = LogSource.BRIDGE
|
|
231
|
-
else:
|
|
232
|
-
source = LogSource.USER
|
|
233
|
-
|
|
222
|
+
def handle_agent_log(self, line: str, level: LogLevel, source: LogSource) -> None:
|
|
234
223
|
self.log(line, level=level, source=source)
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
# one being the actual result of the given callable, and the other one is a boolean flag
|
|
16
16
|
# indicating whether the callable has raised an exception or not.
|
|
17
17
|
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
18
20
|
import base64
|
|
19
21
|
import importlib
|
|
20
22
|
import os
|
|
@@ -24,7 +26,7 @@ import traceback
|
|
|
24
26
|
from argparse import ArgumentParser
|
|
25
27
|
from contextlib import closing
|
|
26
28
|
from multiprocessing.connection import Client
|
|
27
|
-
from typing import TYPE_CHECKING, Any, Callable, ContextManager
|
|
29
|
+
from typing import TYPE_CHECKING, Any, Callable, ContextManager
|
|
28
30
|
|
|
29
31
|
if TYPE_CHECKING:
|
|
30
32
|
# Somhow mypy can't figure out that `ConnectionWrapper`
|
|
@@ -47,13 +49,13 @@ else:
|
|
|
47
49
|
from multiprocessing.connection import ConnectionWrapper
|
|
48
50
|
|
|
49
51
|
|
|
50
|
-
def decode_service_address(address: str) ->
|
|
52
|
+
def decode_service_address(address: str) -> tuple[str, int]:
|
|
51
53
|
host, port = base64.b64decode(address).decode("utf-8").rsplit(":", 1)
|
|
52
54
|
return host, int(port)
|
|
53
55
|
|
|
54
56
|
|
|
55
57
|
def child_connection(
|
|
56
|
-
serialization_method: str, address:
|
|
58
|
+
serialization_method: str, address: tuple[str, int]
|
|
57
59
|
) -> ContextManager[ConnectionWrapper]:
|
|
58
60
|
serialization_backend = importlib.import_module(serialization_method)
|
|
59
61
|
return closing(
|
|
@@ -70,7 +72,11 @@ DEBUG_TIMEOUT = 60 * 15
|
|
|
70
72
|
|
|
71
73
|
|
|
72
74
|
def run_client(
|
|
73
|
-
serialization_method: str,
|
|
75
|
+
serialization_method: str,
|
|
76
|
+
address: tuple[str, int],
|
|
77
|
+
*,
|
|
78
|
+
with_pdb: bool = False,
|
|
79
|
+
log_fd: int | None = None,
|
|
74
80
|
) -> None:
|
|
75
81
|
# Debug Mode
|
|
76
82
|
# ==========
|
|
@@ -96,13 +102,22 @@ def run_client(
|
|
|
96
102
|
|
|
97
103
|
pdb.set_trace()
|
|
98
104
|
|
|
99
|
-
|
|
105
|
+
if log_fd is None:
|
|
106
|
+
_log = sys.stdout
|
|
107
|
+
else:
|
|
108
|
+
_log = os.fdopen(log_fd, "w")
|
|
109
|
+
|
|
110
|
+
def log(_msg):
|
|
111
|
+
_log.write(_msg)
|
|
112
|
+
_log.flush()
|
|
113
|
+
|
|
114
|
+
log(f"Trying to create a connection to {address}")
|
|
100
115
|
# TODO(feat): this should probably run in a loop instead of
|
|
101
116
|
# receiving a single function and then exitting immediately.
|
|
102
117
|
with child_connection(serialization_method, address) as connection:
|
|
103
|
-
|
|
118
|
+
log(f"Created child connection to {address}")
|
|
104
119
|
callable = connection.recv()
|
|
105
|
-
|
|
120
|
+
log(f"Received the callable at {address}")
|
|
106
121
|
|
|
107
122
|
result = None
|
|
108
123
|
did_it_raise = False
|
|
@@ -149,12 +164,11 @@ def _get_shell_bootstrap() -> str:
|
|
|
149
164
|
|
|
150
165
|
|
|
151
166
|
def main() -> int:
|
|
152
|
-
print(f"[trace] Starting the isolated process at PID {os.getpid()}")
|
|
153
|
-
|
|
154
167
|
parser = ArgumentParser()
|
|
155
168
|
parser.add_argument("listen_at")
|
|
156
169
|
parser.add_argument("--with-pdb", action="store_true", default=False)
|
|
157
170
|
parser.add_argument("--serialization-backend", default="pickle")
|
|
171
|
+
parser.add_argument("--log-fd", type=int)
|
|
158
172
|
|
|
159
173
|
options = parser.parse_args()
|
|
160
174
|
if IS_DEBUG_MODE:
|
|
@@ -178,7 +192,12 @@ def main() -> int:
|
|
|
178
192
|
|
|
179
193
|
serialization_method = options.serialization_backend
|
|
180
194
|
address = decode_service_address(options.listen_at)
|
|
181
|
-
run_client(
|
|
195
|
+
run_client(
|
|
196
|
+
serialization_method,
|
|
197
|
+
address,
|
|
198
|
+
with_pdb=options.with_pdb,
|
|
199
|
+
log_fd=options.log_fd,
|
|
200
|
+
)
|
|
182
201
|
return 0
|
|
183
202
|
|
|
184
203
|
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/agent_pb2_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{isolate-0.12.15 → isolate-0.12.16}/src/isolate/connections/grpc/definitions/common_pb2_grpc.py
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|