indexify 0.2__py3-none-any.whl → 0.2.1__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.
indexify/__init__.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from . import data_loaders
2
2
  from .client import create_client
3
3
  from .functions_sdk.graph import Graph
4
+ from .functions_sdk.image import Image
4
5
  from .functions_sdk.indexify_functions import (
5
6
  indexify_function,
6
7
  indexify_router,
@@ -12,6 +13,7 @@ from .settings import DEFAULT_SERVICE_URL
12
13
  __all__ = [
13
14
  "data_loaders",
14
15
  "Graph",
16
+ "Image",
15
17
  "indexify_function",
16
18
  "indexify_router",
17
19
  "DEFAULT_SERVICE_URL",
indexify/cli.py CHANGED
@@ -19,7 +19,7 @@ from rich.theme import Theme
19
19
 
20
20
  from indexify.executor.agent import ExtractorAgent
21
21
  from indexify.executor.function_worker import FunctionWorker
22
- from indexify.functions_sdk.image import Image
22
+ from indexify.functions_sdk.image import Image, DEFAULT_IMAGE
23
23
 
24
24
  custom_theme = Theme(
25
25
  {
@@ -130,7 +130,6 @@ def build_image(workflow_file_path: str, func_names: List[str]):
130
130
  )
131
131
 
132
132
  found_funcs = []
133
- graph = None
134
133
  for name, obj in globals_dict.items():
135
134
  for func_name in func_names:
136
135
  if name == func_name:
@@ -143,6 +142,15 @@ def build_image(workflow_file_path: str, func_names: List[str]):
143
142
  )
144
143
 
145
144
 
145
+ @app.command(help="Build default image for indexify")
146
+ def build_default_image():
147
+ _build_image(image=DEFAULT_IMAGE)
148
+
149
+ console.print(
150
+ Text(f"Built default indexify image", style="cyan"),
151
+ )
152
+
153
+
146
154
  @app.command(help="Joins the extractors to the coordinator server")
147
155
  def executor(
148
156
  server_addr: str = "localhost:8900",
@@ -211,25 +219,29 @@ def _build_image(image: Image, func_name: str = None):
211
219
  )
212
220
  exit(-1)
213
221
 
214
- docker_file_str_template = """
215
- FROM {base_image}
222
+ docker_file = f"""
223
+ FROM {image._base_image}
224
+
225
+ RUN mkdir -p ~/.indexify
226
+
227
+ RUN touch ~/.indexify/image_name
228
+
229
+ RUN echo {image._image_name} > ~/.indexify/image_name
216
230
 
217
231
  WORKDIR /app
218
232
 
219
233
  """
220
234
 
221
- docker_file_str = docker_file_str_template.format(base_image=image._base_image)
222
-
223
235
  run_strs = ["RUN " + i for i in image._run_strs]
224
236
 
225
- docker_file_str += "\n".join(run_strs)
237
+ docker_file += "\n".join(run_strs)
226
238
 
227
239
  console.print("Creating image using Dockerfile contents:", style="cyan bold")
228
- console.print(f"{docker_file_str}", style="magenta")
240
+ console.print(f"{docker_file}", style="magenta")
229
241
 
230
242
  client = docker.from_env()
231
243
  client.images.build(
232
- fileobj=io.BytesIO(docker_file_str.encode()),
244
+ fileobj=io.BytesIO(docker_file.encode()),
233
245
  tag=f"{image._image_name}:{image._tag}",
234
246
  rm=True,
235
247
  )
indexify/client.py CHANGED
@@ -9,10 +9,10 @@ from .settings import DEFAULT_SERVICE_URL
9
9
  def create_client(
10
10
  service_url: str = DEFAULT_SERVICE_URL,
11
11
  config_path: Optional[str] = None,
12
- local: bool = False,
12
+ in_process: bool = False,
13
13
  *args,
14
14
  **kwargs,
15
15
  ) -> IndexifyClient:
16
- if local:
16
+ if in_process:
17
17
  return LocalClient()
18
18
  return RemoteClient(config_path=config_path, service_url=service_url, **kwargs)
@@ -1,7 +1,6 @@
1
1
  import asyncio
2
2
  import json
3
3
  import ssl
4
- import traceback
5
4
  from concurrent.futures.process import BrokenProcessPool
6
5
  from typing import Dict, List, Optional
7
6
 
@@ -24,6 +23,7 @@ from .api_objects import ExecutorMetadata, Task
24
23
  from .downloader import DownloadedInputs, Downloader
25
24
  from .executor_tasks import DownloadGraphTask, DownloadInputTask, ExtractTask
26
25
  from .function_worker import FunctionWorker
26
+ from .runtime_probes import ProbeInfo, RuntimeProbes
27
27
  from .task_reporter import TaskReporter
28
28
  from .task_store import CompletedTask, TaskStore
29
29
 
@@ -98,6 +98,7 @@ class ExtractorAgent:
98
98
  self._task_reporter = TaskReporter(
99
99
  base_url=self._base_url, executor_id=self._executor_id
100
100
  )
101
+ self._probe = RuntimeProbes()
101
102
 
102
103
  async def task_completion_reporter(self):
103
104
  console.print(Text("Starting task completion reporter", style="bold cyan"))
@@ -305,11 +306,12 @@ class ExtractorAgent:
305
306
  words = snake_str.split("_")
306
307
  return words[0].capitalize() + "" + " ".join(words[1:])
307
308
 
309
+ runtime_probe: ProbeInfo = self._probe.probe()
308
310
  data = ExecutorMetadata(
309
311
  id=self._executor_id,
310
- address="",
311
- runner_name="extractor",
312
- labels={},
312
+ addr="",
313
+ image_name=runtime_probe.image_name,
314
+ labels=runtime_probe.labels,
313
315
  ).model_dump()
314
316
 
315
317
  panel_content = "\n".join(
@@ -318,7 +320,7 @@ class ExtractorAgent:
318
320
  console.print(
319
321
  Panel(
320
322
  panel_content,
321
- title="Attempting to Register Executor",
323
+ title="attempting to Register Executor",
322
324
  border_style="cyan",
323
325
  )
324
326
  )
@@ -333,7 +335,7 @@ class ExtractorAgent:
333
335
  headers={"Content-Type": "application/json"},
334
336
  ) as event_source:
335
337
  console.print(
336
- Text("Executor registered successfully", style="bold green")
338
+ Text("executor registered successfully", style="bold green")
337
339
  )
338
340
  async for sse in event_source.aiter_sse():
339
341
  data = json.loads(sse.data)
@@ -345,14 +347,14 @@ class ExtractorAgent:
345
347
  self._task_store.add_tasks(tasks)
346
348
  except Exception as e:
347
349
  console.print(
348
- Text("Registration Error: ", style="red bold")
349
- + Text(f"Failed to register: {e}", style="red")
350
+ Text("registration Error: ", style="red bold")
351
+ + Text(f"failed to register: {e}", style="red")
350
352
  )
351
353
  await asyncio.sleep(5)
352
354
  continue
353
355
 
354
356
  async def _shutdown(self, loop):
355
- console.print(Text("Shutting down agent...", style="bold yellow"))
357
+ console.print(Text("shutting down agent...", style="bold yellow"))
356
358
  self._should_run = False
357
359
  for task in asyncio.all_tasks(loop):
358
360
  task.cancel()
@@ -18,8 +18,8 @@ class Task(BaseModel):
18
18
 
19
19
  class ExecutorMetadata(BaseModel):
20
20
  id: str
21
- address: str
22
- runner_name: str
21
+ addr: str
22
+ image_name: str
23
23
  labels: Dict[str, Any]
24
24
 
25
25
 
@@ -0,0 +1,48 @@
1
+ import os
2
+ import platform
3
+ import sys
4
+ from typing import Any, Dict, Tuple
5
+
6
+ from pydantic import BaseModel
7
+
8
+
9
+ class ProbeInfo(BaseModel):
10
+ image_name: str
11
+ python_major_version: int
12
+ labels: Dict[str, Any] = {}
13
+
14
+
15
+ class RuntimeProbes:
16
+ def __init__(self) -> None:
17
+ self._image_name = self._read_image_name()
18
+ self._os_name = platform.system()
19
+ self._architecture = platform.machine()
20
+ (
21
+ self._python_version_major,
22
+ self._python_version_minor,
23
+ ) = self._get_python_version()
24
+
25
+ def _read_image_name(self) -> str:
26
+ file_path = os.path.expanduser("~/.indexify/image_name")
27
+ if os.path.exists(file_path):
28
+ with open(file_path, "r") as file:
29
+ return file.read().strip()
30
+ return "indexify-executor-default"
31
+
32
+ def _get_python_version(self) -> Tuple[int, int]:
33
+ version_info = sys.version_info
34
+ return version_info.major, version_info.minor
35
+
36
+ def probe(self) -> ProbeInfo:
37
+ labels = {
38
+ "os": self._os_name,
39
+ "image_name": self._image_name,
40
+ "architecture": self._architecture,
41
+ "python_major_version": self._python_version_major,
42
+ "python_minor_version": self._python_version_minor,
43
+ }
44
+ return ProbeInfo(
45
+ image_name=self._image_name,
46
+ python_major_version=self._python_version_major,
47
+ labels=labels,
48
+ )
@@ -66,6 +66,7 @@ class FunctionMetadata(BaseModel):
66
66
  fn_name: str
67
67
  description: str
68
68
  reducer: bool = False
69
+ image_name: str
69
70
  payload_encoder: str = "cloudpickle"
70
71
 
71
72
 
@@ -74,6 +75,7 @@ class RouterMetadata(BaseModel):
74
75
  description: str
75
76
  source_fn: str
76
77
  target_fns: List[str]
78
+ image_name: str
77
79
  payload_encoder: str = "cloudpickle"
78
80
 
79
81
 
@@ -244,6 +246,7 @@ class Graph:
244
246
  fn_name=start_node.fn_name,
245
247
  description=start_node.description,
246
248
  reducer=start_node.accumulate is not None,
249
+ image_name=start_node.image._image_name,
247
250
  )
248
251
  metadata_edges = self.edges.copy()
249
252
  metadata_nodes = {}
@@ -256,6 +259,7 @@ class Graph:
256
259
  source_fn=node_name,
257
260
  target_fns=self.routers[node_name],
258
261
  payload_encoder=node.payload_encoder,
262
+ image_name=node.image._image_name,
259
263
  )
260
264
  )
261
265
  else:
@@ -265,6 +269,7 @@ class Graph:
265
269
  fn_name=node.fn_name,
266
270
  description=node.description,
267
271
  reducer=node.accumulate is not None,
272
+ image_name=node.image._image_name,
268
273
  )
269
274
  )
270
275
  return ComputeGraphMetadata(
@@ -42,28 +42,25 @@ def validate_route(
42
42
 
43
43
  if signature.return_annotation == inspect.Signature.empty:
44
44
  raise Exception(f"Function {from_node.name} has empty return type annotation")
45
+
46
+ return_annotation = signature.return_annotation
45
47
 
46
- # We lose the exact type string when the object is created
47
- source = inspect.getsource(from_node.run)
48
+ if hasattr(return_annotation, '__origin__') and return_annotation.__origin__ is Union:
49
+ for arg in return_annotation.__args__:
50
+ if hasattr(arg, 'name'):
51
+ if arg not in to_nodes:
52
+ raise Exception(
53
+ f"Unable to find {arg.name} in to_nodes {[node.name for node in to_nodes]}"
54
+ )
48
55
 
49
- union_pattern = r"Union\[((?:\w+(?:,\s*)?)+)\]"
50
- union_match = re.search(union_pattern, source)
51
-
52
- src_route_nodes = None
53
- if union_match:
54
- # nodes = re.findall(r'\w+', match.group(1))
55
- src_route_nodes = [node.strip() for node in union_match.group(1).split(",")]
56
- if len(src_route_nodes) <= 1:
57
- raise Exception(f"Invalid router for {from_node.name}, lte 1 route.")
56
+ if hasattr(return_annotation, '__origin__') and return_annotation.__origin__ is list:
57
+ union_args = return_annotation.__args__[0].__args__
58
+ for arg in union_args:
59
+ if hasattr(arg, 'name'):
60
+ if arg not in to_nodes:
61
+ raise Exception(
62
+ f"Unable to find {arg.name} in to_nodes {[node.name for node in to_nodes]}"
63
+ )
58
64
  else:
59
- raise Exception(
60
- f"Invalid router for {from_node.name}, cannot find output nodes"
61
- )
65
+ raise Exception(f"Return type of {from_node.name} is not a Union")
62
66
 
63
- to_node_names = [i.name for i in to_nodes]
64
-
65
- for src_node in src_route_nodes:
66
- if src_node not in to_node_names:
67
- raise Exception(
68
- f"Unable to find {src_node} in to_nodes " f"{to_node_names}"
69
- )
@@ -7,9 +7,8 @@ class Image:
7
7
  self._base_image = None
8
8
 
9
9
  self._run_strs = []
10
- pass
11
10
 
12
- def image_name(self, image_name):
11
+ def name(self, image_name):
13
12
  self._image_name = image_name
14
13
  return self
15
14
 
@@ -24,3 +23,12 @@ class Image:
24
23
  def run(self, run_str):
25
24
  self._run_strs.append(run_str)
26
25
  return self
26
+
27
+
28
+ DEFAULT_IMAGE = (
29
+ Image()
30
+ .name("indexify-executor-default")
31
+ .base_image("python:3.10.15-slim-bookworm")
32
+ .tag("latest")
33
+ .run("pip install indexify")
34
+ )
@@ -16,7 +16,7 @@ from pydantic import BaseModel
16
16
  from typing_extensions import get_type_hints
17
17
 
18
18
  from .data_objects import IndexifyData, RouterOutput
19
- from .image import Image
19
+ from .image import DEFAULT_IMAGE, Image
20
20
 
21
21
 
22
22
  class EmbeddingIndexes(BaseModel):
@@ -34,8 +34,8 @@ class PlacementConstraints(BaseModel):
34
34
 
35
35
  class IndexifyFunction(ABC):
36
36
  name: str = ""
37
- base_image: Optional[str] = None
38
37
  description: str = ""
38
+ image: Optional[Image] = DEFAULT_IMAGE
39
39
  placement_constraints: List[PlacementConstraints] = []
40
40
  accumulate: Optional[Type[Any]] = None
41
41
  payload_encoder: Optional[str] = "cloudpickle"
@@ -53,7 +53,7 @@ class IndexifyFunction(ABC):
53
53
  class IndexifyRouter(ABC):
54
54
  name: str = ""
55
55
  description: str = ""
56
- image: Image = None
56
+ image: Optional[Image] = DEFAULT_IMAGE
57
57
  placement_constraints: List[PlacementConstraints] = []
58
58
  payload_encoder: Optional[str] = "cloudpickle"
59
59
 
@@ -65,9 +65,9 @@ class IndexifyRouter(ABC):
65
65
  def indexify_router(
66
66
  name: Optional[str] = None,
67
67
  description: Optional[str] = "",
68
- image: Image = None,
68
+ image: Optional[Image] = DEFAULT_IMAGE,
69
69
  placement_constraints: List[PlacementConstraints] = [],
70
- output_encoder: Optional[str] = "cloudpickle",
70
+ payload_encoder: Optional[str] = "cloudpickle",
71
71
  ):
72
72
  def construct(fn):
73
73
  args = locals().copy()
@@ -90,7 +90,7 @@ def indexify_router(
90
90
  setattr(IndexifyRo, key, value)
91
91
 
92
92
  IndexifyRo.image = image
93
- IndexifyRo.payload_encoder = output_encoder
93
+ IndexifyRo.payload_encoder = payload_encoder
94
94
  return IndexifyRo
95
95
 
96
96
  return construct
@@ -99,11 +99,9 @@ def indexify_router(
99
99
  def indexify_function(
100
100
  name: Optional[str] = None,
101
101
  description: Optional[str] = "",
102
- image: Image = None,
102
+ image: Optional[Image] = DEFAULT_IMAGE,
103
103
  accumulate: Optional[Type[BaseModel]] = None,
104
- min_batch_size: Optional[int] = None,
105
- max_batch_size: Optional[int] = None,
106
- output_encoder: Optional[str] = "cloudpickle",
104
+ payload_encoder: Optional[str] = "cloudpickle",
107
105
  placement_constraints: List[PlacementConstraints] = [],
108
106
  ):
109
107
  def construct(fn):
@@ -128,9 +126,7 @@ def indexify_function(
128
126
 
129
127
  IndexifyFn.image = image
130
128
  IndexifyFn.accumulate = accumulate
131
- IndexifyFn.min_batch_size = min_batch_size
132
- IndexifyFn.max_batch_size = max_batch_size
133
- IndexifyFn.payload_encoder = output_encoder
129
+ IndexifyFn.payload_encoder = payload_encoder
134
130
  return IndexifyFn
135
131
 
136
132
  return construct
@@ -48,7 +48,6 @@ class MsgPackSerializer:
48
48
  @staticmethod
49
49
  def deserialize(data: bytes) -> IndexifyData:
50
50
  cached_output = msgpack.unpackb(data)
51
- print(cached_output)
52
51
  return IndexifyData(**cached_output)
53
52
 
54
53
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: indexify
3
- Version: 0.2
3
+ Version: 0.2.1
4
4
  Summary: Python Client for Indexify
5
5
  Home-page: https://github.com/tensorlakeai/indexify
6
6
  License: Apache 2.0
@@ -1,32 +1,33 @@
1
- indexify/__init__.py,sha256=fD9E-i9gawBNwbMd8ZIfiNsxUtL1S5VBCiyvKU5Yslc,497
1
+ indexify/__init__.py,sha256=yJ3K_TyocH2EpFr6kEbKaOqfk8dA33fpoCB_QXV-rko,549
2
2
  indexify/base_client.py,sha256=Si1XnZ6X_mFvkYCnS6qx6axFsBpkrGiorqmKohFwvLQ,3324
3
- indexify/cli.py,sha256=XZYU9iMVHqNzecMU6LdAyfa_JkHb0Xx35akaW319tTI,7091
4
- indexify/client.py,sha256=sopeSA9hc7QHIHdpGq5rehSkr0tiPvdsLSJsaVzw3kY,491
3
+ indexify/cli.py,sha256=kLKruCRNlo1xdezxYQoN6c9EpGWAeNiSs7kAjYfxCao,7311
4
+ indexify/client.py,sha256=6cwCxBky6IJYu4caq0E6SMWIxf3nn5SX795moHfS4Cw,501
5
5
  indexify/data_loaders/__init__.py,sha256=Y5NEuseTcYAICRiweYw5wBQ2m2YplbsY21I7df-rdi4,1339
6
6
  indexify/data_loaders/local_directory_loader.py,sha256=fCrgj5drnW71ZUdDDvcB1-VJjIs1w6Q8sEW0HSGSAiA,1247
7
7
  indexify/data_loaders/url_loader.py,sha256=32SERljcq1Xsi4RdLz2dgyk2TER5pQPTtXl3gUzwHbY,1533
8
8
  indexify/error.py,sha256=vjd5SPPNFIEW35GorSIodsqvm9RKHQm9kdp8t9gv-WM,111
9
- indexify/executor/agent.py,sha256=RxpmnZ4ne-2BCv6Lv0kkkBJX3Dz71bruvaFC8V3O1D0,14563
10
- indexify/executor/api_objects.py,sha256=nvRmKYngjrv8sc_GoYM1MSLdFMYB6hoq7XK4LMx2U-s,814
9
+ indexify/executor/agent.py,sha256=I08CiOWeJ_mz8OHr9_iJfp07Ma1VMQirZ2MsDp8lDZw,14723
10
+ indexify/executor/api_objects.py,sha256=SysjlGYu4JtYdqfexZHHN1IW4TtaDdFUF3hYZ5mpUJU,810
11
11
  indexify/executor/downloader.py,sha256=0MPiKw0AWs3Z7ReC9l2z-3515yqq85ghPzdh485dnuw,3998
12
12
  indexify/executor/executor_tasks.py,sha256=gAZ2pvza1YwGlaR1o_tJW4SXtdCgK7sLJgp4W7rOjR0,1834
13
13
  indexify/executor/function_worker.py,sha256=83ih8TjAJHtrH6LqqDoSfzAj4zB7ZZPR2Voq0RMZ1T8,5410
14
14
  indexify/executor/indexify_executor.py,sha256=2Ut_VX-Su_lm4b4aEROyRJ3gXx-uFHA-V7EN0sWiARE,771
15
+ indexify/executor/runtime_probes.py,sha256=tvi8KCaQTVJqcyBJ4-jzEUAnQ01ZbMmjCxV2KJ96_PI,1449
15
16
  indexify/executor/task_reporter.py,sha256=gnnse0v6rjjni8lNzeb-ZYq6iF2DgafKoT7dcGUZhQ4,3716
16
17
  indexify/executor/task_store.py,sha256=q8s2gImsFffWeXQR0mk1Xlo1Aj_2GfclNPjQ2EA_YBo,3984
17
18
  indexify/foo,sha256=e385Ws-u8zx-LOq3tdfTa-siK9pMaccdAE8_0rrp_k4,5165
18
19
  indexify/functions_sdk/data_objects.py,sha256=2LqAWJ_S2Xkp4OQTmhd3InVIrBs7juV41udnSQFMMfM,840
19
- indexify/functions_sdk/graph.py,sha256=ilImZsALzOQLwioPYqva0_FxHtrn3FscCmtVniPJecU,9764
20
- indexify/functions_sdk/graph_validation.py,sha256=UgP_iMlUsH3jO0aBkQOwfxGQV1_x2qx9_iZvSxvuJBo,2419
21
- indexify/functions_sdk/image.py,sha256=0A8xTjLv0jITRu0u06iCFplS_cu47Zu3fUCXq9Rc7zI,525
22
- indexify/functions_sdk/indexify_functions.py,sha256=sJclmxvOAiAPUONbcwmUwywsUEzMvMA6gIE6zaURr1s,5955
20
+ indexify/functions_sdk/graph.py,sha256=qy9zVbf26oTLW0d0jt991Hd8MP6N0F1CEBLL9VbwLBA,9975
21
+ indexify/functions_sdk/graph_validation.py,sha256=y-f0ZNiGYl_fjPA7v9OJWtoUMPELgtVR_ifpgqZ0IoY,2465
22
+ indexify/functions_sdk/image.py,sha256=euuz2QTZQoS-JmwnPmWJ8lfIgKzrSEsfkUc2qU26xjM,679
23
+ indexify/functions_sdk/indexify_functions.py,sha256=xxgvnw0MQ_csIksunIdero8be0PR4mfwgoHp3UlkMZU,5851
23
24
  indexify/functions_sdk/local_cache.py,sha256=cNWF67zbhbTJe3g86hyLBy3Rqzs6dNvp2SjLazGZWvw,1348
24
- indexify/functions_sdk/object_serializer.py,sha256=HPH0Ym6-Los8lg_RHxmerrAh5fSLBHDeTZikom0eXxk,1689
25
+ indexify/functions_sdk/object_serializer.py,sha256=Zz4GobW3ZamBBtFDF76QxU3TP6oJNdWnhsfKd0OUFoc,1660
25
26
  indexify/local_client.py,sha256=9wPYHjG516ZX6q5sPj5Pnn9C-Fi0sghYiLaNMe7LPPk,7220
26
27
  indexify/remote_client.py,sha256=oKgTqLbIxQVDqkMjQmNCOOEIM156UeYMC1jDWWSqBAQ,12297
27
28
  indexify/settings.py,sha256=LSaWZ0ADIVmUv6o6dHWRC3-Ry5uLbCw2sBSg1e_U7UM,99
28
- indexify-0.2.dist-info/LICENSE.txt,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
29
- indexify-0.2.dist-info/METADATA,sha256=GuigbP7vmakmIXhhijeYAiCbhvaFZX-Np2gZAcHYwrc,6127
30
- indexify-0.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
31
- indexify-0.2.dist-info/entry_points.txt,sha256=Pih7WV-XMpAzI5dEvROcpLr-ybVhd9Y-AtuzBKUdcDs,49
32
- indexify-0.2.dist-info/RECORD,,
29
+ indexify-0.2.1.dist-info/LICENSE.txt,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
30
+ indexify-0.2.1.dist-info/METADATA,sha256=ZwqwbjyxS8vszZH5L_X_1zNJM9OyAr-B0ezxpUDeLJo,6129
31
+ indexify-0.2.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
32
+ indexify-0.2.1.dist-info/entry_points.txt,sha256=Pih7WV-XMpAzI5dEvROcpLr-ybVhd9Y-AtuzBKUdcDs,49
33
+ indexify-0.2.1.dist-info/RECORD,,