← Files Aivana Outcome OperationsARCHIVED FILE

scripts/plugin_support.py

3.31 KB · Oct 2, 2026 · 00:34 UTC

↓ Download file

"""Copied into each standalone plugin; no repository runtime dependency."""
import argparse
from decimal import Decimal, InvalidOperation
import json
import os
from pathlib import Path
import tempfile


def obj(value, field='input'):
    if not isinstance(value, dict):
        raise ValueError(f'{field} must be an object')
    return value


def text(value, field='value'):
    if not isinstance(value, str) or not value.strip():
        raise ValueError(f'{field} must be a nonempty string')
    return value


def records(value, field, id_key='id', limit=10000):
    if not isinstance(value, list) or len(value) > limit:
        raise ValueError(f'{field} must be a list of at most {limit} objects')
    found = set()
    for row in value:
        obj(row, field)
        if id_key is not None:
            key = text(row.get(id_key), f'{field}.{id_key}')
            if key in found:
                raise ValueError(f'Duplicate {field}.{id_key}: {key}')
            found.add(key)
    return value


def amount(value, field='amount', nonnegative=True):
    if type(value) not in (str, int):
        raise ValueError(f'{field} must be a decimal string or integer')
    try:
        result = Decimal(value)
    except InvalidOperation:
        raise ValueError(f'Invalid {field}') from None
    if not result.is_finite() or (nonnegative and result < 0):
        raise ValueError(f'{field} must be finite' + (' and nonnegative' if nonnegative else ''))
    if len(result.as_tuple().digits) > 24 or abs(result.as_tuple().exponent) > 12 or result.adjusted() > 18:
        raise ValueError(f'{field} exceeds supported precision (24 digits, scale/exponent 12, 19 integer digits)')
    return result


def validate_json(value):
    try:
        json.dumps(value, allow_nan=False)
    except (TypeError, ValueError, RecursionError) as exc:
        raise ValueError('Input must contain finite JSON values') from exc


def main(run):
    parser = argparse.ArgumentParser(description='Aivana GmbH local plugin | https://www.aivana-gmbh.ai')
    parser.add_argument('--input', required=True, type=Path)
    parser.add_argument('--output', required=True, type=Path)
    args = parser.parse_args()
    try:
        if args.input.resolve() == args.output.resolve() or (args.output.exists() and args.input.samefile(args.output)):
            raise ValueError('Input and output must be different files')
        with args.input.open('rb') as handle:
            raw = handle.read(8 * 1024 * 1024 + 1)
        if len(raw) > 8 * 1024 * 1024:
            raise ValueError('Input exceeds 8 MiB')
        data = json.loads(raw.decode('utf-8-sig'))
        obj(data)
        validate_json(data)
        result = run(data)
        payload = json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + '\n'
        args.output.parent.mkdir(parents=True, exist_ok=True)
        temporary = None
        try:
            with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', dir=args.output.parent, delete=False) as handle:
                temporary = Path(handle.name)
                handle.write(payload)
            os.replace(temporary, args.output)
        finally:
            if temporary and temporary.exists():
                temporary.unlink()
        return 0
    except (ValueError, TypeError, KeyError, OSError, RecursionError) as exc:
        parser.exit(2, f'Error: {exc}\n')

SHA-256: e4ef0c6520fc0420ba9359720303766f6dfc6f4bfbdd3a3030f3ca12e660a97c