Skip to content

Builder

momapy.builder

Classes and functions for building objects from dataclasses.

This module provides functionality to automatically generate builder classes from dataclasses, allowing for flexible object construction and transformation between objects and their builder representations.

Example

from dataclasses import dataclass @dataclass ... class Person: ... name: str ... age: int

Create a builder class automatically

Builder = get_or_make_builder_cls(Person) builder = Builder(name="John", age=30) person = builder.build() print(person) Person(name='John', age=30)

Classes:

Name Description
Builder

Abstract base class for builder objects.

Functions:

Name Description
builder_from_object

Convert an object (or collection of objects) to builder(s).

get_builder_cls

Get the registered builder class for a given class.

get_or_make_builder_cls

Get an existing builder class or create a new one.

has_builder_cls

Check if a builder class is registered for a given class.

isinstance_or_builder

Check if object is instance of class or its builder class.

issubclass_or_builder

Check if class is subclass of class or its builder class.

new_builder_object

Create a new builder instance for a given class.

object_from_builder

Convert a builder (or collection of builders) to actual object(s).

register_builder_cls

Register a builder class.

super_or_builder

Get super() proxy for a class or its builder class.

Builder dataclass

Builder()

Bases: ABC, Monitored

Abstract base class for builder objects.

Builder classes are automatically generated from dataclasses to provide a mutable representation for constructing immutable objects.

Attributes:

Name Type Description
_cls_to_build type

The class that this builder constructs.

Methods:

Name Description
build

Build and return an object from the builder.

from_object

Create a builder from an existing object.

build abstractmethod

build(inside_collections: bool = True, builder_to_object: dict[int, Any] | None = None) -> Any

Build and return an object from the builder.

Parameters:

Name Type Description Default
inside_collections bool

Whether to recursively build objects inside collections (lists, dicts, etc.). Defaults to True.

True
builder_to_object dict[int, Any] | None

Optional cache mapping builder ids to built objects for handling circular references.

None

Returns:

Type Description
Any

The constructed object of type _cls_to_build.

Source code in src/momapy/builder.py
@abc.abstractmethod
def build(
    self,
    inside_collections: bool = True,
    builder_to_object: dict[int, typing.Any] | None = None,
) -> typing.Any:
    """Build and return an object from the builder.

    Args:
        inside_collections: Whether to recursively build objects inside
            collections (lists, dicts, etc.). Defaults to True.
        builder_to_object: Optional cache mapping builder ids to built
            objects for handling circular references.

    Returns:
        The constructed object of type `_cls_to_build`.
    """
    pass

from_object abstractmethod classmethod

from_object(obj: Any, inside_collections: bool = True, omit_keys: bool = True, object_to_builder: dict[int, Builder] | None = None) -> Self

Create a builder from an existing object.

Parameters:

Name Type Description Default
obj Any

The object to convert to a builder.

required
inside_collections bool

Whether to recursively convert objects inside collections. Defaults to True.

True
omit_keys bool

Whether to skip converting dictionary keys to builders. Defaults to True.

True
object_to_builder dict[int, Builder] | None

Optional cache mapping object ids to builders for handling circular references.

None

Returns:

Type Description
Self

A builder instance representing the input object.

Source code in src/momapy/builder.py
@classmethod
@abc.abstractmethod
def from_object(
    cls,
    obj: typing.Any,
    inside_collections: bool = True,
    omit_keys: bool = True,
    object_to_builder: dict[int, "Builder"] | None = None,
) -> typing_extensions.Self:
    """Create a builder from an existing object.

    Args:
        obj: The object to convert to a builder.
        inside_collections: Whether to recursively convert objects inside
            collections. Defaults to True.
        omit_keys: Whether to skip converting dictionary keys to builders.
            Defaults to True.
        object_to_builder: Optional cache mapping object ids to builders
            for handling circular references.

    Returns:
        A builder instance representing the input object.
    """
    pass

builder_from_object

