Callable Serialization Example

This example demonstrates serializing and deserializing callable objects (functions, methods, and lambda expressions).

  1"""
  2Callable serialization example for Serilux.
  3
  4This example demonstrates:
  5- Serializing and deserializing functions
  6- Serializing and deserializing methods
  7- Serializing lambda expressions
  8- Using ObjectRegistry for method deserialization
  9"""
 10
 11from serilux import (
 12    ObjectRegistry,
 13    Serializable,
 14    deserialize_callable,
 15    deserialize_lambda_expression,
 16    register_serializable,
 17    serialize_callable,
 18    serialize_callable_with_fallback,
 19)
 20
 21
 22# Example 1: Serializing module-level functions
 23def process_data(data):
 24    """A simple processing function."""
 25    return data.upper()
 26
 27
 28def filter_high_priority(item):
 29    """Filter function for high priority items."""
 30    return item.get("priority") == "high"
 31
 32
 33# Example 2: Serializable class with callable fields
 34@register_serializable
 35class DataProcessor(Serializable):
 36    """A processor with a callable handler."""
 37
 38    def __init__(self):
 39        super().__init__()
 40        self.name = ""
 41        self.handler = None  # Will store a function
 42        self.add_serializable_fields(["name", "handler"])
 43
 44
 45@register_serializable
 46class ConditionalRouter(Serializable):
 47    """A router with a condition function."""
 48
 49    def __init__(self):
 50        super().__init__()
 51        self._id = None
 52        self.name = ""
 53        self.condition = None  # Will store a lambda or function
 54        self.add_serializable_fields(["name", "condition"])
 55
 56    def route(self, data):
 57        """Route data based on condition."""
 58        if self.condition and self.condition(data):
 59            return "route_a"
 60        return "route_b"
 61
 62
 63def main():
 64    """Run the callable serialization example."""
 65    print("=== Serilux Callable Serialization Example ===\n")
 66
 67    # Example 1: Serialize and deserialize a module function
 68    print("1. Serializing module-level function...")
 69    serialized_func = serialize_callable(process_data)
 70    print(f"   Serialized: {serialized_func}\n")
 71
 72    # Deserialize the function
 73    deserialized_func = deserialize_callable(serialized_func)
 74    print(f"   Deserialized function works: {deserialized_func('hello') == 'HELLO'}\n")
 75
 76    # Example 2: Serialize a function in a Serializable object
 77    print("2. Serializing function in Serializable object...")
 78    processor = DataProcessor()
 79    processor.name = "Uppercase Processor"
 80    processor.handler = process_data
 81
 82    data = processor.serialize()
 83    print(f"   Processor data: {data}\n")
 84
 85    # Deserialize
 86    new_processor = DataProcessor()
 87    new_processor.deserialize(data)
 88    print(f"   Handler works: {new_processor.handler('test') == 'TEST'}\n")
 89
 90    # Example 3: Serialize lambda expression
 91    print("3. Serializing lambda expression...")
 92    condition = lambda x: x.get("priority") == "high"
 93    serialized_lambda = serialize_callable_with_fallback(condition)
 94    print(f"   Serialized lambda: {serialized_lambda}\n")
 95
 96    # Deserialize lambda
 97    deserialized_lambda = deserialize_lambda_expression(serialized_lambda)
 98    test_data = {"priority": "high"}
 99    print(f"   Lambda works: {deserialized_lambda(test_data) is True}\n")
100
101    # Example 4: Serialize lambda expression in Serializable object
102    # Note: To ensure lambda is serialized as lambda_expression (not as function),
103    # we use serialize_callable_with_fallback and store the serialized data directly.
104    # This is useful when you want to preserve the lambda expression for deserialization.
105    print("4. Serializing lambda expression in Serializable object...")
106    router = ConditionalRouter()
107    router._id = "router1"
108    router.name = "Priority Router"
109
110    # Create lambda and serialize it as expression using fallback
111    condition_lambda = lambda x: x.get("priority") == "high"
112    condition_serialized = serialize_callable_with_fallback(condition_lambda)
113    # Store the serialized data directly (this ensures lambda_expression format)
114    router.condition = condition_serialized
115
116    router_data = router.serialize()
117    print(f"   Router data: {router_data}\n")
118
119    # Check if condition was serialized as lambda_expression
120    condition_data = router_data.get("condition", {})
121    if condition_data.get("_type") == "lambda_expression":
122        print("   ✓ Condition serialized as lambda_expression\n")
123    else:
124        print(f"   Note: Condition serialized as {condition_data.get('_type', 'unknown')}\n")
125
126    # Deserialize with registry
127    new_router = ConditionalRouter()
128    registry = ObjectRegistry()
129    registry.register(new_router, object_id="router1")
130    new_router.deserialize(router_data, registry=registry)
131
132    # The condition should be deserialized as a callable
133    if callable(new_router.condition):
134        test_item = {"priority": "high"}
135        result = new_router.route(test_item)
136        print(f"   Router works: {result == 'route_a'}\n")
137    else:
138        print("   ⚠ Condition not deserialized as callable\n")
139
140    # Example 5: Serialize method with ObjectRegistry
141    print("5. Serializing method with ObjectRegistry...")
142
143    @register_serializable
144    class Handler(Serializable):
145        def __init__(self):
146            super().__init__()
147            self._id = "handler1"
148            self.name = ""
149            self.process = self.process_data
150            self.add_serializable_fields(["name", "process"])
151
152        def process_data(self, data):
153            return data.upper()
154
155    handler = Handler()
156    handler.name = "Uppercase Handler"
157    handler_data = handler.serialize()
158    print(f"   Handler data: {handler_data}\n")
159
160    # Deserialize with registry
161    new_handler = Handler()
162    registry2 = ObjectRegistry()
163    registry2.register(new_handler, object_id="handler1")
164    new_handler.deserialize(handler_data, registry=registry2)
165
166    print(f"   Method works: {new_handler.process('test') == 'TEST'}\n")
167
168    print("✓ All callable serialization examples successful!")
169
170
171if __name__ == "__main__":
172    main()