Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions scripts/generate_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,11 @@ def glob(path, pattern):
schema="nested_union_test.fbs",
)

flatc(
["--python", "--gen-object-api"],
schema="union_name_test.fbs",
)

flatc(
NO_INCL_OPTS + CPP_OPTS,
schema="default_vectors_strings_test.fbs",
Expand Down
6 changes: 3 additions & 3 deletions src/idl_gen_python.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2095,12 +2095,12 @@ class PythonGenerator : public BaseGenerator {
const auto field_method = namer_.Method(field);
const auto struct_var = namer_.Variable(struct_def);
const EnumDef& enum_def = *field.value.type.enum_def;
auto union_type = namer_.Type(enum_def);
auto union_fn = namer_.Function(enum_def);

if (parser_.opts.include_dependence_headers) {
union_type = namer_.NamespacedType(enum_def) + "." + union_type;
union_fn = namer_.NamespacedType(enum_def) + "." + union_fn;
}
code += GenIndents(2) + "self." + field_field + " = " + union_type +
code += GenIndents(2) + "self." + field_field + " = " + union_fn +
"Creator(" + "self." + field_field + "Type, " + struct_var + "." +
field_method + "())";
}
Expand Down
1 change: 1 addition & 0 deletions tests/PythonTest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ ${test_dir}/../flatc -p -o ${gen_code_path} -I include_test monster_extra.fbs --
${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 --python-decode-obj-api-strings
${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} union_name_test.fbs --gen-object-api

# Syntax: run_tests <interpreter> <benchmark vtable dedupes>
# <benchmark read count> <benchmark build count>
Expand Down
48 changes: 48 additions & 0 deletions tests/py_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
import monster_test_generated # the one-file version
import optional_scalars
import optional_scalars.ScalarStuff
import union_name_test.Container # refers to generated code
import union_name_test.Foo # refers to generated code
import union_name_test.Bar # refers to generated code
import union_name_test.my_test_union # refers to generated code


def create_namespace_shortcut(is_onefile):
Expand Down Expand Up @@ -3020,6 +3024,50 @@ def test_nested_union_tables(self):
)


class TestUnionCreatorNaming(unittest.TestCase):
"""Tests that union creator functions use consistent naming (issue #8843).

Uses a schema with a snake_case union name (my_test_union) to verify that
the generated creator function name matches between definition and call site.
"""

def test_union_creator_pack_unpack(self):
"""Pack and UnPack a table with a non-UpperCamel union name."""
containerT = union_name_test.Container.ContainerT()
containerT.uType = union_name_test.my_test_union.my_test_union.Foo
containerT.u = union_name_test.Foo.FooT()
containerT.u.val = 42

b = flatbuffers.Builder(0)
b.Finish(containerT.Pack(b))

container = union_name_test.Container.Container.GetRootAs(
b.Bytes, b.Head()
)
containerT2 = union_name_test.Container.ContainerT.InitFromObj(container)

self.assertEqual(containerT2.uType, union_name_test.my_test_union.my_test_union.Foo)
self.assertEqual(containerT2.u.val, 42)

def test_union_creator_with_bar(self):
"""Test the other union variant to ensure all branches work."""
containerT = union_name_test.Container.ContainerT()
containerT.uType = union_name_test.my_test_union.my_test_union.Bar
containerT.u = union_name_test.Bar.BarT()
containerT.u.name = "hello"

b = flatbuffers.Builder(0)
b.Finish(containerT.Pack(b))

container = union_name_test.Container.Container.GetRootAs(
b.Bytes, b.Head()
)
containerT2 = union_name_test.Container.ContainerT.InitFromObj(container)

self.assertEqual(containerT2.uType, union_name_test.my_test_union.my_test_union.Bar)
self.assertEqual(containerT2.u.name, b"hello")


class TestBuilderClear(unittest.TestCase):

def test_consistency(self):
Expand Down
21 changes: 21 additions & 0 deletions tests/union_name_test.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Test for union creator naming consistency (issue #8843).
// Uses non-UpperCamel union name to verify that Function() naming
// is used consistently for both definition and call site.

namespace union_name_test;

table Foo {
val: int;
}

table Bar {
name: string;
}

union my_test_union { Foo, Bar }

table Container {
u: my_test_union;
}

root_type Container;
93 changes: 93 additions & 0 deletions tests/union_name_test/Bar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# automatically generated by the FlatBuffers compiler, do not modify

# namespace: union_name_test

import flatbuffers
from flatbuffers.compat import import_numpy
np = import_numpy()

class Bar(object):
__slots__ = ['_tab']

@classmethod
def GetRootAs(cls, buf, offset=0):
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset)
x = Bar()
x.Init(buf, n + offset)
return x

@classmethod
def GetRootAsBar(cls, buf, offset=0):
"""This method is deprecated. Please switch to GetRootAs."""
return cls.GetRootAs(buf, offset)
# Bar
def Init(self, buf, pos):
self._tab = flatbuffers.table.Table(buf, pos)

# Bar
def Name(self):
o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4))
if o != 0:
return self._tab.String(o + self._tab.Pos)
return None

