isolate 0.13.10__py3-none-any.whl → 0.14.0__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.10'
16
- __version_tuple__ = version_tuple = (0, 13, 10)
15
+ __version__ = version = '0.14.0'
16
+ __version_tuple__ = version_tuple = (0, 14, 0)
@@ -2,16 +2,20 @@
2
2
  @generated by mypy-protobuf. Do not edit manually!
3
3
  isort:skip_file
4
4
  """
5
-
6
5
  import builtins
7
6
  from isolate.connections.grpc.definitions import common_pb2
8
7
  import google.protobuf.descriptor
9
8
  import google.protobuf.message
10
- import typing
9
+ import sys
10
+
11
+ if sys.version_info >= (3, 8):
12
+ import typing as typing_extensions
13
+ else:
14
+ import typing_extensions
11
15
 
12
16
  DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
13
17
 
14
- @typing.final
18
+ @typing_extensions.final
15
19
  class FunctionCall(google.protobuf.message.Message):
16
20
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
17
21
 
@@ -20,7 +24,6 @@ class FunctionCall(google.protobuf.message.Message):
20
24
  @property
21
25
  def function(self) -> common_pb2.SerializedObject:
22
26
  """The function to execute and return the results to."""
23
-
24
27
  @property
25
28
  def setup_func(self) -> common_pb2.SerializedObject:
26
29
  """Optionally the setup function which will be passed
@@ -28,15 +31,14 @@ class FunctionCall(google.protobuf.message.Message):
28
31
  has to be an idempotent step since the result for
29
32
  this executable will be cached.
30
33
  """
31
-
32
34
  def __init__(
33
35
  self,
34
36
  *,
35
37
  function: common_pb2.SerializedObject | None = ...,
36
38
  setup_func: common_pb2.SerializedObject | None = ...,
37
39
  ) -> None: ...
38
- def HasField(self, field_name: typing.Literal["_setup_func", b"_setup_func", "function", b"function", "setup_func", b"setup_func"]) -> builtins.bool: ...
39
- def ClearField(self, field_name: typing.Literal["_setup_func", b"_setup_func", "function", b"function", "setup_func", b"setup_func"]) -> None: ...
40
- def WhichOneof(self, oneof_group: typing.Literal["_setup_func", b"_setup_func"]) -> typing.Literal["setup_func"] | None: ...
40
+ def HasField(self, field_name: typing_extensions.Literal["_setup_func", b"_setup_func", "function", b"function", "setup_func", b"setup_func"]) -> builtins.bool: ...
41
+ def ClearField(self, field_name: typing_extensions.Literal["_setup_func", b"_setup_func", "function", b"function", "setup_func", b"setup_func"]) -> None: ...
42
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["_setup_func", b"_setup_func"]) -> typing_extensions.Literal["setup_func"] | None: ...
41
43
 
42
44
  global___FunctionCall = FunctionCall
@@ -2,7 +2,6 @@
2
2
  @generated by mypy-protobuf. Do not edit manually!
3
3
  isort:skip_file
4
4
  """
5
-
6
5
  import builtins
7
6
  import collections.abc
8
7
  import google.protobuf.descriptor
@@ -62,7 +61,7 @@ STDOUT: LogLevel.ValueType # 5
62
61
  STDERR: LogLevel.ValueType # 6
63
62
  global___LogLevel = LogLevel
64
63
 
65
- @typing.final
64
+ @typing_extensions.final
66
65
  class SerializedObject(google.protobuf.message.Message):
67
66
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
68
67
 
@@ -90,13 +89,13 @@ class SerializedObject(google.protobuf.message.Message):
90
89
  was_it_raised: builtins.bool = ...,
91
90
  stringized_traceback: builtins.str | None = ...,
92
91
  ) -> None: ...
93
- def HasField(self, field_name: typing.Literal["_stringized_traceback", b"_stringized_traceback", "stringized_traceback", b"stringized_traceback"]) -> builtins.bool: ...
94
- def ClearField(self, field_name: typing.Literal["_stringized_traceback", b"_stringized_traceback", "definition", b"definition", "method", b"method", "stringized_traceback", b"stringized_traceback", "was_it_raised", b"was_it_raised"]) -> None: ...
95
- def WhichOneof(self, oneof_group: typing.Literal["_stringized_traceback", b"_stringized_traceback"]) -> typing.Literal["stringized_traceback"] | None: ...
92
+ def HasField(self, field_name: typing_extensions.Literal["_stringized_traceback", b"_stringized_traceback", "stringized_traceback", b"stringized_traceback"]) -> builtins.bool: ...
93
+ def ClearField(self, field_name: typing_extensions.Literal["_stringized_traceback", b"_stringized_traceback", "definition", b"definition", "method", b"method", "stringized_traceback", b"stringized_traceback", "was_it_raised", b"was_it_raised"]) -> None: ...
94
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["_stringized_traceback", b"_stringized_traceback"]) -> typing_extensions.Literal["stringized_traceback"] | None: ...
96
95
 
97
96
  global___SerializedObject = SerializedObject
98
97
 
99
- @typing.final
98
+ @typing_extensions.final
100
99
  class PartialRunResult(google.protobuf.message.Message):
101
100
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
102
101
 
@@ -110,11 +109,9 @@ class PartialRunResult(google.protobuf.message.Message):
110
109
  """A list of logs collected during this partial execution. It does
111
110
  not include old logs.
112
111
  """
113
-
114
112
  @property
115
113
  def result(self) -> global___SerializedObject:
116
114
  """The result of the run, if it is complete."""
