From 261f89820872a281119ffe6fb0a398380da54661 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sun, 8 Feb 2026 15:01:23 -0800 Subject: [PATCH 1/2] Add name attribute to BytesLogger for add_logger_name compatibility BytesLogger lacked a `name` attribute, causing `add_logger_name` to raise AttributeError when used with BytesLoggerFactory. The first positional argument passed to BytesLoggerFactory (the logger name from `get_logger()`) is now forwarded to BytesLogger as its `name` attribute. Closes #734 Co-Authored-By: Claude Opus 4.6 --- src/structlog/_output.py | 12 +++++++++--- tests/test_output.py | 42 ++++++++++++++++++++++++++++++++++++---- tests/test_stdlib.py | 23 ++++++++++++++++++++++ 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/structlog/_output.py b/src/structlog/_output.py index b1612de6..ac9b8b8e 100644 --- a/src/structlog/_output.py +++ b/src/structlog/_output.py @@ -256,13 +256,17 @@ class BytesLogger: .. versionadded:: 20.2.0 """ - __slots__ = ("_file", "_flush", "_lock", "_write") + __slots__ = ("_file", "_flush", "_lock", "_write", "name") - def __init__(self, file: BinaryIO | None = None): + def __init__( + self, file: BinaryIO | None = None, *, name: str | None = None + ): self._file = file or sys.stdout.buffer self._write = self._file.write self._flush = self._file.flush + self.name = name + self._lock = _get_lock_for_file(self._file) def __getstate__(self) -> str: @@ -345,4 +349,6 @@ def __init__(self, file: BinaryIO | None = None): self._file = file def __call__(self, *args: Any) -> BytesLogger: - return BytesLogger(self._file) + return BytesLogger( + self._file, name=args[0] if args else None + ) diff --git a/tests/test_output.py b/tests/test_output.py index 8553aad9..c298dd9d 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -295,6 +295,22 @@ def test_deepcopy(self, capsys): assert "hello\n" == out assert "" == err + def test_name_attribute(self): + """ + BytesLogger accepts a name keyword argument. + """ + bl = BytesLogger(name="test_logger") + + assert "test_logger" == bl.name + + def test_name_defaults_to_none(self): + """ + BytesLogger name defaults to None when not provided. + """ + bl = BytesLogger() + + assert bl.name is None + def test_deepcopy_no_stdout(self, tmp_path): """ Only BytesLoggers that log to stdout or stderr can be deepcopy-ed. @@ -327,9 +343,27 @@ def test_passes_file(self): assert stderr is pl._file - def test_ignores_args(self): + def test_passes_name_from_args(self): """ - BytesLogger doesn't take positional arguments. If any are passed to - the factory, they are not passed to the logger. + The first positional argument is used as the logger name. """ - BytesLoggerFactory()(1, 2, 3) + bl = BytesLoggerFactory()("my_logger") + + assert "my_logger" == bl.name + + def test_name_defaults_to_none(self): + """ + If no positional arguments are passed to the factory, the logger + name defaults to None. + """ + bl = BytesLoggerFactory()() + + assert bl.name is None + + def test_extra_args_ignored(self): + """ + Positional arguments beyond the first are silently ignored. + """ + bl = BytesLoggerFactory()("my_logger", 2, 3) + + assert "my_logger" == bl.name diff --git a/tests/test_stdlib.py b/tests/test_stdlib.py index f54ae6b2..0488b4da 100644 --- a/tests/test_stdlib.py +++ b/tests/test_stdlib.py @@ -569,6 +569,29 @@ def test_logger_name_added_with_record(self, make_log_record): assert name == event_dict["logger"] + def test_logger_name_added_with_bytes_logger(self): + """ + add_logger_name works with BytesLogger that has a name attribute. + """ + from structlog import BytesLogger + + name = "sample-name" + logger = BytesLogger(name=name) + event_dict = add_logger_name(logger, None, {}) + + assert name == event_dict["logger"] + + def test_logger_name_none_with_unnamed_bytes_logger(self): + """ + add_logger_name works with BytesLogger without a name, returning None. + """ + from structlog import BytesLogger + + logger = BytesLogger() + event_dict = add_logger_name(logger, None, {}) + + assert event_dict["logger"] is None + def extra_dict() -> dict[str, Any]: """ From 520e07494f42640d3113360feea1793781ff7078 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 23:01:53 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/structlog/_output.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/structlog/_output.py b/src/structlog/_output.py index ac9b8b8e..da5495cf 100644 --- a/src/structlog/_output.py +++ b/src/structlog/_output.py @@ -349,6 +349,4 @@ def __init__(self, file: BinaryIO | None = None): self._file = file def __call__(self, *args: Any) -> BytesLogger: - return BytesLogger( - self._file, name=args[0] if args else None - ) + return BytesLogger(self._file, name=args[0] if args else None)