builder_from_object(obj: Any, inside_collections=True, omit_keys=True, object_to_builder: dict[int, Builder] | None = None) -> Builder

Convert an object (or collection of objects) to builder(s).

Recursively converts class instances to their corresponding builder objects. Handles collections (list, tuple, set, dict) and circular references.

Parameters:

Name Type Description Default
obj Any

An object instance, collection of objects, or any value.

required
inside_collections

Whether to recursively process items inside collections. Defaults to True.

True
omit_keys

Whether to skip converting dictionary keys to builders. Defaults to True.

True
object_to_builder dict[int, Builder] | None

Optional cache mapping object ids to already-created builders for handling circular references.

None

Returns:

Type Description
Builder

The builder object, collection of builders, or the original value

Builder

if no builder class exists.

Example

from dataclasses import dataclass @dataclass ... class Point: ... x: float ... y: float

point = Point(x=1.0, y=2.0) builder = builder_from_object(point) print(type(builder).name) PointBuilder

Source code in src/momapy/builder.py
def builder_from_object(
    obj: typing.Any,
    inside_collections=True,
    omit_keys=True,
    object_to_builder: dict[int, "Builder"] | None = None,
) -> Builder:
    """Convert an object (or collection of objects) to builder(s).

    Recursively converts class instances to their corresponding builder objects.
    Handles collections (list, tuple, set, dict) and circular references.

    Args:
        obj: An object instance, collection of objects, or any value.
        inside_collections: Whether to recursively process items inside
            collections. Defaults to True.
        omit_keys: Whether to skip converting dictionary keys to builders.
            Defaults to True.
        object_to_builder: Optional cache mapping object ids to already-created
            builders for handling circular references.

    Returns:
        The builder object, collection of builders, or the original value
        if no builder class exists.

    Example:
        >>> from dataclasses import dataclass
        >>> @dataclass
        ... class Point:
        ...     x: float
        ...     y: float
        >>>
        >>> point = Point(x=1.0, y=2.0)
        >>> builder = builder_from_object(point)
        >>> print(type(builder).__name__)
        PointBuilder
    """
    if object_to_builder is not None:
        builder = object_to_builder.get(id(obj))
        if builder is not None:
            return builder
    else:
        object_to_builder = {}
    cls = get_or_make_builder_cls(type(obj))
    if issubclass(cls, Builder):
        return cls.from_object(
            obj=obj,
            inside_collections=inside_collections,
            omit_keys=omit_keys,
            object_to_builder=object_to_builder,
        )
    if inside_collections:
        if isinstance(obj, (list, tuple, set, frozenset)):
            return type(obj)(
                [
                    builder_from_object(
                        obj=e,
                        inside_collections=inside_collections,
                        omit_keys=omit_keys,
                        object_to_builder=object_to_builder,
                    )
                    for e in obj
                ]
            )
        elif isinstance(obj, (dict, frozendict.frozendict)):
            return type(obj)(
                [
                    (
                        (
                            builder_from_object(
                                obj=k,
                                inside_collections=inside_collections,
                                omit_keys=omit_keys,
                                object_to_builder=object_to_builder,
                            ),
                            builder_from_object(
                                obj=v,
                                inside_collections=inside_collections,
                                omit_keys=omit_keys,
                                object_to_builder=object_to_builder,
                            ),
                        )
                        if not omit_keys
                        else (
                            k,
                            builder_from_object(
                                obj=v,
                                inside_collections=inside_collections,
                                omit_keys=omit_keys,
                                object_to_builder=object_to_builder,
                            ),
                        )
                    )
                    for k, v in obj.items()
                ]
            )
    return obj

get_builder_cls

get_builder_cls(cls: Type) -> Type

Get the registered builder class for a given class.

Parameters:

Name Type Description Default
cls Type

The class to get the builder for.

required

Returns:

Type Description
Type

The builder class if registered, None otherwise.

