From d1bfd08b4bc843227d097decfd99d70272a1f804 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 10 May 2023 16:06:41 +0100 Subject: [PATCH] Allow passing `multiprocessing.Queue`s to `QueueListener`/`QueueHandler` (#10169) --- stdlib/logging/handlers.pyi | 17 +++++++++++------ test_cases/stdlib/check_logging.py | 12 ++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/stdlib/logging/handlers.pyi b/stdlib/logging/handlers.pyi index 8a0373435..3a7c8ade7 100644 --- a/stdlib/logging/handlers.pyi +++ b/stdlib/logging/handlers.pyi @@ -5,10 +5,11 @@ import sys from _typeshed import ReadableBuffer, StrPath from collections.abc import Callable from logging import FileHandler, Handler, LogRecord -from queue import Queue, SimpleQueue from re import Pattern from socket import SocketKind, socket -from typing import Any, ClassVar +from typing import Any, ClassVar, Protocol, TypeVar + +_T = TypeVar("_T") DEFAULT_TCP_LOGGING_PORT: int DEFAULT_UDP_LOGGING_PORT: int @@ -249,17 +250,21 @@ class HTTPHandler(Handler): if sys.version_info >= (3, 9): def getConnection(self, host: str, secure: bool) -> http.client.HTTPConnection: ... # undocumented +class _QueueLike(Protocol[_T]): + def get(self) -> _T: ... + def put_nowait(self, __item: _T) -> None: ... + class QueueHandler(Handler): - queue: SimpleQueue[Any] | Queue[Any] # undocumented - def __init__(self, queue: SimpleQueue[Any] | Queue[Any]) -> None: ... + queue: _QueueLike[Any] + def __init__(self, queue: _QueueLike[Any]) -> None: ... def prepare(self, record: LogRecord) -> Any: ... def enqueue(self, record: LogRecord) -> None: ... class QueueListener: handlers: tuple[Handler, ...] # undocumented respect_handler_level: bool # undocumented - queue: SimpleQueue[Any] | Queue[Any] # undocumented - def __init__(self, queue: SimpleQueue[Any] | Queue[Any], *handlers: Handler, respect_handler_level: bool = False) -> None: ... + queue: _QueueLike[Any] # undocumented + def __init__(self, queue: _QueueLike[Any], *handlers: Handler, respect_handler_level: bool = False) -> None: ... def dequeue(self, block: bool) -> LogRecord: ... def prepare(self, record: LogRecord) -> Any: ... def start(self) -> None: ... diff --git a/test_cases/stdlib/check_logging.py b/test_cases/stdlib/check_logging.py index f51edd19d..fe3d8eb16 100644 --- a/test_cases/stdlib/check_logging.py +++ b/test_cases/stdlib/check_logging.py @@ -1,6 +1,9 @@ from __future__ import annotations import logging +import logging.handlers +import multiprocessing +import queue from typing import Any # This pattern comes from the logging docs, and should therefore pass a type checker @@ -16,3 +19,12 @@ def record_factory(*args: Any, **kwargs: Any) -> logging.LogRecord: logging.setLogRecordFactory(record_factory) + +# The logging docs say that QueueHandler and QueueListener can take "any queue-like object" +# We test that here (regression test for #10168) +logging.handlers.QueueHandler(queue.Queue()) +logging.handlers.QueueHandler(queue.SimpleQueue()) +logging.handlers.QueueHandler(multiprocessing.Queue()) +logging.handlers.QueueListener(queue.Queue()) +logging.handlers.QueueListener(queue.SimpleQueue()) +logging.handlers.QueueListener(multiprocessing.Queue())