Skip to main content

datalogic-py

PyPI CI License: Apache 2.0

Part of datalogic-rs — one engine, every runtime.

Python bindings for datalogic-rs, a fast Rust implementation of JSONLogic. Same rules, same semantics as the Rust crate, with the compile-once / evaluate-many pattern exposed natively — compile a rule once and evaluate it against thousands of data inputs without re-parsing. Every binding runs the same core and passes the same 1,636-case conformance battery (58 suites).

For the cross-runtime overview and the API-tier model every binding implements, see the repo README.

New in v5. datalogic-py is new — there is no v4 Python package. If you were calling the v4 Rust crate or the v4 @goplasmatic/datalogic WASM package, the engine's v4 → v5 changes are catalogued in MIGRATION.md.

Install

pip install datalogic-py

Pre-built wheels are published for:

Platform Architectures
Linux (manylinux) x86_64, aarch64
Linux (musllinux) x86_64, aarch64
macOS x86_64, arm64
Windows x86_64, arm64

Python 3.10 and newer are supported via PEP 384 stable ABI (abi3) — one wheel per platform covers every CPython 3.10+ release.

The package is fully typed (PEP 561): every wheel ships type stubs and a py.typed marker, so mypy, pyright, and IDE autocomplete see the complete API surface out of the box.

Naming: pip install datalogic-py (PyPI distribution name) → import datalogic_py (Python module name). Python modules can't contain hyphens, so the underscore form is the import.

Quick start

from datalogic_py import apply

result = apply(
    {"if": [{">": [{"var": "score"}, 50]}, "pass", "fail"]},
    {"score": 75},
)
# -> "pass"

API reference

The Python binding mirrors the Rust engine's API tier model.

Tier Entry point Use when
One-shot apply(rule, data) Ad-hoc evaluation, one rule + one data shape
Engine Engine().eval(rule, data) Custom configuration (templating, custom operators, config)
Compile once Engine().compile(rule).evaluate(data) Same rule evaluated against many data inputs
Session with engine.session() as sess: … Hot loops — amortise arena reset across iterations
Data handle DataHandle(json)sess.evaluate_data(rule, data) Same payload evaluated many times: parse once, zero parse work per call
Typed sess.evaluate_bool/int/float/truthy(rule, data) Predicates and scalar results, no JSON decode on the way out
Batch sess.evaluate_batch(rule, datas) / sess.evaluate_many(rules, data) Many evaluations per native call, per-item errors

One-shot — apply(rule, data)

from datalogic_py import apply

apply({"+": [1, 2, 3]}, {})                                # 6
apply({"var": "user.age"}, {"user": {"age": 25}})          # 25
apply({"and": [{">": [{"var": "x"}, 0]}, True]}, {"x": 5}) # True

Both arguments accept Python dict / list values, converted by a direct walk between Python objects and the engine's arena values (no JSON text, no intermediate tree — 2.5-3.5× faster than the pythonize-based conversion earlier builds used, and faster than a json.dumpsevaluate_strjson.loads round-trip at every payload size we measure). Payload size still matters: conversion work scales with node count, so an 8 KB dict costs ~20 µs of walk on top of the evaluation. If your data is already JSON text, call the *_str entry points (Rule.evaluate_str, Session.evaluate_str) and skip conversion; if the same payload is evaluated repeatedly, parse it once into a DataHandle and skip the per-call cost entirely. For payloads with types the walk doesn't cover, see Type conversion below.

Engine — Engine().eval(rule, data)

Construct an Engine when you need templating mode or any non-default configuration:

from datalogic_py import Engine

engine = Engine()                          # default config
engine.eval({"==": [1, 1]}, {})            # True

# Templating mode — multi-key objects become output templates
templating_engine = Engine(templating=True)
templating_engine.eval(
    {"name": {"var": "user.name"}, "ok": {">": [{"var": "score"}, 50]}},
    {"user": {"name": "Ada"}, "score": 99},
)
# {"name": "Ada", "ok": True}

Compile once — Engine().compile(rule)Rule.evaluate(data)

