8000 gh-117764: Add more tests for signatures of builtins · python/cpython@b696af4 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit b696af4

Browse files
gh-117764: Add more tests for signatures of builtins
Test signatures of all public builtins and methods of builtin classes in modules builtins, types, sys, and several other modules (either included in the list of standard builtin modules sys.builtin_module_names, or providing a public interface for such modules). Most builtins should have supported signatures, with few known exceptions. When more builtins will be converted to Argument Clinic or support of new signatures be implemented, they will be removed from the exception lists.
1 parent 2d3d9b4 commit b696af4

File tree

1 file changed

+256
-34
lines changed

1 file changed

+256
-34
lines changed

Lib/test/test_inspect/test_inspect.py

Lines changed: 256 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5221,10 +5221,17 @@ class TestSignatureDefinitions(unittest.TestCase):
52215221
# This test case provides a home for checking that particular APIs
52225222
# have signatures available for introspection
52235223

5224+
@staticmethod
5225+
def is_public(name):
5226+
return not name.startswith('_') or name.startswith('__') and name.endswith('__')
5227+
52245228
@cpython_only
52255229
@unittest.skipIf(MISSING_C_DOCSTRINGS,
52265230
"Signature information for builtins requires docstrings")
5227-
def test_builtins_have_signatures(self):
5231+
def _test_module_has_signatures(self, module,
5232+
no_signature=(), unsupported_signature=(),
5233+
methods_no_signature={}, methods_unsupported_signature={},
5234+
good_exceptions=()):
52285235
# This checks all builtin callables in CPython have signatures
52295236
# A few have signatures Signature can't yet handle, so we skip those
52305237
# since they will have to wait until PEP 457 adds the required
@@ -5233,48 +5240,263 @@ def test_builtins_have_signatures(self):
52335240
# reasons, so we also skip those for the time being, but design
52345241
# the test to fail in order to indicate when it needs to be
52355242
# updated.
5236-
no_signature = set()
5237-
# These need PEP 457 groups
5238-
needs_groups = {"range", "slice", "dir", "getattr",
5239-
"next", "iter", "vars"}
5240-
no_signature |= needs_groups
5241-
# These have unrepresentable parameter default values of NULL
5242-
needs_null = {"anext"}
5243-
no_signature |= needs_null
5244-
# These need *args support in Argument Clinic
5245-
needs_varargs = {"min", "max", "__build_class__"}
5246-
no_signature |= needs_varargs
5247-
# These builtin types are expected to provide introspection info
5248-
types_with_signatures = {
5249-
'bool', 'classmethod', 'complex', 'enumerate', 'filter', 'float',
5250-
'frozenset', 'list', 'map', 'memoryview', 'object', 'property',
5251-
'reversed', 'set', 'staticmethod', 'tuple', 'zip'
5252-
}
5243+
no_signature = no_signature or set()
52535244
# Check the signatures we expect to be there
5254-
ns = vars(builtins)
5245+
ns = vars(module)
5246+
try:
5247+
names = set(module.__all__)
5248+
except AttributeError:
5249+
names = set(name for name in ns if self.is_public(name))
52555250
for name, obj in sorted(ns.items()):
5251+
if name not in names:
5252+
continue
52565253
if not callable(obj):
52575254
continue
5258-
# The builtin types haven't been converted to AC yet
5259-
if isinstance(obj, type) and (name not in types_with_signatures):
5260-
# Note that this also skips all the exception types
5255+
if (isinstance(obj, type) and
5256+
issubclass(obj, BaseException) and
5257+
name not in good_exceptions):
52615258
no_signature.add(name)
5262-
if (name in no_signature):
5263-
# Not yet converted
5264-
continue
5265-
if name in {'classmethod', 'staticmethod'}:
5266-
# Bug gh-112006: inspect.unwrap() does not work with types
5267-
# with the __wrapped__ data descriptor.
5268-
continue
5269-
with self.subTest(builtin=name):
5270-
self.assertIsNotNone(inspect.signature(obj))
5259+
if name not in no_signature and name not in unsupported_signature:
5260+
with self.subTest('supported', builtin=name):
5261+
self.assertIsNotNone(inspect.signature(obj))
5262+
if isinstance(obj, type):
5263+
with self.subTest(type=name):
5264+
self._test_builtin_methods_have_signatures(obj,
5265+
methods_no_signature.get(name, ()),
5266+
methods_unsupported_signature.get(name, ()))
52715267
# Check callables that haven't been converted don't claim a signature
52725268
# This ensures this test will start failing as more signatures are
52735269
# added, so the affected items can be moved into the scope of the
52745270
# regression test above
5275-
for name in no_signature - needs_null:
5276-
with self.subTest(builtin=name):
5277-
self.assertIsNone(ns[name].__text_signature__)
5271+
for name in no_signature:
5272+
with self.subTest('none', builtin=name):
5273+
obj = ns[name]
5274+
self.assertIsNone(obj.__text_signature__)
5275+
self.assertRaises(ValueError, inspect.signature, obj)
5276+
for name in unsupported_signature:
5277+
with self.subTest('unsupported', builtin=name):
5278+
obj = ns[name]
5279+
self.assertIsNotNone(obj.__text_signature__)
5280+
self.assertRaises(ValueError, inspect.signature, obj)
5281+
5282+
def _test_builtin_methods_have_signatures(self, cls, no_signature, unsupported_signature):
5283+
ns = vars(cls)
5284+
for name in ns:
5285+
obj = getattr(cls, name, None)
5286+
if not callable(obj) or isinstance(obj, type):
5287+
continue
5288+
if name not in no_signature and name not in unsupported_signature:
5289+
with self.subTest('supported', method=name):
5290+
self.assertIsNotNone(inspect.signature(obj))
5291+
for name in no_signature:
5292+
with self.subTest('none', method=name):
5293+
self.assertIsNone(getattr(cls, name).__text_signature__)
5294+
self.assertRaises(ValueError, inspect.signature, getattr(cls, name))
5295+
for name in unsupported_signature:
5296+
with self.subTest('unsupported', method=name):
5297+
self.assertIsNotNone(getattr(cls, name).__text_signature__)
5298+
self.assertRaises(ValueError, inspect.signature, getattr(cls, name))
5299+
5300+
def test_builtins_have_signatures(self):
5301+
no_signature = {'type', 'super', 'bytearray', 'bytes', 'dict', 'int', 'str'}
5302+
# These need PEP 457 groups
5303+
needs_groups = {"range", "slice", "dir", "getattr",
5304+
"next", "iter", "vars"}
5305+
no_signature |= needs_groups
5306+
# These have unrepresentable parameter default values of NULL
5307+
unsupported_signature = {"anext"}
5308+
# These need *args support in Argument Clinic
5309+
needs_varargs = {"min", "max", "__build_class__"}
5310+
no_signature |= needs_varargs
5311+
5312+
methods_no_signature = {
5313+
'dict': {'update'},
5314+
'object': {'__class__'},
5315+
}
5316+
methods_unsupported_signature = {
5317+
'bytearray': {'count', 'endswith', 'find', 'hex', 'index', 'rfind', 'rindex', 'startswith'},
5318+
'bytes': {'count', 'endswith', 'find', 'hex', 'index', 'rfind', 'rindex', 'startswith'},
5319+
'dict': {'pop'},
5320+
'int': {'__round__'},
5321+
'memoryview': {'cast', 'hex'},
5322+
'str': {'count', 'endswith', 'find', 'index', 'maketrans', 'rfind', 'rindex', 'startswith'},
5323+
}
5324+
self._test_module_has_signatures(builtins,
5325+
no_signature, unsupported_signature,
5326+
methods_no_signature, methods_unsupported_signature)
5327+
5328+
def test_types_module_has_signatures(self):
5329+
unsupported_signature = {'CellType'}
5330+
methods_no_signature = {
5331+
'AsyncGeneratorType': {'athrow'},
5332+
'CoroutineType': {'throw'},
5333+
'GeneratorType': {'throw'},
5334+
}
5335+
self._test_module_has_signatures(types,
5336+
unsupported_signature=unsupported_signature,
5337+
methods_no_signature=methods_no_signature)
5338+
5339+
def test_sys_module_has_signatures(self):
5340+
no_signature = {'getsizeof', 'set_asyncgen_hooks'}
5341+
self._test_module_has_signatures(sys, no_signature)
5342+
5343+
def test_abc_module_has_signatures(self):
5344+
import abc
5345+
self._test_module_has_signatures(abc)
5346+
5347+
def test_atexit_module_has_signatures(self):
5348+
import atexit
5349+
self._test_module_has_signatures(atexit)
5350+
5351+
def test_codecs_module_has_signatures(self):
5352+
import codecs
5353+
methods_no_signature = {'StreamReader': {'charbuffertype'}}
5354+
self._test_module_has_signatures(codecs,
5355+
methods_no_signature=methods_no_signature)
5356+
5357+
def test_collections_module_has_signatures(self):
5358+
no_signature = {'OrderedDict', 'defaultdict'}
5359+
unsupported_signature = {'deque'}
5360+
methods_no_signature = {
5361+
'OrderedDict': {'update'},
5362+
}
5363+
methods_unsupported_signature = {
5364+
'deque': {'index'},
5365+
'OrderedDict': {'pop'},
5366+
'UserString': {'maketrans'},
5367+
}
5368+
self._test_module_has_signatures(collections,
5369+
no_signature, unsupported_signature,
5370+
methods_no_signature, methods_unsupported_signature)
5371+
5372+
def test_collections_abc_module_has_signatures(self):
5373+
import collections.abc
5374+
self._test_module_has_signatures(collections.abc)
5375+
5376+
def test_errno_module_has_signatures(self):
5377+
import errno
5378+
self._test_module_has_signatures(errno)
5379+
5380+
def test_faulthandler_module_has_signatures(self):
5381+
import faulthandler
5382+
unsupported_signature = {'dump_traceback', 'dump_traceback_later', 'enable', 'register'}
5383+
self._test_module_has_signatures(faulthandler, unsupported_signature=unsupported_signature)
5384+
5385+
def test_functools_module_has_signatures(self):
5386+
no_signature = {'reduce'}
5387+
self._test_module_has_signatures(functools, no_signature)
5388+
5389+
def test_gc_module_has_signatures(self):
5390+
import gc
5391+
no_signature = {'set_threshold'}
5392+
self._test_module_has_signatures(gc, no_signature)
5393+
5394+
def test_io_module_has_signatures(self):
5395+
methods_no_signature = {
5396+
'BufferedRWPair': {'read', 'peek', 'read1', 'readinto', 'readinto1', 'write'},
5397+
}
5398+
self._test_module_has_signatures(io,
5399+
methods_no_signature=methods_no_signature)
5400+
5401+
def test_itertools_module_has_signatures(self):
5402+
import itertools
5403+
no_signature = {'islice', 'repeat'}
5404+
self._test_module_has_signatures(itertools, no_signature)
5405+
5406+
def test_locale_module_has_signatures(self):
5407+
import locale
5408+
self._test_module_has_signatures(locale)
5409+
5410+
def test_marshal_module_has_signatures(self):
5411+
import marshal
5412+
self._test_module_has_signatures(marshal)
5413+
5414+
def test_operator_module_has_signatures(self):
5415+
import operator
5416+
self._test_module_has_signatures(operator)
5417+
5418+
def test_os_module_has_signatures(self):
5419+
unsupported_signature = {'chmod', 'get_terminal_size', 'posix_spawn', 'posix_spawnp', 'register_at_fork', 'utime'}
5420+
self._test_module_has_signatures(os, unsupported_signature=unsupported_signature)
5421+
5422+
def test_pwd_module_has_signatures(self):
5423+
import pwd
5424+
self._test_module_has_signatures(pwd)
5425+
5426+
def test_re_module_has_signatures(self):
5427+
import re
5428+
methods_no_signature = {'Match': {'group'}}
5429+
self._test_module_has_signatures(re,
5430+
methods_no_signature=methods_no_signature,
5431+
good_exceptions={'error', 'PatternError'})
5432+
5433+
def test_signal_module_has_signatures(self):
5434+
import signal
5435+
self._test_module_has_signatures(signal)
5436+
5437+
def test_stat_module_has_signatures(self):
5438+
import stat
5439+
self._test_module_has_signatures(stat)
5440+
5441+
def test_string_module_has_signatures(self):
5442+
import string
5443+
self._test_module_has_signatures(string)
5444+
5445+
def test_symtable_module_has_signatures(self):
5446+
import symtable
5447+
self._test_module_has_signatures(symtable)
5448+
5449+
def test_sysconfig_module_has_signatures(self):
5450+
import sysconfig
5451+
self._test_module_has_signatures(sysconfig)
5452+
5453+
def test_threading_module_has_signatures(self):
5454+
import threading
5455+
self._test_module_has_signatures(threading)
5456+
5457+
def test_thread_module_has_signatures(self):
5458+
import _thread
5459+
no_signature = {'RLock'}
5460+
self._test_module_has_signatures(_thread, no_signature)
5461+
5462+
def test_time_module_has_signatures(self):
5463+
no_signature = {
5464+
'asctime', 'clock_getres', 'clock_settime', 'clock_settime_ns',
5465+
'ctime', 'get_clock_info', 'gmtime', 'localtime',
5466+
'pthread_getcpuclockid', 'strftime', 'strptime'
5467+
}
5468+
self._test_module_has_signatures(time, no_signature)
5469+
5470+
def test_tokenize_module_has_signatures(self):
5471+
import tokenize
5472+
self._test_module_has_signatures(tokenize)
5473+
5474+
def test_tracemalloc_module_has_signatures(self):
5475+
import tracemalloc
5476+
self._test_module_has_signatures(tracemalloc)
5477+
5478+
def test_typing_module_has_signatures(self):
5479+
import typing
5480+
no_signature = {'ParamSpec', 'ParamSpecArgs', 'ParamSpecKwargs',
5481+
'Text', 'TypeAliasType', 'TypeVar', 'TypeVarTuple'}
5482+
methods_no_signature = {
5483+
'Generic': {'__class_getitem__', '__init_subclass__'},
5484+
}
5485+
methods_unsupported_signature = {
5486+
'Text': {'count', 'find', 'index', 'rfind', 'rindex', 'startswith', 'endswith', 'maketrans'},
5487+
}
5488+
self._test_module_has_signatures(typing, no_signature,
5489+
methods_no_signature=methods_no_signature,
5490+
methods_unsupported_signature=methods_unsupported_signature)
5491+
5492+
def test_warnings_module_has_signatures(self):
5493+
unsupported_signature = {'warn', 'warn_explicit'}
5494+
self._test_module_has_signatures(warnings, unsupported_signature=unsupported_signature)
5495+
5496+
def test_weakref_module_has_signatures(self):
5497+
import weakref
5498+
no_signature = {'ReferenceType', 'ref'}
5499+
self._test_module_has_signatures(weakref, no_signature)
52785500

52795501
def test_python_function_override_signature(self):
52805502
def func(*args, **kwargs):

0 commit comments

Comments
 (0)
0