isolate 0.13.8__py3-none-any.whl → 0.13.9__py3-none-any.whl

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.

@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.13.8'
16
- __version_tuple__ = version_tuple = (0, 13, 8)
15
+ __version__ = version = '0.13.9'
16
+ __version_tuple__ = version_tuple = (0, 13, 9)
isolate/server/server.py CHANGED
@@ -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
- def main() -> None:
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=MAX_THREADS),
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
- definitions.register_isolate(IsolateServicer(bridge_manager), server)
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")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: isolate
3
- Version: 0.13.8
3
+ Version: 0.13.9
4
4
  Summary: Managed isolated environments for Python
5
5
  Author-email: Features & Labels <hello@fal.ai>
6
6
  Project-URL: Issues, https://github.com/fal-ai/isolate/issues
@@ -8,23 +8,23 @@ Project-URL: Source, https://github.com/fal-ai/isolate
8
8
  Requires-Python: >=3.8
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: grpcio ==1.64.0
11
+ Requires-Dist: grpcio==1.64.0
12
12
  Requires-Dist: protobuf
13
- Requires-Dist: tblib >=1.7.0
13
+ Requires-Dist: tblib>=1.7.0
14
14
  Requires-Dist: platformdirs
15
- Requires-Dist: importlib-metadata >=4.4 ; python_version < "3.10"
15
+ Requires-Dist: importlib-metadata>=4.4; python_version < "3.10"
16
16
  Provides-Extra: build
17
- Requires-Dist: virtualenv >=20.4 ; extra == 'build'
18
- Requires-Dist: PyYAML >=6.0 ; extra == 'build'
17
+ Requires-Dist: virtualenv>=20.4; extra == "build"
18
+ Requires-Dist: PyYAML>=6.0; extra == "build"
19
19
  Provides-Extra: dev
20
- Requires-Dist: isolate[test] ; extra == 'dev'
21
- Requires-Dist: grpcio-tools ==1.64.0 ; extra == 'dev'
20
+ Requires-Dist: isolate[test]; extra == "dev"
21
+ Requires-Dist: grpcio-tools==1.64.0; extra == "dev"
22
22
  Provides-Extra: test
23
- Requires-Dist: isolate[build] ; extra == 'test'
24
- Requires-Dist: pytest ; extra == 'test'
25
- Requires-Dist: cloudpickle >=2.2.0 ; extra == 'test'
26
- Requires-Dist: dill >=0.3.5.1 ; extra == 'test'
27
- Requires-Dist: pytest-rerunfailures ; extra == 'test'
23
+ Requires-Dist: isolate[build]; extra == "test"
24
+ Requires-Dist: pytest; extra == "test"
25
+ Requires-Dist: cloudpickle>=2.2.0; extra == "test"
26
+ Requires-Dist: dill>=0.3.5.1; extra == "test"
27
+ Requires-Dist: pytest-rerunfailures; extra == "test"
28
28
 
29
29
  # Isolate
30
30
 
@@ -1,5 +1,5 @@
1
1
  isolate/__init__.py,sha256=uXOKnONs7sXgARNgElwr4_A1sKoA6ACHVEvs3IDiX1M,127
2
- isolate/_isolate_version.py,sha256=hnyeLxwVkL3Abdy189hhIoA4c9gB_O3-7cct0Y_GGj8,413
2
+ isolate/_isolate_version.py,sha256=I63RKqTcPf9RTCjzPcwnvKN-SWCoNcNIRzHaQnVkYig,413
3
3
  isolate/_version.py,sha256=05pXvy-yr5t3I1m9JMn42Ilzpg7fa8IB2J8a3G7t1cU,274
4
4
  isolate/logger.py,sha256=SehnK6rPx-HDqQHJ3sKWqhDGmD3fTDGBIkjnNQYnFJU,453
5
5
  isolate/logs.py,sha256=R_AHUVYD18z_PhtK_mDWi9Gch79CxmwHY09hUDShtwg,2079
