diff --git a/stdlib/logging/__init__.pyi b/stdlib/logging/__init__.pyi index 938410ae6..6ebd305aa 100644 --- a/stdlib/logging/__init__.pyi +++ b/stdlib/logging/__init__.pyi @@ -80,7 +80,7 @@ _levelToName: dict[int, str] _nameToLevel: dict[str, int] class Filterer: - filters: list[Filter] + filters: list[_FilterType] def addFilter(self, filter: _FilterType) -> None: ... def removeFilter(self, filter: _FilterType) -> None: ... def filter(self, record: LogRecord) -> bool: ... diff --git a/stdlib/logging/config.pyi b/stdlib/logging/config.pyi index bec0ab02d..e92658f7f 100644 --- a/stdlib/logging/config.pyi +++ b/stdlib/logging/config.pyi @@ -1,32 +1,59 @@ import sys from _typeshed import StrOrBytesPath -from collections.abc import Callable, Sequence +from collections.abc import Callable, Hashable, Iterable, Sequence from configparser import RawConfigParser from re import Pattern from threading import Thread -from typing import IO, Any -from typing_extensions import Literal, TypedDict +from typing import IO, Any, overload +from typing_extensions import Literal, SupportsIndex, TypeAlias, TypedDict -from . import _Level +from . import Filter, Filterer, Formatter, Handler, Logger, _FilterType, _FormatStyle, _Level DEFAULT_LOGGING_CONFIG_PORT: int RESET_ERROR: int # undocumented IDENTIFIER: Pattern[str] # undocumented -class _RootLoggerConfiguration(TypedDict, total=False): - level: _Level - filters: Sequence[str] - handlers: Sequence[str] +if sys.version_info >= (3, 11): + class _RootLoggerConfiguration(TypedDict, total=False): + level: _Level + filters: Sequence[str | _FilterType] + handlers: Sequence[str] + +else: + class _RootLoggerConfiguration(TypedDict, total=False): + level: _Level + filters: Sequence[str] + handlers: Sequence[str] class _LoggerConfiguration(_RootLoggerConfiguration, TypedDict, total=False): propagate: bool +if sys.version_info >= (3, 8): + _FormatterConfigurationTypedDict = TypedDict( + "_FormatterConfigurationTypedDict", {"class": str, "format": str, "datefmt": str, "style": _FormatStyle}, total=False + ) +else: + _FormatterConfigurationTypedDict = TypedDict( + "_FormatterConfigurationTypedDict", + {"class": str, "format": str, "datefmt": str, "style": _FormatStyle, "validate": bool}, + total=False, + ) + +class _FilterConfigurationTypedDict(TypedDict): + name: str + +# Formatter and filter configs can specify custom factories via the special `()` key. +# If that is the case, the dictionary can contain any additional keys +# https://docs.python.org/3/library/logging.config.html#user-defined-objects +_FormatterConfiguration: TypeAlias = _FormatterConfigurationTypedDict | dict[str, Any] +_FilterConfiguration: TypeAlias = _FilterConfigurationTypedDict | dict[str, Any] +# Handler config can have additional keys even when not providing a custom factory so we just use `dict`. +_HandlerConfiguration: TypeAlias = dict[str, Any] + class _OptionalDictConfigArgs(TypedDict, total=False): - # these two can have custom factories (key: `()`) which can have extra keys - formatters: dict[str, dict[str, Any]] - filters: dict[str, dict[str, Any]] - # type checkers would warn about extra keys if this was a TypedDict - handlers: dict[str, dict[str, Any]] + formatters: dict[str, _FormatterConfiguration] + filters: dict[str, _FilterConfiguration] + handlers: dict[str, _HandlerConfiguration] loggers: dict[str, _LoggerConfiguration] root: _RootLoggerConfiguration | None incremental: bool @@ -60,3 +87,57 @@ else: def valid_ident(s: str) -> Literal[True]: ... # undocumented def listen(port: int = 9030, verify: Callable[[bytes], bytes | None] | None = None) -> Thread: ... def stopListening() -> None: ... + +class ConvertingMixin: # undocumented + def convert_with_key(self, key: Any, value: Any, replace: bool = True) -> Any: ... + def convert(self, value: Any) -> Any: ... + +class ConvertingDict(dict[Hashable, Any], ConvertingMixin): # undocumented + def __getitem__(self, key: Hashable) -> Any: ... + def get(self, key: Hashable, default: Any = None) -> Any: ... + def pop(self, key: Hashable, default: Any = None) -> Any: ... + +class ConvertingList(list[Any], ConvertingMixin): # undocumented + @overload + def __getitem__(self, key: SupportsIndex) -> Any: ... + @overload + def __getitem__(self, key: slice) -> Any: ... + def pop(self, idx: SupportsIndex = -1) -> Any: ... + +class ConvertingTuple(tuple[Any, ...], ConvertingMixin): # undocumented + @overload + def __getitem__(self, key: SupportsIndex) -> Any: ... + @overload + def __getitem__(self, key: slice) -> Any: ... + +class BaseConfigurator: # undocumented + CONVERT_PATTERN: Pattern[str] + WORD_PATTERN: Pattern[str] + DOT_PATTERN: Pattern[str] + INDEX_PATTERN: Pattern[str] + DIGIT_PATTERN: Pattern[str] + value_converters: dict[str, str] + importer: Callable[..., Any] + + def __init__(self, config: _DictConfigArgs | dict[str, Any]) -> None: ... + def resolve(self, s: str) -> Any: ... + def ext_convert(self, value: str) -> Any: ... + def cfg_convert(self, value: str) -> Any: ... + def convert(self, value: Any) -> Any: ... + def configure_custom(self, config: dict[str, Any]) -> Any: ... + def as_tuple(self, value: list[Any] | tuple[Any]) -> tuple[Any]: ... + +class DictConfigurator(BaseConfigurator): + def configure(self) -> None: ... # undocumented + def configure_formatter(self, config: _FormatterConfiguration) -> Formatter | Any: ... # undocumented + def configure_filter(self, config: _FilterConfiguration) -> Filter | Any: ... # undocumented + def add_filters(self, filterer: Filterer, filters: Iterable[_FilterType]) -> None: ... # undocumented + def configure_handler(self, config: _HandlerConfiguration) -> Handler | Any: ... # undocumented + def add_handlers(self, logger: Logger, handlers: Iterable[str]) -> None: ... # undocumented + def common_logger_config( + self, logger: Logger, config: _LoggerConfiguration, incremental: bool = False + ) -> None: ... # undocumented + def configure_logger(self, name: str, config: _LoggerConfiguration, incremental: bool = False) -> None: ... # undocumented + def configure_root(self, config: _LoggerConfiguration, incremental: bool = False) -> None: ... # undocumented + +dictConfigClass = DictConfigurator diff --git a/tests/stubtest_allowlists/py3_common.txt b/tests/stubtest_allowlists/py3_common.txt index bb1639f44..5a41f9b9e 100644 --- a/tests/stubtest_allowlists/py3_common.txt +++ b/tests/stubtest_allowlists/py3_common.txt @@ -244,13 +244,6 @@ ctypes.c_voidp ctypes.util.test importlib.abc.Finder.find_module lib2to3.pgen2.grammar.Grammar.loads -logging.config.BaseConfigurator -logging.config.ConvertingDict -logging.config.ConvertingList -logging.config.ConvertingMixin -logging.config.ConvertingTuple -logging.config.DictConfigurator -logging.config.dictConfigClass mimetypes.MimeTypes.add_type modulefinder.test multiprocessing.managers.Server.accepter