summaryrefslogtreecommitdiff
path: root/test/lib/ansible_test/_internal/locale_util.py
blob: 3fb74ad5c0460a6273fa0770628a55b7f12bd733 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
"""Initialize locale settings. This must be imported very early in ansible-test startup."""

from __future__ import annotations

import locale
import sys
import typing as t

STANDARD_LOCALE = 'en_US.UTF-8'
"""
The standard locale used by ansible-test and its subprocesses and delegated instances.
"""

FALLBACK_LOCALE = 'C.UTF-8'
"""
The fallback locale to use when the standard locale is not available.
This was added in ansible-core 2.14 to allow testing in environments without the standard locale.
It was not needed in previous ansible-core releases since they do not verify the locale during startup.
"""


class LocaleError(SystemExit):
    """Exception to raise when locale related errors occur."""
    def __init__(self, message: str) -> None:
        super().__init__(f'ERROR: {message}')


def configure_locale() -> tuple[str, t.Optional[str]]:
    """Configure the locale, returning the selected locale and an optional warning."""

    if (fs_encoding := sys.getfilesystemencoding()).lower() != 'utf-8':
        raise LocaleError(f'ansible-test requires the filesystem encoding to be UTF-8, but "{fs_encoding}" was detected.')

    candidate_locales = STANDARD_LOCALE, FALLBACK_LOCALE

    errors: dict[str, str] = {}
    warning: t.Optional[str] = None
    configured_locale: t.Optional[str] = None

    for candidate_locale in candidate_locales:
        try:
            locale.setlocale(locale.LC_ALL, candidate_locale)
            locale.getlocale()
        except (locale.Error, ValueError) as ex:
            errors[candidate_locale] = str(ex)
        else:
            configured_locale = candidate_locale
            break

    if not configured_locale:
        raise LocaleError('ansible-test could not initialize a supported locale:\n' +
                          '\n'.join(f'{key}: {value}' for key, value in errors.items()))

    if configured_locale != STANDARD_LOCALE:
        warning = (f'Using locale "{configured_locale}" instead of "{STANDARD_LOCALE}". '
                   'Tests which depend on the locale may behave unexpectedly.')

    return configured_locale, warning


CONFIGURED_LOCALE, LOCALE_WARNING = configure_locale()