mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Redirecting stdout to both devtools and logfile
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
from io import StringIO
|
||||
|
||||
import pytest
|
||||
|
||||
from textual.devtools.server import _make_devtools_aiohttp_app
|
||||
@@ -25,3 +27,8 @@ async def devtools(aiohttp_client, server):
|
||||
yield devtools
|
||||
await devtools.disconnect()
|
||||
await client.close()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def in_memory_logfile():
|
||||
yield StringIO()
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import types
|
||||
from asyncio import Queue
|
||||
from datetime import datetime
|
||||
|
||||
@@ -11,6 +12,9 @@ from tests.utilities.render import wait_for_predicate
|
||||
from textual.devtools.client import DevtoolsClient
|
||||
from textual.devtools.redirect_output import DevtoolsLog
|
||||
|
||||
CALLER_LINENO = 123
|
||||
CALLER_PATH = "a/b/c.py"
|
||||
CALLER = types.SimpleNamespace(filename=CALLER_PATH, lineno=CALLER_LINENO)
|
||||
TIMESTAMP = 1649166819
|
||||
|
||||
|
||||
@@ -26,15 +30,15 @@ async def test_devtools_client_is_connected(devtools):
|
||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
||||
async def test_devtools_log_places_encodes_and_queues_message(devtools):
|
||||
await devtools._stop_log_queue_processing()
|
||||
devtools.log(DevtoolsLog("Hello, world!"))
|
||||
devtools.log(DevtoolsLog("Hello, world!", CALLER))
|
||||
queued_log = await devtools.log_queue.get()
|
||||
queued_log_json = json.loads(queued_log)
|
||||
assert queued_log_json == {
|
||||
"type": "client_log",
|
||||
"payload": {
|
||||
"timestamp": TIMESTAMP,
|
||||
"path": "",
|
||||
"line_number": 0,
|
||||
"path": CALLER_PATH,
|
||||
"line_number": CALLER_LINENO,
|
||||
"encoded_segments": "gANdcQAoY3JpY2guc2VnbWVudApTZWdtZW50CnEBWA0AAABIZWxsbywgd29ybGQhcQJOTodxA4FxBGgBWAEAAAAKcQVOTodxBoFxB2Uu",
|
||||
},
|
||||
}
|
||||
@@ -43,15 +47,15 @@ async def test_devtools_log_places_encodes_and_queues_message(devtools):
|
||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
||||
async def test_devtools_log_places_encodes_and_queues_many_logs_as_string(devtools):
|
||||
await devtools._stop_log_queue_processing()
|
||||
devtools.log(DevtoolsLog(("hello", "world")))
|
||||
devtools.log(DevtoolsLog(("hello", "world"), CALLER))
|
||||
queued_log = await devtools.log_queue.get()
|
||||
queued_log_json = json.loads(queued_log)
|
||||
assert queued_log_json == {
|
||||
"type": "client_log",
|
||||
"payload": {
|
||||
"timestamp": TIMESTAMP,
|
||||
"path": "",
|
||||
"line_number": 0,
|
||||
"path": CALLER_PATH,
|
||||
"line_number": CALLER_LINENO,
|
||||
"encoded_segments": "gANdcQAoY3JpY2guc2VnbWVudApTZWdtZW50CnEBWAsAAABoZWxsbyB3b3JsZHECTk6HcQOBcQRoAVgBAAAACnEFTk6HcQaBcQdlLg==",
|
||||
},
|
||||
}
|
||||
@@ -63,10 +67,10 @@ async def test_devtools_log_spillover(devtools):
|
||||
devtools.log_queue = Queue(maxsize=2)
|
||||
|
||||
# Force spillover of 2
|
||||
devtools.log(DevtoolsLog((Panel("hello, world"),)))
|
||||
devtools.log(DevtoolsLog("second message"))
|
||||
devtools.log(DevtoolsLog("third message")) # Discarded by rate-limiting
|
||||
devtools.log(DevtoolsLog("fourth message")) # Discarded by rate-limiting
|
||||
devtools.log(DevtoolsLog((Panel("hello, world"),), CALLER))
|
||||
devtools.log(DevtoolsLog("second message", CALLER))
|
||||
devtools.log(DevtoolsLog("third message", CALLER)) # Discarded by rate-limiting
|
||||
devtools.log(DevtoolsLog("fourth message", CALLER)) # Discarded by rate-limiting
|
||||
|
||||
assert devtools.spillover == 2
|
||||
|
||||
@@ -75,7 +79,7 @@ async def test_devtools_log_spillover(devtools):
|
||||
await devtools.log_queue.get()
|
||||
|
||||
# Add another message now that we're under spillover threshold
|
||||
devtools.log(DevtoolsLog("another message"))
|
||||
devtools.log(DevtoolsLog("another message", CALLER))
|
||||
await devtools.log_queue.get()
|
||||
|
||||
# Ensure we're informing the server of spillover rate-limiting
|
||||
|
||||
@@ -5,16 +5,16 @@ from datetime import datetime
|
||||
|
||||
import time_machine
|
||||
|
||||
from textual.devtools.redirect_output import DevtoolsRedirector
|
||||
from textual.devtools.redirect_output import StdoutRedirector
|
||||
|
||||
TIMESTAMP = 1649166819
|
||||
|
||||
|
||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
||||
async def test_print_is_redirected_to_devtools(devtools):
|
||||
async def test_print_is_redirected_to_devtools(devtools, in_memory_logfile):
|
||||
await devtools._stop_log_queue_processing()
|
||||
|
||||
with redirect_stdout(DevtoolsRedirector(devtools)):
|
||||
with redirect_stdout(StdoutRedirector(devtools, in_memory_logfile)): # type: ignore
|
||||
print("Hello, world!")
|
||||
|
||||
assert devtools.log_queue.qsize() == 1
|
||||
@@ -32,10 +32,10 @@ async def test_print_is_redirected_to_devtools(devtools):
|
||||
|
||||
|
||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
||||
async def test_print_without_flush_not_sent_to_devtools(devtools):
|
||||
async def test_print_without_flush_not_sent_to_devtools(devtools, in_memory_logfile):
|
||||
await devtools._stop_log_queue_processing()
|
||||
|
||||
with redirect_stdout(DevtoolsRedirector(devtools)):
|
||||
with redirect_stdout(StdoutRedirector(devtools, in_memory_logfile)): # type: ignore
|
||||
# End is no longer newline character, so print will no longer
|
||||
# flush the output buffer by default.
|
||||
print("Hello, world!", end="")
|
||||
@@ -44,20 +44,20 @@ async def test_print_without_flush_not_sent_to_devtools(devtools):
|
||||
|
||||
|
||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
||||
async def test_print_forced_flush_sent_to_devtools(devtools):
|
||||
async def test_print_forced_flush_sent_to_devtools(devtools, in_memory_logfile):
|
||||
await devtools._stop_log_queue_processing()
|
||||
|
||||
with redirect_stdout(DevtoolsRedirector(devtools)):
|
||||
with redirect_stdout(StdoutRedirector(devtools, in_memory_logfile)): # type: ignore
|
||||
print("Hello, world!", end="", flush=True)
|
||||
|
||||
assert devtools.log_queue.qsize() == 1
|
||||
|
||||
|
||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
||||
async def test_print_multiple_args_batched_as_one_log(devtools):
|
||||
async def test_print_multiple_args_batched_as_one_log(devtools, in_memory_logfile):
|
||||
await devtools._stop_log_queue_processing()
|
||||
|
||||
with redirect_stdout(DevtoolsRedirector(devtools)):
|
||||
with redirect_stdout(StdoutRedirector(devtools, in_memory_logfile)): # type: ignore
|
||||
# We call print with multiple arguments here, but it
|
||||
# results in a single log added to the log queue.
|
||||
print("Hello", "world", "multiple")
|
||||
@@ -77,10 +77,10 @@ async def test_print_multiple_args_batched_as_one_log(devtools):
|
||||
|
||||
|
||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
||||
async def test_print_multiple_args_batched_as_one_log(devtools):
|
||||
async def test_print_multiple_args_batched_as_one_log(devtools, in_memory_logfile):
|
||||
await devtools._stop_log_queue_processing()
|
||||
redirector = DevtoolsRedirector(devtools)
|
||||
with redirect_stdout(redirector):
|
||||
redirector = StdoutRedirector(devtools, in_memory_logfile)
|
||||
with redirect_stdout(redirector): # type: ignore
|
||||
# This print adds 3 messages to the buffer that can be batched
|
||||
print("The first", "batch", "of logs", end="")
|
||||
# This message cannot be batched with the previous message,
|
||||
@@ -91,10 +91,10 @@ async def test_print_multiple_args_batched_as_one_log(devtools):
|
||||
|
||||
|
||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
||||
async def test_print_strings_containing_newline_flushed(devtools):
|
||||
async def test_print_strings_containing_newline_flushed(devtools, in_memory_logfile):
|
||||
await devtools._stop_log_queue_processing()
|
||||
|
||||
with redirect_stdout(DevtoolsRedirector(devtools)):
|
||||
with redirect_stdout(StdoutRedirector(devtools, in_memory_logfile)): # type: ignore
|
||||
# Flushing is disabled since end="", but the first
|
||||
# string will be flushed since it contains a newline
|
||||
print("Hel\nlo", end="")
|
||||
@@ -104,11 +104,11 @@ async def test_print_strings_containing_newline_flushed(devtools):
|
||||
|
||||
|
||||
@time_machine.travel(datetime.fromtimestamp(TIMESTAMP))
|
||||
async def test_flush_flushes_buffered_logs(devtools):
|
||||
async def test_flush_flushes_buffered_logs(devtools, in_memory_logfile):
|
||||
await devtools._stop_log_queue_processing()
|
||||
|
||||
redirector = DevtoolsRedirector(devtools)
|
||||
with redirect_stdout(redirector):
|
||||
redirector = StdoutRedirector(devtools, in_memory_logfile)
|
||||
with redirect_stdout(redirector): # type: ignore
|
||||
print("x", end="")
|
||||
|
||||
assert devtools.log_queue.qsize() == 0
|
||||
|
||||
Reference in New Issue
Block a user