Exemple #1
0
    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 '}'
Exemple #2
0
    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
Exemple #3
0
    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 '}'
Exemple #4
0
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 + '}'
Exemple #5
0
def _generate_enums(member: ast_pb2.Decl, class_name: str):
    """Generates enums."""
    for s in enums.generate_from(member.enum, class_name):
        yield s