Protocol Buffers Playground: Compila y prueba Protobufs en el navegador
Trabajar con Protocol Buffers generalmente significa configurar protoc, plugins específicos del lenguaje y pipelines de construcción. ¿Qué pasaría si pudieras simplemente subir un archivo .proto e inmediatamente comenzar a serializar y deserializar mensajes?
Hoy te mostraremos cómo usar unsandbox como un playground de protobuf - perfecto para aprender, depurar y hacer prototipos rápidos.
La configuración: No se requiere instalación
Los contenedores de unsandbox vienen con:
-
protoc3.21.12 (compilador de protobuf) -
Biblioteca Python
protobuf -
Gema Ruby
google-protobuf - Soporte de protobuf para Go
- Soporte completo de archivos de entrada
Esto significa que puedes subir un esquema .proto e inmediatamente trabajar con él - sin necesidad de instalación local.
Ejemplo 1: Tu primer Protobuf en Python
Definamos un mensaje simple Person y serialicémoslo:
person.proto:
syntax = "proto3";
message Person {
string name = 1;
int32 age = 2;
}
Solicitud de API:
{
"language": "python",
"code": "import subprocess, sys, os\nos.chdir('/tmp/input')\nsubprocess.run(['protoc', '--python_out=.', 'person.proto'], check=True)\nsys.path.insert(0, '.')\nimport person_pb2\n\n# Create a Person message\nperson = person_pb2.Person(name='Alice', age=30)\nprint(f'Created: {person}')\n\n# Serialize to binary\ndata = person.SerializeToString()\nprint(f'Serialized ({len(data)} bytes): {data.hex()}')\n\n# Deserialize back\nperson2 = person_pb2.Person()\nperson2.ParseFromString(data)\nprint(f'Deserialized: name={person2.name}, age={person2.age}')",
"input_files": [
{"filename": "person.proto", "content": "c3ludGF4ID0gInByb3RvMyI7CgptZXNzYWdlIFBlcnNvbiB7CiAgc3RyaW5nIG5hbWUgPSAxOwogIGludDMyIGFnZSA9IDI7Cn0K"}
]
}
Salida:
Created: name: "Alice"
age: 30
Serialized (9 bytes): 0a05416c696365101e
Deserialized: name=Alice, age=30
¿Qué acaba de pasar:
-
Subimos
person.protocomo base64 -
protoclo compiló aperson_pb2.py - Creamos un mensaje Person
-
Lo serializamos a 9 bytes:
0a05416c696365101e - Lo deserializamos de nuevo
Ejemplo 2: Decodificar Binary Protobuf desconocido
¿Tienes un archivo .bin misterioso y su esquema? Sube ambos y decodifica:
{
"language": "python",
"code": "import subprocess, sys, os\nos.chdir('/tmp/input')\nsubprocess.run(['protoc', '--python_out=.', 'person.proto'], check=True)\nsys.path.insert(0, '.')\nimport person_pb2\n\n# Read the binary protobuf data\nwith open('data.bin', 'rb') as f:\n data = f.read()\nprint(f'Binary data: {data.hex()}')\n\n# Decode it\nperson = person_pb2.Person()\nperson.ParseFromString(data)\nprint(f'Decoded: name={person.name}, age={person.age}')",
"input_files": [
{"filename": "person.proto", "content": "c3ludGF4ID0gInByb3RvMyI7CgptZXNzYWdlIFBlcnNvbiB7CiAgc3RyaW5nIG5hbWUgPSAxOwogIGludDMyIGFnZSA9IDI7Cn0K"},
{"filename": "data.bin", "content": "CgVBbGljZRAe"}
]
}
Salida:
Binary data: 0a05416c696365101e
Decoded: name=Alice, age=30
¡Esto es perfecto para depurar - pega tu volcado hexadecimal, conviértelo a base64 y mira qué hay dentro!
Ejemplo 3: Mensajes anidados y campos repetidos
Intentemos algo más complejo:
addressbook.proto:
syntax = "proto3";
message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
Código Python para rellenar y serializar:
import subprocess, sys, os
os.chdir('/tmp/input')
subprocess.run(['protoc', '--python_out=.', 'addressbook.proto'], check=True)
sys.path.insert(0, '.')
import addressbook_pb2
# Create an address book
book = addressbook_pb2.AddressBook()
# Add first person
alice = book.people.add()
alice.name = "Alice"
alice.id = 1234
alice.email = "alice@example.com"
phone = alice.phones.add()
phone.number = "555-1234"
phone.type = addressbook_pb2.Person.MOBILE
# Add second person
bob = book.people.add()
bob.name = "Bob"
bob.id = 5678
bob.email = "bob@example.com"
phone = bob.phones.add()
phone.number = "555-5678"
phone.type = addressbook_pb2.Person.WORK
# Serialize
data = book.SerializeToString()
print(f"Address book ({len(data)} bytes):")
print(f"Hex: {data.hex()}")
# Show the text format
print("\nText format:")
print(book)
Salida:
Address book (62 bytes):
Hex: 0a1e0a05416c696365...
Text format:
people {
name: "Alice"
id: 1234
email: "alice@example.com"
phones {
number: "555-1234"
}
}
people {
name: "Bob"
id: 5678
email: "bob@example.com"
phones {
number: "555-5678"
type: WORK
}
}
Ejemplo 4: Inspección del formato Wire en bruto
¿Quieres entender el formato wire de protobuf? Decodifiquémoslo byte por byte:
import subprocess, sys, os
os.chdir('/tmp/input')
subprocess.run(['protoc', '--python_out=.', 'person.proto'], check=True)
sys.path.insert(0, '.')
import person_pb2
person = person_pb2.Person(name="Alice", age=30)
data = person.SerializeToString()
print("Wire format breakdown:")
print(f"Raw bytes: {data.hex()}")
print()
# Decode wire format manually
i = 0
while i < len(data):
tag_byte = data[i]
field_number = tag_byte >> 3
wire_type = tag_byte & 0x07
wire_type_names = {0: 'varint', 1: '64-bit', 2: 'length-delimited', 5: '32-bit'}
print(f"Field {field_number}, Wire type {wire_type} ({wire_type_names.get(wire_type, 'unknown')})")
i += 1
if wire_type == 0: # Varint
value = 0
shift = 0
while data[i] & 0x80:
value |= (data[i] & 0x7f) << shift
shift += 7
i += 1
value |= data[i] << shift
i += 1
print(f" Value: {value}")
elif wire_type == 2: # Length-delimited
length = data[i]
i += 1
content = data[i:i+length]
i += length
print(f" Length: {length}, Content: {content} ({content.hex()})")
Salida:
Wire format breakdown:
Raw bytes: 0a05416c696365101e
Field 1, Wire type 2 (length-delimited)
Length: 5, Content: b'Alice' (416c696365)
Field 2, Wire type 0 (varint)
Value: 30
¡Ahora puedes ver exactamente cómo protobuf codifica los datos!
Ejemplo 5: Prueba de evolución de esquema
Protobuf es famoso por la compatibilidad hacia atrás/adelante. Pruébalo:
v1.proto (original):
syntax = "proto3";
package v1;
message User {
string name = 1;
int32 age = 2;
}
v2.proto (nuevo campo añadido):
syntax = "proto3";
package v2;
message User {
string name = 1;
int32 age = 2;
string email = 3; // New field!
}
Probar compatibilidad hacia adelante:
import subprocess, sys, os
os.chdir('/tmp/input')
# Compile both versions
subprocess.run(['protoc', '--python_out=.', 'v1.proto'], check=True)
subprocess.run(['protoc', '--python_out=.', 'v2.proto'], check=True)
sys.path.insert(0, '.')
import v1_pb2
import v2_pb2
# Create v2 message with new field
user_v2 = v2_pb2.User(name="Alice", age=30, email="alice@example.com")
data = user_v2.SerializeToString()
print(f"v2 serialized: {data.hex()}")
# Read with v1 (old code reading new data)
user_v1 = v1_pb2.User()
user_v1.ParseFromString(data)
print(f"v1 reads: name={user_v1.name}, age={user_v1.age}")
print("Email field? v1 ignores unknown fields - forward compatible!")
Referencia rápida: Codificación Base64 de tus archivos Proto
Para subir archivos, necesitas base64. Aquí está cómo:
Linux/Mac:
base64 -w 0 person.proto
Python:
import base64
with open('person.proto', 'rb') as f:
print(base64.b64encode(f.read()).decode())
JavaScript:
const content = "syntax = \"proto3\";\n\nmessage Person {\n string name = 1;\n int32 age = 2;\n}\n";
console.log(btoa(content));
Casos de uso
1. Aprender Protobufs
Sáltate la molestia de la instalación. Solo escribe un .proto, súbelo y experimenta.
2. Depurar formato Wire
¿Tienes un volcado hexadecimal de una captura de red? Pégalo, decodifícalo, entiéndelo.
3. Validación de esquema
Prueba si tu esquema compila antes de hacer commit. Verifica que los números de campo no entren en conflicto.
4. Pruebas entre lenguajes
Serializa en Python, deserializa en Go. Verifica que tus mensajes funcionen entre lenguajes.
5. Documentación de API
Muestra ejemplos funcionales de tus mensajes protobuf con salida serializada real.
Límites
- Tamaño de archivo: Máx. 5MB por archivo (suficiente para esquemas)
- Tiempo de ejecución: Tiempo de espera de 30 segundos
- Versión de protoc: 3.21.12 (proto3 totalmente compatible)
Pruébalo ahora
- Ve a unsandbox.com
- Selecciona Python
- Pega tu código protobuf
-
Sube tu archivo
.proto(o usa el parámetro input_files en base64) - ¡Ejecuta!
Sin instalación. Sin pipeline de construcción. Solo protobufs.
¡Feliz serialización!