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.
Projecthynek/structlog
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
| 12 | 12 | >>> with tmp_bind(logger, x=5) as tmp_logger: | |
| 13 | 13 | ... tmp_logger.msg('event') | |
| 14 | 14 | 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. | |
| 27 | 15 | """ | |
| 28 | 16 | if not issubclass(logger._current_context_class, _ThreadLocalDictWrapper): | |
| 29 | 17 | raise ValueError( | |
| ⋯ | |||
| 31 | 19 | 'wrapped with wrap_dict. You context class is {0!r}.' | |
| 32 | 20 | .format(logger._current_context_class) | |
| 33 | 21 | ) | |
| 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) | |
| 34 | 26 | saved = logger._context.copy() | |
| 35 | 27 | tmp_logger = logger.bind(**tmp_values) | |
| 36 | 28 | yield tmp_logger | |
The check that tells the two apart
fail→pass·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.