isolate 0.14.0__py3-none-any.whl → 0.14.2__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.14.0'
16
- __version_tuple__ = version_tuple = (0, 14, 0)
15
+ __version__ = version = '0.14.2'
16
+ __version_tuple__ = version_tuple = (0, 14, 2)
@@ -117,6 +117,7 @@ def _io_observer(
117
117
  return # Nothing to read
118
118
 
119
119
  for line in raw_data.splitlines():
120
+ # TODO: parse the lines to include `extra={...}` added by the logger?
120
121
  hook(line)
121
122
 
122
123
  def _reader():
isolate/logger.py CHANGED
@@ -9,6 +9,8 @@ from isolate.logs import LogLevel, LogSource
9
9
  # but it handling `source` would be not trivial, so we are better off
10
10
  # just keeping it simple for now.
11
11
  class IsolateLogger:
12
+ extra_labels: Dict[str, str] = {}
13
+
12
14
  def __init__(self, log_labels: Dict[str, str]):
13
15
  self.log_labels = log_labels
14
16
 
@@ -18,6 +20,7 @@ class IsolateLogger:
18
20
  "level": level.name,
19
21
  "message": message,
20
22
  **self.log_labels,
23
+ **self.extra_labels,
21
24
  }
22
25
  print(json.dumps(record))
23
26
 
isolate/server/server.py CHANGED
@@ -36,6 +36,7 @@ from isolate.server.health_server import HealthServicer
36
36
  from isolate.server.interface import from_grpc, to_grpc
37
37
 
38
38
  EMPTY_MESSAGE_INTERVAL = float(os.getenv("ISOLATE_EMPTY_MESSAGE_INTERVAL", "600"))
39
+ SKIP_EMPTY_LOGS = os.getenv("ISOLATE_SKIP_EMPTY_LOGS") == "1"
39
40
  MAX_GRPC_WAIT_TIMEOUT = float(os.getenv("ISOLATE_MAX_GRPC_WAIT_TIMEOUT", "10.0"))
40
41
 
41
42
  # Whether to inherit all the packages from the current environment or not.
@@ -366,9 +367,7 @@ class IsolateServicer(definitions.IsolateServicer):
366
367
 
367
368
  task = self.background_tasks[request.task_id]
368
369
 
369
- task.logger = IsolateLogger.with_env_expanded(
370
- dict(request.metadata.logger_labels)
371
- )
370
+ task.logger.extra_labels = dict(request.metadata.logger_labels)
372
371
 
373
372
  return definitions.SetMetadataResponse()
374
373
 
@@ -378,13 +377,19 @@ class IsolateServicer(definitions.IsolateServicer):
378
377
  context: ServicerContext,
379
378
  ) -> Iterator[definitions.PartialRunResult]:
380
379
  try:
381
- yield from self._run_task(RunTask(request=request))
380
+ # HACK: we can support only one task at a time for Run
381
+ # TODO: move away from this when we use submit for env-aware tasks
382
+ task = RunTask(request=request)
383
+ self.background_tasks["RUN"] = task
384
+ yield from self._run_task(task)
382
385
  except GRPCException as exc:
383
386
  return self.abort_with_msg(
384
387
  exc.message,
385
388
  context,
386
389
  code=exc.code,
387
390
  )
391
+ finally:
392
+ self.background_tasks.pop("RUN", None)
388
393
 
