This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: inspect.getsource only works for objects loaded from files, not interactive session
Type: behavior Stage: needs patch
Components: IDLE, Interpreter Core Versions: Python 3.8, Python 3.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: Ark-kun, Claudiu.Popa, Ivan.Pozdeev, Naddiseo, Steven.Barker, eric.araujo, ned.deily, nikitakit, r.david.murray, terry.reedy, vmsp, zorceta
Priority: normal Keywords:

Created on 2011-09-06 18:37 by Claudiu.Popa, last changed 2022-04-11 14:57 by admin.

Messages (15)
msg143645 - (view) Author: PCManticore (Claudiu.Popa) * (Python triager) Date: 2011-09-06 18:37
inspect.getsource called with a class defined in the same file fails with
TypeError: <module '__main__' (built-in)> is a built-in class, although the documentation says that:

"The argument may be a module, class, method, function, traceback, frame,
or code object.  The source code is returned as a single string." I think that should be specified in documentation that this function works only for objects living in a module.
msg143773 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-09-09 17:16
> inspect.getsource called with a class defined in the same file fails
> with TypeError: <module '__main__' (built-in)> is a built-in class

The error message makes me think that getsource(__main__) was used, not getsource(SomeClass).  Can you check again?
msg143790 - (view) Author: PCManticore (Claudiu.Popa) * (Python triager) Date: 2011-09-09 18:59
Yes. On Python 3.2 (r32:88445, Feb 20 2011, 21:29:02) [MSC v.1500 32 bit (Intel)] on win32, the result for the following lines:

import inspect
class A:
    pass
inspect.getsource(A)

is:

Traceback (most recent call last):
  File "E:/Scripts/Snippets/test_inspect_bug.py", line 4, in <module>
    inspect.getsource(A)
  File "C:\Python32\lib\inspect.py", line 694, in getsource
    lines, lnum = getsourcelines(object)
  File "C:\Python32\lib\inspect.py", line 683, in getsourcelines
    lines, lnum = findsource(object)
  File "C:\Python32\lib\inspect.py", line 522, in findsource
    file = getsourcefile(object)
  File "C:\Python32\lib\inspect.py", line 441, in getsourcefile
    filename = getfile(object)
  File "C:\Python32\lib\inspect.py", line 406, in getfile
    raise TypeError('{!r} is a built-in class'.format(object))
TypeError: <module '__main__' (built-in)> is a built-in class
>>>
msg143831 - (view) Author: PCManticore (Claudiu.Popa) * (Python triager) Date: 2011-09-10 06:45
I forgot to mention that I executed this code directly in IDLE. It seems to work perfectly on command line though.
msg185179 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2013-03-25 02:54
> It seems to work perfectly on command line though.

If the code is saved in a file, yes, but not in an interactive interpreter.  This is not actually related to IDLE, but to the fact that inspect.getsource merely finds the __file__ attribute of the module object for its argument.  If a module object has no file, the error message indicates that it’s a built-in module (like sys), but this fails to take into account the special __main__ module in an interactive interpreter.

It might be worth it to improve the error message, and in any case the documentation can be improved.
msg245714 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2015-06-24 04:04
In duplicate Issue24491, zorceta notes: "Both python.exe and IDLE can't. IPython is able to, as it inserts REPL input into linecache."
msg245721 - (view) Author: Zorceta (zorceta) Date: 2015-06-24 05:45
When provided object is not from a file, like input in interactive shell, `inspect` internals will check for it in `linecache`, which official Python shell and IDLE won't put interactive shell input into, yet. This can be simply solved.

Whether interactive shell input can be put into `linecache` may be a problem, but it'll make life easier, as interactive shell saves time from edit-save-run 'loop'.

btw, I changed the title, since I don't think, what original author thought need to be documented, is absolutely right.
msg245723 - (view) Author: Zorceta (zorceta) Date: 2015-06-24 06:05
> When provided object is not from a file

should be

'When `inspect` can't find the source file of provided object'.

My mistake.
msg252846 - (view) Author: (nikitakit) Date: 2015-10-12 06:33
I just ran into this issue trying to introspect an IPython session, in which case the __main__ module doesn't have a file associated with it.

But it turns out that methods defined in a class do have source code associated with them, so it's possible to add a workaround for the common case where a class actually has methods.

Code: https://gist.github.com/nikitakit/642cb96febdf2f812d0b
msg265815 - (view) Author: Steven Barker (Steven.Barker) * Date: 2016-05-18 06:45
The problem being discussed here just came up on Stack Overflow today: http://stackoverflow.com/questions/37288135/inspect-module-for-python/

The cause of the incorrect error message is pretty clear. The relevant code from `inspect.getfile` should do something better when the object has a `__module__` attribute, but the module named (when looked up in `sys.modules`) does not have a `__file__` attribute. Currently it says the module is a builtin class, which is total nonsense.

A very basic fix would be to have an extra error case:

    if isclass(object):
        if hasattr(object, '__module__'):
            object = sys.modules.get(object.__module__)
            if hasattr(object, '__file__'):
                return object.__file__
            raise TypeError()              # need a relevant message here!!!
        raise TypeError('{!r} is a built-in class'.format(object))

It might be easier to make a meaningful message if the code after the first `if` didn't overwrite `object` with the module.