Compile the rule once when you'll evaluate it against many data inputs.

from datalogic_py import Engine

engine = Engine()
rule = engine.compile({"if": [{">": [{"var": "score"}, 50]}, "pass", "fail"]})

for payload in batch:
    result = rule.evaluate(payload)         # accepts a dict
    fast   = rule.evaluate_str(json_text)   # accepts a JSON string (skips dict conversion)

Rule is thread-safe — clone the reference into worker threads and evaluate concurrently. The Rust eval call releases the GIL, so a multi-threaded server gains real parallelism.

Session — hot loops

For batches where you want to amortise arena reset across iterations, open a Session. The arena is reset between iterations automatically.

from datalogic_py import Engine

engine = Engine()
rule = engine.compile({"+": [{"var": "x"}, 1]})

with engine.session() as sess:
    for payload in batch:
        result = sess.evaluate(rule, payload)

Session is the per-thread workhorse — open one per worker thread. The arena that makes it fast can't be shared across threads (the same way a database connection is per-task in a connection-pool model); Engine and Rule are both thread-safe, so share those.

Data handles, typed results, and batch evaluation

The ABI v2 tiers. A DataHandle is an immutable, pre-parsed JSON document: parse a payload once and every evaluation against it skips JSON parsing (and dict conversion) entirely. Handles are engine-independent (one handle can feed rules compiled by different engines), safe to share across threads for reads, and not consumed by evaluation — the native memory is released when the handle is garbage-collected.

from datalogic_py import DataHandle

data = DataHandle('{"age": 25, "status": "active"}')  # raises ParseError on bad JSON
data.allocated_bytes                    # bytes held by the handle's arena

rule.evaluate_data(data)                # thread-safe, like rule.evaluate
rule.evaluate_data_str(data)            # same, JSON str out
sess.evaluate_data(rule, data)          # hot path: session arena + no parse
sess.evaluate_data_str(rule, data)

For predicates and scalar results, the typed session evaluations skip the result conversion too:

ok = sess.evaluate_bool(rule, data)     # strict JSON boolean
n  = sess.evaluate_int(rule, data)      # exact integer result
f  = sess.evaluate_float(rule, data)    # any JSON number
t  = sess.evaluate_truthy(rule, data)   # JSONLogic truthiness, never mismatches

evaluate_bool, evaluate_int, and evaluate_float raise EvaluateError with error_type == "TypeMismatch" when the rule evaluates fine but the result is not of the requested type. evaluate_truthy coerces any result through the engine's configured truthiness rules (the same coercion if/and/or apply).

The batch entry points evaluate a whole set in one native call and report failures per item, so one bad input never poisons its neighbours:

from datalogic_py import BatchItemError

# One rule, many payloads:
results = sess.evaluate_batch(rule, [d0, d1, d2])
# Many rules, one payload (the rule-set / feature-flag shape):
results = sess.evaluate_many([r0, r1], data)

for i, r in enumerate(results):
    if isinstance(r, BatchItemError):   # not raised — a result object
        print(f"item {i} failed: {r.message} ({r.tag}, operator={r.operator})")
    else:
        print(f"item {i}: {r}")         # the item's JSON string

Exceptions are reserved for argument problems (a rule compiled by a different engine, a non-handle list element, …). Typed and batch evaluations take data handles only; rules must belong to the session's engine, and sessions stay single-threaded.

Custom operators

Pass custom_operators={"name": callable} to Engine(...). Each callable receives the operator's pre-evaluated arguments as a JSON-array string and returns a JSON string of the result:

import json
from datalogic_py import Engine

engine = Engine(custom_operators={
    "double": lambda args_json: json.dumps(json.loads(args_json)[0] * 2),
})
engine.eval_str('{"double": [21]}', '{}')  # "42"

Built-ins win: a custom registration of a built-in name (+, if, var, ...) never dispatches. Callbacks run with the GIL held.

Engine configuration

Pass config= to Engine(...) to change evaluation semantics. The value is a dict (or a JSON string) with an optional preset plus per-field overrides. Unknown keys raise EvaluateError, so typos fail loudly:

