Metadata-Version: 2.4
Name: sap_rfc_connector
Version: 0.3.6
Summary: SAP RFC wrapper written in C++ with Python bindings
Author: Dominik Tyrala
Author-email: dominik.tyrala@dyvenia.com
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: C++
Classifier: Operating System :: OS Independent
Requires-Python: >=3.7
Description-Content-Type: text/markdown
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: requires-python
Dynamic: summary

# sap_rfc_connector

`sap_rfc_connector` is a C++17 + pybind11 wrapper around the SAP NetWeaver RFC SDK.
It exposes a small Python API for opening a SAP connection, calling RFC functions, and reading metadata.

## What this package provides

- Python extension module: `sap_rfc_connector`
- C++ connector class: `SapRfcConnector`
- C++ function caller: `sap_rfc::SapFunctionCaller`
- Python bindings for:
  - `SapRfcConnector`
  - `SapFunctionCaller`

All RFC payload values are currently exchanged as strings (`dict[str, str]` style), including table rows.

## Requirements

- Python `>=3.7`
- C++ compiler with C++17 support
- SAP NetWeaver RFC SDK (headers + libraries)
- Environment variable `SAPNWRFC_HOME` pointing to the SDK root

Expected SDK layout:

- `${SAPNWRFC_HOME}/include`
- `${SAPNWRFC_HOME}/lib`

## Build / Install

From this directory:

```bash
python -m pip install --upgrade pip setuptools wheel pybind11
python -m pip install .
```

For local development build:

```bash
python setup.py build_ext --inplace
```

## Quick start (Python)

```python
from sap_rfc_connector import SapRfcConnector, SapFunctionCaller

conn = SapRfcConnector(
    user="YOUR_USER",
    passwd="YOUR_PASSWORD",
    ashost="YOUR_ASHOST",
    sysnr="00",
)

caller = SapFunctionCaller(conn)

result = caller.call(
    "STFC_CONNECTION",
    REQUTEXT="Hello SAP",
)

print(result)
```

Example with table parameters:

```python
result = caller.call(
    "BBP_RFC_READ_TABLE",
    QUERY_TABLE="KONP",
    DELIMITER="|",
    ROWCOUNT=1000,
    FIELDS=["KNUMH", "KOPOS", "KAPPL", "KSCHL", "KBETR"],
    OPTIONS=[{"TEXT": "KAPPL = 'KA'"}],
)
```

## Python binding behavior

### `SapRfcConnector(**kwargs)` validation

The kwargs constructor requires:

- `user`
- `passwd`
- `ashost`
- `sysnr`

If one is missing, it raises `ValueError`.
If connection fails, it raises `RuntimeError`.

### `SapFunctionCaller.call(func, **kwargs)`

Binding-side parsing rules:

- Non-list kwargs are treated as scalar RFC params.
- List kwargs are treated as table params.
- Special-case `FIELDS=["COL1", "COL2"]` is converted to:
  - `[{"FIELDNAME": "COL1"}, {"FIELDNAME": "COL2"}]`
- Table row dict values are normalized to string via Python `str(...)`.
  - `None` becomes empty string.
  - `bytes` are decoded as Python bytes-to-str cast result.

## C++ design notes

- `SapRfcConnector` is intentionally non-copyable and non-movable to avoid handle ownership bugs.
- RFC function handles are wrapped with RAII (`RfcFunctionHandle`).
- Input table field write errors now fail fast with an exception (no silent partial row writes).
- Output field extraction uses a safe fallback marker when SAP field read fails:
  - `"<unreadable:RFCTYPE_...>"`

## Encoding behavior

`utils::fromSAPUC` attempts UTF conversion first.
If conversion fails, it returns escaped code units (`\uXXXX`) instead of replacing data with spaces.

## Public API summary

### `SapRfcConnector`

- `connect(user, passwd, ashost, sysnr) -> bool`
- `check_connection() -> bool`
- `close_connection()`
- `get_library_info() -> dict[str, int]`

### `sap_rfc::SapFunctionCaller`

- `get_function_description(function_name) -> list[dict[str, str]]`
- `call(func, params, tables) -> dict[str, list[dict[str, str]]]`
- `get_table_metadata(table_name) -> list[dict[str, str]]`

## Notes for high-volume workloads

Current API materializes output tables in memory as nested Python/C++ maps of strings.
For very large result sets, plan calls in chunks at the query/source level where possible.

