classification
Title: inconsistent results with inspect.getsource / .getsourcelines
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.5, Python 3.4, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: isedev, xtreak, yselivanov
Priority: normal Keywords:

Created on 2014-09-07 14:45 by isedev, last changed 2018-11-07 06:22 by xtreak.

Messages (3)
msg226537 - (view) Author: Iestyn Elfick (isedev) Date: 2014-09-07 14:45
The functions inspect.getsource() and inspect.getsourcelines() return inconsistent results for frames corresponding to class definitions within a function.

Test code:

import sys
import inspect

def case1():
    class C:
        def __init__(self):
            pass
    c = C()

def case2():
    a = 1
    class C:
        def __init__(self):
            pass
    c = C()

def case3():
    def fn():
        pass
    class C:
        def __init__(self):
            pass
    c = C()

def trace(frame,event,arg):
    code = frame.f_code
    print('name:',code.co_name)
    print('source:\n',inspect.getsource(code),'\n')

for case in ('case1','case2','case3'):
    print('#####',case)
    call = getattr(sys.modules[__name__],case)
    sys.settrace(trace)
    try:
        call()
    finally:
        sys.settrace(None)

Result:

##### case1
name: case1
source:
 def case1():
    class C:
        def __init__(self):
            pass
    c = C()

name: C
source:
 def case1():
    class C:
        def __init__(self):
            pass
    c = C()

name: __init__
source:
         def __init__(self):
            pass

##### case2
name: case2
source:
 def case2():
    a = 1
    class C:
        def __init__(self):
            pass
    c = C()

name: C
source:
 def case2():
    a = 1
    class C:
        def __init__(self):
            pass
    c = C()

name: __init__
source:
         def __init__(self):
            pass

##### case3
name: case3
source:
 def case3():
    def fn():
        pass
    class C:
        def __init__(self):
            pass
    c = C()

name: C
source:
     def fn():
        pass

name: __init__
source:
         def __init__(self):
            pass

The source listed for frames named 'C' (the class creation code) is not consistent across all three cases. It could be considered incorrect in all cases as it does not correspond only to the class definition source lines.
msg226538 - (view) Author: Iestyn Elfick (isedev) Date: 2014-09-07 15:42
Possible fix:

--- /usr/lib64/python3.3/inspect.py     2014-06-30 19:21:52.000000000 +0200
+++ inspect.py  2014-09-07 17:41:29.463936079 +0200
@@ -600,7 +600,8 @@
         if not hasattr(object, 'co_firstlineno'):
             raise IOError('could not find function definition')
         lnum = object.co_firstlineno - 1
-        pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
+        pat = re.compile(
+            r'^(\s*(?:def|class)\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
         while lnum > 0:
             if pat.match(lines[lnum]): break
             lnum = lnum - 1
msg329406 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2018-11-07 06:22
I think this issue is covered with issue35101. It has an open PR as per the proposed fix with tests : https://github.com/python/cpython/pull/10209/files
History
Date User Action Args
2018-11-07 06:22:23xtreaksetnosy: + xtreak
messages: + msg329406
2014-09-12 21:47:43terry.reedysettitle: inconsistent results with inspect.getsource() / inspect.getsourcelines() -> inconsistent results with inspect.getsource / .getsourcelines
versions: + Python 2.7, Python 3.4, Python 3.5, - Python 3.3
2014-09-08 11:45:43berker.peksagsetnosy: + yselivanov
2014-09-07 15:42:25isedevsetmessages: + msg226538
2014-09-07 14:45:41isedevcreate