diff --git a/litellm/proxy/common_utils/banner.py b/litellm/proxy/common_utils/banner.py index 25ad5e295f6..dd203352b05 100644 --- a/litellm/proxy/common_utils/banner.py +++ b/litellm/proxy/common_utils/banner.py @@ -17,3 +17,10 @@ def show_banner(): click.echo(f"\n{LITELLM_BANNER}\n") except ImportError: print("\n") # noqa: T201 + except UnicodeEncodeError: + # Reaching here means the import succeeded and only the encode failed, so `click` is + # bound and is the same channel the banner just went out on. Using it rather than + # `print` keeps the line free of a T201 suppression, which the two ruff configs + # disagree about: ruff.toml enables T20 and needs one, ruff-strict.toml does not + # enable it and counts one as dead under RUF100. + click.echo("\n LiteLLM\n") diff --git a/tests/test_litellm/proxy/common_utils/test_banner.py b/tests/test_litellm/proxy/common_utils/test_banner.py new file mode 100644 index 00000000000..ce0915720ad --- /dev/null +++ b/tests/test_litellm/proxy/common_utils/test_banner.py @@ -0,0 +1,36 @@ +import io +import os +import sys +from contextlib import redirect_stdout + +sys.path.insert(0, os.path.abspath("../../../..")) + +from litellm.proxy.common_utils.banner import show_banner + + +def _cp1252_stdout() -> io.TextIOWrapper: + """A stdout that cannot encode the banner, like a Windows console on the default code page.""" + return io.TextIOWrapper(io.BytesIO(), encoding="cp1252", newline="") + + +class TestShowBanner: + def test_show_banner_survives_a_stdout_that_cannot_encode_it(self): + """The banner is decorative; an unencodable console must not stop the proxy from starting.""" + stream = _cp1252_stdout() + + with redirect_stdout(stream): + show_banner() + + stream.flush() + printed = stream.buffer.getvalue().decode("cp1252") + assert "LiteLLM" in printed + + def test_show_banner_prints_the_full_banner_when_stdout_can_encode_it(self): + stream = io.TextIOWrapper(io.BytesIO(), encoding="utf-8", newline="") + + with redirect_stdout(stream): + show_banner() + + stream.flush() + printed = stream.buffer.getvalue().decode("utf-8") + assert "██" in printed