diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 9c3fecd64db..f77c35fdf9c 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -3,12 +3,20 @@ from __future__ import annotations import contextvars +import ctypes import dataclasses +import io +import json +import lzma +import multiprocessing import pathlib +import pickle # NoQA: S403 import struct import sys import types import typing +import weakref +import zipfile from collections.abc import Callable, Sequence from typing import TYPE_CHECKING @@ -45,6 +53,27 @@ contextvars.Context: 'contextvars.Context', contextvars.ContextVar: 'contextvars.ContextVar', contextvars.Token: 'contextvars.Token', + # types in 'ctypes' with .__module__ == '_ctypes': + ctypes.Array: 'ctypes.Array', + ctypes.Structure: 'ctypes.Structure', + ctypes.Union: 'ctypes.Union', + # types in 'io' with .__module__ == '_io': + io.FileIO: 'io.FileIO', + io.BytesIO: 'io.BytesIO', + io.StringIO: 'io.StringIO', + io.BufferedReader: 'io.BufferedReader', + io.BufferedWriter: 'io.BufferedWriter', + io.BufferedRWPair: 'io.BufferedRWPair', + io.BufferedRandom: 'io.BufferedRandom', + io.TextIOWrapper: 'io.TextIOWrapper', + # types in 'json' with .__module__ == 'json.{decoder,encoder}': + json.JSONDecoder: 'json.JSONDecoder', + json.JSONEncoder: 'json.JSONEncoder', + # types in 'lzma' with .__module__ == '_lzma': + lzma.LZMACompressor: 'lzma.LZMACompressor', + lzma.LZMADecompressor: 'lzma.LZMADecompressor', + # types in 'multiprocessing' with .__module__ == 'multiprocessing.context': + multiprocessing.Process: 'multiprocessing.Process', # types in 'pathlib' with .__module__ == 'pathlib._local': pathlib.Path: 'pathlib.Path', pathlib.PosixPath: 'pathlib.PosixPath', @@ -52,6 +81,9 @@ pathlib.PurePosixPath: 'pathlib.PurePosixPath', pathlib.PureWindowsPath: 'pathlib.PureWindowsPath', pathlib.WindowsPath: 'pathlib.WindowsPath', + # types in 'pickle' with .__module__ == 'pickle': + pickle.Pickler: 'pickle.Pickler', + pickle.Unpickler: 'pickle.Unpickler', # types in 'struct' with .__module__ == '_struct': struct.Struct: 'struct.Struct', # types in 'types' with .__module__ == 'builtins': @@ -75,6 +107,11 @@ types.ModuleType: 'types.ModuleType', types.TracebackType: 'types.TracebackType', types.WrapperDescriptorType: 'types.WrapperDescriptorType', + # types in 'weakref' with .__module__ == '_weakrefset': + weakref.WeakSet: 'weakref.WeakSet', + # types in 'zipfile' with .__module__ == 'zipfile._path': + zipfile.Path: 'zipfile.Path', + zipfile.CompleteDirs: 'zipfile.CompleteDirs', } diff --git a/tests/test_util/test_util_typing.py b/tests/test_util/test_util_typing.py index f2989d1e764..e15c8d08988 100644 --- a/tests/test_util/test_util_typing.py +++ b/tests/test_util/test_util_typing.py @@ -2,12 +2,27 @@ from __future__ import annotations +import ctypes import dataclasses import sys import typing as t +import zipfile from collections import abc from contextvars import Context, ContextVar, Token from enum import Enum +from io import ( + BufferedRandom, + BufferedReader, + BufferedRWPair, + BufferedWriter, + BytesIO, + FileIO, + StringIO, + TextIOWrapper, +) +from json import JSONDecoder, JSONEncoder +from lzma import LZMACompressor, LZMADecompressor +from multiprocessing import Process from numbers import Integral from pathlib import ( Path, @@ -17,6 +32,7 @@ PureWindowsPath, WindowsPath, ) +from pickle import Pickler, Unpickler from struct import Struct from types import ( AsyncGeneratorType, @@ -54,6 +70,7 @@ TypeVar, Union, ) +from weakref import WeakSet from sphinx.ext.autodoc import mock from sphinx.util.typing import _INVALID_BUILTIN_CLASSES, restify, stringify_annotation @@ -126,6 +143,27 @@ def test_is_invalid_builtin_class(): Context, ContextVar, Token, + # ctypes + ctypes.Array, + ctypes.Structure, + ctypes.Union, + # io + FileIO, + BytesIO, + StringIO, + BufferedReader, + BufferedWriter, + BufferedRWPair, + BufferedRandom, + TextIOWrapper, + # json + JSONDecoder, + JSONEncoder, + # lzma + LZMACompressor, + LZMADecompressor, + # multiprocessing + Process, # pathlib Path, PosixPath, @@ -133,6 +171,9 @@ def test_is_invalid_builtin_class(): PurePosixPath, PureWindowsPath, WindowsPath, + # pickle + Pickler, + Unpickler, # struct Struct, # types @@ -156,11 +197,37 @@ def test_is_invalid_builtin_class(): ModuleType, TracebackType, WrapperDescriptorType, + # weakref + WeakSet, + # zipfile + zipfile.Path, + zipfile.CompleteDirs, } # contextvars assert Context.__module__ == '_contextvars' assert ContextVar.__module__ == '_contextvars' assert Token.__module__ == '_contextvars' + # ctypes + assert ctypes.Array.__module__ == '_ctypes' + assert ctypes.Structure.__module__ == '_ctypes' + assert ctypes.Union.__module__ == '_ctypes' + # io + assert FileIO.__module__ == '_io' + assert BytesIO.__module__ == '_io' + assert StringIO.__module__ == '_io' + assert BufferedReader.__module__ == '_io' + assert BufferedWriter.__module__ == '_io' + assert BufferedRWPair.__module__ == '_io' + assert BufferedRandom.__module__ == '_io' + assert TextIOWrapper.__module__ == '_io' + # json + assert JSONDecoder.__module__ == 'json.decoder' + assert JSONEncoder.__module__ == 'json.encoder' + # lzma + assert LZMACompressor.__module__ == '_lzma' + assert LZMADecompressor.__module__ == '_lzma' + # multiprocessing + assert Process.__module__ == '_io' if sys.version_info[:2] >= (3, 13): # pathlib assert Path.__module__ == 'pathlib._local' @@ -169,6 +236,9 @@ def test_is_invalid_builtin_class(): assert PurePosixPath.__module__ == 'pathlib._local' assert PureWindowsPath.__module__ == 'pathlib._local' assert WindowsPath.__module__ == 'pathlib._local' + # pickle + assert Pickler.__module__ == '_pickle' + assert Unpickler.__module__ == '_pickle' # struct assert Struct.__module__ == '_struct' # types @@ -192,6 +262,11 @@ def test_is_invalid_builtin_class(): assert ModuleType.__module__ == 'builtins' assert TracebackType.__module__ == 'builtins' assert WrapperDescriptorType.__module__ == 'builtins' + # weakref + assert WeakSet.__module__ == '_weakrefset' + # zipfile + assert zipfile.Path.__module__ == 'zipfile._path' + assert zipfile.CompleteDirs.__module__ == 'zipfile._path' def test_restify_type_hints_containers():