Repository: ray-project/ray
File: ci/build/build_image.py (Python)

Code before:
WandaSpecKey = tuple[str, str]

WANDA_SPEC_PATHS: dict[WandaSpecKey, str] = {
    ("ray", "cpu"): "ci/docker/ray-image-cpu.wanda.yaml",
    ("ray", "cuda"): "ci/docker/ray-image-cuda.wanda.yaml",
    ("ray", "tpu"): "ci/docker/ray-image-tpu.wanda.yaml",
    ("ray-extra", "cpu"): "ci/docker/ray-extra-image-cpu.wanda.yaml",
    ("ray-extra", "cuda"): "ci/docker/ray-extra-image-cuda.wanda.yaml",
    ("ray-llm", "cuda"): "ci/docker/ray-llm-image-cuda.wanda.yaml",
    ("ray-llm-extra", "cuda"): "ci/docker/ray-llm-extra-image-cuda.wanda.yaml",
}

SUPPORTED_IMAGE_TYPES: list[str] = list(dict.fromkeys(t for t, _ in WANDA_SPEC_PATHS))
REGISTRY_PREFIX = "cr.ray.io/rayproject/"


@dataclass(frozen=True)
class ImageBuildConfig:
    ray_image: RayImage
    ray_root: Path
    raymake_version: str
    manylinux_version: str
    ray_version: str
    commit: str

    @property
    def wanda_spec_path(self) -> str:
        return self.ray_image.get_wanda_spec_path()

    @property
    def wanda_image_tag(self) -> str:
        return f"{REGISTRY_PREFIX}{self.ray_image.wanda_image_name}"

    @property
    def nightly_alias(self) -> str | None:
        cfg = IMAGE_TYPE_CONFIG[self.ray_image.image_type]
        if (
            self.ray_image.python_version != cfg["default_python"]
            or self.ray_image.platform != cfg["default_platform"]
            or self.ray_image.architecture != DEFAULT_ARCHITECTURE
        ):
            return None
        return f"{REGISTRY_PREFIX}{self.ray_image.repo}:nightly{self.ray_image.variation_suffix}"

    @property
    def build_env(self) -> dict[str, str]:
        env = {
            "PYTHON_VERSION": self.ray_image.python_version,
            "MANYLINUX_VERSION": self.manylinux_version,
            "HOSTTYPE": self.ray_image.architecture,
            "ARCH_SUFFIX": self.ray_image.arch_suffix,

Review comment (suggestion):
is it okay if we remove all properties? I don't like properties because they are function calls that look like a simple member var access, and hides complexities.

and especially in a dataclass, it is unclear which ones are the real member fields.

Code after:
WandaSpecKey = tuple[str, str]

WANDA_SPEC_PATHS: dict[WandaSpecKey, str] = {
    ("ray", "cpu"): "ci/docker/ray-image-cpu.wanda.yaml",
    ("ray", "cuda"): "ci/docker/ray-image-cuda.wanda.yaml",
    ("ray", "tpu"): "ci/docker/ray-image-tpu.wanda.yaml",
    ("ray-extra", "cpu"): "ci/docker/ray-extra-image-cpu.wanda.yaml",
    ("ray-extra", "cuda"): "ci/docker/ray-extra-image-cuda.wanda.yaml",
    ("ray-llm", "cuda"): "ci/docker/ray-llm-image-cuda.wanda.yaml",
    ("ray-llm-extra", "cuda"): "ci/docker/ray-llm-extra-image-cuda.wanda.yaml",
}

SUPPORTED_IMAGE_TYPES: list[str] = list(dict.fromkeys(t for t, _ in WANDA_SPEC_PATHS))
REGISTRY_PREFIX = "cr.ray.io/rayproject/"


@dataclass(frozen=True)
class ImageBuildConfig:
    ray_image: RayImage
    ray_root: Path
    raymake_version: str
    manylinux_version: str
    ray_version: str
    commit: str
    wanda_spec_path: str
    wanda_image_tag: str
    nightly_alias: str | None
    build_env: dict[str, str]

    @classmethod
    def from_args(
        cls,
        image_type: str,
        python_version: str,
        image_platform: str,
    ) -> ImageBuildConfig:
        ray_image = RayImage(
            image_type=image_type,
            python_version=python_version,
            platform=image_platform,
            architecture=cls._detect_host_arch(),
        )
        try:
            ray_image.validate()
            ray_image.get_wanda_spec_path()
        except RayImageError as e:
            raise BuildError(str(e)) from e

        root = find_ray_root()
        manylinux_version = parse_file(
            root / "rayci.env", r'MANYLINUX_VERSION=["\']?([^"\'\s]+)'