blob: 1e57f9990535cfe977172ca37fd3e8a1543bbd89 [file] [log] [blame]
// Copyright 2019 Google Inc.
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////
// Type conversion utilities for pybind11 and util::Status/StatusOr.
//
// Usage: Just include this file in the .cc file with your bindings and add the
// appropriate dependency. Any functions which take or return the supported
// types will have those types automatically converted.
//
// Supported types:
// - util::Status- converts a non-ok return status into a python exception.
// Can be passed as an argument too if you import the status pybind module.
// - util::StatusOr- converts a non-ok return status into a python exception,
// otherwise converts/returns the payload. Can only be used as a return value.
//
// Author: Ken Oslund (kenoslund@)
// Adapted for tink: rwgk@
// TODO(b/146426040): Remove this code when OSS absl::Status, StatusOr
// are available.
#ifndef TINK_PYTHON_CC_PYBIND_STATUS_CASTERS_H_
#define TINK_PYTHON_CC_PYBIND_STATUS_CASTERS_H_
#include <pybind11/cast.h>
#include <pybind11/pybind11.h>
#include <stdexcept>
#include <utility>
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "tink/cc/pybind/status_utils.h"
namespace pybind11 {
namespace detail {
namespace util = crypto::tink::util;
// Convert NoThrowStatus by dispatching to a caster for StatusType with the
// argument throw_exception = false. StatusType should be a util::Status
// (rvalue, lvalue, reference, or pointer), or a util::StatusOr value.
// Only return values trigger exceptions, so NoThrowStatus has no meaning for
// input values. Therefore only C++->Python casting is supported.
template <typename StatusType>
struct type_caster<google::NoThrowStatus<StatusType>> {
using InputType = google::NoThrowStatus<StatusType>;
using StatusCaster = make_caster<StatusType>;
static constexpr auto name = StatusCaster::name;
// Convert C++->Python.
static handle cast(const InputType& src, return_value_policy policy,
handle parent) {
// pybind11::cast applies a const qualifier, so this takes a const reference
// argument. The qualifiers we care about are in StatusType, and we will
// forward those, but to do so, we must strip the cost off the InputType.
return StatusCaster::cast(
std::forward<StatusType>(const_cast<InputType&>(src).status), policy,
parent, false);
}
};
// Convert util::Status.
template <>
struct type_caster<util::Status> : public type_caster_base<util::Status> {
public:
// Conversion part 1 (Python->C++) handled by built in caster.
bool load(handle src, bool convert) {
google::ImportStatusModule();
return type_caster_base<util::Status>::load(src, convert);
}
// Conversion part 2 (C++ -> Python)
static handle cast(const util::Status* src, return_value_policy policy,
handle parent, bool throw_exception = true) {
if (!src) return none().release();
return cast_impl<const util::Status&>(*src, policy, parent,
throw_exception);
}
static handle cast(const util::Status& src, return_value_policy policy,
handle parent, bool throw_exception = true) {
return cast_impl<const util::Status&>(src, policy, parent, throw_exception);
}
static handle cast(util::Status&& src, return_value_policy policy,
handle parent, bool throw_exception = true) {
return cast_impl<util::Status&&>(std::move(src), policy, parent,
throw_exception);
}
private:
template <typename CType>
static handle cast_impl(CType src, return_value_policy policy, handle parent,
bool throw_exception) {
google::ImportStatusModule();
if (!throw_exception) {
// Use the built-in/standard pybind11 caster.
return type_caster_base<util::Status>::cast(std::forward<CType>(src),
policy, parent);
} else if (!src.ok()) {
// Convert a non-ok status into an exception.
throw google::StatusNotOk(std::forward<CType>(src));
} else {
// Return none for an ok status.
return none().release();
}
}
};
// Convert a util::StatusOr.
template <typename PayloadType>
struct type_caster<util::StatusOr<PayloadType>> {
public:
using PayloadCaster = make_caster<PayloadType>;
using StatusCaster = make_caster<util::Status>;
static constexpr auto name = _("StatusOr[") + PayloadCaster::name + _("]");
// Conversion part 2 (C++ -> Python).
static handle cast(util::StatusOr<PayloadType>&& src,
return_value_policy policy, handle parent,
bool throw_exception = true) {
if (src.ok()) {
// Convert and return the payload.
return PayloadCaster::cast(std::forward<PayloadType>(src.ValueOrDie()),
policy, parent);
} else {
// Convert and return the error.
return StatusCaster::cast(std::move(src.status()),
return_value_policy::move, parent,
throw_exception);
}
}
};
} // namespace detail
} // namespace pybind11
#endif // TINK_PYTHON_CC_PYBIND_STATUS_CASTERS_H_