Module refinery.lib.inno.constants

Inno Setup constants as Setup expands them: how the end of a constant is found and its parameters are split, which names each version of Setup knows, and how each kind of constant is expanded. The values come from a ConstantHost, which stands for the machine and the Setup that run the script.

Expand source code Browse git
"""
Inno Setup constants as Setup expands them: how the end of a constant is found and its parameters
are split, which names each version of Setup knows, and how each kind of constant is expanded. The
values come from a `ConstantHost`, which stands for the machine and the Setup that run the script.
"""
from __future__ import annotations

import re

from typing import Any, Callable, Generator, Mapping, NamedTuple, Protocol

from refinery.lib.inno.emulation import InternalError
from refinery.lib.inno.release import SetupRelease

_HEX_DIGITS = frozenset('0123456789abcdefABCDEF')

_Version = tuple[int, ...]

CodeRunner = Callable[[str, str], Generator[Any, Any, str]]
"""
Runs the function of the script with the given name and argument for a code constant and returns
its text; the run of the script that asks for an expansion gives it.
"""


class _Name(NamedTuple):
    since: _Version = (0,)
    until: _Version | None = None


_NAMES: dict[str, _Name] = {
    'app'               : _Name(),
    'cmd'               : _Name(),
    'commonappdata'     : _Name(),
    'commoncf'          : _Name(since=(6, 0, 0)),
    'commoncf32'        : _Name(since=(6, 0, 0)),
    'commoncf64'        : _Name(since=(6, 0, 0)),
    'commondesktop'     : _Name(),
    'commondocs'        : _Name(),
    'commonfavorites'   : _Name(),
    'commonfonts'       : _Name(since=(6, 1, 0)),
    'commonpf'          : _Name(since=(6, 0, 0)),
    'commonpf32'        : _Name(since=(6, 0, 0)),
    'commonpf64'        : _Name(since=(6, 0, 0)),
    'commonprograms'    : _Name(),
    'commonstartmenu'   : _Name(),
    'commonstartup'     : _Name(),
    'commontemplates'   : _Name(),
    'computername'      : _Name(),
    'dao'               : _Name(),
    'dotnet11'          : _Name(),
    'dotnet20'          : _Name(),
    'dotnet2032'        : _Name(),
    'dotnet2064'        : _Name(),
    'dotnet40'          : _Name(),
    'dotnet4032'        : _Name(),
    'dotnet4064'        : _Name(),
    'group'             : _Name(),
    'groupname'         : _Name(),
    'hwnd'              : _Name(until=(6, 4, 0)),
    'language'          : _Name(),
    'localappdata'      : _Name(),
    'log'               : _Name(),
    'sd'                : _Name(),
    'src'               : _Name(),
    'srcexe'            : _Name(),
    'sys'               : _Name(),
    'sysnative'         : _Name(since=(6, 0, 2)),
    'sysuserinfoname'   : _Name(),
    'sysuserinfoorg'    : _Name(),
    'syswow64'          : _Name(),
    'tmp'               : _Name(),
    'uninstallexe'      : _Name(),
    'userappdata'       : _Name(),
    'usercf'            : _Name(since=(5, 5, 2)),
    'userdesktop'       : _Name(),
    'userdocs'          : _Name(),
    'userfavorites'     : _Name(),
    'userfonts'         : _Name(since=(6, 1, 0)),
    'userinfoname'      : _Name(),
    'userinfoorg'       : _Name(),
    'userinfoserial'    : _Name(),
    'username'          : _Name(),
    'userpf'            : _Name(since=(5, 5, 2)),
    'userprograms'      : _Name(),
    'usersavedgames'    : _Name(since=(6, 0, 4)),
    'usersendto'        : _Name(since=(5, 6, 1)),
    'userstartmenu'     : _Name(),
    'userstartup'       : _Name(),
    'usertemplates'     : _Name(),
    'win'               : _Name(),
    'wizardhwnd'        : _Name(),
}
"""
The names of the constants without parameters that Setup knows, with the first version that knows
each one and the first that no longer does. Versions before 5.4.3 are taken to know the names of
5.4.3.
"""


