Skip to content

Commit

Permalink
chore: temporarily revert serialization/logging changes back to v0.12…
Browse files Browse the repository at this point in the history
….7 (#136)

* chore: revert to v0.12.7

* build: add cloudpickle to agent requirements (#118)

* chore: drop python 3.7 and use ruff/poetry-check/pyupgrade (#119)

* lint: use ruff

* chore: add poetry-check to pre-commit

* chore: drop python 3.7

* chore: add pre-commit to gha

* chore: run tests on schedule

* chore: only test 3.8 and 3.11 on macos

* fix(test): readlinks for python before comparing

* fix(conda): use --yes instead of --force (#123)

A few days ago conda 24.3.0 removed `--force` completely causing this
to no longer work. `--yes` has been around for more than a year now
and we don't really need to support old conda/miniconda so we can just
directly replace it and not bother with compatibility with older versions.

https://github.com/conda/conda/releases/tag/24.3.0

* chore: revisit build/test/release process (#126)

* chore: use setuptools instead of poetry

* chore: introduce dev extra

* chore(gha): revisit release process

* chore: activate more ruff rules

* chore: delete cliff.toml (#127)

No longer used.

* feat(backend): Add container backend (#135)

---------

Co-authored-by: Kemal Akkoyun <[email protected]>
  • Loading branch information
efiop and kakkoyun authored May 24, 2024
1 parent 6563a56 commit a8e38be
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 135 deletions.
3 changes: 1 addition & 2 deletions src/isolate/backends/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
)

from isolate.backends.settings import DEFAULT_SETTINGS, IsolateSettings
from isolate.exceptions import IsolateException
from isolate.logs import Log, LogLevel, LogSource

__all__ = [
Expand All @@ -28,7 +27,7 @@
BasicCallable = Callable[[], CallResultType]


class EnvironmentCreationError(IsolateException):
class EnvironmentCreationError(Exception):
"""Raised when the environment cannot be created."""


Expand Down
7 changes: 2 additions & 5 deletions src/isolate/backends/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,26 +171,23 @@ def _unblocked_pipe() -> tuple[int, int]:
def logged_io(
stdout_hook: HookT,
stderr_hook: HookT | None = None,
log_hook: HookT | None = None,
) -> Iterator[tuple[int, int, int]]:
) -> Iterator[tuple[int, int]]:
"""Open two new streams (for stdout and stderr, respectively) and start relaying all
the output from them to the given hooks."""

stdout_reader_fd, stdout_writer_fd = _unblocked_pipe()
stderr_reader_fd, stderr_writer_fd = _unblocked_pipe()
log_reader_fd, log_writer_fd = _unblocked_pipe()

termination_event = threading.Event()
io_observer = _io_observer(
hooks={
stdout_reader_fd: stdout_hook,
stderr_reader_fd: stderr_hook or stdout_hook,
log_reader_fd: log_hook or stdout_hook,
},
termination_event=termination_event,
)
try:
yield stdout_writer_fd, stderr_writer_fd, log_writer_fd
yield stdout_writer_fd, stderr_writer_fd
finally:
termination_event.set()
try:
Expand Down
2 changes: 1 addition & 1 deletion src/isolate/backends/conda.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def _run_destroy(self, connection_key: str) -> None:

def _run_conda(self, *args: Any) -> None:
conda_executable = get_executable(self._exec_command, self._exec_home)
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr, _):
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr):
subprocess.check_call(
[conda_executable, *args],
stdout=stdout,
Expand Down
4 changes: 2 additions & 2 deletions src/isolate/backends/pyenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def _try_get_prefix(self, pyenv: Path, root_path: Path) -> Path | None:
return Path(prefix.strip())

def _install_python(self, pyenv: Path, root_path: Path) -> None:
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr, _):
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr):
try:
subprocess.check_call(
[pyenv, "install", "--skip-existing", self.python_version],
Expand All @@ -102,7 +102,7 @@ def destroy(self, connection_key: Path) -> None:
return None

pyenv_root = connection_key.parent.parent
with logged_io(self.log) as (stdout, stderr, _):
with logged_io(self.log) as (stdout, stderr):
subprocess.check_call(
[pyenv, "uninstall", "-f", connection_key.name],
env={**os.environ, "PYENV_ROOT": str(pyenv_root)},
Expand Down
2 changes: 1 addition & 1 deletion src/isolate/backends/virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def install_requirements(self, path: Path) -> None:
for extra_index_url in self.extra_index_urls:
pip_cmd.extend(["--extra-index-url", extra_index_url])

with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr, _):
with logged_io(partial(self.log, level=LogLevel.INFO)) as (stdout, stderr):
try:
subprocess.check_call(
pip_cmd,
Expand Down
21 changes: 6 additions & 15 deletions src/isolate/connections/_local/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from isolate.backends.common import get_executable_path, logged_io
from isolate.connections.common import AGENT_SIGNATURE
from isolate.logs import LogLevel, LogSource
from isolate.logs import LogLevel

if TYPE_CHECKING:
from isolate.backends import BaseEnvironment
Expand Down Expand Up @@ -113,22 +113,14 @@ def start_process(

python_executable = get_executable_path(self.environment_path, "python")
with logged_io(
partial(
self.handle_agent_log, source=LogSource.USER, level=LogLevel.STDOUT
),
partial(
self.handle_agent_log, source=LogSource.USER, level=LogLevel.STDERR
),
partial(
self.handle_agent_log, source=LogSource.BRIDGE, level=LogLevel.TRACE
),
) as (stdout, stderr, log_fd):
partial(self.handle_agent_log, level=LogLevel.STDOUT),
partial(self.handle_agent_log, level=LogLevel.STDERR),
) as (stdout, stderr):
yield subprocess.Popen(
self.get_python_cmd(python_executable, connection, log_fd),
self.get_python_cmd(python_executable, connection),
env=self.get_env_vars(),
stdout=stdout,
stderr=stderr,
pass_fds=(log_fd,),
text=True,
)

Expand Down Expand Up @@ -166,12 +158,11 @@ def get_python_cmd(
self,
executable: Path,
connection: ConnectionType,
log_fd: int,
) -> list[str | Path]:
"""Return the command to run the agent process with."""
raise NotImplementedError

def handle_agent_log(self, line: str, level: LogLevel, source: LogSource) -> None:
def handle_agent_log(self, line: str, level: LogLevel) -> None:
"""Handle a log line emitted by the agent process. The level will be either
STDOUT or STDERR."""
raise NotImplementedError
49 changes: 20 additions & 29 deletions src/isolate/connections/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import importlib
import os
from typing import TYPE_CHECKING, Any, cast
from contextlib import contextmanager
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Iterator, cast

from tblib import Traceback, TracebackParseError

from isolate.exceptions import IsolateException

if TYPE_CHECKING:
from typing import Protocol

Expand All @@ -20,18 +20,24 @@ def dumps(self, obj: Any) -> bytes: ...
AGENT_SIGNATURE = "IS_ISOLATE_AGENT"


class BaseSerializationError(IsolateException):
@dataclass
class SerializationError(Exception):
"""An error that happened during the serialization process."""

pass

message: str

class SerializationError(BaseSerializationError):
pass

@contextmanager
def _step(message: str) -> Iterator[None]:
"""A context manager to capture every expression
underneath it and if any of them fails for any reason
then it will raise a SerializationError with the
given message."""

class DeserializationError(BaseSerializationError):
pass
try:
yield
except BaseException as exception:
raise SerializationError("Error while " + message) from exception


def as_serialization_method(backend: Any) -> SerializationBackend:
Expand Down Expand Up @@ -60,22 +66,13 @@ def load_serialized_object(
flag is set to true, then the given object will be raised as an exception (instead
of being returned)."""

try:
with _step(f"preparing the serialization backend ({serialization_method})"):
serialization_backend = as_serialization_method(
importlib.import_module(serialization_method)
)
except BaseException as exc:
raise DeserializationError(
"Error while preparing the serialization backend "
f"({serialization_method})"
) from exc

try:
with _step("deserializing the given object"):
result = serialization_backend.loads(raw_object)
except BaseException as exc:
raise DeserializationError(
"Error while deserializing the given object"
) from exc

if was_it_raised:
raise prepare_exc(result, stringized_traceback=stringized_traceback)
Expand All @@ -87,19 +84,13 @@ def serialize_object(serialization_method: str, object: Any) -> bytes:
"""Serialize the given object using the given serialization method. If
anything fails, then a SerializationError will be raised."""

try:
with _step(f"preparing the serialization backend ({serialization_method})"):
serialization_backend = as_serialization_method(
importlib.import_module(serialization_method)
)
except BaseException as exc:
raise SerializationError(
f"Error while preparing the serialization backend ({serialization_method})"
) from exc

try:
with _step("serializing the given object"):
return serialization_backend.dumps(object)
except BaseException as exc:
raise SerializationError("Error while serializing the given object") from exc


def is_agent() -> bool:
Expand Down
11 changes: 3 additions & 8 deletions src/isolate/connections/grpc/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
from isolate.connections.grpc import agent, definitions
from isolate.connections.grpc.configuration import get_default_options
from isolate.connections.grpc.interface import from_grpc
from isolate.exceptions import IsolateException
from isolate.logs import LogLevel, LogSource


class AgentError(IsolateException):
class AgentError(Exception):
"""An internal problem caused by (most probably) the agent."""


Expand Down Expand Up @@ -137,17 +136,13 @@ def get_python_cmd(
self,
executable: Path,
connection: str,
log_fd: int,
) -> List[Union[str, Path]]:
return [
executable,
agent_startup.__file__,
agent.__file__,
connection,
"--log-fd",
str(log_fd),
]

def handle_agent_log(self, line: str, level: LogLevel, source: LogSource) -> None:
print(f"[{source}] [{level}] {line}")
self.log(line, level=level, source=source)
def handle_agent_log(self, line: str, level: LogLevel) -> None:
self.log(line, level=level, source=LogSource.USER)
Loading

0 comments on commit a8e38be

Please sign in to comment.