Python Reference

Type Mappings

IDL Type Python Type Example
string str "hello"
int int 42
float float 3.14
bool bool True, False
[]Type list [1, 2, 3]
map[string]Type dict {"key": "value"}
Enum str "pending"
Struct dict {"field": "value"}
T [optional] None or type None or value

Structs as Dictionaries

Each struct in your IDL becomes a dictionary in Python:

from checkout import Product, Cart, CartItem

# Create instances using dicts
product = {
    "productId": "prod001",
    "name": "Wireless Mouse",
    "description": "Ergonomic mouse",
    "price": 29.99,
    "stock": 50,
    "imageUrl": "https://example.com/mouse.jpg"  # optional field
}

cart = {
    "cartId": "cart_1234",
    "items": [],
    "subtotal": 0.0
}

Optional Fields

Optional fields can be None:

# Define with optional field
product = {
    "productId": "prod001",
    "name": "Wireless Mouse",
    "description": "Ergonomic mouse",
    "price": 29.99,
    "stock": 50,
    "imageUrl": None  # optional field can be None
}

# Check for optional field
if product.get("imageUrl"):
    print(product["imageUrl"])

Error Handling

Throw RPCError with custom codes:

from pulserpc import RPCError

# Standard JSON-RPC errors
throw RPCError(-32602, "Invalid params")

# Custom application errors (use codes >= 1000)
throw RPCError(1001, "CartNotFound: Cart does not exist")
throw RPCError(1002, "CartEmpty: Cannot create order from empty cart")

Common error codes:

Server Implementation

Extend generated service classes:

from server import PulseRPCServer, CatalogService

class CatalogServiceImpl(CatalogService):
    def listProducts(self):
        # Return list of Product dicts
        return [
            {"productId": "p1", "name": "Item 1", "price": 10.0, "stock": 5},
            {"productId": "p2", "name": "Item 2", "price": 20.0, "stock": 3}
        ]

    def getProduct(self, productId):
        # Return None for optional return type
        for p in products:
            if p["productId"] == productId:
                return p
        return None

# Start server
server = PulseRPCServer(host="0.0.0.0", port=8080)
server.register("CatalogService", CatalogServiceImpl())
server.serve_forever()

Client Usage

from client import HTTPTransport, CatalogServiceClient

transport = HTTPTransport("http://localhost:8080")
catalog = CatalogServiceClient(transport)

# Method calls return Python dicts or None
products = catalog.listProducts()
for product in products:
    print(f"{product['name']}: ${product['price']}")

# Optional methods return None if not found
product = catalog.getProduct("prod001")
if product:
    print(product['name'])

Validation

PulseRPC automatically validates:

# This will raise RPCError (-32602) if validation fails
cart = cart.addToCart({
    'productId': 'prod001',
    'quantity': 2
})

Best Practices

  1. Use dicts for struct values: All struct values should be dictionaries
  2. Handle None for optionals: Always check if optional return values are None
  3. Use descriptive error codes: Custom errors should have codes >= 1000
  4. Validate early: Let PulseRPC validate input, validate business logic in handlers
  5. Keep state in handlers: Store in-memory state in service implementation classes

Working with Nested Structs

# Nested structs work naturally as dicts
order = {
    "orderId": "order_123",
    "cart": {
        "cartId": "cart_123",
        "items": [...],
        "subtotal": 59.98
    },
    "shippingAddress": {
        "street": "123 Main St",
        "city": "San Francisco",
        "state": "CA",
        "zipCode": "94105",
        "country": "USA"
    },
    "paymentMethod": "credit_card",
    "status": "pending",
    "total": 59.98,
    "createdAt": 1642000000
}