This release is a pre-release and may not be stable for production use.
guatiao for Python
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/listdata. - Numbers keep their exact text —
1.10stays1.10and an integer of any size survives; you chooseint/float,Decimalorstrwhen 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-serdeandguatiao-intakelibraries 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.collect() # ["acme"]: closed, once nothing of it is alive
reg.unload("acme2") # out and unmapped at once, when it agrees
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 intake, serde
value = serde.loads('{"port": 5900}') # format="json" by default
text = serde.dumps(value, format="yaml")
pretty = serde.dumps(value, pretty=True)
problem = intake.check(schema, form_doc) # None when the form fits the schema
sections = intake.layout(schema, form_doc)
shown = intake.is_visible(schema, form_doc, "tls.verify", values)
# A front end that keeps text under flat keys (`agent[0].name`, `env[PATH]`):
field = intake.resolve(schema, "auth.password") # the schema field, or None
keys = intake.flat_keys(schema, "auth") # ["auth", "auth.username", ...]
store = intake.flatten(schema, "auth", auth_value) # {"auth": "userpass", ...}
back = intake.unflatten(schema, "auth", store) # a Value, or None
A document that does not parse raises guatiao.errors.ParseError, a
ValueError carrying format, line and column (from 1; 0 when the
parser did not say where). 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:
1e400parses as1e+400. Everything else about a number's text survives a round trip. List.insertandlist[i] = xrebuild the list, because the C ABI only appends and removes.appendanddel list[i]are direct.- Runs where CPython can load a native library, on 64-bit targets only:
every pointer and
size_tin the ABI is eight bytes. It does not run in a browser (Pyodide has no way to load the library) or on a 32-bit interpreter. - A
Registryis used from one thread at a time, and aValueis not shared between threads without a lock of your own. Registry.unloadunmaps a library once its ownunloadfunction agrees, andcollecta retired one; neither can see a table orctxyou took withprovider_table. Let those go first: they are valid until the library is closed, byunload, or once retired bycollector the registry's next load, retire or unload.
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, at, for_schema, form_for, resolve, flat_keys, flatten, unflatten |
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.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| guatiao-0.0.0a1.tar.gz | 42.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| guatiao-0.0.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 82.1 kB
Release files / guatiao-0.0.0a1.tar.gz
| Download URL | guatiao-0.0.0a1.tar.gz |
|---|---|
| Size | 42.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e8d6ad40e3aec742625ff3397fe131ff5fd8e904242f312838856a3678884895
|
|
BLAKE2b-256 checksum How to use checksums |
1fd155d29898e51a4c595e0deaa33d673e2695f069fc1880864c3f8ea069180e
|
| 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 Oct 19, 2026.
Transparency logRelease files / guatiao-0.0.0a1-py3-none-any.whl
| Download URL | guatiao-0.0.0a1-py3-none-any.whl |
|---|---|
| Size | 39.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
73585e6056c943050f140a4cf1282afab6bd37b01d03c81effab3fca46f9d6ae
|
|
BLAKE2b-256 checksum How to use checksums |
ab65f4003952db72a99d83407679852cca627661382e5fac54d8b03382a6ce4a
|
| 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 Oct 19, 2026.
Transparency logSecurity for guatiao 0.0.0a1
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.
Attestation for guatiao-0.0.0a1.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 Oct 19, 2026.
Transparency logA 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.