mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-02 04:04:19 +00:00
Python: Improve Builder user interface.
+ Add state to the Builder object to track if we are inside a table,
and if we are finished building the buffer.
+ Use this data to check that a buffer is being built correctly.
+ Raise an exception if a buffer is not being built correctly.
+ Test that the exceptions happen as expected.
Based on d236dea.
This commit is contained in:
@@ -32,7 +32,7 @@ class OffsetArithmeticError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class NotInObjectError(RuntimeError):
|
||||
class IsNotNestedError(RuntimeError):
|
||||
"""
|
||||
Error caused by using a Builder to write Object data when not inside
|
||||
an Object.
|
||||
@@ -40,7 +40,7 @@ class NotInObjectError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class ObjectIsNestedError(RuntimeError):
|
||||
class IsNestedError(RuntimeError):
|
||||
"""
|
||||
Error caused by using a Builder to begin an Object when an Object is
|
||||
already being built.
|
||||
@@ -63,6 +63,12 @@ class BuilderSizeError(RuntimeError):
|
||||
"""
|
||||
pass
|
||||
|
||||
class BuilderNotFinishedError(RuntimeError):
|
||||
"""
|
||||
Error caused by not calling `Finish` before calling `Output`.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# VtableMetadataFields is the count of metadata fields in each vtable.
|
||||
VtableMetadataFields = 2
|
||||
@@ -85,7 +91,7 @@ class Builder(object):
|
||||
"""
|
||||
|
||||
__slots__ = ("Bytes", "current_vtable", "head", "minalign", "objectEnd",
|
||||
"vtables")
|
||||
"vtables", "nested", "finished")
|
||||
|
||||
"""
|
||||
Maximum buffer size constant, in bytes.
|
||||
@@ -110,13 +116,19 @@ class Builder(object):
|
||||
self.minalign = 1
|
||||
self.objectEnd = None
|
||||
self.vtables = []
|
||||
self.nested = False
|
||||
self.finished = False
|
||||
|
||||
def Output(self):
|
||||
"""
|
||||
Output returns the portion of the buffer that has been used for
|
||||
writing data.
|
||||
writing data. It raises BuilderNotFinishedError if the buffer has not
|
||||
been finished with `Finish`.
|
||||
"""
|
||||
|
||||
if not self.finished:
|
||||
raise BuilderNotFinishedError()
|
||||
|
||||
return self.Bytes[self.Head():]
|
||||
|
||||
def StartObject(self, numfields):
|
||||
@@ -128,6 +140,7 @@ class Builder(object):
|
||||
self.current_vtable = [0 for _ in range_func(numfields)]
|
||||
self.objectEnd = self.Offset()
|
||||
self.minalign = 1
|
||||
self.nested = True
|
||||
|
||||
def WriteVtable(self):
|
||||
"""
|
||||
@@ -236,10 +249,8 @@ class Builder(object):
|
||||
|
||||
def EndObject(self):
|
||||
"""EndObject writes data necessary to finish object construction."""
|
||||
if self.current_vtable is None:
|
||||
msg = ("flatbuffers: Tried to write the end of an Object when "
|
||||
"the Builder was not currently writing an Object.")
|
||||
raise NotInObjectError(msg)
|
||||
self.assertNested()
|
||||
self.nested = False
|
||||
return self.WriteVtable()
|
||||
|
||||
def growByteBuffer(self):
|
||||
@@ -336,6 +347,7 @@ class Builder(object):
|
||||
"""
|
||||
|
||||
self.assertNotNested()
|
||||
self.nested = True
|
||||
self.Prep(N.Uint32Flags.bytewidth, elemSize*numElems)
|
||||
self.Prep(alignment, elemSize*numElems) # In case alignment > int.
|
||||
return self.Offset()
|
||||
@@ -343,6 +355,8 @@ class Builder(object):
|
||||
def EndVector(self, vectorNumElems):
|
||||
"""EndVector writes data necessary to finish vector construction."""
|
||||
|
||||
self.assertNested()
|
||||
self.nested = False
|
||||
# we already made space for this, so write without PrependUint32
|
||||
self.PlaceUOffsetT(vectorNumElems)
|
||||
return self.Offset()
|
||||
@@ -351,6 +365,7 @@ class Builder(object):
|
||||
"""CreateString writes a null-terminated byte string as a vector."""
|
||||
|
||||
self.assertNotNested()
|
||||
self.nested = True
|
||||
|
||||
if isinstance(s, compat.string_types):
|
||||
x = s.encode()
|
||||
@@ -369,18 +384,24 @@ class Builder(object):
|
||||
|
||||
return self.EndVector(len(x))
|
||||
|
||||
def assertNested(self):
|
||||
"""
|
||||
Check that we are in the process of building an object.
|
||||
"""
|
||||
|
||||
if not self.nested:
|
||||
raise IsNotNestedError()
|
||||
|
||||
def assertNotNested(self):
|
||||
"""
|
||||
Check that no other objects are being built while making this
|
||||
object. If not, raise an exception.
|
||||
"""
|
||||
|
||||
if self.current_vtable is not None:
|
||||
msg = ("flatbuffers: Tried to write a new Object when the "
|
||||
"Builder was already writing an Object.")
|
||||
raise ObjectIsNestedError(msg)
|
||||
if self.nested:
|
||||
raise IsNestedError()
|
||||
|
||||
def assertNested(self, obj):
|
||||
def assertStructIsInline(self, obj):
|
||||
"""
|
||||
Structs are always stored inline, so need to be created right
|
||||
where they are used. You'll get this error if you created it
|
||||
@@ -399,11 +420,7 @@ class Builder(object):
|
||||
buffer.
|
||||
|
||||
"""
|
||||
if self.current_vtable is None:
|
||||
msg = ("flatbuffers: Tried to write an Object field when "
|
||||
"the Builder was not currently writing an Object.")
|
||||
raise NotInObjectError(msg)
|
||||
|
||||
self.assertNested()
|
||||
self.current_vtable[slotnum] = self.Offset()
|
||||
|
||||
def Finish(self, rootTable):
|
||||
@@ -411,6 +428,7 @@ class Builder(object):
|
||||
N.enforce_number(rootTable, N.UOffsetTFlags)
|
||||
self.Prep(self.minalign, N.UOffsetTFlags.bytewidth)
|
||||
self.PrependUOffsetTRelative(rootTable)
|
||||
self.finished = True
|
||||
return self.Head()
|
||||
|
||||
def Prepend(self, flags, off):
|
||||
@@ -470,7 +488,7 @@ class Builder(object):
|
||||
|
||||
N.enforce_number(d, N.UOffsetTFlags)
|
||||
if x != d:
|
||||
self.assertNested(x)
|
||||
self.assertStructIsInline(x)
|
||||
self.Slot(v)
|
||||
|
||||
def PrependBool(self, x): self.Prepend(N.BoolFlags, x)
|
||||
|
||||
Reference in New Issue
Block a user