Metadata-Version: 2.4
Name: ark-fbs
Version: 0.2.1
Summary: FlatBuffers schema parsing + JSON<->binary helpers (pybind11)
Author: StarHeartHunt
License-Expression: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: C++
Classifier: Operating System :: OS Independent
Project-URL: Homepage, https://github.com/MooncellWiki/ArkFBS
Project-URL: Bug Tracker, https://github.com/MooncellWiki/ArkFBS/issues
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# Ark FBS

This is a standalone PyPI package that provides a thin `pybind11` wrapper around
`flatbuffers::Parser` and `GenerateText`:

- load `.fbs` / `.bfbs`
- JSON -> FlatBuffer binary
- FlatBuffer binary -> JSON (verified against the schema first, so mismatched or
  corrupt input raises instead of crashing the interpreter)

## Installation

### Install from PyPI

```bash
python -m pip install -U ark-fbs
```

## Repo layout

- `cpp/`: C++ binding source (pybind11)
- `src/ark_fbs/`: Python package wrapper + typing stubs
- `tests/`: pytest suite
- `third_party/flatbuffers/`: **your fork** of FlatBuffers as a git submodule (recommended)
- `third_party/nlohmann_json/`: single-header `nlohmann/json.hpp` (required by your FlatBuffers fork)

## Local build (editable)

```bash
uv sync --group dev
uv pip install -e .
uv run pytest
```

## Usage

```python
import ark_fbs

s = ark_fbs.Schema.from_fbs_text(
    "namespace t; table A { x:int; } root_type A;"
)
b = s.json_to_binary('{"x":1}')
print(s.binary_to_json(b))
```

### Error handling

All errors raised by this package derive from `ark_fbs.Error`, which is itself a
subclass of `ValueError`:

```python
import ark_fbs

try:
    schema.binary_to_json(data)
except ark_fbs.VerifyError:
    # data does not match `schema` (stale .fbs?), is truncated, or is corrupt
    ...
except ark_fbs.SchemaError:
    # schema has no root type, etc.
    ...
```

### Load from a `.fbs` file (with include paths)

```python
import ark_fbs

schema = ark_fbs.Schema.from_fbs_file(
    "schema.fbs",
    include_paths=["./includes", "./third_party/schemas"],
)
data = schema.json_to_binary('{"x": 123}')
print(schema.binary_to_json(data))
```

### Options and root type override

```python
import ark_fbs

opts = ark_fbs.Options(  # keyword-only
    strict_json=True,
    natural_utf8=True,
    defaults_json=True,
    size_prefixed=False,
    output_enum_identifiers=True,
)

schema = ark_fbs.Schema.from_fbs_text(
    "namespace t; table A { x:int; } root_type A;",
    options=opts,
    root_type_override="A",
)
```

### Load from `.bfbs` (serialized schema)

```python
import ark_fbs

schema = ark_fbs.Schema.from_fbs_text("namespace t; table A { x:int; } root_type A;")
bfbs = schema.serialize_schema_bfbs()

schema2 = ark_fbs.Schema.from_bfbs(bfbs)
print(schema2.binary_to_json(schema2.json_to_binary('{"x": 1}')))
```

## API Reference

### Exceptions

- `ark_fbs.Error(ValueError)`: base class for everything below.
- `ark_fbs.SchemaError(Error)`: the schema could not be parsed / deserialized, the
  `root_type_override` is unknown or names a `struct`, or an operation needs a
  root type and none is set.