from datalogic_py import Engine, EvaluateError

strict = Engine(config={"preset": "strict"})
try:
    strict.eval({"+": ["", 1]}, {})   # strict rejects non-numeric coercion
except EvaluateError as e:
    print(e.error_type)

lenient = Engine(config={"division_by_zero": "return_null"})
lenient.eval({"/": [1.5, 0]}, {})     # None
Key Values
preset "default", "safe_arithmetic", "strict"
arithmetic_nan_handling "throw_error", "ignore_value", "coerce_to_zero", "return_null"
division_by_zero "return_saturated", "throw_error", "return_null", "return_infinity"
loose_equality_errors bool
truthy_evaluator "javascript", "python", "strict_boolean"
numeric_coercion object of bools: empty_string_to_zero, null_to_zero, bool_to_number, reject_non_numeric
max_recursion_depth integer >= 1

The preset applies first; the remaining keys override individual fields on top of it. Every binding shares this JSON schema and parses it with the same core code, so a config that works here works in the WASM and Node bindings too. The full semantics of each knob are documented on the Rust crate's EvaluationConfig.

Error handling

All exceptions descend from DataLogicError:

Exception When
ParseError Malformed rule or data JSON, or an unsupported Python type in the input
EvaluateError Operator failure at runtime (including unknown operators, tag InvalidOperator) — carries .error_type, .operator, .path

