How to use cucumber

cucumber is a module that handles serialization and deserialization of Python objects.

Basically any object in Python will work, and it covers more types than pickle, cloudpickle, and dill combined.

To see a list of supported types, see the supported types page.

To see how cucumber stacks up against other serialization libraries, see the performance page.

cucumber is mainly meant for internal, cross-process communication, not for external or cross-language serialization.

However, you can convert the intermediate representation (IR) to JSON, and you are welcome to use that to send data to other languages using your own tools.

Importing

from suitkaise import cucumber
from suitkaise.cucumber import serialize, deserialize, serialize_ir, deserialize_ir, ir_to_jsonable, ir_to_json, to_jsonable, to_json, reconnect_all

serialize()

Serializes any Python object to bytes using cucumber's intermediate representation (IR).

data = cucumber.serialize(obj)

Arguments

obj: Any Python object

debug: Enables deep error context and path reporting.

verbose: Prints progress and handler selection information.

Returns

bytes: cucumber IR as bytes.

Raises

SerializationError: If serialization fails.

This is a SerializationError that appears when you try to serialize a suitkaise.processing.Pipe.Anchor point.

Traceback (most recent call last):
  File "my_app.py", line 42, in <module>
    payload = cucumber.serialize(obj)
  File ".../suitkaise/cucumber/api.py", line 90, in serialize
    return _default_serializer.serialize(obj)
  File ".../suitkaise/cucumber/_int/serializer.py", line 113, in serialize
    raise SerializationError(message)
suitkaise.cucumber._int.serializer.SerializationError:
======================================================================
HANDLER FAILED
======================================================================
Path: Anchor
Handler: ClassInstanceHandler
Object: Anchor

The handler failed to extract state from this object.

Error: Locked pipe endpoint cannot be serialized. Keep it in the parent process.
======================================================================

debug and verbose

Note: debug and verbose are keyword-only arguments.

When debug=True, cucumber shows you where it failed to serialize an object.

======================================================================
HANDLER FAILED
======================================================================
Path: Anchor@1234567890
Handler: ClassInstanceHandler
Object: Anchor

The handler failed to extract state from this object.

Error: Locked pipe endpoint cannot be serialized. Keep it in the parent process.
======================================================================

When verbose=True, cucumber prints the path it's taking through your object, color-coded by depth.

[CUCUMBER] Starting serialization of dict
  [1] dict
    [2] dict → Widget
        ↳ Handler: ClassInstanceHandler
      [3] dict → Widget → dict
        [4] dict → Widget → dict → dict
          [5] dict → Widget → dict → dict → list
          [5] dict → Widget → dict → dict → dict
        [4] dict → Widget → dict → dict
          [5] dict → Widget → dict → dict → tuple
    [2] dict → dict
      [3] dict → dict → list
    [2] dict → list
      [3] dict → list → dict
        [4] dict → list → dict → set
      [3] dict → list → tuple
[CUCUMBER] Built IR successfully, size: 1464 chars
[CUCUMBER] Serialization complete, bytes: 760

[5] dict → Widget → dict → dict → tuple colors:

Colors will only display if your terminal supports color.

deserialize()

Reconstructs a Python object from bytes created by cucumber.serialize.

Will not work if the object was serialized with pickle, cloudpickle, or dill, as these libraries do not use cucumber's IR.

data = cucumber.serialize(obj)

restored = cucumber.deserialize(data)

Arguments

data: Serialized bytes from cucumber.serialize.

debug: Enables deep error context and path reporting.

verbose: Prints progress and handler selection information.

Returns

obj: Reconstructed Python object.

Raises

DeserializationError: If deserialization/reconstruction fails.

This is a DeserializationError that appears when you try to deserialize an object that is missing a required key.

Traceback (most recent call last):
  File "my_app.py", line 44, in <module>
    restored = cucumber.deserialize(data)
  File ".../suitkaise/cucumber/api.py", line 177, in deserialize
    return _default_deserializer.deserialize(data)
  File ".../suitkaise/cucumber/_int/deserializer.py", line 88, in deserialize
    raise DeserializationError(message)
suitkaise.cucumber._int.deserializer.DeserializationError:
======================================================================
DESERIALIZATION FAILED
======================================================================
Path: Payload -> 1 -> state
Handler: ClassInstanceHandler
Object: dict

The handler failed to reconstruct this object.

Error: Missing key 'state' for class reconstruction
======================================================================

reconnect_all() and Reconnectors

Reconnector objects are returned for certain types when you deserialize an object.

These objects are placeholders for certain objects that cannot be directly serialized and deserialized for various reasons.

Each Reconnector has a reconnect() method that creates a new live resource, using stored metadata. If the resource requires authentication, you must provide it again. We do not store secrets in the IR for security reasons.

For more information on each Reconnector, see the how it works page.

reconnect_all() allows you to reconnect all Reconnectors in an object at once.

Arguments

obj: Object or container to traverse for Reconnectors.

start_threads: Auto-start reconnected threads.

