Skip to content

Commit

Permalink
Major status_casters.h internal refactoring.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 426017387
  • Loading branch information
Ralf W. Grosse-Kunstleve authored and copybara-github committed Feb 3, 2022
1 parent 9490b25 commit 1bb411e
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 208 deletions.
59 changes: 51 additions & 8 deletions pybind11_abseil/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
],
)
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
// 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() {
Expand All @@ -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_
29 changes: 29 additions & 0 deletions pybind11_abseil/import_status_module.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#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
26 changes: 26 additions & 0 deletions pybind11_abseil/import_status_module.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#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_
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
#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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -171,5 +180,6 @@ void RegisterStatusBindings(module m) {
});
}

} // namespace internal
} // namespace google
} // namespace pybind11
18 changes: 18 additions & 0 deletions pybind11_abseil/register_status_bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#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_
4 changes: 2 additions & 2 deletions pybind11_abseil/status.cc
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#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
93 changes: 93 additions & 0 deletions pybind11_abseil/status_caster.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// 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_
Loading

0 comments on commit 1bb411e

Please sign in to comment.