Skip to content
This repository was archived by the owner on Nov 11, 2025. It is now read-only.

Commit 47a082b

Browse files
wangxfrwgk
authored andcommitted
Also generate Clif_PyObj functions for protobufs.
Many user defined `Clif_PyObj` functions depend on `Clif_PyObj`functions of protobufs. For example, https://source.corp.google.com/piper///depot/google3/devtools/clif/contrib/python/protobuf/repeated_field.h;l=24. PiperOrigin-RevId: 494856800
1 parent b2070c9 commit 47a082b

File tree

3 files changed

+137
-11
lines changed

3 files changed

+137
-11
lines changed

clif/pybind11/gen_type_info.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,87 @@ class CapsuleType(BaseType):
216216
def generate_clif_use(self) -> Generator[str, None, None]:
217217
yield (f'// CLIF use `{self.cpp_name}` as {self.py_name}, '
218218
'PythonCapsule, Pybind11Ignore')
219+
220+
221+
@dataclasses.dataclass
222+
class ProtoType(BaseType):
223+
"""Wraps a C++ proto Message."""
224+
225+
def generate_header(self) -> Generator[str, None, None]:
226+
yield ''
227+
yield from self.generate_clif_use()
228+
yield (f'PyObject* Clif_PyObjFrom(const {self.cpp_name}&, '
229+
'::clif::py::PostConv);')
230+
yield (f'PyObject* Clif_PyObjFrom(std::shared_ptr<const {self.cpp_name}>,'
231+
'::clif::py::PostConv);')
232+
yield (f'PyObject* Clif_PyObjFrom(std::unique_ptr<const {self.cpp_name}>,'
233+
'::clif::py::PostConv);')
234+
yield ''
235+
yield f'bool Clif_PyObjAs(PyObject* input, {self.cpp_name}* output);'
236+
yield ('bool Clif_PyObjAs(PyObject* input, '
237+
f'std::unique_ptr<{self.cpp_name}>* output);')
238+
239+
def generate_converters(self) -> Generator[str, None, None]:
240+
yield ''
241+
yield (f'PyObject* Clif_PyObjFrom(const {self.cpp_name}& c, '
242+
'::clif::py::PostConv) {')
243+
yield I + 'return pybind11::cast(c).release().ptr();'
244+
yield '}'
245+
yield (f'PyObject* Clif_PyObjFrom(std::shared_ptr<const {self.cpp_name}> c'
246+
', ::clif::py::PostConv) {')
247+
yield I + 'return pybind11::cast(std::move(c)).release().ptr();'
248+
yield '}'
249+
yield ''
250+
yield (f'PyObject* Clif_PyObjFrom(std::unique_ptr<const {self.cpp_name}> c'
251+
', ::clif::py::PostConv) {')
252+
yield I + 'return pybind11::cast(std::move(c)).release().ptr();'
253+
yield '}'
254+
yield ''
255+
yield f'bool Clif_PyObjAs(PyObject* input, {self.cpp_name}* output) {{'
256+
yield I + 'try {'
257+
yield I + I + (f'*output = pybind11::cast<{self.cpp_name}>'
258+
'(pybind11::handle(input));')
259+
yield I + '} catch (pybind11::cast_error) {'
260+
yield I + I + 'return false;'
261+
yield I + '}'
262+
yield I + 'return true;'
263+
yield '}'
264+
yield ''
265+
yield ('bool Clif_PyObjAs(PyObject* input, '
266+
f'std::unique_ptr<{self.cpp_name}>* output) {{')
267+
yield I + 'try {'
268+
yield I + I + ('*output = pybind11::cast<std::unique_ptr'
269+
f'<{self.cpp_name}>>(pybind11::handle(input));')
270+
yield I + '} catch (pybind11::cast_error) {'
271+
yield I + I + 'return false;'
272+
yield I + '}'
273+
yield I + 'return true;'
274+
yield '}'
275+
yield ''
276+
277+
278+
@dataclasses.dataclass
279+
class ProtoEnumType(BaseType):
280+
"""Wraps a C++ proto Enum."""
281+
282+
def generate_header(self) -> Generator[str, None, None]:
283+
yield ''
284+
yield from self.generate_clif_use()
285+
yield f'PyObject* Clif_PyObjFrom({self.cpp_name}, ::clif::py::PostConv);'
286+
yield f'bool Clif_PyObjAs(PyObject* input, {self.cpp_name}* output);'
287+
288+
def generate_converters(self) -> Generator[str, None, None]:
289+
yield ''
290+
yield (f'PyObject* Clif_PyObjFrom({self.cpp_name} c, '
291+
'::clif::py::PostConv) {')
292+
yield I + 'return pybind11::cast(c).release().ptr();'
293+
yield '}'
294+
yield f'bool Clif_PyObjAs(PyObject* input, {self.cpp_name}* output) {{'
295+
yield I + 'try {'
296+
yield I + I + (f'*output = pybind11::cast<{self.cpp_name}>'
297+
'(pybind11::handle(input));')
298+
yield I + '} catch (pybind11::cast_error) {'
299+
yield I + I + 'return false;'
300+
yield I + '}'
301+
yield I + 'return true;'
302+
yield '}'

