Make filename, stream, and handlers parameters of logging.basicConfig mutually-exclusive (#15435)

This commit is contained in:
Brian Schubert
2026-02-18 03:49:35 -05:00
committed by GitHub
parent 2b2a93d632
commit d4a1c39d5c
2 changed files with 48 additions and 3 deletions
+24
View File
@@ -28,3 +28,27 @@ logging.handlers.QueueHandler(multiprocessing.Queue())
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
+24 -3
View File
@@ -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: ...