Commit 0c45d0f5 by Ralf W. Grosse-Kunstleve Committed by Copybara-Service

third_party/pybind11_abseil `StatusNotOk` convergence with the Google-internal type, step 1.

This change preserves the established behavior, but sets the stage for follow-on convergence steps.

PiperOrigin-RevId: 473058712
parent bfadbe3a
#include "pybind11_abseil/register_status_bindings.h" #include "pybind11_abseil/register_status_bindings.h"
#include <pybind11/embed.h>
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <exception> #include <exception>
...@@ -93,16 +94,6 @@ void def_status_factory( ...@@ -93,16 +94,6 @@ void def_status_factory(
arg("message")); arg("message"));
} }
object BuildStatusNotOk(handle PyStatusNotOk, const StatusNotOk& e) {
object py_e = PyStatusNotOk(e.what());
py_e.attr("status") = cast(google::NoThrowStatus<absl::Status>(e.status()));
// code is int by choice. Sorry it would be a major API break to make this an
// enum. Use status.code() to obtain the enum.
py_e.attr("code") = cast(e.status().raw_code());
py_e.attr("message") = cast(e.status().message());
return py_e;
}
} // namespace } // namespace
namespace internal { namespace internal {
...@@ -142,6 +133,10 @@ void RegisterStatusBindings(module m) { ...@@ -142,6 +133,10 @@ void RegisterStatusBindings(module m) {
arg("other")) arg("other"))
.def("to_string", [](const absl::Status& s) { return s.ToString(); }) .def("to_string", [](const absl::Status& s) { return s.ToString(); })
.def("__repr__", [](const absl::Status& s) { return s.ToString(); }) .def("__repr__", [](const absl::Status& s) { return s.ToString(); })
.def("to_string_status_not_ok",
[](const absl::Status& s) {
return s.ToString();
})
.def_static("OkStatus", DoNotThrowStatus(&absl::OkStatus)) .def_static("OkStatus", DoNotThrowStatus(&absl::OkStatus))
.def("raw_code", &absl::Status::raw_code) .def("raw_code", &absl::Status::raw_code)
.def("CanonicalCode", [](const absl::Status& self) { .def("CanonicalCode", [](const absl::Status& self) {
...@@ -176,8 +171,39 @@ void RegisterStatusBindings(module m) { ...@@ -176,8 +171,39 @@ void RegisterStatusBindings(module m) {
def_status_factory(m, "unimplemented_error", WrapUnimplementedError); def_status_factory(m, "unimplemented_error", WrapUnimplementedError);
def_status_factory(m, "unknown_error", WrapUnknownError); def_status_factory(m, "unknown_error", WrapUnknownError);
// Generate the Python exception type. pybind11::exec(R"(
static exception<StatusNotOk> PyStatusNotOk(m, "StatusNotOk"); class StatusNotOk(Exception):
def __init__(self, *args):
if len(args) != 1 or isinstance(args[0], str):
# THIS WILL BECOME AN ERROR IN THE FUTURE.
self._status = None
Exception.__init__(self, *args)
return
status = args[0]
assert status is not None
assert not status.ok()
self._status = status
Exception.__init__(self, status.to_string_status_not_ok())
@property
def status(self):
assert self._status is not None
return self._status
@property
def code(self):
assert self._status is not None
# code is int by choice. Sorry it would be a major API break to make
# this an enum.
return self._status.raw_code()
@property
def message(self):
assert self._status is not None
return self._status.message()
)",
m.attr("__dict__"), m.attr("__dict__"));
static pybind11::object PyStatusNotOk = m.attr("StatusNotOk");
// Register a custom handler which converts a C++ StatusNotOk to a // Register a custom handler which converts a C++ StatusNotOk to a
// PyStatusNotOk. // PyStatusNotOk.
...@@ -185,15 +211,15 @@ void RegisterStatusBindings(module m) { ...@@ -185,15 +211,15 @@ void RegisterStatusBindings(module m) {
try { try {
if (p) std::rethrow_exception(p); if (p) std::rethrow_exception(p);
} catch (const StatusNotOk& e) { } catch (const StatusNotOk& e) {
PyErr_SetObject(PyStatusNotOk.ptr(), PyErr_SetObject(
BuildStatusNotOk(PyStatusNotOk, e).ptr()); PyStatusNotOk.ptr(),
PyStatusNotOk(google::NoThrowStatus<absl::Status>(e.status())).ptr());
} }
}); });
m.def("BuildStatusNotOk", [](int code, const std::string& msg) { m.def("BuildStatusNotOk", [](int code, const std::string& msg) {
return BuildStatusNotOk( return PyStatusNotOk(google::NoThrowStatus<absl::Status>(
PyStatusNotOk, absl::Status(static_cast<absl::StatusCode>(code), msg)));
StatusNotOk(absl::Status(static_cast<absl::StatusCode>(code), msg)));
}); });
} }
......
...@@ -51,6 +51,20 @@ class StatusTest(absltest.TestCase): ...@@ -51,6 +51,20 @@ class StatusTest(absltest.TestCase):
self.assertEqual(e.code, int(status.StatusCode.ALREADY_EXISTS)) self.assertEqual(e.code, int(status.StatusCode.ALREADY_EXISTS))
self.assertEqual(e.message, 'Msg int.') self.assertEqual(e.message, 'Msg int.')
def test_status_not_ok_status(self):
e = status.StatusNotOk(status.Status(status.StatusCode.CANCELLED, 'Cnclld'))
self.assertEqual(e.code, int(status.StatusCode.CANCELLED))
self.assertEqual(e.message, 'Cnclld')
def test_status_nok_ok_str(self):
e = status.StatusNotOk('Deprecated.')
self.assertEqual(str(e), 'Deprecated.')
def test_status_nok_ok_none(self):
with self.assertRaises(AssertionError) as cm:
status.StatusNotOk(None)
self.assertEqual(str(cm.exception), '')
def test_return_not_ok_twice(self): def test_return_not_ok_twice(self):
# Each exception is a different instance with different messages. # Each exception is a different instance with different messages.
with self.assertRaises(status.StatusNotOk) as cm1: with self.assertRaises(status.StatusNotOk) as cm1:
......
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