From bdbc00dbeac2a160a025dac533fe03c27c07c2b3 Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Mon, 17 Nov 2025 22:20:50 -0500 Subject: [PATCH 01/11] have python generate enum types as IntEnum --- include/flatbuffers/idl.h | 2 + src/flatc.cpp | 4 ++ src/idl_gen_python.cpp | 93 +++++++++++++++++++++++++++++---------- 3 files changed, 75 insertions(+), 24 deletions(-) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 1c551e999db..4ef79db3b0d 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -712,6 +712,7 @@ struct IDLOptions { /********************************** Python **********************************/ bool python_no_type_prefix_suffix; bool python_typing; + bool python_enum; bool python_decode_obj_api_strings = false; // The target Python version. Can be one of the following: @@ -855,6 +856,7 @@ struct IDLOptions { keep_proto_id(false), python_no_type_prefix_suffix(false), python_typing(false), + python_enum(false), python_gen_numpy(true), ts_omit_entrypoint(false), proto_id_gap_action(ProtoIdGapAction::WARNING), diff --git a/src/flatc.cpp b/src/flatc.cpp index 612f797cc18..a7a012b506c 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -262,6 +262,8 @@ const static FlatCOption flatc_options[] = { {"", "python-no-type-prefix-suffix", "", "Skip emission of Python functions that are prefixed with typenames"}, {"", "python-typing", "", "Generate Python type annotations"}, + {"", "python-enum", "", + "Generate enum types as IntEnum (requires python-version >= 3"}, {"", "python-version", "", "Generate code for the given Python version."}, {"", "python-decode-obj-api-strings", "", "Decode bytes to strings for the Python Object API"}, @@ -694,6 +696,8 @@ FlatCOptions FlatCompiler::ParseFromCommandLineArguments(int argc, opts.python_no_type_prefix_suffix = true; } else if (arg == "--python-typing") { opts.python_typing = true; + } else if (arg == "--python-enum") { + opts.python_enum = true; } else if (arg.rfind("--python-version=", 0) == 0) { opts.python_version = arg.substr(std::string("--python-version=").size()); diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 4c48b21b17d..2f93e904664 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -721,7 +721,17 @@ class PythonGenerator : public BaseGenerator { // Begin enum code with a class declaration. void BeginEnum(const EnumDef& enum_def, std::string* code_ptr) const { auto& code = *code_ptr; - code += "class " + namer_.Type(enum_def) + "(object):\n"; + + code += "class " + namer_.Type(enum_def); + + python::Version version{parser_.opts.python_version}; + if (version.major >= 3) { + code += "(IntEnum)"; + } else { + code += "(object)"; + } + + code += ":\n"; } // Starts a new line and then indents. @@ -1685,6 +1695,12 @@ class PythonGenerator : public BaseGenerator { auto& field = **it; if (field.deprecated) continue; + // include import for enum type if used in this struct + if (IsEnum(field.value.type)) { + imports.insert(ImportMapEntry{GenPackageReference(field.value.type), + namer_.Type(*field.value.type.enum_def)}); + } + GenStructAccessor(struct_def, field, code_ptr, imports); } @@ -1739,6 +1755,11 @@ class PythonGenerator : public BaseGenerator { } else if (IsFloat(base_type)) { return float_const_gen_.GenFloatConstant(field); } else if (IsInteger(base_type)) { + // wrap the default value in the enum constructor to aid type hinting + if (parser_.opts.python_enum && IsEnum(field.value.type)) { + auto enum_type = namer_.Type(*field.value.type.enum_def); + return enum_type + "(" + field.value.constant + ")"; + } return field.value.constant; } else { // For string, struct, and table. @@ -1865,7 +1886,7 @@ class PythonGenerator : public BaseGenerator { break; } default: - // Scalar or sting fields. + // Scalar or string fields. field_type = GetBasePythonTypeForScalarAndString(base_type); if (field.IsScalarOptional()) { import_typing_list.insert("Optional"); @@ -2647,6 +2668,11 @@ class PythonGenerator : public BaseGenerator { std::string GenFieldTy(const FieldDef& field) const { if (IsScalar(field.value.type.base_type) || IsArray(field.value.type)) { + if (parser_.opts.python_enum) { + if (IsEnum(field.value.type)) { + return namer_.Type(*field.value.type.enum_def); + } + } const std::string ty = GenTypeBasic(field.value.type); if (ty.find("int") != std::string::npos) { return "int"; @@ -2761,6 +2787,11 @@ class PythonGenerator : public BaseGenerator { bool generate() { std::string one_file_code; ImportMap one_file_imports; + + if (parser_.opts.python_enum) { + one_file_imports.insert({"enum", "IntEnum"}); + } + if (!generateEnums(&one_file_code)) return false; if (!generateStructs(&one_file_code, one_file_imports)) return false; @@ -2790,6 +2821,11 @@ class PythonGenerator : public BaseGenerator { *one_file_code += enumcode + "\n\n"; } else { ImportMap imports; + + if (parser_.opts.python_enum) { + imports.insert({"enum", "IntEnum"}); + } + const std::string mod = namer_.File(enum_def, SkipFile::SuffixAndExtension); @@ -2835,49 +2871,52 @@ 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& mod, - const ImportMap& imports) const { + void BeginFile(const std::string& name_space_name, + const bool needs_default_imports, 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; - + if (needs_default_imports) { code += "import flatbuffers\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"; - - 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"; - } - } } - if (parser_.opts.python_gen_numpy) { - code += "np = import_numpy()\n\n"; + } + for (auto import_entry : imports) { + const std::string local_import = "." + mod; + + // 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 += "\n"; + + if (needs_default_imports && parser_.opts.python_gen_numpy) { + 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, const ImportMap& imports, - const std::string& mod, bool needs_imports) const { + const std::string& mod, bool needs_default_imports) const { if (classcode.empty()) return true; std::string code = ""; - BeginFile(LastNamespacePart(ns), needs_imports, &code, mod, imports); + BeginFile(LastNamespacePart(ns), needs_default_imports, &code, mod, + imports); code += classcode; const std::string directories = @@ -2907,6 +2946,12 @@ static bool GeneratePython(const Parser& parser, const std::string& path, python::Version version{parser.opts.python_version}; if (!version.IsValid()) return false; + if (parser.opts.python_enum && version.major < 3) { + // TODO: there doesn't seem to be a way to send an error message up + // we can't call parser.Error here because this is a const Parser& + return false; + } + python::PythonGenerator generator(parser, path, file_name, version); if (!generator.generate()) return false; From 9ef9ca6d91f64b67c225940abd68d7def6f4418f Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Wed, 19 Nov 2025 21:20:55 -0500 Subject: [PATCH 02/11] update doc --- docs/source/flatc.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/flatc.md b/docs/source/flatc.md index be6d8f169a6..cfaf661e194 100644 --- a/docs/source/flatc.md +++ b/docs/source/flatc.md @@ -259,6 +259,8 @@ list of `FILES...`. - `--python-typing` : Generate Python type annotations +- `--python-enum` : Generated enumerations inherit from `IntEnum` instead of `object` + - `--python-decode-obj-api-strings` : Decode bytes automaticaly with utf-8 Additional gRPC options: From e31c02ec10182e8b087e8e503d8a541648f06313 Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Wed, 19 Nov 2025 21:21:35 -0500 Subject: [PATCH 03/11] get IntFlag working, needs cleanup --- src/idl_gen_python.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 2f93e904664..41f82b59b92 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -613,8 +613,14 @@ class PythonStubGenerator { imports->Import("typing", "cast"); if (version_.major == 3) { + // TODO: only include what necessary imports->Import("enum", "IntEnum"); - stub << "(IntEnum)"; + imports->Import("enum", "IntFlag"); + if (enum_def->attributes.Lookup("big_flags")) { + stub << "(IntFlag)"; + } else { + stub << "(IntEnum)"; + } } else { stub << "(object)"; } @@ -726,7 +732,11 @@ class PythonGenerator : public BaseGenerator { python::Version version{parser_.opts.python_version}; if (version.major >= 3) { - code += "(IntEnum)"; + if (enum_def.attributes.Lookup("bit_flags")) { + code += "(IntFlag)"; + } else { + code += "(IntEnum)"; + } } else { code += "(object)"; } @@ -2789,7 +2799,10 @@ class PythonGenerator : public BaseGenerator { ImportMap one_file_imports; if (parser_.opts.python_enum) { + // TODO: only include this if python-typing and if the appropriate enum + // include is necessary one_file_imports.insert({"enum", "IntEnum"}); + one_file_imports.insert({"enum", "IntFlag"}); } if (!generateEnums(&one_file_code)) return false; @@ -2823,7 +2836,9 @@ class PythonGenerator : public BaseGenerator { ImportMap imports; if (parser_.opts.python_enum) { + // TODO: only include the one necessary imports.insert({"enum", "IntEnum"}); + imports.insert({"enum", "IntFlag"}); } const std::string mod = From 6a34e34a78a2bb59da1dab763e9cd2c9a4345c17 Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Wed, 19 Nov 2025 21:22:26 -0500 Subject: [PATCH 04/11] update test script --- tests/PythonTest.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/PythonTest.sh b/tests/PythonTest.sh index 90bee57244e..b855880464e 100755 --- a/tests/PythonTest.sh +++ b/tests/PythonTest.sh @@ -23,11 +23,18 @@ 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 --python-enum --python-version 3 ${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 --python-typing --gen-compare +${test_dir}/../flatc -p -o ${gen_code_path} -I include_test monster_extra.fbs --gen-object-api --python-typing --gen-compare --python-enum --python-version 3 ${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 arrays_test.fbs --gen-object-api --python-enum --python-version 3 +${test_dir}/../flatc -p -o ${gen_code_path} -I include_test arrays_test.fbs --gen-object-api --python-enum --python-typing --python-version 3 +${test_dir}/../flatc -p -o ${gen_code_path} -I include_test arrays_test.fbs --gen-object-api --python-enum --python-typing --gen-onefile --python-version 3 ${test_dir}/../flatc -p -o ${gen_code_path} -I include_test nested_union_test.fbs --gen-object-api --python-typing --python-decode-obj-api-strings +${test_dir}/../flatc -p -o ${gen_code_path} -I include_test nested_union_test.fbs --gen-object-api --python-typing --python-decode-obj-api-strings --python-enum --python-version 3 ${test_dir}/../flatc -p -o ${gen_code_path} -I include_test service_test.fbs --grpc --grpc-python-typed-handlers --python-typing --no-python-gen-numpy --gen-onefile +${test_dir}/../flatc -p -o ${gen_code_path} -I include_test service_test.fbs --grpc --grpc-python-typed-handlers --python-typing --python-enum --no-python-gen-numpy --gen-onefile --python-version 3 # Syntax: run_tests # From 95e948b92ddc70d71d301d4aefe925e51dc716de Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Wed, 19 Nov 2025 21:30:48 -0500 Subject: [PATCH 05/11] fix --- src/idl_gen_python.cpp | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 41f82b59b92..8a809dd8b22 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -613,12 +613,11 @@ class PythonStubGenerator { imports->Import("typing", "cast"); if (version_.major == 3) { - // TODO: only include what necessary - imports->Import("enum", "IntEnum"); - imports->Import("enum", "IntFlag"); if (enum_def->attributes.Lookup("big_flags")) { + imports->Import("enum", "IntFlag"); stub << "(IntFlag)"; } else { + imports->Import("enum", "IntEnum"); stub << "(IntEnum)"; } } else { @@ -731,7 +730,7 @@ class PythonGenerator : public BaseGenerator { code += "class " + namer_.Type(enum_def); python::Version version{parser_.opts.python_version}; - if (version.major >= 3) { + if (parser_.opts.python_enum) { if (enum_def.attributes.Lookup("bit_flags")) { code += "(IntFlag)"; } else { @@ -2798,14 +2797,7 @@ class PythonGenerator : public BaseGenerator { std::string one_file_code; ImportMap one_file_imports; - if (parser_.opts.python_enum) { - // TODO: only include this if python-typing and if the appropriate enum - // include is necessary - one_file_imports.insert({"enum", "IntEnum"}); - one_file_imports.insert({"enum", "IntFlag"}); - } - - if (!generateEnums(&one_file_code)) return false; + if (!generateEnums(&one_file_code, one_file_imports)) return false; if (!generateStructs(&one_file_code, one_file_imports)) return false; if (parser_.opts.one_file) { @@ -2820,7 +2812,8 @@ class PythonGenerator : public BaseGenerator { } private: - bool generateEnums(std::string* one_file_code) const { + bool generateEnums(std::string* one_file_code, + ImportMap& one_file_imports) const { for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); ++it) { auto& enum_def = **it; @@ -2831,14 +2824,24 @@ class PythonGenerator : public BaseGenerator { } if (parser_.opts.one_file && !enumcode.empty()) { + if (parser_.opts.python_enum) { + if (enum_def.attributes.Lookup("bit_flags")) { + one_file_imports.insert({"enum", "IntFlag"}); + } else { + one_file_imports.insert({"enum", "IntEnum"}); + } + } + *one_file_code += enumcode + "\n\n"; } else { ImportMap imports; if (parser_.opts.python_enum) { - // TODO: only include the one necessary - imports.insert({"enum", "IntEnum"}); - imports.insert({"enum", "IntFlag"}); + if (enum_def.attributes.Lookup("bit_flags")) { + imports.insert({"enum", "IntFlag"}); + } else { + imports.insert({"enum", "IntEnum"}); + } } const std::string mod = @@ -2961,12 +2964,6 @@ static bool GeneratePython(const Parser& parser, const std::string& path, python::Version version{parser.opts.python_version}; if (!version.IsValid()) return false; - if (parser.opts.python_enum && version.major < 3) { - // TODO: there doesn't seem to be a way to send an error message up - // we can't call parser.Error here because this is a const Parser& - return false; - } - python::PythonGenerator generator(parser, path, file_name, version); if (!generator.generate()) return false; From 7a35b5936bc200a4fda4b3cf37a774a316989bcb Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Wed, 19 Nov 2025 21:42:29 -0500 Subject: [PATCH 06/11] better option validation --- docs/source/flatc.md | 2 +- src/flatc.cpp | 8 +++++++- src/idl_gen_python.cpp | 3 +-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/source/flatc.md b/docs/source/flatc.md index cfaf661e194..9b45311a697 100644 --- a/docs/source/flatc.md +++ b/docs/source/flatc.md @@ -259,7 +259,7 @@ list of `FILES...`. - `--python-typing` : Generate Python type annotations -- `--python-enum` : Generated enumerations inherit from `IntEnum` instead of `object` +- `--python-enum` : Generated enumerations inherit from `IntEnum` or `IntFlag` instead of `object` - `--python-decode-obj-api-strings` : Decode bytes automaticaly with utf-8 diff --git a/src/flatc.cpp b/src/flatc.cpp index a7a012b506c..262a713830a 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -263,7 +263,8 @@ const static FlatCOption flatc_options[] = { "Skip emission of Python functions that are prefixed with typenames"}, {"", "python-typing", "", "Generate Python type annotations"}, {"", "python-enum", "", - "Generate enum types as IntEnum (requires python-version >= 3"}, + "Generate enum types as IntEnum and IntFlag (assumes python-version >= " + "3"}, {"", "python-version", "", "Generate code for the given Python version."}, {"", "python-decode-obj-api-strings", "", "Decode bytes to strings for the Python Object API"}, @@ -804,6 +805,11 @@ void FlatCompiler::ValidateOptions(const FlatCOptions& options) { Error("no options: specify at least one generator.", true); } + if (opts.python_enum && + (opts.python_version.empty() || opts.python_version[0] != '3')) { + Error("--python-enum requires --python-version >= 3"); + } + if (opts.cs_gen_json_serializer && !opts.generate_object_based_api) { Error( "--cs-gen-json-serializer requires --gen-object-api to be set as " diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 8a809dd8b22..41b09b3f6d1 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -612,7 +612,7 @@ class PythonStubGenerator { imports->Import("typing", "cast"); - if (version_.major == 3) { + if (parser_.opts.python_enum) { if (enum_def->attributes.Lookup("big_flags")) { imports->Import("enum", "IntFlag"); stub << "(IntFlag)"; @@ -729,7 +729,6 @@ class PythonGenerator : public BaseGenerator { code += "class " + namer_.Type(enum_def); - python::Version version{parser_.opts.python_version}; if (parser_.opts.python_enum) { if (enum_def.attributes.Lookup("bit_flags")) { code += "(IntFlag)"; From a1092bf7f4745c153e36e7ee50464c64f2ba91bd Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Fri, 21 Nov 2025 20:44:37 -0500 Subject: [PATCH 07/11] fix type comment --- src/idl_gen_python.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 41b09b3f6d1..545cc42a926 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -1895,10 +1895,14 @@ class PythonGenerator : public BaseGenerator { } default: // Scalar or string fields. - field_type = GetBasePythonTypeForScalarAndString(base_type); - if (field.IsScalarOptional()) { - import_typing_list.insert("Optional"); - field_type = "Optional[" + field_type + "]"; + if (parser_.opts.python_enum && IsEnum(field.value.type)) { + field_type = namer_.Type(*field.value.type.enum_def); + } else { + field_type = GetBasePythonTypeForScalarAndString(base_type); + if (field.IsScalarOptional()) { + import_typing_list.insert("Optional"); + field_type = "Optional[" + field_type + "]"; + } } break; } From 3ab6995b031a662ab8c59092842b47df731b8db9 Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Fri, 21 Nov 2025 20:45:32 -0500 Subject: [PATCH 08/11] revert changes to PythonTest --- tests/PythonTest.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/PythonTest.sh b/tests/PythonTest.sh index b855880464e..90bee57244e 100755 --- a/tests/PythonTest.sh +++ b/tests/PythonTest.sh @@ -23,18 +23,11 @@ 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 --python-enum --python-version 3 ${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 --python-typing --gen-compare -${test_dir}/../flatc -p -o ${gen_code_path} -I include_test monster_extra.fbs --gen-object-api --python-typing --gen-compare --python-enum --python-version 3 ${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 arrays_test.fbs --gen-object-api --python-enum --python-version 3 -${test_dir}/../flatc -p -o ${gen_code_path} -I include_test arrays_test.fbs --gen-object-api --python-enum --python-typing --python-version 3 -${test_dir}/../flatc -p -o ${gen_code_path} -I include_test arrays_test.fbs --gen-object-api --python-enum --python-typing --gen-onefile --python-version 3 ${test_dir}/../flatc -p -o ${gen_code_path} -I include_test nested_union_test.fbs --gen-object-api --python-typing --python-decode-obj-api-strings -${test_dir}/../flatc -p -o ${gen_code_path} -I include_test nested_union_test.fbs --gen-object-api --python-typing --python-decode-obj-api-strings --python-enum --python-version 3 ${test_dir}/../flatc -p -o ${gen_code_path} -I include_test service_test.fbs --grpc --grpc-python-typed-handlers --python-typing --no-python-gen-numpy --gen-onefile -${test_dir}/../flatc -p -o ${gen_code_path} -I include_test service_test.fbs --grpc --grpc-python-typed-handlers --python-typing --python-enum --no-python-gen-numpy --gen-onefile --python-version 3 # Syntax: run_tests # From 03c31bdaca76733100edb96f03f6d904c3d80f6a Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Wed, 26 Nov 2025 20:59:12 -0500 Subject: [PATCH 09/11] don't import things when not needed. remove extra space. --- src/idl_gen_python.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 545cc42a926..49c8f022b3d 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -1703,8 +1703,10 @@ class PythonGenerator : public BaseGenerator { auto& field = **it; if (field.deprecated) continue; - // include import for enum type if used in this struct - if (IsEnum(field.value.type)) { + // include import for enum type if used in this struct, we want type + // information, and we want modern enums. + if (IsEnum(field.value.type) && parser_.opts.python_typing && + parser_.opts.python_enum) { imports.insert(ImportMapEntry{GenPackageReference(field.value.type), namer_.Type(*field.value.type.enum_def)}); } @@ -2922,8 +2924,6 @@ class PythonGenerator : public BaseGenerator { } } - code += "\n"; - if (needs_default_imports && parser_.opts.python_gen_numpy) { code += "np = import_numpy()\n\n"; } From d9e65fcb78d651e11b103f7806ada70ee779b24e Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Fri, 5 Dec 2025 20:43:56 -0500 Subject: [PATCH 10/11] PR comment --- src/idl_gen_python.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index ed22475e031..eca6e3876a8 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -612,7 +612,7 @@ class PythonStubGenerator { imports->Import("typing", "cast"); - if (parser_.opts.python_enum) { + if (parser_.opts.python_typing && parser_.opts.python_enum) { if (enum_def->attributes.Lookup("big_flags")) { imports->Import("enum", "IntFlag"); stub << "(IntFlag)"; From f580530584d05a939bd57c826bb67ae030962127 Mon Sep 17 00:00:00 2001 From: Justin Davis Date: Wed, 18 Feb 2026 21:27:17 -0500 Subject: [PATCH 11/11] simplify interface and patch up pyi files --- docs/source/flatc.md | 2 - include/flatbuffers/idl.h | 2 - src/flatc.cpp | 10 ---- src/idl_gen_python.cpp | 48 +++++++++---------- tests/MyGame/Example/ArrayStruct.pyi | 2 +- tests/MyGame/Example/NestedStruct.py | 1 + tests/MyGame/Example/NestedStruct.pyi | 14 +++--- tests/MyGame/Example/NestedUnion/Any.pyi | 15 +++--- tests/MyGame/Example/NestedUnion/Color.pyi | 12 +++-- .../Example/NestedUnion/NestedUnionTest.py | 1 + .../Example/NestedUnion/NestedUnionTest.pyi | 8 ++-- .../NestedUnion/TestSimpleTableWithEnum.py | 1 + .../NestedUnion/TestSimpleTableWithEnum.pyi | 8 ++-- tests/MyGame/Example/NestedUnion/Vec3.py | 1 + tests/MyGame/Example/NestedUnion/Vec3.pyi | 8 ++-- tests/MyGame/Example/TestEnum.pyi | 12 +++-- 16 files changed, 71 insertions(+), 74 deletions(-) diff --git a/docs/source/flatc.md b/docs/source/flatc.md index a0db6b5068b..330ebc3d9a6 100644 --- a/docs/source/flatc.md +++ b/docs/source/flatc.md @@ -259,8 +259,6 @@ list of `FILES...`. - `--python-typing` : Generate Python type annotations -- `--python-enum` : Generated enumerations inherit from `IntEnum` or `IntFlag` instead of `object` - - `--python-decode-obj-api-strings` : Decode bytes automaticaly with utf-8 - `--file-names-only` : Prints out files which would be generated by this command, one per diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index a8724f181f8..4139529a11a 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -717,7 +717,6 @@ struct IDLOptions { /********************************** Python **********************************/ bool python_no_type_prefix_suffix; bool python_typing; - bool python_enum; bool python_decode_obj_api_strings = false; // The target Python version. Can be one of the following: @@ -861,7 +860,6 @@ struct IDLOptions { keep_proto_id(false), python_no_type_prefix_suffix(false), python_typing(false), - python_enum(false), python_gen_numpy(true), ts_omit_entrypoint(false), proto_id_gap_action(ProtoIdGapAction::WARNING), diff --git a/src/flatc.cpp b/src/flatc.cpp index 4e27b37f8ba..cab0ee395a3 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -262,9 +262,6 @@ const static FlatCOption flatc_options[] = { {"", "python-no-type-prefix-suffix", "", "Skip emission of Python functions that are prefixed with typenames"}, {"", "python-typing", "", "Generate Python type annotations"}, - {"", "python-enum", "", - "Generate enum types as IntEnum and IntFlag (assumes python-version >= " - "3"}, {"", "python-version", "", "Generate code for the given Python version."}, {"", "python-decode-obj-api-strings", "", "Decode bytes to strings for the Python Object API"}, @@ -697,8 +694,6 @@ FlatCOptions FlatCompiler::ParseFromCommandLineArguments(int argc, opts.python_no_type_prefix_suffix = true; } else if (arg == "--python-typing") { opts.python_typing = true; - } else if (arg == "--python-enum") { - opts.python_enum = true; } else if (arg.rfind("--python-version=", 0) == 0) { opts.python_version = arg.substr(std::string("--python-version=").size()); @@ -804,11 +799,6 @@ void FlatCompiler::ValidateOptions(const FlatCOptions& options) { Error("no options: specify at least one generator.", true); } - if (opts.python_enum && - (opts.python_version.empty() || opts.python_version[0] != '3')) { - Error("--python-enum requires --python-version >= 3"); - } - if (opts.cs_gen_json_serializer && !opts.generate_object_based_api) { Error( "--cs-gen-json-serializer requires --gen-object-api to be set as " diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 0a60bfdbf62..60c02ed48ef 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -208,15 +208,9 @@ class PythonStubGenerator { 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::ignore = 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 + "]"; + return namer_.Type(enum_def); } std::string TypeOf(const Type& type, Imports* imports) const { @@ -530,7 +524,7 @@ class PythonStubGenerator { StructBuilderArgs(*struct_def, "", imports, &args); stub << '\n'; - stub << "def Create" + namer_.Type(*struct_def) + stub << "def Create" + namer_.Function(*struct_def) << "(builder: flatbuffers.Builder"; for (const std::string& arg : args) { stub << ", " << arg; @@ -610,10 +604,10 @@ class PythonStubGenerator { stub << "class " << namer_.Type(*enum_def); imports->Export(ModuleFor(enum_def), namer_.Type(*enum_def)); - imports->Import("typing", "cast"); + imports->Import("typing", "Final"); - if (parser_.opts.python_typing && parser_.opts.python_enum) { - if (enum_def->attributes.Lookup("big_flags")) { + if (parser_.opts.python_typing) { + if (enum_def->attributes.Lookup("bit_flags")) { imports->Import("enum", "IntFlag"); stub << "(IntFlag)"; } else { @@ -626,13 +620,15 @@ class PythonStubGenerator { stub << ":\n"; for (const EnumVal* val : enum_def->Vals()) { - stub << " " << namer_.Variant(*val) << " = cast(" - << ScalarType(enum_def->underlying_type.base_type) << ", ...)\n"; + stub << " " << namer_.Variant(*val) << ": Final[" + << namer_.Type(*enum_def) << "]\n"; } + stub << " def __new__(cls, value: int) -> " << namer_.Type(*enum_def) + << ": ...\n"; if (parser_.opts.generate_object_based_api & enum_def->is_union) { imports->Import("flatbuffers", "table"); - stub << "def " << namer_.Function(*enum_def) + stub << "\ndef " << namer_.Function(*enum_def) << "Creator(union_type: " << EnumType(*enum_def, imports) << ", table: table.Table) -> " << UnionType(*enum_def, imports) << ": ...\n"; @@ -729,7 +725,8 @@ class PythonGenerator : public BaseGenerator { code += "class " + namer_.Type(enum_def); - if (parser_.opts.python_enum) { + python::Version version{parser_.opts.python_version}; + if (version.major == 3) { if (enum_def.attributes.Lookup("bit_flags")) { code += "(IntFlag)"; } else { @@ -870,7 +867,7 @@ class PythonGenerator : public BaseGenerator { std::string getter = GenGetter(field.value.type); GenReceiver(struct_def, code_ptr); code += namer_.Method(field); - code += "(self):"; + code += "(self):"; // TODO: add typing code += OffsetPrefix(field); getter += "o + self._tab.Pos)"; auto is_bool = IsBool(field.value.type.base_type); @@ -1705,8 +1702,7 @@ class PythonGenerator : public BaseGenerator { // include import for enum type if used in this struct, we want type // information, and we want modern enums. - if (IsEnum(field.value.type) && parser_.opts.python_typing && - parser_.opts.python_enum) { + if (IsEnum(field.value.type) && parser_.opts.python_typing) { imports.insert(ImportMapEntry{GenPackageReference(field.value.type), namer_.Type(*field.value.type.enum_def)}); } @@ -1766,7 +1762,8 @@ class PythonGenerator : public BaseGenerator { return float_const_gen_.GenFloatConstant(field); } else if (IsInteger(base_type)) { // wrap the default value in the enum constructor to aid type hinting - if (parser_.opts.python_enum && IsEnum(field.value.type)) { + python::Version version{parser_.opts.python_version}; + if (version.major == 3 && IsEnum(field.value.type)) { auto enum_type = namer_.Type(*field.value.type.enum_def); return enum_type + "(" + field.value.constant + ")"; } @@ -1897,7 +1894,8 @@ class PythonGenerator : public BaseGenerator { } default: // Scalar or string fields. - if (parser_.opts.python_enum && IsEnum(field.value.type)) { + python::Version version{parser_.opts.python_version}; + if (version.major == 3 && IsEnum(field.value.type)) { field_type = namer_.Type(*field.value.type.enum_def); } else { field_type = GetBasePythonTypeForScalarAndString(base_type); @@ -2682,7 +2680,8 @@ class PythonGenerator : public BaseGenerator { std::string GenFieldTy(const FieldDef& field) const { if (IsScalar(field.value.type.base_type) || IsArray(field.value.type)) { - if (parser_.opts.python_enum) { + python::Version version{parser_.opts.python_version}; + if (version.major == 3) { if (IsEnum(field.value.type)) { return namer_.Type(*field.value.type.enum_def); } @@ -2828,8 +2827,9 @@ class PythonGenerator : public BaseGenerator { GenUnionCreator(enum_def, &enumcode); } + python::Version version{parser_.opts.python_version}; if (parser_.opts.one_file && !enumcode.empty()) { - if (parser_.opts.python_enum) { + if (version.major == 3) { if (enum_def.attributes.Lookup("bit_flags")) { one_file_imports.insert({"enum", "IntFlag"}); } else { @@ -2841,7 +2841,7 @@ class PythonGenerator : public BaseGenerator { } else { ImportMap imports; - if (parser_.opts.python_enum) { + if (version.major == 3) { if (enum_def.attributes.Lookup("bit_flags")) { imports.insert({"enum", "IntFlag"}); } else { diff --git a/tests/MyGame/Example/ArrayStruct.pyi b/tests/MyGame/Example/ArrayStruct.pyi index c96c28f723d..6664e80e82d 100644 --- a/tests/MyGame/Example/ArrayStruct.pyi +++ b/tests/MyGame/Example/ArrayStruct.pyi @@ -53,5 +53,5 @@ class ArrayStructT(object): def _UnPack(self, arrayStruct: ArrayStruct) -> None: ... def Pack(self, builder: flatbuffers.Builder) -> None: ... -def CreateArrayStruct(builder: flatbuffers.Builder, a: float, b: int, c: int, d_a: int, d_b: typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C], d_c: typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C], d_d: int, e: int, f: int) -> uoffset: ... +def CreateArrayStruct(builder: flatbuffers.Builder, a: float, b: int, c: int, d_a: int, d_b: TestEnum, d_c: TestEnum, d_d: int, e: int, f: int) -> uoffset: ... diff --git a/tests/MyGame/Example/NestedStruct.py b/tests/MyGame/Example/NestedStruct.py index c53cc4c7eb0..b7dd1766828 100644 --- a/tests/MyGame/Example/NestedStruct.py +++ b/tests/MyGame/Example/NestedStruct.py @@ -5,6 +5,7 @@ import flatbuffers from flatbuffers.compat import import_numpy from typing import Any +from MyGame.Example.TestEnum import TestEnum np = import_numpy() class NestedStruct(object): diff --git a/tests/MyGame/Example/NestedStruct.pyi b/tests/MyGame/Example/NestedStruct.pyi index 0ae7f5c87a8..1b661c3f02f 100644 --- a/tests/MyGame/Example/NestedStruct.pyi +++ b/tests/MyGame/Example/NestedStruct.pyi @@ -17,8 +17,8 @@ class NestedStruct(object): def AAsNumpy(self) -> np.ndarray: ... def ALength(self) -> int: ... def AIsNone(self) -> bool: ... - def B(self) -> typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C]: ... - def C(self, i: int) -> typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C]: ... + def B(self) -> TestEnum: ... + def C(self, i: int) -> TestEnum: ... def CAsNumpy(self) -> np.ndarray: ... def CLength(self) -> int: ... def CIsNone(self) -> bool: ... @@ -28,14 +28,14 @@ class NestedStruct(object): def DIsNone(self) -> bool: ... class NestedStructT(object): a: typing.List[int] - b: typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C] - c: typing.List[typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C]] + b: TestEnum + c: typing.List[TestEnum] d: typing.List[int] def __init__( self, a: typing.List[int] | None = ..., - b: typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C] = ..., - c: typing.List[typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C]] | None = ..., + b: TestEnum = ..., + c: typing.List[TestEnum] | None = ..., d: typing.List[int] | None = ..., ) -> None: ... @classmethod @@ -47,5 +47,5 @@ class NestedStructT(object): def _UnPack(self, nestedStruct: NestedStruct) -> None: ... def Pack(self, builder: flatbuffers.Builder) -> None: ... -def CreateNestedStruct(builder: flatbuffers.Builder, a: int, b: typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C], c: typing.Literal[TestEnum.A, TestEnum.B, TestEnum.C], d: int) -> uoffset: ... +def CreateNestedStruct(builder: flatbuffers.Builder, a: int, b: TestEnum, c: TestEnum, d: int) -> uoffset: ... diff --git a/tests/MyGame/Example/NestedUnion/Any.pyi b/tests/MyGame/Example/NestedUnion/Any.pyi index a32f7f88aff..9221f48815d 100644 --- a/tests/MyGame/Example/NestedUnion/Any.pyi +++ b/tests/MyGame/Example/NestedUnion/Any.pyi @@ -6,14 +6,17 @@ import numpy as np import typing from MyGame.Example.NestedUnion.TestSimpleTableWithEnum import TestSimpleTableWithEnum from MyGame.Example.NestedUnion.Vec3 import Vec3 +from enum import IntEnum from flatbuffers import table -from typing import cast +from typing import Final uoffset: typing.TypeAlias = flatbuffers.number_types.UOffsetTFlags.py_type -class Any(object): - NONE = cast(int, ...) - Vec3 = cast(int, ...) - TestSimpleTableWithEnum = cast(int, ...) -def AnyCreator(union_type: typing.Literal[Any.NONE, Any.Vec3, Any.TestSimpleTableWithEnum], table: table.Table) -> typing.Union[None, Vec3, TestSimpleTableWithEnum]: ... +class Any(IntEnum): + NONE: Final[Any] + Vec3: Final[Any] + TestSimpleTableWithEnum: Final[Any] + def __new__(cls, value: int) -> Any: ... + +def AnyCreator(union_type: Any, table: table.Table) -> typing.Union[None, Vec3, TestSimpleTableWithEnum]: ... diff --git a/tests/MyGame/Example/NestedUnion/Color.pyi b/tests/MyGame/Example/NestedUnion/Color.pyi index 2e0157cd5ab..91c043ee4c0 100644 --- a/tests/MyGame/Example/NestedUnion/Color.pyi +++ b/tests/MyGame/Example/NestedUnion/Color.pyi @@ -4,12 +4,14 @@ import flatbuffers import numpy as np import typing -from typing import cast +from enum import IntFlag +from typing import Final uoffset: typing.TypeAlias = flatbuffers.number_types.UOffsetTFlags.py_type -class Color(object): - Red = cast(int, ...) - Green = cast(int, ...) - Blue = cast(int, ...) +class Color(IntFlag): + Red: Final[Color] + Green: Final[Color] + Blue: Final[Color] + def __new__(cls, value: int) -> Color: ... diff --git a/tests/MyGame/Example/NestedUnion/NestedUnionTest.py b/tests/MyGame/Example/NestedUnion/NestedUnionTest.py index e3d68855add..2ff3b9f6813 100644 --- a/tests/MyGame/Example/NestedUnion/NestedUnionTest.py +++ b/tests/MyGame/Example/NestedUnion/NestedUnionTest.py @@ -5,6 +5,7 @@ import flatbuffers from flatbuffers.compat import import_numpy from typing import Any +from MyGame.Example.NestedUnion.Any import Any from flatbuffers.table import Table from typing import Optional np = import_numpy() diff --git a/tests/MyGame/Example/NestedUnion/NestedUnionTest.pyi b/tests/MyGame/Example/NestedUnion/NestedUnionTest.pyi index 444bddac1fa..4f722305adf 100644 --- a/tests/MyGame/Example/NestedUnion/NestedUnionTest.pyi +++ b/tests/MyGame/Example/NestedUnion/NestedUnionTest.pyi @@ -18,18 +18,18 @@ class NestedUnionTest(object): def GetRootAsNestedUnionTest(cls, buf: bytes, offset: int) -> NestedUnionTest: ... def Init(self, buf: bytes, pos: int) -> None: ... def Name(self) -> str | None: ... - def DataType(self) -> typing.Literal[Any.NONE, Any.Vec3, Any.TestSimpleTableWithEnum]: ... + def DataType(self) -> Any: ... def Data(self) -> table.Table | None: ... def Id(self) -> int: ... class NestedUnionTestT(object): name: str | None - dataType: typing.Literal[Any.NONE, Any.Vec3, Any.TestSimpleTableWithEnum] + dataType: Any data: typing.Union[None, Vec3T, TestSimpleTableWithEnumT] id: int def __init__( self, name: str | None = ..., - dataType: typing.Literal[Any.NONE, Any.Vec3, Any.TestSimpleTableWithEnum] = ..., + dataType: Any = ..., data: typing.Union[None, Vec3T, TestSimpleTableWithEnumT] = ..., id: int = ..., ) -> None: ... @@ -44,7 +44,7 @@ class NestedUnionTestT(object): def NestedUnionTestStart(builder: flatbuffers.Builder) -> None: ... def Start(builder: flatbuffers.Builder) -> None: ... def NestedUnionTestAddName(builder: flatbuffers.Builder, name: uoffset) -> None: ... -def NestedUnionTestAddDataType(builder: flatbuffers.Builder, dataType: typing.Literal[Any.NONE, Any.Vec3, Any.TestSimpleTableWithEnum]) -> None: ... +def NestedUnionTestAddDataType(builder: flatbuffers.Builder, dataType: Any) -> None: ... def NestedUnionTestAddData(builder: flatbuffers.Builder, data: uoffset) -> None: ... def NestedUnionTestAddId(builder: flatbuffers.Builder, id: int) -> None: ... def NestedUnionTestEnd(builder: flatbuffers.Builder) -> uoffset: ... diff --git a/tests/MyGame/Example/NestedUnion/TestSimpleTableWithEnum.py b/tests/MyGame/Example/NestedUnion/TestSimpleTableWithEnum.py index 9c7d48a1153..010e4a7eee2 100644 --- a/tests/MyGame/Example/NestedUnion/TestSimpleTableWithEnum.py +++ b/tests/MyGame/Example/NestedUnion/TestSimpleTableWithEnum.py @@ -5,6 +5,7 @@ import flatbuffers from flatbuffers.compat import import_numpy from typing import Any +from MyGame.Example.NestedUnion.Color import Color np = import_numpy() class TestSimpleTableWithEnum(object): diff --git a/tests/MyGame/Example/NestedUnion/TestSimpleTableWithEnum.pyi b/tests/MyGame/Example/NestedUnion/TestSimpleTableWithEnum.pyi index 78469535c4e..d743a867106 100644 --- a/tests/MyGame/Example/NestedUnion/TestSimpleTableWithEnum.pyi +++ b/tests/MyGame/Example/NestedUnion/TestSimpleTableWithEnum.pyi @@ -14,12 +14,12 @@ class TestSimpleTableWithEnum(object): @classmethod def GetRootAsTestSimpleTableWithEnum(cls, buf: bytes, offset: int) -> TestSimpleTableWithEnum: ... def Init(self, buf: bytes, pos: int) -> None: ... - def Color(self) -> typing.Literal[Color.Red, Color.Green, Color.Blue]: ... + def Color(self) -> Color: ... class TestSimpleTableWithEnumT(object): - color: typing.Literal[Color.Red, Color.Green, Color.Blue] + color: Color def __init__( self, - color: typing.Literal[Color.Red, Color.Green, Color.Blue] = ..., + color: Color = ..., ) -> None: ... @classmethod def InitFromBuf(cls, buf: bytes, pos: int) -> TestSimpleTableWithEnumT: ... @@ -31,7 +31,7 @@ class TestSimpleTableWithEnumT(object): def Pack(self, builder: flatbuffers.Builder) -> None: ... def TestSimpleTableWithEnumStart(builder: flatbuffers.Builder) -> None: ... def Start(builder: flatbuffers.Builder) -> None: ... -def TestSimpleTableWithEnumAddColor(builder: flatbuffers.Builder, color: typing.Literal[Color.Red, Color.Green, Color.Blue]) -> None: ... +def TestSimpleTableWithEnumAddColor(builder: flatbuffers.Builder, color: Color) -> None: ... def TestSimpleTableWithEnumEnd(builder: flatbuffers.Builder) -> uoffset: ... def End(builder: flatbuffers.Builder) -> uoffset: ... diff --git a/tests/MyGame/Example/NestedUnion/Vec3.py b/tests/MyGame/Example/NestedUnion/Vec3.py index 5157da6e7c2..da06c1d847e 100644 --- a/tests/MyGame/Example/NestedUnion/Vec3.py +++ b/tests/MyGame/Example/NestedUnion/Vec3.py @@ -5,6 +5,7 @@ import flatbuffers from flatbuffers.compat import import_numpy from typing import Any +from MyGame.Example.NestedUnion.Color import Color from MyGame.Example.NestedUnion.Test import Test from typing import Optional np = import_numpy() diff --git a/tests/MyGame/Example/NestedUnion/Vec3.pyi b/tests/MyGame/Example/NestedUnion/Vec3.pyi index 17a474c3723..9643500e29a 100644 --- a/tests/MyGame/Example/NestedUnion/Vec3.pyi +++ b/tests/MyGame/Example/NestedUnion/Vec3.pyi @@ -19,14 +19,14 @@ class Vec3(object): def Y(self) -> float: ... def Z(self) -> float: ... def Test1(self) -> float: ... - def Test2(self) -> typing.Literal[Color.Red, Color.Green, Color.Blue]: ... + def Test2(self) -> Color: ... def Test3(self) -> Test | None: ... class Vec3T(object): x: float y: float z: float test1: float - test2: typing.Literal[Color.Red, Color.Green, Color.Blue] + test2: Color test3: TestT | None def __init__( self, @@ -34,7 +34,7 @@ class Vec3T(object): y: float = ..., z: float = ..., test1: float = ..., - test2: typing.Literal[Color.Red, Color.Green, Color.Blue] = ..., + test2: Color = ..., test3: 'TestT' | None = ..., ) -> None: ... @classmethod @@ -51,7 +51,7 @@ def Vec3AddX(builder: flatbuffers.Builder, x: float) -> None: ... def Vec3AddY(builder: flatbuffers.Builder, y: float) -> None: ... def Vec3AddZ(builder: flatbuffers.Builder, z: float) -> None: ... def Vec3AddTest1(builder: flatbuffers.Builder, test1: float) -> None: ... -def Vec3AddTest2(builder: flatbuffers.Builder, test2: typing.Literal[Color.Red, Color.Green, Color.Blue]) -> None: ... +def Vec3AddTest2(builder: flatbuffers.Builder, test2: Color) -> None: ... def Vec3AddTest3(builder: flatbuffers.Builder, test3: uoffset) -> None: ... def Vec3End(builder: flatbuffers.Builder) -> uoffset: ... def End(builder: flatbuffers.Builder) -> uoffset: ... diff --git a/tests/MyGame/Example/TestEnum.pyi b/tests/MyGame/Example/TestEnum.pyi index 5679cd820e6..deb1ee6192b 100644 --- a/tests/MyGame/Example/TestEnum.pyi +++ b/tests/MyGame/Example/TestEnum.pyi @@ -4,12 +4,14 @@ import flatbuffers import numpy as np import typing -from typing import cast +from enum import IntEnum +from typing import Final uoffset: typing.TypeAlias = flatbuffers.number_types.UOffsetTFlags.py_type -class TestEnum(object): - A = cast(int, ...) - B = cast(int, ...) - C = cast(int, ...) +class TestEnum(IntEnum): + A: Final[TestEnum] + B: Final[TestEnum] + C: Final[TestEnum] + def __new__(cls, value: int) -> TestEnum: ...