Docs · Context Transfer Engine

The clio_cte_core_ext module.

clio_cte_core_ext is the Python binding of the CTE client. It ships in the iowarp-core wheel and is built with nanobind from context-transfer-engine/wrapper/python/core_bindings.cc. Payloads are bytes. Every blocking call releases the GIL. The higher-level clio_cee module is documented in the Context Exploration Engine reference.

Installed by
pip install iowarp-core
Attach with
CLIO_WITH_RUNTIME=0
Keyword search needs
CLIO_CTE_POOL=564.0

Connecting

Set environment variables before the import; the client reads them once when it connects.

python
import os
os.environ["CLIO_WITH_RUNTIME"] = "0"    # attach to a running clio_run
os.environ["CLIO_CTE_POOL"] = "564.0"    # only needed for SemanticSearch

import clio_cte_core_ext as cte
cte.clio_init(cte.RuntimeMode.kClient, False)
cte.initialize_cte("", cte.PoolQuery.Dynamic())
client = cte.get_cte_client()
  • clio_init(mode, default_with_runtime=False, is_restart=False) -> bool. CLIO_WITH_RUNTIME overrides the second argument. Waits up to CLIO_WAIT_SERVER seconds (default 30) for the runtime.
  • initialize_cte(config_path, pool_query) -> bool. Both arguments are required; pass "" and PoolQuery.Dynamic() for the defaults.
  • get_cte_client() -> Client: a copy of the shared client. Client() and Client(pool_id) construct one directly.

Tag

CallReturns
Tag(tag_name)Opens the tag, creating it if needed (GetOrCreateTag). Tag(tag_id) wraps an existing id without an RPC.
.PutBlob(blob_name, data: bytes, off=0)None. Writes at offset off; a new blob lands at score 1.0. Raises on a non-zero return code.
.GetBlob(blob_name, data_size, off=0)bytes of length data_size. Raises RuntimeError on failure.
.GetBlobSize(blob_name)int, bytes
.GetBlobScore(blob_name)float, 0.0 to 1.0
.GetContainedBlobs()list[str]
.ReorganizeBlob(blob_name, new_score)None. Moves the blob to the tier the score selects.
.GetTagId()UniqueId

Client: blocking calls

CallReturns
TagQuery(tag_regex, max_tags=0, pool_query=PoolQuery.Broadcast())list[str] of tag names
BlobQuery(tag_regex, blob_regex, max_blobs=0, pool_query=Broadcast())list[tuple[str, str]] of (tag_name, blob_name)
SemanticSearch(tag_regex, blob_regex, query_text, k=10, pool_query=Broadcast())list[SemanticSearchResult], best first; k=0 means no cap
TemporalSearch(tag_regex, blob_regex, time_begin=0, time_end=0, max_entries=0, pool_query=Broadcast())list[TemporalSearchResult], ascending by last_modified
ReorganizeBlob(tag_id, blob_name, new_score)True on success
DelBlob(tag_id, blob_name)True on success
PollTelemetryLog(minimum_logical_time)list[CteTelemetry]
RegisterTarget(target_name, bdev_type, total_size) or RegisterTarget(target_name, bdev_type, total_size, target_query, bdev_id)int, 0 on success

Patterns must match the whole name (std::regex_match), so use .*part.* for a substring. SemanticSearch ranks every blob matching both patterns, including blobs with no query word (score 0). For files in a mount, use "[0-9]+" as the blob pattern to search pages and skip each file's attribute blob.

Time values are not Unix time

last_modified comes from a steady clock, although the docstrings say epoch nanoseconds. A time_begin computed from time.time() matches nothing. Treat last_modified as an ordering key within one run of the runtime. This is a suspected bug in the source.

Client: asynchronous calls

Each returns a Future. Submission runs without the GIL and may block while shared memory is allocated.