Source code in src/momapy/builder.py
def get_builder_cls(cls: typing.Type) -> typing.Type:
    """Get the registered builder class for a given class.

    Args:
        cls: The class to get the builder for.

    Returns:
        The builder class if registered, None otherwise.
    """
    return builders.get(cls)

get_or_make_builder_cls

get_or_make_builder_cls(cls: Type, builder_fields: Collection[tuple[str, Type, Field]] | None = None, builder_bases: Collection[Type] | None = None, builder_namespace: dict[str, Any] | None = None) -> Type

Get an existing builder class or create a new one.

Returns the registered builder class for the given class if it exists, otherwise creates and registers a new builder class.

Parameters:

Name Type Description Default
cls Type

The class to get or create a builder for.

required
builder_fields Collection[tuple[str, Type, Field]] | None

Optional collection of additional fields to include in the builder class.

None
builder_bases Collection[Type] | None

Optional collection of base classes for the builder.

None
builder_namespace dict[str, Any] | None

Optional namespace dictionary for the builder class.

None

Returns:

Type Description
Type

The builder class for the given class, or the original class if

Type

it's not a dataclass.

Example

from dataclasses import dataclass @dataclass ... class Point: ... x: float ... y: float

PointBuilder = get_or_make_builder_cls(Point) print(PointBuilder.name) PointBuilder

Source code in src/momapy/builder.py
def get_or_make_builder_cls(
    cls: typing.Type,
    builder_fields: (
        typing.Collection[tuple[str, typing.Type, dataclasses.Field]] | None
    ) = None,
    builder_bases: typing.Collection[typing.Type] | None = None,
    builder_namespace: dict[str, typing.Any] | None = None,
) -> typing.Type:
    """Get an existing builder class or create a new one.

    Returns the registered builder class for the given class if it exists,
    otherwise creates and registers a new builder class.

    Args:
        cls: The class to get or create a builder for.
        builder_fields: Optional collection of additional fields to include
            in the builder class.
        builder_bases: Optional collection of base classes for the builder.
        builder_namespace: Optional namespace dictionary for the builder class.

    Returns:
        The builder class for the given class, or the original class if
        it's not a dataclass.

    Example:
        >>> from dataclasses import dataclass
        >>> @dataclass
        ... class Point:
        ...     x: float
        ...     y: float
        >>>
        >>> PointBuilder = get_or_make_builder_cls(Point)
        >>> print(PointBuilder.__name__)
        PointBuilder
    """
    builder_cls = get_builder_cls(cls)
    if builder_cls is None:
        if dataclasses.is_dataclass(cls):
            builder_cls = _make_builder_cls(
                cls, builder_fields, builder_bases, builder_namespace
            )
            register_builder_cls(builder_cls)
        else:
            builder_cls = cls
    return builder_cls

has_builder_cls

has_builder_cls(cls: Type) -> bool

Check if a builder class is registered for a given class.

Parameters:

Name Type Description Default
cls Type

The class to check.

required

Returns:

Type Description
bool

True if a builder class is registered, False otherwise.

Source code in src/momapy/builder.py
def has_builder_cls(cls: typing.Type) -> bool:
    """Check if a builder class is registered for a given class.

    Args:
        cls: The class to check.

    Returns:
        True if a builder class is registered, False otherwise.
    """
    return cls in builders

isinstance_or_builder

isinstance_or_builder(obj: Any, type_: Type | tuple[Type]) -> bool

Check if object is instance of class or its builder class.

Extends isinstance() to also check against registered builder classes.

Parameters:

Name Type Description Default
obj Any

The object to check.

required
type_ Type | tuple[Type]

The type or tuple of types to check against.

required

Returns:

Type Description
bool

True if obj is an instance of type_ or its builder class(es).

Example

from dataclasses import dataclass @dataclass ... class Point: ... x: float ... y: float

PointBuilder = get_or_make_builder_cls(Point) builder = PointBuilder(x=1.0, y=2.0) isinstance_or_builder(builder, Point) True

