def _generate_constructor( class_name: str, func_decl: ast_pb2.FuncDecl, class_decl: ast_pb2.ClassDecl, capsule_types: Set[str], trampoline_generated: bool) -> Generator[str, None, None]: """Generates pybind11 bindings code for a constructor. Multiple deinitions will be generated when the constructor contains unknown default value arguments. Args: class_name: Name of the class that defines the contructor. func_decl: Constructor declaration in proto format. class_decl: Class declaration in proto format. capsule_types: A set of C++ types that are defined as capsules. trampoline_generated: Did we generate a trampoline for this class? Yields: pybind11 function bindings code. """ num_unknown = function_lib.num_unknown_default_values(func_decl) temp_func_decl = ast_pb2.FuncDecl() temp_func_decl.CopyFrom(func_decl) if num_unknown: for _ in range(num_unknown): yield from _generate_constructor_overload(class_name, temp_func_decl, class_decl, capsule_types, trampoline_generated) del temp_func_decl.params[-1] yield from _generate_constructor_overload(class_name, temp_func_decl, class_decl, capsule_types, trampoline_generated)
def MoveExtendPropertiesInPlace(ast): """See module docstring.""" extend_property_decls = [] extend_getter_decls = [] for decl in ast.decls: member_delete_indices = [] if decl.decltype != ast_pb2.Decl.Type.CLASS: continue for member_index, member in enumerate(decl.class_.members): if member.decltype != ast_pb2.Decl.Type.VAR: continue if not member.var.is_extend_variable: continue property_decl = ast_pb2.Decl() property_decl.CopyFrom(member) property_decl.var.name.native = (decl.class_.name.native + EXTEND_INFIX + member.var.name.native) if member.var.name.cpp_name == member.var.name.native: property_decl.var.name.cpp_name = (decl.class_.name.native + EXTEND_INFIX + member.var.name.cpp_name) p = _GenerateParameterSelf(decl.class_) property_decl.var.cpp_get.params.insert(0, p) property_decl.var.cpp_get.name.cpp_name = ( decl.class_.name.native + EXTEND_INFIX + member.var.cpp_get.name.cpp_name) if member.var.HasField('cpp_set'): property_decl.var.cpp_set.name.cpp_name = ( decl.class_.name.native + EXTEND_INFIX + member.var.cpp_set.name.cpp_name) p = _GenerateParameterSelf(decl.class_) property_decl.var.cpp_set.params.insert(0, p) extend_property_decls.append(property_decl) member_delete_indices.append(member_index) # generate property getters # (setters do not need this kind of functionality) getter_decl = ast_pb2.FuncDecl() getter_decl.CopyFrom(member.var.cpp_get) getter_decl.name.native = getter_decl.name.cpp_name = ( decl.class_.name.native + EXTEND_INFIX + member.var.cpp_get.name.cpp_name) p = _GenerateParameterSelf(decl.class_) getter_decl.params.insert(0, p) getter_decl.is_extend_method = True func_decl = ast_pb2.Decl() func_decl.func.CopyFrom(getter_decl) func_decl.decltype = ast_pb2.Decl.Type.FUNC extend_getter_decls.append(func_decl) for member_index in reversed(member_delete_indices): del decl.class_.members[member_index] ast.decls.extend(extend_property_decls) ast.decls.extend(extend_getter_decls)
def _MoveExtendPropertiesInPlaceOneClass( extend_property_decls, extend_getter_decls, outer_class_names, class_decl): """Helper for MoveExtendPropertiesInPlace.""" member_delete_indices = [] for member_index, member in enumerate(class_decl.members): if member.decltype == ast_pb2.Decl.Type.CLASS: _MoveExtendPropertiesInPlaceOneClass( extend_property_decls, extend_getter_decls, outer_class_names+[class_decl.name], member.class_) if member.decltype != ast_pb2.Decl.Type.VAR: continue if not member.var.is_extend_variable: continue fq_native = '_'.join( [n.native for n in outer_class_names + [class_decl.name]]) property_decl = ast_pb2.Decl() property_decl.CopyFrom(member) property_decl.var.name.native = ( fq_native + EXTEND_INFIX + member.var.name.native) if member.var.name.cpp_name == member.var.name.native: property_decl.var.name.cpp_name = ( fq_native + EXTEND_INFIX + member.var.name.cpp_name) p = _GenerateParameterSelf(class_decl, outer_class_names) property_decl.var.cpp_get.params.insert(0, p) property_decl.var.cpp_get.name.cpp_name = ( fq_native + EXTEND_INFIX + member.var.cpp_get.name.cpp_name) if member.var.HasField('cpp_set'): property_decl.var.cpp_set.name.cpp_name = ( fq_native + EXTEND_INFIX + member.var.cpp_set.name.cpp_name) p = _GenerateParameterSelf(class_decl, outer_class_names) property_decl.var.cpp_set.params.insert(0, p) extend_property_decls.append(property_decl) member_delete_indices.append(member_index) # generate property getters # (setters do not need this kind of functionality) getter_decl = ast_pb2.FuncDecl() getter_decl.CopyFrom(member.var.cpp_get) getter_decl.name.native = getter_decl.name.cpp_name = ( fq_native + EXTEND_INFIX + member.var.cpp_get.name.cpp_name) p = _GenerateParameterSelf(class_decl, outer_class_names) getter_decl.params.insert(0, p) getter_decl.is_extend_method = True func_decl = ast_pb2.Decl() func_decl.func.CopyFrom(getter_decl) func_decl.decltype = ast_pb2.Decl.Type.FUNC extend_getter_decls.append(func_decl) for member_index in reversed(member_delete_indices): del class_decl.members[member_index]
def _generate_overload_for_unknown_default_function( num_unknown: int, module_name: str, func_decl: ast_pb2.FuncDecl, capsule_types: Set[str], class_decl: Optional[ast_pb2.ClassDecl] = None ) -> Generator[str, None, None]: """Generate multiple definitions for functions with unknown default values.""" temp_func_decl = ast_pb2.FuncDecl() temp_func_decl.CopyFrom(func_decl) for _ in range(num_unknown): yield from _generate_function(module_name, temp_func_decl, capsule_types, class_decl) del temp_func_decl.params[-1] yield from _generate_function(module_name, temp_func_decl, capsule_types, class_decl)
def assertFuncEqual(self, proto, code): ast = ast_pb2.FuncDecl() text_format.Parse(proto, ast) out = '\n'.join(self.m.WrapFunc(ast, -1, ''))+'\n' self.assertMultiLineEqual(out, textwrap.dedent(code))
def _class(self, ln, ast, pb, ns=None): """Translate PYTD class IR ast to AST Decl protobuf.""" assert isinstance(pb, ast_pb2.Decl), repr(pb) atln = ' at line %d' % ln pb.line_number = ln pb.decltype = pb.CLASS p = pb.class_ cpp_name = ast.name[0] # Save C++ name without FQ. is_iterator = ast.name[-1] == '__iter__' _set_name(p.name, ast.name, ns, allow_fqcppname=is_iterator) pyname = p.name.native self.check_known_name(pyname) decorators = ast.decorators.asList() if not is_iterator: self._typetable[pyname] = [p.name.cpp_name] if 'final' in decorators: p.final = True decorators.remove('final') if decorators: raise NameError('Unknown class decorator(s)%s: %s' % (atln, ', '.join(decorators))) _set_bases(p.bases, ast.bases, self._names, self._typetable) local_names = set() for decl in ast[-1]: if decl[0] == 'pass': # Pass is a special case as we don't want to add() to the proto. if len(ast[-1]) != 1: raise SyntaxError('pass must be the only class statement' + atln) if not ast.bases: raise SyntaxError( 'only derived class allowed to be empty' + atln) continue line_number = self.line(decl[1]) if is_iterator: if (len(ast[-1]) != 1 or decl[0] != 'func' or decl.name[-1] != '__next__'): raise SyntaxError( '__iter__ class must only def __next__ at line %d' % line_number) else: if decl[0] == 'implements': name = decl[2] self._macro_values = [pyname] + decl[3:] try: nargs, src, _ = self._macros[name] except KeyError: raise NameError('interface %s not defined' % name + atln) if len(self._macro_values) != nargs + 1: raise NameError( 'interface %s needs %d args (%d given)' % (name, len(self._macro_values) - 1, nargs) + atln) for d in src: ln = self.line(d[1]) if d[0] == 'func': if not self.unproperty(ln, d, pyname, p.members, local_names): _add_uniq(pyname, local_names, self._func(ln, d, p.members.add())) elif d[0] == 'var': _add_uniq(pyname, local_names, self._var(ln, d, p.members.add())) else: raise SyntaxError( 'implements %s contains disallowed %s%s' % (name, d[0], atln)) self._macro_values = [] continue if decl[0] == 'func' and self.unproperty( line_number, decl, pyname, p.members, local_names): continue name = getattr(self, '_' + decl[0])(line_number, decl, p.members.add()) _add_uniq(pyname, local_names, name) for m in p.members: # Fix ctor name to be the class name. if m.decltype != m.FUNC: continue if m.func.name.native == '__init__': if (m.func.name.cpp_name and m.func.name.cpp_name not in [cpp_name, '__init__']): print( 'Arbitrary names (like "{0}") for {1} ctor not allowed.' ' Set to {1}'.format(m.func.name.cpp_name, cpp_name), file=sys.stderr) m.func.name.cpp_name = cpp_name m.func.constructor = True elif m.func.name.native == cpp_name: raise NameError('Use __init__ to wrap a "%s" constructor' % pyname + atln) elif m.func.name.cpp_name == cpp_name: # @add__init__ will reset cpp_name to be empty. raise NameError( 'Use @add__init__ to wrap additional "%s" constructor' '(s).' % pyname + atln) elif not m.func.name.cpp_name: # An additional ctor. m.func.name.cpp_name = cpp_name m.func.constructor = True # Fix 'self' for C++ operator function (implemented out of class). elif m.func.name.native in _RIGHT_OPS: if len(m.func.params) != 1: raise ValueError('%s must have only 1 input parameter' % m.func.name.native + atln) m.func.cpp_opfunction = True # Mark a free function. _set_self(m.func.params.add(), cpp_name) # Add the instance parameter. elif '::' in m.func.name.cpp_name: if not m.func.params: m.func.params.add() # else make room for param[0] by shifting 0->1, 1->2 and so on. elif len(m.func.params) == 1: m.func.params.extend([m.func.params[0]]) else: pcopy = ast_pb2.FuncDecl() pcopy.params.extend(m.func.params) del m.func.params[1:] m.func.params.extend(pcopy.params) del pcopy m.func.cpp_opfunction = True # Mark a free function. _set_self(m.func.params[0], cpp_name) # Set the instance parameter. _move_local_types(p.members, pyname + '.', cpp_name + '::', self._typetable) return pyname