117
-
118
115
  def __init__(
119
116
  self,
120
117
  *,
@@ -122,13 +119,13 @@ class PartialRunResult(google.protobuf.message.Message):
122
119
  logs: collections.abc.Iterable[global___Log] | None = ...,
123
120
  result: global___SerializedObject | None = ...,
124
121
  ) -> None: ...
125
- def HasField(self, field_name: typing.Literal["_result", b"_result", "result", b"result"]) -> builtins.bool: ...
126
- def ClearField(self, field_name: typing.Literal["_result", b"_result", "is_complete", b"is_complete", "logs", b"logs", "result", b"result"]) -> None: ...
127
- def WhichOneof(self, oneof_group: typing.Literal["_result", b"_result"]) -> typing.Literal["result"] | None: ...
122
+ def HasField(self, field_name: typing_extensions.Literal["_result", b"_result", "result", b"result"]) -> builtins.bool: ...
123
+ def ClearField(self, field_name: typing_extensions.Literal["_result", b"_result", "is_complete", b"is_complete", "logs", b"logs", "result", b"result"]) -> None: ...
124
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["_result", b"_result"]) -> typing_extensions.Literal["result"] | None: ...
128
125
 
129
126
  global___PartialRunResult = PartialRunResult
130
127
 
131
- @typing.final
128
+ @typing_extensions.final
132
129
  class Log(google.protobuf.message.Message):
133
130
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
134
131
 
@@ -149,7 +146,7 @@ class Log(google.protobuf.message.Message):
149
146
  level: global___LogLevel.ValueType = ...,
150
147
  timestamp: google.protobuf.timestamp_pb2.Timestamp | None = ...,
151
148
  ) -> None: ...
152
- def HasField(self, field_name: typing.Literal["timestamp", b"timestamp"]) -> builtins.bool: ...
153
- def ClearField(self, field_name: typing.Literal["level", b"level", "message", b"message", "source", b"source", "timestamp", b"timestamp"]) -> None: ...
149
+ def HasField(self, field_name: typing_extensions.Literal["timestamp", b"timestamp"]) -> builtins.bool: ...
150
+ def ClearField(self, field_name: typing_extensions.Literal["level", b"level", "message", b"message", "source", b"source", "timestamp", b"timestamp"]) -> None: ...
154
151
 
155
152
  global___Log = Log
isolate/logger.py CHANGED
@@ -1,24 +1,18 @@
1
1
  import json
2
2
  import os
3
+ from typing import Dict
4
+
5
+ from isolate.logs import LogLevel, LogSource
3
6
 
4
7
 
5
8
  # NOTE: we probably should've created a proper `logging.getLogger` here,
6
9
  # but it handling `source` would be not trivial, so we are better off
7
10
  # just keeping it simple for now.
8
11
  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
-
21
- def log(self, level, message, source):
12
+ def __init__(self, log_labels: Dict[str, str]):
13
+ self.log_labels = log_labels
14
+
15
+ def log(self, level: LogLevel, message: str, source: LogSource) -> None:
22
16
  record = {
23
17
  "isolate_source": source.name,
24
18
  "level": level.name,
@@ -27,5 +21,29 @@ class IsolateLogger:
27
21
  }
28
22
  print(json.dumps(record))
29
23
 
24
+ @classmethod
25
+ def with_env_expanded(cls, labels: Dict[str, str]) -> "IsolateLogger":
26
+ for key, value in labels.items():
27
+ if value.startswith("$"):
28
+ expanded = os.getenv(value[1:])
29
+ else:
30
+ expanded = value
31
+ if expanded is not None:
32
+ labels[key] = expanded
33
+
34
+ return cls(labels)
35
+
36
+ @classmethod
37
+ def from_env(cls) -> "IsolateLogger":
38
+ _labels: Dict[str, str] = {}
39
+ raw = os.getenv("ISOLATE_LOG_LABELS")
40
+ if raw:
41
+ try:
42
+ _labels = json.loads(raw)
43
+ except json.JSONDecodeError:
44
+ print("Failed to parse ISOLATE_LOG_LABELS")
45
+
46
+ return cls.with_env_expanded(labels=_labels)
47
+
30
48
 
