From ca2ab35d9469772c3d89c855e6fd6b772fe47f79 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 23 Jan 2025 03:53:25 +0000 Subject: [PATCH] Add further types to ``_INVALID_BUILTIN_CLASSES`` (#13263) --- sphinx/util/typing.py | 37 +++++++++ tests/test_extensions/test_ext_autodoc.py | 2 +- .../test_ext_autodoc_configs.py | 4 +- tests/test_util/test_util_typing.py | 76 +++++++++++++++++++ 4 files changed, 116 insertions(+), 3 deletions(-) diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index ff3328f83..4f024c22c 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 @@ _INVALID_BUILTIN_CLASSES: Final[Mapping[object, str]] = { 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 @@ _INVALID_BUILTIN_CLASSES: Final[Mapping[object, str]] = { 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': @@ -78,6 +110,11 @@ _INVALID_BUILTIN_CLASSES: Final[Mapping[object, str]] = { types.NotImplementedType: 'types.NotImplementedType', 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_extensions/test_ext_autodoc.py b/tests/test_extensions/test_ext_autodoc.py index 057f732f5..afbcb14de 100644 --- a/tests/test_extensions/test_ext_autodoc.py +++ b/tests/test_extensions/test_ext_autodoc.py @@ -2588,7 +2588,7 @@ def test_autodoc_TYPE_CHECKING(app): '', ' .. py:attribute:: Foo.attr1', ' :module: target.TYPE_CHECKING', - ' :type: ~_io.StringIO', + ' :type: ~io.StringIO', '', '', '.. py:function:: spam(ham: ~collections.abc.Iterable[str]) -> tuple[~gettext.NullTranslations, bool]', diff --git a/tests/test_extensions/test_ext_autodoc_configs.py b/tests/test_extensions/test_ext_autodoc_configs.py index 02ef26b30..f56948c3d 100644 --- a/tests/test_extensions/test_ext_autodoc_configs.py +++ b/tests/test_extensions/test_ext_autodoc_configs.py @@ -1356,7 +1356,7 @@ def test_autodoc_type_aliases(app): ' docstring', '', '', - '.. py:function:: read(r: ~_io.BytesIO) -> ~_io.StringIO', + '.. py:function:: read(r: ~io.BytesIO) -> ~io.StringIO', ' :module: target.autodoc_type_aliases', '', ' docstring', @@ -1429,7 +1429,7 @@ def test_autodoc_type_aliases(app): ' docstring', '', '', - '.. py:function:: read(r: ~_io.BytesIO) -> my.module.StringIO', + '.. py:function:: read(r: ~io.BytesIO) -> my.module.StringIO', ' :module: target.autodoc_type_aliases', '', ' docstring', diff --git a/tests/test_util/test_util_typing.py b/tests/test_util/test_util_typing.py index c892e91ec..f3f452fae 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 @@ from pathlib import ( PureWindowsPath, WindowsPath, ) +from pickle import Pickler, Unpickler from struct import Struct from types import ( AsyncGeneratorType, @@ -57,6 +73,7 @@ from typing import ( TypeVar, Union, ) +from weakref import WeakSet from sphinx.ext.autodoc import mock from sphinx.util.typing import _INVALID_BUILTIN_CLASSES, restify, stringify_annotation @@ -129,6 +146,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, @@ -136,6 +174,9 @@ def test_is_invalid_builtin_class(): PurePosixPath, PureWindowsPath, WindowsPath, + # pickle + Pickler, + Unpickler, # struct Struct, # types @@ -162,11 +203,37 @@ def test_is_invalid_builtin_class(): NotImplementedType, 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__ == 'multiprocessing.context' if sys.version_info[:2] >= (3, 13): # pathlib assert Path.__module__ == 'pathlib._local' @@ -175,6 +242,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 @@ -201,6 +271,12 @@ def test_is_invalid_builtin_class(): assert NotImplementedType.__module__ == 'builtins' assert TracebackType.__module__ == 'builtins' assert WrapperDescriptorType.__module__ == 'builtins' + # weakref + assert WeakSet.__module__ == '_weakrefset' + if sys.version_info[:2] >= (3, 12): + # zipfile + assert zipfile.Path.__module__ == 'zipfile._path' + assert zipfile.CompleteDirs.__module__ == 'zipfile._path' def test_restify_type_hints_containers():