class _OldName(NamedTuple):
    name: str
    renamed: _Version
    until: _Version | None = None


_OLD_NAMES: dict[str, _OldName] = {
    'cf'     : _OldName('commoncf', renamed=(6, 0, 0)),
    'cf32'   : _OldName('commoncf32', renamed=(6, 0, 0)),
    'cf64'   : _OldName('commoncf64', renamed=(6, 0, 0)),
    'fonts'  : _OldName('commonfonts', renamed=(6, 1, 0)),
    'pf'     : _OldName('commonpf', renamed=(6, 0, 0)),
    'pf32'   : _OldName('commonpf32', renamed=(6, 0, 0)),
    'pf64'   : _OldName('commonpf64', renamed=(6, 0, 0)),
    'sendto' : _OldName('usersendto', renamed=(6, 0, 0), until=(5, 6, 1)),
}
"""
Names that older versions of Setup knew as their own and that newer versions read as another name.
Before Setup renamed it, each old name is a constant of its own with the value of the new name;
`sendto` is not known from 5.6.1 until Setup renamed it in 6.0.0.
"""

_AUTO_SINCE: _Version = (6, 0, 0)


class ConstantError(InternalError):
    """
    The internal error that Setup raises for a constant that it cannot expand.
    """


class ConstantHost(Protocol):
    """
    The machine and the Setup that a constant is expanded for. A method raises `ConstantError` for
    a value that Setup cannot give on this machine, and any other exception for a value that the
    host does not know.
    """
    release: SetupRelease
    """
    The release of Setup.
    """
    admin: bool
    """
    Whether Setup installs for all users, which decides what the `auto` constants stand for.
    """
    win64: bool
    """
    Whether the machine runs a 64-bit Windows.
    """

    def constant(self, name: str, original: str) -> str:
        """
        The value of a constant without parameters, given by the name that Setup reads it as and
        by the name that the script wrote.
        """
        ...

    def environment(self, name: str, has_default: bool) -> str:
        """
        The value of an environment variable, empty when it is not set. Setup uses the default of
        the constant when the value is empty; `has_default` tells whether the constant has one.
        """
        ...

    def registry(self, root: str, view: int | None, key: str) -> bool:
        """
        Whether the registry key opens; `view` is 32 or 64 for a forced registry view.
        """
        ...

    def registry_value(self, root: str, view: int | None, key: str, value: str, default: str) -> str:
        """
        The text of a value in a registry key that opens, or the default when it has none.
        """
        ...

    def ini_value(self, file: str, section: str, key: str, default: str) -> str:
        """
        The value of a key in an INI file, or the default.
        """
        ...

    def parameter(self, name: str, default: str) -> str:
        """
        The value of a command line parameter `/NAME=value`, or the default.
        """
        ...

    def custom_message(self, name: str) -> str | None:
        """
        The custom message of the active language with the given name, `None` when the setup has
        no such message.
        """
        ...

    def format_message(self, message: str, arguments: list[str]) -> str:
        """
        Puts the arguments into a message as Setup's `FmtMessage` does.
        """
        ...

    def drive(self, path: str) -> str:
        """
        The drive or network share at the start of a path.
        """
        ...


def _read_as(constant: str, host: ConstantHost) -> str:
    """
    The name that Setup reads the given constant as: since Setup renamed an old name, it reads
    it as the new name, and since 6.0.0 it reads `auto` as `common` when it installs for all users
    and as `user` otherwise.
    """
    release = host.release
    if (old := _OLD_NAMES.get(constant)) is not None and release.since(old.renamed):
        return old.name
    if constant.startswith('auto') and release.since(_AUTO_SINCE):
        constant = constant.replace('auto', 'common' if host.admin else 'user')
        if constant in ('userpf32', 'userpf64', 'usercf32', 'usercf64'):
            constant = constant[:-2]
    return constant


