def chz_make_class()

in chz/data_model.py [0:0]


def chz_make_class(cls, version: str | None, typecheck: bool | None) -> type:
    if cls.__class__ is not type:
        if cls.__class__ is typing._ProtocolMeta:
            if typing_extensions.is_protocol(cls):
                raise TypeError("chz class cannot itself be a Protocol)")
        else:
            import abc

            if cls.__class__ is not abc.ABCMeta:
                raise TypeError("Cannot use custom metaclass")

    user_module = cls.__module__
    cls_annotations = cls.__dict__.get("__annotations__", {})

    fields: dict[str, Field] = {}

    # Collect fields from parent classes
    for b in reversed(cls.__mro__):
        if hasattr(b, "__dataclass_fields__"):
            raise ValueError("Cannot mix chz with dataclasses")

        # Only process classes that have been processed by our decorator
        base_fields: dict[str, Field] | None = getattr(b, "__chz_fields__", None)
        if base_fields is None:
            continue
        for f in base_fields.values():
            if (
                f.logical_name in cls.__dict__
                and f.logical_name not in cls_annotations
                and not _is_property_like(getattr(cls, f.logical_name))
            ):
                # Do an LSP check against parent fields (for non-property-like members)
                raise ValueError(
                    f"Cannot override field {f.logical_name!r} with a non-field member; "
                    f"maybe you're missing a type annotation?"
                )
            else:
                fields[f.logical_name] = f

    # Collect fields from the current class
    for name, annotation in cls_annotations.items():
        if _is_classvar_annotation(annotation):
            continue

        # Find the field specification from the class __dict__
        value = cls.__dict__.get(name, MISSING)
        if value is MISSING:
            field = Field(name=name, raw_type=annotation)
        elif isinstance(value, Field):
            field = value
            field._name = name
            field._raw_type = annotation
            delattr(cls, name)
        else:
            if _is_property_like(value) or (
                isinstance(value, types.FunctionType)
                and value.__name__ != "<lambda>"
                and value.__qualname__.startswith(cls.__qualname__)
            ):
                # It's problematic to redefine the field in the same class, because it means we
                # lose any field specification or default value
                raise ValueError(f"Field {name!r} is clobbered by {type_repr(type(value))}")
            field = Field(name=name, raw_type=annotation, default=value)
            delattr(cls, name)
        field._user_module = user_module

        # Do a basic LSP check for new fields
        parent_value = getattr(cls, name, MISSING)  # note the delattr above
        if parent_value is not MISSING and not (
            field.logical_name in fields and isinstance(parent_value, init_property)
        ):
            raise ValueError(
                f"Cannot define field {name!r} because it conflicts with something defined on a "
                f"superclass: {parent_value!r}"
            )
        other_name = field.logical_name if name != field.logical_name else field.x_name
        parent_value = getattr(cls, other_name, MISSING)
        if (
            parent_value is not MISSING
            and not (field.logical_name in fields and isinstance(parent_value, init_property))
            and other_name not in cls.__dict__
        ):
            raise ValueError(
                f"Cannot define field {name!r} because it conflicts with something defined on a "
                f"superclass: {parent_value!r}"
            )

        if (
            name == field.logical_name
            and name not in cls.__dict__
            and name in fields
            and fields[name]._name != name
        ):
            raise ValueError(
                "I'm a little unsure of what the semantics should be here. "
                "See test_conflicting_superclass_x_field_in_base. "
                "Please let @shantanu know if you hit this. "
                f"You can also just rename the field in the subclass to X_{name}."
            )

        # Create a default init_property for the field that accesses the raw X_ field
        munger: Any = field.get_munger()
        if munger is not None:
            if field.logical_name in cls.__dict__:
                raise ValueError(
                    f"Cannot define {field.logical_name!r} in class when the associated field "
                    f"has a munger"
                )
            munger.__name__ = field.logical_name
            munger = init_property(munger)
            munger.__set_name__(cls, field.logical_name)
            setattr(cls, field.logical_name, munger)
        if (
            # but don't clobber existing definitions...
            field.logical_name not in cls.__dict__  # ...if something is already there in class
            and field.logical_name not in fields  # ...if a parent has defined the field
        ):
            fn: Any = lambda self, x_name=field.x_name: getattr(self, x_name)
            fn.__name__ = field.logical_name
            fn = init_property(fn)
            fn.__set_name__(cls, field.logical_name)
            setattr(cls, field.logical_name, fn)

        fields[field.logical_name] = field

    for name, value in cls.__dict__.items():
        if isinstance(value, Field) and name not in cls_annotations:
            raise TypeError(f"{name!r} has no type annotation")

    # Mark the class as having been processed by our decorator
    cls.__chz_fields__ = fields

    if "__init__" in cls.__dict__:
        raise ValueError("Cannot define __init__ on a chz class. " + _INIT_ALTERNATIVES)
    if "__post_init__" in cls.__dict__:
        raise ValueError("Cannot define __post_init__ on a chz class. " + _INIT_ALTERNATIVES)
    cls.__init__ = _synthesise_init(fields.values(), sys.modules[user_module].__dict__)
    cls.__init__.__qualname__ = f"{cls.__qualname__}.__init__"

    cls.__chz_validate__ = __chz_validate__
    cls.__chz_init_property__ = __chz_init_property__

    if "__setattr__" in cls.__dict__:
        raise ValueError("Cannot define __setattr__ on a chz class")
    cls.__setattr__ = __setattr__
    if "__delattr__" in cls.__dict__:
        raise ValueError("Cannot define __delattr__ on a chz class")
    cls.__delattr__ = __delattr__

    if "__repr__" not in cls.__dict__:
        cls.__repr__ = __repr__
    if "__eq__" not in cls.__dict__:
        cls.__eq__ = __eq__

    if "__hash__" not in cls.__dict__:
        cls.__hash__ = __hash__

    if "_repr_pretty_" not in cls.__dict__:
        # Special-cased by IPython
        cls._repr_pretty_ = _repr_pretty_
    if "__chz_pretty__" not in cls.__dict__:
        cls.__chz_pretty__ = __chz_pretty__

    if version is not None:
        import json

        # Hash all the fields and check the version matches
        expected_version = version.split("-")[0]
        key = [f.versioning_key() for f in sorted(fields.values(), key=lambda f: f.x_name)]
        key_bytes = json.dumps(key, separators=(",", ":")).encode()
        actual_version = hashlib.sha1(key_bytes).hexdigest()[:8]
        if actual_version != expected_version:
            raise ValueError(f"Version {version!r} does not match {actual_version!r}")

    if typecheck is not None:
        import chz.validators as chzval

        if typecheck:
            chzval._ensure_chz_validators(cls)
            if chzval._decorator_typecheck not in cls.__chz_validators__:
                cls.__chz_validators__.append(chzval._decorator_typecheck)
        else:
            if chzval._decorator_typecheck in getattr(cls, "__chz_validators__", []):
                raise ValueError("Cannot disable typecheck; all validators are inherited")

    return cls