Ducktools: ClassBuilder

ducktools-classbuilder is the Python package that will bring you the joy of writing… functions… that will bring back the joy of writing classes.

Maybe.

This specific idea came about after seeing people making multiple feature requests to attrs or dataclasses to add features or to merge feature PRs. This project is supposed to both provide users with some basic tools to allow them to make custom class generators that work with the features they need.

A little history

Previously I had a project - PrefabClasses - which came about while getting frustrated at the need to write converters or wrappers for multiple methods when using attrs, where all I really wanted to do was coerce empty values to None (or the other way around).

Further development came when I started investigating CLI tools and noticed the significant overhead of both attrs and dataclasses on import time, even before generating any classes.

This module has largely been reimplemented as ducktools.classbuilder.prefab using the tools provided by the main classbuilder module.

classbuilder and prefab have been intentionally written to avoid importing external modules, including stdlib ones that would have a significant impact on start time. (This is also why all of the typing is done in a stub file).

Slot Class Usage

The building toolkit includes a basic implementation that uses __slots__ to define the fields by assigning a SlotFields instance.

from ducktools.classbuilder.prefab import Prefab, attribute

class Slotted(Prefab):
    the_answer: int = 42
    the_question: str = attribute(
        default="What do you get if you multiply six by nine?",
        doc="Life the universe and everything",
    )

ex = Slotted()
print(ex)
print(ex.__slots__)
help(Slotted)

Using Annotations

It is possible to create slotted classes using Annotations. There is a Prefab base class in the prefab submodule that does this, but it also easy to implement using the provided tools.

In order to correctly implement __slots__ this needs to be done using a metaclass as __slots__ must be defined before the class is created.

from ducktools.classbuilder import (
    SlotMakerMeta,
    builder,
    check_argument_order,
    default_methods,
    unified_gatherer,
)


class AnnotationClass(metaclass=SlotMakerMeta):
    __slots__ = {}

    def __init_subclass__(
            cls,
            methods=default_methods,
            gatherer=unified_gatherer,
            **kwargs
    ):
        # Check class dict otherwise this will always be True as this base
        # class uses slots.
        slots = "__slots__" in cls.__dict__

        builder(cls, gatherer=gatherer, methods=methods, flags={"slotted": slots})
        check_argument_order(cls)
        super().__init_subclass__(**kwargs)


class AnnotatedDC(AnnotationClass):
    the_answer: int = 42
    the_question: str = "What do you get if you multiply six by nine?"


ex = AnnotatedDC()
print(ex)

Indices and tables