Commit 1bb411eb by Ralf W. Grosse-Kunstleve Committed by Copybara-Service

Major status_casters.h internal refactoring.

PiperOrigin-RevId: 426017387
parent 9490b257
......@@ -34,12 +34,19 @@ cc_library(
)
pybind_library(
name = "status_utils",
srcs = ["status_utils.cc"],
hdrs = ["status_utils.h"],
name = "check_status_module_imported",
hdrs = ["check_status_module_imported.h"],
visibility = ["//visibility:private"],
deps = [
":absl_casters",
"@com_google_absl//absl/status",
],
)
pybind_library(
name = "status_caster",
hdrs = ["status_caster.h"],
deps = [
":check_status_module_imported",
":no_throw_status",
":status_not_ok_exception",
"@com_google_absl//absl/status",
......@@ -48,17 +55,53 @@ pybind_library(
)
pybind_library(
name = "status_casters",
hdrs = ["status_casters.h"],
name = "statusor_caster",
hdrs = ["statusor_caster.h"],
deps = [
":status_utils",
":check_status_module_imported",
":no_throw_status",
":status_caster",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
],
)
pybind_library(
name = "register_status_bindings",
srcs = ["register_status_bindings.cc"],
hdrs = ["register_status_bindings.h"],
visibility = ["//visibility:private"],
deps = [
":absl_casters",
":status_not_ok_exception",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
],
)
pybind_extension(
name = "status",
srcs = ["status.cc"],
deps = [":status_utils"],
deps = [":register_status_bindings"],
)
pybind_library(
name = "import_status_module",
srcs = ["import_status_module.cc"],
hdrs = ["import_status_module.h"],
deps = [
":check_status_module_imported",
":register_status_bindings",
"@com_google_absl//absl/status",
],
)
pybind_library(
name = "status_casters",
hdrs = ["status_casters.h"],
deps = [
":import_status_module",
":status_caster",
":statusor_caster",
],
)
// Utility classes functions for absl::Status objects.
// These are needed by both the status module and casters.
#ifndef PYBIND11_ABSEIL_STATUS_UTILS_H_
#define PYBIND11_ABSEIL_STATUS_UTILS_H_
#ifndef PYBIND11_ABSEIL_CHECK_STATUS_MODULE_IMPORTED_H_
#define PYBIND11_ABSEIL_CHECK_STATUS_MODULE_IMPORTED_H_
#include <pybind11/pybind11.h>
#include <exception>
#include <stdexcept>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "pybind11_abseil/no_throw_status.h"
#include "pybind11_abseil/status_not_ok_exception.h"
namespace pybind11 {
namespace google {
// Registers the bindings for the status types in the given module. Can only
// be called once; subsequent calls will fail due to duplicate registrations.
void RegisterStatusBindings(module m);
// If modifying the functions below, see
// g3doc/pybind11_abseil/README.md#importing-the-status-module
namespace internal {
// Returns true if the status module has been imported.
inline bool IsStatusModuleImported() {
......@@ -39,7 +25,8 @@ inline void CheckStatusModuleImported() {
#endif
}
} // namespace internal
} // namespace google
} // namespace pybind11
#endif // PYBIND11_ABSEIL_STATUS_UTILS_H_
#endif // PYBIND11_ABSEIL_CHECK_STATUS_MODULE_IMPORTED_H_
#include "pybind11_abseil/import_status_module.h"
#include <pybind11/pybind11.h>
#include "absl/status/status.h"
#include "pybind11_abseil/check_status_module_imported.h"
#include "pybind11_abseil/register_status_bindings.h"
namespace pybind11 {
namespace google {
module ImportStatusModule(bool bypass_regular_import) {
if (!PyGILState_Check()) {
pybind11_fail("ImportStatusModule() PyGILState_Check() failure.");
}
if (bypass_regular_import) {
auto m = reinterpret_borrow<module>(PyImport_AddModule(
PYBIND11_TOSTRING(PYBIND11_ABSEIL_STATUS_MODULE_PATH)));
if (!internal::IsStatusModuleImported()) {
internal::RegisterStatusBindings(m);
}
// else no-op because bindings are already loaded.
return m;
}
return module::import(PYBIND11_TOSTRING(PYBIND11_ABSEIL_STATUS_MODULE_PATH));
}
} // namespace google
} // namespace pybind11
#ifndef PYBIND11_ABSEIL_IMPORT_STATUS_MODULE_H_
#define PYBIND11_ABSEIL_IMPORT_STATUS_MODULE_H_
#include <pybind11/pybind11.h>
// The value of PYBIND11_ABSEIL_STATUS_MODULE_PATH will be different depending
// on whether this is being used inside or outside of google3. The value used
// inside of google3 is defined here. Outside of google3, change this value by
// passing "-DPYBIND11_ABSEIL_STATUS_MODULE_PATH=..." on the commandline.
#ifndef PYBIND11_ABSEIL_STATUS_MODULE_PATH
#define PYBIND11_ABSEIL_STATUS_MODULE_PATH \
pybind11_abseil.status
#endif
namespace pybind11 {
namespace google {
// Imports the bindings for the status types. This is meant to only be called
// from a PYBIND11_MODULE definition. The Python GIL must be held when calling
// this function (enforced).
module ImportStatusModule(bool bypass_regular_import = true);
} // namespace google
} // namespace pybind11
#endif // PYBIND11_ABSEIL_IMPORT_STATUS_MODULE_H_
#include "pybind11_abseil/status_utils.h"
#include "pybind11_abseil/register_status_bindings.h"
#include <pybind11/pybind11.h>
#include <exception>
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "pybind11_abseil/absl_casters.h"
#include "pybind11_abseil/status_not_ok_exception.h"
namespace pybind11 {
namespace google {
......@@ -90,6 +97,8 @@ class exception_with_attributes : public exception<type> {
} // namespace
namespace internal {
void RegisterStatusBindings(module m) {
enum_<absl::StatusCode>(m, "StatusCode")
.value("OK", absl::StatusCode::kOk)
......@@ -171,5 +180,6 @@ void RegisterStatusBindings(module m) {
});
}
} // namespace internal
} // namespace google
} // namespace pybind11
#ifndef PYBIND11_ABSEIL_REGISTER_STATUS_BINDINGS_H_
#define PYBIND11_ABSEIL_REGISTER_STATUS_BINDINGS_H_
#include <pybind11/pybind11.h>
namespace pybind11 {
namespace google {
namespace internal {
// Registers the bindings for the status types in the given module. Can only
// be called once; subsequent calls will fail due to duplicate registrations.
void RegisterStatusBindings(module m);
} // namespace internal
} // namespace google
} // namespace pybind11
#endif // PYBIND11_ABSEIL_REGISTER_STATUS_BINDINGS_H_
#include <pybind11/pybind11.h>
#include "pybind11_abseil/status_utils.h"
#include "pybind11_abseil/register_status_bindings.h"
namespace pybind11 {
namespace google {
PYBIND11_MODULE(status, m) { RegisterStatusBindings(m); }
PYBIND11_MODULE(status, m) { internal::RegisterStatusBindings(m); }
} // namespace google
} // namespace pybind11
// Author: Ken Oslund (kenoslund@)
#ifndef PYBIND11_ABSEIL_STATUS_CASTER_H_
#define PYBIND11_ABSEIL_STATUS_CASTER_H_
#include <pybind11/pybind11.h>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "absl/status/status.h"
#include "pybind11_abseil/check_status_module_imported.h"
#include "pybind11_abseil/no_throw_status.h"
#include "pybind11_abseil/status_not_ok_exception.h"
namespace pybind11 {
namespace detail {
template <typename StatusType>
struct NoThrowStatusType {
// NoThrowStatus should only wrap absl::Status or absl::StatusOr.
using NoThrowAbslStatus = type_caster_base<absl::Status>;
static constexpr auto name = NoThrowAbslStatus::name;
};
// Convert NoThrowStatus by dispatching to a caster for StatusType with the
// argument throw_exception = false. StatusType should be an absl::Status
// (rvalue, lvalue, reference, or pointer), or an absl::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 = NoThrowStatusType<StatusType>::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 const off the InputType.
return StatusCaster::cast(
std::forward<StatusType>(const_cast<InputType&>(src).status), policy,
parent, false);
}
};
// Convert absl::Status.
template <>
struct type_caster<absl::Status> : public type_caster_base<absl::Status> {
public:
static constexpr auto name = _("None");
// Convert C++ -> Python.
static handle cast(const absl::Status* src, return_value_policy policy,
handle parent, bool throw_exception = true) {
if (!src) return none().release();
return cast_impl(*src, policy, parent, throw_exception);
}
static handle cast(const absl::Status& src, return_value_policy policy,
handle parent, bool throw_exception = true) {
return cast_impl(src, policy, parent, throw_exception);
}
static handle cast(absl::Status&& src, return_value_policy policy,
handle parent, bool throw_exception = true) {
return cast_impl(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::internal::CheckStatusModuleImported();
if (!throw_exception) {
// Use the built-in/standard pybind11 caster.
return type_caster_base<absl::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();
}
}
};
} // namespace detail
} // namespace pybind11
#endif // PYBIND11_ABSEIL_STATUS_CASTER_H_
......@@ -17,184 +17,8 @@
#ifndef PYBIND11_ABSEIL_STATUS_CASTERS_H_
#define PYBIND11_ABSEIL_STATUS_CASTERS_H_
#include <pybind11/cast.h>
#include <pybind11/pybind11.h>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "pybind11_abseil/status_utils.h"
namespace pybind11 {
namespace google {
// The value of PYBIND11_ABSEIL_STATUS_MODULE_PATH will be different depending
// on whether this is being used inside or outside of google3. The value used
// inside of google3 is defined here. Outside of google3, change this value by
// passing "-DPYBIND11_ABSEIL_STATUS_MODULE_PATH=..." on the commandline.
#ifndef PYBIND11_ABSEIL_STATUS_MODULE_PATH
#define PYBIND11_ABSEIL_STATUS_MODULE_PATH \
pybind11_abseil.status
#endif
// Imports the bindings for the status types. This not thread safe and should
// only be called from a PYBIND11_MODULE definition. If modifying this,
// see g3doc/pybind11_abseil/README.md#importing-the-status-module
inline module ImportStatusModule() {
#if PY_MAJOR_VERSION >= 3
auto m = reinterpret_borrow<module>(PyImport_AddModule(
PYBIND11_TOSTRING(PYBIND11_ABSEIL_STATUS_MODULE_PATH)));
if (!IsStatusModuleImported()) RegisterStatusBindings(m);
// else no-op because bindings are already loaded.
return m;
#else
return module::import(PYBIND11_TOSTRING(PYBIND11_ABSEIL_STATUS_MODULE_PATH));
#endif
}
} // namespace google
namespace detail {
template <typename StatusType>
struct NoThrowStatusType {
// NoThrowStatus should only wrap absl::Status or absl::StatusOr.
using NoThrowAbslStatus = type_caster_base<absl::Status>;
static constexpr auto name = NoThrowAbslStatus::name;
};
template <typename PayloadType>
struct NoThrowStatusType<absl::StatusOr<PayloadType>> {
using NoThrowAbslStatus = type_caster_base<absl::Status>;
static constexpr auto name = _("Union[") + NoThrowAbslStatus::name + _(", ") +
make_caster<PayloadType>::name + _("]");
};
// Convert NoThrowStatus by dispatching to a caster for StatusType with the
// argument throw_exception = false. StatusType should be an absl::Status
// (rvalue, lvalue, reference, or pointer), or an absl::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 = NoThrowStatusType<StatusType>::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 const off the InputType.
return StatusCaster::cast(
std::forward<StatusType>(const_cast<InputType&>(src).status), policy,
parent, false);
}
};
// Convert absl::Status.
template <>
struct type_caster<absl::Status> : public type_caster_base<absl::Status> {
public:
static constexpr auto name = _("None");
// Convert C++ -> Python.
static handle cast(const absl::Status* src, return_value_policy policy,
handle parent, bool throw_exception = true) {
if (!src) return none().release();
return cast_impl(*src, policy, parent, throw_exception);
}
static handle cast(const absl::Status& src, return_value_policy policy,
handle parent, bool throw_exception = true) {
return cast_impl(src, policy, parent, throw_exception);
}
static handle cast(absl::Status&& src, return_value_policy policy,
handle parent, bool throw_exception = true) {
return cast_impl(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::CheckStatusModuleImported();
if (!throw_exception) {
// Use the built-in/standard pybind11 caster.
return type_caster_base<absl::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 absl::StatusOr<T>.
// It isn't possible to specify separate return value policies for the container
// (StatusOr) and the payload. Since StatusOr is processed and not ever actually
// represented in python, the return value policy applies to the payload. Eg, if
// you return a StatusOr<MyObject*> (note the * is inside the StatusOr) with a
// take_ownership return val policy and the status is ok (ie, it has a payload),
// python will take ownership of that payload and free it when it is garbage
// collected.
// However, if you return a StatusOr<MyObject>* (note the * is outside the
// StatusOr rather than inside it now) with a take_ownership return val policy,
// python does not take ownership of the StatusOr and will not free it (because
// again, that policy applies to MyObject, not StatusOr).
template <typename PayloadType>
struct type_caster<absl::StatusOr<PayloadType>> {
public:
using PayloadCaster = make_caster<PayloadType>;
using StatusCaster = make_caster<absl::Status>;
static constexpr auto name = PayloadCaster::name;
// Convert C++ -> Python.
static handle cast(const absl::StatusOr<PayloadType>* src,
return_value_policy policy, handle parent,
bool throw_exception = true) {
if (!src) return none().release();
return cast_impl(*src, policy, parent, throw_exception);
}
static handle cast(const absl::StatusOr<PayloadType>& src,
return_value_policy policy, handle parent,
bool throw_exception = true) {
return cast_impl(src, policy, parent, throw_exception);
}
static handle cast(absl::StatusOr<PayloadType>&& src,
return_value_policy policy, handle parent,
bool throw_exception = true) {
return cast_impl(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::CheckStatusModuleImported();
if (src.ok()) {
// Convert and return the payload.
return PayloadCaster::cast(std::forward<CType>(src).value(), policy,
parent);
} else {
// Convert and return the error.
return StatusCaster::cast(std::forward<CType>(src).status(),
return_value_policy::move, parent,
throw_exception);
}
}
};
} // namespace detail
} // namespace pybind11
#include "pybind11_abseil/status_caster.h"
#include "pybind11_abseil/statusor_caster.h"
#include "pybind11_abseil/import_status_module.h"
#endif // PYBIND11_ABSEIL_STATUS_CASTERS_H_
......@@ -2,6 +2,9 @@
#define PYBIND11_ABSEIL_STATUS_NOT_OK_EXCEPTION_H_
#include <exception>
#include <string>
#include <utility>
#include "absl/status/status.h"
namespace pybind11 {
......
// Author: Ken Oslund (kenoslund@)
#ifndef PYBIND11_ABSEIL_STATUSOR_CASTER_H_
#define PYBIND11_ABSEIL_STATUSOR_CASTER_H_
#include <pybind11/pybind11.h>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "pybind11_abseil/check_status_module_imported.h"
#include "pybind11_abseil/no_throw_status.h"
#include "pybind11_abseil/status_caster.h"
#include "pybind11_abseil/statusor_caster.h"
namespace pybind11 {
namespace detail {
template <typename PayloadType>
struct NoThrowStatusType<absl::StatusOr<PayloadType>> {
using NoThrowAbslStatus = type_caster_base<absl::Status>;
static constexpr auto name = _("Union[") + NoThrowAbslStatus::name + _(", ") +
make_caster<PayloadType>::name + _("]");
};
// Convert absl::StatusOr<T>.
// It isn't possible to specify separate return value policies for the container
// (StatusOr) and the payload. Since StatusOr is processed and not ever actually
// represented in python, the return value policy applies to the payload. Eg, if
// you return a StatusOr<MyObject*> (note the * is inside the StatusOr) with a
// take_ownership return val policy and the status is ok (ie, it has a payload),
// python will take ownership of that payload and free it when it is garbage
// collected.
// However, if you return a StatusOr<MyObject>* (note the * is outside the
// StatusOr rather than inside it now) with a take_ownership return val policy,
// python does not take ownership of the StatusOr and will not free it (because
// again, that policy applies to MyObject, not StatusOr).
template <typename PayloadType>
struct type_caster<absl::StatusOr<PayloadType>> {
public:
using PayloadCaster = make_caster<PayloadType>;
using StatusCaster = make_caster<absl::Status>;
static constexpr auto name = PayloadCaster::name;
// Convert C++ -> Python.
static handle cast(const absl::StatusOr<PayloadType>* src,
return_value_policy policy, handle parent,
bool throw_exception = true) {
if (!src) return none().release();
return cast_impl(*src, policy, parent, throw_exception);
}
static handle cast(const absl::StatusOr<PayloadType>& src,
return_value_policy policy, handle parent,
bool throw_exception = true) {
return cast_impl(src, policy, parent, throw_exception);
}
static handle cast(absl::StatusOr<PayloadType>&& src,
return_value_policy policy, handle parent,
bool throw_exception = true) {
return cast_impl(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::internal::CheckStatusModuleImported();
if (src.ok()) {
// Convert and return the payload.
return PayloadCaster::cast(std::forward<CType>(src).value(), policy,
parent);
} else {
// Convert and return the error.
return StatusCaster::cast(std::forward<CType>(src).status(),
return_value_policy::move, parent,
throw_exception);
}
}
};
} // namespace detail
} // namespace pybind11
#endif // PYBIND11_ABSEIL_STATUSOR_CASTER_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment