From 8fe656305049b0bddc372dc2e77770af52f223c4 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Tue, 3 Jan 2023 07:44:22 +0800 Subject: [PATCH] Fall back to non-localized message on Windows Windows does not implement LC_MESSAGES, and since PEP 668 is mainly designed for Linux distributions, we simply take the easier way out until someone wants an equivalent on Windows. --- src/pip/_internal/exceptions.py | 12 ++++++- tests/unit/test_exceptions.py | 62 +++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index 5e0559c9a..d28713ff7 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -703,7 +703,17 @@ class ExternallyManagedEnvironment(DiagnosticPipError): @staticmethod def _iter_externally_managed_error_keys() -> Iterator[str]: - lang, _ = locale.getlocale(locale.LC_MESSAGES) + # LC_MESSAGES is in POSIX, but not the C standard. The most common + # platform that does not implement this category is Windows, where + # using other categories for console message localization is equally + # unreliable, so we fall back to the locale-less vendor message. This + # can always be re-evaluated when a vendor proposes a new alternative. + try: + category = locale.LC_MESSAGES + except AttributeError: + lang: Optional[str] = None + else: + lang, _ = locale.getlocale(category) if lang is not None: yield f"Error-{lang}" for sep in ("-", "_"): diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index 17cccd591..6510b569e 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -492,9 +492,9 @@ class TestExternallyManagedEnvironment: orig_getlocal = locale.getlocale def fake_getlocale(category: int) -> Tuple[Optional[str], Optional[str]]: - """Fake getlocale() that always report zh_Hant.""" + """Fake getlocale() that always reports zh_Hant for LC_MESSASGES.""" result = orig_getlocal(category) - if category == locale.LC_MESSAGES: + if category == getattr(locale, "LC_MESSAGES", None): return "zh_Hant", result[1] return result @@ -541,6 +541,10 @@ class TestExternallyManagedEnvironment: assert not caplog.records assert str(exc.context) == self.default_text + @pytest.mark.skipif( + sys.platform == "win32", + reason="Localization disabled on Windows", + ) @pytest.mark.parametrize( "config, expected", [ @@ -594,3 +598,57 @@ class TestExternallyManagedEnvironment: exc = ExternallyManagedEnvironment.from_config(marker) assert not caplog.records assert str(exc.context) == expected + + @pytest.mark.skipif( + sys.platform != "win32", + reason="Non-Windows should implement localization", + ) + @pytest.mark.parametrize( + "config", + [ + pytest.param( + """\ + [externally-managed] + Error = 最後 + Error-en = English + Error-zh = 中文 + Error-zh_Hant = 繁體 + Error-zh_Hans = 简体 + """, + id="full", + ), + pytest.param( + """\ + [externally-managed] + Error = 最後 + Error-en = English + Error-zh = 中文 + Error-zh_Hans = 简体 + """, + id="no-variant", + ), + pytest.param( + """\ + [externally-managed] + Error = 最後 + Error-en = English + """, + id="fallback", + ), + ], + ) + def test_config_canonical_no_localization( + self, + caplog: pytest.LogCaptureFixture, + marker: pathlib.Path, + config: str, + ) -> None: + marker.write_text( + textwrap.dedent(config), + encoding="utf8", + ) + + with caplog.at_level(logging.WARNING, "pip._internal.exceptions"): + exc = ExternallyManagedEnvironment.from_config(marker) + assert not caplog.records + assert str(exc.context) == "最後"