Source code for spack.concretize_ui

# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Frontends for concretization.

Defines the :class:`ConcretizerUI` contract (a headless no-op base) and the terminal
:class:`TerminalUI` implementation.
"""

import enum
import sys

from spack.spec import Spec
from spack.util import tty


[docs] class SolveKind(enum.Enum): """How the specs to be concretized are distributed over solves.""" #: A single solve produces every spec TOGETHER = "together" #: One solve per set of specs that can be unified WHEN_POSSIBLE = "when_possible" #: One solve per spec, possibly in parallel SEPARATELY = "separately"
[docs] class ConcretizerUI: """Interface between concretization and a frontend. The methods are no-ops, which makes this class usable as a headless frontend. Every event is emitted in the process that owns the frontend, and from a single thread. Frontends therefore need no locking, and no capture of child output. """
[docs] def on_group_started(self, *, group: str, is_default: bool) -> None: """A group of user specs is about to be concretized."""
[docs] def on_concretization_started(self, *, kind: SolveKind, total: int, processes: int) -> None: """``total`` user specs are about to be concretized as ``kind`` prescribes, over ``processes`` processes. Called once per group of user specs, before any spec of that group is reported, with ``total`` equal to the number of ``on_spec_concretized`` calls that will follow for it. """
[docs] def on_spec_concretized( self, abstract: Spec, *, concrete: Spec, count: int, duration: float ) -> None: """A user spec has been concretized. ``count`` is how many specs of the group have been reported so far, including this one, and goes from 1 to the announced ``total``. ``duration`` is the time spent in the solve that produced this spec, so specs that come out of the same solve report the same duration. """
#: Frontend that reports nothing. Same class as the contract it implements, aliased so that call #: sites can say which of the two roles they mean. HeadlessUI = ConcretizerUI
[docs] class TerminalUI(ConcretizerUI): """Terminal frontend: announces groups and solves, and reports per-spec progress.""" def __init__(self) -> None: self.kind = SolveKind.TOGETHER self.total = 0
[docs] def on_group_started(self, *, group: str, is_default: bool) -> None: if is_default: return tty.msg(f"Concretizing the '{group}' group of specs")
[docs] def on_concretization_started(self, *, kind: SolveKind, total: int, processes: int) -> None: self.kind = kind self.total = total if kind is not SolveKind.SEPARATELY or total == 0: return msg = "Starting concretization" if processes > 1: msg += f" pool with {processes} processes" tty.msg(msg)
[docs] def on_spec_concretized( self, abstract: Spec, *, concrete: Spec, count: int, duration: float ) -> None: if self.kind is SolveKind.TOGETHER: return percentage = int(count / self.total * 100) tty.verbose( f"{duration:6.1f}s [{percentage:3d}%] {concrete.cformat('{hash:7}')} " f"{abstract.colored_str}", stream=sys.stdout, ) sys.stdout.flush()