isolate 0.13.8__tar.gz → 0.13.10__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.13.8 → isolate-0.13.10}/PKG-INFO +1 -1
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/_isolate_version.py +2 -2
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/logger.py +14 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/server.py +113 -3
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate.egg-info/PKG-INFO +1 -1
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate.egg-info/SOURCES.txt +1 -0
- isolate-0.13.10/tests/test_logger.py +22 -0
- {isolate-0.13.8 → isolate-0.13.10}/.github/workflows/release.yml +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/.github/workflows/test.yml +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/.gitignore +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/.pre-commit-config.yaml +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/LICENSE +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/README.md +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/pyproject.toml +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/setup.cfg +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/_version.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/_base.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/common.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/conda.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/container.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/local.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/pyenv.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/remote.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/settings.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/backends/virtualenv.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/common/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/common/timestamp.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/_local/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/_local/_base.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/_local/agent_startup.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/common.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/_base.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/agent.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/configuration.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/agent.proto +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/agent_pb2.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/agent_pb2.pyi +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/agent_pb2_grpc.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/common.proto +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/common_pb2.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/common_pb2.pyi +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/common_pb2_grpc.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/interface.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/ipc/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/ipc/_base.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/ipc/agent.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/logs.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/py.typed +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/registry.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/definitions/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/definitions/server.proto +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/definitions/server_pb2.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/definitions/server_pb2.pyi +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/definitions/server_pb2_grpc.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/health/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/health/health.proto +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/health/health_pb2.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/health/health_pb2.pyi +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/health/health_pb2_grpc.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/health_server.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate/server/interface.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate.egg-info/dependency_links.txt +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate.egg-info/entry_points.txt +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate.egg-info/requires.txt +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/src/isolate.egg-info/top_level.txt +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tests/__init__.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tests/conftest.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tests/test_backends.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tests/test_concurrency.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tests/test_connections.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tests/test_isolate.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tests/test_log.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tests/test_serialization.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tests/test_server.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tools/Dockerfile +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tools/agent_requirements.txt +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tools/protobuf-requirements.txt +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tools/regen_grpc.py +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tools/requirements.txt +0 -0
- {isolate-0.13.8 → isolate-0.13.10}/tools/test_agent_requirements.txt +0 -0
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import os
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
# NOTE: we probably should've created a proper `logging.getLogger` here,
|
|
5
6
|
# but it handling `source` would be not trivial, so we are better off
|
|
6
7
|
# just keeping it simple for now.
|
|
7
8
|
class IsolateLogger:
|
|
9
|
+
def __init__(self):
|
|
10
|
+
self.log_labels = {}
|
|
11
|
+
raw = os.getenv("ISOLATE_LOG_LABELS")
|
|
12
|
+
if raw:
|
|
13
|
+
labels = json.loads(raw)
|
|
14
|
+
for key, value in labels.items():
|
|
15
|
+
if value.startswith("$"):
|
|
16
|
+
expanded = os.getenv(value[1:])
|
|
17
|
+
else:
|
|
18
|
+
expanded = value
|
|
19
|
+
self.log_labels[key] = expanded
|
|
20
|
+
|
|
8
21
|
def log(self, level, message, source):
|
|
9
22
|
record = {
|
|
10
23
|
"isolate_source": source.name,
|
|
11
24
|
"level": level.name,
|
|
12
25
|
"message": message,
|
|
26
|
+
**self.log_labels,
|
|
13
27
|
}
|
|
14
28
|
print(json.dumps(record))
|
|
15
29
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import functools
|
|
3
4
|
import os
|
|
4
5
|
import threading
|
|
5
6
|
import time
|
|
6
7
|
import traceback
|
|
7
8
|
import uuid
|
|
9
|
+
from argparse import ArgumentParser
|
|
8
10
|
from collections import defaultdict
|
|
9
11
|
from concurrent import futures
|
|
10
12
|
from concurrent.futures import ThreadPoolExecutor
|
|
@@ -16,6 +18,7 @@ from typing import Any, Callable, Iterator, cast
|
|
|
16
18
|
|
|
17
19
|
import grpc
|
|
18
20
|
from grpc import ServicerContext, StatusCode
|
|
21
|
+
from grpc.experimental import wrap_server_method_handler
|
|
19
22
|
|
|
20
23
|
from isolate.backends import (
|
|
21
24
|
EnvironmentCreationError,
|
|
@@ -465,13 +468,120 @@ class LogHandler:
|
|
|
465
468
|
self.messages.put_nowait(grpc_result)
|
|
466
469
|
|
|
467
470
|
|
|
468
|
-
|
|
471
|
+
@dataclass
|
|
472
|
+
class ServerBoundInterceptor(grpc.ServerInterceptor):
|
|
473
|
+
_server: grpc.Server | None = None
|
|
474
|
+
_servicer: IsolateServicer | None = None
|
|
475
|
+
|
|
476
|
+
def register_server(self, server: grpc.Server) -> None:
|
|
477
|
+
if self._server is not None:
|
|
478
|
+
raise RuntimeError("A server is already bound to this interceptor.")
|
|
479
|
+
|
|
480
|
+
self._server = server
|
|
481
|
+
|
|
482
|
+
@property
|
|
483
|
+
def server(self) -> grpc.Server:
|
|
484
|
+
if self._server is None:
|
|
485
|
+
raise RuntimeError("No server was bound to this interceptor.")
|
|
486
|
+
|
|
487
|
+
return self._server
|
|
488
|
+
|
|
489
|
+
def register_servicer(self, servicer: IsolateServicer) -> None:
|
|
490
|
+
if self._servicer is not None:
|
|
491
|
+
raise RuntimeError("A servicer is already bound to this interceptor.")
|
|
492
|
+
|
|
493
|
+
self._servicer = servicer
|
|
494
|
+
|
|
495
|
+
@property
|
|
496
|
+
def servicer(self) -> IsolateServicer:
|
|
497
|
+
if self._servicer is None:
|
|
498
|
+
raise RuntimeError("No servicer was bound to this interceptor.")
|
|
499
|
+
|
|
500
|
+
return self._servicer
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
@dataclass
|
|
504
|
+
class SingleTaskInterceptor(ServerBoundInterceptor):
|
|
505
|
+
"""Sets server to terminate after the first Submit/Run task."""
|
|
506
|
+
|
|
507
|
+
_done: bool = False
|
|
508
|
+
|
|
509
|
+
def intercept_service(self, continuation, handler_call_details):
|
|
510
|
+
handler = continuation(handler_call_details)
|
|
511
|
+
|
|
512
|
+
is_submit = handler_call_details.method == "/Isolate/Submit"
|
|
513
|
+
is_run = handler_call_details.method == "/Isolate/Run"
|
|
514
|
+
is_new_task = is_submit or is_run
|
|
515
|
+
|
|
516
|
+
if is_new_task and self._done:
|
|
517
|
+
raise grpc.RpcError(
|
|
518
|
+
grpc.StatusCode.UNAVAILABLE,
|
|
519
|
+
"Server has already served one Run/Submit task.",
|
|
520
|
+
)
|
|
521
|
+
elif is_new_task:
|
|
522
|
+
self._done = True
|
|
523
|
+
else:
|
|
524
|
+
# Let other requests like List/Cancel/etc pass through
|
|
525
|
+
return continuation(handler_call_details)
|
|
526
|
+
|
|
527
|
+
def wrapper(method_impl):
|
|
528
|
+
@functools.wraps(method_impl)
|
|
529
|
+
def _wrapper(request, context):
|
|
530
|
+
def _stop():
|
|
531
|
+
if is_submit:
|
|
532
|
+
# Wait for the task to finish
|
|
533
|
+
while self.server.servicer.background_tasks:
|
|
534
|
+
time.sleep(0.1)
|
|
535
|
+
self.server.stop(grace=0.1)
|
|
536
|
+
|
|
537
|
+
context.add_callback(_stop)
|
|
538
|
+
return method_impl(request, context)
|
|
539
|
+
|
|
540
|
+
return _wrapper
|
|
541
|
+
|
|
542
|
+
return wrap_server_method_handler(wrapper, handler)
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def main(argv: list[str] | None = None) -> None:
|
|
546
|
+
parser = ArgumentParser()
|
|
547
|
+
parser.add_argument("--host", default="0.0.0.0")
|
|
548
|
+
parser.add_argument("--port", type=int, default=50001)
|
|
549
|
+
parser.add_argument(
|
|
550
|
+
"--single-use",
|
|
551
|
+
action="store_true",
|
|
552
|
+
help="Terminate the server after the first Run or Submit task is completed.",
|
|
553
|
+
)
|
|
554
|
+
parser.add_argument(
|
|
555
|
+
"--num-workers",
|
|
556
|
+
type=int,
|
|
557
|
+
default=MAX_THREADS,
|
|
558
|
+
help="Number of worker threads to use for the gRPC server.",
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
options = parser.parse_args(argv)
|
|
562
|
+
if options.num_workers is None:
|
|
563
|
+
options.num_workers = 1 if options.single_use else os.cpu_count()
|
|
564
|
+
|
|
565
|
+
interceptors: list[ServerBoundInterceptor] = []
|
|
566
|
+
if options.single_use:
|
|
567
|
+
interceptors.append(SingleTaskInterceptor())
|
|
568
|
+
|
|
469
569
|
server = grpc.server(
|
|
470
|
-
futures.ThreadPoolExecutor(max_workers=
|
|
570
|
+
futures.ThreadPoolExecutor(max_workers=options.num_workers),
|
|
471
571
|
options=get_default_options(),
|
|
572
|
+
interceptors=interceptors,
|
|
472
573
|
)
|
|
574
|
+
|
|
575
|
+
for interceptor in interceptors:
|
|
576
|
+
interceptor.register_server(server)
|
|
577
|
+
|
|
473
578
|
with BridgeManager() as bridge_manager:
|
|
474
|
-
|
|
579
|
+
servicer = IsolateServicer(bridge_manager)
|
|
580
|
+
|
|
581
|
+
for interceptor in interceptors:
|
|
582
|
+
interceptor.register_servicer(servicer)
|
|
583
|
+
|
|
584
|
+
definitions.register_isolate(servicer, server)
|
|
475
585
|
health.register_health(HealthServicer(), server)
|
|
476
586
|
|
|
477
587
|
server.add_insecure_port("[::]:50001")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from isolate.logger import IsolateLogger
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_logger(monkeypatch):
|
|
7
|
+
labels = {
|
|
8
|
+
"foo": "$MYENVVAR1",
|
|
9
|
+
"bar": "$MYENVVAR2",
|
|
10
|
+
"baz": "baz",
|
|
11
|
+
"qux": "qux",
|
|
12
|
+
}
|
|
13
|
+
monkeypatch.setenv("MYENVVAR1", "myenvvar1")
|
|
14
|
+
monkeypatch.setenv("MYENVVAR2", "myenvvar2")
|
|
15
|
+
monkeypatch.setenv("ISOLATE_LOG_LABELS", json.dumps(labels))
|
|
16
|
+
logger = IsolateLogger()
|
|
17
|
+
assert logger.log_labels == {
|
|
18
|
+
"foo": "myenvvar1",
|
|
19
|
+
"bar": "myenvvar2",
|
|
20
|
+
"baz": "baz",
|
|
21
|
+
"qux": "qux",
|
|
22
|
+
}
|
|
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
|
{isolate-0.13.8 → isolate-0.13.10}/src/isolate/connections/grpc/definitions/agent_pb2_grpc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{isolate-0.13.8 → isolate-0.13.10}/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
|