mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-02 12:05:50 +00:00
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:
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user