But, really, it would be nice to figure out a better fix, which would make the relevant inspect functions actually work for classes defined interactively in the `__main__` module.
msg297768 - (view) Author: Vitor Pereira (vmsp) * Date: 2017-07-05 15:25
So, what would be the right approach here? Store the interactive session's input text in memory?
msg297797 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-07-06 04:04
Probably.  Figure out a protocol to inject them into linecache, perhaps.  But I'm not sure such a thing would be accepted.  If you can figure out a way to make it work at least theoretically, it would probably be best to talk about it on python-ideas first.

In the meantime it would be nice to improve the error message, which is what we should use this issue for.
msg328836 - (view) Author: Ivan Pozdeev (Ivan.Pozdeev) * Date: 2018-10-29 15:02
See https://bugs.python.org/issue33826?@ok_message=msg%20328824%20created%0Aissue%2033826%20message_count%2C%20messages%20edited%20ok&@template=item#msg319692 how IPython stores source from interactive input and why it's not appropriate for vanilla REPL IMO.
msg331659 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2018-12-11 22:23
Do we really need to say that getsource(object) can only get the object's source if it is accessible from the object?  Getsource also fails if a module is loaded from a .pyc with not corresponding .py available.

The problem is not the call being in __main__.  When I put the three lines (with the 3rd wrapped with print()) in an IDLE editor and run, and re-inspect, I get

======================== RESTART: F:\Python\a\tem3.py ========================
class A:
    pass

>>> inspect.getsource(A)
'class A:\n    pass\n'

Ditto if I run > py -i -m a.tem3

If I continue in IDLE's Shell

>>> class B: pass

>>> inspect.getsource(B)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    inspect.getsource(B)
  File "F:\dev\37\lib\inspect.py", line 973, in getsource
    lines, lnum = getsourcelines(object)
  File "F:\dev\37\lib\inspect.py", line 955, in getsourcelines
    lines, lnum = findsource(object)
  File "F:\dev\37\lib\inspect.py", line 812, in findsource
    raise OSError('could not find class definition')
OSError: could not find class definition

If I enter the three lines above in a fress python or IDLEs shell, I get the TypeError above.

IDLE does store interactive inputs into linecache, so that tracebacks contain the offending line (unlike interactive python). But it does so on a statement by statement basis, so that each entry is treated as a separate file.  In a traceback for an exception in a multiline statement, the line number is relative to the statement.

>>> def f():
	# line2 of f
	1/0

	
>>> f()
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    f()
  File "<pyshell#12>", line 3, in f
    1/0
ZeroDivisionError: division by zero

Interactive python displays '<stdin>' as the file for all entries.  IDLE numbers them, so previous statements remained cached.  I consider enhanced interactive tracebacks to be an important feature.

But I don't see how to attach individual pseudofile names to classes and functions so that getsource could find their source lines.
msg382107 - (view) Author: Alexey Volkov (Ark-kun) Date: 2020-11-30 02:13
This is also an issue even for non-interactive scenarios:

When doing `python -c '<some code>'` inspect.getsource does not work and there are no stack traces.

Perhaps this case will be easier to fix?
History
Date User Action Args
2022-04-11 14:57:21adminsetgithub: 57129
2020-11-30 02:13:01Ark-kunsetnosy: + Ark-kun
messages: + msg382107
2018-12-11 22:23:06terry.reedysetnosy: + terry.reedy

messages: + msg331659
versions: + Python 3.7, Python 3.8, - Python 2.7, Python 3.4, Python 3.5, Python 3.6
2018-10-29 15:02:29Ivan.Pozdeevsetnosy: + Ivan.Pozdeev
messages: + msg328836
2017-07-06 04:04:18r.david.murraysetnosy: + r.david.murray
messages: + msg297797
2017-07-05 15:25:22vmspsetnosy: + vmsp
messages: + msg297768
2016-05-18 06:45:42Steven.Barkersetnosy: + Steven.Barker
messages: + msg265815
2015-10-12 06:33:34nikitakitsetnosy: + nikitakit
messages: + msg252846
2015-06-24 06:05:03zorcetasetmessages: + msg245723
2015-06-24 05:46:58zorcetasetnosy: - docs@python
2015-06-24 05:46:38zorcetasetcomponents: + IDLE, Interpreter Core, - Documentation
2015-06-24 05:45:05zorcetasetmessages: + msg245721
title: Document that inspect.getsource only works for objects loaded from files, not interactive session -> inspect.getsource only works for objects loaded from files, not interactive session
2015-06-24 04:04:41ned.deilylinkissue24491 superseder
2015-06-24 04:04:29ned.deilysetnosy: + ned.deily, zorceta

messages: + msg245714
versions: + Python 3.5, Python 3.6, - Python 3.2, Python 3.3
2013-03-25 02:54:47eric.araujosettitle: inspect.getsource fails to get source of local classes -> Document that inspect.getsource only works for objects loaded from files, not interactive session
stage: needs patch
messages: + msg185179
versions: + Python 3.4
2013-03-24 00:40:58Naddiseosetnosy: + Naddiseo
2011-09-10 06:45:37Claudiu.Popasetmessages: + msg143831
2011-09-09 18:59:58Claudiu.Popasetmessages: + msg143790
2011-09-09 17:16:18eric.araujosetnosy: + eric.araujo
title: Inspect.getsource fails to get source of local classes -> inspect.getsource fails to get source of local classes
messages: + msg143773

versions: - Python 3.1
2011-09-06 18:37:57Claudiu.Popacreate