1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
import asyncio
import sys
import threading
from unittest.mock import MagicMock
import pytest
from loguru import logger
from loguru._contextvars import load_contextvar_class
def test_contextualize(writer):
logger.add(writer, format="{message} {extra[foo]} {extra[baz]}")
with logger.contextualize(foo="bar", baz=123):
logger.info("Contextualized")
assert writer.read() == "Contextualized bar 123\n"
def test_contextualize_as_decorator(writer):
logger.add(writer, format="{message} {extra[foo]} {extra[baz]}")
@logger.contextualize(foo=123, baz="bar")
def task():
logger.info("Contextualized")
task()
assert writer.read() == "Contextualized 123 bar\n"
def test_contextualize_in_function(writer):
logger.add(writer, format="{message} {extra}")
def foobar():
logger.info("Foobar!")
with logger.contextualize(foobar="baz"):
foobar()
assert writer.read() == "Foobar! {'foobar': 'baz'}\n"
def test_contextualize_reset():
contexts = []
output = []
def sink(message):
contexts.append(message.record["extra"])
output.append(str(message))
logger.add(sink, format="{level} {message}")
logger.info("A")
with logger.contextualize(abc="def"):
logger.debug("B")
logger.warning("C")
logger.info("D")
assert contexts == [{}, {"abc": "def"}, {"abc": "def"}, {}]
assert output == ["INFO A\n", "DEBUG B\n", "WARNING C\n", "INFO D\n"]
@pytest.mark.xfail(sys.version_info < (3, 5, 3), reason="ContextVar backport not supported")
def test_contextualize_async(writer):
logger.add(writer, format="{message} {extra[i]}", catch=False)
async def task():
logger.info("Start")
await asyncio.sleep(0.1)
logger.info("End")
async def worker(i):
with logger.contextualize(i=i):
await task()
async def main():
workers = [worker(i) for i in range(5)]
await asyncio.gather(*workers)
await logger.complete()
asyncio.run(main())
assert sorted(writer.read().splitlines()) == ["End %d" % i for i in range(5)] + [
"Start %d" % i for i in range(5)
]
def test_contextualize_thread(writer):
logger.add(writer, format="{message} {extra[i]}")
def task():
logger.info("Processing")
def worker(entry_barrier, exit_barrier, i):
with logger.contextualize(i=i):
entry_barrier.wait()
task()
exit_barrier.wait()
entry_barrier = threading.Barrier(5)
exit_barrier = threading.Barrier(5)
threads = [
threading.Thread(target=worker, args=(entry_barrier, exit_barrier, i)) for i in range(5)
]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
assert sorted(writer.read().splitlines()) == ["Processing %d" % i for i in range(5)]
def test_contextualize_before_bind(writer):
logger.add(writer, format="{message} {extra[foobar]}")
logger_2 = logger.bind(foobar="baz")
with logger.contextualize(foobar="baz_2"):
logger.info("A")
logger_2.info("B")
logger_2.info("C")
assert writer.read() == "A baz_2\nB baz\nC baz\n"
def test_contextualize_after_bind(writer):
logger.add(writer, format="{message} {extra[foobar]}")
with logger.contextualize(foobar="baz"):
logger_2 = logger.bind(foobar="baz_2")
logger.info("A")
logger_2.info("B")
logger_2.info("C")
assert writer.read() == "A baz\nB baz_2\nC baz_2\n"
def test_contextualize_using_bound(writer):
logger.add(writer, format="{message} {extra[foobar]}")
logger_2 = logger.bind(foobar="baz")
with logger_2.contextualize(foobar="baz_2"):
logger.info("A")
logger_2.info("B")
logger_2.info("C")
assert writer.read() == "A baz_2\nB baz\nC baz\n"
def test_contextualize_before_configure(writer):
logger.add(writer, format="{message} {extra[foobar]}")
logger.configure(extra={"foobar": "baz"})
with logger.contextualize(foobar="baz_2"):
logger.info("A")
logger.info("B")
assert writer.read() == "A baz_2\nB baz\n"
def test_contextualize_after_configure(writer):
logger.add(writer, format="{message} {extra[foobar]}")
with logger.contextualize(foobar="baz"):
logger.configure(extra={"foobar": "baz_2"})
logger.info("A")
logger.info("B")
assert writer.read() == "A baz\nB baz_2\n"
def test_nested_contextualize(writer):
logger.add(writer, format="{message} {extra[foobar]}")
with logger.contextualize(foobar="a"):
with logger.contextualize(foobar="b"):
logger.info("B")
logger.info("A")
with logger.contextualize(foobar="c"):
logger.info("C")
assert writer.read() == "B b\nA a\nC c\n"
def test_context_reset_despite_error(writer):
logger.add(writer, format="{message} {extra}")
try:
with logger.contextualize(foobar=456):
logger.info("Division")
1 / 0 # noqa: B018
except ZeroDivisionError:
logger.info("Error")
assert writer.read() == "Division {'foobar': 456}\nError {}\n"
# There is not CI runner available for Python 3.5.2. Consequently, we are just
# verifying third-library is properly imported to reach 100% coverage.
def test_contextvars_fallback_352(monkeypatch):
mock_module = MagicMock()
with monkeypatch.context() as context:
context.setattr(sys, "version_info", (3, 5, 2))
context.setitem(sys.modules, "contextvars", mock_module)
assert load_contextvar_class() == mock_module.ContextVar
|