def generate_from(self, ast: ast_pb2.AST): """Generates pybind11 bindings code from CLIF ast. Args: ast: CLIF ast protobuf. Yields: Generated pybind11 bindings code. """ yield from self._generate_headlines() # find and keep track of virtual functions python_override_class_names = {} yield from self._generate_python_override_class_names( python_override_class_names, ast) yield f'PYBIND11_MODULE({self._module_name}, m) {{' yield I + ('m.doc() = "CLIF generated pybind11-based module for ' f'{ast.source}";') for decl in ast.decls: if decl.decltype == ast_pb2.Decl.Type.FUNC: yield from function.generate_from('m', decl.func, None) elif decl.decltype == ast_pb2.Decl.Type.CONST: yield from self._generate_const_variables(decl.const) elif decl.decltype == ast_pb2.Decl.Type.CLASS: yield from classes.generate_from( decl.class_, 'm', python_override_class_names.get(decl.class_.name.cpp_name, '')) elif decl.decltype == ast_pb2.Decl.Type.ENUM: yield from enums.generate_from(decl.enum, 'm') yield '' yield '}'
def generate_from(self, ast: ast_pb2.AST): """Generates pybind11 bindings code from CLIF ast. Args: ast: CLIF ast protobuf. Yields: Generated pybind11 bindings code. """ yield from self._generate_headlines() # Find and keep track of virtual functions. trampoline_class_names = set() for decl in ast.decls: yield from self._generate_trampoline_classes( trampoline_class_names, decl) yield '' yield from type_casters.generate_from(ast, self._include_paths) mangled_module_name = _generate_mangled_name_for_module( self._module_path) yield (f'GOOGLE_PYBIND11_MODULE({self._module_name}, ' f'{mangled_module_name}, m) {{') yield from self._generate_import_modules(ast) yield I + ('m.doc() = "CLIF-generated pybind11-based module for ' f'{ast.source}";') if self._requires_status: yield I + 'py::google::ImportStatusModule();' yield I + 'py::google::PatchStatusBindings();' yield I + 'pybind11_protobuf::ImportNativeProtoCasters();' for decl in ast.decls: if decl.decltype == ast_pb2.Decl.Type.FUNC: for s in function.generate_from('m', decl.func, self._capsule_types, None): yield I + s elif decl.decltype == ast_pb2.Decl.Type.CONST: yield from consts.generate_from('m', decl.const) elif decl.decltype == ast_pb2.Decl.Type.CLASS: yield from classes.generate_from(decl.class_, 'm', trampoline_class_names, self._capsule_types, self._registered_types) elif decl.decltype == ast_pb2.Decl.Type.ENUM: yield from enums.generate_from('m', decl.enum) yield '}' yield '' for namespace, typedefs in itertools.groupby( self._types, lambda gen_type: gen_type.cpp_namespace): namespace = namespace.strip(':') or 'clif' yield ' '.join('namespace %s {' % ns for ns in namespace.split('::')) for t in typedefs: yield from t.generate_converters() yield '} ' * (1 + namespace.count('::')) + ' // namespace ' + namespace
def generate_from(self, ast: ast_pb2.AST): """Generates pybind11 bindings code from CLIF ast. Args: ast: CLIF ast protobuf. Yields: Generated pybind11 bindings code. """ yield from self._generate_headlines() # Find and keep track of virtual functions. python_override_class_names = {} # Every unique class requires a pybind11 smart holder type cast macro. unique_classes = set() for decl in ast.decls: yield from self._generate_python_override_class_names( python_override_class_names, decl) self._collect_class_cpp_names(decl, unique_classes) for c in unique_classes: yield f'PYBIND11_SMART_HOLDER_TYPE_CASTERS({c})' yield '\n' yield f'PYBIND11_MODULE({self._module_name}, m) {{' yield I + ('m.doc() = "CLIF-generated pybind11-based module for ' f'{ast.source}";') for decl in ast.decls: if decl.decltype == ast_pb2.Decl.Type.FUNC: yield from function.generate_from('m', decl.func, None) elif decl.decltype == ast_pb2.Decl.Type.CONST: yield from self._generate_const_variables(decl.const) elif decl.decltype == ast_pb2.Decl.Type.CLASS: yield from classes.generate_from( decl.class_, 'm', python_override_class_names.get(decl.class_.name.cpp_name, '')) elif decl.decltype == ast_pb2.Decl.Type.ENUM: yield from enums.generate_from(decl.enum, 'm') yield '' yield '}'
def generate_from(class_decl: ast_pb2.ClassDecl, superclass_name: str, trampoline_class_names: Set[str], capsule_types: Set[str], registered_types: Set[str]) -> Generator[str, None, None]: """Generates a complete py::class_<>. Args: class_decl: Class declaration in proto format. superclass_name: String name of the superclass. trampoline_class_names: A Set of class names whose member functions will be overriden in Python. capsule_types: A set of C++ types that are defined as capsules. registered_types: A set of C++ types that are registered with Pybind11. Yields: pybind11 class bindings code. """ yield I + '{' class_name = f'{class_decl.name.native}_class' definition = f'py::classh<{class_decl.name.cpp_name}' if not class_decl.suppress_upcasts: for base in class_decl.bases: if base.HasField('cpp_name') and base.cpp_name in registered_types: definition += f', {base.cpp_name}' trampoline_class_name = utils.trampoline_name(class_decl) if trampoline_class_name in trampoline_class_names: definition += f', {trampoline_class_name}' definition += (f'> {class_name}({superclass_name}, ' f'"{class_decl.name.native}"') if class_decl.HasField('docstring'): definition += f', {function_lib.generate_docstring(class_decl.docstring)}' if class_decl.enable_instance_dict: definition += ', py::dynamic_attr()' if class_decl.final: definition += ', py::is_final()' definition += ');' yield I + I + definition default_constructor_defined = False trampoline_generated = (utils.trampoline_name(class_decl) in trampoline_class_names) for member in class_decl.members: if member.decltype == ast_pb2.Decl.Type.CONST: for s in consts.generate_from(class_name, member.const): yield I + I + s elif member.decltype == ast_pb2.Decl.Type.FUNC: if member.func.constructor: # Legacy CLIF ignores __init__ for abstract classes. # Potential future cleanup project: generate a user-friendly error # instead. if (not class_decl.cpp_abstract or trampoline_generated) and ( class_decl.cpp_has_def_ctor or member.func.params): if not member.func.params: default_constructor_defined = True for s in _generate_constructor(class_name, member.func, class_decl, capsule_types, trampoline_generated): yield I + I + s else: # This function will be overriden in Python. Do not call it from the # abstract base class. if class_decl.cpp_abstract and member.func.virtual: continue else: for s in function.generate_from(class_name, member.func, capsule_types, class_decl): yield I + I + s elif member.decltype == ast_pb2.Decl.Type.VAR: for s in variables.generate_from(class_name, member.var, class_decl): yield I + I + s elif member.decltype == ast_pb2.Decl.Type.ENUM: for s in enums.generate_from(class_name, member.enum): yield I + I + s elif member.decltype == ast_pb2.Decl.Type.CLASS: if member.class_.name.native == '__iter__': assert len(member.class_.members) == 1, ( '__iter__ class must have only one "def", ' f'{len(member.class_.members)} members found') d = member.class_.members[0] assert d.decltype == d.FUNC, ( f'__iter__ class must have only func_decl members, {d.decltype} ' 'member found') assert d.func.name.native == '__next__', ( '__iter__ class must have only one "def __next__", ' f'"def {d.func.name.native}" found') for s in _generate_iterator(class_name, class_decl, d.func): yield I + I + s else: for s in generate_from(member.class_, class_name, trampoline_class_names, capsule_types, registered_types): yield I + s if (not default_constructor_defined and class_decl.cpp_has_def_ctor and (not class_decl.cpp_abstract or trampoline_generated)): yield I + I + f'{class_name}.def(py::init<>());' yield I + '}'
def _generate_enums(member: ast_pb2.Decl, class_name: str): """Generates enums.""" for s in enums.generate_from(member.enum, class_name): yield s