Source code in src/momapy/builder.py
def isinstance_or_builder(
    obj: typing.Any, type_: typing.Type | tuple[typing.Type]
) -> bool:
    """Check if object is instance of class or its builder class.

    Extends isinstance() to also check against registered builder classes.

    Args:
        obj: The object to check.
        type_: The type or tuple of types to check against.

    Returns:
        True if obj is an instance of type_ or its builder class(es).

    Example:
        >>> from dataclasses import dataclass
        >>> @dataclass
        ... class Point:
        ...     x: float
        ...     y: float
        >>>
        >>> PointBuilder = get_or_make_builder_cls(Point)
        >>> builder = PointBuilder(x=1.0, y=2.0)
        >>> isinstance_or_builder(builder, Point)
        True
    """
    if isinstance(type_, type):
        type_ = (type_,)
    type_ += tuple([get_or_make_builder_cls(t) for t in type_])
    return isinstance(obj, type_)

issubclass_or_builder

issubclass_or_builder(cls: Type, type_: Type | tuple[Type]) -> bool

Check if class is subclass of class or its builder class.

Extends issubclass() to also check against registered builder classes.

Parameters:

Name Type Description Default
cls Type

The class to check.

required
type_ Type | tuple[Type]

The type or tuple of types to check against.

required

Returns:

Type Description
bool

True if cls is a subclass of type_ or its builder class(es).

Source code in src/momapy/builder.py
def issubclass_or_builder(
    cls: typing.Type, type_: typing.Type | tuple[typing.Type]
) -> bool:
    """Check if class is subclass of class or its builder class.

    Extends issubclass() to also check against registered builder classes.

    Args:
        cls: The class to check.
        type_: The type or tuple of types to check against.

    Returns:
        True if cls is a subclass of type_ or its builder class(es).
    """
    if isinstance(type_, type):
        type_ = (type_,)
    type_ += tuple([get_or_make_builder_cls(t) for t in type_])
    return issubclass(cls, type_)

new_builder_object

new_builder_object(cls: Type, *args, **kwargs) -> Builder

Create a new builder instance for a given class.

Parameters:

Name Type Description Default
cls Type

The class to build (or a builder class).

required
*args

Positional arguments for the builder.

()
**kwargs

Keyword arguments for the builder.

{}

Returns:

Type Description
Builder

A new builder instance.

Example

from dataclasses import dataclass @dataclass ... class Person: ... name: str ... age: int = 0

builder = new_builder_object(Person, name="Alice") print(builder.name) Alice

Source code in src/momapy/builder.py
def new_builder_object(cls: typing.Type, *args, **kwargs) -> Builder:
    """Create a new builder instance for a given class.

    Args:
        cls: The class to build (or a builder class).
        *args: Positional arguments for the builder.
        **kwargs: Keyword arguments for the builder.

    Returns:
        A new builder instance.

    Example:
        >>> from dataclasses import dataclass
        >>> @dataclass
        ... class Person:
        ...     name: str
        ...     age: int = 0
        >>>
        >>> builder = new_builder_object(Person, name="Alice")
        >>> print(builder.name)
        Alice
    """
    if not issubclass(cls, Builder):
        cls = get_or_make_builder_cls(cls)
    return cls(*args, **kwargs)

object_from_builder

object_from_builder(builder: Builder, inside_collections=True, builder_to_object: dict[int, Any] | None = None)

Convert a builder (or collection of builders) to actual object(s).

Recursively converts builder objects to their corresponding class instances. Handles collections (list, tuple, set, dict) and circular references.

Parameters:

Name Type Description Default
builder Builder

A builder instance, collection of builders, or regular object.

required
inside_collections

Whether to recursively process items inside collections. Defaults to True.

True
builder_to_object dict[int, Any] | None

Optional cache mapping builder ids to already-built objects for handling circular references.

None

Returns:

Type Description

The built object, collection of built objects, or the original value

if not a builder.

Example

from dataclasses import dataclass @dataclass ... class Point: ... x: float ... y: float