Callresult()
AsyncGetOrCreateTag(tag_name, pool_query=Dynamic())UniqueId
AsyncPutBlob(tag_id, blob_name, data, off=0)None; wait() gives the return code
AsyncPutBlobIfAbsent(tag_id, blob_name, data, off=0){"code", "version"}; a non-zero code means another writer won
AsyncPutBlobIfVersion(tag_id, blob_name, data, version, off=0){"code", "version"}; absent counts as version 0
AsyncGetBlob(tag_id, blob_name, data_size, off=0)bytes
AsyncGetBlobVersioned(tag_id, blob_name, data_size, off=0){"code", "version", "data"}; data is None on failure
AsyncGetBlobDefer(tag_id, blob_name, data_size, off=0)bytes, after any pending deferred put to the blob completes
AsyncGetBlobSize(tag_id, blob_name, pool_query=Dynamic())int
AsyncGetContainedBlobs(tag_id, pool_query=Broadcast())list[str]
AsyncDelBlob(tag_id, blob_name)None; wait() gives the return code
AsyncReorganizeBlob(tag_id, blob_name, new_score)None
AsyncTagQuery(tag_regex, max_tags=0, pool_query=Broadcast())list[str]
AsyncBlobQuery(tag_regex, blob_regex, max_blobs=0, pool_query=Broadcast())list[tuple[str, str]]
AsyncSemanticSearch(tag_regex, blob_regex, query_text, k=10, pool_query=Broadcast())list[SemanticSearchResult]
AsyncTemporalSearch(tag_regex, blob_regex, time_begin=0, time_end=0, max_entries=0, pool_query=Broadcast())list[TemporalSearchResult]
AsyncPollTelemetryLog(minimum_logical_time)list[CteTelemetry]
AsyncRegisterTarget(target_name, bdev_type, total_size, target_query=Local(), bdev_id=UniqueId.GetNull(), pool_query=Dynamic(), attach_existing=0, growth_unit=1<<30)None; wait() gives the return code
AsyncGetCapacity(pool_query=Local())CapacityInfo; raises RuntimeError on a failed query
AsyncListTargets(pool_query=Local())list[str]; Broadcast appends names from replicas without deduplication
AsyncStatTargets(pool_query=Local())None; requests a one-shot statistics refresh
AsyncGetTargetInfo(target_name, pool_query=Local())TargetInfo; raises RuntimeError on a failed query or a missing target

Future

  • wait(max_sec=-1): blocks until done; returns the return code, or None if max_sec (when positive) elapses first.
  • done(): True when the task has completed.
  • result(max_sec=-1): blocks, then returns the value in the table above. Raises TimeoutError if max_sec elapses.

Deferred puts

  • AsyncPutBlobDefer(tag_id, blob_name, data, off=0, max_inflight_bytes=0) -> int: stages a copy of data in shared memory and returns 0 when submitted. data is free to reuse as soon as the call returns. When shared memory is exhausted the call waits for the oldest puts; max_inflight_bytes adds an explicit ceiling (0 means none).
  • AwaitPutsUntilSpace(max_inflight_bytes) -> int: waits until at most that many payload bytes are in flight (0 drains all) and returns the bytes still in flight.
  • DeferErrorCount() -> int: deferred puts that completed with a non-zero return code. The count is sticky.

Supporting types

  • UniqueId(major, minor): the tag id, with major_, minor_, IsNull(), ToU64() and the static GetNull().
  • PoolQuery.Broadcast(), PoolQuery.Dynamic(), PoolQuery.Local(parallelism=32).
  • SemanticSearchResult: tag_id, blob_name, score. TemporalSearchResult: tag_id, blob_name, last_modified.
  • CteTelemetry: op_ (a CteOp), off_, size_, tag_id_, mod_time_, read_time_, logical_time_.
  • CapacityInfo: total_capacity, remaining_capacity. TargetInfo: target_score, remaining_space, bytes_read, bytes_written, ops_read, ops_written.
  • Enums: RuntimeMode (kClient, kServer, kRuntime), BdevType (kFile, kRam), CteOp (kPutBlob, kGetBlob, kDelBlob, kGetOrCreateTag, kDelTag, kGetTagSize).

Example

python
tag = cte.Tag("experiment/run1")
tag.PutBlob("note0", b"Arctic sea ice extent fell to a record low.", 0)
tag.PutBlob("note1", b"Atmospheric CO2 passed 420 ppm.", 0)

for tag_name, blob_name in client.BlobQuery("experiment/.*", "note.*"):
    print(tag_name, blob_name)

for hit in client.SemanticSearch("experiment/.*", ".*", "sea ice", k=5):
    print(hit.blob_name, hit.score)

tag.ReorganizeBlob("note1", 0.0)

futs = [client.AsyncGetBlob(tag.GetTagId(), n, tag.GetBlobSize(n), 0) for n in ("note0", "note1")]
payloads = [f.result() for f in futs]

client.DelBlob(tag.GetTagId(), "note0")