[Python] Fix various codegen problems (#8292)

* [Python] Fix various codegen problems.

This includes:

-  escaping keywords happens **after** converting the case:
   - currently, `table ClassT` generate `class = Class()` which is invalid Python;
-  imports in `one_file` mode use the filename rather than the type name when resolving module names;
-  use `filename_suffix` instead of the hardcoded `_generated` one;
-  generate empty files if no structs or enums are available. This makes the set of output files more predictable for Bazel.

* [Python] Fix various codegen problems.

This includes:

-  escaping keywords happens **after** converting the case:
   - currently, `table ClassT` generate `class = Class()` which is invalid Python;
-  imports in `one_file` mode use the filename rather than the type name when resolving module names;
-  use `filename_suffix` instead of the hardcoded `_generated` one;
-  generate empty files if no structs or enums are available. This makes the set of output files more predictable for Bazel.
This commit is contained in:
Anton Bobukh
2024-05-01 14:39:47 -07:00
committed by GitHub
parent 7106d86685
commit c696275eaf
9 changed files with 53 additions and 16 deletions

View File

@@ -18,7 +18,9 @@
#include "idl_gen_python.h"
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <set>
#include <string>
#include <unordered_set>
@@ -56,7 +58,7 @@ static Namer::Config PythonDefaultConfig() {
/*variable=*/Case::kLowerCamel,
/*variants=*/Case::kKeep,
/*enum_variant_seperator=*/".",
/*escape_keywords=*/Namer::Config::Escape::BeforeConvertingCase,
/*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
/*namespaces=*/Case::kKeep, // Packages in python.
/*namespace_seperator=*/".",
/*object_prefix=*/"",
@@ -424,16 +426,29 @@ class PythonGenerator : public BaseGenerator {
code += Indent + Indent + "return None\n\n";
}
template <typename T>
std::string ModuleFor(const T *def) const {
if (!parser_.opts.one_file) {
return namer_.NamespacedType(*def);
}
std::string filename =
StripExtension(def->file) + parser_.opts.filename_suffix;
if (parser_.file_being_parsed_ == def->file) {
return "." + StripPath(filename); // make it a "local" import
}
std::string module = parser_.opts.include_prefix + filename;
std::replace(module.begin(), module.end(), '/', '.');
return module;
}
// Generate the package reference when importing a struct or enum from its
// module.
std::string GenPackageReference(const Type &type) const {
if (type.struct_def) {
return namer_.NamespacedType(*type.struct_def);
} else if (type.enum_def) {
return namer_.NamespacedType(*type.enum_def);
} else {
return "." + GenTypeGet(type);
}
if (type.struct_def) return ModuleFor(type.struct_def);
if (type.enum_def) return ModuleFor(type.enum_def);
return "." + GenTypeGet(type);
}
// Get the value of a vector's struct member.
@@ -2021,7 +2036,7 @@ class PythonGenerator : public BaseGenerator {
if (!generateStructs(&one_file_code, one_file_imports)) return false;
if (parser_.opts.one_file) {
const std::string mod = file_name_ + "_generated";
const std::string mod = file_name_ + parser_.opts.filename_suffix;
// Legacy file format uses keep casing.
return SaveType(mod + ".py", *parser_.current_namespace_, one_file_code,
@@ -2122,11 +2137,13 @@ class PythonGenerator : public BaseGenerator {
bool SaveType(const std::string &defname, const Namespace &ns,
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, mod, imports);
code += classcode;
if (classcode.empty()) {
BeginFile(LastNamespacePart(ns), false, &code, "", {});
} else {
BeginFile(LastNamespacePart(ns), needs_imports, &code, mod, imports);
code += classcode;
}
const std::string directories =
parser_.opts.one_file ? path_ : namer_.Directories(ns.components);

4
tests/FromInclude.py Normal file
View File

@@ -0,0 +1,4 @@
# automatically generated by the FlatBuffers compiler, do not modify
# namespace: OtherNameSpace

View File

@@ -0,0 +1,4 @@
# automatically generated by the FlatBuffers compiler, do not modify
# namespace: OtherNameSpace

View File

@@ -0,0 +1,4 @@
# automatically generated by the FlatBuffers compiler, do not modify
# namespace: OtherNameSpace

View File

@@ -0,0 +1,4 @@
# automatically generated by the FlatBuffers compiler, do not modify
# namespace: OtherNameSpace

View File

View File

@@ -24,9 +24,9 @@ runtime_library_dir=${test_dir}/../python
# Emit Python code for the example schema in the test dir:
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test monster_test.fbs --gen-object-api
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test monster_test.fbs --gen-object-api --gen-onefile
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test monster_extra.fbs --gen-object-api
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test arrays_test.fbs --gen-object-api
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test nested_union_test.fbs --gen-object-api
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test monster_extra.fbs --gen-object-api --python-typing --gen-compare
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test arrays_test.fbs --gen-object-api --python-typing
${test_dir}/../flatc -p -o ${gen_code_path} -I include_test nested_union_test.fbs --gen-object-api --python-typing
# Syntax: run_tests <interpreter> <benchmark vtable dedupes>
# <benchmark read count> <benchmark build count>

4
tests/TableA.py Normal file
View File

@@ -0,0 +1,4 @@
# automatically generated by the FlatBuffers compiler, do not modify
# namespace:

0
tests/__init__.py Normal file
View File