def _value_name(name: str, release: SetupRelease) -> str | None:
    """
    The name whose value a constant has when Setup knows it as a constant without parameters, or
    `None`. An old name that Setup has not renamed yet has the value of its new name.
    """
    if (entry := _NAMES.get(name)) is not None:
        if release.since(entry.since) and (entry.until is None or not release.since(entry.until)):
            return name
        return None
    if (old := _OLD_NAMES.get(name)) is not None and not release.since(old.renamed):
        if old.until is None or not release.since(old.until):
            return old.name
    return None


def skip_past_constant(string: str, start: int) -> int | None:
    """
    The index after the constant whose opening brace is at the given index, or `None` when the
    constant is not closed. Two opening braces are one escaped brace. Inside a constant, braces
    nest, and two opening braces in a row count as text.
    """
    end = len(string)
    cursor = start + 1
    if cursor >= end:
        return None
    if string[cursor] == '{':
        return cursor + 1
    level = 1
    last_open = -1
    while cursor < end:
        character = string[cursor]
        if character == '{':
            if last_open != cursor - 1:
                level += 1
                last_open = cursor
            else:
                level -= 1
        elif character == '}':
            level -= 1
            if level == 0:
                return cursor + 1
        cursor += 1
    return None


def constant_position(character: str, string: str) -> int:
    """
    The index of the first occurrence of the character in the string outside of any constant, or
    -1 when there is none.
    """
    cursor = 0
    while cursor < len(string):
        if string[cursor] == character:
            return cursor
        if string[cursor] != '{':
            cursor += 1
        elif (cursor := skip_past_constant(string, cursor)) is None:
            break
    return -1


def split_constant_parameter(string: str, character: str) -> tuple[str, str | None]:
    """
    Splits the string at the first occurrence of the character outside of any constant. The second
    part is `None` when the character does not occur.
    """
    if (index := constant_position(character, string)) < 0:
        return string, None
    return string[:index], string[index + 1:]


def decode_percent_escapes(string: str) -> str | None:
    """
    Decodes each percent sign followed by two hex digits into the character with that code, except
    inside constants. The result is `None` when a percent sign is not followed by two hex digits or
    a constant is not closed.
    """
    output: list[str] = []
    cursor = 0
    while cursor < len(string):
        character = string[cursor]
        if character == '{':
            if (end := skip_past_constant(string, cursor)) is None:
                return None
            output.append(string[cursor:end])
            cursor = end
        elif character == '%':
            digits = string[cursor + 1:cursor + 3]
            if len(digits) != 2 or not _HEX_DIGITS.issuperset(digits):
                return None
            output.append(chr(int(digits, 16)))
            cursor += 3
        else:
            output.append(character)
            cursor += 1
    return ''.join(output)


def _decoded(kind: str, *parameters: str) -> list[str]:
    decoded = []
    for parameter in parameters:
        if (value := decode_percent_escapes(parameter)) is None:
            raise ConstantError(F'Failed to parse "{kind}" constant')
        decoded.append(value)
    return decoded


def fmt_message(message: str, arguments: list[str]) -> str:
    """
    Setup's `FmtMessage`: `%1` to `%9` stand for the arguments and `%%` for a percent sign; a
    placeholder without its argument stays.
    """
    def substitute(match: re.Match[str]):
        if (spec := match[1]) == '%':
            return '%'
        if (k := int(spec) - 1) < len(arguments):
            return arguments[k]
        return match[0]
    return re.sub('%([1-9%])', substitute, message)


def expand_constants(
    string: str,
    host: ConstantHost,
    custom: Mapping[str, str] | None = None,
    run_code: CodeRunner | None = None,
) -> Generator[Any, Any, str]:
    """
    Expands the constants in the given string as Setup's `ExpandConstEx2` does, with the custom
    constants that Setup passes for the text, by their names, and the runner of code constants
    of the script that asks for the expansion. The value of a constant is not scanned again, and a
    backslash right after a value that ends in one is dropped. Without a runner, a code constant
    fails as a value that the emulator does not have.
    """
    output: list[str] = []
    cursor = 0
    end = len(string)
    while cursor < end:
        if (brace := string.find('{', cursor)) < 0:
            output.append(string[cursor:])
            break
        output.append(string[cursor:brace])
        if string.startswith('{', brace + 1):
            output.append('{')
            cursor = brace + 2
            continue
        if (after := skip_past_constant(string, brace)) is None:
            raise ConstantError('Unclosed constant')
        value = yield from _expand_constant(string[brace + 1:after - 1], host, custom, run_code)
        output.append(value)
        cursor = after
        if value.endswith('\\') and string.startswith('\\', cursor):
            cursor += 1
    return ''.join(output)


