Repository: microsoft/onnxruntime
File: onnxruntime/python/onnxruntime_pybind_state.cc (C++)

Code before:
          values_ptrs.emplace_back(*ort_values[i].attr(PYTHON_ORTVALUE_NATIVE_OBJECT_ATTR).cast<const OrtValue*>());
        }
        ORT_THROW_IF_ERROR(options->value.AddExternalInitializers(names_ptrs, values_ptrs));
#else
        ORT_UNUSED_PARAMETER(options);
        ORT_UNUSED_PARAMETER(names);
        ORT_UNUSED_PARAMETER(ort_values);
        ORT_THROW("External initializers are not supported in this build.");
#endif
      })
      .def("add_external_initializers_from_files_in_memory", [](PySessionOptions* options, py::list& names, py::list& buffers, py::list& lengths) -> void {
#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_EXTERNAL_INITIALIZERS)
        const auto num = names.size();
        ORT_ENFORCE(num == buffers.size() && num == lengths.size(),
                    "add_external_initializers_from_files_in_memory: expecting 'names', 'buffers' and 'lengths' to have equal length");

        InlinedVector<PathString> file_names;
        InlinedVector<std::pair<char*, const size_t>> files_buffers;
        file_names.reserve(num);
        files_buffers.reserve(num);

        for (size_t i = 0; i < num; ++i) {
          // names[i] must be str
          ORT_ENFORCE(py::isinstance<py::str>(names[i]),
                      "add_external_initializers_from_files_in_memory: each entry in 'names' must be a str");
          file_names.emplace_back(ToPathString(py::str(names[i])));

          // buffers[i] must support the buffer protocol (bytes, bytearray, memoryview, numpy array, etc.)
          py::handle buf_obj = buffers[i];
          ORT_ENFORCE(PyObject_CheckBuffer(buf_obj.ptr()) != 0,
                      "add_external_initializers_from_files_in_memory: each entry in 'buffers' must be bytes-like (supports the buffer protocol)");
          py::buffer buf = py::reinterpret_borrow<py::buffer>(buf_obj);
          py::buffer_info info = buf.request();
          char* data_ptr = reinterpret_cast<char*>(info.ptr);

          // lengths[i] must be int
          ORT_ENFORCE(py::isinstance<py::int_>(lengths[i]),
                      "add_external_initializers_from_files_in_memory: each entry in 'lengths' must be an int");
          size_t len = py::cast<size_t>(lengths[i]);

          files_buffers.emplace_back(std::make_pair(data_ptr, len));
        }

        ORT_THROW_IF_ERROR(options->value.AddExternalInitializersFromFilesInMemory(file_names, files_buffers));
#else
        ORT_UNUSED_PARAMETER(options);
        ORT_UNUSED_PARAMETER(names);
        ORT_UNUSED_PARAMETER(buffers);
        ORT_UNUSED_PARAMETER(lengths);
        ORT_THROW("External initializers are not supported in this build.");
#endif

Review comment (suggestion):
Suggest to declare this argument as std::vector<std::string> and let pybind do the types matching as well as issuing the error message.
This way let's not waste lines on this.

Code after:
          values_ptrs.emplace_back(*ort_values[i].attr(PYTHON_ORTVALUE_NATIVE_OBJECT_ATTR).cast<const OrtValue*>());
        }
        ORT_THROW_IF_ERROR(options->value.AddExternalInitializers(names_ptrs, values_ptrs));
#else
        ORT_UNUSED_PARAMETER(options);
        ORT_UNUSED_PARAMETER(names);
        ORT_UNUSED_PARAMETER(ort_values);
        ORT_THROW("External initializers are not supported in this build.");
#endif
      })
      .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 {
#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_EXTERNAL_INITIALIZERS)
        const auto num = names.size();
        ORT_ENFORCE(num == buffers.size() && num == lengths.size(),
                    "add_external_initializers_from_files_in_memory: expecting 'names', 'buffers' and 'lengths' to have equal length");

        InlinedVector<PathString> file_names;
        InlinedVector<std::pair<char*, const size_t>> files_buffers;
        file_names.reserve(num);
        files_buffers.reserve(num);

        for (size_t i = 0; i < num; ++i) {
          // Convert name and buffer using pybind-provided conversions
          file_names.emplace_back(ToPathString(names[i]));

          // buffers[i] is a py::buffer; request() retrieves pointer without copying
          py::buffer_info info = buffers[i].request();
          char* data_ptr = static_cast<char*>(info.ptr);

          files_buffers.emplace_back(std::make_pair(data_ptr, lengths[i]));
        }

        ORT_THROW_IF_ERROR(options->value.AddExternalInitializersFromFilesInMemory(file_names, files_buffers));
#else
        ORT_UNUSED_PARAMETER(options);
        ORT_UNUSED_PARAMETER(names);
        ORT_UNUSED_PARAMETER(buffers);
        ORT_UNUSED_PARAMETER(lengths);
        ORT_THROW("External initializers are not supported in this build.");
#endif
      },
           R"pbdoc(
Provide external initializer file contents from memory.

Args:
  names: sequence[str] of external file names (as referenced by the model's external_data locations).
  buffers: sequence[bytes-like] objects exposing the buffer protocol (e.g., bytes, bytearray, memoryview, numpy uint8 array) containing the corresponding file contents.
  lengths: sequence[int] sizes in bytes for each buffer.

Notes:
  - Keep the provided buffers alive until after session creation completes. ONNX Runtime copies needed data during session creation.