Skip to main content
Markdown

Generated Message API

Generated Protobuf classes expose typed fields and collections alongside parsing, serialization, copying, and presence operations. This reference describes their behavior, including how child objects remain attached to a parent during mutation. Start with the Protobuf guide to generate your first module.

The collection example uses downloadable fixture.proto and common.proto schemas. This is the generated class API, rather than the runtime reflection API.

Message Operations​

A new message starts with its schema's defaults. You can populate its fields before calling to_bytes(), or use from_bytes() to construct it from wire data. For an existing object, parse() replaces its contents only if parsing succeeds.

OperationContract
MessageType()Empty/default-initialized message; required fields may still be missing
MessageType.from_bytes(data: bytes)Parse and validate a new message
message.parse(data: bytes)Replace only on success; failure preserves values and live attachments
message.to_bytes() -> bytesValidate required fields/depth and encode deterministically
message.has(field: str or int) -> boolPresence by original schema name or field number
message.clear(field: str or int)Reset field/default and detach old live values
message.clone()Detached deep copy
message.CopyFrom(other: MessageType)Checked deep replacement; message types must match
message.SetInParent()Mark this child present without assigning a field
message.close()Clear/invalidate a root or detached message; rejects attached children
with message as entered:Check on entry and close on exit

Messages are owned by Codon's garbage collector, so close() is not required to free a foreign message handle. It is an explicit way to clear and invalidate an object. Closing a parent detaches any children or collection views you retained; those objects remain usable on their own.

Operations on a closed message raise ValueError, as do invalid wire input, missing required fields, unsupported presence checks, out-of-range values, and invalid helper indices. Collection indexing and deletion use the usual IndexError and KeyError. Shared messages need external synchronization during mutation.

Wire Compatibility​

Generated messages support scalar types, packed and unpacked repeated fields, maps, proto2 defaults and required fields, proto3 optional fields, and oneofs. Strings must be valid UTF-8; bytes fields accept arbitrary data.

Repeated occurrences of a singular scalar take the last value. Nested message occurrences merge, with required fields checked after parsing the complete message. Unknown fields survive parsing and serialization.

Open enums preserve unknown numeric values. Closed-enum setters reject them, while parsing retains unknown wire values as unknown fields, including Protobuf's duplicate-map-value rules. Serialization orders known fields by number and maps by key. This is deterministic, not a canonical encoding shared by all runtimes.

FeatureSupport
Maximum encoded buffer2 GiB minus one byte
Maximum nesting depth100
Editions, extensions, groupsNot supported
RPC/gRPC, JSON and text APIsNot generated
Keyword-field constructors and keyword arguments to add()Not supported
Well-known typesOrdinary generated messages, without specialized helpers

Fields and Numeric Types​

Read and assign a singular field with message.field and message.field = value. The generated type reflects its Protobuf width: an int32 field returns i32, for example. Setters also accept selected wider types and check their range before conversion.

Schema typeNative storage/getterSetter input
int32, sint32, sfixed32, enumi32i32 or range-checked int
uint32, fixed32u32u32 or range-checked u64
int64, sint64, sfixed64intint
uint64, fixed64u64u64
floatfloat32float32 or float
doublefloatfloat
string, bytes, boolstr, bytes, boolSame native type
MessageTyped live childSame message type, copied on assignment

The method forms, get_field() and set_field(value), are equivalent to attribute access for singular fields. Repeated fields additionally offer add_field(value) and set_field(index, value), and maps offer put_field(key, value). These helpers perform the same range and copying checks; their repeated-field indices must be nonnegative. Enum declarations provide i32 constants.

When a field name conflicts with a keyword, builtin, dunder name, or generated member, its property gains a trailing underscore. A schema field named class is therefore accessed as class_; a field named clear becomes clear_. Name-based operations still use the schema's original name, as in message.has("class"), and the accessor is get_class(). The generated module shows the final spelling if additional collisions require more underscores.

Repeated Views​

Repeated fields behave like typed, mutable sequences. Both the property and get_field() return the same live view, so appending through either updates the message. Indexing supports negative values. A slice creates a new list, but any message elements in that list are still the original live children.

MemberBehavior
len(view), bool(view), iteration, view[index]Inspect live elements
view[start:stop:step]Native list of selected elements
append(value), extend(values), insert(index, value)Validate inputs; message values are copied
add()Create/return attached message element; unavailable for scalars
view[index] = value, view[slice] = valuesScalar-only assignment with conversion/range checks
del view[index], del view[slice]Remove and detach removed children
pop(index=-1)Remove/return value; message becomes detached
remove(value), count(value)Native value comparison
index(value, start=0, stop=9223372036854775807)Find matching value or raise ValueError
clear(), reverse(), sort(key=None, reverse=False)Mutate collection; custom keys are useful for messages
copy()Native list, deeply copying message elements
Equality / representationNative container semantics with compatible element types

To replace the contents of an existing repeated child, call view[index].CopyFrom(other). Direct assignment to a message element or message slice is not supported. For operations such as sorting, provide a key based on the fields you want to compare; generated messages do not implement Python's complete message-comparison API.

Map Views​

Maps expose typed key/value collections. One behavior differs from an ordinary dictionary: looking up a missing key with view[key] inserts its default value. Use get() or key in view to inspect a map without changing it.

MemberBehavior
len(view), bool(view), iteration, key in viewInspect keys; membership does not insert
view[key], get_or_create(key)Insert default scalar/empty child when missing
get(key, default=None)Optional value without insertion
view[key] = valueScalar-valued maps only; validates key and value
del view[key], pop(key), pop(key, default), popitem()Remove/detach and return values as applicable
update(mapping), setdefault(key, default)Scalar-valued maps only
clear()Empty map and detach children
keys(), values(), items()Native dictionary views
copy()Native dict, deeply copying message values
Equality / representationCompatible native dictionary semantics

For message-valued maps, mutate the child returned by view[key], or copy another message into it with CopyFrom(). The generated put_field(key, value) helper also supports copying a message into a map. Unlike indexing, a missing get() result does not create an entry or mark the field present.

Whole-Collection Assignment​

You can replace an entire repeated field or map with a native list or dictionary. Here the fields use Protobuf's 32-bit integers, so the collection literals use i32 elements:

Source
from fixture_pb import Fixture

message = Fixture()
message.signed32s = [i32(-1), i32(2)]
message.counts = {"visits": i32(3)}
message.signed32s.append(4)
assert message.counts["visits"] == i32(3)

Save both schemas in the current directory, then generate their modules:

Terminal
exaloop protoc -I. --codon_out=. common.proto fixture.proto
exaloop --with data run -release program.codon

The Codon example above belongs in program.codon. Assignment validates and copies all elements before replacing the field, so an invalid value leaves the old collection unchanged. Assigning [] or {} clears the field. To assign another message's collection, use other_view.copy() to obtain a native container.

Retained Children​

Clearing or replacing a field detaches its old children and views. A retained reference remains a valid object, but later changes to it no longer affect the parent. Selecting another oneof member and successfully parsing replacement data have the same effect. An unset child that has only been read remains eligible to become present on its first mutation.

All of these rules are maintained by the public attributes and methods. Private storage such as _f<number>, ownership state, and codec hints is not part of the message API.