def _expand_constant(
    constant: str,
    host: ConstantHost,
    custom: Mapping[str, str] | None,
    run_code: CodeRunner | None,
) -> Generator[Any, Any, str]:
    """
    The value of one constant, given without its braces, as Setup's `ExpandIndividualConst`
    computes it: each part of a parameter is expanded only when Setup needs it.
    """
    original = constant
    if (constant := _read_as(constant, host)) == '\\':
        return '\\'
    if (name := _value_name(constant, host.release)) is not None:
        return host.constant(name, original)
    if constant.startswith('%'):
        variable, default = split_constant_parameter(constant[1:], '|')
        has_default = default is not None
        variable = decode_percent_escapes(variable)
        default = decode_percent_escapes(default or '')
        if variable is None or default is None:
            return ''
        value = host.environment((yield from expand_constants(variable, host, custom, run_code)), has_default)
        if not value:
            value = yield from expand_constants(default, host, custom, run_code)
        return value
    if constant.startswith('reg:'):
        return (yield from _expand_registry_constant(constant[4:], host, custom, run_code))
    if constant.startswith('ini:'):
        return (yield from _expand_ini_constant(constant[4:], host, custom, run_code))
    if constant.startswith('param:'):
        name, default = split_constant_parameter(constant[6:], '|')
        name, default = _decoded('param', name, default or '')
        name = yield from expand_constants(name, host, custom, run_code)
        default = yield from expand_constants(default, host, custom, run_code)
        return host.parameter(name, default)
    if constant.startswith('code:'):
        function, default = split_constant_parameter(constant[5:], '|')
        function, default = _decoded('code', function, default or '')
        default = yield from expand_constants(default, host, custom, run_code)
        if run_code is None:
            raise NotImplementedError(F'The emulator cannot run the function {function} of a code constant without the script.')
        return (yield from run_code(function, default))
    if constant.startswith('drive:'):
        path, = _decoded('drive', constant[6:])
        return host.drive((yield from expand_constants(path, host, custom, run_code)))
    if constant.startswith('cm:'):
        return (yield from _expand_message_constant(constant[3:], host, custom, run_code))
    if custom is not None and constant and (value := custom.get(constant)) is not None:
        return value
    raise ConstantError(F'Unknown constant "{original}"')


def _expand_registry_constant(
    parameter: str,
    host: ConstantHost,
    custom: Mapping[str, str] | None,
    run_code: CodeRunner | None,
) -> Generator[Any, Any, str]:
    """
    Expands `reg:HKxx\\Key,Value|Default`. The root may end in 32 or 64 for a registry view.
    Setup expands the default first, then the key, and the value name only when the key opens.
    """
    root, rest = split_constant_parameter(parameter, '\\')
    if rest is None or not root:
        raise ConstantError('Failed to parse "reg" constant')
    view = None
    if root.endswith('32'):
        root, view = root[:-2], 32
    elif root.endswith('64'):
        if not host.win64:
            raise ConstantError('Cannot access a 64-bit key in a "reg" constant on this version of Windows')
        root, view = root[:-2], 64
    if (root := root.upper()) not in ('HKA', 'HKCR', 'HKCU', 'HKLM', 'HKU', 'HKCC'):
        raise ConstantError('Failed to parse "reg" constant')
    rest, default = split_constant_parameter(rest, '|')
    key, value = split_constant_parameter(rest, ',')
    if value is None:
        raise ConstantError('Failed to parse "reg" constant')
    key, value, default = _decoded('reg', key, value, default or '')
    default = yield from expand_constants(default, host, custom, run_code)
    key = yield from expand_constants(key, host, custom, run_code)
    if not host.registry(root, view, key):
        return default
    value = yield from expand_constants(value, host, custom, run_code)
    return host.registry_value(root, view, key, value, default)