**auth: Mapping of type key to secrets (authentication).

Returns

obj: Object with all Reconnectors replaced by live resources.

Raises

**auth

**auth is a mapping of type key to secrets (authentication).

Type keys are the actual connection types (module.ClassName strings).

"psycopg2.Connection", "redis.Redis", ...

Each of these type keys maps to a dict.

auth = {
    "psycopg2.Connection": {
        "*": "default_psycopg2_password",
        "analytics_db": "analytics_password"  # used for obj.analytics_db specifically
    },
    "redis.Redis": {
        "*": "default_redis_password"
    }
}

restored = cucumber.deserialize(data)
restored = cucumber.reconnect_all(restored, start_threads=True, **auth)

If no auth is provided for a reconnector, reconnect() and reconnect_all() are still called.

This is fine in most cases unless you are using database connections that require authentication (a password, token, ...).

DbReconnector supported database types

IR, serialize_ir() and deserializing an IR

cucumber converts Python objects into an intermediate representation (IR) that is solely comprised of pickle native types. Then, pickle is used to serialize the IR to bytes.

{
    "__cucumber_type__": "class_instance",
    "__handler__": "ClassInstanceHandler",
    "__object_id__": 140123456789232,
    "module": "my_app.models",
    "class_name": "User",
    "state": {
        "id": 1,
        "name": "alice",
        "roles": ["admin", "editor"],
    }
}

serialize_ir()

Returns

the IR without converting to bytes.

Use this to inspect the IR of an object.

ir = cucumber.serialize_ir(obj)

Arguments

obj: Any Python object

debug: Enables deep error context and path reporting.

verbose: Prints progress and handler selection information.

Returns

A pickle native IR (nested dict/list structure)

Raises

SerializationError: If serialization fails.

Deserializing an IR

In order to deserialize an IR, you must first convert it to bytes using pickle.dumps().

Generally, when serializing to an ir, you are doing it to inspect it, or directly work with it.

ir = cucumber.serialize_ir(obj)
data = pickle.dumps(ir)

restored = cucumber.deserialize_ir(ir)

Raises

DeserializationError: If deserialization fails.

JSON conversion

cucumber provides 4 ways to convert an IR to JSON.

2 of them convert the IR to a JSON-serializable structure, and the other 2 convert the IR directly to a JSON string.

What is the difference between a JSON-serializable structure and a JSON string?

A JSON-serializable structure is a Python dict/list tree that only uses JSON-safe types (dict, list, str, int, float, bool, None).

A JSON string is the final serialized text output (the result of json.dumps()). The structure is useful if you want to inspect or modify the IR in Python before turning it into a string.

to_jsonable()

Serialize an object to IR and return a JSON-serializable structure.

jsonable = cucumber.to_jsonable(obj)

Arguments

obj: Any Python object to convert to IR before JSON conversion.

debug: Enables deep error context and path reporting.

verbose: Prints progress and handler selection information.

Returns

Any: A JSON-serializable structure.

Raises

SerializationError: If serialization fails.

to_json()

Serialize an object to IR and return a JSON string.

json_text = cucumber.to_json(obj)

Arguments

obj: Any Python object to convert to IR before JSON conversion.

indent: The number of spaces to use for indentation.

sort_keys: Sort keys in the JSON output.

debug: Enables deep error context and path reporting.

verbose: Prints progress and handler selection information.

Returns

str: A JSON string.

Raises

SerializationError: If serialization fails.

ir_to_jsonable()

Convert an IR to a JSON-serializable structure.

ir = cucumber.serialize_ir(obj)
jsonable = cucumber.ir_to_jsonable(ir)

Arguments

ir: The IR to convert.

Returns

Any: A JSON-serializable structure.

Raises

SerializationError: If conversion fails.

ir_to_json()

Convert an IR to a JSON string.

ir = cucumber.serialize_ir(obj)
json_text = cucumber.ir_to_json(ir)

Arguments

ir: The IR to convert.

indent: The number of spaces to use for indentation.

sort_keys: Sort keys in the JSON output.

Returns

str: A JSON string.

Raises

SerializationError: If conversion fails.

Crossing programming language boundaries using JSON

cucumber is meant to work solely within Python.

Here are the steps to convert a Python object to an object in another programming language:

  1. Convert a Python object to a JSON-serializable IR using cucumber.to_jsonable() (or directly to text using cucumber.to_json()), then write it out.
class Counter:

    def __init__(self):
        self.count = 0

    def increment(self):
        self.count += 1

    def decrement(self):
        self.count -= 1

counter = Counter()

jsonable = cucumber.to_jsonable(counter)
with open("counter.json", "w") as f:
    json.dump(jsonable, f)
  1. Load the JSON data in the target language.
  2. Interpret the IR nodes (__cucumber_type__, __handler__) and map them to equivalent types or a custom schema.
  3. Replace Python-only concepts (modules, class names, callables) with your own equivalents or drop them.
  4. Reconstruct the object graph in the target language.