Two error_type tags come from the binding itself rather than the engine, mirroring the C ABI: "TypeMismatch" (a typed evaluation whose result has the wrong type) and "InvalidArgument" (e.g. a rule compiled by a different engine passed to a session's handle-based entry points). Per-item batch failures don't raise at all — they surface as BatchItemError values (.tag, .message, .operator) in the result list.

from datalogic_py import Engine, EvaluateError

engine = Engine()
try:
    engine.eval({"+": ["x", 1]}, {})  # arithmetic on a non-numeric string raises
except EvaluateError as e:
    print(e.error_type)  # a runtime error tag
    print(e.operator)    # "+"
    print(e.path)        # JSON-pointer-style path through the compiled tree

Threading

Type Pattern
Engine Build once; share across threads
Rule Compile once; share across threads — evaluate releases the GIL for parallelism
Session One per worker thread — the per-task workhorse
DataHandle Parse once; immutable, share across threads for reads (evaluation never mutates it)

Type conversion

The dict-input path walks Python objects directly into the engine's arena representation (with a pythonize fallback for the long tail — behaviour is identical either way, only speed differs):

Fast direct walk: dict, list, tuple, str, int, float, bool, None.

Handled via the fallback: set/frozenset (become JSON arrays, iteration order), container/scalar subclasses (IntEnum, OrderedDict, …), mappings and dataclasses.

Conversion details worth knowing:

  • float('nan') / float('inf') become JSON null (they have no JSON encoding)
  • ints above 2^63 - 1 up to 2^64 - 1 degrade to float; beyond that they raise ParseError
  • dict keys must be str (anything else raises ParseError) and objects are presented to the engine in sorted-key order, so object-iteration results are deterministic
  • result dicts also come back key-sorted

Not supported — these raise ParseError with a clear message:

  • datetime.datetime, datetime.date — convert to ISO string at the Python edge
  • decimal.Decimal — convert to float or str
  • bytes, bytearray

For payloads with exotic types, use rule.evaluate_str(json_text) and bring your own JSON encoder (e.g. with default=str).

Templating mode

engine = Engine(templating=True)
rule = engine.compile({
    "name": {"var": "user.name"},
    "ok": {">": [{"var": "score"}, 50]},
})
rule.evaluate({"user": {"name": "Ada"}, "score": 99})
# -> {"name": "Ada", "ok": True}

Execution tracing

Engine.evaluate_with_trace(logic, data) evaluates with step-by-step tracing and returns a JSON string envelope. The shape is identical to the WASM binding's evaluateWithTrace, so the React debugger component can consume it directly:

import json
from datalogic_py import Engine

engine = Engine()
trace = json.loads(engine.evaluate_with_trace(
    '{">": [{"var": "score"}, 50]}',
    '{"score": 75}',
))
trace["result"]           # True
trace["expression_tree"]  # {"id", "expression", "children"} tree
trace["steps"]            # per-node execution log, in evaluation order

Both arguments are JSON strings. Runtime failures do not raise: the envelope carries an error message and a structured_error object instead, alongside the steps recorded up to the failure. Tracing skips the optimizer so every operator in the rule appears in the trace; use it for debugging, not hot paths.

Performance

Geomean across 51 operator benchmark suites (Apple M2 Pro, median of 3 runs; pairwise shared-suite ratios per the methodology): the native Rust core evaluates at 10.3 ns/op, 7.0× faster than json-logic-engine (compiled, the fastest JS engine), 28.1× faster than jsonlogic-rs (the closest Rust alternative), and 83.6× faster than the json-logic-js reference implementation. The WASM build under Node measures 900.5 ns geomean (88× native); on Node servers, prefer @goplasmatic/datalogic-node.

The pyo3 boundary adds a small per-call marshalling cost on top of the core numbers; the dict paths use direct Python ↔ arena walks, so that cost scales with payload node count, not with a JSON round-trip. Use rule.evaluate_str(json_text) when you already have a JSON string, and a DataHandle when the same payload is evaluated repeatedly — on the boundary harness's 8 KB workload, session.evaluate_data_str measures ~1.3 µs/op against ~12 µs for session.evaluate_str (the per-call JSON parse) and ~24 µs for the dict path (the per-call conversion walk). Every evaluate call releases the GIL, so a multi-threaded server gains real parallelism on top of the engine's native speed.

Learn more

License

Apache-2.0. See the main repository for source and contribution guidelines.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

datalogic_py-5.2.0.tar.gz (354.6 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

datalogic_py-5.2.0-cp310-abi3-win_arm64.whl (1.2 MB view details)

Uploaded CPython 3.10+Windows ARM64

datalogic_py-5.2.0-cp310-abi3-win_amd64.whl (1.5 MB view details)

Uploaded CPython 3.10+Windows x86-64

datalogic_py-5.2.0-cp310-abi3-musllinux_1_2_x86_64.whl (1.8 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

datalogic_py-5.2.0-cp310-abi3-musllinux_1_2_aarch64.whl (1.5 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

datalogic_py-5.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.6 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

datalogic_py-5.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.3 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

datalogic_py-5.2.0-cp310-abi3-macosx_11_0_arm64.whl (1.2 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

datalogic_py-5.2.0-cp310-abi3-macosx_10_12_x86_64.whl (1.5 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file datalogic_py-5.2.0.tar.gz.

File metadata

  • Download URL: datalogic_py-5.2.0.tar.gz
  • Upload date:
  • Size: 354.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for datalogic_py-5.2.0.tar.gz
Algorithm Hash digest
SHA256 19b10b926b2ca2e1bd1519bc94f3f33fdc2771a1194c84c427232042f2266ca0
MD5 5f1a8eb8b300f22defff4e631514c05b
BLAKE2b-256 27cc0ebb8f4a11792436d45fc548b2986bd90dca54442c56c23bb6fa31a81a69

See more details on using hashes here.

Provenance

The following attestation bundles were made for datalogic_py-5.2.0.tar.gz:

Publisher: release.yml on GoPlasmatic/datalogic-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datalogic_py-5.2.0-cp310-abi3-win_arm64.whl.

File metadata

File hashes

Hashes for datalogic_py-5.2.0-cp310-abi3-win_arm64.whl
Algorithm Hash digest
SHA256 3f34ed64457c1a8edb2f74e71fe60c8bf58f59c56f03d6fd12cdd0a5d13f0b90
MD5 c7caaf05ee22320407cc79363c1c9333
BLAKE2b-256 2ebc6a5c171e7a960e8a0f471965472afd9cbe61a7bb3695000c56af0944d15c

See more details on using hashes here.

Provenance

The following attestation bundles were made for datalogic_py-5.2.0-cp310-abi3-win_arm64.whl:

Publisher: release.yml on GoPlasmatic/datalogic-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datalogic_py-5.2.0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: datalogic_py-5.2.0-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for datalogic_py-5.2.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 87f0ba6d1025b57d60749b4613ede1f7386d6d0bf640e0d9fcc9e7995a428352
MD5 241b07903f53e6b1779f58efe251478f
BLAKE2b-256 a311696b356d350a3cfe549e8969a54a0a3edb7871e39df1beaba868123163d4

See more details on using hashes here.

Provenance

The following attestation bundles were made for datalogic_py-5.2.0-cp310-abi3-win_amd64.whl:

Publisher: release.yml on GoPlasmatic/datalogic-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datalogic_py-5.2.0-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for datalogic_py-5.2.0-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 46feffcffe2e7a6068b71ff86c05ca1aab1b86f303179e6842c238b30d8581de
MD5 49db079f39f2a62fd58c2eb36de5b36f
BLAKE2b-256 328330a6f9253c669de9efbaec895d4baa680ac82b43f3c6a295c94627067103

See more details on using hashes here.

Provenance

The following attestation bundles were made for datalogic_py-5.2.0-cp310-abi3-musllinux_1_2_x86_64.whl:

Publisher: release.yml on GoPlasmatic/datalogic-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datalogic_py-5.2.0-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for datalogic_py-5.2.0-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 47388a4abfae8fc7a685d4fbc1a8226c53aa554d5a17814851d2ce31f81276e2
MD5 690cb44f3523a892263562d08f313e98
BLAKE2b-256 246107ea9f0d5dd494fd112a6080bd6fc0389b1df652ffb84898e60ea0479211

See more details on using hashes here.

Provenance

The following attestation bundles were made for datalogic_py-5.2.0-cp310-abi3-musllinux_1_2_aarch64.whl:

Publisher: release.yml on GoPlasmatic/datalogic-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datalogic_py-5.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for datalogic_py-5.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 12619f0cc5bd755dfad4628d64dc69c929cb1034375a25b3b7799a2f92c44c5c
MD5 839cb6bf955453839484fe6e95ca85e6
BLAKE2b-256 9840d2f0468ea08279b49ee17027ca909b38fbc0388026641ec21eff3b2853b4

See more details on using hashes here.

Provenance

The following attestation bundles were made for datalogic_py-5.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on GoPlasmatic/datalogic-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datalogic_py-5.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for datalogic_py-5.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 472ebf66dd84b5150e75f76c89669b6998d1fad94dad95078ba9d539be47f838
MD5 10d2db835c88c7491cc6eaf2a12753f6
BLAKE2b-256 5df58ce04f92b33caf00a7ad091ffd41334b7370dce79b890d0849bba06dc762

See more details on using hashes here.

Provenance

The following attestation bundles were made for datalogic_py-5.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on GoPlasmatic/datalogic-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datalogic_py-5.2.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for datalogic_py-5.2.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f85320f6218e033f4cd619861cecc4ebac1e2570053f164e2a2668483694ff48
MD5 99e49e247c4a802e2ba0f1f4b1b151bc
BLAKE2b-256 bb8b05e6191ec1f6820f9cb71307d62a046707c30fa2a7a2af15eb7d4725d533

See more details on using hashes here.

Provenance

The following attestation bundles were made for datalogic_py-5.2.0-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on GoPlasmatic/datalogic-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datalogic_py-5.2.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for datalogic_py-5.2.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 f05381b3c91f9e5a90740f45efe0002a3ec0d05280916c5381342d1815e4e645
MD5 8ca2010d7c142d31339a7d6cc410ab20
BLAKE2b-256 e61c5e1aafed7c0bbf2e6773cde596b87c83b42a68ff66d2f2f64793b263fcf2

See more details on using hashes here.

Provenance

The following attestation bundles were made for datalogic_py-5.2.0-cp310-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on GoPlasmatic/datalogic-rs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page