Skip to content

Decoder Rules

mosaicolabs.bridges.mcap.decoders.rule

FieldStep dataclass

FieldStep(name)

Descend into container[name] — a struct field lookup (a decoded protobuf submessage dict, keyed by field name).

ListStep dataclass

ListStep()

Fan out over every element of the list found at this position (a decoded repeated field). As the last step, the list itself is the container and each index is the key (the rule's callback runs once per element); otherwise the remaining steps are resolved independently against each element.

MapValuesStep dataclass

MapValuesStep()

Fan out over every value of the map dict found at this position (a decoded protobuf map<K, V> field). Unlike FieldStep, does not look up one named key — a map's keys are arbitrary data, not schema-known field names. As the last step, the map dict itself is the container and each key addresses one value; otherwise the remaining steps are resolved independently against each value.

Rule dataclass

Rule(predicate, callback)

Bases: Generic[T]

A single postprocessing rule, applied by MCAPMsgDecoderBase._postprocess() to fix up one specific field of a decoded message dict so it complies with the target PyArrow schema.

A Rule is used in two stages:

  1. Schema inspection, during register_schema(): an encoding-specific matcher (e.g. ProtobufRulesMatcher.from_descriptor) walks the schema and, for every field whose schema node satisfies predicate (checked via is_respected), associates that field's RulePath with this Rule. The result is a Dict[RulePath, List[Rule]] (MCAPMsgDecoderBase._field_rule_mapper) mapping each field path to every Rule matched for it — a field can satisfy more than one predicate (e.g. a Timestamp field that is also a oneof member), in which case all of them apply, in PROTOBUF_RULES order.
  2. Postprocessing, during decode(): MCAPMsgDecoderBase._postprocess() iterates that mapping and calls rule.apply(data, path) for every (path, rule) pair, so each callback runs only on the specific field path its rule was matched against — never on the rest of the decoded dict. If a field is absent and callback raises KeyError to signal that, apply() logs it and moves on, so a sibling rule registered for the same path (e.g. a _fill_absent_field fallback) still gets to run. A field that is present but None (e.g. already filled in by _fill_absent_field) is left untouched by every transforming callback.

is_respected

is_respected(node)

Whether this rule's predicate matches node (a schema node, e.g. a protobuf FieldDescriptor), meaning this rule should be registered for that field.

apply

apply(data, path)

Resolves path against data, fanning out at every ListStep/MapValuesStep, and invokes callback(container, key) once per (container, key) pair path resolves to. This is what MCAPMsgDecoderBase._postprocess() calls for every rule registered against a given field path.

callback may raise KeyError to signal that the field it needs is absent (e.g. _coerce_int64_field on a field MessageToDict omitted); that's caught and logged at debug level rather than propagated, so it never aborts postprocessing of the rest of data. A field that is present but None is not an error: callback leaves it untouched.

match_rules

match_rules(node, rules)

Returns every rule in rules whose predicate matches node, in the same relative order as rules. Returns None, not an empty list, if no rule applies.