Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

guatiao for Python

PyPI version Python versions License: MPL 2.0 CI

Python bindings for the guatiao ABI: one value model for passing data between languages, a JSON Schema that describes a value, and a registry that loads plugin libraries. Pure Python over ctypes, with no dependencies and nothing to compile. It works with any shared library that exports the ABI, whether that is guatiao itself or an application that carries it.

Status: alpha. A pure Python package: it carries no native library, you point it at one.

Features

  • Value trees as Python objects — build, read and change a tree in place, or convert it to and from plain dict/list data.
  • Numbers keep their exact text — 1.10 stays 1.10 and an integer of any size survives; you choose int/float, Decimal or str when reading.
  • Be a plugin host — scan a directory, filter on what a library declares before loading it, list providers best first, build an instance from a configuration, call a provider through its table.
  • JSON, TOML and YAML, and form checks against a schema, through the guatiao-serde and guatiao-intake libraries when they are present.
  • Importing never fails — a native library is looked for on first use, and a missing one says every place that was searched.

Installation

pip install guatiao

The package drives a shared library that exports the guatiao ABI. Say where it is:

export GUATIAO_LIBRARY=/opt/myapp/lib             # a directory holding guatiao.dll / libguatiao.so / libguatiao.dylib
export GUATIAO_LIBRARY=/opt/myapp/myapp.dll       # or the file itself, whatever it is called

Without the variable the package tries the system's library search (ctypes.util.find_library("guatiao")). Nothing is loaded at import time: import guatiao always succeeds, and the first native call raises guatiao.LibraryNotFound, naming every place it looked. A library that exports only part of the ABI is fine: calling a function it lacks raises guatiao.MissingSymbol naming it, and everything else works.

Exports from Needed for
guatiao values, the registry, provider tables
guatiao_serde guatiao.serde: JSON, and TOML and YAML when the library has them
guatiao_intake guatiao.intake

guatiao_serde and guatiao_intake are looked for the same way, in the same directory, only when guatiao.serde or guatiao.intake is first used.

Quick start

Values

from guatiao import Value

with Value(host="10.0.0.1", port=5900, tags=["a", "b"]) as v:
    m = v.as_map()
    m["port"] = 5901
    m["tags"].as_list().append("c")

    m["port"].to_python()        # 5901
    list(m)                      # ['host', 'port', 'tags'], insertion order
    "missing" in m               # False; m["missing"] raises KeyError
    m["port"].int_or(22)         # 5901, or 22 if it were not an integer
    v.to_python()                # back to a plain dict

A Value owns its tree and frees it on close() or at the end of the with block. m["tags"] is a Ref: a path into the tree that is walked again on every access, so it stays valid while the tree grows around it.

Numbers keep their exact text. 1.10 stays 1.10, and an integer of any size survives. Choose what comes back when reading:

v = Value.number("1.10")
v.to_python()                    # 1.1   ("auto": int when it looks like one, else float)
v.to_python(numbers="decimal")   # Decimal('1.10')
v.to_python(numbers="str")       # '1.10'

Value(1), Value(True), Value("x"), Value([1, 2]) and Value({"host": "h"}, port=1) all work, as the builtins would. None becomes null, bytes the bytes kind, a dict with str keys a map. A missing value reads back as guatiao.ABSENT, which is falsy and is not None. A float that is nan or infinite raises ValueError.

Hosting plugins

from guatiao import Registry

with Registry("my-host", "1.0") as reg:
    report = reg.scan_dir("/opt/myapp/plugins", rules="kind=greeter")
    for p in reg.providers("greeter"):           # best first
        print(p["key"], p["version"], p["from"])

    reg.why_not("codec")                         # why nothing serves a kind

    schema = reg.provider_config("acme_shouter")   # borrowed; do not close
    with reg.create("acme_shouter", {"prefix": "hey"}) as instance:
        ...

    reg.retire("acme")                           # out of the registry, still mapped
    reg.unload("acme")                           # and unmapped, on your word

