Skip to content

Commit 5894137

Browse files
veeceeyTest User
authored andcommitted
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
1 parent 32da603 commit 5894137

3 files changed

Lines changed: 65 additions & 7 deletions

File tree

src/structlog/_output.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,13 +256,17 @@ class BytesLogger:
256256
.. versionadded:: 20.2.0
257257
"""
258258

259-
__slots__ = ("_file", "_flush", "_lock", "_write")
259+
__slots__ = ("_file", "_flush", "_lock", "_write", "name")
260260

261-
def __init__(self, file: BinaryIO | None = None):
261+
def __init__(
262+
self, file: BinaryIO | None = None, *, name: str | None = None
263+
):
262264
self._file = file or sys.stdout.buffer
263265
self._write = self._file.write
264266
self._flush = self._file.flush
265267

268+
self.name = name
269+
266270
self._lock = _get_lock_for_file(self._file)
267271

268272
def __getstate__(self) -> str:
@@ -345,4 +349,4 @@ def __init__(self, file: BinaryIO | None = None):
345349
self._file = file
346350

347351
def __call__(self, *args: Any) -> BytesLogger:
348-
return BytesLogger(self._file)
352+
return BytesLogger(self._file, name=args[0] if args else None)

tests/test_output.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,22 @@ def test_deepcopy(self, capsys):
295295
assert "hello\n" == out
296296
assert "" == err
297297

298+
def test_name_attribute(self):
299+
"""
300+
BytesLogger accepts a name keyword argument.
301+
"""
302+
bl = BytesLogger(name="test_logger")
303+
304+
assert "test_logger" == bl.name
305+
306+
def test_name_defaults_to_none(self):
307+
"""
308+
BytesLogger name defaults to None when not provided.
309+
"""
310+
bl = BytesLogger()
311+
312+
assert bl.name is None
313+
298314
def test_deepcopy_no_stdout(self, tmp_path):
299315
"""
300316
Only BytesLoggers that log to stdout or stderr can be deepcopy-ed.
@@ -327,9 +343,27 @@ def test_passes_file(self):
327343

328344
assert stderr is pl._file
329345

330-
def test_ignores_args(self):
346+
def test_passes_name_from_args(self):
331347
"""
332-
BytesLogger doesn't take positional arguments. If any are passed to
333-
the factory, they are not passed to the logger.
348+
The first positional argument is used as the logger name.
334349
"""
335-
BytesLoggerFactory()(1, 2, 3)
350+
bl = BytesLoggerFactory()("my_logger")
351+
352+
assert "my_logger" == bl.name
353+
354+
def test_name_defaults_to_none(self):
355+
"""
356+
If no positional arguments are passed to the factory, the logger
357+
name defaults to None.
358+
"""
359+
bl = BytesLoggerFactory()()
360+
361+
assert bl.name is None
362+
363+
def test_extra_args_ignored(self):
364+
"""
365+
Positional arguments beyond the first are silently ignored.
366+
"""
367+
bl = BytesLoggerFactory()("my_logger", 2, 3)
368+
369+
assert "my_logger" == bl.name

tests/test_stdlib.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import pytest_asyncio
2121

2222
from structlog import (
23+
BytesLogger,
2324
PrintLogger,
2425
ReturnLogger,
2526
configure,
@@ -569,6 +570,25 @@ def test_logger_name_added_with_record(self, make_log_record):
569570

570571
assert name == event_dict["logger"]
571572

573+
def test_logger_name_added_with_bytes_logger(self):
574+
"""
575+
add_logger_name works with BytesLogger that has a name attribute.
576+
"""
577+
name = "sample-name"
578+
logger = BytesLogger(name=name)
579+
event_dict = add_logger_name(logger, None, {})
580+
581+
assert name == event_dict["logger"]
582+
583+
def test_logger_name_none_with_unnamed_bytes_logger(self):
584+
"""
585+
add_logger_name works with BytesLogger without a name, returning None.
586+
"""
587+
logger = BytesLogger()
588+
event_dict = add_logger_name(logger, None, {})
589+
590+
assert event_dict["logger"] is None
591+
572592

573593
def extra_dict() -> dict[str, Any]:
574594
"""

0 commit comments

Comments
 (0)