# `SnakeBridge.Runtime`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L1)

Thin payload helper for SnakeBridge that delegates execution to Snakepit.

This module is compile-time agnostic and focuses on building payloads that
match the Snakepit Prime runtime contract.

# `args`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L17)

```elixir
@type args() :: list()
```

# `error_reason`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L19)

```elixir
@type error_reason() :: Snakepit.Error.t() | Exception.t()
```

# `function_name`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L16)

```elixir
@type function_name() :: atom() | String.t()
```

# `module_ref`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L15)

```elixir
@type module_ref() :: module()
```

# `opts`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L18)

```elixir
@type opts() :: keyword()
```

# `call`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L43)

```elixir
@spec call(module_ref() | String.t(), function_name() | String.t(), args(), opts()) ::
  {:ok, term()} | {:error, error_reason()}
```

Call a Python function.

## Parameters

- `module` - Either a generated SnakeBridge module atom OR a Python module path string
- `function` - Function name (atom or string)
- `args` - Positional arguments (list)
- `opts` - Options including kwargs, :idempotent, :__runtime__ (e.g., `:pool_name`, `:affinity`)

## Examples

    # With generated module
    {:ok, result} = SnakeBridge.Runtime.call(Numpy, :mean, [[1,2,3]])

    # With string module path (dynamic)
    {:ok, result} = SnakeBridge.Runtime.call("numpy", "mean", [[1,2,3]])
    {:ok, result} = SnakeBridge.Runtime.call("math", :sqrt, [16])

# `call_class`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L311)

```elixir
@spec call_class(module_ref(), function_name(), args(), opts()) ::
  {:ok, term()} | {:error, error_reason()}
```

# `call_dynamic`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L85)

```elixir
@spec call_dynamic(String.t(), function_name(), args(), opts()) ::
  {:ok, term()} | {:error, error_reason()}
```

Calls any Python function dynamically without requiring generated bindings.

This is the no-codegen escape hatch for calling functions that were not
scanned during compilation.

# `call_helper`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L128)

```elixir
@spec call_helper(String.t(), args(), opts() | map()) ::
  {:ok, term()} | {:error, term()}
```

# `call_method`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L341)

```elixir
@spec call_method(SnakeBridge.Ref.t() | map(), function_name(), args(), opts()) ::
  {:ok, term()} | {:error, error_reason()}
```

# `clear_auto_session`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L791)

```elixir
@spec clear_auto_session() :: :ok
```

Clears the auto-session for the current process.

Useful for testing or when you want to force a new session.
Does NOT release the session on the Python side - use `release_auto_session/0` for that.

# `current_session`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L782)

```elixir
@spec current_session() :: String.t()
```

Returns the current session ID (explicit or auto-generated).

This is useful for debugging or when you need to know which session is active.

# `get_attr`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L462)

```elixir
@spec get_attr(SnakeBridge.Ref.t(), atom() | String.t(), opts()) ::
  {:ok, term()} | {:error, error_reason()}
```

# `get_module_attr`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L395)

```elixir
@spec get_module_attr(module_ref() | String.t(), atom() | String.t(), opts()) ::
  {:ok, term()} | {:error, error_reason()}
```

Retrieves a module-level attribute (constant, class, etc.).

## Parameters

- `module` - Either a generated SnakeBridge module atom OR a Python module path string
- `attr` - Attribute name (atom or string)
- `opts` - Runtime options

## Examples

    # Get math.pi
    {:ok, pi} = SnakeBridge.Runtime.get_module_attr("math", "pi")
    {:ok, pi} = SnakeBridge.Runtime.get_module_attr("math", :pi)

# `release_auto_session`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L799)

```elixir
@spec release_auto_session() :: :ok
```

Releases and clears the auto-session for the current process.

This releases all refs associated with the session on both Elixir and Python sides.

# `release_ref`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L547)

```elixir
@spec release_ref(SnakeBridge.Ref.t(), opts()) :: :ok | {:error, error_reason()}
```

# `release_session`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L565)

```elixir
@spec release_session(String.t(), opts()) :: :ok | {:error, error_reason()}
```

# `set_attr`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L516)

```elixir
@spec set_attr(SnakeBridge.Ref.t(), atom() | String.t(), term(), opts()) ::
  {:ok, term()} | {:error, error_reason()}
```

# `stream`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L216)

```elixir
@spec stream(
  module_ref() | String.t(),
  function_name() | String.t(),
  args(),
  opts(),
  (term() -&gt; any())
) ::
  :ok | {:ok, :done} | {:error, error_reason()}
```

Stream results from a Python generator/iterator.

## Parameters

- `module` - Either a generated SnakeBridge module atom OR a Python module path string
- `function` - Function name (atom or string)
- `args` - Positional arguments (list)
- `opts` - Options including kwargs
- `callback` - Function called for each streamed item

## Performance

When called with a **generated module atom**, this function can use Snakepit's
native gRPC streaming for efficient data transfer.

When called with a **string module path**, this delegates to `stream_dynamic/5`
which uses RPC-per-item iteration. See `stream_dynamic/5` docs for performance
guidance on large streams.

## Examples

    # With string module path (dynamic, RPC-per-item)
    SnakeBridge.Runtime.stream("pandas", "read_csv", ["file.csv"], [chunksize: 100], fn chunk ->
      process(chunk)
    end)

    # With generated module (native streaming when available)
    SnakeBridge.Runtime.stream(MyApp.Pandas, :read_csv, ["file.csv"], [chunksize: 100], fn chunk ->
      process(chunk)
    end)

# `stream_dynamic`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L242)

```elixir
@spec stream_dynamic(String.t(), String.t(), args(), opts(), (term() -&gt; any())) ::
  {:ok, :done} | {:error, term()}
```

Stream results from a Python generator using dynamic dispatch.

Creates a stream reference and iterates via stream_next until exhausted.

## Performance Note

Dynamic streaming uses an RPC-per-item approach: each item from the Python
iterator triggers a separate `stream_next` gRPC call. This is correct and
safe but may be slow for large streams (thousands of items).

For high-throughput streaming workloads, consider:
- **Generated streaming wrappers**: Use `SnakeBridge.stream/5` with compiled
  modules, which can leverage Snakepit's server-side streaming for better
  throughput.
- **Batched iteration**: Have Python yield batches of items rather than
  individual items.
- **Dedicated data transfer**: For very large datasets, consider writing
  Python results to files/databases and loading from Elixir.

Dynamic streaming is ideal for convenience and moderate-sized iterables.

# `stream_len`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L300)

```elixir
@spec stream_len(SnakeBridge.StreamRef.t(), opts()) ::
  {:ok, non_neg_integer()} | {:error, term()}
```

Gets the length of a Python iterable (if supported).

# `stream_next`
[🔗](https://github.com/nshkrdotcom/snakebridge/blob/v0.14.0/lib/snakebridge/runtime.ex#L252)

```elixir
@spec stream_next(SnakeBridge.StreamRef.t(), opts()) ::
  {:ok, term()} | {:error, :stop_iteration} | {:error, error_reason()}
```

Gets the next item from a Python iterator or generator.

Each call makes a separate RPC to Python. For high-throughput streaming,
see the performance note on `stream_dynamic/5`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
