One function

tmp_bind in hynek/structlog

The author described this change as Fix edge case in tmp_bind. It counts as a record because the check below fails on the code as it stood at 228661e23 and passes on bf80fa60a, with nothing else changed between the two runs.

Fix saved2013-09-06
Sharing licenceMIT · LICENSE-MIT
Change size+4 12

What the code was meant to do, written into the code itself as a docstring

Context manager for temporarily binding *tmp_values* to *logger*. Use it with a `with`-statement. Anything you bind here *or within* the with block will be erased afterwards. Although the *logger* passed in and the logger yielded log out the same data, it's possible that *logger* hasn't been converted to thread local storage if the context class has been set using :func:`structlog.loggers.BoundLogger.configure` and no values have been bound to it before calling tmp_bind. That means that if you bind additional values to your original logger, you'd get surprising results. Therefore I *strongly* recommend to use *only *the *yielded* logger inside of the `with` block.

The change

1212 >>> with tmp_bind(logger, x=5) as tmp_logger:
1313 ... tmp_logger.msg('event')
1414 x=5 event='event'
15-
16- Although the *logger* passed in and the logger yielded log out the same
17- data, it's possible that *logger* hasn't been converted to thread local
18- storage if the context class has been set using
19- :func:`structlog.loggers.BoundLogger.configure` and no values have been
20- bound to it before calling tmp_bind.
21-
22- That means that if you bind additional values to your original logger,
23- you'd get surprising results.
24-
25- Therefore I *strongly* recommend to use *only *the *yielded* logger inside
26- of the `with` block.
2715 """
2816 if not issubclass(logger._current_context_class, _ThreadLocalDictWrapper):
2917 raise ValueError(
3119 'wrapped with wrap_dict. You context class is {0!r}.'
3220 .format(logger._current_context_class)
3321 )
22+ if not isinstance(logger._context, logger._current_context_class):
23+ # This is a terrible thing to do. But it avoids very confusing
24+ # behavior. Don't do this at home.
25+ logger._context = logger._current_context_class(logger._context)
3426 saved = logger._context.copy()
3527 tmp_logger = logger.bind(**tmp_values)
3628 yield tmp_logger

The check that tells the two apart

failpass·tests/test_threadlocal.py::TestTmpBind::test_converts_passed_and_yielded_logger

Check file tests/test_threadlocal.py, taken without changes from the fix and copied onto the older code, so the exact same check runs against both versions.

Origin and history

The code before it228661e23409ef979ba78f934763743593e6a293
Broken version dated2013-09-06
Modulestructlog.threadlocal
Units changedtmp_bind
Fingerprintd2fc18759342da94
Checked2026-08-18 by goldset/0.1

Every field above is generated by our program. None of it is written by hand.

Other bugs found in hynek/structlog