Skip to content
Open
2 changes: 1 addition & 1 deletion Doc/howto/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ And this is what it gives:
Traceback (most recent call last):
File "prog.py", line 11, in <module>
if args.verbosity >= 2:
TypeError: '>=' not supported between instances of 'NoneType' and 'int'
TypeError: '>=' not supported between instances of 'types.NoneType' and 'int'
* First output went well, and fixes the bug we had before.
Expand Down
4 changes: 2 additions & 2 deletions Doc/howto/descriptor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,7 @@ and :attr:`~function.__doc__`.
'StaticMethod'
>>> f = E_sim.f
>>> type(f).__name__
'function'
'FunctionType'
>>> sm.__name__
'f'
>>> f.__name__
Expand Down Expand Up @@ -1523,7 +1523,7 @@ Using the non-data descriptor protocol, a pure Python version of
# Verify that __wrapped__ was added and works correctly
>>> f = vars(T)['cm'].__wrapped__
>>> type(f).__name__
'function'
'FunctionType'
>>> f.__name__
'cm'
>>> f(T, 11, 22)
Expand Down
4 changes: 2 additions & 2 deletions Doc/library/doctest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ Some details you should read once, but won't need to remember:
File "<stdin>", line 1
1 + None
~~^~~~~~
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
TypeError: unsupported operand type(s) for +: 'int' and 'types.NoneType'

Since the lines showing the position of the error come before the exception type
and detail, they are not checked by doctest. For example, the following test
Expand All @@ -564,7 +564,7 @@ Some details you should read once, but won't need to remember:
File "<stdin>", line 1
1 + None
^~~~~~~~
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
TypeError: unsupported operand type(s) for +: 'int' and 'types.NoneType'


.. _option-flags-and-directives:
Expand Down
2 changes: 1 addition & 1 deletion Doc/library/functools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ The :mod:`!functools` module defines the following functions:
attribute::

