def substitute_variables(text: str, variables: Mapping[str, str]) -> str:
    """Substitute ``${VAR}`` variables in `text` with their values.

    Variables in the text can take a number of shell-inspired forms::

        $VAR
        ${VAR}
        ${VAR?}             strict: an error if VAR isn't defined.
        ${VAR-missing}      defaulted: "missing" if VAR isn't defined.
        $$                  just a dollar sign.

    `variables` is a dictionary of variable values.

    Returns the resulting text with values substituted.

    """
    dollar_pattern = r"""(?x)   # Use extended regex syntax
        \$                      # A dollar sign,
        (?:                     # then
            (?P<dollar> \$ ) |      # a dollar sign, or
            (?P<word1> \w+ ) |      # a plain word, or
            \{                      # a {-wrapped
                (?P<word2> \w+ )        # word,
                (?:                         # either
                    (?P<strict> \? ) |      # with a strict marker
                    -(?P<defval> [^}]* )    # or a default value
                )?                      # maybe.
            }
        )
        """

    dollar_groups = ("dollar", "word1", "word2")

    def dollar_replace(match: re.Match[str]) -> str:
        """Called for each $replacement."""
        # Only one of the dollar_groups will have matched, just get its text.
        word = next(g for g in match.group(*dollar_groups) if g)    # pragma: always breaks
        if word == "$":
            return "$"
        elif word in variables:
            return variables[word]
        elif match["strict"]:
            msg = f"Variable {word} is undefined: {text!r}"
            raise CoverageException(msg)
        else:
            return match["defval"]

    text = re.sub(dollar_pattern, dollar_replace, text)
    return text
Substitute ``${VAR}`` variables in `text` with their values.

    Variables in the text can take a number of shell-inspired forms::

        $VAR
        ${VAR}
        ${VAR?}             strict: an error if VAR isn't defined.
        ${VAR-missing}      defaulted: "missing" if VAR isn't defined.
        $$                  just a dollar sign.

    `variables` is a dictionary of variable values.

    Returns the resulting text with values substituted.

    
coverage/misc.py