--
← Back to blog
The Python Bridge: Call NumPy, PyTorch, and Any Python Library from NSL

The Python Bridge: Call NumPy, PyTorch, and Any Python Library from NSL

How It Works

NSL loads CPython at runtime via LoadLibraryA / GetProcAddress -- no compile-time Python dependency. If Python is installed on your system, py.is_available() returns true and you can call any Python library.

DLL Discovery (Windows)

1. NSL_PYTHON_DLL environment variable

2. Versioned DLLs on PATH: python314.dll .. python310.dll

3. Windows Registry

4. %LOCALAPPDATA%\Programs\Python\PythonXXX\

5. C:\PythonXXX\

Quick Start

if py.is_available()
    print(py.version())  # "3.12.10 ..."

    # Execute Python code
    py.exec("import numpy as np")
    py.exec("arr = np.array([1.0, 2.0, 3.0, 4.0])")

    # Evaluate expressions
    let mean = py.eval("float(arr.mean())")  # 2.5
    let std = py.eval("float(arr.std())")    # 1.118...

    # Import modules and call methods
    let np = py.import("numpy")
    let result = py.call(np, "sqrt", 16.0)   # 4.0
    py.free(np)  # release when done

Type Conversion

Types convert automatically between NSL and Python:

| NSL | Python | Both Ways? |

|-----|--------|------------|

| int | int (PyLong) | Yes |

| float | float (PyFloat) | Yes |

| string | str (PyUnicode) | Yes |

| bool | bool (PyBool) | Yes |

| null | None | Yes |

| list | list (PyList) | Yes |

| dict | dict (PyDict) | Yes |

| -- | tuple | py->nsl as list |

| -- | complex objects | py->nsl as handle |

Handle System

Complex Python objects (modules, classes, numpy arrays) return as integer handles. Use py.free() to release:

let np = py.import("numpy")         # handle
let arr = py.call(np, "zeros", 10)  # handle to ndarray
let val = py.get(arr, "shape")      # get attribute
py.free(arr)
py.free(np)

Working with Globals

# Set NSL values in Python namespace
py.set_global("data", [10, 20, 30, 40, 50])
py.set_global("threshold", 25)

# Use them in Python code
let filtered = py.eval("[x for x in data if x > threshold]")  # [30, 40, 50]
let total = py.eval("sum(data)")  # 150

Running Python Files

py.run_file("analysis.py")  # executes with proper tracebacks

Real-World Example: PyTorch Inference

if py.is_available()
    py.exec("import torch")
    py.exec("model = torch.load('model.pt')")
    py.exec("model.eval()")

    py.set_global("input_data", my_tensor_data)
    py.exec("inp = torch.tensor(input_data).float()")
    py.exec("with torch.no_grad(): out = model(inp)")

    let result = py.eval("out.tolist()")
    print(result)

GIL Management

NSL uses RAII GILGuard -- the GIL is acquired per py.* call and released automatically. The GIL is released between calls so Python threads can run.

All Posts