Skip to content

Commit 06a3ccf

Browse files
authored
python(api): add SessionOptions.add_external_initializers_from_files_… (#26012)
…in_memory (closes #25873) ### Description Adds a Python binding to load external initializer files from in‑memory buffers. Mirrors existing C/C++ API and Node binding to enable full in‑memory model loading. Adds explicit type validation to avoid pybind dumping large raw bytes on argument errors. ### Motivation and Context Problem: Models that use external_data for initializers can’t be fully loaded from bytes in Python because the weights are expected on disk. Goal: Allow providing the external file contents directly from Python memory (e.g., bytes, memoryview, numpy), eliminating filesystem dependency and supporting serverless/remote asset scenarios. Issue: Fixes #25873. ### Changes #### New Python API on SessionOptions: Name: add_external_initializers_from_files_in_memory Signature: names: list[str], buffers: list[bytes-like], lengths: list[int] File: onnxruntime/python/onnxruntime_pybind_state.cc #### Input validation to prevent noisy errors: Validates top-level types are lists and that list lengths match. Validates each name is str, each buffer supports the buffer protocol, and each length is an int. Raises clear RuntimeError messages instead of pybind11’s verbose dumps for mismatched types. #### Test added: onnxruntime/test/python/onnxruntime_test_python.py test_session_options_add_external_initializers_from_files_in_memory: supplies “Pads_not_on_disk.bin” content from a numpy array’s bytes and loads model_with_external_initializer_come_from_user.onnx without touching the filesystem. ### Usage Provide the external file name(s) as referenced by the model’s external_data “location”, plus their bytes and lengths: so.add_external_initializers_from_files_in_memory(["weights.bin"], [weights_bytes], [len(weights_bytes)]) sess = onnxruntime.InferenceSession(model_bytes, sess_options=so) --------- Signed-off-by: Jonah Bernard <[email protected]> Co-authored-by: Jonah Bernard <[email protected]>
1 parent d251f3a commit 06a3ccf

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

onnxruntime/python/onnxruntime_pybind_state.cc

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2300,7 +2300,50 @@ Applies to session load, initialization, etc. Default is 0.)pbdoc")
23002300
ORT_UNUSED_PARAMETER(ort_values);
23012301
ORT_THROW("External initializers are not supported in this build.");
23022302
#endif
2303-
});
2303+
})
2304+
.def("add_external_initializers_from_files_in_memory", [](PySessionOptions* options, std::vector<std::string> names, std::vector<py::buffer> buffers, std::vector<size_t> lengths) -> void {
2305+
#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_EXTERNAL_INITIALIZERS)
2306+
const auto num = names.size();
2307+
ORT_ENFORCE(num == buffers.size() && num == lengths.size(),
2308+
"add_external_initializers_from_files_in_memory: expecting 'names', 'buffers' and 'lengths' to have equal length");
2309+
2310+
InlinedVector<PathString> file_names;
2311+
InlinedVector<std::pair<char*, const size_t>> files_buffers;
2312+
file_names.reserve(num);
2313+
files_buffers.reserve(num);
2314+
2315+
for (size_t i = 0; i < num; ++i) {
2316+
// Convert name and buffer using pybind-provided conversions
2317+
file_names.emplace_back(ToPathString(names[i]));
2318+
2319+
// buffers[i] is a py::buffer; request() retrieves pointer without copying
2320+
py::buffer_info info = buffers[i].request();
2321+
char* data_ptr = static_cast<char*>(info.ptr);
2322+
2323+
files_buffers.emplace_back(std::make_pair(data_ptr, lengths[i]));
2324+
}
2325+
2326+
ORT_THROW_IF_ERROR(options->value.AddExternalInitializersFromFilesInMemory(file_names, files_buffers));
2327+
#else
2328+
ORT_UNUSED_PARAMETER(options);
2329+
ORT_UNUSED_PARAMETER(names);
2330+
ORT_UNUSED_PARAMETER(buffers);
2331+
ORT_UNUSED_PARAMETER(lengths);
2332+
ORT_THROW("External initializers are not supported in this build.");
2333+
#endif
2334+
},
2335+
R"pbdoc(
2336+
Provide external initializer file contents from memory.
2337+
2338+
Args:
2339+
names: sequence[str] of external file names (as referenced by the model's external_data locations).
2340+
buffers: sequence[bytes-like] objects exposing the buffer protocol (e.g., bytes, bytearray, memoryview, numpy uint8 array) containing the corresponding file contents.
2341+
lengths: sequence[int] sizes in bytes for each buffer.
2342+
2343+
Notes:
2344+
- Keep the provided buffers alive until after session creation completes. ONNX Runtime copies needed data during session creation.
2345+
- The bytestream must match the external file layout expected by the model (raw tensor bytes at the specified offsets).
2346+
)pbdoc");
23042347

23052348
py::class_<RunOptions>(m, "RunOptions", R"pbdoc(Configuration information for a single Run.)pbdoc")
23062349
.def(py::init())

onnxruntime/test/python/onnxruntime_test_python.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,25 @@ def test_session_options_add_external_initializers(self):
13051305
providers=["CPUExecutionProvider"],
13061306
)
13071307

1308+
def test_session_options_add_external_initializers_from_files_in_memory(self):
1309+
# Provide external initializer file content directly from memory
1310+
# The model references an external file named "Pads_not_on_disk.bin" for the initializer
1311+
pads_bytes = np.array([0, 0, 1, 1], dtype=np.int64).tobytes()
1312+
1313+
so = onnxrt.SessionOptions()
1314+
so.add_external_initializers_from_files_in_memory(
1315+
["Pads_not_on_disk.bin"],
1316+
[pads_bytes],
1317+
[len(pads_bytes)],
1318+
)
1319+
1320+
# This should not throw
1321+
onnxrt.InferenceSession(
1322+
get_name("model_with_external_initializer_come_from_user.onnx"),
1323+
sess_options=so,
1324+
providers=["CPUExecutionProvider"],
1325+
)
1326+
13081327
def test_register_custom_ops_library(self):
13091328
if sys.platform.startswith("win"):
13101329
shared_library = os.path.abspath("custom_op_library.dll")

0 commit comments

Comments
 (0)