Skip to content

Commit 87256a5

Browse files
tonghuarootmiss-islington
authored andcommitted
gh-152692: Fix annotationlib.type_repr() for bound built-in methods (GH-152693)
type_repr() returned a string naming a nonexistent module None -- e.g. type_repr([].append) == 'None.list.append' -- for bound built-in methods, whose __module__ is None. Return the qualified name when __module__ is None, as already done for the "builtins" module. (cherry picked from commit 6979003) Co-authored-by: tonghuaroot (童话) <tonghuaroot@gmail.com>
1 parent 18e0bcb commit 87256a5

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

‎Lib/annotationlib.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,9 +1128,17 @@ def type_repr(value):
11281128
11291129
"""
11301130
if isinstance(value, (type, types.FunctionType, types.BuiltinFunctionType)):
1131-
if value.__module__ == "builtins":
1131+
module = value.__module__
1132+
if module is None:
1133+
# Built-in methods have __module__ set to None; recover the module
1134+
# from the bound object (the class itself for class-bound methods).
1135+
obj = getattr(value, "__self__", None)
1136+
if obj is not None:
1137+
cls = obj if isinstance(obj, type) else type(obj)
1138+
module = cls.__module__
1139+
if module is None or module == "builtins":
11321140
return value.__qualname__
1133-
return f"{value.__module__}.{value.__qualname__}"
1141+
return f"{module}.{value.__qualname__}"
11341142
elif isinstance(value, _Template):
11351143
tree = _template_to_ast(value)
11361144
return ast.unparse(tree)

‎Lib/test/test_annotationlib.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,6 +1857,18 @@ def nested():
18571857
type_repr(nested), f"{__name__}.TestTypeRepr.test_type_repr.<locals>.nested"
18581858
)
18591859
self.assertEqual(type_repr(len), "len")
1860+
# gh-152692: built-in methods have __module__ set to None
1861+
self.assertEqual(type_repr([].append), "list.append")
1862+
self.assertEqual(type_repr((1).bit_length), "int.bit_length")
1863+
self.assertEqual(type_repr(dict.fromkeys), "dict.fromkeys")
1864+
# Methods of extension types also have __module__ set to None, but
1865+
# are not in the builtins module.
1866+
import datetime
1867+
self.assertEqual(
1868+
type_repr(datetime.datetime(2000, 1, 1).isoformat),
1869+
"datetime.datetime.isoformat",
1870+
)
1871+
self.assertEqual(type_repr(datetime.date.today), "datetime.date.today")
18601872
self.assertEqual(type_repr(type_repr), "annotationlib.type_repr")
18611873
self.assertEqual(type_repr(times_three), f"{__name__}.times_three")
18621874
self.assertEqual(type_repr(...), "...")
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :func:`annotationlib.type_repr` returning a string such as
2+
``None.list.append`` for bound built-in methods, whose ``__module__`` is
3+
``None``. It now returns the qualified name, as it already did for other
4+
builtins.

0 commit comments

Comments
 (0)