Commit 32cf30f1 by pybind11_abseil authors Committed by Jean-Baptiste

Internal change

PiperOrigin-RevId: 348478233
parent d4b36a06
# Pybind11 bindings for the Abseil C++ Common Libraries
## WORK IN PROGRESS
[TOC]
We are currently working on automatically syncing this repo with
Google-internal developments. You can inspect the current state on the
experimental branch:
## Overview
https://github.com/pybind/pybind11_abseil/tree/experimental
These adapters make Abseil types work with Pybind11 bindings. For more
information on using Pybind11, see
g3doc/third_party/pybind11/google3_utils/README.md.
**IMPORTANT**: The `experimental` branch may be purged and re-populated at
any moment. Do NOT use the `experimental` branch for developments, and
do NOT `git pull`. To update, always use:
To use the converters listed below, just include the header
in the .cc file with your bindings:
```
#include "pybind11_abseil/absl_casters.h"
```
Support for non-const `absl::Span` for numeric types is also available by
including a separated header file:
```
#include "pybind11_abseil/absl_numpy_span_caster.h"
```
## absl::Duration
`absl::Duration` objects are converted to/ from python datetime.timedelta objects.
Therefore, C code cannot mutate any datetime.timedelta objects from python.
## absl::Time
`absl::Time` objects are converted to/from python datetime.datetime objects.
Additionally, datetime.date objects can be converted to `absl::Time` objects.
C code cannot mutate any datetime.datetime objects from python.
Python date objects effectively truncate the time to 0 (ie, midnight).
Python time objects are not supported because `absl::Time` would implicitly
assume a year, which could be confusing.
### Time zones
Python `datetime` objects include timezone information, while
`absl::Time` does not. When converting from Python to C++, if a timezone is
specified then it will be used to determine the `absl::Time` instant. If no
timezone is specified by the Python `datetime` object, the local timezone is
assumed.
When converting back from C++ to Python, the resultant time will be presented in
the local timezone and the `tzinfo` property set on the `datetime` object to
reflect that. This means that the caller may receive a datetime formatted
in a different timezone to the one they passed in. To handle this safely, the
caller should take care to check the `tzinfo` of any returned `datetime`s.
## absl::CivilTime
`absl::CivilTime` objects are converted to/from Python datetime.datetime
objects. Fractional Python datetime components are truncated when converting to
less granular C++ types, and time zone information is ignored.
## absl::Span
For non-const `absl::Span` and conversion from `numpy` arrays, see
[non-const absl::Span](#non-const-abslspan) later.
When `absl::Span<const T>` (i.e. the `const` version) is considered, there is
full support to mapping into Python sequences.
Currently, this will always result in the list being copied, so you lose the
efficiency gains of spans in native C++, but you still get the API versatility.
The value type in the span can be any type that pybind knows about. However, it
must be immutable (ie, `absl::Span<const ValueType>`). Theoretically mutable
ValueTypes could be supported, but with some subtle limitations, and this is
not needed right now, so the implementation has been deferred.
The `convert` and `return_value_policy` parameters will apply to the *elements*.
The list containing those elements will aways be converted/copied.
### non-const absl::Span
Support for non-cost `absl::Span`, for numeric types only, is provided for
`numpy` arrays. Support is only for output function parameters and not for
returned value. The rationale behind this decision is that, if a `absl::Span`
were to be returned, the C++ object would have needed to outlive the mapped
Python object. Given the complexity of memory management across languages, we
did not add support of returned `absl::Span`.
That is the following is supported:
```
void Foo(absl::Span<double> some_span);
```
while the following is not (it will generate a compile error):
```
absl::Span<double> Bar();
```
Note: It is possible to use the non-const `absl::Span` bindings to wrap a
function with `absl::Span<const T>` argument if you are using `numpy` arrays
and you do not want a copy to be performed. This can be done by defining a
lambda function in the `pybind11` wrapper, as in the following example. See
b/155596364 for more details.
```
void MyConstSpanFunction(absl::Span<const double> a_span);
...
PYBIND11_MODULE(bindings, m) {
m.def(
"wrap_span",
[](absl::Span<double> span) {
MyConstSpanFunction(span);
});
}
```
## absl::string_view
Supported exactly the same way pybind11 supports `std::string_view`.
## absl::optional
Supported exactly the same way pybind11 supports `std::optional`.
## absl::flat_hash_map
Supported exactly the same way pybind11 supports `std::map`.
## absl::flat_hash_set
Supported exactly the same way pybind11 supports `std::set`.
## absl::Status[Or]
To use the Status[Or] casters:
1. Include the header file `pybind11_abseil/status_casters.h`
in the .cc file with your bindings.
1. Call `pybind11::google::ImportStatusModule();` in your `PYBIND11_MODULE`
definition.
By default, an ok status will be converted into `None`, and a non-ok status will
raise a `status.StatusNotOk` exception. This has a `status` attribute which can
be used to access the status object and check the code/ message.
To get a `status.Status` object rather than having an exception thrown, pass
either the `Status` object or a function returning a `Status` to
`pybind11::google::DoNotThrowStatus` before casting or binding. This works with
references and pointers to `absl::Status` objects too.
See `status_utils.cc` in this directory for details about what methods are
available in wrapped `absl::Status` objects.
Example:
```cpp
#include "pybind11_abseil/status_casters.h"
absl::Status StatusReturningFunction() {
return absl::Status(...);
}
pybind11::object StatusHandlingFunction() {
return pybind11::cast(pybind11::google::DoNotThrowStatus(StatusReturningFunction()));
}
PYBIND11_MODULE(test_bindings, m) {
pybind11::google::ImportStatusModule();
m.def("return_status", &StatusReturningFunction,
"Return None if StatusCode is OK, otherwise raise an error.");
m.def("make_status", google::DoNotThrowStatus(&StatusReturningFunction),
"Return a wrapped status object without raising an error.");
m.def("status_handling_function", &StatusHandlingFunction,
"Same effect as make_status, but cast is done internally.");
};
```
git fetch
git checkout experimental
git reset --hard experimental
Python:
```python
from pybind11_abseil import status
import test_bindings
my_status = make_status()
if my_status.code():
...
try:
return_status()
except status.StatusNotOk as e:
print(e.status)
```
### absl::StatusOr
`absl::StatusOr` objects behave exactly like `absl::Status` objects, except:
- There is no support for passing StatusOr objects. You can only return them.
- Instead of returning None or a wrapped status with OK, this casts and
returns the payload when there is no error.
As with `absl::Status`, the default behavior is to throw an error when casting
a non-ok status. You may pass a StatusOr object or StatusOr returning function
to `pybind11::google::DoNotThrowStatus` in exactly the same way as with
`absl::Status` to change this behavior.
`absl::StatusOr` objects must be returned by value (not reference or pointer).
Why? Because the implementation takes advantage of the fact that python is a
dynamically typed language to cast and return the payload *or* the
`absl::Status` object (or raise an exeception). Python has no concept of a
`absl::StatusOr` object, so it's also impossible to apply the
return_value_policy to a `absl::StatusOr`. Therefore returning a reference or
pointer to a `absl::StatusOr` is meaningless.
Pointers *can* be used as the payload type, and the return_value_policy will
be applied to the payload if the status is OK. However, references cannot be
used as the payload type, because that's a restriction on `absl::StatusOr` in
general, not pybind11 (see https://yaqs/5903163345338368).
This can handle any type of payload that pybind knows about. unique_ptrs
(ie, `absl::StatusOr<std::unique_ptr<...>>`) to wrapped classes or
structs (ie, any type which you created bindings for using
`pybind11::class_<...>`) can be used, but unique_ptrs to converted types (eg,
`int`, `string`, `absl::Time`, `absl::Duration`, etc) cannot be used.
### Aliasing parts of the status module
The need to import the `status` module can be eliminated by aliasing the parts
of the status module that are needed in your own module:
```cpp
PYBIND11_MODULE(test_bindings, m) {
auto status_module = pybind11::google::ImportStatusModule();
m.attr("StatusNotOk") = status_module.attr("StatusNotOk");
...
}
```
Python:
```python
import test_bindings
try:
return_status()
except test_bindings.StatusNotOk as e:
print(e.status)
```
### Importing the status module
The status module uses the same import mechansim as the proto module; see [its
documentation](../pybind11_protobuf/README.md#importing-the-proto-module)
for details. For now there is a `#ifdef` to allow `ImportStatusModule` to work
with python 2 rather than giving an error, but this will be removed eventually.
If modifying the following functions, make the same changes in the
corresponding proto functions:
- ImportStatusModule
- IsStatusModuleImported
- CheckStatusModuleImported
### Use Outside of Google3
The path used for the status module may be changed by altering the value of
`PYBIND11_ABSEIL_STATUS_MODULE_PATH` defined in `status_casters.h`. This uses
the same mechanism as the proto module, so see [its documentation]
(../pybind11_protobuf/README.md?cl=head#use-outside-of-google3)
for details.
workspace(name = "com_google_pybind11_abseil")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# To update a dependency to a new revision,
# a) update URL and strip_prefix to the new git commit hash
# b) get the sha256 hash of the commit by running:
# curl -L https://github.com/<...>.tar.gz | sha256sum
# and update the sha256 with the result.
http_archive(
name = "com_google_absl",
sha256 = "137836d52edb41891cc6d137a228278ae30e76607e3cbd6b8cdb653743c0823e", # SHARED_ABSL_SHA
strip_prefix = "abseil-cpp-6df644c56f31b100bf731e27c3825069745651e3",
urls = [
"https://github.com/abseil/abseil-cpp/archive/6df644c56f31b100bf731e27c3825069745651e3.tar.gz",
],
)
## `pybind11_bazel`
# See https://github.com/pybind/pybind11_bazel
http_archive(
name = "pybind11_bazel",
strip_prefix = "pybind11_bazel-26973c0ff320cb4b39e45bc3e4297b82bc3a6c09",
sha256 = "8f546c03bdd55d0e88cb491ddfbabe5aeb087f87de2fbf441391d70483affe39",
urls = ["https://github.com/pybind/pybind11_bazel/archive/26973c0ff320cb4b39e45bc3e4297b82bc3a6c09.tar.gz"],
)
# We still require the pybind library.
http_archive(
name = "pybind11",
build_file = "@pybind11_bazel//:pybind11.BUILD",
strip_prefix = "pybind11-2.6",
sha256 = "22af7c5c65f1ca5d00cbfee5fa9be6aedcaa1ea0c46af22eaa429526e1b88094",
urls = ["https://github.com/pybind/pybind11/archive/v2.6.tar.gz"],
)
load("@pybind11_bazel//:python_configure.bzl", "python_configure")
python_configure(name = "local_config_python")
# Pybind11 bindings for the Abseil C++ Common Libraries
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension", "pybind_library")
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
exports_files(["LICENSE"])
pybind_library(
name = "absl_casters",
hdrs = ["absl_casters.h"],
deps = [
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
],
)
pybind_library(
name = "absl_numpy_span_caster",
hdrs = ["absl_numpy_span_caster.h"],
deps = ["@com_google_absl//absl/types:span"],
)
cc_library(
name = "status_not_ok_exception",
hdrs = ["status_not_ok_exception.h"],
deps = ["@com_google_absl//absl/status"],
)
pybind_library(
name = "status_utils",
srcs = ["status_utils.cc"],
hdrs = ["status_utils.h"],
deps = [
":absl_casters",
":status_not_ok_exception",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
],
)
pybind_library(
name = "status_casters",
hdrs = ["status_casters.h"],
deps = [
":status_utils",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
],
)
pybind_extension(
name = "status",
srcs = ["status.cc"],
deps = [":status_utils"],
)
#ifndef PYBIND11_ABSEIL_ABSL_NUMPY_SPAN_CASTER_H_
#define PYBIND11_ABSEIL_ABSL_NUMPY_SPAN_CASTER_H_
#include <type_traits>
#include "absl/types/span.h"
#include "pybind11/cast.h"
#include "pybind11/numpy.h"
// Support for conversion from NumPy array to template specializations of
// absl::Span. Note that no data is copied, the `Span`ned memory is owned by the
// NumPy array.
//
// Only one direction of conversion is provided: a Span argument can be exposed
// to Python as a NumPy array argument. No conversion is provided for a Span
// return value.
//
// Lifetime management will become complicated if a `Span` returned by a C++
// object is converted into a NumPy array which outlives the C++ object. Such
// memory management concerns are normal for C++ but undesirable to introduce to
// Python.
//
// A `Span` argument can be used to expose an output argument to Python so that
// Python is wholly responsible for memory management of the output object.
// Using an output argument rather than a return value means that memory can be
// reused.
//
// C++:
// Simulation::Simulation(int buffer_size);
// void Simulation::RenderFrame(int frame_index, Span<uint8> buffer);
//
// Python:
// buffer = np.zeroes(1024*768, dtype='uint8')
// simulation = Simulation(1024*768)
// simulation.renderFrame(0, buffer)
// # RGB data can now be read from the buffer.
namespace pybind11::detail {
// Conversion of non-const Span.
// Note that there are two template specialisations for `absl::Span`: the one in
// this file and the one for `const T` in `absl_caster.h`.
// The use of `std::enable_if_t`, and in particular of the `std::is_const_v`
// condition check, is really important and prevents unpleasant ODR (one
// definition rule) violations when linking together different object files with
// different includes.
// TODO(b/155596364) merge this implementation with the absl::Span<const T>
// caster.
template <typename T>
class type_caster<absl::Span<T>,
// C++11 compatibility.
typename std::enable_if<std::is_arithmetic<T>::value &&
!std::is_const<T>::value>::type> {
public:
// Instead of using the macro PYBIND11_TYPE_CASTER we explicitly define value
// and functions. This is because, the macro has default implementations for
// move constructors and cast method.
// Note: we also disable the linter on these methods and variables.
static constexpr auto name = // NOLINT
_("Span[") + make_caster<T>::name + _("]");
// We do not allow moving because 1) spans are super lightweight, so there is
// no advantage to moving and 2) the span cannot exist without the caster,
// so moving leaves an implicit dependency (while a reference or pointer
// make that dependency explicit).
operator absl::Span<T>*() { // NOLINT(google-explicit-constructor)
return &value_;
}
operator absl::Span<T>&() { // NOLINT(google-explicit-constructor)
return value_;
}
template <typename T_>
using cast_op_type = cast_op_type<T_>;
// Conversion Python->C++: convert a NumPy array into a Span.
bool load(handle src, bool /* convert */) {
// Extract PyObject from handle.
if (!pybind11::isinstance<pybind11::array_t<T>>(src)) {
return false;
}
auto array = src.cast<pybind11::array_t<T>>();
if (!array || array.ndim() != 1 || array.strides()[0] != sizeof(T) ||
!array.writeable()) {
return false;
}
value_ = absl::Span<T>(static_cast<T*>(array.mutable_data()), array.size());
return true;
}
protected:
absl::Span<T> value_;
};
} // namespace pybind11::detail
#endif // PYBIND11_ABSEIL_ABSL_NUMPY_SPAN_CASTER_H_
#include <pybind11/pybind11.h>
#include "pybind11_abseil/status_utils.h"
namespace pybind11 {
namespace google {
PYBIND11_MODULE(status, m) { RegisterStatusBindings(m); }
} // namespace google
} // namespace pybind11
// Type conversion utilities for pybind11 and absl::Status/StatusOr.
//
// Usage:
// 1) Include this file in the .cc file with your bindings.
// 2) Call `pybind11::google::ImportStatusModule()` in your PYBIND11_MODULE
// definition.
//
// Supported types:
// - absl::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.
// - absl::StatusOr- converts a non-ok return status into a python exception,
// otherwise converts/returns the payload. Can only be used as a return value.
//
// For details, see the README.md.
//
// Author: Ken Oslund (kenoslund@)
#ifndef PYBIND11_ABSEIL_STATUS_CASTERS_H_
#define PYBIND11_ABSEIL_STATUS_CASTERS_H_
#include <pybind11/cast.h>
#include <pybind11/pybind11.h>
#include <stdexcept>
#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 {
// 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 = 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 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:
// 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<const absl::Status&>(*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<const absl::Status&>(src, policy, parent, throw_exception);
}
static handle cast(absl::Status&& src, return_value_policy policy,
handle parent, bool throw_exception = true) {
return cast_impl<absl::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::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 an absl::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 = _("StatusOr[") + PayloadCaster::name + _("]");
// Conversion part 2 (C++ -> Python).
static handle cast(absl::StatusOr<PayloadType>&& src,
return_value_policy policy, handle parent,
bool throw_exception = true) {
google::CheckStatusModuleImported();
if (src.ok()) {
// Convert and return the payload.
return PayloadCaster::cast(std::forward<PayloadType>(*src), 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 // PYBIND11_ABSEIL_STATUS_CASTERS_H_
#ifndef PYBIND11_ABSEIL_STATUS_NOT_OK_EXCEPTION_H_
#define PYBIND11_ABSEIL_STATUS_NOT_OK_EXCEPTION_H_
#include <exception>
#include "absl/status/status.h"
namespace pybind11 {
namespace google {
// Exception class which represents a non-ok status.
//
// This is in the pybind::google namespace because it was originally created to
// use with pybind11, but it does NOT depend on the pybind11 library.
class StatusNotOk : public std::exception {
public:
StatusNotOk(absl::Status&& status)
: status_(std::move(status)), what_(status_.ToString()) {}
StatusNotOk(const absl::Status& status)
: status_(status), what_(status_.ToString()) {}
const absl::Status& status() const& { return status_; }
absl::Status&& status() && { return std::move(status_); }
const char* what() const noexcept override { return what_.c_str(); }
private:
absl::Status status_;
std::string what_;
};
} // namespace google
} // namespace pybind11
#endif // PYBIND11_ABSEIL_STATUS_NOT_OK_EXCEPTION_H_
#include "pybind11_abseil/status_utils.h"
#include <pybind11/pybind11.h>
#include "pybind11_abseil/absl_casters.h"
namespace pybind11 {
namespace google {
namespace {
// Returns false if status_or represents a non-ok status object, and true in all
// other cases (including the case that this is passed a non-status object).
bool IsOk(handle status_or) {
detail::make_caster<absl::Status> caster;
// "Not a status" means "ok" for StatusOr.
if (!caster.load(status_or, true)) return true;
return static_cast<absl::Status &>(caster).ok();
}
} // namespace
void RegisterStatusBindings(module m) {
enum_<absl::StatusCode>(m, "StatusCode")
.value("OK", absl::StatusCode::kOk)
.value("CANCELLED", absl::StatusCode::kCancelled)
.value("UNKNOWN", absl::StatusCode::kUnknown)
.value("INVALID_ARGUMENT", absl::StatusCode::kInvalidArgument)
.value("DEADLINE_EXCEEDED", absl::StatusCode::kDeadlineExceeded)
.value("NOT_FOUND", absl::StatusCode::kNotFound)
.value("ALREADY_EXISTS", absl::StatusCode::kAlreadyExists)
.value("PERMISSION_DENIED", absl::StatusCode::kPermissionDenied)
.value("RESOURCE_EXHAUSTED", absl::StatusCode::kResourceExhausted)
.value("FAILED_PRECONDITION", absl::StatusCode::kFailedPrecondition)
.value("ABORTED", absl::StatusCode::kAborted)
.value("OUT_OF_RANGE", absl::StatusCode::kOutOfRange)
.value("UNIMPLEMENTED", absl::StatusCode::kUnimplemented)
.value("INTERNAL", absl::StatusCode::kInternal)
.value("UNAVAILABLE", absl::StatusCode::kUnavailable)
.value("DATA_LOSS", absl::StatusCode::kDataLoss)
.value("UNAUTHENTICATED", absl::StatusCode::kUnauthenticated);
class_<absl::Status>(m, "Status")
.def(init())
.def(init<absl::StatusCode, std::string>())
.def("ok", &absl::Status::ok)
.def("code", &absl::Status::code)
.def("message", &absl::Status::message)
.def(
"update",
(void (absl::Status::*)(const absl::Status &)) & absl::Status::Update,
arg("other"))
.def("to_string", &absl::Status::ToString)
.def("__repr__", &absl::Status::ToString);
m.def("is_ok", &IsOk, arg("status_or"),
"Returns false only if passed a non-ok status; otherwise returns true. "
"This can be used on the return value of a function which returns a "
"StatusOr without raising an exception. The .ok() method cannot be "
"used in this case because an ok status is never returned; instead, a "
"non-status object is returned, which doesn't have a .ok() method.");
// status_casters.h has not been included, so the functions below will
// return a wrapped status, not raise an exception.
// Return canonical errors.
m.def("aborted_error", &absl::AbortedError, arg("message"));
m.def("already_exists_error", &absl::AlreadyExistsError, arg("message"));
// CanceledError has an overload which takes no arguments, so we must cast it.
m.def("cancelled_error",
(absl::Status(*)(absl::string_view)) & absl::CancelledError,
arg("message"));
m.def("data_loss_error", &absl::DataLossError, arg("message"));
m.def("deadline_exceeded_error", &absl::DeadlineExceededError,
arg("message"));
m.def("failed_precondition_error", &absl::FailedPreconditionError,
arg("message"));
m.def("internal_error", &absl::InternalError, arg("message"));
m.def("invalid_argument_error", &absl::InvalidArgumentError, arg("message"));
m.def("not_found_error", &absl::NotFoundError, arg("message"));
m.def("out_of_range_error", &absl::OutOfRangeError, arg("message"));
m.def("permission_denied_error", &absl::PermissionDeniedError,
arg("message"));
m.def("resource_exhausted_error", &absl::ResourceExhaustedError,
arg("message"));
m.def("unauthenticated_error", &absl::UnauthenticatedError, arg("message"));
m.def("unavailable_error", &absl::UnavailableError, arg("message"));
m.def("unimplemented_error", &absl::UnimplementedError, arg("message"));
m.def("unknown_error", &absl::UnknownError, arg("message"));
// Register the exception.
static pybind11::exception<StatusNotOk> status_not_ok(m, "StatusNotOk");
// Register a custom handler which converts a C++ StatusNotOk to a Python
// StatusNotOk exception and adds the status field.
register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (StatusNotOk& e) {
auto rvalue_e = std::move(e);
status_not_ok.attr("status") = cast(rvalue_e.status());
status_not_ok(rvalue_e.what());
}
});
}
} // namespace google
} // namespace pybind11
// 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_
#include <pybind11/pybind11.h>
#include <exception>
#include <stdexcept>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "pybind11_abseil/status_not_ok_exception.h"
namespace pybind11 {
namespace google {
// Wrapper type to signal to the type_caster that a non-ok status should not
// be converted into an object rather than a thrown exception.
template <typename StatusType>
struct NoThrowStatus {
NoThrowStatus(StatusType status_in)
: status(std::forward<StatusType>(status_in)) {}
StatusType status;
};
// Convert a absl::Status(Or) into a NoThrowStatus.
template <typename StatusType>
NoThrowStatus<StatusType> DoNotThrowStatus(StatusType status) {
return NoThrowStatus<StatusType>(std::forward<StatusType>(status));
}
// Convert a function returning a absl::Status(Or) into a function
// returning a NoThrowStatus.
template <typename StatusType, typename... Args>
std::function<NoThrowStatus<StatusType>(Args...)> DoNotThrowStatus(
std::function<StatusType(Args...)> f) {
return [f = std::move(f)](Args... args) {
return NoThrowStatus<StatusType>(
std::forward<StatusType>(f(std::forward<Args>(args)...)));
};
}
template <typename StatusType, typename... Args>
std::function<NoThrowStatus<StatusType>(Args...)> DoNotThrowStatus(
StatusType (*f)(Args...)) {
return [f](Args... args) {
return NoThrowStatus<StatusType>(
std::forward<StatusType>(f(std::forward<Args>(args)...)));
};
}
template <typename StatusType, typename Class, typename... Args>
std::function<NoThrowStatus<StatusType>(Class*, Args...)> DoNotThrowStatus(
StatusType (Class::*f)(Args...)) {
return [f](Class* c, Args... args) {
return NoThrowStatus<StatusType>(
std::forward<StatusType>((c->*f)(std::forward<Args>(args)...)));
};
}
template <typename StatusType, typename Class, typename... Args>
std::function<NoThrowStatus<StatusType>(const Class*, Args...)>
DoNotThrowStatus(StatusType (Class::*f)(Args...) const) {
return [f](const Class* c, Args... args) {
return NoThrowStatus<StatusType>(
std::forward<StatusType>((c->*f)(std::forward<Args>(args)...)));
};
}
// 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
// Returns true if the status module has been imported.
inline bool IsStatusModuleImported() {
return detail::get_type_info(typeid(absl::Status));
}
// In debug mode, throws a type error if the proto module is not imported.
// No-opt if NDEBUG is defined, and inlined so the compiler can optimize it out.
inline void CheckStatusModuleImported() {
#ifndef NDEBUG
if (!IsStatusModuleImported())
throw type_error(
"Status module has not been imported. Did you call ::pybind11::google"
"::ImportStatusModule() in your PYBIND11_MODULE definition?");
#endif
}
} // namespace google
} // namespace pybind11
#endif // PYBIND11_ABSEIL_STATUS_UTILS_H_
# Tests and examples for pybind11_abseil.
# load("//devtools/python/blaze:python3.bzl", "py2and3_strict_test")
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension")
licenses(["notice"])
pybind_extension(
name = "absl_example",
srcs = ["absl_example.cc"],
deps = [
"//pybind11_abseil:absl_casters",
"//pybind11_abseil:absl_numpy_span_caster",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
],
)
py_test(
name = "absl_test",
srcs = ["absl_test.py"],
data = [":absl_example.so"],
srcs_version = "PY2AND3",
)
pybind_extension(
name = "missing_import",
srcs = ["missing_import.cc"],
copts = ["-UNDEBUG"],
deps = [
"//pybind11_abseil:status_casters",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
],
)
py_test(
name = "missing_import_test",
srcs = ["missing_import_test.py"],
data = [":missing_import.so"],
srcs_version = "PY2AND3",
)
pybind_extension(
name = "status_example",
srcs = ["status_example.cc"],
deps = [
"//pybind11_abseil:status_casters",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
],
)
py_test(
name = "status_test",
srcs = ["status_test.py"],
data = [
":status_example.so",
"//pybind11_abseil:status.so",
],
srcs_version = "PY2AND3",
)
// Copyright (c) 2019 The Pybind Development Team. All rights reserved.
//
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include <pybind11/pybind11.h>
#include <vector>
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/time/civil_time.h"
#include "absl/time/time.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "pybind11_abseil/absl_casters.h"
#include "pybind11_abseil/absl_numpy_span_caster.h"
namespace pybind11 {
namespace test {
absl::Duration MakeDuration(double secs) { return absl::Seconds(secs); }
bool CheckDuration(const absl::Duration& duration, double secs) {
return duration == MakeDuration(secs);
}
absl::Time MakeTime(double secs) {
int64_t microsecs = static_cast<int64_t>(secs * 1e6);
return absl::FromUnixMicros(microsecs);
}
bool CheckDatetime(const absl::Time& datetime, double secs) {
return datetime == MakeTime(secs);
}
bool CheckSpan(absl::Span<const int> span, const std::vector<int>& values) {
if (span.size() != values.size()) return false;
for (int i = 0; i < span.size(); ++i) {
if (span[i] != values[i]) return false;
}
return true;
}
absl::CivilSecond MakeCivilSecond(double secs) {
return absl::ToCivilSecond(absl::FromUnixSeconds(static_cast<int64_t>(secs)),
absl::UTCTimeZone());
}
absl::CivilMinute MakeCivilMinute(double secs) {
return absl::ToCivilMinute(absl::FromUnixSeconds(static_cast<int64_t>(secs)),
absl::UTCTimeZone());
}
absl::CivilHour MakeCivilHour(double secs) {
return absl::ToCivilHour(absl::FromUnixSeconds(static_cast<int64_t>(secs)),
absl::UTCTimeZone());
}
absl::CivilDay MakeCivilDay(double secs) {
return absl::ToCivilDay(absl::FromUnixSeconds(static_cast<int64_t>(secs)),
absl::UTCTimeZone());
}
absl::CivilMonth MakeCivilMonth(double secs) {
return absl::ToCivilMonth(absl::FromUnixSeconds(static_cast<int64_t>(secs)),
absl::UTCTimeZone());
}
absl::CivilYear MakeCivilYear(double secs) {
return absl::ToCivilYear(absl::FromUnixSeconds(static_cast<int64_t>(secs)),
absl::UTCTimeZone());
}
bool CheckCivilSecond(absl::CivilSecond datetime, double secs) {
return datetime == MakeCivilSecond(secs);
}
bool CheckCivilMinute(absl::CivilMinute datetime, double secs) {
return datetime == MakeCivilMinute(secs);
}
bool CheckCivilHour(absl::CivilHour datetime, double secs) {
return datetime == MakeCivilHour(secs);
}
bool CheckCivilDay(absl::CivilDay datetime, double secs) {
return datetime == MakeCivilDay(secs);
}
bool CheckCivilMonth(absl::CivilMonth datetime, double secs) {
return datetime == MakeCivilMonth(secs);
}
bool CheckCivilYear(absl::CivilYear datetime, double secs) {
return datetime == MakeCivilYear(secs);
}
// Since a span does not own its elements, we must create a class to own them
// and persist beyond the function that constructs the span for testing.
class VectorContainer {
public:
absl::Span<const int> MakeSpan(const std::vector<int>& values) {
values_ = values;
return values_;
}
private:
std::vector<int> values_;
};
bool CheckStringView(absl::string_view view, const std::string& values) {
return view == values;
}
// Since a string view does not own its data, we must create a class to own
// them and persist beyond the function that constructs the span for testing.
class StringContainer {
public:
absl::string_view MakeStringView(const std::string& values) {
values_ = values;
return values_;
}
private:
std::string values_;
};
bool CheckOptional(const absl::optional<int> optional, bool given, int value) {
if (!given && !optional.has_value()) return true;
if (given && optional.has_value() && optional.value() == value) return true;
return false;
}
absl::optional<int> MakeOptional() { return absl::nullopt; }
absl::optional<int> MakeOptional(int value) { return value; }
absl::flat_hash_map<int, int> MakeMap(
const std::vector<std::pair<int, int>>& keys_and_values) {
absl::flat_hash_map<int, int> map;
for (const auto& kvp : keys_and_values) {
map.insert(kvp);
}
return map;
}
bool CheckMap(const absl::flat_hash_map<int, int>& map,
const std::vector<std::pair<int, int>>& keys_and_values) {
for (const auto& kvp : keys_and_values) {
auto found = map.find(kvp.first);
if (found == map.end()) {
return false;
}
if (found->second != kvp.second) {
return false;
}
}
return true;
}
absl::flat_hash_set<int> MakeSet(const std::vector<int>& values) {
return absl::flat_hash_set<int>(values.begin(), values.end());
}
bool CheckSet(const absl::flat_hash_set<int>& set,
const std::vector<int>& values) {
absl::flat_hash_set<int> check(values.begin(), values.end());
return set == check;
}
// Non-const Span.
template <typename T>
void FillNonConstSpan(T value, absl::Span<T> output_span) {
for (auto& i : output_span) {
i = value;
}
}
struct ObjectForSpan {
explicit ObjectForSpan(int v) : value(v) {}
int value;
};
int SumObjectPointersSpan(absl::Span<ObjectForSpan* const> inputs) {
int result = 0;
for (ObjectForSpan* item : inputs) {
result += item->value;
}
return result;
}
template <typename T>
void DefineNonConstSpan(module* py_m, absl::string_view type_name) {
py_m->def(absl::StrCat("fill_non_const_span_", type_name).c_str(),
&FillNonConstSpan<T>, arg("value"), arg("output_span").noconvert());
}
PYBIND11_MODULE(absl_example, m) {
// absl::Time/Duration bindings.
m.def("make_duration", &MakeDuration, arg("secs"));
m.def("check_duration", &CheckDuration, arg("duration"), arg("secs"));
m.def("make_datetime", &MakeTime, arg("secs"));
m.def("check_datetime", &CheckDatetime, arg("datetime"), arg("secs"));
// absl::CivilTime bindings
m.def("make_civilsecond", &MakeCivilSecond, arg("secs"));
m.def("check_civilsecond", &CheckCivilSecond, arg("datetime"), arg("secs"));
m.def("make_civilminute", &MakeCivilMinute, arg("secs"));
m.def("check_civilminute", &CheckCivilMinute, arg("datetime"), arg("secs"));
m.def("make_civilhour", &MakeCivilHour, arg("secs"));
m.def("check_civilhour", &CheckCivilHour, arg("datetime"), arg("secs"));
m.def("make_civilday", &MakeCivilDay, arg("secs"));
m.def("check_civilday", &CheckCivilDay, arg("datetime"), arg("secs"));
m.def("make_civilmonth", &MakeCivilMonth, arg("secs"));
m.def("check_civilmonth", &CheckCivilMonth, arg("datetime"), arg("secs"));
m.def("make_civilyear", &MakeCivilYear, arg("secs"));
m.def("check_civilyear", &CheckCivilYear, arg("datetime"), arg("secs"));
// absl::Span bindings.
m.def("check_span", &CheckSpan, arg("span"), arg("values"));
class_<VectorContainer>(m, "VectorContainer")
.def(init())
.def("make_span", &VectorContainer::MakeSpan, arg("values"));
// non-const absl::Span bindings.
DefineNonConstSpan<double>(&m, "double");
DefineNonConstSpan<int>(&m, "int");
// Wrap a const Span with a non-const Span lambda to avoid copying data.
m.def(
"check_span_no_copy",
[](absl::Span<int> span, const std::vector<int>& values) -> bool {
return CheckSpan(span, values);
},
arg("span"), arg("values"));
class_<ObjectForSpan>(m, "ObjectForSpan").def(init<int>());
m.def("SumObjectPointersSpan", &SumObjectPointersSpan);
// absl::string_view bindings.
m.def("check_string_view", &CheckStringView, arg("view"), arg("values"));
class_<StringContainer>(m, "StringContainer")
.def(init())
.def("make_string_view", &StringContainer::MakeStringView, arg("values"));
// absl::optional bindings.
m.def("check_optional", &CheckOptional, arg("optional") = absl::nullopt,
arg("given") = false, arg("value") = 0);
m.def("make_optional", (absl::optional<int>(*)()) & MakeOptional);
m.def("make_optional", (absl::optional<int>(*)(int)) & MakeOptional,
arg("value"));
// absl::flat_hash_map bindings
m.def("make_map", &MakeMap, arg("keys_and_values"));
m.def("check_map", &CheckMap, arg("map"), arg("keys_and_values"));
// absl::flat_hash_set bindings
m.def("make_set", &MakeSet, arg("values"));
m.def("check_set", &CheckSet, arg("set"), arg("values"));
}
} // namespace test
} // namespace pybind11
// A pybind11 module which uses the status caster but does not call
// ImportStatusModule (as it should), to test the behavior in that case.
#include <pybind11/pybind11.h>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "pybind11_abseil/status_casters.h"
namespace pybind11 {
namespace test {
absl::Status ReturnStatus() { return absl::InternalError("test"); }
absl::StatusOr<int> ReturnStatusOr() { return absl::InternalError("test"); }
PYBIND11_MODULE(missing_import, m) {
m.def("returns_status", &ReturnStatus);
m.def("returns_status_or", &ReturnStatusOr);
}
} // namespace test
} // namespace pybind11
"""Tests that the casters raise a TypeError if the status module is missing."""
from absl.testing import absltest
from pybind11_abseil.tests import missing_import
class MissingStatusImportTest(absltest.TestCase):
message_regex = 'Status module has not been imported.*'
def test_returns_status(self):
with self.assertRaisesRegex(TypeError, self.message_regex):
missing_import.returns_status()
def test_returns_status_or(self):
with self.assertRaisesRegex(TypeError, self.message_regex):
missing_import.returns_status_or()
if __name__ == '__main__':
absltest.main()
#include <pybind11/pybind11.h>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "pybind11_abseil/status_casters.h"
namespace pybind11 {
namespace test {
struct IntValue {
IntValue() = default;
IntValue(int value_in) : value(value_in) {}
int value;
};
class TestClass {
public:
absl::Status MakeStatus(absl::StatusCode code, const std::string& text = "") {
return absl::Status(code, text);
}
absl::Status MakeStatusConst(absl::StatusCode code,
const std::string& text = "") const {
return absl::Status(code, text);
}
absl::StatusOr<int> MakeFailureStatusOr(absl::StatusCode code,
const std::string& text = "") {
return absl::Status(code, text);
}
};
bool CheckStatus(const absl::Status& status, absl::StatusCode code) {
return status.code() == code;
}
absl::Status ReturnStatus(absl::StatusCode code, const std::string& text = "") {
return absl::Status(code, text);
}
pybind11::object ReturnStatusManualCast(absl::StatusCode code,
const std::string& text = "") {
return pybind11::cast(google::DoNotThrowStatus(absl::Status(code, text)));
}
const absl::Status& ReturnStatusRef(absl::StatusCode code,
const std::string& text = "") {
static absl::Status static_status;
static_status = absl::Status(code, text);
return static_status;
}
const absl::Status* ReturnStatusPtr(absl::StatusCode code,
const std::string& text = "") {
static absl::Status static_status;
static_status = absl::Status(code, text);
return &static_status;
}
absl::StatusOr<int> ReturnFailureStatusOr(absl::StatusCode code,
const std::string& text = "") {
return absl::Status(code, text);
}
pybind11::object ReturnFailureStatusOrManualCast(absl::StatusCode code,
const std::string& text = "") {
return pybind11::cast(google::DoNotThrowStatus(absl::Status(code, text)));
}
absl::StatusOr<int> ReturnValueStatusOr(int value) { return value; }
absl::StatusOr<const IntValue*> ReturnPtrStatusOr(int value) {
static IntValue static_object;
static_object.value = value;
return &static_object;
}
absl::StatusOr<std::unique_ptr<IntValue>> ReturnUniquePtrStatusOr(int value) {
return absl::make_unique<IntValue>(value);
}
PYBIND11_MODULE(status_example, m) {
auto status_module = pybind11::google::ImportStatusModule();
m.attr("StatusNotOk") = status_module.attr("StatusNotOk");
class_<IntValue>(m, "IntValue").def_readonly("value", &IntValue::value);
class_<TestClass>(m, "TestClass")
.def(init())
.def("make_status", google::DoNotThrowStatus(&TestClass::MakeStatus),
arg("code"), arg("text") = "")
.def("make_status_const",
google::DoNotThrowStatus(&TestClass::MakeStatusConst), arg("code"),
arg("text") = "")
.def("make_failure_status_or",
google::DoNotThrowStatus(&TestClass::MakeFailureStatusOr),
arg("code"), arg("text") = "");
// absl::Status bindings
m.def("check_status", &CheckStatus, arg("status"), arg("code"));
m.def("return_status", &ReturnStatus, "Raise an error if code is not OK.",
arg("code"), arg("text") = "");
m.def("make_status", google::DoNotThrowStatus(&ReturnStatus),
"Return a status without raising an error, regardless of what it is.",
arg("code"), arg("text") = "");
m.def("make_status_manual_cast", ReturnStatusManualCast,
"Return a status without raising an error, regardless of what it is.",
arg("code"), arg("text") = "");
m.def("make_status_ref", google::DoNotThrowStatus(&ReturnStatusRef),
"Return a reference to a static status value without raising an error.",
arg("code"), arg("text") = "", return_value_policy::reference);
m.def("make_status_ptr", google::DoNotThrowStatus(&ReturnStatusPtr),
"Return a reference to a static status value without raising an error.",
arg("code"), arg("text") = "", return_value_policy::reference);
// absl::StatusOr bindings
m.def("return_value_status_or", &ReturnValueStatusOr, arg("value"));
m.def("return_failure_status_or", &ReturnFailureStatusOr,
"Raise an error with the given code.", arg("code"), arg("text") = "");
m.def("make_failure_status_or",
google::DoNotThrowStatus(&ReturnFailureStatusOr), arg("code"),
arg("text") = "", "Return a status without raising an error.");
m.def("make_failure_status_or_manual_cast", &ReturnFailureStatusOrManualCast,
arg("code"), arg("text") = "", "Return a status.");
m.def("return_ptr_status_or", &ReturnPtrStatusOr, arg("value"),
"Return a reference in a status or to a static value.",
return_value_policy::reference);
m.def("return_unique_ptr_status_or", &ReturnUniquePtrStatusOr, arg("value"));
}
} // namespace test
} // namespace pybind11
"""Tests for google3.third_party.pybind11_abseil.status_casters."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from absl.testing import absltest
from pybind11_abseil import status
from pybind11_abseil.tests import status_example
class StatusTest(absltest.TestCase):
def test_pass_status(self):
test_status = status.Status(status.StatusCode.CANCELLED, 'test')
self.assertTrue(
status_example.check_status(test_status, status.StatusCode.CANCELLED))
def test_return_ok(self):
# The return_status function should convert an ok status to None.
self.assertIsNone(status_example.return_status(status.StatusCode.OK))
def test_return_not_ok(self):
# The return_status function should convert a non-ok status to an exception.
with self.assertRaises(status.StatusNotOk) as cm:
status_example.return_status(status.StatusCode.CANCELLED, 'test')
self.assertEqual(cm.exception.status.code(), status.StatusCode.CANCELLED)
self.assertEqual(cm.exception.status.message(), 'test')
def test_return_not_ok_catch_with_alias(self):
# Catch as status_example.StatusNotOk, an alias of status.StatusNotOk.
with self.assertRaises(status_example.StatusNotOk) as cm:
status_example.return_status(status.StatusCode.CANCELLED, 'test')
self.assertEqual(cm.exception.status.code(), status.StatusCode.CANCELLED)
self.assertEqual(cm.exception.status.message(), 'test')
def test_return_not_ok_catch_as_generic_exception(self):
# Catch as a generic Exception, the base type of StatusNotOk.
with self.assertRaises(Exception):
status_example.return_status(status.StatusCode.CANCELLED, 'test')
def test_make_ok(self):
# The make_status function has been set up to return a status object
# instead of raising an exception (this is done in status_example.cc).
test_status = status_example.make_status(status.StatusCode.OK)
self.assertEqual(test_status.code(), status.StatusCode.OK)
self.assertTrue(test_status.ok())
def test_make_not_ok(self):
# The make_status function should always return a status object, even if
# it is not ok (ie, it should *not* convert it to an exception).
test_status = status_example.make_status(status.StatusCode.CANCELLED)
self.assertEqual(test_status.code(), status.StatusCode.CANCELLED)
self.assertFalse(test_status.ok())
def test_make_not_ok_manual_cast(self):
test_status = status_example.make_status_manual_cast(
status.StatusCode.CANCELLED)
self.assertEqual(test_status.code(), status.StatusCode.CANCELLED)
def test_make_status_ref(self):
result_1 = status_example.make_status_ref(status.StatusCode.OK)
self.assertEqual(result_1.code(), status.StatusCode.OK)
result_2 = status_example.make_status_ref(status.StatusCode.CANCELLED)
self.assertEqual(result_2.code(), status.StatusCode.CANCELLED)
# result_1 and 2 reference the same value, so they should always be equal.
self.assertEqual(result_1.code(), result_2.code())
def test_make_status_ptr(self):
result_1 = status_example.make_status_ptr(status.StatusCode.OK)
self.assertEqual(result_1.code(), status.StatusCode.OK)
result_2 = status_example.make_status_ptr(status.StatusCode.CANCELLED)
self.assertEqual(result_2.code(), status.StatusCode.CANCELLED)
# result_1 and 2 reference the same value, so they should always be equal.
self.assertEqual(result_1.code(), result_2.code())
def test_canonical_error(self):
test_status = status.aborted_error('test')
self.assertEqual(test_status.code(), status.StatusCode.ABORTED)
self.assertEqual(test_status.message(), 'test')
def test_member_method(self):
test_status = status_example.TestClass().make_status(status.StatusCode.OK)
self.assertEqual(test_status.code(), status.StatusCode.OK)
test_status = status_example.TestClass().make_status_const(
status.StatusCode.OK)
self.assertEqual(test_status.code(), status.StatusCode.OK)
def test_is_ok(self):
ok_status = status_example.make_status(status.StatusCode.OK)
self.assertTrue(status.is_ok(ok_status))
failure_status = status_example.make_status(status.StatusCode.CANCELLED)
self.assertFalse(status.is_ok(failure_status))
class StatusOrTest(absltest.TestCase):
def test_return_value(self):
self.assertEqual(status_example.return_value_status_or(5), 5)
def test_return_not_ok(self):
with self.assertRaises(status.StatusNotOk) as cm:
status_example.return_failure_status_or(status.StatusCode.NOT_FOUND)
self.assertEqual(cm.exception.status.code(), status.StatusCode.NOT_FOUND)
def test_make_not_ok(self):
self.assertEqual(
status_example.make_failure_status_or(
status.StatusCode.CANCELLED).code(), status.StatusCode.CANCELLED)
def test_make_not_ok_manual_cast(self):
self.assertEqual(
status_example.make_failure_status_or_manual_cast(
status.StatusCode.CANCELLED).code(), status.StatusCode.CANCELLED)
def test_return_ptr_status_or(self):
result_1 = status_example.return_ptr_status_or(5)
self.assertEqual(result_1.value, 5)
result_2 = status_example.return_ptr_status_or(6)
self.assertEqual(result_2.value, 6)
# result_1 and 2 reference the same value, so they should always be equal.
self.assertEqual(result_1.value, result_2.value)
def test_return_unique_ptr(self):
result = status_example.return_unique_ptr_status_or(5)
self.assertEqual(result.value, 5)
def test_member_method(self):
test_status = status_example.TestClass().make_failure_status_or(
status.StatusCode.ABORTED)
self.assertEqual(test_status.code(), status.StatusCode.ABORTED)
def test_is_ok(self):
ok_result = status_example.return_value_status_or(5)
self.assertEqual(ok_result, 5)
self.assertTrue(status.is_ok(ok_result))
failure_result = status_example.make_failure_status_or(
status.StatusCode.CANCELLED)
self.assertFalse(status.is_ok(failure_result))
if __name__ == '__main__':
absltest.main()
#!/bin/bash
# The following scripts:
# - creates a virtualenv
# - installs the pip package dependencies
# - builds and runs tests
set -e # exit when any command fails
# set -x # Prints all executed command
MYDIR="$(dirname "$(realpath "$0")")"
BAZEL=$(which bazel)
if [ ! -x $BAZEL ]
then
echo -n "Bazel not found (bazel (https://bazel.build/) is needed to "
echo "compile & test). Exiting..."
exit 1
fi
is_in_virtual_env="false"
# if we are in a virtual_env, we will not create a new one inside.
if [[ "$VIRTUAL_ENV" != "" ]]
then
echo -e "\e[1m\e[93mVirtualenv already detected. We do not create a new one.\e[0m"
is_in_virtual_env="true"
fi
echo -e "\e[33mRunning ${0} from $PWD\e[0m"
PYBIN=`which python3`
if [ ! -x $PYBIN ]
then
echo -e "\e[1m\e[93m$PYBIN not found! Skip build and test.\e[0m"
continue
fi
PYVERSION=$($PYBIN -c 'import sys; print(".".join(map(str, sys.version_info[:3])))')
VENV_DIR="./venv"
if [[ $is_in_virtual_env == "false" ]]; then
if ! [ -d "$VENV_DIR" ]; then
echo "Installing..."
echo -e "\e[33mInstalling a virtualenv to $VENV_DIR. The setup is long the first time, please wait.\e[0m"
virtualenv -p $PYBIN $VENV_DIR
else
echo -e "\e[33mReusing virtualenv from $VENV_DIR.\e[0m"
fi
source $VENV_DIR/bin/activate
fi
# We only exit the virtualenv if we created one.
function cleanup {
if [[ $is_in_virtual_env == "true" ]]; then
echo "Exiting virtualenv"
deactivate
fi
}
trap cleanup EXIT
echo -e "\e[33mInstalling the requirements (use --noinstall to skip).\e[0m"
pip3 install --upgrade -r ./requirements.txt
echo "Building and testing in $PWD using 'python' (version $PYVERSION)."
export PYTHON_BIN_PATH=`which python3`
export PYTHON_LIB_PATH=`python3 -c "import sysconfig; print(sysconfig.get_path('include'))"`
echo "Using PYTHON_BIN_PATH: $PYTHON_BIN_PATH"
echo "Using PYTHON_LIB_PATH: $PYTHON_LIB_PATH"
BAZEL_CXXOPTS="-std=c++11" bazel test ... --test_output=errors
BAZEL_CXXOPTS="-std=c++14" bazel test ... --test_output=errors
BAZEL_CXXOPTS="-std=c++17" bazel test ... --test_output=errors
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