diff --git a/stdlib/@tests/test_cases/check_logging.py b/stdlib/@tests/test_cases/check_logging.py index fe3d8eb16fd0..a7d57cbda132 100644 --- a/stdlib/@tests/test_cases/check_logging.py +++ b/stdlib/@tests/test_cases/check_logging.py @@ -28,3 +28,27 @@ def record_factory(*args: Any, **kwargs: Any) -> logging.LogRecord: logging.handlers.QueueListener(queue.Queue()) logging.handlers.QueueListener(queue.SimpleQueue()) logging.handlers.QueueListener(multiprocessing.Queue()) + +# These all raise at runtime. +logging.basicConfig(filename="foo.log", handlers=[]) # type: ignore +logging.basicConfig(filemode="w", handlers=[]) # type: ignore +logging.basicConfig(stream=None, handlers=[]) # type: ignore +logging.basicConfig(filename="foo.log", stream=None) # type: ignore +logging.basicConfig(filename=None, stream=None) # type: ignore +# These are ok. +logging.basicConfig() +logging.basicConfig(handlers=[]) +logging.basicConfig(filename="foo.log", filemode="w") +logging.basicConfig(filename="foo.log", filemode="w", handlers=None) +logging.basicConfig(stream=None) +logging.basicConfig(stream=None, handlers=None) +# dubious but accepted, has same meaning as 'stream=None'. +logging.basicConfig(filename=None) +# These are technically accepted at runtime, but are forbidden in the stubs to help +# prevent user mistakes. Passing 'filemode' / 'encoding' / 'errors' does nothing +# if 'filename' is not specified. +logging.basicConfig(stream=None, filemode="w") # type: ignore +logging.basicConfig(stream=None, encoding="utf-8") # type: ignore +logging.basicConfig(stream=None, errors="strict") # type: ignore +logging.basicConfig(handlers=[], encoding="utf-8") # type: ignore +logging.basicConfig(handlers=[], errors="strict") # type: ignore diff --git a/stdlib/logging/__init__.pyi b/stdlib/logging/__init__.pyi index 4e73b844543f..89c94816a906 100644 --- a/stdlib/logging/__init__.pyi +++ b/stdlib/logging/__init__.pyi @@ -576,20 +576,41 @@ if sys.version_info >= (3, 11): def getLevelNamesMapping() -> dict[str, int]: ... def makeLogRecord(dict: Mapping[str, object]) -> LogRecord: ... +@overload # handlers is non-None def basicConfig( *, - filename: StrPath | None = None, + format: str = ..., # default value depends on the value of `style` + datefmt: str | None = None, + style: _FormatStyle = "%", + level: _Level | None = None, + handlers: Iterable[Handler], + force: bool | None = False, +) -> None: ... +@overload # handlers is None, filename is passed (but possibly None) +def basicConfig( + *, + filename: StrPath | None, filemode: str = "a", format: str = ..., # default value depends on the value of `style` datefmt: str | None = None, style: _FormatStyle = "%", level: _Level | None = None, - stream: SupportsWrite[str] | None = None, - handlers: Iterable[Handler] | None = None, + handlers: None = None, force: bool | None = False, encoding: str | None = None, errors: str | None = "backslashreplace", ) -> None: ... +@overload # handlers is None, filename is not passed +def basicConfig( + *, + format: str = ..., # default value depends on the value of `style` + datefmt: str | None = None, + style: _FormatStyle = "%", + level: _Level | None = None, + stream: SupportsWrite[str] | None = None, + handlers: None = None, + force: bool | None = False, +) -> None: ... def shutdown(handlerList: Sequence[Any] = ...) -> None: ... # handlerList is undocumented def setLoggerClass(klass: type[Logger]) -> None: ... def captureWarnings(capture: bool) -> None: ...