Every answer that is data comes back as a plain dict or list. rules is one [!]KEY=VALUE per line and filters on what a library declares about itself, before it is loaded.

To call a provider, describe its table with the ctypes.Structure the kind's C header declares and let the package check it:

from guatiao import kinds

table_ptr, size, ctx = reg.provider_table("acme_greeter", kind="greeter")
greeter = kinds.table(table_ptr, size, GreeterVtable, floor_hash=FLOOR_HASH)
greeter.greet(ctx, name, out_map, err)

tests/test_greeter.py is a complete worked example.

JSON, TOML, YAML and forms

from guatiao import serde, form

value = serde.loads('{"port": 5900}')                  # format="json" by default
text = serde.dumps(value, format="yaml")
pretty = serde.dumps(value, pretty=True)

problem = form.check(schema, form_doc)                 # None when the form fits the schema
sections = form.layout(schema, form_doc)
shown = form.is_visible(schema, form_doc, "tls.verify", values)

A format the loaded library does not export raises NotImplementedError naming it.

Errors

A failed native call raises guatiao.GuatiaoError, as one of BadValue, AllocFailed, WrongKind, NotFound, NullArgument, Gone, Unsupported, Busy or Internal. .status is the C status code, and .message carries a provider's own words when it gave any.

Known limits

  • Reading JSON writes an exponent's sign: 1e400 parses as 1e+400. Everything else about a number's text survives a round trip.
  • List.insert and list[i] = x rebuild the list, because the C ABI only appends and removes. append and del list[i] are direct.
  • A Registry is used from one thread at a time, and a Value is not shared between threads without a lock of your own.
  • Registry.unload unmaps a library, and nothing can check that you have released what you took from it first -- every value it built through its own allocator, every table, ctx and Instance. retire has no such condition and leaves the library mapped.

API overview

Module Purpose
guatiao Value, Ref, Map, List, ABSENT, Registry, Instance, the error classes
guatiao.registry the plugin host: load, scan, list, rank, configure, instantiate
guatiao.kinds check and cast a provider's function table
guatiao.serde loads / dumps for JSON, TOML, YAML
guatiao.intake check, layout, is_visible

AGENTS.md is the full API reference and ships inside the wheel. The C ABI is described by guatiao.h.

Development

pip install -e "bindings/python[dev]"     # from the repository root, in a virtual environment
pytest bindings/python/tests -rs

The tests need the three libraries and the repository's example plugins in one directory. conftest.py points GUATIAO_LIBRARY at the repository's build output when the variable is unset; the repository's own AGENTS.md says how to produce it. With everything present nothing skips; a test that needs a missing library skips and says which. CI runs the suite on Python 3.9 and 3.14.

License

MPL 2.0 — see LICENSE.

Metadata

Release files for guatiao 0.0.0a0

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

Source distribution (sdist)

Source distribution for guatiao 0.0.0a0
File Size Uploaded Provenance
guatiao-0.0.0a0.tar.gz 34.7 kB With provenance Details

Built distribution (wheel)

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

Total release size: 68.9 kB

Release files / guatiao-0.0.0a0.tar.gz

Download URL guatiao-0.0.0a0.tar.gz
Size 34.7 kB
Tags Source
SHA-256 checksum
How to use checksums
66878967d7428e78333fefde2b7150c05da92e3cebd7681b109cef9527c65903
BLAKE2b-256 checksum
How to use checksums
be2c2654e63d440ba3cd17a8b22b504eb859dd18fa99f7c2e30b37955d2917d4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / guatiao-0.0.0a0-py3-none-any.whl

Download URL guatiao-0.0.0a0-py3-none-any.whl
Size 34.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4253423c1a33f5862b441486c11d970732206f5d7522946891018df52dbd419d
BLAKE2b-256 checksum
How to use checksums
849734d0b045a5f6ee65aa453b67b5ae4eb973c974adcf83989b72cb8e341484
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Security for guatiao 0.0.0a0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for guatiao-0.0.0a0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

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.

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