https://github.com/pybind/pybind11_abseil
Pybind11 bindings for the Abseil C++ Common Libraries
https://github.com/pybind/pybind11_abseil
Last synced: 8 days ago
JSON representation
Pybind11 bindings for the Abseil C++ Common Libraries
- Host: GitHub
- URL: https://github.com/pybind/pybind11_abseil
- Owner: pybind
- License: other
- Created: 2019-10-09T15:44:24.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2026-04-03T16:16:25.000Z (10 days ago)
- Last Synced: 2026-04-05T17:44:24.801Z (8 days ago)
- Language: C++
- Size: 260 KB
- Stars: 26
- Watchers: 8
- Forks: 20
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Pybind11 bindings for the Abseil C++ Common Libraries
Github-CI:
| OS \ Build system | Bazel | CMake |
|:------- | :---: | :---: |
| Linux (`amd64`) | [![Build Status][amd64_linux_bazel_status]][amd64_linux_bazel_link] | [![Build Status][amd64_linux_cmake_status]][amd64_linux_cmake_link] |
| MacOS (`amd64`) | [![Build Status][amd64_macos_bazel_status]][amd64_macos_bazel_link] | [![Build Status][amd64_macos_cmake_status]][amd64_macos_cmake_link] |
| MacOS (`arm64`) | [![Build Status][arm64_macos_bazel_status]][arm64_macos_bazel_link] | [![Build Status][arm64_macos_cmake_status]][arm64_macos_cmake_link] |
| Windows (`amd64`) | [![Build Status][amd64_windows_bazel_status]][amd64_windows_bazel_link] | [![Build Status][amd64_windows_cmake_status]][amd64_windows_cmake_link] |
[amd64_linux_bazel_status]: ./../../actions/workflows/amd64_linux_bazel.yml/badge.svg
[amd64_linux_bazel_link]: ./../../actions/workflows/amd64_linux_bazel.yml
[amd64_macos_bazel_status]: ./../../actions/workflows/amd64_macos_bazel.yml/badge.svg
[amd64_macos_bazel_link]: ./../../actions/workflows/amd64_macos_bazel.yml
[arm64_macos_bazel_status]: ./../../actions/workflows/arm64_macos_bazel.yml/badge.svg
[arm64_macos_bazel_link]: ./../../actions/workflows/arm64_macos_bazel.yml
[amd64_windows_bazel_status]: ./../../actions/workflows/amd64_windows_bazel.yml/badge.svg
[amd64_windows_bazel_link]: ./../../actions/workflows/amd64_windows_bazel.yml
[amd64_linux_cmake_status]: ./../../actions/workflows/amd64_linux_cmake.yml/badge.svg
[amd64_linux_cmake_link]: ./../../actions/workflows/amd64_linux_cmake.yml
[amd64_macos_cmake_status]: ./../../actions/workflows/amd64_macos_cmake.yml/badge.svg
[amd64_macos_cmake_link]: ./../../actions/workflows/amd64_macos_cmake.yml
[arm64_macos_cmake_status]: ./../../actions/workflows/arm64_macos_cmake.yml/badge.svg
[arm64_macos_cmake_link]: ./../../actions/workflows/arm64_macos_cmake.yml
[amd64_windows_cmake_status]: ./../../actions/workflows/amd64_windows_cmake.yml/badge.svg
[amd64_windows_cmake_link]: ./../../actions/workflows/amd64_windows_cmake.yml
## Overview
These adapters make Abseil types work with Pybind11 bindings. For more
information on using Pybind11, see
g3doc/third_party/pybind11/google3_utils/README.md.
To use the converters listed below, just include the header
in the .cc file with your bindings:
```cpp
#include "pybind11_abseil/absl_casters.h"
```
## Installation
pybind11_abseil can be built with Bazel or CMake. Instructions for both are below.
### Bazel
In your BUILD file:
```bzl
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension")
```
#### Bzlmod
You can depend on the Bazel module and dependencies via one of the following
commands in your MODULE.bazel:
To depend on a release:
```bzl
bazel_dep(
name = "pybind11_abseil",
version = "",
)
```
To depend on floating `master`:
```bzl
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "pybind11_bazel",
strip_prefix = "pybind11_bazel-master",
urls = ["https://github.com/pybind/pybind11_bazel/archive/refs/heads/master.tar.gz"],
)
http_archive(
name = "pybind11",
build_file = "@pybind11_bazel//:pybind11-BUILD.bazel",
strip_prefix = "pybind11-master",
urls = ["https://github.com/pybind/pybind11/archive/refs/heads/master.tar.gz"],
)
http_archive(
name = "pybind11_abseil",
strip_prefix = "pybind11_abseil-master",
urls = ["https://github.com/pybind/pybind11_abseil/archive/refs/heads/master.tar.gz"],
)
```
#### WORKSPACE
Bazel workspace support is deprecated and will be removed at a later date.
You will need to depend on `pybind11`, `pybind11_bazel`(see
[doc](https://github.com/pybind/pybind11_bazel#installation), and on
`pybind11_abseil`), e.g.
```bzl
http_archive(
name = "pybind11_bazel",
strip_prefix = "pybind11_bazel-master",
urls = ["https://github.com/pybind/pybind11_bazel/archive/refs/heads/master.tar.gz"],
)
http_archive(
name = "pybind11",
build_file = "@pybind11_bazel//:pybind11-BUILD.bazel",
strip_prefix = "pybind11-master",
urls = ["https://github.com/pybind/pybind11/archive/refs/heads/master.tar.gz"],
)
http_archive(
name = "pybind11_abseil",
strip_prefix = "pybind11_abseil-master",
urls = ["https://github.com/pybind/pybind11_abseil/archive/refs/heads/master.tar.gz"],
)
```
### CMake
In your project, add a FetchContent for pybind11_abseil. This will also fetch
the appropriate versions of Abseil and pybind11 which your project can use
(eliminating the need for submoduling Abseil or using find_package).
Add the following to your CMakeLists.txt:
```cmake
include(FetchContent)
FetchContent_Declare {
pybind11_abseil
GIT_REPOSITORY https://github.com/pybind/pybind11_abseil.git
GIT_TAG master
}
FetchContent_MakeAvailable(pybind11 abseil-cpp pybind11_abseil)
```
To install the package so that it is accessible from system Python, run cmake
with the flag `-DCMAKE_INSTALL_PYDIR` set to a directory on your PYTHONPATH and
subsequently run `make install`. This also works on projects that include
pybind11_abseil via FetchContent.
## 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 (i.e., 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
### Loading
Some python types can be loaded (Python->C++) without copying or converting the
list, while some require copying/ converting the list. The non-converting load
methods will be tried first, and, if the span elements are const, the converting
load methods will be tried next.
Arguments cast to a span with *non-const* elements can never be copied/converted.
To prevent an argument cast to a span with *const* elements from being copied or
converted, mark it as `noconvert()` (see go/pybind11-non-converting-arguments).
The following python types can be loaded *without* copying or converting:
- Numpy array (or anything else that supports [buffer protocol](
https://docs.python.org/3/c-api/buffer.htm)) => `Span<{const or non-const} T>`
if *all* of the following conditions are satisfied:
- The buffer is 1-D.
- T is a numeric type.
- The array dtype matches T exactly.
- If T is not const, the buffer allows writing.
- The stride does not indicate to skip elements or go in reverse order.
- [Opaque](https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html#making-opaque-types) `std::vector` => `Span<{const or non-const} T>`.
- T can be any type, including converted or pointer types, but must
match exactly between C++ and python.
- Opaque vectors are *not* currently compatible with the smart holder.
The following python types must be copied/converted to be loaded:
- Python sequence of elements that require conversion (numbers, strings,
datetimes, etc) => `Span`.
- The elements will be copied/ converted, so that conversion must be legal.
- T *cannot* be a pointer.
- Python sequence of elements that do *not* require conversion (ie, classes
wrapped with py::class_) => `Span` (elements *will* be copied) or
`Span<{const or non-const} T* const>` (elements will *not* be copied).
Specifically, this conversion will *fail* if any of the following are true:
- `noconvert()` was specified (see go/pybind11-non-converting-arguments).
- The element conversion is not allowed (eg, floating point to integer).
- The sequence is being loaded into a `Span<{non-const} T>` or
`Span<{const or non-const} T* {non-const}>`.
- The elements require conversion *and* the sequence is being loaded into a
`Span` (regardless of any `const`s; the element caster which owns the
converted value would be destroyed before `load` is complete, resulting in
dangling references).
- The span is nested (ie, `absl::Span>`, regardless of any `const`s).
Note: These failure conditions only apply to *converted* python types.
### Casting
Spans are cast (C++->Python) with the standard list caster, which always
converts the list. This could be changed in the future (eg, using
[buffer protocol](https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html#buffer-protocol)
) but generally using spans as return values is not recommended.
## 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 and absl::btree_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.
(For use outside google3:
The path used for the `status` module may be changed by altering the value of
`PYBIND11_ABSEIL_STATUS_MODULE_PATH` defined in `import_status_module.h`.)
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.
It isn't possible to specify separate return value policies for a `StatusOr`
object and its payload. Since `StatusOr` is processed and not ever actually
represented in Python, the return value policy applies to the payload. E.g., if
you return a `StatusOr` (note the `*` is inside the `StatusOr`) with
a take_ownership return val policy and the status is OK (i.e., 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*` (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`).
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.");
};
```
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 (i.e.,
`absl::StatusOr>`) to wrapped classes or structs (i.e., any
type which you created bindings for using `pybind11::class_<...>`) can be used,
but unique_ptrs to converted types (e.g., `int`, `string`, `absl::Time`,
`absl::Duration`, etc.) cannot be used.
### absl::StatusCode
The `status` module provides `pybind11::enum_` bindings for `absl::StatusCode`.
These use python constant style, e.g. `status.StatusCode.OK`,
`status.StatusCode.CANCELLED`, etc.
Warning: Pybind enums are their own type, and will never compare equally to
integers due to being a different type, regardless of their value. In particular
, note that the [status proto](http://google3/util/task/status.proto)
`code` field is an integer, so it will never directly compare as equal to a
`StatusCode`. To fix this, convert an integer to a `StatusCode` or vice-versa.
```python
status_code = 0 # An integer.
if status_code == status.StatusCode.OK: # Wrong: always evaluates to false.
...
if status.StatusCode(status_code) == status.StatusCode.OK: # Correct.
...
if status_code == int(status.StatusCode.OK): # Also correct.
...
```