Skip to content

How to migrate from 0.7

Code written for sharedbox 0.7 needs two changes before it runs on the next release: a Shape now takes its dimensions as one tuple, and a box field can no longer take the name of a metadata member, such as metadata. A third change concerns only type checkers: they refuse update(**values) for some dictionaries that 0.7 accepted. This guide shows the old and the new form of each.

Pass the dimensions of a Shape as a tuple

Shape used to take each dimension as its own argument. It now takes a single tuple, so a one-dimensional shape needs the trailing comma that makes a tuple of one element:

# 0.7
image: Annotated[numpy.ndarray, Shape(480, 640), DType("uint8")]
samples: Annotated[numpy.ndarray, Shape(1024), DType("float32")]

# 0.8
image: Annotated[numpy.ndarray, Shape((480, 640)), DType("uint8")]
samples: Annotated[numpy.ndarray, Shape((1024,)), DType("float32")]

The old form fails as the class is defined: Shape(480, 640) raises TypeError because it passes two arguments, and Shape(1024) raises ValueError because 1024 is not a tuple. Code that builds the dimensions at run time passes the tuple it already has, Shape(dims), in place of Shape(*dims).

Rename a field called metadata

A box now has metadata of its own, which it reads through three new members: metadata, metadata_version and metadata_version_of. A field or an InitVar can no longer take one of those names, so give it another:

# 0.7
class Parcel(SharedBox):
    metadata: Annotated[str, Capacity(64)] = ""

# 0.8
class Parcel(SharedBox):
    label: Annotated[str, Capacity(64)] = ""

The old form fails as the class is defined, with a TypeError that names the field. Rename it wherever your code reads or assigns it as well, since parcel.metadata now gives the box's metadata, usually None, and assigning to it raises AttributeError. The field's name is part of the schema hash, so a process that still runs the old class cannot attach to a box the new class created: update every process that opens the box together.

Type a dictionary passed to update as dict[str, Any]

update now has a keyword metadata besides the field names it takes. A type checker cannot tell which keys a dict holds, so it assumes that update(**values) may fill metadata, and it refuses the call when the values have a type the box's metadata type does not accept, such as str. Type the dictionary as dict[str, Any], or cast it where you pass it:

# 0.7
def restock(parcel: Parcel, values: dict[str, str]) -> None:
    parcel.update(**values)

# 0.8
def restock(parcel: Parcel, values: dict[str, Any]) -> None:
    parcel.update(**values)

def restock_cast(parcel: Parcel, values: dict[str, str]) -> None:
    parcel.update(**cast("dict[str, Any]", values))

Nothing changes when the program runs.