cocoindex 0.2.5__cp311-abi3-manylinux_2_28_aarch64.whl → 0.2.7__cp311-abi3-manylinux_2_28_aarch64.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.
cocoindex/_engine.abi3.so CHANGED
Binary file
cocoindex/cli.py CHANGED
@@ -363,6 +363,13 @@ def drop(app_target: str | None, flow_name: tuple[str, ...], force: bool) -> Non
363
363
  default=False,
364
364
  help="Continuously watch changes from data sources and apply to the target index.",
365
365
  )
366
+ @click.option(
367
+ "--reexport",
368
+ is_flag=True,
369
+ show_default=True,
370
+ default=False,
371
+ help="Reexport to targets even if there's no change.",
372
+ )
366
373
  @click.option(
367
374
  "--setup",
368
375
  is_flag=True,
@@ -389,6 +396,7 @@ def drop(app_target: str | None, flow_name: tuple[str, ...], force: bool) -> Non
389
396
  def update(
390
397
  app_flow_specifier: str,
391
398
  live: bool,
399
+ reexport: bool,
392
400
  setup: bool, # pylint: disable=redefined-outer-name
393
401
  force: bool,
394
402
  quiet: bool,
@@ -408,7 +416,11 @@ def update(
408
416
  fg="yellow",
409
417
  )
410
418
 
411
- options = flow.FlowLiveUpdaterOptions(live_mode=live, print_stats=not quiet)
419
+ options = flow.FlowLiveUpdaterOptions(
420
+ live_mode=live,
421
+ reexport_targets=reexport,
422
+ print_stats=not quiet,
423
+ )
412
424
  if flow_name is None:
413
425
  if setup:
414
426
  _setup_flows(
@@ -519,6 +531,13 @@ def evaluate(
519
531
  default=False,
520
532
  help="Automatically setup backends for the flow if it's not setup yet.",
521
533
  )
534
+ @click.option(
535
+ "--reexport",
536
+ is_flag=True,
537
+ show_default=True,
538
+ default=False,
539
+ help="Reexport to targets even if there's no change.",
540
+ )
522
541
  @click.option(
523
542
  "-f",
524
543
  "--force",
@@ -548,6 +567,7 @@ def server(
548
567
  address: str | None,
549
568
  live_update: bool,
550
569
  setup: bool, # pylint: disable=redefined-outer-name
570
+ reexport: bool,
551
571
  force: bool,
552
572
  quiet: bool,
553
573
  cors_origin: str | None,
@@ -571,6 +591,7 @@ def server(
571
591
  cors_local,
572
592
  live_update,
573
593
  setup,
594
+ reexport,
574
595
  force,
575
596
  quiet,
576
597
  )
@@ -619,6 +640,7 @@ def _run_server(
619
640
  cors_local: int | None = None,
620
641
  live_update: bool = False,
621
642
  run_setup: bool = False,
643
+ reexport: bool = False,
622
644
  force: bool = False,
623
645
  quiet: bool = False,
624
646
  ) -> None:
@@ -652,8 +674,12 @@ def _run_server(
652
674
 
653
675
  click.secho("Press Ctrl+C to stop the server.", fg="yellow")
654
676
 
655
- if live_update:
656
- options = flow.FlowLiveUpdaterOptions(live_mode=True, print_stats=not quiet)
677
+ if live_update or reexport:
678
+ options = flow.FlowLiveUpdaterOptions(
679
+ live_mode=live_update,
680
+ reexport_targets=reexport,
681
+ print_stats=not quiet,
682
+ )
657
683
  asyncio.run_coroutine_threadsafe(
658
684
  _update_all_flows_with_hint_async(options), execution_context.event_loop
659
685
  )
cocoindex/flow.py CHANGED
@@ -563,9 +563,14 @@ class FlowBuilder:
563
563
  class FlowLiveUpdaterOptions:
564
564
  """
565
565
  Options for live updating a flow.
566
+
567
+ - live_mode: Whether to perform live update for data sources with change capture mechanisms.
568
+ - reexport_targets: Whether to reexport to targets even if there's no change.
569
+ - print_stats: Whether to print stats during update.
566
570
  """
567
571
 
568
572
  live_mode: bool = True
573
+ reexport_targets: bool = False
569
574
  print_stats: bool = False
570
575
 
571
576
 
@@ -759,20 +764,25 @@ class Flow:
759
764
  """
760
765
  return self._full_name
761
766
 
762
- def update(self) -> _engine.IndexUpdateInfo:
767
+ def update(self, /, *, reexport_targets: bool = False) -> _engine.IndexUpdateInfo:
763
768
  """
764
769
  Update the index defined by the flow.
765
770
  Once the function returns, the index is fresh up to the moment when the function is called.
766
771
  """
767
- return execution_context.run(self.update_async())
772
+ return execution_context.run(
773
+ self.update_async(reexport_targets=reexport_targets)
774
+ )
768
775
 
769
- async def update_async(self) -> _engine.IndexUpdateInfo:
776
+ async def update_async(
777
+ self, /, *, reexport_targets: bool = False
778
+ ) -> _engine.IndexUpdateInfo:
770
779
  """
771
780
  Update the index defined by the flow.
772
781
  Once the function returns, the index is fresh up to the moment when the function is called.
773
782
  """
774
783
  async with FlowLiveUpdater(
775
- self, FlowLiveUpdaterOptions(live_mode=False)
784
+ self,
785
+ FlowLiveUpdaterOptions(live_mode=False, reexport_targets=reexport_targets),
776
786
  ) as updater:
777
787
  await updater.wait_async()
778
788
  return updater.update_stats()
@@ -134,24 +134,43 @@ def _start_parent_watchdog(
134
134
  This runs in a background daemon thread so it never blocks pool work.
135
135
  """
136
136
 
137
+ import psutil # type: ignore
138
+
139
+ if parent_pid is None:
140
+ parent_pid = os.getppid()
141
+
142
+ try:
143
+ p = psutil.Process(parent_pid)
144
+ # Cache create_time to defeat PID reuse.
145
+ created = p.create_time()
146
+ except psutil.Error:
147
+ # Parent already gone or not accessible
148
+ os._exit(1)
149
+
137
150
  def _watch() -> None:
138
151
  while True:
139
- # If PPID changed (parent died and we were reparented), exit.
140
- if os.getppid() != parent_pid:
141
- os._exit(1)
142
-
143
- # Best-effort liveness probe in case PPID was reused.
144
152
  try:
145
- os.kill(parent_pid, 0)
146
- except OSError:
153
+ # is_running() + same create_time => same process and still alive
154
+ if not (p.is_running() and p.create_time() == created):
155
+ os._exit(1)
156
+ except psutil.NoSuchProcess:
147
157
  os._exit(1)
148
-
149
158
  time.sleep(interval_seconds)
150
159
 
151
160
  threading.Thread(target=_watch, name="parent-watchdog", daemon=True).start()
152
161
 
153
162
 
154
163
  def _subprocess_init(user_apps: list[str], parent_pid: int) -> None:
164
+ import signal
165
+ import faulthandler
166
+
167
+ faulthandler.enable()
168
+ # Ignore SIGINT in the subprocess on best-effort basis.
169
+ try:
170
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
171
+ except Exception:
172
+ pass
173
+
155
174
  _start_parent_watchdog(parent_pid)
156
175
 
157
176
  # In case any user app is already in this subprocess, e.g. the subprocess is forked, we need to avoid loading it again.
@@ -193,10 +212,15 @@ _SUBPROC_EXECUTORS: dict[bytes, _ExecutorEntry] = {}
193
212
 
194
213
  def _call_method(method: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
195
214
  """Run an awaitable/coroutine to completion synchronously, otherwise return as-is."""
196
- if asyncio.iscoroutinefunction(method):
197
- return asyncio.run(method(*args, **kwargs))
198
- else:
199
- return method(*args, **kwargs)
215
+ try:
216
+ if asyncio.iscoroutinefunction(method):
217
+ return asyncio.run(method(*args, **kwargs))
218
+ else:
219
+ return method(*args, **kwargs)
220
+ except Exception as e:
221
+ raise RuntimeError(
222
+ f"Error calling method `{method.__name__}` from subprocess"
223
+ ) from e
200
224
 
201
225
 
202
226
  def _get_or_create_entry(key_bytes: bytes) -> _ExecutorEntry:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cocoindex
3
- Version: 0.2.5
3
+ Version: 0.2.7
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: License :: OSI Approved :: Apache Software License
6
6
  Classifier: Operating System :: OS Independent
@@ -21,6 +21,7 @@ Requires-Dist: rich>=14.0.0
21
21
  Requires-Dist: python-dotenv>=1.1.0
22
22
  Requires-Dist: watchfiles>=1.1.0
23
23
  Requires-Dist: numpy>=1.23.2
24
+ Requires-Dist: psutil>=7.0.0
24
25
  Requires-Dist: pytest ; extra == 'dev'
25
26
  Requires-Dist: pytest-asyncio ; extra == 'dev'
26
27
  Requires-Dist: ruff ; extra == 'dev'
@@ -34,6 +35,7 @@ Provides-Extra: dev
34
35
  Provides-Extra: embeddings
35
36
  Provides-Extra: colpali
36
37
  Provides-Extra: all
38
+ License-File: THIRD_PARTY_NOTICES.html
37
39
  Summary: With CocoIndex, users declare the transformation, CocoIndex creates & maintains an index, and keeps the derived index up to date based on source update, with minimal computation and changes.
38
40
  Keywords: indexing,real-time,incremental,pipeline,search,ai,etl,rag,dataflow,context-engineering
39
41
  Author-email: CocoIndex <cocoindex.io@gmail.com>
@@ -1,12 +1,13 @@
1
- cocoindex-0.2.5.dist-info/METADATA,sha256=103rko_UCWpL42QrZxf3AoZoVTqfwJ5fz3rSfSLCUzA,12930
2
- cocoindex-0.2.5.dist-info/WHEEL,sha256=pQhpX1zEYym7lHVMSudaqVXS44siTf8XBrjmsFdZ39M,108
3
- cocoindex-0.2.5.dist-info/entry_points.txt,sha256=_NretjYVzBdNTn7dK-zgwr7YfG2afz1u1uSE-5bZXF8,46
1
+ cocoindex-0.2.7.dist-info/METADATA,sha256=MvtUzkoSmDgJRFLhsratRoLuH0Tu_ijCV4N3aiT1-58,12998
2
+ cocoindex-0.2.7.dist-info/WHEEL,sha256=pQhpX1zEYym7lHVMSudaqVXS44siTf8XBrjmsFdZ39M,108
3
+ cocoindex-0.2.7.dist-info/entry_points.txt,sha256=_NretjYVzBdNTn7dK-zgwr7YfG2afz1u1uSE-5bZXF8,46
4
+ cocoindex-0.2.7.dist-info/licenses/THIRD_PARTY_NOTICES.html,sha256=LoD8IOKM2beOhYqtdnA9YRt6xoXzKr_73KFP9RYqjBQ,716358
4
5
  cocoindex/__init__.py,sha256=sLpSVO5Cotgn_82lawxvXnaqfa-qj33rytWBAe2MTtU,2201
5
- cocoindex/_engine.abi3.so,sha256=gjYvpa0oyrac85mTxI4BSEV56CLdVAKAdlcIpbcKsuM,70104560
6
+ cocoindex/_engine.abi3.so,sha256=Om6PdmrEsmpIfnN1f4z2WYQo0HG078aAbL8IgLPp_nE,70109568
6
7
  cocoindex/auth_registry.py,sha256=PE1-kVkcyC1G2C_V7b1kvYzeq73OFQehWKQP7ln7fJ8,1478
7
- cocoindex/cli.py,sha256=D-mlZrH-trT5UR3N5KfQeq2CQ95odO2FTi67WIPjzPM,21120
8
+ cocoindex/cli.py,sha256=laLLHtEQvEt7Ua4xulsWEI_dH5IpHiaXyTWY8p1VF_c,21665
8
9
  cocoindex/convert.py,sha256=Eh9Co37BtW_PK3Oi-pFEiFt8cc_6g7XLcurV-NeH6GU,22090
9
- cocoindex/flow.py,sha256=AoWt8i05MfEnnoAQR0EWzZU-9k36UmCFuNLb3itlGO0,37204
10
+ cocoindex/flow.py,sha256=wKT_RWeB9_sm764aNrOl2XU-RJIu4EK_sIkjcyZI8jA,37669
10
11
  cocoindex/functions.py,sha256=09erNt3WbzY9l1KER-akBF2O5-6xEahV2ORBECaL6yk,12260
11
12
  cocoindex/index.py,sha256=j93B9jEvvLXHtpzKWL88SY6wCGEoPgpsQhEGHlyYGFg,540
12
13
  cocoindex/lib.py,sha256=f--9dAYd84CZosbDZqNW0oGbBLsY3dXiUTR1VrfQ_QY,817
@@ -17,10 +18,9 @@ cocoindex/runtime.py,sha256=povilB3HH3y1JF-yxKwU-pD8n2WnAqyQxIgvXXHNc60,1080
17
18
  cocoindex/setting.py,sha256=cuudZ2uJvS48wh-rDToPAUN7-KMjlyQ-0hWhkHMIx4U,5282
18
19
  cocoindex/setup.py,sha256=7uIHKN4FOCuoidPXcKyGTrkqpkl9luL49-6UcnMxYzw,3068
19
20
  cocoindex/sources.py,sha256=FYz7cWYasLGDaYoIEQ1dF2uprgUETHWsTIrIS7n6pQE,3188
20
- cocoindex/subprocess_exec.py,sha256=aq459hCNjEnZvldIPB0eSuosr2jZDqPfT4zMwLsAnqk,9423
21
+ cocoindex/subprocess_exec.py,sha256=r1xO84uek4VP4I6i87JMwsH5xFm3vKW0ABvgn0jskt4,10088
21
22
  cocoindex/targets.py,sha256=Nfh_tpFd1goTnS_cxBjIs4j9zl3Z4Z1JomAQ1dl3Sic,2796
22
23
  cocoindex/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- cocoindex/tests/conftest.py,sha256=Q1ev90H_6zncEL0nlzgd4idIaE6OQW_8cMf05V-Wgec,1054
24
24
  cocoindex/tests/test_convert.py,sha256=UsQDuz7qukT9UAU5LBv3atNTqdUVc6uW8lrmLA7ys_w,50207
25
25
  cocoindex/tests/test_optional_database.py,sha256=snAmkNa6wtOSaxoZE1HgjvL5v_ylitt3Jt_9df4Cgdc,8506
26
26
  cocoindex/tests/test_transform_flow.py,sha256=G69w-n-vnCTo3r9hVIk2lJNAQEkGUA7PZfHsXna3oS0,6030
@@ -30,4 +30,4 @@ cocoindex/typing.py,sha256=lEQYIzAGVKQp6RnhyeopY9Q7xEED7yQj3ZMxvTPblV8,14200
30
30
  cocoindex/user_app_loader.py,sha256=bc3Af-gYRxJ9GpObtpjegZY855oQBCv5FGkrkWV2yGY,1873
31
31
  cocoindex/utils.py,sha256=hUhX-XV6XGCtJSEIpBOuDv6VvqImwPlgBxztBTw7u0U,598
32
32
  cocoindex/validation.py,sha256=PZnJoby4sLbsmPv9fOjOQXuefjfZ7gmtsiTGU8SH-tc,3090
33
- cocoindex-0.2.5.dist-info/RECORD,,
33
+ cocoindex-0.2.7.dist-info/RECORD,,