>>> fun.registry.keys()
dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,
dict_keys([<class 'types.NoneType'>, <class 'int'>, <class 'object'>,
<class 'decimal.Decimal'>, <class 'list'>,
<class 'float'>])
>>> fun.registry[float]
Expand Down
2 changes: 1 addition & 1 deletion Doc/library/multiprocessing.shared_memory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ instance:
>>> from multiprocessing import shared_memory
>>> a = shared_memory.ShareableList(['howdy', b'HoWdY', -273.154, 100, None, True, 42])
>>> [ type(entry) for entry in a ]
[<class 'str'>, <class 'bytes'>, <class 'float'>, <class 'int'>, <class 'NoneType'>, <class 'bool'>, <class 'int'>]
[<class 'str'>, <class 'bytes'>, <class 'float'>, <class 'int'>, <class 'types.NoneType'>, <class 'bool'>, <class 'int'>]
>>> a[2]
-273.154
>>> a[2] = -78.5
Expand Down
2 changes: 1 addition & 1 deletion Doc/library/re.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1681,7 +1681,7 @@ To find out what card the pair consists of, one could use the
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
pair_re.prefixmatch("718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'
AttributeError: 'types.NoneType' object has no attribute 'group'

>>> pair_re.prefixmatch("354aa").group(1)
'a'
Expand Down
4 changes: 2 additions & 2 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1484,9 +1484,9 @@ These can be used as types in annotations. They all support subscription using
>>> def func(x: Annotated[int, "metadata"]) -> None: pass
...
>>> get_type_hints(func)
{'x': <class 'int'>, 'return': <class 'NoneType'>}
{'x': <class 'int'>, 'return': <class 'types.NoneType'>}
>>> get_type_hints(func, include_extras=True)
{'x': typing.Annotated[int, 'metadata'], 'return': <class 'NoneType'>}
{'x': typing.Annotated[int, 'metadata'], 'return': <class 'types.NoneType'>}

At runtime, the metadata associated with an ``Annotated`` type can be
retrieved via the :attr:`!__metadata__` attribute:
Expand Down
2 changes: 1 addition & 1 deletion Doc/library/unittest.mock.rst
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ object::
...
>>> mock = MagicMock(async_func)
>>> mock
<MagicMock spec='function' id='...'>
<MagicMock spec='FunctionType' id='...'>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not types.FunctionType?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the MagicMock repr contains only spec_class.__name__, not the fully qualified name.

>>> mock() # doctest: +SKIP
<coroutine object AsyncMockMixin._mock_call at ...>

Expand Down
7 changes: 7 additions & 0 deletions Lib/_pyrepl/fancycompleter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from _colorize import ANSIColors, get_colors, get_theme
import rlcompleter
import keyword
import re
import types

TYPE_CHECKING = False
Expand Down Expand Up @@ -43,6 +44,12 @@ def _color_for_obj(name: str, value: Any, theme: Theme) -> str:

def _color_by_type(t, theme):
typename = t.__name__
if t.__module__ == 'types' and typename.endswith('Type'):
# MethodWrapperType -> method_wrapper
# 1. Remove the "Type" suffix.
# 2. Insert "_" before each upper letter in the middle.
# 3. Convert to lower case.
typename = re.sub(r'(?<!\A)(?=[A-Z])', '_', typename[:-4]).lower()
# this is needed e.g. to turn method-wrapper into method_wrapper,
# because if we want _colorize.FancyCompleter to be "dataclassable"
# our keys need to be valid identifiers.
Expand Down
14 changes: 11 additions & 3 deletions Lib/json/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ def replace(match):
encode_basestring_ascii = (
c_encode_basestring_ascii or py_encode_basestring_ascii)

def _T(obj):
cls = type(obj)
module = cls.__module__
if module in (None, 'builtins', '__main__'):
return cls.__qualname__
return f'{module}.{cls.__qualname__}'


class JSONEncoder(object):
"""Extensible JSON <https://json.org> encoder for Python data structures.
Expand Down Expand Up @@ -316,7 +324,7 @@ def _iterencode_list(lst, _current_indent_level):
except GeneratorExit:
raise
except BaseException as exc:
exc.add_note(f'when serializing {type(lst).__name__} item {i}')
exc.add_note(f'when serializing {_T(lst)} item {i}')
raise
if newline_indent is not None:
_current_indent_level -= 1
Expand Down Expand Up @@ -403,7 +411,7 @@ def _iterencode_dict(dct, _current_indent_level):
except GeneratorExit:
raise
except BaseException as exc:
exc.add_note(f'when serializing {type(dct).__name__} item {key!r}')
exc.add_note(f'when serializing {_T(dct)} item {key!r}')
raise
if not first and newline_indent is not None:
_current_indent_level -= 1
Expand Down Expand Up @@ -443,7 +451,7 @@ def _iterencode(o, _current_indent_level):
except GeneratorExit:
raise
except BaseException as exc:
exc.add_note(f'when serializing {type(o).__name__} object')
exc.add_note(f'when serializing {_T(o)} object')
raise
if markers is not None:
del markers[markerid]
Expand Down
33 changes: 31 additions & 2 deletions Lib/test/pickletester.py
Original file line number Diff line number Diff line change
Expand Up @@ -1340,15 +1340,15 @@ def test_find_class(self):
r"Can't resolve path 'log\.spam' on module 'math'") as cm:
unpickler4.find_class('math', 'log.spam')
self.assertEqual(str(cm.exception.__context__),
"'builtin_function_or_method' object has no attribute 'spam'")
"'types.BuiltinFunctionType' object has no attribute 'spam'")
with self.assertRaisesRegex(AttributeError,
r"module 'math' has no attribute 'log\.<locals>\.spam'"):
unpickler.find_class('math', 'log.<locals>.spam')
with self.assertRaisesRegex(AttributeError,
r"Can't resolve path 'log\.<locals>\.spam' on module 'math'") as cm:
unpickler4.find_class('math', 'log.<locals>.spam')
self.assertEqual(str(cm.exception.__context__),
"'builtin_function_or_method' object has no attribute '<locals>'")
"'types.BuiltinFunctionType' object has no attribute '<locals>'")
with self.assertRaisesRegex(AttributeError,
"module 'math' has no attribute ''"):
unpickler.find_class('math', '')
Expand Down Expand Up @@ -3300,6 +3300,35 @@ def test_builtin_types(self):
s = self.dumps(t, proto)
self.assertIs(self.loads(s), t)

def test_types_types(self):
if self.py_version < (3, 8):
self.skipTest('not supported in Python < 3.8')
# types with __module__ == 'types' added before 3.16
old_names = {
'EllipsisType': (3, 8),
'GenericAlias': (3, 9),
'NoneType': (3, 8),
'NotImplementedType': (3, 8),
'SimpleNamespace': (3, 8),
'Union': (3, 8),
'DynamicClassAttribute': (3, 8),
'_GeneratorWrapper': (3, 8),
}
# new types added after 3.16
new_names = {
}
for t in types.__dict__.values():
if isinstance(t, type):
if self.py_version < (3, 16):
if t.__name__ not in old_names or self.py_version < old_names[t.__name__]:
continue
elif t.__name__ in new_names and self.py_version < new_names[t.__name__]:
continue
for proto in protocols:
with self.subTest(name=t.__name__, proto=proto):
s = self.dumps(t, proto)
self.assertIs(self.loads(s), t)

def test_builtin_exceptions(self):
new_names = {
'BlockingIOError': (3, 3),
Expand Down
10 changes: 5 additions & 5 deletions Lib/test/test_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,29 +227,29 @@ def test_object_not_callable(self):
self.assertRaisesRegex(TypeError, msg, object())

def test_module_not_callable_no_suggestion_0(self):
msg = r"^'module' object is not callable$"
msg = r"^'types\.ModuleType' object is not callable$"
self.assertRaisesRegex(TypeError, msg, types.ModuleType("mod"))

def test_module_not_callable_no_suggestion_1(self):
msg = r"^'module' object is not callable$"
msg = r"^'types\.ModuleType' object is not callable$"
mod = types.ModuleType("mod")
mod.mod = 42
self.assertRaisesRegex(TypeError, msg, mod)

def test_module_not_callable_no_suggestion_2(self):
msg = r"^'module' object is not callable$"
msg = r"^'types\.ModuleType' object is not callable$"
mod = types.ModuleType("mod")
del mod.__name__
self.assertRaisesRegex(TypeError, msg, mod)

def test_module_not_callable_no_suggestion_3(self):
msg = r"^'module' object is not callable$"
msg = r"^'types\.ModuleType' object is not callable$"
mod = types.ModuleType("mod")
mod.__name__ = 42
self.assertRaisesRegex(TypeError, msg, mod)

def test_module_not_callable_suggestion(self):
msg = r"^'module' object is not callable\. Did you mean: 'mod\.mod\(\.\.\.\)'\?$"
msg = r"^'types\.ModuleType' object is not callable\. Did you mean: 'mod\.mod\(\.\.\.\)'\?$"
mod = types.ModuleType("mod")
mod.mod = lambda: ...
self.assertRaisesRegex(TypeError, msg, mod)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ def check(z, x, y):
"argument must be a string or a number, not dict",
complex, {})
self.assertRaisesRegex(TypeError,
"argument must be a string or a number, not NoneType",
"argument must be a string or a number, not types.NoneType",
complex, None)
self.assertRaisesRegex(TypeError,
"argument 'real' must be a real number, not dict",
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_coroutines.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ async def foo():
coro = foo()

check = lambda: self.assertRaisesRegex(
TypeError, "'coroutine' object is not iterable")
TypeError, r"'types\.CoroutineType' object is not iterable")

with check():
list(coro)
Expand Down Expand Up @@ -601,7 +601,7 @@ async def foo():
await bar()

check = lambda: self.assertRaisesRegex(
TypeError, "'coroutine' object is not iterable")
TypeError, r"'types\.CoroutineType' object is not iterable")

coro = foo()
with check():
Expand Down Expand Up @@ -963,7 +963,7 @@ def test_corotype_1(self):
self.assertIn('in coroutine', ct.throw.__doc__)
self.assertIn('of the coroutine', ct.__dict__['__name__'].__doc__)
self.assertIn('of the coroutine', ct.__dict__['__qualname__'].__doc__)
self.assertEqual(ct.__name__, 'coroutine')
self.assertEqual(ct.__name__, 'CoroutineType')

async def f(): pass
c = f()
Expand Down
12 changes: 4 additions & 8 deletions Lib/test/test_crossinterp.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def ignore_byteswarning():

METHOD = defs.SpamOkay().okay
BUILTIN_METHOD = [].append
MEMBER_DESCRIPTOR = types.FunctionType.__globals__
METHOD_DESCRIPTOR_WRAPPER = str.join
METHOD_WRAPPER = object().__str__
WRAPPER_DESCRIPTOR = object.__init__
Expand All @@ -69,7 +70,7 @@ def ignore_byteswarning():
BUILTIN_METHOD: types.BuiltinMethodType,
dict.__dict__['fromkeys']: types.ClassMethodDescriptorType,
types.FunctionType.__code__: types.GetSetDescriptorType,
types.FunctionType.__globals__: types.MemberDescriptorType,
MEMBER_DESCRIPTOR: types.MemberDescriptorType,
METHOD_DESCRIPTOR_WRAPPER: types.MethodDescriptorType,
METHOD_WRAPPER: types.MethodWrapperType,
WRAPPER_DESCRIPTOR: types.WrapperDescriptorType,
Expand Down Expand Up @@ -278,16 +279,11 @@ def ignore_byteswarning():
*defs.TOP_CLASSES,
*USER_TOP_INSTANCES,
*USER_EXCEPTIONS,
# from OTHER_TYPES
types.NoneType,
types.EllipsisType,
types.NotImplementedType,
types.GenericAlias,
types.UnionType,
types.SimpleNamespace,
*OTHER_TYPES,
# from BUILTIN_WRAPPERS
METHOD,
BUILTIN_METHOD,
MEMBER_DESCRIPTOR,
METHOD_DESCRIPTOR_WRAPPER,
METHOD_WRAPPER,
WRAPPER_DESCRIPTOR,
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,7 @@ class mydialect(csv.Dialect):
with self.assertRaises(csv.Error) as cm:
mydialect()
self.assertEqual(str(cm.exception),
'"delimiter" must be a unicode character, not NoneType')
'"delimiter" must be a unicode character, not types.NoneType')

def test_escapechar(self):
class mydialect(csv.Dialect):
Expand Down Expand Up @@ -1281,7 +1281,7 @@ class mydialect(csv.Dialect):
with self.assertRaises(csv.Error) as cm:
mydialect()
self.assertEqual(str(cm.exception),
'"lineterminator" must be a string, not NoneType')
'"lineterminator" must be a string, not types.NoneType')

def test_invalid_chars(self):
def create_invalid(field_name, value, **kwargs):
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5171,11 +5171,11 @@ class X:

def test_qualname(self):
descriptors = [str.lower, complex.real, float.real, int.__add__]
types = ['method', 'member', 'getset', 'wrapper']
types = ['Method', 'Member', 'GetSet', 'Wrapper']

# make sure we have an example of each type of descriptor
for d, n in zip(descriptors, types):
self.assertEqual(type(d).__name__, n + '_descriptor')
self.assertEqual(type(d).__name__, n + 'DescriptorType')

for d in descriptors:
qualname = d.__objclass__.__qualname__ + '.' + d.__name__
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_exception_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ def __repr__(self):
seq = MySeq(None)
with self.assertRaisesRegex(
TypeError,
r".*MySeq\.__repr__\(\) must return a str, not NoneType"
r".*MySeq\.__repr__\(\) must return a str, not types.NoneType"
):
ExceptionGroup("test", seq)

Expand Down
Loading
Loading