def _event_extra_factory()

in glean-core/python/glean/_loader.py [0:0]


def _event_extra_factory(name: str, argnames: List[Tuple[str, str]]) -> Any:
    """
    Generate a new class, inheriting from `metrics.EventExtras`
    and implementing the `to_ffi_extra` method,
    which serializes expected attributes to pass over FFI.
    """

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            typ = next((t for (k, t) in argnames if key == k), None)
            if typ is None:
                raise TypeError(f"Argument '{key}' not valid for {self.__class__.__name__}")
            elif typ == "boolean" and isinstance(value, bool):
                pass
            elif typ == "string" and isinstance(value, str):
                pass
            elif typ == "quantity" and isinstance(value, int):
                pass
            else:
                raise TypeError(f"Field '{key}' requires type {typ} in {self.__class__.__name__}")
            setattr(self, key, value)

    def to_ffi_extra(self):
        extras = {}

        for name, typ in argnames:
            attr = getattr(self, name, None)
            if attr is not None:
                if typ == "boolean" and isinstance(attr, bool):
                    # Special-case needed for booleans to turn them lowercase (true/false)
                    extras[name] = str(attr).lower()
                elif typ == "string" and isinstance(attr, str):
                    extras[name] = str(attr)
                elif typ == "quantity" and isinstance(attr, int):
                    extras[name] = str(attr)
                # Don't support other data types
                else:
                    raise TypeError(f"Type {type(attr)} not supported for {name}")

        return extras

    attr = {name: None for (name, _) in argnames}  # type: Dict[str, Any]
    attr["__init__"] = __init__
    attr["to_ffi_extra"] = to_ffi_extra
    newclass = type(name, (metrics.EventExtras,), attr)
    return newclass