Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

inspect.getsource only works for objects loaded from files, not interactive session #57129

Closed
PCManticore mannequin opened this issue Sep 6, 2011 · 20 comments
Closed
Labels
3.13 new features, bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error

Comments

@PCManticore
Copy link
Mannequin

PCManticore mannequin commented Sep 6, 2011

BPO 12920
Nosy @terryjreedy, @ned-deily, @merwok, @bitdancer, @PCManticore, @Naddiseo, @BlckKnght, @native-api, @vmsp, @Ark-kun

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2011-09-06.18:37:57.848>
labels = ['interpreter-core', '3.8', 'expert-IDLE', 'type-bug', '3.7']
title = 'inspect.getsource only works for objects loaded from files, not interactive session'
updated_at = <Date 2020-11-30.02:13:01.074>
user = 'https://github.com/PCManticore'

bugs.python.org fields:

activity = <Date 2020-11-30.02:13:01.074>
actor = 'Ark-kun'
assignee = 'docs@python'
closed = False
closed_date = None
closer = None
components = ['IDLE', 'Interpreter Core']
creation = <Date 2011-09-06.18:37:57.848>
creator = 'Claudiu.Popa'
dependencies = []
files = []
hgrepos = []
issue_num = 12920
keywords = []
message_count = 15.0
messages = ['143645', '143773', '143790', '143831', '185179', '245714', '245721', '245723', '252846', '265815', '297768', '297797', '328836', '331659', '382107']
nosy_count = 12.0
nosy_names = ['terry.reedy', 'ned.deily', 'eric.araujo', 'r.david.murray', 'Claudiu.Popa', 'Naddiseo', 'Steven.Barker', 'Ivan.Pozdeev', 'zorceta', 'nikitakit', 'vmsp', 'Ark-kun']
pr_nums = []
priority = 'normal'
resolution = None
stage = 'needs patch'
status = 'open'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue12920'
versions = ['Python 3.7', 'Python 3.8']

Linked PRs

@PCManticore
Copy link
Mannequin Author

PCManticore mannequin commented Sep 6, 2011

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.

@PCManticore PCManticore mannequin added the docs Documentation in the Doc dir label Sep 6, 2011
@PCManticore PCManticore mannequin assigned docspython Sep 6, 2011
@PCManticore PCManticore mannequin added the type-bug An unexpected behavior, bug, or error label Sep 6, 2011
@merwok
Copy link
Member

merwok commented Sep 9, 2011

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?

@merwok merwok changed the title Inspect.getsource fails to get source of local classes inspect.getsource fails to get source of local classes Sep 9, 2011
@PCManticore
Copy link
Mannequin Author

PCManticore mannequin commented Sep 9, 2011

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
>>>

@PCManticore
Copy link
Mannequin Author

PCManticore mannequin commented Sep 10, 2011

I forgot to mention that I executed this code directly in IDLE. It seems to work perfectly on command line though.

@merwok
Copy link
Member

merwok commented Mar 25, 2013

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.

@merwok merwok changed the title inspect.getsource fails to get source of local classes Document that inspect.getsource only works for objects loaded from files, not interactive session Mar 25, 2013
@ned-deily
Copy link
Member

In duplicate bpo-24491, zorceta notes: "Both python.exe and IDLE can't. IPython is able to, as it inserts REPL input into linecache."

@zorceta
Copy link
Mannequin

zorceta mannequin commented Jun 24, 2015

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.

@zorceta zorceta mannequin changed the 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 Jun 24, 2015
@zorceta zorceta mannequin added topic-IDLE interpreter-core (Objects, Python, Grammar, and Parser dirs) and removed docs Documentation in the Doc dir labels Jun 24, 2015
@zorceta
Copy link
Mannequin

zorceta mannequin commented Jun 24, 2015

When provided object is not from a file

should be

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

My mistake.

@nikitakit
Copy link
Mannequin

nikitakit mannequin commented Oct 12, 2015

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

@BlckKnght
Copy link
Mannequin

BlckKnght mannequin commented May 18, 2016

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.

@vmsp
Copy link
Mannequin

vmsp mannequin commented Jul 5, 2017

So, what would be the right approach here? Store the interactive session's input text in memory?

@bitdancer
Copy link
Member

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.

@native-api
Copy link
Mannequin

native-api mannequin commented Oct 29, 2018

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.

@terryjreedy
Copy link
Member

terryjreedy commented Dec 11, 2018

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

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.

@terryjreedy terryjreedy added 3.7 (EOL) end of life 3.8 only security fixes labels Dec 11, 2018
@Ark-kun
Copy link
Mannequin

Ark-kun mannequin commented Nov 30, 2020

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?

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
@liudonghua123
Copy link

liudonghua123 commented Jul 22, 2022

It would be convenient if inspect.getsource support user defined functions/classes in REPL environment.

Is there some simple hack ways like ipython does. I am writing an app which embed a simple repl shell, and I really need this feature. I want to find how ipython achieved this, but it involved a lot of other stuffs.

yihong0618 added a commit to greenplum-db/GreenplumPython that referenced this issue Jan 17, 2023
this commit fix can not run greenplumpython in python repl
more to check issue python/cpython#57129
@iritkatriel
Copy link
Member

CC @pablogsal

@pablogsal
Copy link
Member

This works now as part of #110775

We can add some tests and close the issue

pablogsal added a commit to pablogsal/cpython that referenced this issue Oct 23, 2023
pablogsal added a commit to pablogsal/cpython that referenced this issue Oct 23, 2023
Signed-off-by: Pablo Galindo <pablogsal@gmail.com>
pablogsal added a commit to pablogsal/cpython that referenced this issue Oct 24, 2023
@liudonghua123
Copy link

From greenplum-db/GreenplumPython#139, I found I can use dill.source.getsource as fallback to inspect.getsource.

6d167b52a45059c758348e43584e5a7

@merwok
Copy link
Member

merwok commented Oct 24, 2023

Please read previous comments: the next version of Python will fix this directly.

@merwok merwok added 3.13 new features, bugs and security fixes and removed topic-IDLE 3.8 only security fixes 3.7 (EOL) end of life labels Oct 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.13 new features, bugs and security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error
Projects
Status: Done
Development

No branches or pull requests

7 participants