389
394
  def List(
390
395
  self,
@@ -464,7 +469,8 @@ class IsolateServicer(definitions.IsolateServicer):
464
469
  return None
465
470
 
466
471
  def cancel_tasks(self):
467
- for task in self.background_tasks.values():
472
+ tasks_copy = self.background_tasks.copy()
473
+ for task in tasks_copy.values():
468
474
  task.cancel()
469
475
 
470
476
 
@@ -484,8 +490,9 @@ class LogHandler:
484
490
  task: RunTask
485
491
 
486
492
  def handle(self, log: Log) -> None:
487
- self.task.logger.log(log.level, log.message, source=log.source)
488
- self._add_log_to_queue(log)
493
+ if not SKIP_EMPTY_LOGS or log.message.strip():
494
+ self.task.logger.log(log.level, log.message, source=log.source)
495
+ self._add_log_to_queue(log)
489
496
 
490
497
  def _add_log_to_queue(self, log: Log) -> None:
491
498
  grpc_log = cast(definitions.Log, to_grpc(log))
@@ -534,6 +541,16 @@ class SingleTaskInterceptor(ServerBoundInterceptor):
534
541
  """Sets server to terminate after the first Submit/Run task."""
535
542
 
536
543
  _done: bool = False
544
+ _task_id: str | None = None
545
+
546
+ def __init__(self):
547
+ def terminate(request: Any, context: grpc.ServicerContext) -> Any:
548
+ context.abort(
549
+ grpc.StatusCode.RESOURCE_EXHAUSTED,
550
+ "Server has already served one Run/Submit task.",
551
+ )
552
+
553
+ self._terminator = grpc.unary_unary_rpc_method_handler(terminate)
537
554
 
538
555
  def intercept_service(self, continuation, handler_call_details):
539
556
  handler = continuation(handler_call_details)
@@ -542,29 +559,62 @@ class SingleTaskInterceptor(ServerBoundInterceptor):
542
559
  is_run = handler_call_details.method == "/Isolate/Run"
543
560
  is_new_task = is_submit or is_run
544
561
 
545
- if is_new_task and self._done:
546
- raise grpc.RpcError(
547
- grpc.StatusCode.UNAVAILABLE,
548
- "Server has already served one Run/Submit task.",
549
- )
550
- elif is_new_task:
551
- self._done = True
552
- else:
562
+ if not is_new_task:
553
563
  # Let other requests like List/Cancel/etc pass through
554
- return continuation(handler_call_details)
564
+ return handler
565
+
566
+ if self._done:
567
+ # Fail the request if the server has already served or is serving
568
+ # a Run/Submit task.
569
+ return self._terminator
570
+
571
+ self._done = True
555
572
 
556
573
  def wrapper(method_impl):
557
574
  @functools.wraps(method_impl)
558
- def _wrapper(request, context):
559
- def _stop():
560
- if is_submit:
561
- # Wait for the task to finish
562
- while self.server.servicer.background_tasks:
575
+ def _wrapper(request: Any, context: grpc.ServicerContext) -> Any:
576
+ def termination() -> None:
577
+ if is_run:
578
+ print("Stopping server since run is finished")
579
+ # Stop the server after the Run task is finished
580
+ self.server.stop(grace=0.1)
581
+
582
+ elif is_submit:
583
+ # Wait until the task_id is assigned
584
+ while self._task_id is None:
563
585
  time.sleep(0.1)
564
- self.server.stop(grace=0.1)
565
586
 
566
- context.add_callback(_stop)
567
- return method_impl(request, context)
587
+ # Get the task from the background tasks
588
+ task = self.servicer.background_tasks.get(self._task_id)
589
+
590
+ if task is not None:
591
+ # Wait until the task future is assigned
592
+ tries = 0
593
+ while task.future is None:
594
+ time.sleep(0.1)
595
+ tries += 1
596
+ if tries > 100:
597
+ raise RuntimeError(
598
+ "Task future was not assigned in time."
599
+ )
600
+
601
+ def _stop(*args):
602
+ # Small sleep to make sure the cancellation is processed
603
+ time.sleep(0.1)
604
+ print("Stopping server since the task is finished")
605
+ self.server.stop(grace=0.1)
606
+
607
+ # Add a callback which will stop the server
608
+ # after the task is finished
609
+ task.future.add_done_callback(_stop)
610
+
611
+ context.add_callback(termination)
612
+ res = method_impl(request, context)
613
+
614
+ if is_submit:
615
+ self._task_id = cast(definitions.SubmitResponse, res).task_id
616
+
617
+ return res
568
618
 
569
619
  return _wrapper
570
620
 
@@ -598,7 +648,7 @@ def main(argv: list[str] | None = None) -> None:
598
648
  server = grpc.server(
599
649
  futures.ThreadPoolExecutor(max_workers=options.num_workers),
600
650
  options=get_default_options(),
601
- interceptors=interceptors,
651
+ interceptors=interceptors, # type: ignore
602
652
  )
603
653
 
604
654
  for interceptor in interceptors:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: isolate
3
- Version: 0.14.0
3
+ Version: 0.14.2
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
@@ -1,13 +1,13 @@
1
1
  isolate/__init__.py,sha256=uXOKnONs7sXgARNgElwr4_A1sKoA6ACHVEvs3IDiX1M,127
2
- isolate/_isolate_version.py,sha256=9XbM6DRatFmjNkeSNxF4a6enpQeo8nPyAy9RTPPwPpY,413
2
+ isolate/_isolate_version.py,sha256=rlRT2sdhOGuXEsCM8RZA3pLtxwYV_87Qc0n3HT-mx2U,413
3
3
  isolate/_version.py,sha256=05pXvy-yr5t3I1m9JMn42Ilzpg7fa8IB2J8a3G7t1cU,274
4
- isolate/logger.py,sha256=d9mZyTOtplMZeQSNgRNiXhOPwJ0be2B1nGGaQx09C3g,1450
4
+ isolate/logger.py,sha256=gN_HzbvNkfloXw5iom6M7jL8e8VOgrMSi1Wp5I1eoX0,1522
5
5
  isolate/logs.py,sha256=R_AHUVYD18z_PhtK_mDWi9Gch79CxmwHY09hUDShtwg,2079
6
6
  isolate/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  isolate/registry.py,sha256=hpzv4HI7iihG5I7i5r8Pb257ibhEKY18xQcG-w1-BgI,1590
8
8
  isolate/backends/__init__.py,sha256=LLrSM7UnDFW8tIT5oYlE1wVJrxKcaj_v7cFwvTjQTlc,119
9
9
  isolate/backends/_base.py,sha256=Kt5pkhDzXZblq4vxaM3DQTo-Bj-7pIRZFlqJR7OfejY,4112
10
- isolate/backends/common.py,sha256=Zx0HXnBX_jlOLpFNJzY4ue8NcW-kktqo_WZOJmPSjvI,8481
10
+ isolate/backends/common.py,sha256=GzmgzIkCY90elMH_7lpj-LwRNu-GLia2j4PrS6zNHwA,8567
11
11
  isolate/backends/conda.py,sha256=S5q5bdY787AMTck2iMGtwu-LHMH4a1qCIjNHDKTkqok,7649
12
12
  isolate/backends/container.py,sha256=MCQJbcmQvRUS-tTgTW_pKYBMKwSJO2KZsLeaBMXpPC0,1645
13
13
  isolate/backends/local.py,sha256=woxe4dmXuEHxWKsGNndoRA1_sP6yG-dg6tlFZni0mZc,1360
@@ -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=THjWTLFxy9Y9atlHtenjtN7ltSGOLAVUAj9lDzl0qUw,21197
45
+ isolate/server/server.py,sha256=41Y_3dtUmeJR8RuQF2VMC5x_kM_k0w1hyzPFl5uW5Do,23477
46
46
  isolate/server/definitions/__init__.py,sha256=f_Q3pdjMuZrjgNlbM60btFKiB1Vg8cnVyKEbp0RmU0A,572
47
47
  isolate/server/definitions/server.proto,sha256=UihlFbYG8fbwm0IUfKDRH1vNkopzP3C-wplXXcAO1c8,1761
48
48
  isolate/server/definitions/server_pb2.py,sha256=TYBJC_z_dNf2J6FgHukY9mDyc3WBt9EPQOVFd_ayQNc,4304
@@ -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=AK-DPCpJzoYhU6DydD856c0Ywx84x6k-Cs4m6HpNv5A,2459
55
55
  isolate/server/health/health_pb2_grpc.py,sha256=XgsULrnRBmYIqvKr8eI7bqs6NIea5A0kkqdOOc2JHBY,5303
56
- isolate-0.14.0.dist-info/LICENSE,sha256=427vuyirL5scgBLqA9UWcdnxKrtSGc0u_JfUupk6lAA,11359
57
- isolate-0.14.0.dist-info/METADATA,sha256=STY6fLw2onLEsBbyyseZBKbWamPsAYrps5b9PvXC_MM,3191
58
- isolate-0.14.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
59
- isolate-0.14.0.dist-info/entry_points.txt,sha256=s3prh2EERaVCbL8R45tfY5WFPZ1TsYOsz305YR7s-Pc,360
60
- isolate-0.14.0.dist-info/top_level.txt,sha256=W9QJBHcq5WXRkbOXf25bvftzFsOZZN4n1DAatdroZrs,8
61
- isolate-0.14.0.dist-info/RECORD,,
56
+ isolate-0.14.2.dist-info/LICENSE,sha256=427vuyirL5scgBLqA9UWcdnxKrtSGc0u_JfUupk6lAA,11359
57
+ isolate-0.14.2.dist-info/METADATA,sha256=kFsnmpSczOcMgcM6idLXEuVDLWyOD8oG7MR2hV2x8KI,3191
58
+ isolate-0.14.2.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
59
+ isolate-0.14.2.dist-info/entry_points.txt,sha256=s3prh2EERaVCbL8R45tfY5WFPZ1TsYOsz305YR7s-Pc,360
60
+ isolate-0.14.2.dist-info/top_level.txt,sha256=W9QJBHcq5WXRkbOXf25bvftzFsOZZN4n1DAatdroZrs,8
61
+ isolate-0.14.2.dist-info/RECORD,,