Skip to content

Commit 923f0a8

Browse files
rwgkcopybara-github
authored andcommitted
Internal change: Replace proto_casters.h with a temporary fake/stub (forwarding to native_proto_caster.h).
PiperOrigin-RevId: 458227296
1 parent f1dda84 commit 923f0a8

File tree

4 files changed

+85
-80
lines changed

4 files changed

+85
-80
lines changed

README.md

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -41,31 +41,58 @@ PYBIND11_MODULE(my_module, m) {
4141
```
4242

4343

44-
## Wrapped C++ vs Python Native Types
45-
46-
When passing protos between C++ and python, these bindings will convert the
47-
protocol buffer into the native python type, which usually involves protocol
48-
buffer serialization. The objects returned to python are the native python
49-
protocol buffer objects, and so `isinstance()` and normal protocol buffer
50-
operations and methods will behave as expected.
44+
## C++ Native vs Python Native Types
45+
46+
When passing protos between C++ and Python, the `native_proto_caster.h`
47+
bindings will convert protobuf objects to the native type on either side.
48+
49+
While C++ has only one native type, Python has two native types
50+
(https://rules-proto-grpc.com/en/latest/lang/python.html):
51+
52+
* `--define=use_fast_cpp_protos=false` (aka `use_pure_python_protos`)
53+
* `--define=use_fast_cpp_protos=true`
54+
55+
With `use_pure_python_protos`, protobuf objects passed between C++ and Python
56+
are always serialized/deserialized between the native C++ type and the pure
57+
Python native type. This is very safe but also slow.
58+
59+
With `use_fast_cpp_protos`, the native Python type is internally backed by
60+
the native C++ type, which unlocks various performance benefits, even when
61+
only used from Python. When passing protobuf objects between Python and C++,
62+
in certain situations the serialization/deserialization overhead can be
63+
avoided, but not universally. Fundamentally, sharing C++ native protobuf
64+
objects between C++ and Python is unsafe because C++ assumes that it has
65+
exclusive ownership and may manipulate references in a way that undermines
66+
Python's much safer ownership semantics. Because of this, sharing mutable
67+
references or pointers between C++ and Python is not allowed. However, when
68+
passing a Python protobuf object to C++, the bindings may share the underlying
69+
C++ native protobuf object with C++ when passed by `const &` or `const *`.
70+
71+
### Protobuf Extensions
72+
73+
With `use_fast_cpp_protos`, any `py_proto_library` becomes implicitly dependent
74+
on the corresponding `cc_proto_library`, but this is currently not handled
75+
automatically, nor clearly diagnosed or formally enforced. In the absence of
76+
protobuf extensions
77+
usually there is no problem: Python code tends to depend organically on
78+
a given `py_proto_library`, and pybind11-wrapped C++ code tends to depend
79+
organically on the corresponding `cc_proto_library`. However, when extensions
80+
are involved, a well-known pitfall is to accidentally omit the corresponding
81+
`cc_proto_library`. Currently this needs to be kept in mind as a pitfall
82+
(sorry), but the usual best-practice unit testing is likely to catch such
83+
situations. Once discovered, the fix is easy: add the `cc_proto_library`
84+
to the `deps` of the relevant `pybind_library` or `pybind_extension`.
85+
86+
### Enumerations
5187

5288
Enumerations are passed and returned as integers. You may use the enum values
5389
from the native python proto module to set and check the enum values used
5490
by a bound proto enum (see `tests/proto_enum_test.py` for an example).
5591

56-
Fundamentally sharing protocol buffer types between C++ and python runtimes
57-
is unsafe because C++ assumes that it has full ownership of a protocol buffer
58-
message, and may manipulate references in a way that undermines python
59-
ownership semantics. Because of this sharing mutable references or pointers
60-
between C++ and python is not allowed. However when using the python fast
61-
proto implementation, the bindings may share an underlying protocol buffer
62-
with C++ when passed by `const &` into a C++ function.
92+
### In / Out Parameters
6393

6494
In cases where a protocol buffer is used as an in/out parameter in C++,
65-
additional logic will be required in the wrapper.
66-
67-
68-
### In / Out example
95+
additional logic will be required in the wrapper. For example:
6996

7097
```cpp
7198
#include <pybind11/pybind11.h>
@@ -87,39 +114,15 @@ PYBIND11_MODULE(my_module, m) {
87114
```
88115
89116
90-
## Integration with the Old Way
91-
92-
Due to the nature of pybind11, extension modules built using `pybind11_protobuf/native_proto_caster.h`
93-
cannot interoperate with the older bindings (those modules built using `pybind11_protobuf/proto_casters.h`)
94-
as that may generate duplicate pybind11 specializations for a given protocol buffer
95-
which would violate C++ ODR rules in an undetectable way. In order to workaround
96-
that failure mode a nearly-transparent wrapper is also provided.
97-
98-
To use the wrappers:
99-
100-
1. Include the header file `pybind11_protobuf/wrapped_proto_caster.h`
101-
in the .cc file with your bindings.
102-
1. Call `pybind11_protobuf::ImportWrappedProtoCasters();` in your `PYBIND11_MODULE` definition.
103-
1. Wrap the C++ code with `pybind11_protobuf::WithWrappedProtos`.
104-
105-
### Wrapped Example
106-
107-
```cpp
108-
#include <pybind11/pybind11.h>
109-
110-
#include "path/to/my/my_message.proto.h"
111-
#include "pybind11_protobuf/wrapped_proto_caster.h"
112-
113-
// In real use, these 2 functions would probably be defined in a python-agnostic library.
114-
MyMessage ReturnMyMessage() { ... }
115-
void TakeMyMessage(const MyMessage& in) { ... }
116-
117-
PYBIND11_MODULE(my_module, m) {
118-
pybind11_protobuf::ImportWrappedProtoCasters();
117+
### `pybind11_protobuf/wrapped_proto_caster.h`
119118
120-
using pybind11_protobuf::WithWrappedProtos;
119+
TL;DR: Ignore `wrapped_proto_caster.h` if you can, this header was added as
120+
a migration aid before the removal of `proto_casters.h`.
121121
122-
m.def("return_my_message", WithWrappedProtos(&ReturnMyMessage));
123-
m.def("take_my_message", WithWrappedProtos(&TakeMyMessage), pybind11::arg("in"));
124-
}
125-
```
122+
Historic background: Due to the nature of pybind11, extension modules
123+
built using `native_proto_caster.h` could not interoperate with the
124+
older `proto_casters.h` bindings, as that would have led to C++ ODR
125+
violations. `wrapped_proto_caster.h` is a nearly-transparent wrapper for
126+
`native_proto_caster.h` to work around the ODR issue. With the migration to
127+
`native_proto_caster.h` now completed, `wrapped_proto_caster.h` is obsolete
128+
and will be removed in the future.

pybind11_protobuf/BUILD

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ pybind_library(
2222
name = "native_proto_caster",
2323
srcs = ["native_proto_caster.cc"],
2424
hdrs = ["native_proto_caster.h"],
25-
defines = [
26-
# Force a build error when build variants are mixed.
27-
"PYBIND11_PROTOBUF_MODE=native_protos",
28-
],
2925
visibility = [
3026
"//visibility:public",
3127
],

pybind11_protobuf/proto_casters.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Minimal fake/stub to be used as a means to globally switch all dependents
2+
// from proto_casters.h to native_proto_caster.h (b/229395875).
3+
4+
#ifndef PYBIND11_PROTOBUF_PROTO_CASTERS_H_
5+
#define PYBIND11_PROTOBUF_PROTO_CASTERS_H_
6+
7+
// Satisfy existing transitive dependencies. To be cleaned up along with
8+
// replacing the proto_casters.h includes.
9+
#include <pybind11/functional.h>
10+
#include <pybind11/stl.h>
11+
12+
// Pull in the new caster.
13+
#include "pybind11_protobuf/native_proto_caster.h"
14+
15+
namespace pybind11 {
16+
namespace google {
17+
18+
inline void ImportProtoModule() {
19+
pybind11_protobuf::ImportNativeProtoCasters();
20+
}
21+
22+
template <typename ProtoType>
23+
void RegisterProtoMessageType(module_) {
24+
// NOOP
25+
}
26+
27+
} // namespace google
28+
} // namespace pybind11
29+
30+
#endif // PYBIND11_PROTOBUF_PROTO_CASTERS_H_

pybind11_protobuf/tests/missing_import_test.py

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)