Skip to content

serialization

fastforward.serialization.yamlable(cls, /, tag='!ff.obj', yaml_constructor=_ff_obj_yaml_constructor, yaml_representer=_ff_obj_yaml_representer) #

Decorator that makes a class serializable to/from YAML.

This decorator wraps the init and new methods of a class to store their arguments, enabling automatic YAML serialization and deserialization. It also registers custom YAML constructor and representer functions for the class.

When a class uses this decorator, instances will have the following attributes automatically added to store constructor arguments: - newargs_ex: tuple containing (args, kwargs) passed to new - initargs_ex: tuple containing (args, kwargs) passed to init

The decorator ensures that both the current class and all its subclasses have their init and new methods wrapped when they are overridden.

Parameters:

Name Type Description Default
cls type[_T]

The class to decorate.

required
tag str

YAML tag to use for this class (default: "!ff.obj").

'!ff.obj'
yaml_constructor Callable[[Loader | FullLoader | UnsafeLoader, str, Node], Any]

Function to construct objects from YAML (default: _ff_obj_yaml_constructor).

_ff_obj_yaml_constructor
yaml_representer Callable[[Dumper, Any], MappingNode]

Function to represent objects as YAML (default: _ff_obj_yaml_representer).

_ff_obj_yaml_representer

Returns:

Type Description
type[_T]

The decorated class with YAML serialization capabilities and wrapped methods.

Example

@yamlable class MyClass: def init(self, value: int): self.value = value

Objects can now be serialized to/from YAML automatically#

obj = MyClass(42) yaml_str = yaml.dump(obj) restored_obj = yaml.load(yaml_str, Loader=yaml.Loader)

Source code in fastforward/serialization.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
def yamlable(
    cls: type[_T],
    /,
    tag: str = "!ff.obj",
    yaml_constructor: Callable[
        [yaml.Loader | yaml.FullLoader | yaml.UnsafeLoader, str, yaml.Node], Any
    ] = _ff_obj_yaml_constructor,
    yaml_representer: Callable[[yaml.Dumper, Any], yaml.MappingNode] = _ff_obj_yaml_representer,
) -> type[_T]:
    """Decorator that makes a class serializable to/from YAML.

    This decorator wraps the __init__ and __new__ methods of a class to store their
    arguments, enabling automatic YAML serialization and deserialization. It also
    registers custom YAML constructor and representer functions for the class.

    When a class uses this decorator, instances will have the following attributes
    automatically added to store constructor arguments:
    - __newargs_ex__: tuple containing (args, kwargs) passed to __new__
    - __initargs_ex__: tuple containing (args, kwargs) passed to __init__

    The decorator ensures that both the current class and all its subclasses
    have their __init__ and __new__ methods wrapped when they are overridden.

    Args:
        cls: The class to decorate.
        tag: YAML tag to use for this class (default: "!ff.obj").
        yaml_constructor: Function to construct objects from YAML
            (default: _ff_obj_yaml_constructor).
        yaml_representer: Function to represent objects as YAML
            (default: _ff_obj_yaml_representer).

    Returns:
        The decorated class with YAML serialization capabilities and wrapped methods.

    Example:
        @yamlable
        class MyClass:
            def __init__(self, value: int):
                self.value = value

        # Objects can now be serialized to/from YAML automatically
        obj = MyClass(42)
        yaml_str = yaml.dump(obj)
        restored_obj = yaml.load(yaml_str, Loader=yaml.Loader)
    """
    _wrap_to_save_method_args(cls, "__new__", "__init__")

    def wrap_init_subclass(
        func: Callable[Concatenate[type[_DerivedT], _P], None], is_custom_impl: bool
    ) -> Callable[Concatenate[type[_DerivedT], _P], None]:
        def wrapper(subcls: type[_DerivedT], /, *args: _P.args, **kwargs: _P.kwargs) -> None:
            if is_custom_impl:
                # since func is a classmethod, the first argument is passed automatically,
                # subcls should not be passed to avoid an extra positional argument.
                func(*args, **kwargs)  # type: ignore[arg-type]
            else:
                super(cls, subcls).__init_subclass__(*args, **kwargs)  # type: ignore[misc]
            _wrap_to_save_method_args(subcls, "__new__", "__init__")

        return classmethod(wrapper)  # type: ignore[return-value]

    setattr(
        cls,
        "__init_subclass__",
        wrap_init_subclass(cls.__init_subclass__, _has_custom_method(cls, "__init_subclass__")),  # type: ignore[arg-type]
    )

    yaml.add_multi_constructor(tag, yaml_constructor)
    yaml.add_multi_representer(cls, yaml_representer)

    return cls