def _expand_ini_constant(
    parameter: str,
    host: ConstantHost,
    custom: Mapping[str, str] | None,
    run_code: CodeRunner | None,
) -> Generator[Any, Any, str]:
    """
    Expands `ini:File,Section,Key|Default`; Setup expands all four parts in this order.
    """
    file, rest = split_constant_parameter(parameter, ',')
    if rest is None or not file:
        raise ConstantError('Failed to parse "ini" constant')
    rest, default = split_constant_parameter(rest, '|')
    section, key = split_constant_parameter(rest, ',')
    if key is None:
        raise ConstantError('Failed to parse "ini" constant')
    parts = []
    for part in _decoded('ini', file, section, key, default or ''):
        parts.append((yield from expand_constants(part, host, custom, run_code)))
    file, section, key, default = parts
    return host.ini_value(file, section, key, default)


def _expand_message_constant(
    parameter: str,
    host: ConstantHost,
    custom: Mapping[str, str] | None,
    run_code: CodeRunner | None,
) -> Generator[Any, Any, str]:
    """
    Expands `cm:Name,Argument,...`: the name is taken as written, each of up to nine arguments is
    decoded and expanded, and the message of the active language receives them.
    """
    name, rest = split_constant_parameter(parameter, ',')
    arguments: list[str] = []
    while rest is not None and len(arguments) < 9:
        argument, rest = split_constant_parameter(rest, ',')
        argument, = _decoded('cm', argument)
        arguments.append((yield from expand_constants(argument, host, custom, run_code)))
    if (message := host.custom_message(name)) is None:
        raise ConstantError(F'Unknown custom message name "{name}" in "cm" constant')
    return host.format_message(message, arguments)

Global variables

var CodeRunner

Runs the function of the script with the given name and argument for a code constant and returns its text; the run of the script that asks for an expansion gives it.

Functions

def skip_past_constant(string, start)

The index after the constant whose opening brace is at the given index, or None when the constant is not closed. Two opening braces are one escaped brace. Inside a constant, braces nest, and two opening braces in a row count as text.

Expand source code Browse git
def skip_past_constant(string: str, start: int) -> int | None:
    """
    The index after the constant whose opening brace is at the given index, or `None` when the
    constant is not closed. Two opening braces are one escaped brace. Inside a constant, braces
    nest, and two opening braces in a row count as text.
    """
    end = len(string)
    cursor = start + 1
    if cursor >= end:
        return None
    if string[cursor] == '{':
        return cursor + 1
    level = 1
    last_open = -1
    while cursor < end:
        character = string[cursor]
        if character == '{':
            if last_open != cursor - 1:
                level += 1
                last_open = cursor
            else:
                level -= 1
        elif character == '}':
            level -= 1
            if level == 0:
                return cursor + 1
        cursor += 1
    return None
def constant_position(character, string)

The index of the first occurrence of the character in the string outside of any constant, or -1 when there is none.

Expand source code Browse git
def constant_position(character: str, string: str) -> int:
    """
    The index of the first occurrence of the character in the string outside of any constant, or
    -1 when there is none.
    """
    cursor = 0
    while cursor < len(string):
        if string[cursor] == character:
            return cursor
        if string[cursor] != '{':
            cursor += 1
        elif (cursor := skip_past_constant(string, cursor)) is None:
            break
    return -1
def split_constant_parameter(string, character)

Splits the string at the first occurrence of the character outside of any constant. The second part is None when the character does not occur.

Expand source code Browse git
def split_constant_parameter(string: str, character: str) -> tuple[str, str | None]:
    """
    Splits the string at the first occurrence of the character outside of any constant. The second
    part is `None` when the character does not occur.
    """
    if (index := constant_position(character, string)) < 0:
        return string, None
    return string[:index], string[index + 1:]
def decode_percent_escapes(string)

Decodes each percent sign followed by two hex digits into the character with that code, except inside constants. The result is None when a percent sign is not followed by two hex digits or a constant is not closed.