def BarStart(builder):
builder.StartObject(1)

def Start(builder):
BarStart(builder)

def BarAddName(builder, name):
builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(name), 0)

def AddName(builder, name):
BarAddName(builder, name)

def BarEnd(builder):
return builder.EndObject()

def End(builder):
return BarEnd(builder)


class BarT(object):

# BarT
def __init__(
self,
name = None,
):
self.name = name # type: Optional[str]

@classmethod
def InitFromBuf(cls, buf, pos):
bar = Bar()
bar.Init(buf, pos)
return cls.InitFromObj(bar)

@classmethod
def InitFromPackedBuf(cls, buf, pos=0):
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, pos)
return cls.InitFromBuf(buf, pos+n)

@classmethod
def InitFromObj(cls, bar):
x = BarT()
x._UnPack(bar)
return x

# BarT
def _UnPack(self, bar):
if bar is None:
return
self.name = bar.Name()

# BarT
def Pack(self, builder):
if self.name is not None:
name = builder.CreateString(self.name)
BarStart(builder)
if self.name is not None:
BarAddName(builder, name)
bar = BarEnd(builder)
return bar
120 changes: 120 additions & 0 deletions tests/union_name_test/Container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# automatically generated by the FlatBuffers compiler, do not modify

# namespace: union_name_test

import flatbuffers
from flatbuffers.compat import import_numpy
np = import_numpy()

class Container(object):
__slots__ = ['_tab']

@classmethod
def GetRootAs(cls, buf, offset=0):
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset)
x = Container()
x.Init(buf, n + offset)
return x

@classmethod
def GetRootAsContainer(cls, buf, offset=0):
"""This method is deprecated. Please switch to GetRootAs."""
return cls.GetRootAs(buf, offset)
# Container
def Init(self, buf, pos):
self._tab = flatbuffers.table.Table(buf, pos)

# Container
def UType(self):
o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4))
if o != 0:
return self._tab.Get(flatbuffers.number_types.Uint8Flags, o + self._tab.Pos)
return 0

# Container
def U(self):
o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6))
if o != 0:
from flatbuffers.table import Table
obj = Table(bytearray(), 0)
self._tab.Union(obj, o)
return obj
return None

def ContainerStart(builder):
builder.StartObject(2)

def Start(builder):
ContainerStart(builder)

def ContainerAddUType(builder, uType):
builder.PrependUint8Slot(0, uType, 0)

def AddUType(builder, uType):
ContainerAddUType(builder, uType)

def ContainerAddU(builder, u):
builder.PrependUOffsetTRelativeSlot(1, flatbuffers.number_types.UOffsetTFlags.py_type(u), 0)

def AddU(builder, u):
ContainerAddU(builder, u)

def ContainerEnd(builder):
return builder.EndObject()

def End(builder):
return ContainerEnd(builder)

import union_name_test.Bar
import union_name_test.Foo
import union_name_test.my_test_union
try:
from typing import Union
except:
pass

class ContainerT(object):

# ContainerT
def __init__(
self,
uType = 0,
u = None,
):
self.uType = uType # type: int
self.u = u # type: Union[None, 'union_name_test.Foo.FooT', 'union_name_test.Bar.BarT']

@classmethod
def InitFromBuf(cls, buf, pos):
container = Container()
container.Init(buf, pos)
return cls.InitFromObj(container)

@classmethod
def InitFromPackedBuf(cls, buf, pos=0):
n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, pos)
return cls.InitFromBuf(buf, pos+n)

@classmethod
def InitFromObj(cls, container):
x = ContainerT()
x._UnPack(container)
return x

# ContainerT
def _UnPack(self, container):
if container is None:
return
self.uType = container.UType()
self.u = union_name_test.my_test_union.MyTestUnionCreator(self.uType, container.U())

# ContainerT
def Pack(self, builder):
if self.u is not None:
u = self.u.Pack(builder)
ContainerStart(builder)
ContainerAddUType(builder, self.uType)
if self.u is not None:
ContainerAddU(builder, u)
container = ContainerEnd(builder)
return container
Loading
Loading