Optionally generate Python type annotations (#7858)

* optionally generate type prefixes and suffixes for python code

* fix codegen error when qualified name is empty

* WIP: Python typing

* more progress towards python typing

* Further iterate on Python generated code typing

* clang-format

* Regenerate code

* add documentation for Python type annotations option

* generate code with Python type annotations

* handle forward references

* clang-format
This commit is contained in:
Max Burke
2023-04-28 09:38:29 -07:00
committed by GitHub
parent 6eae49a79a
commit a397dd7e8c
35 changed files with 1295 additions and 979 deletions

View File

@@ -35,6 +35,9 @@ namespace python {
namespace {
typedef std::pair<std::string, std::string> ImportMapEntry;
typedef std::set<ImportMapEntry> ImportMap;
static std::set<std::string> PythonKeywords() {
return { "False", "None", "True", "and", "as", "assert",
"break", "class", "continue", "def", "del", "elif",
@@ -129,7 +132,11 @@ class PythonGenerator : public BaseGenerator {
code += Indent + "@classmethod\n";
code += Indent + "def GetRootAs";
code += "(cls, buf, offset=0):";
if (parser_.opts.python_typing) {
code += "(cls, buf, offset: int = 0):";
} else {
code += "(cls, buf, offset=0):";
}
code += "\n";
code += Indent + Indent;
code += "n = flatbuffers.encode.Get";
@@ -156,7 +163,11 @@ class PythonGenerator : public BaseGenerator {
auto &code = *code_ptr;
GenReceiver(struct_def, code_ptr);
code += "Init(self, buf, pos):\n";
if (parser_.opts.python_typing) {
code += "Init(self, buf: bytes, pos: int):\n";
} else {
code += "Init(self, buf, pos):\n";
}
code += Indent + Indent + "self._tab = flatbuffers.table.Table(buf, pos)\n";
code += "\n";
}
@@ -167,8 +178,11 @@ class PythonGenerator : public BaseGenerator {
auto &code = *code_ptr;
GenReceiver(struct_def, code_ptr);
code += namer_.Method(field) + "Length(self";
code += "):";
code += namer_.Method(field) + "Length(self)";
if (parser_.opts.python_typing) {
code += " -> int";
}
code += ":";
if(!IsArray(field.value.type)){
code += OffsetPrefix(field,false);
code += GenIndents(3) + "return self._tab.VectorLen(o)";
@@ -184,8 +198,11 @@ class PythonGenerator : public BaseGenerator {
auto &code = *code_ptr;
GenReceiver(struct_def, code_ptr);
code += namer_.Method(field) + "IsNone(self";
code += "):";
code += namer_.Method(field) + "IsNone(self)";
if (parser_.opts.python_typing) {
code += " -> bool";
}
code += ":";
if(!IsArray(field.value.type)){
code += GenIndents(2) +
"o = flatbuffers.number_types.UOffsetTFlags.py_type" +
@@ -253,17 +270,32 @@ class PythonGenerator : public BaseGenerator {
// Get the value of a fixed size array.
void GetArrayOfStruct(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) const {
std::string *code_ptr, ImportMap &imports) const {
auto &code = *code_ptr;
const auto vec_type = field.value.type.VectorType();
GenReceiver(struct_def, code_ptr);
code += namer_.Method(field);
code += "(self, i: int):";
if (parser_.opts.include_dependence_headers) {
code += GenIndents(2);
code += "from " + GenPackageReference(field.value.type) + " import " +
TypeName(field);
const ImportMapEntry import_entry = {
"." + GenPackageReference(field.value.type), TypeName(field)
};
if (parser_.opts.python_typing) {
const std::string return_type = ReturnType(struct_def, field);
code += "(self, i: int)";
code += " -> " + return_type + ":";
imports.insert(import_entry);
} else {
code += "(self, i):";
}
if (parser_.opts.include_dependence_headers && !parser_.opts.python_typing) {
code += GenIndents(2);
code += "from " + import_entry.first + " import " + import_entry.second +
"\n";
}
code += GenIndents(2) + "obj = " + TypeName(field) + "()";
code += GenIndents(2) + "obj.Init(self._tab.Bytes, self._tab.Pos + ";
code += NumToString(field.value.offset) + " + i * ";
@@ -299,11 +331,22 @@ class PythonGenerator : public BaseGenerator {
// Get a struct by initializing an existing struct.
// Specific to Table.
void GetStructFieldOfTable(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) const {
std::string *code_ptr, ImportMap &imports) const {
auto &code = *code_ptr;
GenReceiver(struct_def, code_ptr);
code += namer_.Method(field);
code += "(self):";
code += namer_.Method(field) + "(self)";
const ImportMapEntry import_entry = {
"." + GenPackageReference(field.value.type), TypeName(field)
};
if (parser_.opts.python_typing) {
const std::string return_type = ReturnType(struct_def, field);
code += " -> Optional[" + return_type + "]";
imports.insert(ImportMapEntry{ "typing", "Optional" });
imports.insert(import_entry);
}
code += ":";
code += OffsetPrefix(field);
if (field.value.type.struct_def->fixed) {
code += Indent + Indent + Indent + "x = o + self._tab.Pos\n";
@@ -311,10 +354,11 @@ class PythonGenerator : public BaseGenerator {
code += Indent + Indent + Indent;
code += "x = self._tab.Indirect(o + self._tab.Pos)\n";
}
if (parser_.opts.include_dependence_headers) {
if (parser_.opts.include_dependence_headers && !parser_.opts.python_typing) {
code += Indent + Indent + Indent;
code += "from " + GenPackageReference(field.value.type) + " import " +
TypeName(field) + "\n";
code += "from " + import_entry.first + " import " + import_entry.second +
"\n";
}
code += Indent + Indent + Indent + "obj = " + TypeName(field) + "()\n";
code += Indent + Indent + Indent + "obj.Init(self._tab.Bytes, x)\n";
@@ -324,11 +368,18 @@ class PythonGenerator : public BaseGenerator {
// Get the value of a string.
void GetStringField(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) const {
std::string *code_ptr, ImportMap &imports) const {
auto &code = *code_ptr;
GenReceiver(struct_def, code_ptr);
code += namer_.Method(field);
code += "(self):";
if (parser_.opts.python_typing) {
code += "(self) -> Optional[str]:";
imports.insert(ImportMapEntry{ "typing", "Optional" });
} else {
code += "(self):";
}
code += OffsetPrefix(field);
code += Indent + Indent + Indent + "return " + GenGetter(field.value.type);
code += "o + self._tab.Pos)\n";
@@ -337,21 +388,34 @@ class PythonGenerator : public BaseGenerator {
// Get the value of a union from an object.
void GetUnionField(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) const {
std::string *code_ptr, ImportMap &imports) const {
auto &code = *code_ptr;
GenReceiver(struct_def, code_ptr);
code += namer_.Method(field) + "(self):";
std::string return_ty = "flatbuffers.table.Table";
bool is_native_table = TypeName(field) == "*flatbuffers.Table";
ImportMapEntry import_entry;
if (is_native_table) {
import_entry = ImportMapEntry{ "flatbuffers.table", "Table" };
} else {
return_ty = TypeName(field);
import_entry = ImportMapEntry{ GenPackageReference(field.value.type),
TypeName(field) };
}
code += namer_.Method(field) + "(self)";
if (parser_.opts.python_typing) {
code += " -> Optional[" + return_ty + "]";
imports.insert(ImportMapEntry{ "typing", "Optional" });
imports.insert(import_entry);
}
code += ":";
code += OffsetPrefix(field);
// TODO(rw): this works and is not the good way to it:
bool is_native_table = TypeName(field) == "*flatbuffers.Table";
if (is_native_table) {
code +=
Indent + Indent + Indent + "from flatbuffers.table import Table\n";
} else if (parser_.opts.include_dependence_headers) {
if (!parser_.opts.python_typing) {
code += Indent + Indent + Indent;
code += "from " + GenPackageReference(field.value.type) + " import " +
TypeName(field) + "\n";
code += "from " + import_entry.first + " import " + import_entry.second +
"\n";
}
code += Indent + Indent + Indent + "obj = Table(bytearray(), 0)\n";
code += Indent + Indent + Indent + GenGetter(field.value.type);
@@ -373,14 +437,26 @@ class PythonGenerator : public BaseGenerator {
// Get the value of a vector's struct member.
void GetMemberOfVectorOfStruct(const StructDef &struct_def,
const FieldDef &field,
std::string *code_ptr) const {
const FieldDef &field, std::string *code_ptr,
ImportMap &imports) const {
auto &code = *code_ptr;
auto vectortype = field.value.type.VectorType();
GenReceiver(struct_def, code_ptr);
code += namer_.Method(field);
code += "(self, j):" + OffsetPrefix(field);
const ImportMapEntry import_entry = {
"." + GenPackageReference(field.value.type), TypeName(field)
};
if (parser_.opts.python_typing) {
const std::string return_type = ReturnType(struct_def, field);
code += "(self, j: int) -> Optional[" + return_type + "]";
imports.insert(ImportMapEntry{ "typing", "Optional" });
imports.insert(import_entry);
} else {
code += "(self, j)";
}
code += ":" + OffsetPrefix(field);
code += Indent + Indent + Indent + "x = self._tab.Vector(o)\n";
code += Indent + Indent + Indent;
code += "x += flatbuffers.number_types.UOffsetTFlags.py_type(j) * ";
@@ -388,10 +464,10 @@ class PythonGenerator : public BaseGenerator {
if (!(vectortype.struct_def->fixed)) {
code += Indent + Indent + Indent + "x = self._tab.Indirect(x)\n";
}
if (parser_.opts.include_dependence_headers) {
if (parser_.opts.include_dependence_headers && !parser_.opts.python_typing) {
code += Indent + Indent + Indent;
code += "from " + GenPackageReference(field.value.type) + " import " +
TypeName(field) + "\n";
code += "from " + import_entry.first + " import " + import_entry.second +
"\n";
}
code += Indent + Indent + Indent + "obj = " + TypeName(field) + "()\n";
code += Indent + Indent + Indent + "obj.Init(self._tab.Bytes, x)\n";
@@ -409,7 +485,12 @@ class PythonGenerator : public BaseGenerator {
GenReceiver(struct_def, code_ptr);
code += namer_.Method(field);
code += "(self, j):";
if (parser_.opts.python_typing) {
code += "(self, j: int)";
} else {
code += "(self, j)";
}
code += ":";
code += OffsetPrefix(field);
code += Indent + Indent + Indent + "a = self._tab.Vector(o)\n";
code += Indent + Indent + Indent;
@@ -476,8 +557,8 @@ class PythonGenerator : public BaseGenerator {
// Returns a nested flatbuffer as itself.
void GetVectorAsNestedFlatbuffer(const StructDef &struct_def,
const FieldDef &field,
std::string *code_ptr) const {
const FieldDef &field, std::string *code_ptr,
ImportMap &imports) const {
auto nested = field.attributes.Lookup("nested_flatbuffer");
if (!nested) { return; } // There is no nested flatbuffer.
@@ -487,14 +568,26 @@ class PythonGenerator : public BaseGenerator {
qualified_name = nested->constant;
}
const ImportMapEntry import_entry = { "." + qualified_name,
unqualified_name };
auto &code = *code_ptr;
GenReceiver(struct_def, code_ptr);
code += namer_.Method(field) + "NestedRoot(self):";
code += namer_.Method(field) + "NestedRoot(self)";
if (parser_.opts.python_typing) {
code += " -> Union[" + unqualified_name + ", int]";
imports.insert(ImportMapEntry{ "typing", "Union" });
imports.insert(import_entry);
}
code += ":";
code += OffsetPrefix(field);
code += Indent + Indent + Indent;
code += "from " + qualified_name + " import " + unqualified_name + "\n";
if (!parser_.opts.python_typing) {
code += Indent + Indent + Indent;
code += "from " + import_entry.first + " import " + import_entry.second +
"\n";
}
code += Indent + Indent + Indent + "return " + unqualified_name;
code += ".GetRootAs";
code += "(self._tab.Bytes, self._tab.Vector(o))\n";
@@ -613,15 +706,25 @@ class PythonGenerator : public BaseGenerator {
const auto name = parser_.opts.python_no_type_prefix_suffix ? "Start" : struct_type + "Start";
code += "def " + name + "(builder):\n";
code += Indent + "return builder.StartObject(";
code += "def " + name;
if (parser_.opts.python_typing) {
code += "(builder: flatbuffers.Builder):\n";
} else {
code += "(builder):\n";
}
code += Indent + "builder.StartObject(";
code += NumToString(struct_def.fields.vec.size());
code += ")\n\n";
if (!parser_.opts.one_file && !parser_.opts.python_no_type_prefix_suffix) {
// Generate method without struct name.
code += "def Start(builder):\n";
code += Indent + "return " + struct_type + "Start(builder)\n\n";
if (parser_.opts.python_typing) {
code += "def Start(builder: flatbuffers.Builder):\n";
} else {
code += "def Start(builder):\n";
}
code += Indent + struct_type + "Start(builder)\n\n";
}
}
@@ -631,15 +734,19 @@ class PythonGenerator : public BaseGenerator {
auto &code = *code_ptr;
const std::string field_var = namer_.Variable(field);
const std::string field_method = namer_.Method(field);
const std::string field_ty = GenFieldTy(field);
const auto name = parser_.opts.python_no_type_prefix_suffix ? "Add" + field_method : namer_.Type(struct_def) + "Add" + field_method;
// Generate method with struct name.
code += "def " + name;
code += "(builder, ";
code += field_var;
if (parser_.opts.python_typing) {
code += "(builder: flatbuffers.Builder, " + field_var + ": " + field_ty;
} else {
code += "(builder, " + field_var;
}
code += "):\n";
code += Indent + "return builder.Prepend";
code += Indent + "builder.Prepend";
code += GenMethod(field) + "Slot(";
code += NumToString(offset) + ", ";
if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) {
@@ -660,9 +767,9 @@ class PythonGenerator : public BaseGenerator {
if (!parser_.opts.one_file && !parser_.opts.python_no_type_prefix_suffix) {
// Generate method without struct name.
code += "def Add" + field_method + "(builder, " + field_var + "):\n";
code += "def Add" + field_method + "(builder: flatbuffers.Builder, " + field_var + ": " + field_ty + "):\n";
code +=
Indent + "return " + namer_.Type(struct_def) + "Add" + field_method;
Indent + namer_.Type(struct_def) + "Add" + field_method;
code += "(builder, ";
code += field_var;
code += ")\n\n";
@@ -679,7 +786,12 @@ class PythonGenerator : public BaseGenerator {
// Generate method with struct name.
const auto name = parser_.opts.python_no_type_prefix_suffix ? "Start" + field_method : struct_type + "Start" + field_method;
code += "def " + name;
code += "Vector(builder, numElems):\n";
if (parser_.opts.python_typing) {
code += "Vector(builder, numElems: int) -> int:\n";
} else {
code += "Vector(builder, numElems):\n";
}
code += Indent + "return builder.StartVector(";
auto vector_type = field.value.type.VectorType();
auto alignment = InlineAlignment(vector_type);
@@ -690,7 +802,7 @@ class PythonGenerator : public BaseGenerator {
if (!parser_.opts.one_file && !parser_.opts.python_no_type_prefix_suffix) {
// Generate method without struct name.
code += "def Start" + field_method + "Vector(builder, numElems):\n";
code += "def Start" + field_method + "Vector(builder, numElems: int) -> int:\n";
code += Indent + "return " + struct_type + "Start";
code += field_method + "Vector(builder, numElems)\n\n";
}
@@ -739,12 +851,20 @@ class PythonGenerator : public BaseGenerator {
const auto name = parser_.opts.python_no_type_prefix_suffix ? "End" : namer_.Type(struct_def) + "End";
// Generate method with struct name.
code += "def " + name + "(builder):\n";
if (parser_.opts.python_typing) {
code += "def " + name + "(builder: flatbuffers.Builder) -> int:\n";
} else {
code += "def " + name + "(builder):\n";
}
code += Indent + "return builder.EndObject()\n\n";
if (!parser_.opts.one_file && !parser_.opts.python_no_type_prefix_suffix) {
// Generate method without struct name.
code += "def End(builder):\n";
if (parser_.opts.python_typing) {
code += "def End(builder: flatbuffers.Builder) -> int:\n";
} else {
code += "def End(builder):\n";
}
code += Indent + "return " + namer_.Type(struct_def) + "End(builder)";
code += "\n";
}
@@ -759,7 +879,7 @@ class PythonGenerator : public BaseGenerator {
// Generate a struct field, conditioned on its child type(s).
void GenStructAccessor(const StructDef &struct_def, const FieldDef &field,
std::string *code_ptr) const {
std::string *code_ptr, ImportMap &imports) const {
GenComment(field.doc_comment, code_ptr, &def_comment, Indent.c_str());
if (IsScalar(field.value.type.base_type)) {
if (struct_def.fixed) {
@@ -773,35 +893,35 @@ class PythonGenerator : public BaseGenerator {
if (struct_def.fixed) {
GetStructFieldOfStruct(struct_def, field, code_ptr);
} else {
GetStructFieldOfTable(struct_def, field, code_ptr);
GetStructFieldOfTable(struct_def, field, code_ptr, imports);
}
break;
case BASE_TYPE_STRING:
GetStringField(struct_def, field, code_ptr);
GetStringField(struct_def, field, code_ptr, imports);
break;
case BASE_TYPE_VECTOR: {
auto vectortype = field.value.type.VectorType();
if (vectortype.base_type == BASE_TYPE_STRUCT) {
GetMemberOfVectorOfStruct(struct_def, field, code_ptr);
GetMemberOfVectorOfStruct(struct_def, field, code_ptr, imports);
} else {
GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr);
GetVectorOfNonStructAsNumpy(struct_def, field, code_ptr);
GetVectorAsNestedFlatbuffer(struct_def, field, code_ptr);
GetVectorAsNestedFlatbuffer(struct_def, field, code_ptr, imports);
}
break;
}
case BASE_TYPE_ARRAY: {
auto vectortype = field.value.type.VectorType();
if (vectortype.base_type == BASE_TYPE_STRUCT) {
GetArrayOfStruct(struct_def, field, code_ptr);
GetArrayOfStruct(struct_def, field, code_ptr, imports);
} else {
GetArrayOfNonStruct(struct_def, field, code_ptr);
GetVectorOfNonStructAsNumpy(struct_def, field, code_ptr);
GetVectorAsNestedFlatbuffer(struct_def, field, code_ptr);
GetVectorAsNestedFlatbuffer(struct_def, field, code_ptr, imports);
}
break;
}
case BASE_TYPE_UNION: GetUnionField(struct_def, field, code_ptr); break;
case BASE_TYPE_UNION: GetUnionField(struct_def, field, code_ptr, imports); break;
default: FLATBUFFERS_ASSERT(0);
}
}
@@ -816,7 +936,11 @@ class PythonGenerator : public BaseGenerator {
std::string *code_ptr) const {
auto &code = *code_ptr;
code += Indent + "@classmethod\n";
code += Indent + "def SizeOf(cls):\n";
if (parser_.opts.python_typing) {
code += Indent + "def SizeOf(cls) -> int:\n";
} else {
code += Indent + "def SizeOf(cls):\n";
}
code +=
Indent + Indent + "return " + NumToString(struct_def.bytesize) + "\n";
code += "\n";
@@ -868,7 +992,8 @@ class PythonGenerator : public BaseGenerator {
}
// Generates struct or table methods.
void GenStruct(const StructDef &struct_def, std::string *code_ptr) const {
void GenStruct(const StructDef &struct_def, std::string *code_ptr,
ImportMap &imports) const {
if (struct_def.generated) return;
GenComment(struct_def.doc_comment, code_ptr, &def_comment);
@@ -893,7 +1018,7 @@ class PythonGenerator : public BaseGenerator {
auto &field = **it;
if (field.deprecated) continue;
GenStructAccessor(struct_def, field, code_ptr);
GenStructAccessor(struct_def, field, code_ptr, imports);
}
if (struct_def.fixed) {
@@ -1763,6 +1888,31 @@ class PythonGenerator : public BaseGenerator {
}
}
std::string GenFieldTy(const FieldDef &field) const {
if (IsScalar(field.value.type.base_type) || IsArray(field.value.type)) {
const std::string ty = GenTypeBasic(field.value.type);
if (ty.find("int") != std::string::npos) {
return "int";
}
if (ty.find("float") != std::string::npos) {
return "float";
}
if (ty == "bool") {
return "bool";
}
return "Any";
} else {
if (IsStruct(field.value.type)) {
return "Any";
} else {
return "int";
}
}
}
// Returns the method name for use with add/put calls.
std::string GenMethod(const FieldDef &field) const {
return (IsScalar(field.value.type.base_type) || IsArray(field.value.type))
@@ -1805,6 +1955,31 @@ class PythonGenerator : public BaseGenerator {
return GenTypeGet(field.value.type);
}
std::string ReturnType(const StructDef &struct_def,
const FieldDef &field) const {
// If we have a class member that returns an instance of the same class,
// for example:
// class Field(object):
// def Children(self, j: int) -> Optional[Field]:
// pass
//
// we need to quote the return type:
// class Field(object):
// def Children(self, j: int) -> Optional['Field']:
// pass
//
// because Python is unable to resolve the name during parse and will return
// an error.
// (see PEP 484 under forward references:
// https://peps.python.org/pep-0484/#forward-references)
const std::string self_type = struct_def.name;
std::string field_type = TypeName(field);
if (self_type == field_type) { field_type = "'" + field_type + "'"; }
return field_type;
}
// Create a struct with a builder and the struct's arguments.
void GenStructBuilder(const StructDef &struct_def,
std::string *code_ptr) const {
@@ -1822,13 +1997,16 @@ class PythonGenerator : public BaseGenerator {
bool generate() {
std::string one_file_code;
ImportMap one_file_imports;
if (!generateEnums(&one_file_code)) return false;
if (!generateStructs(&one_file_code)) return false;
if (!generateStructs(&one_file_code, one_file_imports)) return false;
if (parser_.opts.one_file) {
const std::string mod = file_name_ + "_generated";
// Legacy file format uses keep casing.
return SaveType(file_name_ + "_generated.py", *parser_.current_namespace_,
one_file_code, true);
return SaveType(mod + ".py", *parser_.current_namespace_, one_file_code,
one_file_imports, mod, true);
}
return true;
@@ -1848,29 +2026,45 @@ class PythonGenerator : public BaseGenerator {
if (parser_.opts.one_file && !enumcode.empty()) {
*one_file_code += enumcode + "\n\n";
} else {
ImportMap imports;
const std::string mod =
namer_.File(enum_def, SkipFile::SuffixAndExtension);
if (!SaveType(namer_.File(enum_def, SkipFile::Suffix),
*enum_def.defined_namespace, enumcode, false))
*enum_def.defined_namespace, enumcode, imports, mod,
false))
return false;
}
}
return true;
}
bool generateStructs(std::string *one_file_code) const {
bool generateStructs(std::string *one_file_code,
ImportMap &one_file_imports) const {
for (auto it = parser_.structs_.vec.begin();
it != parser_.structs_.vec.end(); ++it) {
auto &struct_def = **it;
std::string declcode;
GenStruct(struct_def, &declcode);
ImportMap imports;
GenStruct(struct_def, &declcode, imports);
if (parser_.opts.generate_object_based_api) {
GenStructForObjectAPI(struct_def, &declcode);
}
if (parser_.opts.one_file && !declcode.empty()) {
*one_file_code += declcode + "\n\n";
if (parser_.opts.one_file) {
if (!declcode.empty()) {
*one_file_code += declcode + "\n\n";
}
for (auto import_str: imports) {
one_file_imports.insert(import_str);
}
} else {
const std::string mod =
namer_.File(struct_def, SkipFile::SuffixAndExtension);
if (!SaveType(namer_.File(struct_def, SkipFile::Suffix),
*struct_def.defined_namespace, declcode, true))
*struct_def.defined_namespace, declcode, imports, mod,
true))
return false;
}
}
@@ -1879,24 +2073,44 @@ class PythonGenerator : public BaseGenerator {
// Begin by declaring namespace and imports.
void BeginFile(const std::string &name_space_name, const bool needs_imports,
std::string *code_ptr) const {
std::string *code_ptr, const std::string &mod,
const ImportMap &imports) const {
auto &code = *code_ptr;
code = code + "# " + FlatBuffersGeneratedWarning() + "\n\n";
code += "# namespace: " + name_space_name + "\n\n";
if (needs_imports) {
const std::string local_import = "." + mod;
code += "import flatbuffers\n";
code += "from flatbuffers.compat import import_numpy\n";
if (parser_.opts.python_typing) {
code += "from typing import Any\n";
for (auto import_entry : imports) {
// If we have a file called, say, "MyType.py" and in it we have a
// class "MyType", we can generate imports -- usually when we
// have a type that contains arrays of itself -- of the type
// "from .MyType import MyType", which Python can't resolve. So
// if we are trying to import ourself, we skip.
if (import_entry.first != local_import) {
code += "from " + import_entry.first + " import " +
import_entry.second + "\n";
}
}
}
code += "np = import_numpy()\n\n";
}
}
// Save out the generated code for a Python Table type.
bool SaveType(const std::string &defname, const Namespace &ns,
const std::string &classcode, bool needs_imports) const {
const std::string &classcode, const ImportMap &imports,
const std::string &mod, bool needs_imports) const {
if (!classcode.length()) return true;
std::string code = "";
BeginFile(LastNamespacePart(ns), needs_imports, &code);
BeginFile(LastNamespacePart(ns), needs_imports, &code, mod, imports);
code += classcode;
const std::string directories =