31
- logger = IsolateLogger()
49
+ ENV_LOGGER = IsolateLogger.from_env()
@@ -11,6 +11,9 @@ service Isolate {
11
11
  // Submit a function to be run without waiting for results.
12
12
  rpc Submit (SubmitRequest) returns (SubmitResponse) {}
13
13
 
14
+ // Set the metadata for a task.
15
+ rpc SetMetadata (SetMetadataRequest) returns (SetMetadataResponse) {}
16
+
14
17
  // List running tasks
15
18
  rpc List (ListRequest) returns (ListResponse) {}
16
19
 
@@ -36,12 +39,27 @@ message EnvironmentDefinition {
36
39
  message SubmitRequest {
37
40
  // The function to run.
38
41
  BoundFunction function = 1;
42
+ // Task metadata.
43
+ TaskMetadata metadata = 2;
44
+ }
45
+
46
+ message TaskMetadata {
47
+ // Labels to attach to the logs.
48
+ map<string, string> logger_labels = 1;
39
49
  }
40
50
 
41
51
  message SubmitResponse {
42
52
  string task_id = 1;
43
53
  }
44
54
 
55
+ message SetMetadataRequest{
56
+ string task_id = 1;
57
+ TaskMetadata metadata = 2;
58
+ }
59
+
60
+ message SetMetadataResponse {
61
+ }
62
+
45
63
  message ListRequest {
46
64
  }
47
65
 
@@ -16,31 +16,41 @@ from isolate.connections.grpc.definitions import common_pb2 as common__pb2
16
16
  from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2
17
17
 
18
18
 
19
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cserver.proto\x1a\x0c\x63ommon.proto\x1a\x1cgoogle/protobuf/struct.proto\"\x9d\x01\n\rBoundFunction\x12,\n\x0c\x65nvironments\x18\x01 \x03(\x0b\x32\x16.EnvironmentDefinition\x12#\n\x08\x66unction\x18\x02 \x01(\x0b\x32\x11.SerializedObject\x12*\n\nsetup_func\x18\x03 \x01(\x0b\x32\x11.SerializedObjectH\x00\x88\x01\x01\x42\r\n\x0b_setup_func\"d\n\x15\x45nvironmentDefinition\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12.\n\rconfiguration\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\r\n\x05\x66orce\x18\x03 \x01(\x08\"1\n\rSubmitRequest\x12 \n\x08\x66unction\x18\x01 \x01(\x0b\x32\x0e.BoundFunction\"!\n\x0eSubmitResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"\r\n\x0bListRequest\"\x1b\n\x08TaskInfo\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"(\n\x0cListResponse\x12\x18\n\x05tasks\x18\x01 \x03(\x0b\x32\t.TaskInfo\" \n\rCancelRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"\x10\n\x0e\x43\x61ncelResponse2\xb8\x01\n\x07Isolate\x12,\n\x03Run\x12\x0e.BoundFunction\x1a\x11.PartialRunResult\"\x00\x30\x01\x12+\n\x06Submit\x12\x0e.SubmitRequest\x1a\x0f.SubmitResponse\"\x00\x12%\n\x04List\x12\x0c.ListRequest\x1a\r.ListResponse\"\x00\x12+\n\x06\x43\x61ncel\x12\x0e.CancelRequest\x1a\x0f.CancelResponse\"\x00\x62\x06proto3')
19
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cserver.proto\x1a\x0c\x63ommon.proto\x1a\x1cgoogle/protobuf/struct.proto\"\x9d\x01\n\rBoundFunction\x12,\n\x0c\x65nvironments\x18\x01 \x03(\x0b\x32\x16.EnvironmentDefinition\x12#\n\x08\x66unction\x18\x02 \x01(\x0b\x32\x11.SerializedObject\x12*\n\nsetup_func\x18\x03 \x01(\x0b\x32\x11.SerializedObjectH\x00\x88\x01\x01\x42\r\n\x0b_setup_func\"d\n\x15\x45nvironmentDefinition\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12.\n\rconfiguration\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\r\n\x05\x66orce\x18\x03 \x01(\x08\"R\n\rSubmitRequest\x12 \n\x08\x66unction\x18\x01 \x01(\x0b\x32\x0e.BoundFunction\x12\x1f\n\x08metadata\x18\x02 \x01(\x0b\x32\r.TaskMetadata\"{\n\x0cTaskMetadata\x12\x36\n\rlogger_labels\x18\x01 \x03(\x0b\x32\x1f.TaskMetadata.LoggerLabelsEntry\x1a\x33\n\x11LoggerLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"!\n\x0eSubmitResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"F\n\x12SetMetadataRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x1f\n\x08metadata\x18\x02 \x01(\x0b\x32\r.TaskMetadata\"\x15\n\x13SetMetadataResponse\"\r\n\x0bListRequest\"\x1b\n\x08TaskInfo\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"(\n\x0cListResponse\x12\x18\n\x05tasks\x18\x01 \x03(\x0b\x32\t.TaskInfo\" \n\rCancelRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"\x10\n\x0e\x43\x61ncelResponse2\xf4\x01\n\x07Isolate\x12,\n\x03Run\x12\x0e.BoundFunction\x1a\x11.PartialRunResult\"\x00\x30\x01\x12+\n\x06Submit\x12\x0e.SubmitRequest\x1a\x0f.SubmitResponse\"\x00\x12:\n\x0bSetMetadata\x12\x13.SetMetadataRequest\x1a\x14.SetMetadataResponse\"\x00\x12%\n\x04List\x12\x0c.ListRequest\x1a\r.ListResponse\"\x00\x12+\n\x06\x43\x61ncel\x12\x0e.CancelRequest\x1a\x0f.CancelResponse\"\x00\x62\x06proto3')
20
20
 
21
21
  _globals = globals()
22
22
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
23
23
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'server_pb2', _globals)
24
24
  if not _descriptor._USE_C_DESCRIPTORS:
25
25
  DESCRIPTOR._loaded_options = None
26
+ _globals['_TASKMETADATA_LOGGERLABELSENTRY']._loaded_options = None
27
+ _globals['_TASKMETADATA_LOGGERLABELSENTRY']._serialized_options = b'8\001'
26
28
  _globals['_BOUNDFUNCTION']._serialized_start=61
27
29
  _globals['_BOUNDFUNCTION']._serialized_end=218
28
30
  _globals['_ENVIRONMENTDEFINITION']._serialized_start=220
29
31
  _globals['_ENVIRONMENTDEFINITION']._serialized_end=320
30
32
  _globals['_SUBMITREQUEST']._serialized_start=322
31
- _globals['_SUBMITREQUEST']._serialized_end=371
32
- _globals['_SUBMITRESPONSE']._serialized_start=373
33
- _globals['_SUBMITRESPONSE']._serialized_end=406
34
- _globals['_LISTREQUEST']._serialized_start=408
35
- _globals['_LISTREQUEST']._serialized_end=421
36
- _globals['_TASKINFO']._serialized_start=423
37
- _globals['_TASKINFO']._serialized_end=450
38
- _globals['_LISTRESPONSE']._serialized_start=452
39
- _globals['_LISTRESPONSE']._serialized_end=492
40
- _globals['_CANCELREQUEST']._serialized_start=494
41
- _globals['_CANCELREQUEST']._serialized_end=526
42
- _globals['_CANCELRESPONSE']._serialized_start=528
43
- _globals['_CANCELRESPONSE']._serialized_end=544
44
- _globals['_ISOLATE']._serialized_start=547
45
- _globals['_ISOLATE']._serialized_end=731
33
+ _globals['_SUBMITREQUEST']._serialized_end=404
34
+ _globals['_TASKMETADATA']._serialized_start=406
35
+ _globals['_TASKMETADATA']._serialized_end=529
36
+ _globals['_TASKMETADATA_LOGGERLABELSENTRY']._serialized_start=478
37
+ _globals['_TASKMETADATA_LOGGERLABELSENTRY']._serialized_end=529
38
+ _globals['_SUBMITRESPONSE']._serialized_start=531
39
+ _globals['_SUBMITRESPONSE']._serialized_end=564
40
+ _globals['_SETMETADATAREQUEST']._serialized_start=566
41
+ _globals['_SETMETADATAREQUEST']._serialized_end=636
42
+ _globals['_SETMETADATARESPONSE']._serialized_start=638
43
+ _globals['_SETMETADATARESPONSE']._serialized_end=659
44
+ _globals['_LISTREQUEST']._serialized_start=661
45
+ _globals['_LISTREQUEST']._serialized_end=674
46
+ _globals['_TASKINFO']._serialized_start=676
47
+ _globals['_TASKINFO']._serialized_end=703
48
+ _globals['_LISTRESPONSE']._serialized_start=705
49
+ _globals['_LISTRESPONSE']._serialized_end=745
50
+ _globals['_CANCELREQUEST']._serialized_start=747
51
+ _globals['_CANCELREQUEST']._serialized_end=779
52
+ _globals['_CANCELRESPONSE']._serialized_start=781
53
+ _globals['_CANCELRESPONSE']._serialized_end=797
54
+ _globals['_ISOLATE']._serialized_start=800
55
+ _globals['_ISOLATE']._serialized_end=1044
46
56
  # @@protoc_insertion_point(module_scope)
@@ -2,7 +2,6 @@
2
2
  @generated by mypy-protobuf. Do not edit manually!
3
3
  isort:skip_file
4
4
  """
5
-
6
5
  import builtins
7
6
  import collections.abc
8
7
  from isolate.connections.grpc.definitions import common_pb2
@@ -10,11 +9,16 @@ import google.protobuf.descriptor
10
9
  import google.protobuf.internal.containers
11
10
  import google.protobuf.message
12
11
  import google.protobuf.struct_pb2
13
- import typing
12
+ import sys
13
+
14
+ if sys.version_info >= (3, 8):
15
+ import typing as typing_extensions
16
+ else:
17
+ import typing_extensions
14
18
 
15
19
  DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
16
20
 
17
- @typing.final
21
+ @typing_extensions.final
18
22
  class BoundFunction(google.protobuf.message.Message):
19
23
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
20
24
 
@@ -34,13 +38,13 @@ class BoundFunction(google.protobuf.message.Message):
34
38
  function: common_pb2.SerializedObject | None = ...,
35
39
  setup_func: common_pb2.SerializedObject | None = ...,
36
40
  ) -> None: ...
37
- def HasField(self, field_name: typing.Literal["_setup_func", b"_setup_func", "function", b"function", "setup_func", b"setup_func"]) -> builtins.bool: ...
38
- def ClearField(self, field_name: typing.Literal["_setup_func", b"_setup_func", "environments", b"environments", "function", b"function", "setup_func", b"setup_func"]) -> None: ...
39
- def WhichOneof(self, oneof_group: typing.Literal["_setup_func", b"_setup_func"]) -> typing.Literal["setup_func"] | None: ...
41
+ def HasField(self, field_name: typing_extensions.Literal["_setup_func", b"_setup_func", "function", b"function", "setup_func", b"setup_func"]) -> builtins.bool: ...
42
+ def ClearField(self, field_name: typing_extensions.Literal["_setup_func", b"_setup_func", "environments", b"environments", "function", b"function", "setup_func", b"setup_func"]) -> None: ...
43
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["_setup_func", b"_setup_func"]) -> typing_extensions.Literal["setup_func"] | None: ...
40
44
 
41
45
  global___BoundFunction = BoundFunction
42
46
 
43
- @typing.final
47
+ @typing_extensions.final
44
48
  class EnvironmentDefinition(google.protobuf.message.Message):
45
49
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
46
50
 
@@ -49,12 +53,11 @@ class EnvironmentDefinition(google.protobuf.message.Message):
49
53
  FORCE_FIELD_NUMBER: builtins.int
50
54
  kind: builtins.str
51
55
  """Kind of the isolate environment."""
52
- force: builtins.bool
53
- """Whether to force-create this environment or not."""
54
56
  @property
55
57
  def configuration(self) -> google.protobuf.struct_pb2.Struct:
56
58
  """A free-form definition of environment properties."""
57
-
59
+ force: builtins.bool
60
+ """Whether to force-create this environment or not."""
58
61
  def __init__(
59
62
  self,
60
63
  *,
@@ -62,31 +65,68 @@ class EnvironmentDefinition(google.protobuf.message.Message):
62
65
  configuration: google.protobuf.struct_pb2.Struct | None = ...,
63
66
  force: builtins.bool = ...,
64
67
  ) -> None: ...
65
- def HasField(self, field_name: typing.Literal["configuration", b"configuration"]) -> builtins.bool: ...
66
- def ClearField(self, field_name: typing.Literal["configuration", b"configuration", "force", b"force", "kind", b"kind"]) -> None: ...
68
+ def HasField(self, field_name: typing_extensions.Literal["configuration", b"configuration"]) -> builtins.bool: ...
69
+ def ClearField(self, field_name: typing_extensions.Literal["configuration", b"configuration", "force", b"force", "kind", b"kind"]) -> None: ...
67
70
 
68
71
  global___EnvironmentDefinition = EnvironmentDefinition
69
72
 
70
- @typing.final
73
+ @typing_extensions.final
71
74
  class SubmitRequest(google.protobuf.message.Message):
72
75
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
73
76
 
74
77
  FUNCTION_FIELD_NUMBER: builtins.int
78
+ METADATA_FIELD_NUMBER: builtins.int
75
79
  @property
76
80
  def function(self) -> global___BoundFunction:
77
81
  """The function to run."""
78
-
82
+ @property
83
+ def metadata(self) -> global___TaskMetadata:
84
+ """Task metadata."""
79
85
  def __init__(
80
86
  self,
81
87
  *,
82
88
  function: global___BoundFunction | None = ...,
89
+ metadata: global___TaskMetadata | None = ...,
83
90
  ) -> None: ...
84
- def HasField(self, field_name: typing.Literal["function", b"function"]) -> builtins.bool: ...
85
- def ClearField(self, field_name: typing.Literal["function", b"function"]) -> None: ...
91
+ def HasField(self, field_name: typing_extensions.Literal["function", b"function", "metadata", b"metadata"]) -> builtins.bool: ...
92
+ def ClearField(self, field_name: typing_extensions.Literal["function", b"function", "metadata", b"metadata"]) -> None: ...
86
93
 
87
94
  global___SubmitRequest = SubmitRequest
88
95
 
89
- @typing.final
96
+ @typing_extensions.final
97
+ class TaskMetadata(google.protobuf.message.Message):
98
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
99
+
100
+ @typing_extensions.final
101
+ class LoggerLabelsEntry(google.protobuf.message.Message):
102
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
103
+
104
+ KEY_FIELD_NUMBER: builtins.int
105
+ VALUE_FIELD_NUMBER: builtins.int
106
+ key: builtins.str
107
+ value: builtins.str
108
+ def __init__(
109
+ self,
110
+ *,
111
+ key: builtins.str = ...,
112
+ value: builtins.str = ...,
113
+ ) -> None: ...
114
+ def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ...
115
+
116
+ LOGGER_LABELS_FIELD_NUMBER: builtins.int
117
+ @property
118
+ def logger_labels(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]:
119
+ """Labels to attach to the logs."""
120
+ def __init__(
121
+ self,
122
+ *,
123
+ logger_labels: collections.abc.Mapping[builtins.str, builtins.str] | None = ...,
124
+ ) -> None: ...
125
+ def ClearField(self, field_name: typing_extensions.Literal["logger_labels", b"logger_labels"]) -> None: ...
126
+
127
+ global___TaskMetadata = TaskMetadata
128
+
129
+ @typing_extensions.final
90
130
  class SubmitResponse(google.protobuf.message.Message):
91
131
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
92
132
 
@@ -97,11 +137,41 @@ class SubmitResponse(google.protobuf.message.Message):
97
137
  *,
98
138
  task_id: builtins.str = ...,
99
139
  ) -> None: ...
100
- def ClearField(self, field_name: typing.Literal["task_id", b"task_id"]) -> None: ...
140
+ def ClearField(self, field_name: typing_extensions.Literal["task_id", b"task_id"]) -> None: ...
101
141
 
102
142
  global___SubmitResponse = SubmitResponse
103
143
 
104
- @typing.final
144
+ @typing_extensions.final
145
+ class SetMetadataRequest(google.protobuf.message.Message):
146
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
147
+
148
+ TASK_ID_FIELD_NUMBER: builtins.int
149
+ METADATA_FIELD_NUMBER: builtins.int
150
+ task_id: builtins.str
151
+ @property
152
+ def metadata(self) -> global___TaskMetadata: ...
153
+ def __init__(
154
+ self,
155
+ *,
156
+ task_id: builtins.str = ...,
157
+ metadata: global___TaskMetadata | None = ...,
158
+ ) -> None: ...
159
+ def HasField(self, field_name: typing_extensions.Literal["metadata", b"metadata"]) -> builtins.bool: ...
160
+ def ClearField(self, field_name: typing_extensions.Literal["metadata", b"metadata", "task_id", b"task_id"]) -> None: ...
161
+
162
+ global___SetMetadataRequest = SetMetadataRequest
163
+
164
+ @typing_extensions.final
165
+ class SetMetadataResponse(google.protobuf.message.Message):
166
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
167
+
168
+ def __init__(
169
+ self,
170
+ ) -> None: ...
171
+
172
+ global___SetMetadataResponse = SetMetadataResponse
173
+
174
+ @typing_extensions.final
105
175
  class ListRequest(google.protobuf.message.Message):
106
176
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
107
177
 
@@ -111,7 +181,7 @@ class ListRequest(google.protobuf.message.Message):
111
181
 
112
182
  global___ListRequest = ListRequest
113
183
 
114
- @typing.final
184
+ @typing_extensions.final
115
185
  class TaskInfo(google.protobuf.message.Message):
116
186
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
117
187
 
@@ -122,11 +192,11 @@ class TaskInfo(google.protobuf.message.Message):
122
192
  *,
123
193
  task_id: builtins.str = ...,
124
194
  ) -> None: ...
125
- def ClearField(self, field_name: typing.Literal["task_id", b"task_id"]) -> None: ...
195
+ def ClearField(self, field_name: typing_extensions.Literal["task_id", b"task_id"]) -> None: ...
126
196
 
127
197
  global___TaskInfo = TaskInfo
128
198
 
129
- @typing.final
199
+ @typing_extensions.final
130
200
  class ListResponse(google.protobuf.message.Message):
131
201
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
132
202
 
@@ -138,11 +208,11 @@ class ListResponse(google.protobuf.message.Message):
138
208
  *,
139
209
  tasks: collections.abc.Iterable[global___TaskInfo] | None = ...,
140
210
  ) -> None: ...
141
- def ClearField(self, field_name: typing.Literal["tasks", b"tasks"]) -> None: ...
211
+ def ClearField(self, field_name: typing_extensions.Literal["tasks", b"tasks"]) -> None: ...
142
212
 
143
213
  global___ListResponse = ListResponse
144
214
 
145
- @typing.final
215
+ @typing_extensions.final
146
216
  class CancelRequest(google.protobuf.message.Message):
147
217
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
148
218
 
@@ -153,11 +223,11 @@ class CancelRequest(google.protobuf.message.Message):
153
223
  *,
154
224
  task_id: builtins.str = ...,
155
225
  ) -> None: ...
156
- def ClearField(self, field_name: typing.Literal["task_id", b"task_id"]) -> None: ...
226
+ def ClearField(self, field_name: typing_extensions.Literal["task_id", b"task_id"]) -> None: ...
157
227
 
158
228
  global___CancelRequest = CancelRequest
159
229
 
160
- @typing.final
230
+ @typing_extensions.final
161
231
  class CancelResponse(google.protobuf.message.Message):
162
232
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
163
233
 
@@ -50,6 +50,11 @@ class IsolateStub(object):
50
50
  request_serializer=server__pb2.SubmitRequest.SerializeToString,
51
51
  response_deserializer=server__pb2.SubmitResponse.FromString,
52
52
  _registered_method=True)
53
+ self.SetMetadata = channel.unary_unary(
54
+ '/Isolate/SetMetadata',
55
+ request_serializer=server__pb2.SetMetadataRequest.SerializeToString,
56
+ response_deserializer=server__pb2.SetMetadataResponse.FromString,
57
+ _registered_method=True)
53
58
  self.List = channel.unary_unary(
54
59
  '/Isolate/List',
55
60
  request_serializer=server__pb2.ListRequest.SerializeToString,
@@ -80,6 +85,13 @@ class IsolateServicer(object):
80
85
  context.set_details('Method not implemented!')
81
86
  raise NotImplementedError('Method not implemented!')
82
87
 
88
+ def SetMetadata(self, request, context):
89
+ """Set the metadata for a task.
90
+ """
91
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
92
+ context.set_details('Method not implemented!')
93
+ raise NotImplementedError('Method not implemented!')
94
+
83
95
  def List(self, request, context):
84
96
  """List running tasks
85
97
  """
@@ -107,6 +119,11 @@ def add_IsolateServicer_to_server(servicer, server):
107
119
  request_deserializer=server__pb2.SubmitRequest.FromString,
108
120
  response_serializer=server__pb2.SubmitResponse.SerializeToString,
109
121
  ),
122
+ 'SetMetadata': grpc.unary_unary_rpc_method_handler(
123
+ servicer.SetMetadata,
124
+ request_deserializer=server__pb2.SetMetadataRequest.FromString,
125
+ response_serializer=server__pb2.SetMetadataResponse.SerializeToString,
126
+ ),
110
127
  'List': grpc.unary_unary_rpc_method_handler(
111
128
  servicer.List,
112
129
  request_deserializer=server__pb2.ListRequest.FromString,
@@ -182,6 +199,33 @@ class Isolate(object):
182
199
  metadata,
183
200
  _registered_method=True)
184
201
 
202
+ @staticmethod
203
+ def SetMetadata(request,
204
+ target,
205
+ options=(),
206
+ channel_credentials=None,
207
+ call_credentials=None,
208
+ insecure=False,
209
+ compression=None,
210
+ wait_for_ready=None,
211
+ timeout=None,
212
+ metadata=None):
213
+ return grpc.experimental.unary_unary(
214
+ request,
215
+ target,
216
+ '/Isolate/SetMetadata',
217
+ server__pb2.SetMetadataRequest.SerializeToString,
218
+ server__pb2.SetMetadataResponse.FromString,
219
+ options,
220
+ channel_credentials,
221
+ insecure,
222
+ call_credentials,
223
+ compression,
224
+ wait_for_ready,
225
+ timeout,
226
+ metadata,
227
+ _registered_method=True)
228
+
185
229
  @staticmethod
186
230
  def List(request,
187
231
  target,
@@ -2,7 +2,6 @@
2
2
  @generated by mypy-protobuf. Do not edit manually!
3
3
  isort:skip_file
4
4
  """
5
-
6
5
  import builtins
7
6
  import google.protobuf.descriptor
8
7
  import google.protobuf.internal.enum_type_wrapper
@@ -17,7 +16,7 @@ else:
17
16
 
18
17
  DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
19
18
 
20
- @typing.final
19
+ @typing_extensions.final
21
20
  class HealthCheckRequest(google.protobuf.message.Message):
22
21
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
23
22
 
@@ -28,11 +27,11 @@ class HealthCheckRequest(google.protobuf.message.Message):
28
27
  *,
29
28
  service: builtins.str = ...,
30
29
  ) -> None: ...
31
- def ClearField(self, field_name: typing.Literal["service", b"service"]) -> None: ...
30
+ def ClearField(self, field_name: typing_extensions.Literal["service", b"service"]) -> None: ...
32
31
 
33
32
  global___HealthCheckRequest = HealthCheckRequest
34
33
 
35
- @typing.final
34
+ @typing_extensions.final
36
35
  class HealthCheckResponse(google.protobuf.message.Message):
37
36
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
38
37
 
@@ -62,6 +61,6 @@ class HealthCheckResponse(google.protobuf.message.Message):
62
61
  *,
63
62
  status: global___HealthCheckResponse.ServingStatus.ValueType = ...,
64
63
  ) -> None: ...
65
- def ClearField(self, field_name: typing.Literal["status", b"status"]) -> None: ...
64
+ def ClearField(self, field_name: typing_extensions.Literal["status", b"status"]) -> None: ...
66
65
 
67
66
  global___HealthCheckResponse = HealthCheckResponse
isolate/server/server.py CHANGED
@@ -29,7 +29,7 @@ from isolate.backends.local import LocalPythonEnvironment
29
29
  from isolate.backends.virtualenv import VirtualPythonEnvironment
30
30
  from isolate.connections.grpc import AgentError, LocalPythonGRPC
31
31
  from isolate.connections.grpc.configuration import get_default_options
32
- from isolate.logger import logger
32
+ from isolate.logger import ENV_LOGGER, IsolateLogger
33
33
  from isolate.logs import Log, LogLevel, LogSource
34
34
  from isolate.server import definitions, health
35
35
  from isolate.server.health_server import HealthServicer
@@ -174,6 +174,7 @@ class RunTask:
174
174
  request: definitions.BoundFunction
175
175
  future: futures.Future | None = None
176
176
  agent: RunnerAgent | None = None
177
+ logger: IsolateLogger = ENV_LOGGER
177
178
 
178
179
  def cancel(self):
179
180
  while True:
@@ -210,7 +211,8 @@ class IsolateServicer(definitions.IsolateServicer):
210
211
  StatusCode.INVALID_ARGUMENT,
211
212
  )
212
213
 
213
- log_handler = LogHandler(messages)
214
+ log_handler = LogHandler(messages, task=task)
215
+
214
216
  run_settings = replace(
215
217
  self.default_settings,
216
218
  log_hook=log_handler.handle,
@@ -321,11 +323,17 @@ class IsolateServicer(definitions.IsolateServicer):
321
323
  request: definitions.SubmitRequest,
322
324
  context: ServicerContext,
323
325
  ) -> definitions.SubmitResponse:
324
- task = RunTask(request=request.function)
325
- task.future = RUNNER_THREAD_POOL.submit(
326
- self._run_task_in_background,
327
- task,
328
- )
326
+ logger = ENV_LOGGER
327
+ if request.metadata.logger_labels:
328
+ logger_labels_dict = dict(request.metadata.logger_labels)
329
+ try:
330
+ logger = IsolateLogger.with_env_expanded(logger_labels_dict)
331
+ except BaseException:
332
+ # Ignore the error if the logger couldn't be created.
333
+ pass
334
+
335
+ task = RunTask(request=request.function, logger=logger)
336
+ task.future = RUNNER_THREAD_POOL.submit(self._run_task_in_background, task)
329
337
  task_id = str(uuid.uuid4())
330
338
 
331
339
  print(f"Submitted a task {task_id}")
@@ -345,6 +353,25 @@ class IsolateServicer(definitions.IsolateServicer):
345
353
 
346
354
  return definitions.SubmitResponse(task_id=task_id)
347
355
 
356
+ def SetMetadata(
357
+ self,
358
+ request: definitions.SetMetadataRequest,
359
+ context: ServicerContext,
360
+ ) -> definitions.SetMetadataResponse:
361
+ if request.task_id not in self.background_tasks:
362
+ raise GRPCException(
363
+ f"Task {request.task_id} not found.",
364
+ StatusCode.NOT_FOUND,
365
+ )
366
+
367
+ task = self.background_tasks[request.task_id]
368
+
369
+ task.logger = IsolateLogger.with_env_expanded(
370
+ dict(request.metadata.logger_labels)
371
+ )
372
+
373
+ return definitions.SetMetadataResponse()
374
+
348
375
  def Run(
349
376
  self,
350
377
  request: definitions.BoundFunction,
@@ -453,9 +480,11 @@ def _proxy_to_queue(
453
480
  @dataclass
454
481
  class LogHandler:
455
482
  messages: Queue
483
+ # Reference to the task so we can change the logger
484
+ task: RunTask
456
485
 
457
486
  def handle(self, log: Log) -> None:
458
- logger.log(log.level, log.message, source=log.source)
487
+ self.task.logger.log(log.level, log.message, source=log.source)
459
488
  self._add_log_to_queue(log)
460
489
 
461
490
  def _add_log_to_queue(self, log: Log) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: isolate
3
- Version: 0.13.10
3
+ Version: 0.14.0
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,7 +1,7 @@
1
1
  isolate/__init__.py,sha256=uXOKnONs7sXgARNgElwr4_A1sKoA6ACHVEvs3IDiX1M,127
2
- isolate/_isolate_version.py,sha256=B_BL6eQ9yadF9gaiiJ3EqZhMaaHCCNUTzs-755F92es,415
2
+ isolate/_isolate_version.py,sha256=9XbM6DRatFmjNkeSNxF4a6enpQeo8nPyAy9RTPPwPpY,413
3
3
  isolate/_version.py,sha256=05pXvy-yr5t3I1m9JMn42Ilzpg7fa8IB2J8a3G7t1cU,274
4
- isolate/logger.py,sha256=Pr-P1UVDIHJkel3ZA3pkAOwXgnGiGjTw-KsxTVc5Y6E,894
4
+ isolate/logger.py,sha256=d9mZyTOtplMZeQSNgRNiXhOPwJ0be2B1nGGaQx09C3g,1450
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
@@ -30,11 +30,11 @@ isolate/connections/grpc/interface.py,sha256=yt63kytgXRXrTnjePGJVdXz4LJJVSSrNkJC
30
30
  isolate/connections/grpc/definitions/__init__.py,sha256=Z0453Bbjoq-Oxm2Wfi9fae-BFf8YsZwmuh88strmvxo,459
31
31
  isolate/connections/grpc/definitions/agent.proto,sha256=Hx11hHc8PKwhWzyasViLeq7JL33KsRex2-iibfWruTw,568
32
32
  isolate/connections/grpc/definitions/agent_pb2.py,sha256=F8KVIE1CK542c7_EJKJ40LY8mPZwEqvdmgBPntAMqE0,1424
33
- isolate/connections/grpc/definitions/agent_pb2.pyi,sha256=kZsyy2PJX21J6n-hPN_bZPMl8UKNXGTNErdqKkmenes,1589
33
+ isolate/connections/grpc/definitions/agent_pb2.pyi,sha256=IKJHEq01DZRIy4xrtpHalJmM56IQCZRxFEOZEs7hCUw,1744
34
34
  isolate/connections/grpc/definitions/agent_pb2_grpc.py,sha256=7tm-RB4CTu_YmqvI7QGRll1Z1NmkMWY2Vm-4AHr57X8,3632
35
35
  isolate/connections/grpc/definitions/common.proto,sha256=4W1upvDIPezNj-Ab6FVNa-7cA9_N-2xJMJpwytRhpCw,1260
36
36
  isolate/connections/grpc/definitions/common_pb2.py,sha256=lkpvpARZbPDrzrVkKJ-4WZWISN2kzrzJPNBUczmvkHU,2464
37
- isolate/connections/grpc/definitions/common_pb2.pyi,sha256=R-den8f1m7Lv2EnMXb3do3UjpO0Ncp3zSLtnKdW895c,6082
37
+ isolate/connections/grpc/definitions/common_pb2.pyi,sha256=J3av86ZHoHR28_5zshqCJ0I7v9WCxuQsvOAin-zig9w,6222
38
38
  isolate/connections/grpc/definitions/common_pb2_grpc.py,sha256=EvGJ0LYaWTflBesxg0P1nh_EeWKYKqUVRf0_plMISTs,1123
39
39
  isolate/connections/ipc/__init__.py,sha256=j2Mbsph2mRhAWmkMyrtPOz0VG-e75h1OOZLwzs6pXUo,131
40
40
  isolate/connections/ipc/_base.py,sha256=Jk715XK2ei3yBpFcwUnFZ0owQMMf5jekZFNh2WlKRT4,8009
@@ -42,20 +42,20 @@ 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=9abptODXQIdswNBQqEYY3yRGz6Jr4NFVs15FiftfRdw,20125
45
+ isolate/server/server.py,sha256=THjWTLFxy9Y9atlHtenjtN7ltSGOLAVUAj9lDzl0qUw,21197
46
46
  isolate/server/definitions/__init__.py,sha256=f_Q3pdjMuZrjgNlbM60btFKiB1Vg8cnVyKEbp0RmU0A,572
47
- isolate/server/definitions/server.proto,sha256=GIou0c_RP4sVDxjdYQzUtSptOfJtvbgsU4MGhL9hsMQ,1372
48
- isolate/server/definitions/server_pb2.py,sha256=SdbigRwRd1pU1yFwvyrA4vkmwQHPelPkQT7dtggKUlk,3194
49
- isolate/server/definitions/server_pb2.pyi,sha256=kEo781qnfiYpR9ok7CDdn4kaqdkEgB_66wT4qLMk3M4,5594
50
- isolate/server/definitions/server_pb2_grpc.py,sha256=lSGH6BeQf9cbdESlteNfMlDkFXIFjeVR7PVo-b0k3HM,8455
47
+ isolate/server/definitions/server.proto,sha256=UihlFbYG8fbwm0IUfKDRH1vNkopzP3C-wplXXcAO1c8,1761
48
+ isolate/server/definitions/server_pb2.py,sha256=TYBJC_z_dNf2J6FgHukY9mDyc3WBt9EPQOVFd_ayQNc,4304
49
+ isolate/server/definitions/server_pb2.pyi,sha256=bGDzwxvYJEq_N7R4mvE86t_GhBfUx8eXRYbrvOzFhqc,8352
50
+ isolate/server/definitions/server_pb2_grpc.py,sha256=TUKXKlT-6oOpZ2zg8EhJ-XFvjKGX90ze4iY0f5R6ZHU,10107
51
51
  isolate/server/health/__init__.py,sha256=sy349GRK2YGX9KFKqDxM-jdYtBpMXjZu1QwBkjn0SsM,337
52
52
  isolate/server/health/health.proto,sha256=wE2_QD0OQAblKkEBG7sALLXEOj1mOLKG-FbC4tFopWE,455
53
53
  isolate/server/health/health_pb2.py,sha256=onOdP3M4Tpqhqs2PlGcyfoKe2VVKUEDx5ALeRcObb9A,1899
54
- isolate/server/health/health_pb2.pyi,sha256=CPyvxvDzra-1d-mBsukaJnscMUDBaqSACvo9LiXlFzo,2416
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.13.10.dist-info/LICENSE,sha256=427vuyirL5scgBLqA9UWcdnxKrtSGc0u_JfUupk6lAA,11359
57
- isolate-0.13.10.dist-info/METADATA,sha256=EbD9rw4Ys3Z9DcN0pI5ak-aHndo9zGuqOgAjypuK-uw,3192
58
- isolate-0.13.10.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
59
- isolate-0.13.10.dist-info/entry_points.txt,sha256=s3prh2EERaVCbL8R45tfY5WFPZ1TsYOsz305YR7s-Pc,360
60
- isolate-0.13.10.dist-info/top_level.txt,sha256=W9QJBHcq5WXRkbOXf25bvftzFsOZZN4n1DAatdroZrs,8
61
- isolate-0.13.10.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (73.0.1)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5