Skip to main content

dataoil

The Data Oil client for Python. One statement over rows, vectors, keywords and relationships, streamed as NDJSON, with the two tenancy headers, typed errors and the retry a lost race needs built in. Standard library only; Python 3.10+.

pip install dataoil
pip install "dataoil[langchain]"     # + a LangChain retriever
pip install "dataoil[llamaindex]"    # + a LlamaIndex retriever

Sixty seconds

from dataoil import DataOilClient

client = DataOilClient("https://acme.dataoil.tech", "bookshop", username="your-account", password="...")
# or, with an API key:  DataOilClient("https://acme.dataoil.tech", "bookshop", token="dot_...")

client.statement("INSERT INTO Book SET isbn = :isbn, title = :title", {"isbn": "978-py", "title": "From Python"})

for row in client.statement("SELECT isbn, title FROM Book ORDER BY isbn"):
    print(row["isbn"], row["title"])

# A large scan streams, and follows the cursor across pages.
for row in client.query("SELECT FROM Report WHERE filedAt > :since", {"since": "2026-01-01"}, page_size=2000):
    handle(row)

# One statement over keywords, meaning and a column.
hits = client.statement("""
    SELECT title, VECTOR_DISTANCE(embedding, EMBED(:q), 'cosine') AS distance, $score AS keywordScore
    FROM Report
    WHERE SEARCH_INDEX('Report[body]', :q) = true AND filedAt > date('2026-01-01')
    ORDER BY distance LIMIT 10
""", {"q": "casing failure"})

What it does for you

The two headers X-Job-ID and X-Database on every call. The engine refuses a call without either, one at a time. client.with_job("nightly-42") names a batch.
NDJSON, streamed query() yields rows as they arrive and sends the cursor back for the next page. The trailer line is never a row. statement() reads one page into a list and raises IncompleteResultError rather than pretend a page that did not finish was the whole answer.
Typed errors Every refusal is a DataOilError with status, error_code (the stable code, e.g. TypeNotFound, RequestQuotaExceeded), detail, correlation_id, arguments (a quota's used, allowed, resetsAt) and retryable. Four subclasses: DataOilAuthorizationError (401/403), DataOilNotFoundError (404), DataOilConflictError (409/412), DataOilQuotaError (429/507, with retry_after).
The 409 retry A lost race is answered 409 with X-Retryable: true; nothing was written, and the same request a moment later succeeds. It is retried with full jitter on its own budget (RetryPolicy(attempts=5, base_delay=0.1, max_delay=5.0)). A duplicate key (X-Retryable: false) is never retried. 429 waits what Retry-After says when that is inside the budget; 503 is retried like a race. Nothing else is.

Retrievers: the AI project that does not start with an export

from dataoil import DataOilClient, Search
from dataoil.langchain import DataOilRetriever          # or dataoil.llamaindex

client = DataOilClient("https://acme.dataoil.tech", "kb", token="dot_...")
search = Search(client, "customer_docs", embed=my_embedding_model)   # the same model the ingestion used
retriever = DataOilRetriever(search=search)

docs = retriever.invoke("what changed in the March release?")

Search asks the database for the passages nearest a question: by keyword through the full-text index (SEARCH_INDEX, ranked by BM25), by meaning through the vector index (vector.neighbors), or both fused by rank (mode="hybrid", the default when an embed function is given). Every hit carries the record it came from, so a chain cites the row rather than a copy of it - and every fence the engine applies to a statement applies here, because a retrieval is a statement. Nothing is copied out.

The rest of the surface

record = client.create("Book", {"isbn": "1", "title": "A"})     # {rid, type, properties}
client.get(record["rid"]); client.delete(record["rid"])
client.batch([BatchOperation("INSERT INTO Book SET isbn = :i", {"i": "1"}), BatchOperation("INSERT INTO Book SET isbn = :i", {"i": "2"})])
tx = client.begin(); client.statement("INSERT INTO Book SET isbn = '3'", transaction=tx); client.commit(tx)   # or rollback
client.schema(); client.schema_narrative()
client.mint_token("the reporting job", rights=["read"], lifetime_seconds=30 * 86400)
client.ask("which reports mention casing?", rows, statement=sql)     # the tenant's own model, sent only the rows you read
client.request("GET", "/api/v2/databases/bookshop/statistics")

ca_file= names a certificate to trust for a deployment on its own authority.

Tests

pytest runs 28 checks against a stub that answers exactly what the engine answers - NDJSON with its trailer, problem documents with X-Retryable, a 429 with Retry-After. Set DATAOIL_URL, DATAOIL_DATABASE and a credential (and DATAOIL_CA for a self-signed deployment) and one more creates a type, writes, streams, searches, refuses and drops against a running engine.

The reference manual is at https://documents.dataoil.tech/tenant/.

Source and licensing

Questions, issues and releases: https://documents.dataoil.tech/tenant/ and info@dataoil.tech. The packages are published to the public registries as compiled packages; their source is not published.

This library is proprietary and licensed only for legitimate use with Data Oil, under the Data Oil Software Licence (see LICENSE): copying, modifying, redistributing or reverse engineering it, and any attack on or unauthorised access to the platform, are prohibited. Data Oil itself is a commercial service of MaxDragon Technology, and using it needs a credential issued under the Data Oil customer agreement. The library grants no right to the service, and the service applies its own rights, budgets and audit to every credential. The NOTICE file in the package has the full statement, the trademark note and the third-party components.

Metadata

Release files for dataoil 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for dataoil 0.1.5
File Interpreter ABI Platform
dataoil-0.1.5-py3-none-any.whl Python 3 none any Details

Release files / dataoil-0.1.5-py3-none-any.whl

Download URL dataoil-0.1.5-py3-none-any.whl
Size 21.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c8395ec897c8791c0d43ab16f6df4ebf2e6931448ec3ddbe91ae774526c6fe44
BLAKE2b-256 checksum
How to use checksums
1cbb7799ab286beb6164ec2d6f6639c5ab5f3fe1227bb15500b37b5f0bd80d93
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Security for dataoil 0.1.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.5 This release

1 release file No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page