2021-10-03 12:45:28 +02:00
|
|
|
|
"""Tests the presentation style of exceptions."""
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
import io
|
2022-12-29 09:37:51 +01:00
|
|
|
|
import locale
|
|
|
|
|
import logging
|
|
|
|
|
import pathlib
|
|
|
|
|
import sys
|
2021-10-03 12:45:28 +02:00
|
|
|
|
import textwrap
|
2022-12-29 09:37:51 +01:00
|
|
|
|
from typing import Optional, Tuple
|
2021-10-03 12:45:28 +02:00
|
|
|
|
|
|
|
|
|
import pytest
|
2021-11-12 14:19:45 +01:00
|
|
|
|
from pip._vendor import rich
|
2021-10-03 12:45:28 +02:00
|
|
|
|
|
2022-12-29 09:37:51 +01:00
|
|
|
|
from pip._internal.exceptions import DiagnosticPipError, ExternallyManagedEnvironment
|
2021-10-03 12:45:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestDiagnosticPipErrorCreation:
|
|
|
|
|
def test_fails_without_reference(self) -> None:
|
|
|
|
|
class DerivedError(DiagnosticPipError):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
with pytest.raises(AssertionError) as exc_info:
|
|
|
|
|
DerivedError(message="", context=None, hint_stmt=None)
|
|
|
|
|
|
|
|
|
|
assert str(exc_info.value) == "error reference not provided!"
|
|
|
|
|
|
|
|
|
|
def test_can_fetch_reference_from_subclass(self) -> None:
|
|
|
|
|
class DerivedError(DiagnosticPipError):
|
|
|
|
|
reference = "subclass-reference"
|
|
|
|
|
|
|
|
|
|
obj = DerivedError(message="", context=None, hint_stmt=None)
|
|
|
|
|
assert obj.reference == "subclass-reference"
|
|
|
|
|
|
|
|
|
|
def test_can_fetch_reference_from_arguments(self) -> None:
|
|
|
|
|
class DerivedError(DiagnosticPipError):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
obj = DerivedError(
|
|
|
|
|
message="", context=None, hint_stmt=None, reference="subclass-reference"
|
|
|
|
|
)
|
|
|
|
|
assert obj.reference == "subclass-reference"
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"name",
|
|
|
|
|
[
|
|
|
|
|
"BADNAME",
|
|
|
|
|
"BadName",
|
|
|
|
|
"bad_name",
|
|
|
|
|
"BAD_NAME",
|
|
|
|
|
"_bad",
|
|
|
|
|
"bad-name-",
|
|
|
|
|
"bad--name",
|
|
|
|
|
"-bad-name",
|
|
|
|
|
"bad-name-due-to-1-number",
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_rejects_non_kebab_case_names(self, name: str) -> None:
|
|
|
|
|
class DerivedError(DiagnosticPipError):
|
|
|
|
|
reference = name
|
|
|
|
|
|
|
|
|
|
with pytest.raises(AssertionError) as exc_info:
|
|
|
|
|
DerivedError(message="", context=None, hint_stmt=None)
|
|
|
|
|
|
|
|
|
|
assert str(exc_info.value) == "error reference must be kebab-case!"
|
|
|
|
|
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
def rendered_in_ascii(error: DiagnosticPipError, *, color: bool = False) -> str:
|
|
|
|
|
with io.BytesIO() as stream:
|
|
|
|
|
console = rich.console.Console(
|
|
|
|
|
force_terminal=False,
|
2021-12-03 16:06:44 +01:00
|
|
|
|
file=io.TextIOWrapper(stream, encoding="ascii", newline=""),
|
2021-11-12 14:19:45 +01:00
|
|
|
|
color_system="truecolor" if color else None,
|
|
|
|
|
)
|
|
|
|
|
console.print(error)
|
|
|
|
|
return stream.getvalue().decode("ascii")
|
|
|
|
|
|
|
|
|
|
|
2021-10-03 12:45:28 +02:00
|
|
|
|
class TestDiagnosticPipErrorPresentation_ASCII:
|
|
|
|
|
def test_complete(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong, which is what caused this error.",
|
2021-10-03 12:45:28 +02:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
assert rendered_in_ascii(err) == textwrap.dedent(
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""\
|
2021-11-12 14:19:45 +01:00
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
2021-10-03 12:45:28 +02:00
|
|
|
|
Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
|
|
|
|
Something went wrong
|
|
|
|
|
very wrong.
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
note: You did something wrong, which is what caused this error.
|
|
|
|
|
hint: Do it better next time, by trying harder.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_complete_color(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke.",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong.",
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def esc(code: str = "0") -> str:
|
|
|
|
|
return f"\x1b[{code}m"
|
|
|
|
|
|
|
|
|
|
assert rendered_in_ascii(err, color=True) == textwrap.dedent(
|
|
|
|
|
f"""\
|
|
|
|
|
{esc("1;31")}error{esc("0")}: {esc("1")}test-diagnostic{esc("0")}
|
|
|
|
|
|
|
|
|
|
Oh no!
|
|
|
|
|
It broke.
|
|
|
|
|
|
|
|
|
|
Something went wrong
|
|
|
|
|
very wrong.
|
|
|
|
|
|
|
|
|
|
{esc("1;35")}note{esc("0")}: You did something wrong.
|
|
|
|
|
{esc("1;36")}hint{esc("0")}: Do it better next time, by trying harder.
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_context(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context=None,
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong, which is what caused this error.",
|
2021-10-03 12:45:28 +02:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
assert rendered_in_ascii(err) == textwrap.dedent(
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""\
|
2021-11-12 14:19:45 +01:00
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
2021-10-03 12:45:28 +02:00
|
|
|
|
Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
note: You did something wrong, which is what caused this error.
|
|
|
|
|
hint: Do it better next time, by trying harder.
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_note(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt=None,
|
2021-10-03 12:45:28 +02:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
assert rendered_in_ascii(err) == textwrap.dedent(
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""\
|
2021-11-12 14:19:45 +01:00
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
2021-10-03 12:45:28 +02:00
|
|
|
|
Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
|
|
|
|
Something went wrong
|
|
|
|
|
very wrong.
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint: Do it better next time, by trying harder.
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_hint(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong, which is what caused this error.",
|
2021-10-03 12:45:28 +02:00
|
|
|
|
hint_stmt=None,
|
|
|
|
|
)
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
assert rendered_in_ascii(err) == textwrap.dedent(
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""\
|
2021-11-12 14:19:45 +01:00
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
2021-10-03 12:45:28 +02:00
|
|
|
|
Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
|
|
|
|
Something went wrong
|
|
|
|
|
very wrong.
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
note: You did something wrong, which is what caused this error.
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_context_no_hint(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context=None,
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong, which is what caused this error.",
|
2021-10-03 12:45:28 +02:00
|
|
|
|
hint_stmt=None,
|
|
|
|
|
)
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
assert rendered_in_ascii(err) == textwrap.dedent(
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""\
|
2021-11-12 14:19:45 +01:00
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
2021-10-03 12:45:28 +02:00
|
|
|
|
Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
note: You did something wrong, which is what caused this error.
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_context_no_note(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context=None,
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt=None,
|
2021-10-03 12:45:28 +02:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
assert rendered_in_ascii(err) == textwrap.dedent(
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""\
|
2021-11-12 14:19:45 +01:00
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
2021-10-03 12:45:28 +02:00
|
|
|
|
Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint: Do it better next time, by trying harder.
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_hint_no_note(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt=None,
|
2021-10-03 12:45:28 +02:00
|
|
|
|
hint_stmt=None,
|
|
|
|
|
)
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
assert rendered_in_ascii(err) == textwrap.dedent(
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""\
|
2021-11-12 14:19:45 +01:00
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
2021-10-03 12:45:28 +02:00
|
|
|
|
Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
|
|
|
|
Something went wrong
|
|
|
|
|
very wrong.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_hint_no_note_no_context(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context=None,
|
|
|
|
|
hint_stmt=None,
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt=None,
|
2021-10-03 12:45:28 +02:00
|
|
|
|
)
|
|
|
|
|
|
2021-11-12 14:19:45 +01:00
|
|
|
|
assert rendered_in_ascii(err) == textwrap.dedent(
|
2021-10-03 12:45:28 +02:00
|
|
|
|
"""\
|
2021-11-12 14:19:45 +01:00
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
2021-10-03 12:45:28 +02:00
|
|
|
|
Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
"""
|
|
|
|
|
)
|
2021-11-12 14:19:45 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def rendered(error: DiagnosticPipError, *, color: bool = False) -> str:
|
|
|
|
|
with io.StringIO() as stream:
|
|
|
|
|
console = rich.console.Console(
|
|
|
|
|
force_terminal=False,
|
|
|
|
|
file=stream,
|
|
|
|
|
color_system="truecolor" if color else None,
|
|
|
|
|
)
|
|
|
|
|
console.print(error)
|
|
|
|
|
return stream.getvalue()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestDiagnosticPipErrorPresentation_Unicode:
|
|
|
|
|
def test_complete(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong, which is what caused this error.",
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert rendered(err) == textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
|
|
|
|
× Oh no!
|
|
|
|
|
│ It broke. :(
|
|
|
|
|
╰─> Something went wrong
|
|
|
|
|
very wrong.
|
|
|
|
|
|
|
|
|
|
note: You did something wrong, which is what caused this error.
|
|
|
|
|
hint: Do it better next time, by trying harder.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_complete_color(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke.",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong.",
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def esc(code: str = "0") -> str:
|
|
|
|
|
return f"\x1b[{code}m"
|
|
|
|
|
|
|
|
|
|
assert rendered(err, color=True) == textwrap.dedent(
|
|
|
|
|
f"""\
|
|
|
|
|
{esc("1;31")}error{esc("0")}: {esc("1")}test-diagnostic{esc("0")}
|
|
|
|
|
|
|
|
|
|
{esc("31")}×{esc("0")} Oh no!
|
|
|
|
|
{esc("31")}│{esc("0")} It broke.
|
|
|
|
|
{esc("31")}╰─>{esc("0")} Something went wrong
|
|
|
|
|
{esc("31")} {esc("0")} very wrong.
|
|
|
|
|
|
|
|
|
|
{esc("1;35")}note{esc("0")}: You did something wrong.
|
|
|
|
|
{esc("1;36")}hint{esc("0")}: Do it better next time, by trying harder.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_context(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context=None,
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong, which is what caused this error.",
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert rendered(err) == textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
|
|
|
|
× Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
|
|
|
|
note: You did something wrong, which is what caused this error.
|
|
|
|
|
hint: Do it better next time, by trying harder.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_note(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt=None,
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert rendered(err) == textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
|
|
|
|
× Oh no!
|
|
|
|
|
│ It broke. :(
|
|
|
|
|
╰─> Something went wrong
|
|
|
|
|
very wrong.
|
|
|
|
|
|
|
|
|
|
hint: Do it better next time, by trying harder.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_hint(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong, which is what caused this error.",
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint_stmt=None,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert rendered(err) == textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
|
|
|
|
× Oh no!
|
|
|
|
|
│ It broke. :(
|
|
|
|
|
╰─> Something went wrong
|
|
|
|
|
very wrong.
|
|
|
|
|
|
|
|
|
|
note: You did something wrong, which is what caused this error.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_context_no_hint(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context=None,
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt="You did something wrong, which is what caused this error.",
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint_stmt=None,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert rendered(err) == textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
|
|
|
|
× Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
|
|
|
|
note: You did something wrong, which is what caused this error.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_context_no_note(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context=None,
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt=None,
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint_stmt="Do it better next time, by trying harder.",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert rendered(err) == textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
|
|
|
|
× Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
|
|
|
|
|
hint: Do it better next time, by trying harder.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_hint_no_note(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context="Something went wrong\nvery wrong.",
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt=None,
|
2021-11-12 14:19:45 +01:00
|
|
|
|
hint_stmt=None,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert rendered(err) == textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
|
|
|
|
× Oh no!
|
|
|
|
|
│ It broke. :(
|
|
|
|
|
╰─> Something went wrong
|
|
|
|
|
very wrong.
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_no_hint_no_note_no_context(self) -> None:
|
|
|
|
|
err = DiagnosticPipError(
|
|
|
|
|
reference="test-diagnostic",
|
|
|
|
|
message="Oh no!\nIt broke. :(",
|
|
|
|
|
context=None,
|
|
|
|
|
hint_stmt=None,
|
2021-12-03 15:22:18 +01:00
|
|
|
|
note_stmt=None,
|
2021-11-12 14:19:45 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert rendered(err) == textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
error: test-diagnostic
|
|
|
|
|
|
|
|
|
|
× Oh no!
|
|
|
|
|
It broke. :(
|
|
|
|
|
"""
|
|
|
|
|
)
|
2022-12-29 09:37:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestExternallyManagedEnvironment:
|
|
|
|
|
default_text = (
|
|
|
|
|
f"The Python environment under {sys.prefix} is managed externally, "
|
|
|
|
|
f"and may not be\nmanipulated by the user. Please use specific "
|
|
|
|
|
f"tooling from the distributor of\nthe Python installation to "
|
|
|
|
|
f"interact with this environment instead.\n"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
|
def patch_locale(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
|
|
orig_getlocal = locale.getlocale
|
|
|
|
|
|
|
|
|
|
def fake_getlocale(category: int) -> Tuple[Optional[str], Optional[str]]:
|
2023-01-03 00:44:22 +01:00
|
|
|
|
"""Fake getlocale() that always reports zh_Hant for LC_MESSASGES."""
|
2022-12-29 09:37:51 +01:00
|
|
|
|
result = orig_getlocal(category)
|
2023-01-03 00:44:22 +01:00
|
|
|
|
if category == getattr(locale, "LC_MESSAGES", None):
|
2022-12-29 09:37:51 +01:00
|
|
|
|
return "zh_Hant", result[1]
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(locale, "getlocale", fake_getlocale)
|
|
|
|
|
|
|
|
|
|
@pytest.fixture()
|
|
|
|
|
def marker(self, tmp_path: pathlib.Path) -> pathlib.Path:
|
|
|
|
|
marker = tmp_path.joinpath("EXTERNALLY-MANAGED")
|
|
|
|
|
marker.touch()
|
|
|
|
|
return marker
|
|
|
|
|
|
|
|
|
|
def test_invalid_config_format(
|
|
|
|
|
self,
|
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
|
marker: pathlib.Path,
|
|
|
|
|
) -> None:
|
|
|
|
|
marker.write_text("invalid", encoding="utf8")
|
|
|
|
|
|
|
|
|
|
with caplog.at_level(logging.WARNING, "pip._internal.exceptions"):
|
|
|
|
|
exc = ExternallyManagedEnvironment.from_config(marker)
|
|
|
|
|
assert len(caplog.records) == 1
|
|
|
|
|
assert caplog.records[-1].getMessage() == f"Failed to read {marker}"
|
|
|
|
|
|
|
|
|
|
assert str(exc.context) == self.default_text
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"config",
|
|
|
|
|
[
|
|
|
|
|
pytest.param("", id="empty"),
|
|
|
|
|
pytest.param("[foo]\nblah = blah", id="no-section"),
|
|
|
|
|
pytest.param("[externally-managed]\nblah = blah", id="no-key"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_config_without_key(
|
|
|
|
|
self,
|
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
|
marker: pathlib.Path,
|
|
|
|
|
config: str,
|
|
|
|
|
) -> None:
|
|
|
|
|
marker.write_text(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) == self.default_text
|
|
|
|
|
|
2023-01-03 00:44:22 +01:00
|
|
|
|
@pytest.mark.skipif(
|
|
|
|
|
sys.platform == "win32",
|
|
|
|
|
reason="Localization disabled on Windows",
|
|
|
|
|
)
|
2022-12-29 09:37:51 +01:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"config, expected",
|
|
|
|
|
[
|
|
|
|
|
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(
|
|
|
|
|
self,
|
|
|
|
|
caplog: pytest.LogCaptureFixture,
|
|
|
|
|
marker: pathlib.Path,
|
|
|
|
|
config: str,
|
|
|
|
|
expected: 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) == expected
|
2023-01-03 00:44:22 +01:00
|
|
|
|
|
|
|
|
|
@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) == "最後"
|