mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-03 04:21:13 +00:00
[Python] Generate .pyi stub files when --python-typing is on. (#8312)
* [Python] Generate `.pyi` stub files when `--python-typing` is on. To support this change, the following modifications were made: - added a new option to disable `numpy` helpers generation; - added a new flag to control the target Python version: `--python-version` can be one of the following: - `0.x.x` – compatible with any Python version; - `2.x.x` – compatible with Python 2; - `3.x.x` – compatible with Python 3. - added codegen utilities for Python; - added a note that the generated .py file is empty. * [Python] Update Bazel build rules. * [Python] Update Bazel build rules. * [Python] Run buildifier on BUILD.bazel files. --------- Co-authored-by: Derek Bailey <derekbailey@google.com>
This commit is contained in:
@@ -19,13 +19,17 @@
|
||||
#include "idl_gen_python.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "codegen/python.h"
|
||||
#include "flatbuffers/code_generators.h"
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
#include "flatbuffers/idl.h"
|
||||
@@ -40,15 +44,6 @@ 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",
|
||||
"else", "except", "finally", "for", "from", "global",
|
||||
"if", "import", "in", "is", "lambda", "nonlocal",
|
||||
"not", "or", "pass", "raise", "return", "try",
|
||||
"while", "with", "yield" };
|
||||
}
|
||||
|
||||
static Namer::Config PythonDefaultConfig() {
|
||||
return { /*types=*/Case::kKeep,
|
||||
/*constants=*/Case::kScreamingSnake,
|
||||
@@ -72,21 +67,594 @@ static Namer::Config PythonDefaultConfig() {
|
||||
/*filename_extension=*/".py" };
|
||||
}
|
||||
|
||||
static Namer::Config kStubConfig = {
|
||||
/*types=*/Case::kKeep,
|
||||
/*constants=*/Case::kScreamingSnake,
|
||||
/*methods=*/Case::kUpperCamel,
|
||||
/*functions=*/Case::kUpperCamel,
|
||||
/*fields=*/Case::kLowerCamel,
|
||||
/*variables=*/Case::kLowerCamel,
|
||||
/*variants=*/Case::kKeep,
|
||||
/*enum_variant_seperator=*/".",
|
||||
/*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
|
||||
/*namespaces=*/Case::kKeep, // Packages in python.
|
||||
/*namespace_seperator=*/".",
|
||||
/*object_prefix=*/"",
|
||||
/*object_suffix=*/"T",
|
||||
/*keyword_prefix=*/"",
|
||||
/*keyword_suffix=*/"_",
|
||||
/*filenames=*/Case::kKeep,
|
||||
/*directories=*/Case::kKeep,
|
||||
/*output_path=*/"",
|
||||
/*filename_suffix=*/"",
|
||||
/*filename_extension=*/".pyi",
|
||||
};
|
||||
|
||||
// Hardcode spaces per indentation.
|
||||
static const CommentConfig def_comment = { nullptr, "#", nullptr };
|
||||
static const std::string Indent = " ";
|
||||
|
||||
} // namespace
|
||||
class PythonStubGenerator {
|
||||
public:
|
||||
PythonStubGenerator(const Parser &parser, const std::string &path,
|
||||
const Version &version)
|
||||
: parser_{parser},
|
||||
namer_{WithFlagOptions(kStubConfig, parser.opts, path),
|
||||
Keywords(version)},
|
||||
version_(version) {}
|
||||
|
||||
bool Generate() {
|
||||
if (parser_.opts.one_file) {
|
||||
Imports imports;
|
||||
std::stringstream stub;
|
||||
|
||||
DeclareUOffset(stub, &imports);
|
||||
for (const EnumDef *def : parser_.enums_.vec) {
|
||||
if (def->generated) continue;
|
||||
GenerateEnumStub(stub, def, &imports);
|
||||
}
|
||||
for (const StructDef *def : parser_.structs_.vec) {
|
||||
if (def->generated) continue;
|
||||
GenerateStructStub(stub, def, &imports);
|
||||
}
|
||||
|
||||
std::string filename =
|
||||
namer_.config_.output_path +
|
||||
StripPath(StripExtension(parser_.file_being_parsed_)) +
|
||||
namer_.config_.filename_suffix + namer_.config_.filename_extension;
|
||||
|
||||
return SaveFile(filename, imports, stub);
|
||||
}
|
||||
|
||||
for (const EnumDef *def : parser_.enums_.vec) {
|
||||
if (def->generated) continue;
|
||||
|
||||
Imports imports;
|
||||
std::stringstream stub;
|
||||
|
||||
DeclareUOffset(stub, &imports);
|
||||
GenerateEnumStub(stub, def, &imports);
|
||||
|
||||
std::string filename = namer_.Directories(*def->defined_namespace) +
|
||||
namer_.File(*def, SkipFile::Suffix);
|
||||
if (!SaveFile(filename, imports, stub)) return false;
|
||||
}
|
||||
|
||||
for (const StructDef *def : parser_.structs_.vec) {
|
||||
if (def->generated) continue;
|
||||
|
||||
Imports imports;
|
||||
std::stringstream stub;
|
||||
|
||||
DeclareUOffset(stub, &imports);
|
||||
GenerateStructStub(stub, def, &imports);
|
||||
|
||||
std::string filename = namer_.Directories(*def->defined_namespace) +
|
||||
namer_.File(*def, SkipFile::Suffix);
|
||||
if (!SaveFile(filename, imports, stub)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool SaveFile(const std::string &filename, const Imports &imports,
|
||||
const std::stringstream &content) {
|
||||
std::stringstream ss;
|
||||
GenerateImports(ss, imports);
|
||||
ss << '\n';
|
||||
ss << content.str() << '\n';
|
||||
|
||||
EnsureDirExists(StripFileName(filename));
|
||||
return flatbuffers::SaveFile(filename.c_str(), ss.str(), false);
|
||||
}
|
||||
|
||||
static void DeclareUOffset(std::stringstream &stub, Imports *imports) {
|
||||
imports->Import("flatbuffers");
|
||||
imports->Import("typing");
|
||||
stub << "uoffset: typing.TypeAlias = "
|
||||
"flatbuffers.number_types.UOffsetTFlags.py_type\n"
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
std::string ModuleForFile(const std::string &file) const {
|
||||
if (parser_.file_being_parsed_ == file) return ".";
|
||||
|
||||
std::string module = parser_.opts.include_prefix + StripExtension(file) +
|
||||
parser_.opts.filename_suffix;
|
||||
std::replace(module.begin(), module.end(), '/', '.');
|
||||
return module;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string ModuleFor(const T *def) const {
|
||||
if (parser_.opts.one_file) return ModuleForFile(def->file);
|
||||
return namer_.NamespacedType(*def);
|
||||
}
|
||||
|
||||
const StructDef *GetNestedStruct(const FieldDef *field) const {
|
||||
const Value *nested = field->attributes.Lookup("nested_flatbuffer");
|
||||
if (nested == nullptr) return nullptr;
|
||||
|
||||
StructDef *nested_def = parser_.LookupStruct(nested->constant);
|
||||
if (nested_def != nullptr) return nested_def;
|
||||
|
||||
return parser_.LookupStruct(namer_.NamespacedType(
|
||||
parser_.current_namespace_->components, nested->constant));
|
||||
}
|
||||
|
||||
static std::string ScalarType(BaseType type) {
|
||||
if (IsBool(type)) return "bool";
|
||||
if (IsInteger(type)) return "int";
|
||||
if (IsFloat(type)) return "float";
|
||||
FLATBUFFERS_ASSERT(false);
|
||||
return "None";
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
std::string UnionType(const EnumDef &enum_def, Imports *imports,
|
||||
F type) const {
|
||||
imports->Import("typing");
|
||||
|
||||
std::string result = "";
|
||||
for (const EnumVal *val : enum_def.Vals()) {
|
||||
if (!result.empty()) result += ", ";
|
||||
|
||||
switch (val->union_type.base_type) {
|
||||
case BASE_TYPE_STRUCT: {
|
||||
Import import = imports->Import(ModuleFor(val->union_type.struct_def),
|
||||
type(*val->union_type.struct_def));
|
||||
result += import.name;
|
||||
break;
|
||||
}
|
||||
case BASE_TYPE_STRING:
|
||||
result += "str";
|
||||
break;
|
||||
case BASE_TYPE_NONE:
|
||||
result += "None";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "typing.Union[" + result + "]";
|
||||
}
|
||||
|
||||
std::string UnionObjectType(const EnumDef &enum_def, Imports *imports) const {
|
||||
return UnionType(enum_def, imports, [this](const StructDef &struct_def) {
|
||||
return namer_.ObjectType(struct_def);
|
||||
});
|
||||
}
|
||||
|
||||
std::string UnionType(const EnumDef &enum_def, Imports *imports) const {
|
||||
return UnionType(enum_def, imports, [this](const StructDef &struct_def) {
|
||||
return namer_.Type(struct_def);
|
||||
});
|
||||
}
|
||||
|
||||
std::string EnumType(const EnumDef &enum_def, Imports *imports) const {
|
||||
imports->Import("typing");
|
||||
const Import &import =
|
||||
imports->Import(ModuleFor(&enum_def), namer_.Type(enum_def));
|
||||
|
||||
std::string result = "";
|
||||
for (const EnumVal *val : enum_def.Vals()) {
|
||||
if (!result.empty()) result += ", ";
|
||||
result += import.name + "." + namer_.Variant(*val);
|
||||
}
|
||||
return "typing.Literal[" + result + "]";
|
||||
}
|
||||
|
||||
std::string TypeOf(const Type &type, Imports *imports) const {
|
||||
if (type.enum_def != nullptr) return EnumType(*type.enum_def, imports);
|
||||
if (IsScalar(type.base_type)) return ScalarType(type.base_type);
|
||||
|
||||
switch (type.base_type) {
|
||||
case BASE_TYPE_STRUCT: {
|
||||
const Import &import = imports->Import(ModuleFor(type.struct_def),
|
||||
namer_.Type(*type.struct_def));
|
||||
return import.name;
|
||||
}
|
||||
case BASE_TYPE_STRING:
|
||||
return "str";
|
||||
case BASE_TYPE_ARRAY:
|
||||
case BASE_TYPE_VECTOR: {
|
||||
imports->Import("typing");
|
||||
return "typing.List[" + TypeOf(type.VectorType(), imports) + "]";
|
||||
}
|
||||
case BASE_TYPE_UNION:
|
||||
return UnionType(*type.enum_def, imports);
|
||||
default:
|
||||
FLATBUFFERS_ASSERT(0);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string GenerateObjectFieldStub(const FieldDef *field,
|
||||
Imports *imports) const {
|
||||
std::string field_name = namer_.Field(*field);
|
||||
|
||||
const Type &field_type = field->value.type;
|
||||
if (IsScalar(field_type.base_type)) {
|
||||
std::string result = field_name + ": " + TypeOf(field_type, imports);
|
||||
if (field->IsOptional()) result += " | None";
|
||||
return result;
|
||||
}
|
||||
|
||||
switch (field_type.base_type) {
|
||||
case BASE_TYPE_STRUCT: {
|
||||
Import import =
|
||||
imports->Import(ModuleFor(field_type.struct_def),
|
||||
namer_.ObjectType(*field_type.struct_def));
|
||||
return field_name + ": " + import.name + " | None";
|
||||
}
|
||||
case BASE_TYPE_STRING:
|
||||
return field_name + ": str | None";
|
||||
case BASE_TYPE_ARRAY:
|
||||
case BASE_TYPE_VECTOR: {
|
||||
imports->Import("typing");
|
||||
if (field_type.element == BASE_TYPE_STRUCT) {
|
||||
Import import =
|
||||
imports->Import(ModuleFor(field_type.struct_def),
|
||||
namer_.ObjectType(*field_type.struct_def));
|
||||
return field_name + ": typing.List[" + import.name + "]";
|
||||
}
|
||||
if (field_type.element == BASE_TYPE_STRING) {
|
||||
return field_name + ": typing.List[str]";
|
||||
}
|
||||
return field_name + ": typing.List[" +
|
||||
TypeOf(field_type.VectorType(), imports) + "]";
|
||||
}
|
||||
case BASE_TYPE_UNION:
|
||||
return field_name + ": " +
|
||||
UnionObjectType(*field->value.type.enum_def, imports);
|
||||
default:
|
||||
return field_name;
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateObjectStub(std::stringstream &stub, const StructDef *struct_def,
|
||||
Imports *imports) const {
|
||||
std::string name = namer_.ObjectType(*struct_def);
|
||||
|
||||
stub << "class " << name;
|
||||
if (version_.major != 3) stub << "(object)";
|
||||
stub << ":\n";
|
||||
for (const FieldDef *field : struct_def->fields.vec) {
|
||||
if (field->deprecated) continue;
|
||||
stub << " " << GenerateObjectFieldStub(field, imports) << "\n";
|
||||
}
|
||||
|
||||
stub << " @classmethod\n";
|
||||
stub << " def InitFromBuf(cls, buf: bytes, pos: int) -> " << name
|
||||
<< ": ...\n";
|
||||
|
||||
stub << " @classmethod\n";
|
||||
stub << " def InitFromPackedBuf(cls, buf: bytes, pos: int = 0) -> " << name
|
||||
<< ": ...\n";
|
||||
|
||||
const Import &import =
|
||||
imports->Import(ModuleFor(struct_def), namer_.Type(*struct_def));
|
||||
|
||||
stub << " @classmethod\n";
|
||||
stub << " def InitFromObj(cls, " << namer_.Variable(*struct_def)
|
||||
<< ": " + import.name + ") -> " << name << ": ...\n";
|
||||
|
||||
stub << " def _UnPack(self, " << namer_.Variable(*struct_def) << ": "
|
||||
<< import.name << ") -> None: ...\n";
|
||||
|
||||
stub << " def Pack(self, builder: flatbuffers.Builder) -> None: ...\n";
|
||||
|
||||
if (parser_.opts.gen_compare) {
|
||||
stub << " def __eq__(self, other: " << name + ") -> bool: ...\n";
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateStructStub(std::stringstream &stub, const StructDef *struct_def,
|
||||
Imports *imports) const {
|
||||
std::string type = namer_.Type(*struct_def);
|
||||
|
||||
stub << "class " << type;
|
||||
if (version_.major != 3) stub << "(object)";
|
||||
stub << ":\n";
|
||||
if (struct_def->fixed) {
|
||||
stub << " @classmethod\n";
|
||||
stub << " def SizeOf(cls) -> int: ...\n\n";
|
||||
} else {
|
||||
stub << " @classmethod\n";
|
||||
stub << " def GetRootAs(cls, buf: bytes, offset: int) -> " << type
|
||||
<< ": ...\n";
|
||||
|
||||
if (!parser_.opts.python_no_type_prefix_suffix) {
|
||||
stub << " @classmethod\n";
|
||||
stub << " def GetRootAs" << type
|
||||
<< "(cls, buf: bytes, offset: int) -> " << type << ": ...\n";
|
||||
}
|
||||
if (parser_.file_identifier_.length()) {
|
||||
stub << " @classmethod\n";
|
||||
stub << " def " << type
|
||||
<< "BufferHasIdentifier(cls, buf: bytes, offset: int, "
|
||||
"size_prefixed: bool) -> bool: ...\n";
|
||||
}
|
||||
}
|
||||
|
||||
stub << " def Init(self, buf: bytes, pos: int) -> None: ...\n";
|
||||
|
||||
for (const FieldDef *field : struct_def->fields.vec) {
|
||||
if (field->deprecated) continue;
|
||||
|
||||
std::string name = namer_.Method(*field);
|
||||
|
||||
const Type &field_type = field->value.type;
|
||||
if (IsScalar(field_type.base_type)) {
|
||||
stub << " def " << name << "(self) -> " << TypeOf(field_type, imports);
|
||||
if (field->IsOptional()) stub << " | None";
|
||||
stub << ": ...\n";
|
||||
} else {
|
||||
switch (field_type.base_type) {
|
||||
case BASE_TYPE_STRUCT: {
|
||||
const Import &import =
|
||||
imports->Import(ModuleFor(field_type.struct_def),
|
||||
namer_.Type(*field_type.struct_def));
|
||||
if (struct_def->fixed) {
|
||||
stub << " def " << name << "(self, obj: " << import.name
|
||||
<< ") -> " << import.name << ": ...\n";
|
||||
} else {
|
||||
stub << " def " << name + "(self) -> " << import.name
|
||||
<< " | None: ...\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BASE_TYPE_STRING:
|
||||
stub << " def " << name << "(self) -> str | None: ...\n";
|
||||
break;
|
||||
case BASE_TYPE_ARRAY:
|
||||
case BASE_TYPE_VECTOR: {
|
||||
switch (field_type.element) {
|
||||
case BASE_TYPE_STRUCT: {
|
||||
const Import &import =
|
||||
imports->Import(ModuleFor(field_type.struct_def),
|
||||
namer_.Type(*field_type.struct_def));
|
||||
stub << " def " << name << "(self, i: int) -> " << import.name
|
||||
<< " | None: ...\n";
|
||||
break;
|
||||
}
|
||||
case BASE_TYPE_STRING:
|
||||
stub << " def " << name << "(self, i: int) -> str: ...\n";
|
||||
break;
|
||||
default: // scalars
|
||||
stub << " def " << name << "(self, i: int) -> "
|
||||
<< TypeOf(field_type, imports) << ": ...\n";
|
||||
|
||||
if (parser_.opts.python_gen_numpy) {
|
||||
stub << " def " << name
|
||||
<< "AsNumpy(self) -> np.ndarray: ...\n";
|
||||
}
|
||||
|
||||
const StructDef *nested_def = GetNestedStruct(field);
|
||||
if (nested_def != nullptr) {
|
||||
const Import &import = imports->Import(
|
||||
ModuleFor(nested_def), namer_.Type(*nested_def));
|
||||
|
||||
stub << " def " << name + "NestedRoot(self) -> "
|
||||
<< import.name << " | None: ...\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
stub << " def " << name << "Length(self) -> int: ...\n";
|
||||
stub << " def " << name << "IsNone(self) -> bool: ...\n";
|
||||
break;
|
||||
}
|
||||
case BASE_TYPE_UNION: {
|
||||
imports->Import("flatbuffers", "table");
|
||||
stub << " def " << name << "(self) -> table.Table | None: ...\n";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parser_.opts.generate_object_based_api) {
|
||||
GenerateObjectStub(stub, struct_def, imports);
|
||||
}
|
||||
|
||||
if (struct_def->fixed) {
|
||||
GenerateStructBuilderStub(stub, struct_def, imports);
|
||||
} else {
|
||||
GenerateTableBuilderStub(stub, struct_def, imports);
|
||||
}
|
||||
}
|
||||
|
||||
void StructBuilderArgs(const StructDef &struct_def, const std::string prefix,
|
||||
Imports *imports,
|
||||
std::vector<std::string> *args) const {
|
||||
for (const FieldDef *field : struct_def.fields.vec) {
|
||||
const Type type = IsArray(field->value.type)
|
||||
? field->value.type.VectorType()
|
||||
: field->value.type;
|
||||
if (type.base_type == BASE_TYPE_STRUCT) {
|
||||
StructBuilderArgs(*field->value.type.struct_def,
|
||||
prefix + namer_.Field(*field) + "_", imports, args);
|
||||
} else {
|
||||
args->push_back(prefix + namer_.Field(*field) + ": " +
|
||||
TypeOf(type, imports));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateStructBuilderStub(std::stringstream &stub,
|
||||
const StructDef *struct_def,
|
||||
Imports *imports) const {
|
||||
imports->Import("flatbuffers");
|
||||
|
||||
std::vector<std::string> args;
|
||||
StructBuilderArgs(*struct_def, "", imports, &args);
|
||||
|
||||
stub << '\n';
|
||||
stub << "def Create" + namer_.Type(*struct_def)
|
||||
<< "(builder: flatbuffers.Builder";
|
||||
for (const std::string &arg : args) {
|
||||
stub << ", " << arg;
|
||||
}
|
||||
stub << ") -> uoffset: ...\n";
|
||||
}
|
||||
|
||||
void GenerateTableBuilderStub(std::stringstream &stub,
|
||||
const StructDef *struct_def,
|
||||
Imports *imports) const {
|
||||
std::string type = namer_.Type(*struct_def);
|
||||
|
||||
/**************************** def TableStart ****************************/
|
||||
stub << "def ";
|
||||
if (!parser_.opts.python_no_type_prefix_suffix) stub << type;
|
||||
stub << "Start(builder: flatbuffers.Builder) -> None: ...\n";
|
||||
if (!parser_.opts.one_file && !parser_.opts.python_no_type_prefix_suffix) {
|
||||
stub << "def Start(builder: flatbuffers.Builder) -> None: ...\n";
|
||||
}
|
||||
|
||||
/************************** def TableAddField ***************************/
|
||||
for (const FieldDef *field : struct_def->fields.vec) {
|
||||
if (field->deprecated) continue;
|
||||
|
||||
stub << "def ";
|
||||
if (!parser_.opts.python_no_type_prefix_suffix) stub << type;
|
||||
stub << "Add" << namer_.Method(*field)
|
||||
<< "(builder: flatbuffers.Builder, "
|
||||
<< namer_.Variable(*field) + ": ";
|
||||
if (IsScalar(field->value.type.base_type)) {
|
||||
stub << TypeOf(field->value.type, imports);
|
||||
} else if (IsArray(field->value.type)) {
|
||||
stub << TypeOf(field->value.type.VectorType(), imports);
|
||||
} else {
|
||||
stub << "uoffset";
|
||||
}
|
||||
stub << ") -> None: ...\n";
|
||||
|
||||
if (IsVector(field->value.type)) {
|
||||
stub << "def ";
|
||||
if (!parser_.opts.python_no_type_prefix_suffix) stub << type;
|
||||
stub << "Start" << namer_.Method(*field)
|
||||
<< "Vector(builder: flatbuffers.Builder, num_elems: int) -> "
|
||||
"uoffset: ...\n";
|
||||
|
||||
if (!parser_.opts.one_file &&
|
||||
!parser_.opts.python_no_type_prefix_suffix) {
|
||||
stub << "def Start" << namer_.Method(*field)
|
||||
<< "Vector(builder: flatbuffers.Builder, num_elems: int) -> "
|
||||
"uoffset: ...\n";
|
||||
}
|
||||
|
||||
if (GetNestedStruct(field) != nullptr) {
|
||||
stub << "def " << type << "Make" << namer_.Method(*field)
|
||||
<< "VectorFromBytes(builder: flatbuffers.Builder, buf: "
|
||||
"bytes) -> uoffset: ...\n";
|
||||
if (!parser_.opts.one_file) {
|
||||
stub << "def Make" << namer_.Method(*field)
|
||||
<< "VectorFromBytes(builder: flatbuffers.Builder, buf: "
|
||||
"bytes) -> uoffset: ...\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************** def TableEnd *****************************/
|
||||
stub << "def ";
|
||||
if (!parser_.opts.python_no_type_prefix_suffix) stub << type;
|
||||
stub << "End(builder: flatbuffers.Builder) -> uoffset: ...\n";
|
||||
if (!parser_.opts.one_file && !parser_.opts.python_no_type_prefix_suffix) {
|
||||
stub << "def End(builder: flatbuffers.Builder) -> uoffset: ...\n";
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateEnumStub(std::stringstream &stub, const EnumDef *enum_def,
|
||||
Imports *imports) const {
|
||||
stub << "class " << namer_.Type(*enum_def);
|
||||
if (version_.major != 3) stub << "(object)";
|
||||
stub << ":\n";
|
||||
for (const EnumVal *val : enum_def->Vals()) {
|
||||
stub << " " << namer_.Variant(*val) << ": "
|
||||
<< ScalarType(enum_def->underlying_type.base_type) << "\n";
|
||||
}
|
||||
if (parser_.opts.generate_object_based_api & enum_def->is_union) {
|
||||
imports->Import("flatbuffers", "table");
|
||||
stub << "def " << namer_.Function(*enum_def)
|
||||
<< "Creator(union_type: " << EnumType(*enum_def, imports)
|
||||
<< ", table: table.Table) -> " << UnionType(*enum_def, imports)
|
||||
<< ": ...\n";
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateImports(std::stringstream &ss, const Imports &imports) {
|
||||
ss << "from __future__ import annotations\n";
|
||||
ss << '\n';
|
||||
ss << "import flatbuffers\n";
|
||||
if (parser_.opts.python_gen_numpy) {
|
||||
ss << "import numpy as np\n";
|
||||
}
|
||||
ss << '\n';
|
||||
|
||||
std::set<std::string> modules;
|
||||
std::map<std::string, std::set<std::string>> names_by_module;
|
||||
for (const Import &import : imports.imports) {
|
||||
if (import.IsLocal()) continue; // skip all local imports
|
||||
if (import.name == "") {
|
||||
modules.insert(import.module);
|
||||
} else {
|
||||
names_by_module[import.module].insert(import.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string &module : modules) {
|
||||
ss << "import " << module << '\n';
|
||||
}
|
||||
for (const auto &import : names_by_module) {
|
||||
ss << "from " << import.first << " import ";
|
||||
size_t i = 0;
|
||||
for (const std::string &name : import.second) {
|
||||
if (i > 0) ss << ", ";
|
||||
ss << name;
|
||||
++i;
|
||||
}
|
||||
ss << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
const Parser &parser_;
|
||||
const IdlNamer namer_;
|
||||
const Version version_;
|
||||
};} // namespace
|
||||
|
||||
class PythonGenerator : public BaseGenerator {
|
||||
public:
|
||||
PythonGenerator(const Parser &parser, const std::string &path,
|
||||
const std::string &file_name)
|
||||
const std::string &file_name, const Version &version)
|
||||
: BaseGenerator(parser, path, file_name, "" /* not used */,
|
||||
"" /* not used */, "py"),
|
||||
float_const_gen_("float('nan')", "float('inf')", "float('-inf')"),
|
||||
namer_(WithFlagOptions(PythonDefaultConfig(), parser.opts, path),
|
||||
PythonKeywords()) {}
|
||||
Keywords(version)) {}
|
||||
|
||||
// Most field accessors need to retrieve and test the field offset first,
|
||||
// this is the prefix code for that.
|
||||
@@ -940,7 +1508,9 @@ class PythonGenerator : public BaseGenerator {
|
||||
GetMemberOfVectorOfStruct(struct_def, field, code_ptr, imports);
|
||||
} else {
|
||||
GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr);
|
||||
GetVectorOfNonStructAsNumpy(struct_def, field, code_ptr);
|
||||
if (parser_.opts.python_gen_numpy) {
|
||||
GetVectorOfNonStructAsNumpy(struct_def, field, code_ptr);
|
||||
}
|
||||
GetVectorAsNestedFlatbuffer(struct_def, field, code_ptr, imports);
|
||||
}
|
||||
break;
|
||||
@@ -951,7 +1521,9 @@ class PythonGenerator : public BaseGenerator {
|
||||
GetArrayOfStruct(struct_def, field, code_ptr, imports);
|
||||
} else {
|
||||
GetArrayOfNonStruct(struct_def, field, code_ptr);
|
||||
GetVectorOfNonStructAsNumpy(struct_def, field, code_ptr);
|
||||
if (parser_.opts.python_gen_numpy) {
|
||||
GetVectorOfNonStructAsNumpy(struct_def, field, code_ptr);
|
||||
}
|
||||
GetVectorAsNestedFlatbuffer(struct_def, field, code_ptr, imports);
|
||||
}
|
||||
break;
|
||||
@@ -1480,13 +2052,17 @@ class PythonGenerator : public BaseGenerator {
|
||||
return;
|
||||
}
|
||||
|
||||
code += GenIndents(3) + "if np is None:";
|
||||
GenUnpackforScalarVectorHelper(struct_def, field, code_ptr, 4);
|
||||
if (parser_.opts.python_gen_numpy) {
|
||||
code += GenIndents(3) + "if np is None:";
|
||||
GenUnpackforScalarVectorHelper(struct_def, field, code_ptr, 4);
|
||||
|
||||
// If numpy exists, use the AsNumpy method to optimize the unpack speed.
|
||||
code += GenIndents(3) + "else:";
|
||||
code += GenIndents(4) + "self." + field_field + " = " + struct_var + "." +
|
||||
field_method + "AsNumpy()";
|
||||
// If numpy exists, use the AsNumpy method to optimize the unpack speed.
|
||||
code += GenIndents(3) + "else:";
|
||||
code += GenIndents(4) + "self." + field_field + " = " + struct_var + "." +
|
||||
field_method + "AsNumpy()";
|
||||
} else {
|
||||
GenUnpackforScalarVectorHelper(struct_def, field, code_ptr, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void GenUnPackForScalar(const StructDef &struct_def, const FieldDef &field,
|
||||
@@ -1685,14 +2261,20 @@ class PythonGenerator : public BaseGenerator {
|
||||
return;
|
||||
}
|
||||
|
||||
code_prefix += GenIndents(3) + "if np is not None and type(self." +
|
||||
field_field + ") is np.ndarray:";
|
||||
code_prefix += GenIndents(4) + field_field +
|
||||
" = builder.CreateNumpyVector(self." + field_field + ")";
|
||||
code_prefix += GenIndents(3) + "else:";
|
||||
GenPackForScalarVectorFieldHelper(struct_def, field, code_prefix_ptr, 4);
|
||||
code_prefix += "(self." + field_field + "[i])";
|
||||
code_prefix += GenIndents(4) + field_field + " = builder.EndVector()";
|
||||
if (parser_.opts.python_gen_numpy) {
|
||||
code_prefix += GenIndents(3) + "if np is not None and type(self." +
|
||||
field_field + ") is np.ndarray:";
|
||||
code_prefix += GenIndents(4) + field_field +
|
||||
" = builder.CreateNumpyVector(self." + field_field + ")";
|
||||
code_prefix += GenIndents(3) + "else:";
|
||||
GenPackForScalarVectorFieldHelper(struct_def, field, code_prefix_ptr, 4);
|
||||
code_prefix += "(self." + field_field + "[i])";
|
||||
code_prefix += GenIndents(4) + field_field + " = builder.EndVector()";
|
||||
} else {
|
||||
GenPackForScalarVectorFieldHelper(struct_def, field, code_prefix_ptr, 3);
|
||||
code_prefix += "(self." + field_field + "[i])";
|
||||
code_prefix += GenIndents(4) + field_field + " = builder.EndVector()";
|
||||
}
|
||||
}
|
||||
|
||||
void GenPackForStructField(const StructDef &struct_def, const FieldDef &field,
|
||||
@@ -2113,7 +2695,9 @@ class PythonGenerator : public BaseGenerator {
|
||||
const std::string local_import = "." + mod;
|
||||
|
||||
code += "import flatbuffers\n";
|
||||
code += "from flatbuffers.compat import import_numpy\n";
|
||||
if (parser_.opts.python_gen_numpy) {
|
||||
code += "from flatbuffers.compat import import_numpy\n";
|
||||
}
|
||||
if (parser_.opts.python_typing) {
|
||||
code += "from typing import Any\n";
|
||||
|
||||
@@ -2129,7 +2713,9 @@ class PythonGenerator : public BaseGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
code += "np = import_numpy()\n\n";
|
||||
if (parser_.opts.python_gen_numpy) {
|
||||
code += "np = import_numpy()\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2140,6 +2726,7 @@ class PythonGenerator : public BaseGenerator {
|
||||
std::string code = "";
|
||||
if (classcode.empty()) {
|
||||
BeginFile(LastNamespacePart(ns), false, &code, "", {});
|
||||
code += "# NOTE " + defname + " does not declare any structs or enums\n";
|
||||
} else {
|
||||
BeginFile(LastNamespacePart(ns), needs_imports, &code, mod, imports);
|
||||
code += classcode;
|
||||
@@ -2169,8 +2756,17 @@ class PythonGenerator : public BaseGenerator {
|
||||
|
||||
static bool GeneratePython(const Parser &parser, const std::string &path,
|
||||
const std::string &file_name) {
|
||||
python::PythonGenerator generator(parser, path, file_name);
|
||||
return generator.generate();
|
||||
python::Version version{parser.opts.python_version};
|
||||
if (!version.IsValid()) return false;
|
||||
|
||||
python::PythonGenerator generator(parser, path, file_name, version);
|
||||
if (!generator.generate()) return false;
|
||||
|
||||
if (parser.opts.python_typing) {
|
||||
python::PythonStubGenerator stub_generator(parser, path, version);
|
||||
if (!stub_generator.Generate()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Reference in New Issue
Block a user