Files
tensorflow--tensorflow/tensorflow/python/client/tf_session_wrapper.cc
T
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

2239 lines
82 KiB
C++

/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <cstdint>
#include <memory>
#include <stdexcept>
#include <string>
#include "Python.h"
#include "absl/types/optional.h"
#include "Eigen/Core" // from @eigen_archive
#include "pybind11/attr.h" // from @pybind11
#include "pybind11/cast.h" // from @pybind11
#include "pybind11/chrono.h" // from @pybind11
#include "pybind11/complex.h" // from @pybind11
#include "pybind11/detail/common.h" // from @pybind11
#include "pybind11/functional.h" // from @pybind11
#include "pybind11/numpy.h" // from @pybind11
#include "pybind11/pybind11.h" // from @pybind11
#include "pybind11/pytypes.h" // from @pybind11
#include "pybind11/stl.h" // from @pybind11
#include "pybind11/stl_bind.h" // from @pybind11
#include "pybind11_protobuf/native_proto_caster.h" // from @pybind11_protobuf
#include "tensorflow/c/c_api.h"
#include "tensorflow/c/c_api_experimental.h"
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/python_api.h"
#include "tensorflow/c/safe_ptr.h"
#include "tensorflow/c/tf_buffer.h"
#include "tensorflow/c/tf_datatype.h"
#include "xla/tsl/python/lib/core/numpy.h"
#include "tensorflow/core/distributed_runtime/server_lib.h"
#include "tensorflow/core/framework/full_type.pb.h"
#include "tensorflow/core/framework/versions.pb.h"
#include "tensorflow/core/public/release_version.h"
#include "tensorflow/core/public/version.h"
#include "tensorflow/core/util/version_info.h"
#include "tensorflow/python/client/tf_session_helper.h"
#include "tensorflow/python/lib/core/pybind11_lib.h"
#include "tensorflow/python/lib/core/pybind11_status.h"
#include "tensorflow/python/lib/core/safe_pyobject_ptr.h"
#include "tsl/platform/mutex.h"
namespace pybind11 {
namespace detail {
// Convert between absl::optional and python.
//
// pybind11 supports std::optional, and absl::optional is meant to be a
// drop-in replacement for std::optional, so we can just use the built in
// implementation.
#ifndef ABSL_USES_STD_OPTIONAL
template <typename T>
struct type_caster<absl::optional<T>>
: public optional_caster<absl::optional<T>> {};
template <>
struct type_caster<absl::nullopt_t> : public void_caster<absl::nullopt_t> {};
#endif
} // namespace detail
} // namespace pybind11
// TODO(amitpatankar): Consolidate Buffer methods into a separate header file.
TF_Buffer* ProtoStringToTFBuffer(PyObject* input) {
// Convert a Python string object to TF_Buffer.
char* c_string;
Py_ssize_t py_size;
// PyBytes_AsStringAndSize() does not copy but simply interprets the input
if (PyBytes_AsStringAndSize(input, &c_string, &py_size) == -1) {
// Python has raised an error (likely TypeError or UnicodeEncodeError).
throw py::error_already_set();
}
return TF_NewBufferFromString(static_cast<void*>(c_string),
static_cast<size_t>(py_size));
}
// Copied from tf_session.i
// We have to do convoluted logic of passing in a vector of py::bytes. If we
// pass in strings they are freed prior to the necessary function calls.
tensorflow::NameVector ConvertPyListToNameVector(
const std::vector<py::bytes>& py_vector) {
tensorflow::NameVector temp;
for (size_t i = 0; i < py_vector.size(); ++i) {
const char* string_elem = PyBytes_AsString(py_vector.at(i).ptr());
temp.push_back(string_elem);
}
return temp;
}
namespace py = pybind11;
// TODO(power) -- share these with JAX (see python_utils.h)
template <typename Func, typename... Extra>
pybind11::object property_readonly(Func&& get, const char* doc = "") {
pybind11::handle property_class(
reinterpret_cast<PyObject*>(&PyProperty_Type));
return property_class(
pybind11::cpp_function(std::forward<Func>(get),
py::return_value_policy::reference_internal),
pybind11::none(), pybind11::none(), doc);
}
template <typename GetFunc, typename SetFunc>
pybind11::object property(GetFunc&& get, SetFunc&& set) {
pybind11::handle property_class(
reinterpret_cast<PyObject*>(&PyProperty_Type));
return property_class(
pybind11::cpp_function(std::forward<GetFunc>(get),
py::return_value_policy::reference_internal),
pybind11::cpp_function(std::forward<SetFunc>(set)), pybind11::none(), "");
}
template <typename Constructor>
pybind11::object def_static(Constructor&& constructor) {
return pybind11::staticmethod(
pybind11::cpp_function(std::forward<Constructor>(constructor)));
}
template <typename Func, typename... Extra>
pybind11::object method(pybind11::object type, Func&& function,
const Extra&... extra) {
return pybind11::cpp_function(std::forward<Func>(function),
pybind11::is_method(type), extra...);
}
// Construct a "TF" Python object. This covers the boiler-plate for Python type
// generation. The type is assumed to be a GC type (containing other types).
// To add the required Python type fields, classes definitions must start with
//
// TFObject_Head(classname, TfObjectDataType)
//
// Required attributes/methods for TfObjectDataType type:
//
// Constructor(PyObject* args, PyObject* kw)
// ~Destructor
// Clear()
// Visit(visitproc visit, void* arg)
//
// Individual methods/attributes are added to the type later, as seen below.
template <typename T>
void MakeTfObjectType(PyObject** py_type) {
using TfObjectDataType = typename T::TfObjectDataType;
py::str name = py::str(T::kTypeName);
py::str qualname = py::str(T::kTypeName);
PyHeapTypeObject* heap_type = reinterpret_cast<PyHeapTypeObject*>(
PyType_Type.tp_alloc(&PyType_Type, 0));
heap_type->ht_name = name.release().ptr();
heap_type->ht_qualname = qualname.release().ptr();
PyTypeObject* type = &heap_type->ht_type;
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE |
Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE;
type->tp_name = T::kTypeName;
// Allocation size for both Python object header and the TF data members.
type->tp_basicsize = sizeof(T) + sizeof(TfObjectDataType);
type->tp_new = [](PyTypeObject* subtype, PyObject* args,
PyObject* kwds) -> PyObject* {
T* self = reinterpret_cast<T*>(subtype->tp_alloc(subtype, 0));
TfObjectDataType* data = reinterpret_cast<TfObjectDataType*>(&self[1]);
if (!self) return nullptr;
// PyType_GenericAlloc (the default implementation of tp_alloc) by default
// enables the garbage collector immediately for our object. This makes
// initialization extremely tricky as we need to avoid having the object
// in an invalid intermediate state.
//
// We disable the GC here until initialization is finished.
PyObject_GC_UnTrack(self);
new (data) TfObjectDataType(args, kwds);
self->dict = PyDict_New();
PyObject_GC_Track(self);
if (PyErr_Occurred()) {
return nullptr;
}
return reinterpret_cast<PyObject*>(self);
};
type->tp_dealloc = [](PyObject* self) {
VLOG(3) << "Destroy: " << T::kTypeName;
PyObject_GC_UnTrack(self);
PyTypeObject* tp = Py_TYPE(self);
PyObject_ClearWeakRefs(self);
T* o = reinterpret_cast<T*>(self);
TfObjectDataType* data = reinterpret_cast<TfObjectDataType*>(&o[1]);
Py_CLEAR(o->dict);
data->~TfObjectDataType();
tp->tp_free(self);
Py_DECREF(tp);
};
type->tp_traverse = [](PyObject* self, visitproc visit, void* arg) {
VLOG(3) << "Visit: " << T::kTypeName;
T* o = reinterpret_cast<T*>(self);
TfObjectDataType* data = reinterpret_cast<TfObjectDataType*>(&o[1]);
Py_VISIT(Py_TYPE(self));
Py_VISIT(o->dict);
return data->Visit(visit, arg);
};
type->tp_clear = [](PyObject* self) {
VLOG(3) << "Clear: " << T::kTypeName;
T* o = reinterpret_cast<T*>(self);
TfObjectDataType* data = reinterpret_cast<TfObjectDataType*>(&o[1]);
Py_CLEAR(o->dict);
data->Clear();
return 0;
};
type->tp_weaklistoffset = offsetof(T, weakrefs);
// All TF objects use a dictionary today, so we initialize it at construction.
// If some types become fully C++ based or require only thin Python wrappers,
// we can instead defer dictionary creation using a custom getter/setter.
type->tp_dictoffset = offsetof(T, dict);
// type->tp_getset = &tp_getset[0];
type->tp_descr_get = nullptr;
type->tp_descr_set = nullptr;
type->tp_call = nullptr;
type->tp_vectorcall_offset = 0;
type->tp_repr = nullptr;
if (PyType_Ready(type) != 0) {
PyErr_Print();
LOG(FATAL) << "Failed to build type."; // Crash ok. In module init.
}
*py_type = reinterpret_cast<PyObject*>(type);
}
#define TFObject_HEAD(typename, datatypename) \
using TfObjectDataType = datatypename; \
PyObject_HEAD; \
PyObject* dict = nullptr; \
PyObject* weakrefs = nullptr; \
TfObjectDataType data[0]; \
static PyObject* py_type; \
static constexpr const char* kTypeName = #typename;
struct PyGraph;
struct PyOperation;
struct PyTensor;
// Bind operation maps opaquely to avoid copying.
typedef absl::flat_hash_map<int64_t, py::object> OpsByIdMap;
typedef absl::flat_hash_map<std::string, py::object> OpsByNameMap;
PYBIND11_MAKE_OPAQUE(TF_Operation);
PYBIND11_MAKE_OPAQUE(TF_Graph);
PYBIND11_MAKE_OPAQUE(TF_Session);
PYBIND11_MAKE_OPAQUE(TF_Buffer);
PYBIND11_MAKE_OPAQUE(TF_ImportGraphDefOptions);
PYBIND11_MAKE_OPAQUE(TF_ImportGraphDefResults);
PYBIND11_MAKE_OPAQUE(TF_DeprecatedSession);
PYBIND11_MAKE_OPAQUE(TF_OperationDescription);
PYBIND11_MAKE_OPAQUE(TF_Library);
PYBIND11_MAKE_OPAQUE(TF_SessionOptions);
PYBIND11_MAKE_OPAQUE(TF_ApiDefMap);
PYBIND11_MAKE_OPAQUE(TF_Server);
PYBIND11_MAKE_OPAQUE(TF_DeviceList);
PYBIND11_MAKE_OPAQUE(TF_Status);
PYBIND11_MAKE_OPAQUE(OpsByIdMap);
PYBIND11_MAKE_OPAQUE(OpsByNameMap);
// Convert the given handle to a TF object type.
template <typename T>
T* AsPyTfObject(py::handle handle) {
if (handle.get_type() == T::py_type) {
return reinterpret_cast<T*>(handle.ptr());
}
if (PyType_IsSubtype(Py_TYPE(handle.ptr()),
reinterpret_cast<PyTypeObject*>(T::py_type))) {
return reinterpret_cast<T*>(handle.ptr());
}
// The tf_should_use wrapper masquerades as a base class, and forwards
// attribute lookups to an underlying class. This should be removed (it is
// slow, confusing, and not so relevant with TF2), or at least moved to the
// C++ wrapper classes (it is only used on Tensor and Operation). In the
// meantime, use a custom caster to handle the cases where we are passed a
// `tf_should_use` instead of the original class.
if (py::hasattr(handle, "_tf_should_use_wrapped_value")) {
return AsPyTfObject<T>(py::getattr(handle, "_tf_should_use_wrapped_value"));
}
throw std::runtime_error(
absl::StrCat("Expected a ", T::kTypeName, " got ",
py::cast<std::string>(py::str(handle))));
}
template <typename T>
py::object AsPyObject(T* obj) {
return py::reinterpret_borrow<py::object>(reinterpret_cast<PyObject*>(obj));
}
template <typename T>
typename T::TfObjectDataType* AsPyTfObjectData(py::handle handle) {
return AsPyTfObject<T>(handle)->data;
}
// Reference counting helper for PyTfObjects.
//
// Similar to the pybind holder types, this manages the Python reference
// counting while allowing access to the underlying PyTfObject type.
//
// As a special case to support Dismantle(), this allows setting our underlying
// pointer to None when clearing the type. Direct access to attributes is not
// allowed after this point.
template <typename T>
class tf_handle {
public:
tf_handle() : obj_(nullptr) {}
explicit tf_handle(PyObject* obj) : obj_(nullptr) {
obj_ = AsPyTfObject<T>(obj);
Py_INCREF(obj);
}
~tf_handle() { Py_CLEAR(obj_); }
tf_handle(const tf_handle<T>& other) { Reset(other.obj_); }
tf_handle<T>& operator=(tf_handle<T>&& other) noexcept {
if (this == &other) {
return *this;
}
obj_ = other.obj_;
other.obj_ = nullptr;
}
tf_handle<T>& operator=(const tf_handle<T>& other) {
Reset(other.ptr());
return *this;
}
tf_handle<T>& operator=(PyObject* obj) {
Reset(obj);
return *this;
}
void Destroy() {
Py_INCREF(Py_None);
Py_CLEAR(obj_);
obj_ = reinterpret_cast<T*>(Py_None);
}
void Reset(PyObject* obj) {
if (obj == reinterpret_cast<PyObject*>(obj_)) {
return;
}
Py_INCREF(obj);
Py_CLEAR(obj_);
obj_ = AsPyTfObject<T>(obj);
}
void Clear() { Py_CLEAR(obj_); }
T* operator->() {
if (reinterpret_cast<PyObject*>(obj_) == Py_None) {
throw std::runtime_error("Tried to deference None as a TF type.");
}
return obj_;
}
PyObject* ptr() const { return reinterpret_cast<PyObject*>(obj_); }
py::handle borrow() { return py::reinterpret_borrow<py::object>(ptr()); }
py::handle steal() { return py::reinterpret_steal<py::object>(ptr()); }
private:
T* obj_;
};
namespace pybind11 {
namespace detail {
#define TF_CASTER(TfObject) \
template <> \
struct type_caster<TfObject> : public type_caster_base<TfObject> { \
public: \
using base = type_caster_base<TfObject>; \
bool load(py::handle src, bool convert) { \
value = AsPyTfObject<TfObject>(src); \
return true; \
} \
static py::handle cast(TfObject* src, return_value_policy policy, \
py::handle parent) { \
PyObject* src_obj = reinterpret_cast<PyObject*>(src); \
return py::reinterpret_borrow<py::object>(src_obj); \
} \
};
TF_CASTER(PyGraph);
TF_CASTER(PyOperation);
TF_CASTER(PyTensor);
} // namespace detail
} // namespace pybind11
// TF_Operation's are owned by their graph.
struct TF_OperationDeleter {
void operator()(TF_Operation* op) {}
};
struct PyGraphData {
TF_Graph* graph;
// The C++ graph maintains an ID for every node, however our Python code has
// _also_ previously assigned a node ID, which is independent and different
// from the C++ ID. Moreover, the Python IDs are _dense_ and the Python
// implementation relies on the `ops_by_id` map having "insertion order"
// for the implementation of `get_operations` and auto control-deps.
//
// To keep compatibility and improve performance, we use 3 collections:
//
// * A py::list which tracks operations in insertion order.
// * A flat-map from C++ ID to PyOperation.
// * A flat-map from std::string to PyOperation.
py::list op_list;
// Operation ownership is maintained in ops_by_id.
OpsByIdMap ops_by_id;
OpsByNameMap ops_by_name;
PyGraphData(PyObject* args, PyObject* kwds) {
graph = TF_NewGraph();
// By default shape inference functions are required, however this breaks
// many custom ops. Disable this check for Python graphs.
tsl::mutex_lock l(graph->mu);
graph->refiner.set_require_shape_inference_fns(false);
}
~PyGraphData() {
Clear();
TF_DeleteGraph(graph);
}
void Dismantle();
void Clear() {
Py_CLEAR(op_list.ptr());
op_list.release();
for (auto it = ops_by_id.begin(); it != ops_by_id.end(); ++it) {
Py_CLEAR(it->second.ptr());
it->second.release();
}
ops_by_id.clear();
for (auto it = ops_by_name.begin(); it != ops_by_name.end(); ++it) {
Py_CLEAR(it->second.ptr());
it->second.release();
}
ops_by_name.clear();
}
int Visit(visitproc visit, void* arg) {
Py_VISIT(op_list.ptr());
for (auto it = ops_by_id.begin(); it != ops_by_id.end(); ++it) {
Py_VISIT(it->second.ptr());
}
for (auto it = ops_by_name.begin(); it != ops_by_name.end(); ++it) {
Py_VISIT(it->second.ptr());
}
return 0;
}
};
struct PyGraph {
TFObject_HEAD(PyGraph, PyGraphData);
int64_t add_op(py::object obj);
py::list operations() { return data->op_list; }
int64_t num_operations() const { return data->op_list.size(); }
// Return operations that are part of the Graph, but do not yet have
// OperationHandle's. This logic is only invoked when importing an existing
// GraphDef into Python. It should be removed once all logic moves to C++.
std::vector<TF_Operation*> new_operations() {
tsl::mutex_lock l(tf_graph()->mu);
std::vector<TF_Operation*> ops;
// SUBTLE: `op_nodes` skips the SOURCE and SINK nodes
for (auto n : tf_graph()->graph.op_nodes()) {
if (data->ops_by_name.find(n->name()) == data->ops_by_name.end()) {
ops.push_back(reinterpret_cast<TF_Operation*>(n));
}
}
return ops;
}
py::object get_operation_by_name(const std::string& name) {
tsl::mutex_lock l(tf_graph()->mu);
auto it = data->ops_by_name.find(name);
if (it == data->ops_by_name.end()) {
throw py::key_error();
}
return it->second;
}
int version() const { return data->ops_by_id.size(); }
py::bytes version_def() const {
// Potential deadlock:
//
// If different threads are building and executing the graph, there is a
// potential for a deadlock. This can happen if one thread holds the GIL and
// waits for the graph mutex, while another thread holds the graph mutex and
// waits for the GIL.
//
// To avoid this, the GIL must be released before acquiring the graph mutex.
// The graph mutex must then be held while getting the VersionDef. Finally,
// the GIL must be reacquired.
std::string versions;
{
py::gil_scoped_release release;
tsl::mutex_lock l(tf_graph()->mu);
versions = tf_graph()->graph.versions().SerializeAsString();
}
pybind11::gil_scoped_acquire acquire;
return py::bytes(versions);
}
absl::StatusOr<py::bytes> _op_def_for_type(
const std::string& kTypeName) const {
tsl::mutex_lock l(tf_graph()->mu);
const tensorflow::OpDef* op_def;
TF_RETURN_IF_ERROR(
tf_graph()->graph.op_registry()->LookUpOpDef(kTypeName, &op_def));
return py::bytes(op_def->SerializeAsString());
}
void add_control_input(tensorflow::Node* src, tensorflow::Node* dst) {
tsl::mutex_lock l(tf_graph()->mu);
tf_graph()->graph.AddControlEdge(src, dst);
record_mutation(*dst, "adding control edge");
}
void remove_all_control_inputs(const tensorflow::Node& node) {
tsl::mutex_lock l(tf_graph()->mu);
std::vector<const tensorflow::Edge*> control_edges;
for (const tensorflow::Edge* edge : node.in_edges()) {
if (!edge->IsControlEdge()) continue;
control_edges.push_back(edge);
}
for (const tensorflow::Edge* edge : control_edges) {
tf_graph()->graph.RemoveControlEdge(edge);
}
}
void record_mutation(const tensorflow::Node& node, const std::string& reason)
TF_EXCLUSIVE_LOCKS_REQUIRED(tf_graph()->mu) {
tensorflow::RecordMutation(tf_graph(),
reinterpret_cast<const TF_Operation&>(node),
reason.c_str());
}
TF_Graph* tf_graph() const { return data->graph; }
};
struct PyOperationData {
TF_Operation* tf_op = nullptr;
py::list outputs;
// N.B. initialized later by Python.
tf_handle<PyGraph> graph;
py::function tensor_fn;
PyOperationData(PyObject* args, PyObject* kwds) {
PyObject *py_op, *py_tensor_fn;
if (!PyArg_ParseTuple(args, "OO", &py_op, &py_tensor_fn)) {
return;
}
tf_op = py::cast<TF_Operation*>(py_op);
tensor_fn = py::cast<py::function>(py_tensor_fn);
}
~PyOperationData() { Clear(); }
void Dismantle(PyOperation* py_op);
void Clear() {
Py_CLEAR(outputs.ptr());
outputs.release();
graph.Clear();
}
int Visit(visitproc visit, void* arg) {
Py_VISIT(graph.ptr());
Py_VISIT(outputs.ptr());
return 0;
}
};
struct PyOperation {
TFObject_HEAD(PyOperation, PyOperationData);
TF_Operation* tf_op() const { return data->tf_op; }
void _init_outputs() {
int num_outputs = TF_OperationNumOutputs(tf_op());
for (int i = 0; i < num_outputs; ++i) {
int dtype = TF_OperationOutputType(TF_Output{tf_op(), i});
data->outputs.append(data->tensor_fn(AsPyObject(this), i, dtype));
}
}
absl::Status _add_outputs(py::list dtypes, py::list shapes);
TF_Output _tf_output(int idx) const { return TF_Output{tf_op(), idx}; }
TF_Input _tf_input(int idx) const { return TF_Input{tf_op(), idx}; }
py::bytes node_def() {
return py::bytes(tf_op()->node.def().SerializeAsString());
}
py::bytes op_def() const {
return py::bytes(tf_op()->node.op_def().SerializeAsString());
}
bool is_stateful() const { return tf_op()->node.op_def().is_stateful(); }
const std::string& type() { return tf_op()->node.type_string(); }
void add_control_input(PyOperation* input) {
data->graph->add_control_input(&input->tf_op()->node, &tf_op()->node);
}
void add_control_inputs(py::iterable inputs);
py::list control_inputs() {
py::list output;
for (const auto* edge : tf_op()->node.in_edges()) {
if (edge->IsControlEdge() && !edge->src()->IsSource()) {
output.append(data->graph->data->ops_by_id[edge->src()->id()]);
}
}
return output;
}
py::list control_outputs() {
py::list output;
for (const auto* edge : tf_op()->node.out_edges()) {
if (edge->IsControlEdge() && !edge->dst()->IsSink()) {
output.append(data->graph->data->ops_by_id[edge->dst()->id()]);
}
}
return output;
}
void remove_all_control_inputs() {
data->graph->remove_all_control_inputs(tf_op()->node);
}
void set_device(const std::string& device) {
tsl::mutex_lock l(data->graph->tf_graph()->mu);
tf_op()->node.set_requested_device(device);
data->graph->record_mutation(tf_op()->node, "setting device");
}
const std::string& device() { return tf_op()->node.requested_device(); }
const std::string& name() { return tf_op()->node.name(); }
};
struct PyTensorData {
py::object tf_output = py::none();
py::object name = py::none();
py::object dtype = py::none();
py::object shape_val = py::none();
py::object uid = py::none();
tf_handle<PyOperation> op;
tf_handle<PyGraph> graph;
int value_index = -1;
PyTensorData(PyObject* args, PyObject* kwds) {
PyObject *py_op, *py_index, *py_dtype, *py_uid;
if (!PyArg_ParseTuple(args, "OOOO", &py_op, &py_index, &py_dtype,
&py_uid)) {
return;
}
dtype = py::reinterpret_borrow<py::object>(py_dtype);
value_index = py::cast<int>(py::handle(py_index));
op = py_op;
graph = op->data->graph;
name = py::str(absl::StrCat(op->name(), ":", value_index));
tf_output = py::cast(TF_Output{op->tf_op(), value_index});
uid = py::reinterpret_borrow<py::object>(py_uid);
}
~PyTensorData() { Clear(); }
void Clear() {
Py_CLEAR(tf_output.ptr());
tf_output.release();
Py_CLEAR(name.ptr());
name.release();
Py_CLEAR(dtype.ptr());
dtype.release();
Py_CLEAR(shape_val.ptr());
shape_val.release();
Py_CLEAR(uid.ptr());
uid.release();
op.Clear();
graph.Clear();
}
int Visit(visitproc visit, void* arg) {
Py_VISIT(op.ptr());
Py_VISIT(tf_output.ptr());
Py_VISIT(graph.ptr());
Py_VISIT(name.ptr());
Py_VISIT(dtype.ptr());
Py_VISIT(shape_val.ptr());
Py_VISIT(uid.ptr());
return 0;
}
};
struct PyTensor {
TFObject_HEAD(PyTensor, PyTensorData);
int value_index() const { return data->value_index; }
absl::StatusOr<py::object> shape() {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
bool unknown_shape = false;
auto dims = tensorflow::TF_GraphGetTensorShapeHelper(
data->graph->tf_graph(), TF_Output{data->op->tf_op(), value_index()},
status.get(), &unknown_shape);
if (!status.get()->status.ok()) {
return status.get()->status;
}
py::list py_list;
for (int64_t dim : dims) {
py_list.append(dim == -1 ? py::none() : py::cast(dim));
}
return py::make_tuple(py_list, py::cast(unknown_shape));
}
absl::Status set_shape(py::iterable shape, bool unknown_shape) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
std::vector<int64_t> dims;
if (!unknown_shape) {
for (py::handle dim : shape) {
if (dim.is_none()) {
dims.push_back(-1);
} else {
dims.push_back(py::cast<int64_t>(dim));
}
}
}
tensorflow::TF_GraphSetTensorShape_wrapper(
data->graph->tf_graph(), TF_Output{data->op->tf_op(), value_index()},
dims, unknown_shape, status.get());
return status.get()->status;
}
int64_t rank() {
tsl::mutex_lock l(data->graph->tf_graph()->mu);
tensorflow::shape_inference::InferenceContext* ic =
data->graph->tf_graph()->refiner.GetContext(&data->op->tf_op()->node);
tensorflow::shape_inference::ShapeHandle shape = ic->output(value_index());
if (ic->RankKnown(shape)) {
return ic->Rank(shape);
}
return -1;
}
py::list consumers() {
py::list out;
for (const auto* edge : data->op->tf_op()->node.out_edges()) {
if (edge->src_output() != value_index()) {
continue;
}
out.append(data->graph->data->ops_by_id[edge->dst()->id()]);
}
return out;
}
};
PyObject* PyOperation::py_type = nullptr;
PyObject* PyTensor::py_type = nullptr;
PyObject* PyGraph::py_type = nullptr;
void PyOperationData::Dismantle(PyOperation* py_op) {
outputs = py::list();
graph.Destroy();
PyDict_Clear(py_op->dict);
}
absl::Status PyOperation::_add_outputs(py::list dtypes, py::list shapes) {
int orig_outputs = data->outputs.size();
for (int i = 0; i < dtypes.size(); ++i) {
py::object tensor =
data->tensor_fn(AsPyObject(this), orig_outputs + i, dtypes[i]);
// The passed in `shapes` may be TensorShapes, convert them to lists if
// needed.
bool unknown_shape;
py::object dims;
if (py::hasattr(shapes[i], "as_list")) {
unknown_shape = shapes[i].attr("rank").is_none();
if (!unknown_shape) {
dims = shapes[i].attr("as_list")();
} else {
dims = py::list();
}
} else {
unknown_shape = false;
dims = shapes[i];
}
TF_RETURN_IF_ERROR(
AsPyTfObject<PyTensor>(tensor)->set_shape(dims, unknown_shape));
data->outputs.append(tensor);
}
return absl::OkStatus();
}
void PyOperation::add_control_inputs(py::iterable inputs) {
tsl::mutex_lock l(data->graph->tf_graph()->mu);
for (py::handle input : inputs) {
auto* input_handle = py::cast<PyOperation*>(input);
data->graph->tf_graph()->graph.AddControlEdge(&input_handle->tf_op()->node,
&tf_op()->node);
}
data->graph->record_mutation(tf_op()->node, "adding control input");
}
void PyGraphData::Dismantle() {
for (auto& op : op_list) {
AsPyTfObjectData<PyOperation>(op.ptr())->Dismantle(
AsPyTfObject<PyOperation>(op.ptr()));
}
op_list = py::list();
ops_by_id.clear();
ops_by_name.clear();
}
int64_t PyGraph::add_op(py::object obj) {
PyOperation* op_handle = AsPyTfObject<PyOperation>(obj);
int64_t op_id = op_handle->tf_op()->node.id();
data->op_list.append(obj);
data->ops_by_id[op_id] = obj;
data->ops_by_name[op_handle->name()] = obj;
return op_id;
}
PYBIND11_MODULE(_pywrap_tf_session, m) {
pybind11_protobuf::ImportNativeProtoCasters();
// Numpy initialization code for array checks.
tsl::ImportNumpy();
py::bind_map<OpsByIdMap>(m, "OpsById");
py::bind_map<OpsByNameMap>(m, "OpsByName");
py::str module_name(m.attr("__name__"));
MakeTfObjectType<PyGraph>(&PyGraph::py_type);
py::object c_graph = py::reinterpret_borrow<py::object>(PyGraph::py_type);
m.attr("PyGraph") = c_graph;
c_graph.attr("__module__") = module_name;
c_graph.attr("Dismantle") = method(c_graph, [](py::handle handle) {
AsPyTfObjectData<PyGraph>(handle)->Dismantle();
});
c_graph.attr("_version_def") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyGraph>(handle)->version_def();
});
c_graph.attr("version") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyGraph>(handle)->version();
});
c_graph.attr("_op_def_for_type") =
method(c_graph, [](py::handle handle, std::string type) {
return AsPyTfObject<PyGraph>(handle)->_op_def_for_type(type);
});
c_graph.attr("_nodes_by_name") = property_readonly([](py::handle handle) {
return AsPyTfObjectData<PyGraph>(handle)->ops_by_name;
});
c_graph.attr("_nodes_by_id") = property_readonly([](py::handle handle) {
return AsPyTfObjectData<PyGraph>(handle)->ops_by_id;
});
c_graph.attr("_get_operation_by_name") =
method(c_graph, [](py::handle handle, std::string name) {
return AsPyTfObject<PyGraph>(handle)->get_operation_by_name(name);
});
c_graph.attr("get_operations") = method(c_graph, [](py::handle handle) {
auto ops = AsPyTfObject<PyGraph>(handle)->operations();
py::list copy;
for (auto& op : ops) {
copy.append(op);
}
return copy;
});
c_graph.attr("operations") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyGraph>(handle)->operations();
});
c_graph.attr("new_operations") = method(c_graph, [](py::handle handle) {
return AsPyTfObject<PyGraph>(handle)->new_operations();
});
c_graph.attr("num_operations") = method(c_graph, [](py::handle handle) {
return AsPyTfObject<PyGraph>(handle)->num_operations();
});
c_graph.attr("_add_op") =
method(c_graph, [](py::handle handle, py::object op) {
return AsPyTfObject<PyGraph>(handle)->add_op(op);
});
MakeTfObjectType<PyOperation>(&PyOperation::py_type);
py::object c_op = py::reinterpret_borrow<py::object>(PyOperation::py_type);
m.attr("PyOperation") = c_op;
c_op.attr("__module__") = module_name;
c_op.attr("_tf_output") = method(c_op, [](py::handle handle, int index) {
return AsPyTfObject<PyOperation>(handle)->_tf_output(index);
});
c_op.attr("_tf_input") = method(c_op, [](py::handle handle, int index) {
return AsPyTfObject<PyOperation>(handle)->_tf_input(index);
});
c_op.attr("_set_device_from_string") =
method(c_op, [](py::handle handle, std::string device) {
return AsPyTfObject<PyOperation>(handle)->set_device(device);
});
c_op.attr("_add_control_input") =
method(c_op, [](py::handle handle, py::handle input) {
return AsPyTfObject<PyOperation>(handle)->add_control_input(
AsPyTfObject<PyOperation>(input));
});
c_op.attr("_add_control_inputs") =
method(c_op, [](py::handle handle, py::iterable inputs) {
return AsPyTfObject<PyOperation>(handle)->add_control_inputs(inputs);
});
c_op.attr("_remove_all_control_inputs") = method(c_op, [](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->remove_all_control_inputs();
});
c_op.attr("outputs") = property_readonly([](py::handle handle) {
return AsPyTfObjectData<PyOperation>(handle)->outputs;
});
c_op.attr("graph") = property(
[](py::handle handle) {
return AsPyTfObjectData<PyOperation>(handle)->graph.borrow();
},
[](py::handle handle, py::handle graph) {
auto op = AsPyTfObject<PyOperation>(handle);
op->data->graph = graph.ptr();
});
c_op.attr("_c_op") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->tf_op();
});
c_op.attr("_is_stateful") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->is_stateful();
});
c_op.attr("_op_def") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->op_def();
});
c_op.attr("_node_def") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->node_def();
});
c_op.attr("type") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->type();
});
c_op.attr("name") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->name();
});
c_op.attr("device") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->device();
});
c_op.attr("_control_outputs") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->control_outputs();
});
c_op.attr("_init_outputs") = method(c_op, [](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->_init_outputs();
});
c_op.attr("_add_outputs") =
method(c_op, [](py::handle handle, py::list dtypes, py::list shapes) {
return AsPyTfObject<PyOperation>(handle)->_add_outputs(dtypes, shapes);
});
c_op.attr("control_inputs") = property_readonly(
[](py::handle handle) {
return AsPyTfObject<PyOperation>(handle)->control_inputs();
},
R"doc(
The `Operation` objects on which this op has a control dependency.
Before this op is executed, TensorFlow will ensure that the
operations in `self.control_inputs` have finished executing. This
mechanism can be used to run ops sequentially for performance
reasons, or to ensure that the side effects of an op are observed
in the correct order.
Returns:
A list of `Operation` objects.
)doc");
[&m, &module_name]() {
MakeTfObjectType<PyTensor>(&PyTensor::py_type);
py::object c_tensor = py::reinterpret_borrow<py::object>(PyTensor::py_type);
m.attr("PyTensor") = c_tensor;
c_tensor.attr("__module__") = module_name;
c_tensor.attr("device") = property_readonly([](py::handle handle) {
return AsPyTfObjectData<PyTensor>(handle)->op->device();
});
c_tensor.attr("ndim") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyTensor>(handle)->rank();
});
c_tensor.attr("_rank") = method(c_tensor, [](py::handle handle) {
return AsPyTfObject<PyTensor>(handle)->rank();
});
c_tensor.attr("_shape") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyTensor>(handle)->shape();
});
c_tensor.attr("_dtype") = property_readonly([](py::handle handle) {
return AsPyTfObjectData<PyTensor>(handle)->dtype;
});
c_tensor.attr("_name") = property(
[](py::handle handle) {
return AsPyTfObjectData<PyTensor>(handle)->name;
},
[](py::handle handle, py::object name) {
AsPyTfObjectData<PyTensor>(handle)->name = name;
});
c_tensor.attr("_shape_val") = property(
[](py::handle handle) {
auto py_tensor = AsPyTfObject<PyTensor>(handle);
return py_tensor->data->shape_val;
},
[](py::handle handle, py::object shape) {
AsPyTfObjectData<PyTensor>(handle)->shape_val = shape;
});
c_tensor.attr("_id") = property(
[](py::handle handle) {
return AsPyTfObjectData<PyTensor>(handle)->uid;
},
[](py::handle handle, py::object uid) {
AsPyTfObjectData<PyTensor>(handle)->uid = uid;
});
c_tensor.attr("graph") =
property_readonly([](py::handle handle) -> py::handle {
auto& graph = AsPyTfObjectData<PyTensor>(handle)->graph;
if (graph.ptr() != nullptr) {
return graph.borrow();
}
return py::none();
});
c_tensor.attr("_as_tf_output") = method(c_tensor, [](py::handle handle) {
return AsPyTfObjectData<PyTensor>(handle)->tf_output;
});
c_tensor.attr("_op") =
property_readonly([](py::handle handle) -> py::handle {
auto& op = AsPyTfObjectData<PyTensor>(handle)->op;
if (op.ptr() != nullptr) {
return op.borrow();
}
return py::none();
});
c_tensor.attr("op") =
property_readonly([](py::handle handle) -> py::handle {
auto& op = AsPyTfObjectData<PyTensor>(handle)->op;
if (op.ptr() != nullptr) {
return op.borrow();
}
return py::none();
});
c_tensor.attr("_set_shape") = method(c_tensor, [](py::handle handle,
py::iterable shape,
bool unknown_shape) {
return AsPyTfObject<PyTensor>(handle)->set_shape(shape, unknown_shape);
});
c_tensor.attr("value_index") = property_readonly([](py::handle handle) {
return AsPyTfObject<PyTensor>(handle)->value_index();
});
c_tensor.attr("consumers") = method(c_tensor, [](py::handle handle) {
return AsPyTfObject<PyTensor>(handle)->consumers();
});
}();
py::class_<TF_Operation, std::unique_ptr<TF_Operation, TF_OperationDeleter>>
TF_Operation_class(m, "TF_Operation");
py::class_<TF_Output>(m, "TF_Output")
.def(py::init<>())
.def_readwrite("oper", &TF_Output::oper)
.def_readwrite("index", &TF_Output::index);
py::class_<TF_Input>(m, "TF_Input")
.def(py::init<>())
.def_readwrite("oper", &TF_Input::oper)
.def_readwrite("index", &TF_Input::index);
py::class_<TF_ImportGraphDefOptions> TF_ImportGraphDefOptions_class(
m, "TF_ImportGraphDefOptions");
py::class_<TF_ImportGraphDefResults> TF_ImportGraphDefResults_class(
m, "TF_ImportGraphDefResults");
py::class_<TF_DeprecatedSession> TF_DeprecatedSession_class(
m, "TF_DeprecatedSession");
py::class_<TF_Session> TF_Session_class(m, "TF_Session");
py::class_<TF_OperationDescription> TF_OperationDescription_class(
m, "TF_OperationDescription");
py::class_<TF_Library> TF_Library_class(m, "TF_Library");
py::class_<TF_SessionOptions> TF_SessionOptions_class(m, "TF_SessionOptions");
py::class_<TF_Buffer> TF_Buffer_class(m, "TF_Buffer");
py::class_<TF_ApiDefMap> TF_ApiDefMap_class(m, "TF_ApiDefMap");
py::class_<TF_Server> TF_Server_class(m, "TF_Server");
py::class_<TF_Status> TF_Status_class(m, "TF_Status");
// We only release the Python GIL for certain methods that are
// not explicitly marked. We disable this behavior for some functions
// because they uses Python method(s) that expect the GIL to be held
// (at least PyArray_Return, maybe others).
// Do not release GIL.
m.def("TF_OperationGetControlOutputs_wrapper",
tensorflow::TF_OperationGetControlOutputs_wrapper);
// Do not release GIL.
m.def("GetOperationInputs", tensorflow::GetOperationInputs);
// Do not release GIL.
m.def("TF_ImportGraphDefOptionsSetValidateColocationConstraints",
TF_ImportGraphDefOptionsSetValidateColocationConstraints);
// Do not release GIL.
m.def("TF_ImportGraphDefResultsMissingUnusedInputMappings_wrapper",
tensorflow::TF_ImportGraphDefResultsMissingUnusedInputMappings_wrapper);
m.def("TF_SessionMakeCallable",
[](TF_Session* session, const TF_Buffer* callable_options) {
int64_t out_handle;
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
tensorflow::TF_SessionMakeCallable(session, callable_options,
&out_handle, status.get());
// Acquire GIL for returning int conversion.
pybind11::gil_scoped_acquire acquire;
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return out_handle;
});
m.def("_TF_SetTarget", TF_SetTarget);
m.def("_TF_SetConfig", [](TF_SessionOptions* options, py::bytes proto) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
tensorflow::Safe_TF_BufferPtr buf =
tensorflow::make_safe(ProtoStringToTFBuffer(proto.ptr()));
TF_SetConfig(options, buf.get()->data, buf.get()->length, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
});
m.def("_TF_NewSessionOptions", TF_NewSessionOptions,
py::return_value_policy::reference,
py::call_guard<py::gil_scoped_release>());
m.def("TF_DeleteSessionOptions", TF_DeleteSessionOptions,
py::call_guard<py::gil_scoped_release>());
m.def("EqualGraphDefWrapper", tensorflow::EqualGraphDefWrapper,
py::call_guard<py::gil_scoped_release>());
m.def("EqualAttrValueWrapper", tensorflow::EqualAttrValueWrapper,
py::call_guard<py::gil_scoped_release>());
m.def(
"TF_GraphToFunction_wrapper",
[](PyGraph* fn_body, const char* fn_name, bool append_hash_to_fn_name,
std::optional<std::vector<TF_Operation*>> opers_opt,
const std::vector<TF_Output>& inputs,
const std::vector<TF_Output>& outputs,
const std::vector<py::bytes> output_names,
const std::vector<TF_Operation*> control_outputs,
const std::vector<py::bytes> control_output_names, py::none opts,
const char* description) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// TODO(b/147674626): Use pybind11 list_caster instead.
tensorflow::NameVector output_names_name_vector =
ConvertPyListToNameVector(output_names);
// TODO(b/147674626): Use pybind11 list_caster instead.
tensorflow::NameVector control_output_names_name_vector =
ConvertPyListToNameVector(control_output_names);
// Release GIL.
py::gil_scoped_release release;
auto output = tensorflow::TF_GraphToFunction_wrapper(
fn_body->tf_graph(), fn_name, append_hash_to_fn_name,
opers_opt.has_value() ? &opers_opt.value() : nullptr, inputs,
outputs, output_names_name_vector, &control_outputs,
control_output_names_name_vector,
/*opts=*/nullptr, description, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return output;
},
py::return_value_policy::reference);
m.def("TF_GraphSetOutputHandleShapesAndTypes_wrapper",
[](PyGraph* graph, TF_Output output,
const std::vector<std::optional<std::vector<int64_t>>>& shapes,
const std::vector<int>& ranks, py::handle& types) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Cast types
std::vector<TF_DataType> types_local;
PyObject* seq =
PySequence_Fast(types.ptr(), "$symname: expected list");
if (seq == nullptr) {
PyErr_SetString(PyExc_RuntimeError,
"$symname: PySequence_Fast returned NULL.");
throw py::error_already_set();
}
int size = PySequence_Fast_GET_SIZE(seq);
if (size == 0) {
PyErr_SetString(PyExc_ValueError,
"$symname: shapes list must be non-empty");
throw py::error_already_set();
}
for (int i = 0; i < size; ++i) {
PyObject* item = PySequence_Fast_GET_ITEM(seq, i);
types_local.push_back((TF_DataType)PyLong_AsLong(item));
}
// Convert shapes nested vector
std::vector<std::vector<int64_t>> shapes_local;
for (size_t i = 0; i < shapes.size(); ++i) {
std::vector<int64_t> dims;
std::vector<int64_t> item =
shapes[i].has_value() ? shapes[i].value() : dims;
shapes_local.push_back(item);
}
Py_DECREF(seq);
tensorflow::TF_GraphSetOutputHandleShapesAndTypes_wrapper(
graph->tf_graph(), output, shapes_local, ranks, types_local,
status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
});
// Do not release GIL.
m.def("TF_CreatePlaceholders",
[](PyGraph* graph, py::handle& dtypes, const char* prefix) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
auto output = tensorflow::TF_CreatePlaceholders(
graph->tf_graph(), dtypes.ptr(), prefix, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
});
m.def(
"TF_NewSession",
[](PyGraph* graph, const TF_SessionOptions* opts) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
auto output = TF_NewSession(graph->tf_graph(), opts, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return output;
},
py::return_value_policy::reference);
m.def(
"TF_NewSessionRef",
[](PyGraph* graph, const TF_SessionOptions* opts) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
auto output =
tensorflow::TF_NewSessionRef(graph->tf_graph(), opts, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return output;
},
py::return_value_policy::reference);
m.def("TF_CloseSession", [](TF_Session* session) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_CloseSession(session, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_DeleteSession", [](TF_Session* session) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_DeleteSession(session, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
// Do not release GIL.
m.def("TF_TryEvaluateConstant_wrapper",
[](PyGraph* graph, const TF_Output output) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
auto result = tensorflow::TF_TryEvaluateConstant_wrapper(
graph->tf_graph(), output, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return tensorflow::PyoOrThrow(result);
});
m.def("ExtendSession", [](TF_Session* session) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL for threading.
pybind11::gil_scoped_release release;
tensorflow::ExtendSession(session, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("GetHandleShapeAndType", [](PyGraph* graph, TF_Output output) {
std::string output_string =
tensorflow::GetHandleShapeAndType(graph->tf_graph(), output);
// Override default py3 behavior of attempting to encode into Unicode as
// the dependent functions expect bytes.
return py::bytes(output_string);
});
m.def("SetHandleShapeAndType",
[](PyGraph* graph, TF_Output output, py::bytes proto) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
tensorflow::Safe_TF_BufferPtr buf =
tensorflow::make_safe(ProtoStringToTFBuffer(proto.ptr()));
tensorflow::SetHandleShapeAndType(graph->tf_graph(), output,
buf.get()->data, buf.get()->length,
status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
});
// Do not release GIL.
m.def("TF_SessionRun_wrapper", [](TF_Session* session, TF_Buffer* run_options,
const py::handle& input_dict,
const std::vector<TF_Output>& outputs,
const std::vector<TF_Operation*>& targets,
TF_Buffer* run_metadata) {
// Convert inputs dictionary
std::vector<TF_Output> inputs;
std::vector<PyObject*> input_ndarrays;
if (!PyDict_Check(input_dict.ptr())) {
PyErr_SetString(
PyExc_TypeError,
"Expected a dictionary as an argument to TF_SessionRun_wrapper.");
throw py::error_already_set();
}
PyObject* key;
PyObject* value;
Py_ssize_t pos = 0;
while (PyDict_Next(input_dict.ptr(), &pos, &key, &value)) {
TF_Output item = py::cast<TF_Output>(key);
inputs.push_back(item);
// TODO(amitpatankar): Fix this PyArray check. (b/147855599)
// if (!PyArray_Check(value)) {
// PyErr_SetString(
// PyExc_TypeError,
// "$symname: Expected all values in input dict to be ndarray.");
// throw py::error_already_set();
// }
input_ndarrays.push_back(value);
}
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
std::vector<PyObject*> py_outputs;
tensorflow::TF_SessionRun_wrapper(session, run_options, inputs,
input_ndarrays, outputs, targets,
run_metadata, status.get(), &py_outputs);
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
// Create a Python list using the C API rather than py::list. b/147855599
PyObject* result = PyList_New(py_outputs.size());
if (result == nullptr) {
PyErr_SetString(PyExc_MemoryError, "Failed to create a list.");
throw py::error_already_set();
}
for (size_t i = 0; i < py_outputs.size(); ++i) {
PyList_SET_ITEM(result, i, py_outputs.at(i));
}
return tensorflow::PyoOrThrow(result);
});
// Do not release GIL.
m.def("TF_SessionPRun_wrapper", [](TF_Session* session, const char* handle,
const py::handle& input_dict,
const std::vector<TF_Output>& outputs) {
// Convert inputs dictionary
std::vector<TF_Output> inputs;
std::vector<PyObject*> input_ndarrays;
if (!PyDict_Check(input_dict.ptr())) {
PyErr_SetString(
PyExc_TypeError,
"Expected a dictionary as an argument to TF_SessionPRun_wrapper.");
throw py::error_already_set();
}
PyObject* key;
PyObject* value;
Py_ssize_t pos = 0;
while (PyDict_Next(input_dict.ptr(), &pos, &key, &value)) {
TF_Output item = py::cast<TF_Output>(key);
inputs.push_back(item);
// TODO(amitpatankar): Fix this PyArray check. (b/147855599)
// if (!PyArray_Check(value)) {
// PyErr_SetString(
// PyExc_TypeError,
// "$symname: Expected all values in input dict to be ndarray.");
// throw py::error_already_set();
// }
input_ndarrays.push_back(value);
}
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
std::vector<PyObject*> py_outputs;
tensorflow::TF_SessionPRun_wrapper(session, handle, inputs, input_ndarrays,
outputs, status.get(), &py_outputs);
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
PyObject* result = PyList_New(py_outputs.size());
if (result == nullptr) {
PyErr_SetString(PyExc_MemoryError, "Failed to create a list.");
throw py::error_already_set();
}
for (size_t i = 0; i < py_outputs.size(); ++i) {
PyList_SET_ITEM(result, i, py_outputs.at(i));
}
return tensorflow::PyoOrThrow(result);
});
// Do not release GIL.
m.def("TF_SessionPRunSetup_wrapper",
[](TF_Session* session, const std::vector<TF_Output>& inputs,
const std::vector<TF_Output>& outputs,
const std::vector<TF_Operation*>& targets) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
const char* out_handle;
tensorflow::TF_SessionPRunSetup_wrapper(
session, inputs, outputs, targets, &out_handle, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return out_handle;
});
// Do not release GIL.
m.def("TF_SessionRunCallable", [](TF_Session* session, int64_t handle,
py::object feed_values,
TF_Buffer* run_metadata) {
tensorflow::PyObjectVector out_values;
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
tensorflow::TF_SessionRunCallable(session, handle, feed_values.ptr(),
&out_values, run_metadata, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
// Return out_values
py::list py_list;
for (size_t i = 0; i < out_values.size(); ++i) {
py::object obj = tensorflow::Pyo(out_values.at(i));
py_list.append(obj);
}
return py_list;
});
m.def("TF_SessionReleaseCallable", [](TF_Session* session, int64_t handle) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
tensorflow::TF_SessionReleaseCallable(session, handle, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def(
"TF_NewOperation",
[](PyGraph* graph, const char* op_type, const char* oper_name) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_OperationDescription* output =
TF_NewOperation(graph->tf_graph(), op_type, oper_name);
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return output;
},
py::return_value_policy::reference);
m.def(
"TF_FinishOperation",
[](TF_OperationDescription* desc) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_Operation* output = TF_FinishOperation(desc, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return output;
},
py::return_value_policy::reference);
m.def("TF_SetOpStackTrace",
[](TF_Operation* op,
std::shared_ptr<tensorflow::AbstractStackTrace> trace) {
op->node.SetStackTrace(trace);
});
m.def("TF_OperationGetAttrInt",
[](TF_Operation* oper, const char* attr_name) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
int64_t value;
// Release GIL.
py::gil_scoped_release release;
TF_OperationGetAttrInt(oper, attr_name, &value, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
// Convert TF_OperationGetAttrInt int64_t* out-argument to Python
// bool.
// Acquire GIL for returning output returning.
pybind11::gil_scoped_acquire acquire;
return tensorflow::Pyo(PyLong_FromLongLong(value));
});
m.def("TF_SetAttrValueProto", [](TF_OperationDescription* desc,
const char* attr_name, py::bytes proto) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
tensorflow::Safe_TF_BufferPtr buf =
tensorflow::make_safe(ProtoStringToTFBuffer(proto.ptr()));
TF_SetAttrValueProto(desc, attr_name, buf.get()->data, buf.get()->length,
status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
});
m.def("TF_OperationNumOutputs", TF_OperationNumOutputs,
py::call_guard<py::gil_scoped_release>());
// Convert types to ints
m.def("TF_OperationInputType", TF_OperationInputType,
py::call_guard<py::gil_scoped_release>());
m.def("TF_OperationOutputType", TF_OperationOutputType,
py::call_guard<py::gil_scoped_release>());
m.def("TF_OperationName", TF_OperationName,
py::call_guard<py::gil_scoped_release>());
m.def("TF_OperationOpType", TF_OperationOpType,
py::call_guard<py::gil_scoped_release>());
m.def("TF_OperationDevice", TF_OperationDevice,
py::call_guard<py::gil_scoped_release>());
m.def("TF_AddInput", TF_AddInput);
m.def(
"TF_AddInputList", [](TF_OperationDescription* desc, py::handle& inputs) {
std::vector<TF_Output> vec;
size_t size = PyList_Size(inputs.ptr());
for (size_t i = 0; i < size; ++i) {
TF_Output item = py::cast<TF_Output>(PyList_GetItem(inputs.ptr(), i));
vec.push_back(item);
}
TF_AddInputList(desc, vec.data(), vec.size());
});
m.def("TF_OperationToNodeDef",
[](TF_Operation* oper, TF_Buffer* output_node_def) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
TF_OperationToNodeDef(oper, output_node_def, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
});
m.def("TF_OperationGetAttrValueProto",
[](TF_Operation* oper, const char* attr_name,
TF_Buffer* output_attr_value) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
TF_OperationGetAttrValueProto(oper, attr_name, output_attr_value,
status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
});
m.def("TF_OperationGetStackTrace", [](TF_Operation* oper) -> py::object {
const std::shared_ptr<tensorflow::AbstractStackTrace> trace =
oper->node.GetStackTrace();
if (!trace) {
return py::none();
}
return py::cast(*trace, py::return_value_policy::reference);
});
// TF_Buffer util methods
// TODO(amitpatankar): Consolidate Buffer methods into a separate header
// file.
m.def("TF_NewBuffer", TF_NewBuffer, py::return_value_policy::reference);
m.def("TF_GetBuffer", [](TF_Buffer* buf) {
if (buf == nullptr) {
PyErr_SetString(PyExc_ValueError, "TF_Buffer is null.");
throw py::error_already_set();
}
TF_Buffer buffer = TF_GetBuffer(buf);
return tensorflow::PyoOrThrow(PyBytes_FromStringAndSize(
reinterpret_cast<const char*>(buffer.data), buffer.length));
});
m.def("TF_DeleteBuffer", &TF_DeleteBuffer);
m.def(
"TF_NewBufferFromString",
[](py::bytes buffer_as_string) {
tensorflow::Safe_TF_BufferPtr buf = tensorflow::make_safe(
ProtoStringToTFBuffer(buffer_as_string.ptr()));
return TF_NewBufferFromString(buf.get()->data, buf.get()->length);
},
py::return_value_policy::reference);
m.def("SetAttr", [](PyGraph* graph, TF_Operation* op, const char* attr_name,
TF_Buffer* attr_value_proto) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
tensorflow::SetAttr(graph->tf_graph(), op, attr_name, attr_value_proto,
status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("ClearAttr",
[](PyGraph* graph, TF_Operation* op, const char* attr_name) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
tensorflow::ClearAttr(graph->tf_graph(), op, attr_name, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
// Note: users should prefer using tf.cast or equivalent, and only when
// it's infeasible to set the type via OpDef's type constructor and
// inference function.
m.def("SetFullType",
[](PyGraph* graph, TF_Operation* op, const TF_Buffer* full_type_proto) {
tensorflow::SetFullType(graph->tf_graph(), op, full_type_proto);
});
m.def(
"TF_LoadLibrary",
[](const char* library_filename) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
auto output = TF_LoadLibrary(library_filename, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
},
py::return_value_policy::reference);
m.def(
"TF_LoadPluggableDeviceLibrary",
[](const char* library_filename) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
auto output =
TF_LoadPluggableDeviceLibrary(library_filename, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
},
py::return_value_policy::reference);
m.def("TF_GetOpList", [](TF_Library* lib_handle) {
TF_Buffer output_buffer = TF_GetOpList(lib_handle);
return tensorflow::PyoOrThrow(PyBytes_FromStringAndSize(
reinterpret_cast<const char*>(output_buffer.data),
output_buffer.length));
});
m.def("TF_DeleteLibraryHandle", TF_DeleteLibraryHandle,
py::call_guard<py::gil_scoped_release>());
m.def("TF_PluggableDeviceLibraryHandle",
TF_DeletePluggableDeviceLibraryHandle,
py::call_guard<py::gil_scoped_release>());
m.def("TF_AddControlInput", TF_AddControlInput);
m.def("UpdateEdge", [](PyGraph* graph, TF_Output new_src, TF_Input dst) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
tensorflow::UpdateEdge(graph->tf_graph(), new_src, dst, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_NewImportGraphDefOptions", TF_NewImportGraphDefOptions,
py::return_value_policy::reference,
py::call_guard<py::gil_scoped_release>());
m.def("TF_ImportGraphDefOptionsSetPrefix", TF_ImportGraphDefOptionsSetPrefix,
py::call_guard<py::gil_scoped_release>());
m.def("TF_ImportGraphDefOptionsSetUniquifyNames",
TF_ImportGraphDefOptionsSetUniquifyNames,
py::call_guard<py::gil_scoped_release>());
m.def("TF_ImportGraphDefOptionsSetPropagateDeviceSpec",
tensorflow::TF_ImportGraphDefOptionsSetPropagateDeviceSpec,
py::call_guard<py::gil_scoped_release>());
m.def("TF_ImportGraphDefOptionsRemapControlDependency",
TF_ImportGraphDefOptionsRemapControlDependency,
py::call_guard<py::gil_scoped_release>());
m.def("TF_ImportGraphDefOptionsAddInputMapping",
TF_ImportGraphDefOptionsAddInputMapping,
py::call_guard<py::gil_scoped_release>());
m.def("TF_ImportGraphDefOptionsAddReturnOperation",
TF_ImportGraphDefOptionsAddReturnOperation,
py::call_guard<py::gil_scoped_release>());
m.def("TF_ImportGraphDefOptionsAddReturnOutput",
TF_ImportGraphDefOptionsAddReturnOutput,
py::call_guard<py::gil_scoped_release>());
m.def(
"TF_GraphImportGraphDefWithResults",
[](PyGraph* graph, const TF_Buffer* graph_def,
const TF_ImportGraphDefOptions* options) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
auto output = TF_GraphImportGraphDefWithResults(
graph->tf_graph(), graph_def, options, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
},
py::return_value_policy::reference);
m.def(
"TF_GraphImportGraphDefWithResultsNoSerialization",
[](PyGraph* graph, const tensorflow::GraphDef* graph_def,
const TF_ImportGraphDefOptions* options) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
TF_ImportGraphDefResults* output;
{
TF_Buffer graph_def_buffer;
graph_def_buffer.data = reinterpret_cast<const void*>(graph_def);
graph_def_buffer.length = sizeof(tensorflow::GraphDef*);
output = TF_GraphImportGraphDefWithResultsNoSerialization(
graph->tf_graph(), &graph_def_buffer, options, status.get());
}
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
},
py::return_value_policy::reference);
m.def(
"TF_GraphNextOperation",
[](PyGraph* graph, size_t pos) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
auto output = TF_GraphNextOperation(graph->tf_graph(), &pos);
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
// Returns a (TF_Operation*, int pos) tuple.
py::tuple result_tuple = py::make_tuple(
py::cast(output), tensorflow::Pyo(PyLong_FromSize_t(pos)));
return result_tuple;
},
py::return_value_policy::reference);
// Python needs to own deletion of outputs
m.def("TF_ImportGraphDefResultsReturnOutputs",
[](TF_ImportGraphDefResults* results) {
int num_outputs;
TF_Output* outputs;
TF_ImportGraphDefResultsReturnOutputs(results, &num_outputs,
&outputs);
py::list py_list;
for (int i = 0; i < num_outputs; ++i) {
TF_Output tf_output = TF_Output(outputs[i]);
py_list.append(tf_output);
}
return py_list;
});
m.def(
"TF_ImportGraphDefResultsReturnOperations",
[](TF_ImportGraphDefResults* results) {
int num_opers;
TF_Operation** opers;
TF_ImportGraphDefResultsReturnOperations(results, &num_opers, &opers);
py::list py_list;
for (int i = 0; i < num_opers; ++i) {
py_list.append(opers[i]);
}
return py_list;
},
py::return_value_policy::reference);
m.def("TF_GraphToGraphDefPybind", [](PyGraph* graph) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_Graph* tf_graph = graph->tf_graph();
auto def = new tensorflow::GraphDef();
{
tensorflow::mutex_lock l(tf_graph->mu);
tf_graph->graph.ToGraphDef(def);
}
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return def;
});
m.def("TF_GraphToGraphDef", [](PyGraph* graph, TF_Buffer* output_graph_def) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_GraphToGraphDef(graph->tf_graph(), output_graph_def, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_OperationNumInputs", TF_OperationNumInputs,
py::call_guard<py::gil_scoped_release>());
m.def("TF_DeleteFunction", TF_DeleteFunction,
py::call_guard<py::gil_scoped_release>());
m.def("TF_DeleteImportGraphDefResults", TF_DeleteImportGraphDefResults,
py::call_guard<py::gil_scoped_release>());
m.def("TF_DeleteImportGraphDefOptions", TF_DeleteImportGraphDefOptions,
py::call_guard<py::gil_scoped_release>());
m.def("TF_FunctionSetAttrValueProto",
[](TF_Function* func, const char* attr_name, py::bytes proto) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
tensorflow::Safe_TF_BufferPtr buf =
tensorflow::make_safe(ProtoStringToTFBuffer(proto.ptr()));
// Release GIL.
py::gil_scoped_release release;
TF_FunctionSetAttrValueProto(func, attr_name, buf.get()->data,
buf.get()->length, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_FunctionToFunctionDef",
[](TF_Function* graph, TF_Buffer* output_func_def) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_FunctionToFunctionDef(graph, output_func_def, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_GraphCopyFunction",
[](PyGraph* graph, const TF_Function* func, const TF_Function* grad) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_GraphCopyFunction(graph->tf_graph(), func, grad, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_GraphRemoveFunction", [](PyGraph* graph, const char* func_name) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_GraphRemoveFunction(graph->tf_graph(), func_name, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def(
"TF_FunctionImportFunctionDef",
[](py::bytes proto) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
tensorflow::Safe_TF_BufferPtr buf =
tensorflow::make_safe(ProtoStringToTFBuffer(proto.ptr()));
// Release GIL.
py::gil_scoped_release release;
auto output = TF_FunctionImportFunctionDef(
buf.get()->data, buf.get()->length, status.get());
// Acquire GIL for returning output returning.
pybind11::gil_scoped_acquire acquire;
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
},
py::return_value_policy::reference);
m.def(
"TF_FunctionImportFunctionDefNoSerialization",
[](tensorflow::FunctionDef fdef) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_Function* func = new TF_Function();
func->record =
new tensorflow::FunctionRecord(std::move(fdef), {}, false);
status.get()->status = absl::OkStatus();
// Acquire GIL for returning output returning.
pybind11::gil_scoped_acquire acquire;
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return func;
},
py::return_value_policy::reference);
m.def("EqualAttrValueWrapper", tensorflow::EqualAttrValueWrapper,
py::call_guard<py::gil_scoped_release>());
m.def(
"TF_GetAllRegisteredKernels",
[]() {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
auto output = TF_GetAllRegisteredKernels(status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return output;
},
py::return_value_policy::reference);
m.def(
"TF_GetRegisteredKernelsForOp",
[](const char* name) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
auto output = TF_GetRegisteredKernelsForOp(name, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return output;
},
py::return_value_policy::reference);
m.def("TF_GetAllOpList", TF_GetAllOpList, py::return_value_policy::reference,
py::call_guard<py::gil_scoped_release>());
m.def(
"TF_NewApiDefMap",
[](TF_Buffer* op_list_buffer) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
auto output = TF_NewApiDefMap(op_list_buffer, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return output;
},
py::return_value_policy::reference);
m.def("TF_DeleteApiDefMap", TF_DeleteApiDefMap,
py::call_guard<py::gil_scoped_release>());
m.def(
"TF_ApiDefMapGet",
[](TF_ApiDefMap* api_def_map, const char* name, size_t name_len) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
auto output =
TF_ApiDefMapGet(api_def_map, name, name_len, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return output;
},
py::return_value_policy::reference);
m.def("TF_ApiDefMapPut",
[](TF_ApiDefMap* api_def_map, const char* name, size_t name_len) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_ApiDefMapPut(api_def_map, name, name_len, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_OperationGetAttrType",
[](TF_Operation* oper, const char* attr_name) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
TF_DataType value;
// Release GIL.
py::gil_scoped_release release;
TF_OperationGetAttrType(oper, attr_name, &value, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
return value;
});
m.def(
"TF_NewServer",
[](py::bytes proto) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
tensorflow::Safe_TF_BufferPtr buf =
tensorflow::make_safe(ProtoStringToTFBuffer(proto.ptr()));
TF_Server* output =
TF_NewServer(buf.get()->data, buf.get()->length, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
},
py::return_value_policy::reference);
m.def("TF_ServerStart", [](TF_Server* server) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL.
py::gil_scoped_release release;
TF_ServerStart(server, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_ServerStop", [](TF_Server* server) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL for threading.
py::gil_scoped_release release;
TF_ServerStop(server, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_ServerJoin", [](TF_Server* server) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL for threading.
py::gil_scoped_release release;
TF_ServerJoin(server, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def(
"TF_ServerTarget",
[](TF_Server* server) { return TF_ServerTarget(server); },
py::call_guard<py::gil_scoped_release>());
m.def(
"TF_SessionListDevices",
[](TF_Session* session) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
TF_DeviceList* output = TF_SessionListDevices(session, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
},
py::return_value_policy::reference);
m.def("TF_DeviceListCount",
[](const TF_DeviceList* list) { return TF_DeviceListCount(list); });
m.def("TF_DeviceListName", [](const TF_DeviceList* list, int index) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
const char* output = TF_DeviceListName(list, index, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
});
m.def("TF_DeviceListType", [](const TF_DeviceList* list, int index) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
const char* output = TF_DeviceListType(list, index, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
});
m.def("TF_DeviceListMemoryBytes", [](const TF_DeviceList* list, int index) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
int64_t output = TF_DeviceListMemoryBytes(list, index, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
});
m.def("TF_DeviceListIncarnation", [](const TF_DeviceList* list, int index) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
int64_t output = TF_DeviceListIncarnation(list, index, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
return output;
});
m.def("TF_SetDevice", TF_SetDevice);
m.def("TF_DeleteDeviceList", TF_DeleteDeviceList);
m.def("TF_OperationGetAttrBool",
[](TF_Operation* oper, const char* attr_name) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
unsigned char value;
// Release GIL for threading.
{
py::gil_scoped_release release;
TF_OperationGetAttrBool(oper, attr_name, &value, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
}
return tensorflow::Pyo(PyBool_FromLong(value));
});
m.def("TF_NewStatus", TF_NewStatus, py::return_value_policy::reference);
m.def("TF_DeleteStatus", TF_DeleteStatus);
m.def("TF_DeleteDeviceList", TF_DeleteDeviceList);
m.def("AddWhileInputHack",
[](PyGraph* graph, TF_Output new_src, TF_Operation* dst) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL for threading.
py::gil_scoped_release release;
tensorflow::AddWhileInputHack(graph->tf_graph(), new_src, dst,
status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_Reset_wrapper", [](const TF_SessionOptions* opt,
const std::vector<py::bytes> containers) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
// Release GIL for threading.
py::gil_scoped_release release;
tensorflow::NameVector containers_name_vector =
ConvertPyListToNameVector(containers);
tensorflow::TF_Reset_wrapper(opt, containers_name_vector, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatusWithGIL(status.get());
});
m.def("TF_GetCode", TF_GetCode);
m.def("TF_SetXlaAutoJitMode", TF_SetXlaAutoJitMode);
m.def("TF_GetXlaAutoJitEnabled", TF_GetXlaAutoJitEnabled);
m.def("TF_SetXlaEnableLazyCompilation", TF_SetXlaEnableLazyCompilation);
m.def("TF_SetTfXlaCpuGlobalJit", TF_SetTfXlaCpuGlobalJit);
m.def("TF_SetXlaMinClusterSize", TF_SetXlaMinClusterSize);
m.def("TF_GetXlaConstantFoldingDisabled", TF_GetXlaConstantFoldingDisabled);
m.def("TF_SetXlaConstantFoldingDisabled", TF_SetXlaConstantFoldingDisabled);
// // Static constants are not working on Windows. b/145559202
// // Creating getters instead.
m.def("get_version", []() { return TF_VERSION_STRING; });
m.def("get_git_version", []() { return TF_GIT_VERSION; });
m.def("get_compiler_version", []() { return TF_COMPILER_VERSION; });
m.def("get_cxx11_abi_flag", []() { return TF_CXX11_ABI_FLAG; });
m.def("get_cxx_version", []() { return TF_CXX_VERSION; });
m.def("get_eigen_max_align_bytes", []() { return EIGEN_MAX_ALIGN_BYTES; });
m.def("get_monolithic_build", []() { return TF_MONOLITHIC_BUILD; });
m.def("get_graph_def_version", []() { return TF_GRAPH_DEF_VERSION; });
m.def("get_graph_def_version_min_consumer",
[]() { return TF_GRAPH_DEF_VERSION_MIN_CONSUMER; });
m.def("get_graph_def_version_min_producer",
[]() { return TF_GRAPH_DEF_VERSION_MIN_PRODUCER; });
m.def("get_tensor_handle_key", []() {
// TODO(amitpatankar): Look into a more elegant solution.
// Since this is a shared object we will hard code the value from
// third_party/tensorflow/core/common_runtime/session_state.cc because
// the Windows import will not load the libraries necessarily
// in order. b/145559202
return "TensorHandle";
});
m.def("TF_RegisterFilesystemPlugin", [](const char* plugin_filename) {
tensorflow::Safe_TF_StatusPtr status =
tensorflow::make_safe(TF_NewStatus());
TF_RegisterFilesystemPlugin(plugin_filename, status.get());
tensorflow::MaybeRaiseRegisteredFromTFStatus(status.get());
});
py::enum_<TF_DataType>(m, "TF_DataType")
.value("TF_FLOAT", TF_FLOAT)
.value("TF_DOUBLE", TF_DOUBLE)
.value("TF_INT32", TF_INT32)
.value("TF_UINT8", TF_UINT8)
.value("TF_INT16", TF_INT16)
.value("TF_INT8", TF_INT8)
.value("TF_STRING", TF_STRING)
.value("TF_COMPLEX64", TF_COMPLEX64)
.value("TF_COMPLEX", TF_COMPLEX)
.value("TF_INT64", TF_INT64)
.value("TF_BOOL", TF_BOOL)
.value("TF_QINT8", TF_QINT8)
.value("TF_QUINT8", TF_QUINT8)
.value("TF_QINT32", TF_QINT32)
.value("TF_BFLOAT16", TF_BFLOAT16)
.value("TF_QINT16", TF_QINT16)
.value("TF_QUINT16", TF_QUINT16)
.value("TF_UINT16", TF_UINT16)
.value("TF_COMPLEX128", TF_COMPLEX128)
.value("TF_HALF", TF_HALF)
.value("TF_RESOURCE", TF_RESOURCE)
.value("TF_VARIANT", TF_VARIANT)
.value("TF_UINT32", TF_UINT32)
.value("TF_UINT64", TF_UINT64)
.export_values();
py::enum_<TF_Code>(m, "TF_Code")
.value("TF_OK", TF_OK)
.value("TF_CANCELLED", TF_CANCELLED)
.value("TF_UNKNOWN", TF_UNKNOWN)
.value("TF_INVALID_ARGUMENT", TF_INVALID_ARGUMENT)
.value("TF_DEADLINE_EXCEEDED", TF_DEADLINE_EXCEEDED)
.value("TF_PERMISSION_DENIED", TF_PERMISSION_DENIED)
.value("TF_UNAUTHENTICATED", TF_UNAUTHENTICATED)
.value("TF_RESOURCE_EXHAUSTED", TF_RESOURCE_EXHAUSTED)
.value("TF_FAILED_PRECONDITION", TF_FAILED_PRECONDITION)
.value("TF_ABORTED", TF_ABORTED)
.value("TF_OUT_OF_RANGE", TF_OUT_OF_RANGE)
.value("TF_UNIMPLEMENTED", TF_UNIMPLEMENTED)
.value("TF_INTERNAL", TF_INTERNAL)
.value("TF_DATA_LOSS", TF_DATA_LOSS)
.export_values();
};