PointBuilder = get_or_make_builder_cls(Point) builder = PointBuilder(x=1.0, y=2.0) point = object_from_builder(builder) print(point) Point(x=1.0, y=2.0)

Source code in src/momapy/builder.py
def object_from_builder(
    builder: Builder,
    inside_collections=True,
    builder_to_object: dict[int, typing.Any] | None = None,
):
    """Convert a builder (or collection of builders) to actual object(s).

    Recursively converts builder objects to their corresponding class instances.
    Handles collections (list, tuple, set, dict) and circular references.

    Args:
        builder: A builder instance, collection of builders, or regular object.
        inside_collections: Whether to recursively process items inside
            collections. Defaults to True.
        builder_to_object: Optional cache mapping builder ids to already-built
            objects for handling circular references.

    Returns:
        The built object, collection of built objects, or the original value
        if not a builder.

    Example:
        >>> from dataclasses import dataclass
        >>> @dataclass
        ... class Point:
        ...     x: float
        ...     y: float
        >>>
        >>> PointBuilder = get_or_make_builder_cls(Point)
        >>> builder = PointBuilder(x=1.0, y=2.0)
        >>> point = object_from_builder(builder)
        >>> print(point)
        Point(x=1.0, y=2.0)
    """
    if builder_to_object is not None:
        if id(builder) in builder_to_object:
            return builder_to_object[id(builder)]
    else:
        builder_to_object = {}
    if isinstance(builder, Builder):
        obj = builder.build(
            inside_collections=inside_collections,
            builder_to_object=builder_to_object,
        )
        builder_to_object[id(builder)] = obj
        return obj
    if inside_collections:
        if isinstance(builder, (list, tuple, set, frozenset)):
            return type(builder)(
                [
                    object_from_builder(
                        builder=e,
                        inside_collections=inside_collections,
                        builder_to_object=builder_to_object,
                    )
                    for e in builder
                ]
            )
        elif isinstance(builder, (dict, frozendict.frozendict)):
            return type(builder)(
                [
                    (
                        object_from_builder(
                            builder=k,
                            inside_collections=inside_collections,
                            builder_to_object=builder_to_object,
                        ),
                        object_from_builder(
                            builder=v,
                            inside_collections=inside_collections,
                            builder_to_object=builder_to_object,
                        ),
                    )
                    for k, v in builder.items()
                ]
            )
    return builder

register_builder_cls

register_builder_cls(builder_cls: Type) -> None

Register a builder class.

Registers a builder class so it can be looked up by its target class.

Parameters:

Name Type Description Default
builder_cls Type

The builder class to register. Must have a _cls_to_build attribute indicating the target class.

required
Source code in src/momapy/builder.py
def register_builder_cls(builder_cls: typing.Type) -> None:
    """Register a builder class.

    Registers a builder class so it can be looked up by its target class.

    Args:
        builder_cls: The builder class to register. Must have a `_cls_to_build`
            attribute indicating the target class.
    """
    builders[builder_cls._cls_to_build] = builder_cls

super_or_builder

super_or_builder(type_: Type, obj: Any) -> Type

Get super() proxy for a class or its builder class.

Attempts to get the super() proxy for the given type. If that fails, tries with the builder class of the given type.

Parameters:

Name Type Description Default
type_ Type

The class to get super() for.

required
obj Any

The object to get the super proxy of.

required

Returns:

Type Description
Type

A super() proxy object.

Source code in src/momapy/builder.py
def super_or_builder(type_: typing.Type, obj: typing.Any) -> typing.Type:
    """Get super() proxy for a class or its builder class.

    Attempts to get the super() proxy for the given type. If that fails,
    tries with the builder class of the given type.

    Args:
        type_: The class to get super() for.
        obj: The object to get the super proxy of.

    Returns:
        A super() proxy object.
    """
    try:
        s = super(type_, obj)
    except TypeError:
        builder = get_or_make_builder_cls(type_)
        s = super(builder, obj)
    finally:
        return s