How to store text and bytes¶
Text and bytes vary in length, but every field of a
box has a fixed size. So when you declare a str or bytes
field, you also say how much room it gets, its capacity:
the most bytes it can hold. This guide shows how to choose it and what
happens when a value doesn't fit.
1. Declare the field with a capacity¶
Wrap the type in Annotated and add a Capacity:
from typing import Annotated
from sharedbox import Capacity, SharedBox
class Camera(SharedBox, name="example-camera"):
label: Annotated[str, Capacity(16)] = ""
frame: Annotated[bytes, Capacity(1024)] = b""
2. Write and read values¶
From then on you use the field like any other attribute:
camera = Camera()
camera.label = "front"
camera.frame = bytes(range(8))
print(camera.label, len(camera.frame)) # front 8
You can also assign a bytearray or a memoryview to a bytes field;
reading it back always gives you bytes.
3. Handle a value that is too long¶
If you assign a value longer than the capacity, you get a ValueError and
the field keeps its old value, so a reader never sees half of it:
try:
camera.label = "front camera, left side"
except ValueError as error:
print(error) # Camera.label holds at most 16 bytes; the value encodes to 23
print(camera.label) # front
4. Count bytes, not characters¶
The capacity counts bytes of the UTF-8 encoding, not characters, and a character takes between 1 and 4 bytes. Text with accents or other scripts fills the room faster than it looks:
To be sure n characters of any language fit, give the field a capacity of
4 * n. If you know the text is plain ASCII, n is enough.
5. Cut a value to fit¶
Sometimes you'd rather keep the start of a long text than refuse it. Cut its encoding to the capacity, and drop any character the cut split in two, as this helper does:
def fit(text: str, size: int) -> str:
return text.encode()[:size].decode(errors="ignore")
camera.label = fit("front camera, left side", 16)
print(camera.label) # front camera, le
The whole script
"""The script of the guide "How to store text and bytes"."""
from typing import Annotated
from sharedbox import Capacity, SharedBox
class Camera(SharedBox, name="example-camera"):
label: Annotated[str, Capacity(16)] = ""
frame: Annotated[bytes, Capacity(1024)] = b""
camera = Camera()
camera.label = "front"
camera.frame = bytes(range(8))
print(camera.label, len(camera.frame)) # front 8
try:
camera.label = "front camera, left side"
except ValueError as error:
print(error) # Camera.label holds at most 16 bytes; the value encodes to 23
print(camera.label) # front
print(len("été"), len("été".encode())) # 3 5
def fit(text: str, size: int) -> str:
return text.encode()[:size].decode(errors="ignore")
camera.label = fit("front camera, left side", 16)
print(camera.label) # front camera, le
camera.close()
Camera.unlink()