indexify 0.2.47__py3-none-any.whl → 0.3.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.
Files changed (59) hide show
  1. indexify/{cli.py → cli/cli.py} +75 -82
  2. indexify/executor/README.md +35 -0
  3. indexify/executor/api_objects.py +9 -3
  4. indexify/executor/downloader.py +5 -5
  5. indexify/executor/executor.py +35 -22
  6. indexify/executor/function_executor/function_executor.py +14 -3
  7. indexify/executor/function_executor/function_executor_state.py +13 -10
  8. indexify/executor/function_executor/invocation_state_client.py +2 -1
  9. indexify/executor/function_executor/server/subprocess_function_executor_server_factory.py +22 -10
  10. indexify/executor/function_executor/single_task_runner.py +43 -26
  11. indexify/executor/function_executor/task_input.py +1 -3
  12. indexify/executor/task_fetcher.py +5 -7
  13. indexify/executor/task_reporter.py +3 -5
  14. indexify/executor/task_runner.py +30 -23
  15. indexify/function_executor/README.md +18 -0
  16. indexify/function_executor/handlers/run_function/function_inputs_loader.py +13 -14
  17. indexify/function_executor/handlers/run_function/handler.py +16 -40
  18. indexify/function_executor/handlers/run_function/request_validator.py +7 -5
  19. indexify/function_executor/handlers/run_function/response_helper.py +6 -8
  20. indexify/function_executor/initialize_request_validator.py +1 -2
  21. indexify/function_executor/invocation_state/invocation_state_proxy_server.py +1 -1
  22. indexify/function_executor/invocation_state/proxied_invocation_state.py +1 -3
  23. indexify/function_executor/main.py +50 -0
  24. indexify/function_executor/proto/configuration.py +8 -0
  25. indexify/function_executor/proto/function_executor.proto +9 -4
  26. indexify/function_executor/proto/function_executor_pb2.py +24 -24
  27. indexify/function_executor/proto/function_executor_pb2.pyi +24 -4
  28. indexify/function_executor/server.py +4 -6
  29. indexify/function_executor/{function_executor_service.py → service.py} +35 -18
  30. indexify/utils/README.md +3 -0
  31. indexify/{common_util.py → utils/http_client.py} +2 -2
  32. indexify/{logging.py → utils/logging.py} +36 -2
  33. indexify-0.3.0.dist-info/METADATA +38 -0
  34. indexify-0.3.0.dist-info/RECORD +44 -0
  35. {indexify-0.2.47.dist-info → indexify-0.3.0.dist-info}/WHEEL +1 -1
  36. indexify-0.3.0.dist-info/entry_points.txt +4 -0
  37. indexify/__init__.py +0 -31
  38. indexify/data_loaders/__init__.py +0 -58
  39. indexify/data_loaders/local_directory_loader.py +0 -37
  40. indexify/data_loaders/url_loader.py +0 -52
  41. indexify/error.py +0 -8
  42. indexify/functions_sdk/data_objects.py +0 -27
  43. indexify/functions_sdk/graph.py +0 -364
  44. indexify/functions_sdk/graph_definition.py +0 -63
  45. indexify/functions_sdk/graph_validation.py +0 -70
  46. indexify/functions_sdk/image.py +0 -210
  47. indexify/functions_sdk/indexify_functions.py +0 -354
  48. indexify/functions_sdk/invocation_state/invocation_state.py +0 -22
  49. indexify/functions_sdk/invocation_state/local_invocation_state.py +0 -30
  50. indexify/functions_sdk/object_serializer.py +0 -68
  51. indexify/functions_sdk/pipeline.py +0 -33
  52. indexify/http_client.py +0 -379
  53. indexify/remote_graph.py +0 -138
  54. indexify/remote_pipeline.py +0 -25
  55. indexify/settings.py +0 -1
  56. indexify-0.2.47.dist-info/LICENSE.txt +0 -201
  57. indexify-0.2.47.dist-info/METADATA +0 -154
  58. indexify-0.2.47.dist-info/RECORD +0 -60
  59. indexify-0.2.47.dist-info/entry_points.txt +0 -3