Expand source code Browse git
def decode_percent_escapes(string: str) -> str | None:
    """
    Decodes each percent sign followed by two hex digits into the character with that code, except
    inside constants. The result is `None` when a percent sign is not followed by two hex digits or
    a constant is not closed.
    """
    output: list[str] = []
    cursor = 0
    while cursor < len(string):
        character = string[cursor]
        if character == '{':
            if (end := skip_past_constant(string, cursor)) is None:
                return None
            output.append(string[cursor:end])
            cursor = end
        elif character == '%':
            digits = string[cursor + 1:cursor + 3]
            if len(digits) != 2 or not _HEX_DIGITS.issuperset(digits):
                return None
            output.append(chr(int(digits, 16)))
            cursor += 3
        else:
            output.append(character)
            cursor += 1
    return ''.join(output)
def fmt_message(message, arguments)

Setup's FmtMessage: %1 to %9 stand for the arguments and %% for a percent sign; a placeholder without its argument stays.

Expand source code Browse git
def fmt_message(message: str, arguments: list[str]) -> str:
    """
    Setup's `FmtMessage`: `%1` to `%9` stand for the arguments and `%%` for a percent sign; a
    placeholder without its argument stays.
    """
    def substitute(match: re.Match[str]):
        if (spec := match[1]) == '%':
            return '%'
        if (k := int(spec) - 1) < len(arguments):
            return arguments[k]
        return match[0]
    return re.sub('%([1-9%])', substitute, message)
def expand_constants(string, host, custom=None, run_code=None)

Expands the constants in the given string as Setup's ExpandConstEx2 does, with the custom constants that Setup passes for the text, by their names, and the runner of code constants of the script that asks for the expansion. The value of a constant is not scanned again, and a backslash right after a value that ends in one is dropped. Without a runner, a code constant fails as a value that the emulator does not have.

Expand source code Browse git
def expand_constants(
    string: str,
    host: ConstantHost,
    custom: Mapping[str, str] | None = None,
    run_code: CodeRunner | None = None,
) -> Generator[Any, Any, str]:
    """
    Expands the constants in the given string as Setup's `ExpandConstEx2` does, with the custom
    constants that Setup passes for the text, by their names, and the runner of code constants
    of the script that asks for the expansion. The value of a constant is not scanned again, and a
    backslash right after a value that ends in one is dropped. Without a runner, a code constant
    fails as a value that the emulator does not have.
    """
    output: list[str] = []
    cursor = 0
    end = len(string)
    while cursor < end:
        if (brace := string.find('{', cursor)) < 0:
            output.append(string[cursor:])
            break
        output.append(string[cursor:brace])
        if string.startswith('{', brace + 1):
            output.append('{')
            cursor = brace + 2
            continue
        if (after := skip_past_constant(string, brace)) is None:
            raise ConstantError('Unclosed constant')
        value = yield from _expand_constant(string[brace + 1:after - 1], host, custom, run_code)
        output.append(value)
        cursor = after
        if value.endswith('\\') and string.startswith('\\', cursor):
            cursor += 1
    return ''.join(output)

Classes

class ConstantError (reason)

The internal error that Setup raises for a constant that it cannot expand.

Expand source code Browse git
class ConstantError(InternalError):
    """
    The internal error that Setup raises for a constant that it cannot expand.
    """

Ancestors

class ConstantHost (*args, **kwargs)

The machine and the Setup that a constant is expanded for. A method raises ConstantError for a value that Setup cannot give on this machine, and any other exception for a value that the host does not know.

