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.