@@ -1,210 +0,0 @@
1
- import datetime
2
- import hashlib
3
- import importlib
4
- import logging
5
- import os
6
- import pathlib
7
- import sys
8
- import tarfile
9
- from io import BytesIO
10
- from typing import List, Optional
11
-
12
- import docker
13
- import docker.api.build
14
- from pydantic import BaseModel
15
-
16
-
17
- # Pydantic object for API
18
- class ImageInformation(BaseModel):
19
- image_name: str
20
- image_hash: str
21
- image_url: Optional[str] = ""
22
- sdk_version: str
23
-
24
- # These are deprecated and here for backwards compatibility
25
- run_strs: List[str] | None = []
26
- tag: str | None = ""
27
- base_image: str | None = ""
28
-
29
-
30
- HASH_BUFF_SIZE = 1024**2
31
-
32
-
33
- class BuildOp(BaseModel):
34
- op_type: str
35
- args: List[str]
36
-
37
- def hash(self, hash):
38
- match self.op_type:
39
- case "RUN":
40
- hash.update("RUN".encode())
41
- for a in self.args:
42
- hash.update(a.encode())
43
-
44
- case "COPY":
45
- hash.update("COPY".encode())
46
- for root, dirs, files in os.walk(self.args[0]):
47
- for file in files:
48
- filename = pathlib.Path(root, file)
49
- with open(filename, "rb") as fp:
50
- data = fp.read(HASH_BUFF_SIZE)
51
- while data:
52
- hash.update(data)
53
- data = fp.read(HASH_BUFF_SIZE)
54
-
55
- case _:
56
- raise ValueError(f"Unsupported build op type {self.op_type}")
57
-
58
- def render(self):
59
- match self.op_type:
60
- case "RUN":
61
- return f"RUN {''.join(self.args)}"
62
- case "COPY":
63
- return f"COPY {self.args[0]} {self.args[1]}"
64
- case _:
65
- raise ValueError(f"Unsupported build op type {self.op_type}")
66
-
67
-
68
- class Build(BaseModel):
69
- """
70
- Model for talking with the build service.
71
- """
72
-
73
- id: int | None = None
74
- namespace: str
75
- image_name: str
76
- image_hash: str
77
- status: str | None
78
- result: str | None
79
-
80
- created_at: datetime.datetime | None
81
- started_at: datetime.datetime | None = None
82
- build_completed_at: datetime.datetime | None = None
83
- push_completed_at: datetime.datetime | None = None
84
- uri: str | None = None
85
-
86
-
87
- class Image:
88
- def __init__(self):
89
- self._image_name = None
90
- self._tag = "latest"
91
- self._base_image = BASE_IMAGE_NAME
92
- self._python_version = LOCAL_PYTHON_VERSION
93
- self._build_ops = [] # List of ImageOperation
94
- self._sdk_version = importlib.metadata.version("indexify")
95
-
96
- def name(self, image_name):
97
- self._image_name = image_name
98
- return self
99
-
100
- def tag(self, tag):
101
- self._tag = tag
102
- return self
103
-
104
- def base_image(self, base_image):
105
- self._base_image = base_image
106
- return self
107
-
108
- def run(self, run_str):
109
- self._build_ops.append(BuildOp(op_type="RUN", args=[run_str]))
110
- return self
111
-
112
- def copy(self, source: str, dest: str):
113
- self._build_ops.append(BuildOp(op_type="COPY", args=[source, dest]))
114
- return self
115
-
116
- def to_image_information(self):
117
- return ImageInformation(
118
- image_name=self._image_name,
119
- sdk_version=self._sdk_version,
120
- image_hash=self.hash(),
121
- )
122
-
123
- def build_context(self, filename: str):
124
- with tarfile.open(filename, "w:gz") as tf:
125
- for op in self._build_ops:
126
- if op.op_type == "COPY":
127
- src = op.args[0]
128
- logging.info(f"Adding {src}")
129
- tf.add(src, src)
130
-
131
- dockerfile = self._generate_dockerfile()
132
- tarinfo = tarfile.TarInfo("Dockerfile")
133
- tarinfo.size = len(dockerfile)
134
-
135
- tf.addfile(tarinfo, BytesIO(dockerfile.encode()))
136
-
137
- def _generate_dockerfile(self, python_sdk_path: Optional[str] = None):
138
- docker_contents = [
139
- f"FROM {self._base_image}",
140
- "RUN mkdir -p ~/.indexify",
141
- f"RUN echo {self._image_name} > ~/.indexify/image_name",
142
- f"RUN echo {self.hash()} > ~/.indexify/image_hash",
143
- "WORKDIR /app",
144
- ]
145
-
146
- for build_op in self._build_ops:
147
- docker_contents.append(build_op.render())
148
-
149
- if python_sdk_path is not None:
150
- logging.info(
151
- f"Building image {self._image_name} with local version of the SDK"
152
- )
153
- if not os.path.exists(python_sdk_path):
154
- print(f"error: {python_sdk_path} does not exist")
155
- os.exit(1)
156
- docker_contents.append(f"COPY {python_sdk_path} /app/python-sdk")
157
- docker_contents.append("RUN (cd /app/python-sdk && pip install .)")
158
- else:
159
- docker_contents.append(f"RUN pip install indexify=={self._sdk_version}")
160
-
161
- docker_file = "\n".join(docker_contents)
162
- return docker_file
163
-
164
- def build(self, python_sdk_path: Optional[str] = None, docker_client=None):
165
- if docker_client is None:
166
- docker_client = docker.from_env()
167
- docker_client.ping()
168
-
169
- docker_file = self._generate_dockerfile(python_sdk_path=python_sdk_path)
170
- image_name = f"{self._image_name}:{self._tag}"
171
-
172
- docker.api.build.process_dockerfile = lambda dockerfile, path: (
173
- "Dockerfile",
174
- dockerfile,
175
- )
176
-
177
- return docker_client.images.build(
178
- path=".",
179
- dockerfile=docker_file,
180
- tag=image_name,
181
- rm=True,
182
- )
183
-
184
- def hash(self) -> str:
185
- hash = hashlib.sha256(
186
- self._image_name.encode()
187
- ) # Make a hash of the image name
188
- hash.update(self._base_image.encode())
189
- for op in self._build_ops:
190
- op.hash(hash)
191
-
192
- hash.update(self._sdk_version.encode())
193
-
194
- return hash.hexdigest()
195
-
196
-
197
- LOCAL_PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}"
198
- BASE_IMAGE_NAME = f"python:{LOCAL_PYTHON_VERSION}-slim-bookworm"
199
-
200
-
201
- def GetDefaultPythonImage(python_version: str):
202
- return (
203
- Image()
204
- .name("tensorlake/indexify-executor-default")
205
- .base_image(f"python:{python_version}-slim-bookworm")
206
- .tag(python_version)
207
- )
208
-
209
-
210
- DEFAULT_IMAGE = GetDefaultPythonImage(LOCAL_PYTHON_VERSION)
@@ -1,354 +0,0 @@
1
- import inspect
2
- import traceback
3
- from inspect import Parameter
4
- from typing import (
5
- Any,
6
- Dict,
7
- List,
8
- Optional,
9
- Tuple,
10
- Type,
11
- Union,
12
- get_args,
13
- get_origin,
14
- )
15
-
16
- from pydantic import BaseModel
17
- from typing_extensions import get_type_hints
18
-
19
- from .data_objects import IndexifyData
20
- from .image import DEFAULT_IMAGE, Image
21
- from .invocation_state.invocation_state import InvocationState
22
- from .object_serializer import get_serializer
23
-
24
-
25
- class GraphInvocationContext:
26
- def __init__(
27
- self,
28
- invocation_id: str,
29
- graph_name: str,
30
- graph_version: str,
31
- invocation_state: InvocationState,
32
- ):
33
- self.invocation_id = invocation_id
34
- self.graph_name = graph_name
35
- self.graph_version = graph_version
36
- self.invocation_state = invocation_state
37
-
38
-
39
- def is_pydantic_model_from_annotation(type_annotation):
40
- # If it's a string representation
41
- if isinstance(type_annotation, str):
42
- # Extract the class name from the string
43
- class_name = type_annotation.split("'")[-2].split(".")[-1]
44
- # This part is tricky and might require additional context or imports
45
- # You might need to import the actual class or module where it's defined
46
- # For example:
47
- # from indexify.functions_sdk.data_objects import File
48
- # return issubclass(eval(class_name), BaseModel)
49
- return False # Default to False if we can't evaluate
50
-
51
- # If it's a Type object
52
- origin = get_origin(type_annotation)
53
- if origin is not None:
54
- # Handle generic types like List[File], Optional[File], etc.
55
- args = get_args(type_annotation)
56
- if args:
57
- return is_pydantic_model_from_annotation(args[0])
58
-
59
- # If it's a direct class reference
60
- if isinstance(type_annotation, type):
61
- return issubclass(type_annotation, BaseModel)
62
-
63
- return False
64
-
65
-
66
- class PlacementConstraints(BaseModel):
67
- min_python_version: Optional[str] = "3.9"
68
- max_python_version: Optional[str] = None
69
- platform: Optional[str] = None
70
- image_name: Optional[str] = None
71
-
72
-
73
- class IndexifyFunction:
74
- name: str = ""
75
- description: str = ""
76
- image: Optional[Image] = DEFAULT_IMAGE
77
- placement_constraints: List[PlacementConstraints] = []
78
- accumulate: Optional[Type[Any]] = None
79
- input_encoder: Optional[str] = "cloudpickle"
80
- output_encoder: Optional[str] = "cloudpickle"
81
-
82
- def run(self, *args, **kwargs) -> Union[List[Any], Any]:
83
- pass
84
-
85
- def _call_run(self, *args, **kwargs) -> Union[List[Any], Any]:
86
- # Process dictionary argument mapping it to args or to kwargs.
87
- if self.accumulate and len(args) == 2 and isinstance(args[1], dict):
88
- sig = inspect.signature(self.run)
89
- new_args = [args[0]] # Keep the accumulate argument
90
- dict_arg = args[1]
91
- new_args_from_dict, new_kwargs = _process_dict_arg(dict_arg, sig)
92
- new_args.extend(new_args_from_dict)
93
- return self.run(*new_args, **new_kwargs)
94
- elif len(args) == 1 and isinstance(args[0], dict):
95
- sig = inspect.signature(self.run)
96
- dict_arg = args[0]
97
- new_args, new_kwargs = _process_dict_arg(dict_arg, sig)
98
- return self.run(*new_args, **new_kwargs)
99
-
100
- return self.run(*args, **kwargs)
101
-
102
- @classmethod
103
- def deserialize_output(cls, output: IndexifyData) -> Any:
104
- serializer = get_serializer(cls.output_encoder)
105
- return serializer.deserialize(output.payload)
106
-
107
-
108
- class IndexifyRouter:
109
- name: str = ""
110
- description: str = ""
111
- image: Optional[Image] = DEFAULT_IMAGE
112
- placement_constraints: List[PlacementConstraints] = []
113
- input_encoder: Optional[str] = "cloudpickle"
114
- output_encoder: Optional[str] = "cloudpickle"
115
-
116
- def run(self, *args, **kwargs) -> Optional[List[IndexifyFunction]]:
117
- pass
118
-
119
- # Create run method that preserves signature
120
- def _call_run(self, *args, **kwargs):
121
- # Process dictionary argument mapping it to args or to kwargs.
122
- if len(args) == 1 and isinstance(args[0], dict):
123
- sig = inspect.signature(self.run)
124
- dict_arg = args[0]
125
- new_args, new_kwargs = _process_dict_arg(dict_arg, sig)
126
- return self.run(*new_args, **new_kwargs)
127
-
128
- return self.run(*args, **kwargs)
129
-
130
-
131
- def _process_dict_arg(dict_arg: dict, sig: inspect.Signature) -> Tuple[list, dict]:
132
- new_args = []
133
- new_kwargs = {}
134
- remaining_kwargs = dict_arg.copy()
135
-
136
- # Match dictionary keys to function parameters
137
- for param_name, param in sig.parameters.items():
138
- if param_name in dict_arg:
139
- new_args.append(dict_arg[param_name])
140
- remaining_kwargs.pop(param_name, None)
141
-
142
- if any(v.kind == Parameter.VAR_KEYWORD for v in sig.parameters.values()):
143
- # Combine remaining dict items with additional kwargs
144
- new_kwargs.update(remaining_kwargs)
145
- elif len(remaining_kwargs) > 0:
146
- # If there are remaining kwargs, add them as a single dict argument
147
- new_args.append(remaining_kwargs)
148
-
149
- return new_args, new_kwargs
150
-
151
-
152
- def indexify_router(
153
- name: Optional[str] = None,
154
- description: Optional[str] = "",
155
- image: Optional[Image] = DEFAULT_IMAGE,
156
- placement_constraints: List[PlacementConstraints] = [],
157
- input_encoder: Optional[str] = "cloudpickle",
158
- output_encoder: Optional[str] = "cloudpickle",
159
- ):
160
- def construct(fn):
161
- attrs = {
162
- "name": name if name else fn.__name__,
163
- "description": (
164
- description
165
- if description
166
- else (fn.__doc__ or "").strip().replace("\n", "")
167
- ),
168
- "image": image,
169
- "placement_constraints": placement_constraints,
170
- "input_encoder": input_encoder,
171
- "output_encoder": output_encoder,
172
- "run": staticmethod(fn),
173
- }
174
-
175
- return type("IndexifyRouter", (IndexifyRouter,), attrs)
176
-
177
- return construct
178
-
179
-
180
- def indexify_function(
181
- name: Optional[str] = None,
182
- description: Optional[str] = "",
183
- image: Optional[Image] = DEFAULT_IMAGE,
184
- accumulate: Optional[Type[BaseModel]] = None,
185
- input_encoder: Optional[str] = "cloudpickle",
186
- output_encoder: Optional[str] = "cloudpickle",
187
- placement_constraints: List[PlacementConstraints] = [],
188
- ):
189
- def construct(fn):
190
- attrs = {
191
- "name": name if name else fn.__name__,
192
- "description": (
193
- description
194
- if description
195
- else (fn.__doc__ or "").strip().replace("\n", "")
196
- ),
197
- "image": image,
198
- "placement_constraints": placement_constraints,
199
- "accumulate": accumulate,
200
- "input_encoder": input_encoder,
201
- "output_encoder": output_encoder,
202
- "run": staticmethod(fn),
203
- }
204
-
205
- return type("IndexifyFunction", (IndexifyFunction,), attrs)
206
-
207
- return construct
208
-
209
-
210
- class FunctionCallResult(BaseModel):
211
- ser_outputs: List[IndexifyData]
212
- traceback_msg: Optional[str] = None
213
-
214
-
215
- class RouterCallResult(BaseModel):
216
- edges: List[str]
217
- traceback_msg: Optional[str] = None
218
-
219
-
220
- class IndexifyFunctionWrapper:
221
- def __init__(
222
- self,
223
- indexify_function: Union[IndexifyFunction, IndexifyRouter],
224
- context: GraphInvocationContext,
225
- ):
226
- self.indexify_function: Union[IndexifyFunction, IndexifyRouter] = (
227
- indexify_function()
228
- )
229
- self.indexify_function._ctx = context
230
-
231
- def get_output_model(self) -> Any:
232
- if not isinstance(self.indexify_function, IndexifyFunction):
233
- raise TypeError("Input must be an instance of IndexifyFunction")
234
-
235
- extract_method = self.indexify_function.run
236
- type_hints = get_type_hints(extract_method)
237
- return_type = type_hints.get("return", Any)
238
- if get_origin(return_type) is list:
239
- return_type = get_args(return_type)[0]
240
- elif get_origin(return_type) is Union:
241
- inner_types = get_args(return_type)
242
- if len(inner_types) == 2 and type(None) in inner_types:
243
- return_type = (
244
- inner_types[0] if inner_types[1] is type(None) else inner_types[1]
245
- )
246
- return return_type
247
-
248
- def get_input_types(self) -> Dict[str, Any]:
249
- if not isinstance(self.indexify_function, IndexifyFunction):
250
- raise TypeError("Input must be an instance of IndexifyFunction")
251
-
252
- extract_method = self.indexify_function.run
253
- type_hints = get_type_hints(extract_method)
254
- return {
255
- k: v
256
- for k, v in type_hints.items()
257
- if k != "return" and not is_pydantic_model_from_annotation(v)
258
- }
259
-
260
- def run_router(
261
- self, input: Union[Dict, Type[BaseModel]]
262
- ) -> Tuple[List[str], Optional[str]]:
263
- args = []
264
- kwargs = {}
265
- try:
266
- # tuple and list are considered positional arguments, list is used for compatibility
267
- # with json encoding which won't deserialize in tuple.
268
- if isinstance(input, tuple) or isinstance(input, list):
269
- args += input
270
- elif isinstance(input, dict):
271
- kwargs.update(input)
272
- else:
273
- args.append(input)
274
- extracted_data = self.indexify_function._call_run(*args, **kwargs)
275
- except Exception as e:
276
- return [], traceback.format_exc()
277
- if not isinstance(extracted_data, list) and extracted_data is not None:
278
- return [extracted_data.name], None
279
- edges = []
280
- for fn in extracted_data or []:
281
- edges.append(fn.name)
282
- return edges, None
283
-
284
- def run_fn(
285
- self, input: Union[Dict, Type[BaseModel], List, Tuple], acc: Type[Any] = None
286
- ) -> Tuple[List[Any], Optional[str]]:
287
- args = []
288
- kwargs = {}
289
-
290
- if acc is not None:
291
- args.append(acc)
292
-
293
- # tuple and list are considered positional arguments, list is used for compatibility
294
- # with json encoding which won't deserialize in tuple.
295
- if isinstance(input, tuple) or isinstance(input, list):
296
- args += input
297
- elif isinstance(input, dict):
298
- kwargs.update(input)
299
- else:
300
- args.append(input)
301
-
302
- try:
303
- extracted_data = self.indexify_function._call_run(*args, **kwargs)
304
- except Exception as e:
305
- return [], traceback.format_exc()
306
- if extracted_data is None:
307
- return [], None
308
-
309
- output = (
310
- extracted_data if isinstance(extracted_data, list) else [extracted_data]
311
- )
312
- return output, None
313
-
314
- def invoke_fn_ser(
315
- self, name: str, input: IndexifyData, acc: Optional[Any] = None
316
- ) -> FunctionCallResult:
317
- input = self.deserialize_input(name, input)
318
- input_serializer = get_serializer(self.indexify_function.input_encoder)
319
- output_serializer = get_serializer(self.indexify_function.output_encoder)
320
- if acc is not None:
321
- acc = input_serializer.deserialize(acc.payload)
322
- if acc is None and self.indexify_function.accumulate is not None:
323
- acc = self.indexify_function.accumulate()
324
- outputs, err = self.run_fn(input, acc=acc)
325
- ser_outputs = [
326
- IndexifyData(
327
- payload=output_serializer.serialize(output),
328
- encoder=self.indexify_function.output_encoder,
329
- )
330
- for output in outputs
331
- ]
332
- return FunctionCallResult(ser_outputs=ser_outputs, traceback_msg=err)
333
-
334
- def invoke_router(self, name: str, input: IndexifyData) -> RouterCallResult:
335
- input = self.deserialize_input(name, input)
336
- edges, err = self.run_router(input)
337
- return RouterCallResult(edges=edges, traceback_msg=err)
338
-
339
- def deserialize_input(self, compute_fn: str, indexify_data: IndexifyData) -> Any:
340
- encoder = indexify_data.encoder
341
- payload = indexify_data.payload
342
- serializer = get_serializer(encoder)
343
- return serializer.deserialize(payload)
344
-
345
-
346
- def get_ctx() -> GraphInvocationContext:
347
- frame = inspect.currentframe()
348
- caller_frame = frame.f_back.f_back
349
- function_instance = caller_frame.f_locals["self"]
350
- del frame
351
- del caller_frame
352
- if isinstance(function_instance, IndexifyFunctionWrapper):
353
- return function_instance.indexify_function._ctx
354
- return function_instance._ctx
@@ -1,22 +0,0 @@
1
- from typing import Any, Optional
2
-
3
-
4
- class InvocationState:
5
- """Abstract interface for Graph invocation state key-value API.
6
-
7
- The API allows to set and get key-value pairs from Indexify functions.
8
- The key-value pairs are scoped per Graph invocation.
9
- Each new invocation starts with an empty state (empty set of key-value pairs).
10
- A value can be any CloudPickleSerializer serializable object."""
11
-
12
- def set(self, key: str, value: Any) -> None:
13
- """Set a key-value pair.
14
-
15
- Raises Exception if an error occured."""
16
- raise NotImplementedError()
17
-
18
- def get(self, key: str, default: Optional[Any] = None) -> Optional[Any]:
19
- """Get a value by key. If the key does not exist, return the default value.
20
-
21
- Raises Exception if an error occured."""
22
- raise NotImplementedError()
@@ -1,30 +0,0 @@
1
- from typing import Any, Dict, Optional
2
-
3
- from ..object_serializer import CloudPickleSerializer
4
- from .invocation_state import InvocationState
5
-
6
-
7
- class LocalInvocationState(InvocationState):
8
- """InvocationState that stores the key-value pairs in memory.
9
-
10
- This is intended to be used with local graphs."""
11
-
12
- def __init__(self):
13
- """Creates a new instance.
14
-
15
- Caller needs to ensure that the returned instance is only used for a single invocation state.
16
- """
17
- self._state: Dict[str, bytes] = {}
18
-
19
- def set(self, key: str, value: Any) -> None:
20
- # It's important to serialize the value even in the local implementation
21
- # so there are no unexpected errors when running in remote graph mode.
22
- self._state[key] = CloudPickleSerializer.serialize(value)
23
-
24
- def get(self, key: str, default: Optional[Any] = None) -> Optional[Any]:
25
- serialized_value: Optional[bytes] = self._state.get(key, None)
26
- return (
27
- default
28
- if serialized_value is None
29
- else CloudPickleSerializer.deserialize(serialized_value)
30
- )
@@ -1,68 +0,0 @@
1
- import json
2
- from typing import Any, List, Type
3
-
4
- import cloudpickle
5
-
6
-
7
- def get_serializer(serializer_type: str) -> Any:
8
- if serializer_type == "cloudpickle":
9
- return CloudPickleSerializer()
10
- elif serializer_type == "json":
11
- return JsonSerializer()
12
- elif serializer_type == JsonSerializer.content_type:
13
- return JsonSerializer()
14
- elif serializer_type == CloudPickleSerializer.content_type:
15
- return CloudPickleSerializer()
16
- raise ValueError(f"Unknown serializer type: {serializer_type}")
17
-
18
-
19
- class JsonSerializer:
20
- content_type = "application/json"
21
- encoding_type = "json"
22
-
23
- @staticmethod
24
- def serialize(data: Any) -> str:
25
- try:
26
- return json.dumps(data)
27
- except Exception as e:
28
- raise ValueError(f"failed to serialize data with json: {e}")
29
-
30
- @staticmethod
31
- def deserialize(data: str) -> Any:
32
- try:
33
- if isinstance(data, bytes):
34
- data = data.decode("utf-8")
35
- return json.loads(data)
36
- except Exception as e:
37
- raise ValueError(f"failed to deserialize data with json: {e}")
38
-
39
- @staticmethod
40
- def serialize_list(data: List[Any]) -> str:
41
- return json.dumps(data)
42
-
43
- @staticmethod
44
- def deserialize_list(data: str, t: Type) -> List[Any]:
45
- if isinstance(data, bytes):
46
- data = data.decode("utf-8")
47
- return json.loads(data)
48
-
49
-
50
- class CloudPickleSerializer:
51
- content_type = "application/octet-stream"
52
- encoding_type = "cloudpickle"
53
-
54
- @staticmethod
55
- def serialize(data: Any) -> bytes:
56
- return cloudpickle.dumps(data)
57
-
58
- @staticmethod
59
- def deserialize(data: bytes) -> Any:
60
- return cloudpickle.loads(data)
61
-
62
- @staticmethod
63
- def serialize_list(data: List[Any]) -> bytes:
64
- return cloudpickle.dumps(data)
65
-
66
- @staticmethod
67
- def deserialize_list(data: bytes) -> List[Any]:
68
- return cloudpickle.loads(data)