mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-27 18:39:00 +08:00
chore: prevent UnicodeEncodeError crashing error logging on Windows consoles (#13482)
fix windows testes folder
This commit is contained in:
committed by
GitHub
parent
cc97a32aee
commit
1d3a186138
@ -1,5 +1,8 @@
|
||||
"""Logging module for lfx package."""
|
||||
|
||||
from lfx.log._streams import make_streams_resilient
|
||||
from lfx.log.logger import configure, logger
|
||||
|
||||
make_streams_resilient()
|
||||
|
||||
__all__ = ["configure", "logger"]
|
||||
|
||||
29
src/lfx/src/lfx/log/_streams.py
Normal file
29
src/lfx/src/lfx/log/_streams.py
Normal file
@ -0,0 +1,29 @@
|
||||
"""Make process stdout/stderr tolerate characters their console codec cannot encode."""
|
||||
|
||||
import contextlib
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _reconfigure(stream: Any) -> None:
|
||||
reconfigure = getattr(stream, "reconfigure", None)
|
||||
if reconfigure is None:
|
||||
return
|
||||
with contextlib.suppress(Exception):
|
||||
reconfigure(errors="backslashreplace")
|
||||
|
||||
|
||||
def make_streams_resilient() -> None:
|
||||
"""Escape unencodable characters on write instead of raising.
|
||||
|
||||
Windows consoles default to a legacy code page (cp1252) whose codec raises
|
||||
UnicodeEncodeError on the box-drawing glyphs structlog's ConsoleRenderer
|
||||
emits when formatting a traceback. That second exception is raised inside
|
||||
the logging call itself, so it masks the original error and aborts the
|
||||
request that logged it. ``errors="backslashreplace"`` keeps the stream
|
||||
encoding but escapes anything the code page can't represent, so logging
|
||||
never crashes the caller. The underlying streams are reconfigured too
|
||||
because colorama wraps those same objects, regardless of wrap order.
|
||||
"""
|
||||
for stream in (sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__):
|
||||
_reconfigure(stream)
|
||||
1
src/lfx/tests/unit/log/__init__.py
Normal file
1
src/lfx/tests/unit/log/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Test suite for LFX logging."""
|
||||
61
src/lfx/tests/unit/log/test_streams.py
Normal file
61
src/lfx/tests/unit/log/test_streams.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""Regression tests for stdout/stderr encoding resilience on Windows consoles."""
|
||||
|
||||
import importlib
|
||||
import io
|
||||
|
||||
import structlog
|
||||
from lfx.log._streams import make_streams_resilient
|
||||
|
||||
|
||||
def _cp1252_stream() -> tuple[io.TextIOWrapper, io.BytesIO]:
|
||||
"""A text stream that behaves like a strict Windows cp1252 console."""
|
||||
raw = io.BytesIO()
|
||||
return io.TextIOWrapper(raw, encoding="cp1252"), raw
|
||||
|
||||
|
||||
def test_make_streams_resilient_sets_backslashreplace(monkeypatch):
|
||||
out, _ = _cp1252_stream()
|
||||
err, _ = _cp1252_stream()
|
||||
monkeypatch.setattr("sys.stdout", out)
|
||||
monkeypatch.setattr("sys.stderr", err)
|
||||
|
||||
make_streams_resilient()
|
||||
|
||||
assert out.errors == "backslashreplace"
|
||||
assert err.errors == "backslashreplace"
|
||||
|
||||
|
||||
def test_logging_exception_to_cp1252_stdout_does_not_crash(monkeypatch):
|
||||
"""A logged traceback must never raise UnicodeEncodeError on a cp1252 console.
|
||||
|
||||
structlog's ConsoleRenderer emits box-drawing glyphs that cp1252 cannot
|
||||
encode; without the resilience shim the encode error is raised inside the
|
||||
logging call, masking the original error and aborting the request.
|
||||
"""
|
||||
stream, raw = _cp1252_stream()
|
||||
monkeypatch.setattr("sys.stdout", stream)
|
||||
|
||||
make_streams_resilient()
|
||||
|
||||
logmod = importlib.import_module("lfx.log.logger")
|
||||
logmod.configure(log_level="ERROR", cache=False)
|
||||
log = structlog.get_logger("test")
|
||||
|
||||
msg = "THIS IS A TEST ERROR MESSAGE"
|
||||
try:
|
||||
raise ValueError(msg)
|
||||
except ValueError:
|
||||
log.exception("boom")
|
||||
|
||||
stream.flush()
|
||||
rendered = raw.getvalue().decode("cp1252", errors="replace")
|
||||
assert "THIS IS A TEST ERROR MESSAGE" in rendered
|
||||
|
||||
|
||||
def test_reconfigure_is_safe_when_stream_is_none(monkeypatch):
|
||||
monkeypatch.setattr("sys.stdout", None)
|
||||
monkeypatch.setattr("sys.stderr", None)
|
||||
monkeypatch.setattr("sys.__stdout__", None)
|
||||
monkeypatch.setattr("sys.__stderr__", None)
|
||||
|
||||
make_streams_resilient()
|
||||
Reference in New Issue
Block a user