Expand source code Browse git
class ConstantHost(Protocol):
    """
    The machine and the Setup that a constant is expanded for. A method raises `ConstantError` for
    a value that Setup cannot give on this machine, and any other exception for a value that the
    host does not know.
    """
    release: SetupRelease
    """
    The release of Setup.
    """
    admin: bool
    """
    Whether Setup installs for all users, which decides what the `auto` constants stand for.
    """
    win64: bool
    """
    Whether the machine runs a 64-bit Windows.
    """

    def constant(self, name: str, original: str) -> str:
        """
        The value of a constant without parameters, given by the name that Setup reads it as and
        by the name that the script wrote.
        """
        ...

    def environment(self, name: str, has_default: bool) -> str:
        """
        The value of an environment variable, empty when it is not set. Setup uses the default of
        the constant when the value is empty; `has_default` tells whether the constant has one.
        """
        ...

    def registry(self, root: str, view: int | None, key: str) -> bool:
        """
        Whether the registry key opens; `view` is 32 or 64 for a forced registry view.
        """
        ...

    def registry_value(self, root: str, view: int | None, key: str, value: str, default: str) -> str:
        """
        The text of a value in a registry key that opens, or the default when it has none.
        """
        ...

    def ini_value(self, file: str, section: str, key: str, default: str) -> str:
        """
        The value of a key in an INI file, or the default.
        """
        ...

    def parameter(self, name: str, default: str) -> str:
        """
        The value of a command line parameter `/NAME=value`, or the default.
        """
        ...

    def custom_message(self, name: str) -> str | None:
        """
        The custom message of the active language with the given name, `None` when the setup has
        no such message.
        """
        ...

    def format_message(self, message: str, arguments: list[str]) -> str:
        """
        Puts the arguments into a message as Setup's `FmtMessage` does.
        """
        ...

    def drive(self, path: str) -> str:
        """
        The drive or network share at the start of a path.
        """
        ...

Ancestors

  • typing.Protocol
  • typing.Generic

Class variables

var release

The release of Setup.

var admin

Whether Setup installs for all users, which decides what the auto constants stand for.

var win64

Whether the machine runs a 64-bit Windows.

Methods

def constant(self, name, original)

The value of a constant without parameters, given by the name that Setup reads it as and by the name that the script wrote.

Expand source code Browse git
def constant(self, name: str, original: str) -> str:
    """
    The value of a constant without parameters, given by the name that Setup reads it as and
    by the name that the script wrote.
    """
    ...
def environment(self, name, has_default)

The value of an environment variable, empty when it is not set. Setup uses the default of the constant when the value is empty; has_default tells whether the constant has one.

Expand source code Browse git
def environment(self, name: str, has_default: bool) -> str:
    """
    The value of an environment variable, empty when it is not set. Setup uses the default of
    the constant when the value is empty; `has_default` tells whether the constant has one.
    """
    ...
def registry(self, root, view, key)

Whether the registry key opens; view is 32 or 64 for a forced registry view.

Expand source code Browse git
def registry(self, root: str, view: int | None, key: str) -> bool:
    """
    Whether the registry key opens; `view` is 32 or 64 for a forced registry view.
    """
    ...
def registry_value(self, root, view, key, value, default)

The text of a value in a registry key that opens, or the default when it has none.

Expand source code Browse git
def registry_value(self, root: str, view: int | None, key: str, value: str, default: str) -> str:
    """
    The text of a value in a registry key that opens, or the default when it has none.
    """
    ...
def ini_value(self, file, section, key, default)

The value of a key in an INI file, or the default.

Expand source code Browse git
def ini_value(self, file: str, section: str, key: str, default: str) -> str:
    """
    The value of a key in an INI file, or the default.
    """
    ...
def parameter(self, name, default)

The value of a command line parameter /NAME=value, or the default.

Expand source code Browse git
def parameter(self, name: str, default: str) -> str:
    """
    The value of a command line parameter `/NAME=value`, or the default.
    """
    ...
def custom_message(self, name)

The custom message of the active language with the given name, None when the setup has no such message.

Expand source code Browse git
def custom_message(self, name: str) -> str | None:
    """
    The custom message of the active language with the given name, `None` when the setup has
    no such message.
    """
    ...
def format_message(self, message, arguments)

Puts the arguments into a message as Setup's FmtMessage does.

Expand source code Browse git
def format_message(self, message: str, arguments: list[str]) -> str:
    """
    Puts the arguments into a message as Setup's `FmtMessage` does.
    """
    ...
def drive(self, path)

The drive or network share at the start of a path.

Expand source code Browse git
def drive(self, path: str) -> str:
    """
    The drive or network share at the start of a path.
    """
    ...