Skip to content

Commit 2b4c31d

Browse files
[3.13] gh-122981: Fix inspect.getsource() for generated classes with Python base classes (GH-123001) (#123182)
gh-122981: Fix inspect.getsource() for generated classes with Python base classes (GH-123001) Look up __firstlineno__ only in the class' dict, without searching in base classes. (cherry picked from commit f88c14d) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 8b6dd92 commit 2b4c31d

File tree

4 files changed

+66
-4
lines changed

4 files changed

+66
-4
lines changed

Lib/inspect.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,10 +1082,10 @@ def findsource(object):
10821082

10831083
if isclass(object):
10841084
try:
1085-
firstlineno = object.__firstlineno__
1086-
except AttributeError:
1085+
firstlineno = vars(object)['__firstlineno__']
1086+
except (TypeError, KeyError):
10871087
raise OSError('source code not available')
1088-
return lines, object.__firstlineno__ - 1
1088+
return lines, firstlineno - 1
10891089

10901090
if ismethod(object):
10911091
object = object.__func__

Lib/test/test_inspect/inspect_fodder2.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,45 @@ def g():
315315
class ClassWithCodeObject:
316316
import sys
317317
code = sys._getframe(0).f_code
318+
319+
import enum
320+
321+
# line 321
322+
class enum322(enum.Enum):
323+
A = 'a'
324+
325+
# line 325
326+
class enum326(enum.IntEnum):
327+
A = 1
328+
329+
# line 329
330+
class flag330(enum.Flag):
331+
A = 1
332+
333+
# line 333
334+
class flag334(enum.IntFlag):
335+
A = 1
336+
337+
# line 337
338+
simple_enum338 = enum.Enum('simple_enum338', 'A')
339+
simple_enum339 = enum.IntEnum('simple_enum339', 'A')
340+
simple_flag340 = enum.Flag('simple_flag340', 'A')
341+
simple_flag341 = enum.IntFlag('simple_flag341', 'A')
342+
343+
import typing
344+
345+
# line 345
346+
class nt346(typing.NamedTuple):
347+
x: int
348+
y: int
349+
350+
# line 350
351+
nt351 = typing.NamedTuple('nt351', (('x', int), ('y', int)))
352+
353+
# line 353
354+
class td354(typing.TypedDict):
355+
x: int
356+
y: int
357+
358+
# line 358
359+
td359 = typing.TypedDict('td359', (('x', int), ('y', int)))

Lib/test/test_inspect/test_inspect.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ def test_getsource_on_code_object(self):
824824
self.assertSourceEqual(mod.eggs.__code__, 12, 18)
825825

826826
def test_getsource_on_generated_class(self):
827-
A = type('A', (), {})
827+
A = type('A', (unittest.TestCase,), {})
828828
self.assertEqual(inspect.getsourcefile(A), __file__)
829829
self.assertEqual(inspect.getfile(A), __file__)
830830
self.assertIs(inspect.getmodule(A), sys.modules[__name__])
@@ -932,6 +932,24 @@ def test_anonymous(self):
932932
# as argument to another function.
933933
self.assertSourceEqual(mod2.anonymous, 55, 55)
934934

935+
def test_enum(self):
936+
self.assertSourceEqual(mod2.enum322, 322, 323)
937+
self.assertSourceEqual(mod2.enum326, 326, 327)
938+
self.assertSourceEqual(mod2.flag330, 330, 331)
939+
self.assertSourceEqual(mod2.flag334, 334, 335)
940+
self.assertRaises(OSError, inspect.getsource, mod2.simple_enum338)
941+
self.assertRaises(OSError, inspect.getsource, mod2.simple_enum339)
942+
self.assertRaises(OSError, inspect.getsource, mod2.simple_flag340)
943+
self.assertRaises(OSError, inspect.getsource, mod2.simple_flag341)
944+
945+
def test_namedtuple(self):
946+
self.assertSourceEqual(mod2.nt346, 346, 348)
947+
self.assertRaises(OSError, inspect.getsource, mod2.nt351)
948+
949+
def test_typeddict(self):
950+
self.assertSourceEqual(mod2.td354, 354, 356)
951+
self.assertRaises(OSError, inspect.getsource, mod2.td359)
952+
935953
class TestBlockComments(GetSourceBase):
936954
fodderModule = mod
937955

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :func:`inspect.getsource` for generated classes with Python base classes
2+
(e.g. enums).

0 commit comments

Comments
 (0)