@@ -42,7 +42,7 @@ isolate/connections/ipc/agent.py,sha256=hGlL4x78FhRvMZ4DkVh3dk-EmWQqxHW4LIipgyOk
42
42
  isolate/server/__init__.py,sha256=7R3GuWmxuqe0q28rVqETJN9OCrP_-Svjv9h0NR1GFL0,79
43
43
  isolate/server/health_server.py,sha256=yN7F1Q28DdX8-Zk3gef7XcQEE25XwlHwzV5GBM75aQM,1249
44
44
  isolate/server/interface.py,sha256=nGbjdxrN0p9m1LNdeds8NIoJOwPYW2NM6ktmbhfG4_s,687
45
- isolate/server/server.py,sha256=D02YtYvGv3K3bYV5DFkb1o5VJpl3RQy37xyXGOqWeQQ,16560
45
+ isolate/server/server.py,sha256=9abptODXQIdswNBQqEYY3yRGz6Jr4NFVs15FiftfRdw,20125
46
46
  isolate/server/definitions/__init__.py,sha256=f_Q3pdjMuZrjgNlbM60btFKiB1Vg8cnVyKEbp0RmU0A,572
47
47
  isolate/server/definitions/server.proto,sha256=GIou0c_RP4sVDxjdYQzUtSptOfJtvbgsU4MGhL9hsMQ,1372
48
48
  isolate/server/definitions/server_pb2.py,sha256=SdbigRwRd1pU1yFwvyrA4vkmwQHPelPkQT7dtggKUlk,3194
@@ -53,9 +53,9 @@ isolate/server/health/health.proto,sha256=wE2_QD0OQAblKkEBG7sALLXEOj1mOLKG-FbC4t
53
53
  isolate/server/health/health_pb2.py,sha256=onOdP3M4Tpqhqs2PlGcyfoKe2VVKUEDx5ALeRcObb9A,1899
54
54
  isolate/server/health/health_pb2.pyi,sha256=CPyvxvDzra-1d-mBsukaJnscMUDBaqSACvo9LiXlFzo,2416
55
55
  isolate/server/health/health_pb2_grpc.py,sha256=XgsULrnRBmYIqvKr8eI7bqs6NIea5A0kkqdOOc2JHBY,5303
56
- isolate-0.13.8.dist-info/LICENSE,sha256=427vuyirL5scgBLqA9UWcdnxKrtSGc0u_JfUupk6lAA,11359
57
- isolate-0.13.8.dist-info/METADATA,sha256=nHwIIZULtQ-heNKVZd4IRw89JuKGff-XR-jngc4sBVI,3209
58
- isolate-0.13.8.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
59
- isolate-0.13.8.dist-info/entry_points.txt,sha256=s3prh2EERaVCbL8R45tfY5WFPZ1TsYOsz305YR7s-Pc,360
60
- isolate-0.13.8.dist-info/top_level.txt,sha256=W9QJBHcq5WXRkbOXf25bvftzFsOZZN4n1DAatdroZrs,8
61
- isolate-0.13.8.dist-info/RECORD,,
56
+ isolate-0.13.9.dist-info/LICENSE,sha256=427vuyirL5scgBLqA9UWcdnxKrtSGc0u_JfUupk6lAA,11359
57
+ isolate-0.13.9.dist-info/METADATA,sha256=nSnYPN5KxPVH23bG5jW0ffaaQZXCoTG-pCIHeOrViB4,3191
58
+ isolate-0.13.9.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
59
+ isolate-0.13.9.dist-info/entry_points.txt,sha256=s3prh2EERaVCbL8R45tfY5WFPZ1TsYOsz305YR7s-Pc,360
60
+ isolate-0.13.9.dist-info/top_level.txt,sha256=W9QJBHcq5WXRkbOXf25bvftzFsOZZN4n1DAatdroZrs,8
61
+ isolate-0.13.9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.2.0)
2
+ Generator: setuptools (72.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5