- `ark_fbs.JsonParseError(Error)`: `json_to_binary()` could not parse the JSON
  text against the schema (includes the parser's line/column message).
- `ark_fbs.VerifyError(Error)`: `binary_to_json()` rejected the buffer. Raised
  when the buffer is too small, its `file_identifier` / size prefix does not
  match, it fails FlatBuffers verification against the schema's root type
  (schema mismatch, truncation, corruption, or `max_depth` / `max_tables`
  exceeded), or the JSON generator hit inconsistent data.

`Schema.from_fbs_file()` raises the usual `OSError` subclasses
(`FileNotFoundError`, `PermissionError`, `IsADirectoryError`, ...) when the file
cannot be read.

### `ark_fbs.Options`

`Options(...)` controls how schemas are parsed and how JSON/text is emitted.

Constructor (keyword-only):

- `Options(*, strict_json: bool = True, natural_utf8: bool = True, defaults_json: bool = True, size_prefixed: bool = False, output_enum_identifiers: bool = True)`

Fields (all are `bool`, same defaults as constructor):

- `strict_json` (default `True`): Require quoted field names on input **and emit
  quoted field names on output**. With `False` the emitted text uses unquoted
  keys and is not valid JSON.
- `natural_utf8` (default `True`): Emit/interpret UTF-8 in a “natural” way (FlatBuffers option).
- `defaults_json` (default `True`): Include default scalar values in emitted JSON.
- `size_prefixed` (default `False`): Data buffers carry a 4-byte size prefix.
  This affects `json_to_binary()`, `binary_to_json()` **and**
  `serialize_schema_bfbs()` (the bfbs is size-prefixed too), so use the same
  `Options` when loading a bfbs back with `from_bfbs()`.
- `output_enum_identifiers` (default `True`): Output enum identifiers rather than numeric values.

### `ark_fbs.Schema`

`Schema` wraps a `flatbuffers::Parser` instance plus a cached reflection schema
used for buffer verification.

#### Constructors

- `Schema.from_fbs_file(schema_path: str, include_paths: list[str] = [], options: Options = Options(), root_type_override: str = "") -> Schema`
  - **schema_path**: Path to the `.fbs` file.
  - **include_paths**: Extra include directories for `include "foo.fbs"` resolution.
  - **options**: Parsing/JSON options.
  - **root_type_override**: If non-empty, forces the root type (overrides `root_type` in schema).
  - **Raises**: `OSError` (subclass) if the file cannot be read; `SchemaError`
    if parsing fails or `root_type_override` is unknown / not a table.

- `Schema.from_fbs_text(schema_text: str, include_paths: list[str] = [], options: Options = Options(), source_filename: str = "", root_type_override: str = "") -> Schema`
  - **schema_text**: The schema content (as text).
  - **source_filename**: Optional filename used in error messages and for include resolution context.
  - **Raises**: `SchemaError` on parse errors or unknown / non-table root type override.

- `Schema.from_bfbs(bfbs: bytes | bytearray | memoryview, options: Options = Options(), root_type_override: str = "") -> Schema`
  - **bfbs**: Serialized schema bytes (`.bfbs`). Any contiguous buffer-protocol object is accepted.
  - **Raises**: `SchemaError` if deserialization fails or `root_type_override` is unknown / not a table.

#### Methods

- `Schema.json_to_binary(json: str) -> bytes`
  - Parses JSON using the loaded schema and returns the FlatBuffer binary.
  - **Raises**: `JsonParseError` if JSON parsing fails (including embedded NUL
    bytes); `SchemaError` if no root type is set.

- `Schema.binary_to_json(data: bytes | bytearray | memoryview, *, verify: bool = True, max_depth: int = 64, max_tables: int = 1_000_000) -> str`
  - Converts a FlatBuffer binary (for the schema's root type) to JSON text.
    Any contiguous buffer-protocol object is accepted without copying.
  - **verify**: Run the FlatBuffers verifier against the root type before
    generating text. Leave this on unless the buffer is known-good and you have
    measured that verification matters; with `verify=False` a mismatched or
    corrupt buffer is undefined behaviour and can crash the interpreter.
  - **max_depth** / **max_tables**: Verifier limits on nesting depth and total
    table count. Very large data files can exceed the default table limit;
    raise `max_tables` in that case.
  - **Raises**: `VerifyError` if the buffer fails verification or conversion
    fails; `SchemaError` if no root type is set; `TypeError` if `data` is not a
    contiguous byte buffer.

- `Schema.serialize_schema_bfbs() -> bytes`
  - Returns the loaded schema serialized as `.bfbs` bytes (cached at construction).
  - **Raises**: `SchemaError` if the schema is not initialized.

#### Properties

- `Schema.root_type: str` — name of the current root table, or `""` if none.
- `Schema.file_identifier: str` — the schema's `file_identifier`, or `""` if none.
  When non-empty, `binary_to_json()` requires buffers to carry it.

#### Root type behavior

`Schema.json_to_binary()` and `Schema.binary_to_json()` require a root type. You must either:

- Provide `root_type` in the schema (`root_type A;`), or
- Pass `root_type_override="A"` when constructing the `Schema`.

The root type must be a `table`; a `struct` is rejected with `SchemaError`.

#### Thread safety

A `Schema` instance is not safe for concurrent use from multiple threads
(`json_to_binary()` reuses an internal builder). Create one `Schema` per thread
or guard calls with a lock.

### Wheels / PyPI

CI uses `cibuildwheel` to produce wheels for multiple CPython versions and
architectures. See `.github/workflows/wheels.yml`. The pytest suite runs on
Linux/macOS/Windows via `.github/workflows/tests.yml`.
