Metadata-Version: 2.1
Name: awk-polycall
Version: 2.0.0
Summary: AWK binding for Polycall, installed as source files (not a Python API)
Home-page: https://github.com/obinexus/awk-polycall
Author: Nnamdi Michael Okpala
Author-email: Nnamdi Michael Okpala <okpalan@protonmail.com>
License: MIT
Project-URL: Repository, https://github.com/obinexus/awk-polycall
Project-URL: Core, https://github.com/obinexus/polycall
Keywords: polycall,obinexus,awk
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.8
Description-Content-Type: text/markdown

> **awk-polycall is the AWK binding for [Polycall](https://github.com/obinexus/polycall), distributed through PyPI as source files.** It is not a Python library: `pip install awk-polycall` places the binding's files (commit `f10853722fe1`) inside the package. `python -m awk_polycall path` prints where they are and `python -m awk_polycall extract DIR` copies them out; build and use them as described below.

# awk-polycall

AWK binding for [Polycall](https://github.com/obinexus/polycall) (binding
ABI v1, Polycall >= 1.1.0), published as `awk-polycall`. It has
two layers, because only gawk can load C:

| layer | AWKs | reaches the core | covers |
| --- | --- | --- | --- |
| **gawk extension** `ext/awk_polycall.c` (`@load "awk_polycall"`) | gawk >= 5.0 | in-process: links `libpolycall` (pkg-config `polycall`) | the whole binding ABI: `polycall_ffi_run_config(path, 1)` exactly, describe, call, peer nodes with caller-sized receive buffers, unregister, binary payloads |
| **POSIX library** `src/awk_polycall.awk` | gawk, mawk, busybox awk, any POSIX awk | runs the installed `polycall` CLI through `getline` pipes | version check, config validation (strict mode approximated, see below), call, peer nodes as `polycall peer serve` processes |

## gawk extension

```sh
make ext                          # build/awk_polycall.so (.dll on Windows)
AWKLIBPATH=build gawk -b -f examples/gawk_ext.awk
```

```awk
@load "awk_polycall"
BEGIN {
    if (polycall::run_config("awk-polycallrc") != polycall::OK) {      # = polycall_ffi_run_config(path, 1)
        print polycall::strerror(polycall::STATUS) ": " polycall::last_error() > "/dev/stderr"
        exit 1
    }
    inbox  = polycall::peer_open("inbox", "127.0.0.1:0")             # handle > 0, or a negative status
    outbox = polycall::peer_open("outbox")                           # no bind = send-only
    polycall::peer_send(outbox, polycall::peer_endpoint(inbox), "hello", "m-1")
    if (polycall::peer_recv(inbox, 5000, msg) == polycall::OK)
        print msg["from"], msg["id"], msg["payload"], msg["length"]
    polycall::peer_close(outbox); polycall::peer_close(inbox)
}
```

All functions live in the `polycall::` namespace and map 1:1 onto
`polycall.h`:

| function | returns |
| --- | --- |
| `abi_version()`, `version()` | `polycall_ffi_abi_version()`, `polycall_ffi_version()` |
| `strerror(code)`, `last_error()` | the core's status name / the latest failure's detail |
| `run_config(path [, run = 1])` | status |
| `describe(path)` | JSON text (`""` on failure) |
| `call(endpoint, service, op [, input_json [, timeout_ms [, result]]])` | status; `result["output"]` = output JSON or the error object, `result["length"]` |
| `peer_open(node_id [, bind [, token]])` | handle (> 0) or status (< 0); `bind` `""`/absent = send-only |
| `peer_close(h)`, `peer_register(h, id, ep)`, `peer_unregister(h, id)`, `peer_ping(h, peer [, timeout])`, `peer_cancel(h)` | status |
| `peer_endpoint(h)`, `peer_node_id(h)`, `peer_list(h)`, `peer_health(h)` | text (`""` on failure) |
| `peer_send(h, peer, payload [, message_id [, timeout_ms]])` | status; `payload` is binary-safe |
| `peer_recv(h [, timeout_ms [, msg [, payload_cap]]])` | status; `msg["from"]`, `msg["id"]`, `msg["payload"]`, `msg["length"]`; a message larger than `payload_cap` (default 1 MiB) gives `E_TOO_LARGE`, `msg["length"]` = needed, and stays queued |

Every call sets `polycall::STATUS` (0 = `polycall::OK`, negative =
`polycall::E_*`, all 19 codes are defined); `polycall::FOREVER`
(4294967295) waits indefinitely. Arguments the C API cannot take (a path
with a NUL byte, a timeout outside 0..2^32-1, a fractional number) are
refused by the binding with `E_INVALID_ARGUMENT` and a detail in
`last_error()`. Payloads are byte strings: run gawk with `-b` (or
`LC_ALL=C`) so `length()`/`substr()` count bytes, and set `BINMODE = 3` on
Windows to read binary files without CRLF translation. A gawk program has
one thread, so a blocked `peer_recv` ends on a message, its timeout, or the
node's close; `peer_cancel` exists for completeness.

Loading: the extension checks `polycall_ffi_abi_version() == 1` before it
registers anything (otherwise it prints why and defines no function), and
on Linux it is linked with `-z now`, so an old 1.0 `libpolycall.so.1`
without the ABI v1 symbols fails at load time ("undefined symbol"), never at
a later call. The module is named `awk_polycall`, not `polycall`: on
Windows a DLL called `polycall.dll` would shadow the MSVC core of the same
name.

Building needs gawk's `gawkapi.h`, a C compiler and the core's pkg-config
file (or `POLYCALL_CFLAGS` / `POLYCALL_LIBS`). On Windows the extension
is built with MSYS2 UCRT64 gcc and loaded by Git-for-Windows or MSYS2 gawk
(both API 4.1); give `GAWK_CFLAGS=-I<dir holding only gawkapi.h>` so the
MSYS headers next to it do not replace the mingw ones, and link either core:

```sh
make ext CC=gcc GAWK_CFLAGS=-I/path/to/gawkapi-dir \
  POLYCALL_CFLAGS=-I$CORE/include/polycall POLYCALL_LIBS="-L$CORE/lib -lpolycall"
```

## POSIX library

```sh
awk -v polycall_cli=/path/to/polycall -f src/awk_polycall.awk -f examples/basic.awk
```

Functions: `polycall_check()`, `polycall_version()`, `polycall_strerror()`
(the status name), `polycall_last_error()`, `polycall_run_config(path
[, strict])`, `polycall_call(endpoint, service, op, input, timeout)`
(output in `POLYCALL_OUTPUT`), `polycall_peer_open/close/endpoint/node_id/
register/list/ping/health/send/send_file/recv/recv_file`,
`polycall_cleanup()`; data comes back in `POLYCALL_OUTPUT` and
`POLYCALL_MSG_*`. See the comments in `src/awk_polycall.awk`.

Limits, because the CLI is the transport: the CLI has no strict validation
mode, so `polycall_run_config(path, 1)` applies strictness to the CLI's
report (warning count, `tls_enabled=true`) — use the gawk extension for the
core's exact `run=1` semantics. There is no unregister (no CLI command), no
caller-sized receive buffer, and receive timeouts are capped at 30 s. Text
payloads cannot contain NUL in a POSIX awk string; use
`polycall_peer_send_file` / `polycall_peer_recv_file` for binary data. On
MSYS / Git Bash / Cygwin, file arguments are handed to a native
`polycall.exe` as Windows paths (`cygpath -m`).

## Tests

```sh
npm test            # = sh tests/run-all.sh, plus the npm package test
make valgrind       # the gawk extension test under valgrind memcheck
```

`tests/run-gawk-ext-test.sh` builds the extension and runs
`tests/gawk_ext_test.awk` (the binding-ABI checklist, `call` against
`polycall start` and `polycall daemon start`, payloads both ways with a
`polycall peer serve` C node, four concurrent gawk sender processes) plus
the loader-error cases (missing / old / ABI-2 library, extension not on
`AWKLIBPATH`). `tests/run-awk-test.sh` runs `tests/awk_polycall_test.awk`
with every AWK it finds (gawk, mawk, busybox awk). Both use the real core
and the real CLI; a missing AWK, CLI or toolchain is exit 77 (SKIP), never
a pass. Tested on Linux x86-64 (Debian 12: gawk 5.2.1, mawk, busybox awk)
and Windows x64 (Git-for-Windows gawk 5.4.1 with both the MSVC
`polycall.dll` and the UCRT64 `libpolycall.dll` cores).

## Install from npm

```sh
npm install awk-polycall
```

The npm package is a source package: `require('awk-polycall')`
returns absolute paths (`awk`, `extSource`, `makefile`, `config`, ...);
build the extension from it with `make -C node_modules/awk-polycall ext`.

The configuration override [`awk-polycallrc`](awk-polycallrc) uses the
core's own grammar (`polycall config validate`).

## Author

Nnamdi Michael Okpala — <okpalan@protonmail.com>
