RabbitMQ’s CC/BCC headers needed actual array values. Encoding a Python list as a JSON string would not give the broker the same input. The Python binding needed to translate nested AMQP tables and arrays into RabbitMQ’s C types and back. I submitted a librabbitmq patch for that translation. It was closed without merging.
The core was mutual recursion. A Python dictionary became an amqp_table_t; a list became an amqp_array_t. Either container could hold the other, so each encoder called back into the corresponding container encoder as needed. An array-valued entry explicitly carried both its kind and its union member:
entry->kind = AMQP_FIELD_KIND_ARRAY;
entry->value.array = value;The incoming path mirrored that structure, with separate table-to-dictionary and array-to-list functions. Its switch handled the AMQP numeric kinds, strings, tables, and arrays. Unknown incoming kinds became None rather than an invented representation.
The outgoing path was narrower. It accepted nested dictionaries and lists, integers, and strings through the existing conversion conventions. It did not symmetrically implement every primitive its decoder understood. Unsupported outgoing values raised ValueError, including a key or index to locate the problem.
Storage for the C entries came from the connection’s frame pool. Python objects had their own reference ownership, including the list API’s ownership transfer when a decoded value was installed. Crossing the boundary meant tracking shape, field kind, allocation lifetime, and Python references at the same time.