clif/pybind11/generator.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,14 +248,16 @@ def _generate_headlines(self):
248248
yield '// potential future optimization: generate this line only as needed.'
249249
yield '#include "third_party/pybind11/include/pybind11/stl.h"'
250250
yield ''
251+
yield '// See pybind11_protobuf/proto_caster_impl.h'
252+
yield '#if !defined(PYBIND11_PROTOBUF_UNSAFE)'
253+
yield I + '#define PYBIND11_PROTOBUF_UNSAFE 1'
254+
yield '#endif'
255+
yield ''
251256
for include in includes:
252257
yield f'#include "{include}"'
253258
yield f'#include "{self._header_path}"'
254259
yield '#include "clif/pybind11/runtime.h"'
255260
yield '#include "clif/pybind11/type_casters.h"'
256-
257-
yield '// See pybind11_protobuf/proto_caster_impl.h'
258-
yield '#define PYBIND11_PROTOBUF_UNSAFE 1'
259261
yield '#include "third_party/pybind11_protobuf/native_proto_caster.h"'
260262
yield ''
261263
yield 'namespace py = pybind11;'

clif/python/proto.py

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import argparse
2525
import itertools
2626
import sys
27+
from clif.pybind11 import gen_type_info
2728
from clif.python import gen
2829
from clif.python import clif_types as types
2930
from clif.python.utils import proto_util
@@ -94,17 +95,56 @@ def CreatePyTypeInfo(desc, path,
9495
return messages
9596

9697

97-
def GenerateForPybind11(messages, proto_hdr):
98+
def GenerateForPybind11(messages, clif_hdr, proto_hdr):
9899
"""Generate no-op header files to bypass the checks of PyCLIF."""
100+
proto_types = []
101+
for m in messages:
102+
ctor = None
103+
if isinstance(m, types.ProtoType):
104+
ctor = gen_type_info.ProtoType
105+
elif isinstance(m, types.CapsuleType):
106+
ctor = gen_type_info.CapsuleType
107+
elif isinstance(m, types.ProtoEnumType):
108+
ctor = gen_type_info.ProtoEnumType
109+
if not ctor:
110+
raise _ParseError('Unsupported proto type.')
111+
proto_type = ctor(
112+
cpp_name=m.cname, py_name=m.pyname,
113+
cpp_namespace=m.namespace)
114+
proto_types.append(proto_type)
115+
proto_types = sorted(proto_types, key=lambda gen_type: gen_type.cpp_name)
99116
with open(FLAGS.header_out, 'w') as hout:
100-
hout.write(f'#include "{proto_hdr}"')
101-
hout.write('\n')
102-
for m in messages:
103-
hout.write(f'// CLIF use `{m.cname}` as {m.pyname}, Pybind11Ignore')
104-
hout.write('\n')
117+
gen.WriteTo(
118+
hout,
119+
[f'#include "{proto_hdr}"',
120+
'#include "clif/python/postconv.h"',
121+
'#include "third_party/pybind11/include/pybind11/smart_holder.h"',
122+
'#include "third_party/pybind11_protobuf/native_proto_caster.h"'])
123+
for namespace, typedefs in itertools.groupby(
124+
proto_types, lambda gen_type: gen_type.cpp_namespace):
125+
namespace = namespace.strip(':') or 'clif'
126+
gen.WriteTo(
127+
hout,
128+
[' '.join('namespace %s {' % ns for ns in namespace.split('::'))])
129+
for t in typedefs:
130+
gen.WriteTo(hout, t.generate_header())
131+
gen.WriteTo(
132+
hout,
133+
['} ' * (1 + namespace.count('::')) + ' // namespace ' + namespace])
105134

106135
with open(FLAGS.ccdeps_out, 'w') as cout:
107-
cout.write('')
136+
gen.WriteTo(cout, [f'#include "{clif_hdr}"'])
137+
for namespace, typedefs in itertools.groupby(
138+
proto_types, lambda gen_type: gen_type.cpp_namespace):
139+
namespace = namespace.strip(':') or 'clif'
140+
gen.WriteTo(
141+
cout,
142+
[' '.join('namespace %s {' % ns for ns in namespace.split('::'))])
143+
for t in typedefs:
144+
gen.WriteTo(cout, t.generate_converters())
145+
gen.WriteTo(
146+
cout,
147+
['} ' * (1 + namespace.count('::')) + ' // namespace ' + namespace])
108148

109149

110150
def GenerateFrom(messages, proto_filename, clif_hdr, proto_hdr):
@@ -167,7 +207,7 @@ def main(_):
167207
if FLAGS.pyclif_codegen_mode == 'c_api':
168208
GenerateFrom(messages, name, hdr, pypath+'.pb.h')
169209
else:
170-
GenerateForPybind11(messages, pypath+'.pb.h')
210+
GenerateForPybind11(messages, hdr, pypath+'.pb.h')
171211

172212

173213
def ParseFlags():

0 commit comments

Comments
 (0)