Skip to content

linecache.cache sometimes has an entry for <string> under Python 3.13.0a5 #117174

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

Closed
Zac-HD opened this issue Mar 23, 2024 · 12 comments
Closed

linecache.cache sometimes has an entry for <string> under Python 3.13.0a5 #117174

Zac-HD opened this issue Mar 23, 2024 · 12 comments
Labels
type-bug An unexpected behavior, bug, or error

Comments

@Zac-HD
Copy link
Contributor

Zac-HD commented Mar 23, 2024

Bug report

Bug description:

I noticed this via Hypothesis' pretty-printer for lambda functions, and tracked the divergence through the inspect module to linecache:

import linecache

def test():
    print(linecache.cache["<string>"])  # expected to raise KeyError
    assert False

If I run this snippet with python3.13 -Wignore -m pytest repro.py, or any older Python version, you'll get the expected KeyError. But if I append -n2 to the command, it prints (44, None, ['import sys;exec(eval(sys.stdin.readline()))\n'], '<string>')!

(python3.13 -m pip install pytest pytest-xdist will get the dependencies for this)

That's the popen bootstrap line from execnet, which handles running code across multiple processes for pytest-xdist. At this point I've found linecache.cache.pop("<string>", None) to be an acceptable workaround, and since I don't have any particular knowledge of either the CPython or execnet code that's as far as I investigated.

CPython versions tested on:

3.8, 3.9, 3.10, 3.11, 3.12, 3.13

Operating systems tested on:

Linux

Linked PRs

@gaogaotiantian
Copy link
Member

I believe this is a feature from #110805 which allows the traceback to show source code with python -c.

python -c "import linecache;print(linecache.cache);1/0"

3.12:

{}
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ZeroDivisionError: division by zero

3.13:

{'<string>': (44, None, ['import linecache;print(linecache.cache);1/0\n'], '<string>')}
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import linecache;print(linecache.cache);1/0
                                            ~^~
ZeroDivisionError: division by zero

However, I can see that the secret <string> key in the cache causing issues for inspect because that module heavily relies on it. An example:

./python -c "import inspect;x=eval('lambda x: x');print(inspect.getsource(x))"

The code above should trigger an exception because we should not be able to get the source of x (it's dynamically generated) but now it gives the full string.

I think a proper fix would be to change the code object name <string> for code passed with -c. <string> is commonly used for many dynamically generated code objects and if we want to do something special with the source code from -c we should name it differently. It might not be 100% backward compatible but I don't see a giant gap there. We can even keep the <string> in the traceback like what we did for python inputs.

A simpler way would be just disable this feature for -c - it's designed for interactive inputs and I don't even know if this is intentional or a pleasant side effect.

Or we could do even more aggressive and do similar numbering for dynamically compiled code and always give source code, which is a bit ambitious and definitely beyond this issue.

@pablogsal thoughts on this? I can do the PR if you want.

pablogsal added a commit to pablogsal/cpython that referenced this issue Apr 3, 2024
…ractive code objects

Using <string> leads to confusion when linecache it's populated with
<string> entries as the inspect module heavily relies on it. An example:

./python -c "import inspect;x=eval('lambda x: x');print(inspect.getsource(x))"

The code above should trigger an exception because we should not be able
to get the source of x (it's dynamically generated) but if we use
<string> as name for interactive code it will return gives the full string.
pablogsal added a commit to pablogsal/cpython that referenced this issue Apr 3, 2024
…ractive code objects

Using <string> leads to confusion when linecache it's populated with
<string> entries as the inspect module heavily relies on it. An example:

./python -c "import inspect;x=eval('lambda x: x');print(inspect.getsource(x))"

The code above should trigger an exception because we should not be able
to get the source of x (it's dynamically generated) but if we use
<string> as name for interactive code it will return gives the full string.

Signed-off-by: Pablo Galindo <pablogsal@gmail.com>
@pablogsal
Copy link
Member

I have opened #117500 to do a renaming of the string to something less common. We still want to preserve this behaviour for -c since this still leads to better error locations and tracebacks.

pablogsal added a commit to pablogsal/cpython that referenced this issue Apr 3, 2024
…ractive code objects

Using <string> leads to confusion when linecache it's populated with
<string> entries as the inspect module heavily relies on it. An example:

./python -c "import inspect;x=eval('lambda x: x');print(inspect.getsource(x))"

The code above should trigger an exception because we should not be able
to get the source of x (it's dynamically generated) but if we use
<string> as name for interactive code it will return gives the full string.

Signed-off-by: Pablo Galindo <pablogsal@gmail.com>
pablogsal added a commit to pablogsal/cpython that referenced this issue Apr 25, 2024
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Mar 10, 2025
…urce code (pythonGH-117500)

(cherry picked from commit a931a8b)

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
pablogsal added a commit to pablogsal/cpython that referenced this issue Mar 10, 2025
pablogsal added a commit that referenced this issue Mar 10, 2025
…ource code (GH-117500) (#131060)

gh-117174: Add a new route in linecache to fetch interactive source code (GH-117500)
(cherry picked from commit a931a8b)

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
@encukou
Copy link
Member

encukou commented Mar 11, 2025

After this was merged I'm seeing failures like this:

======================================================================
ERROR: test_frames (test.test_gdb.test_pretty_print.PrettyPrintTests.test_frames)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.cstratak-RHEL8-x86_64.lto/build/Lib/test/test_gdb/test_pretty_print.py", line 426, in test_frames
            gdb_output = self.get_stack_trace('''
                         ~~~~~~~~~~~~~~~~~~~~^^^^
    import sys
    ^^^^^^^^^^
    ...<7 lines>...
                                              cmds_after_breakpoint=['print (PyFrameObject*)x']
                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                              )
                                              ^
  File "/home/buildbot/buildarea/3.x.cstratak-RHEL8-x86_64.lto/build/Lib/test/test_gdb/util.py", line 242, in get_stack_trace
    out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED)
               ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/buildbot/buildarea/3.x.cstratak-RHEL8-x86_64.lto/build/Lib/test/test_gdb/util.py", line 67, in run_gdb
    raise Exception(f"{cmd_text} failed with exit code {proc.returncode}, "
    ...<2 lines>...
                    f"stderr={stderr!r}")
Exception: /usr/bin/gdb --batch -nx --init-eval-command 'add-auto-load-safe-path /home/buildbot/buildarea/3.x.cstratak-RHEL8-x86_64.lto/build/python-gdb.py' '--eval-command=set breakpoint pending yes' '--eval-command=break _typing__idfunc' '--eval-command=set print address off' --eval-command=run '--eval-command=set print entry-values no' '--eval-command=print (PyFrameObject*)x' --args /home/buildbot/buildarea/3.x.cstratak-RHEL8-x86_64.lto/build/python -bb -E -Wdefault -S -c '
import sys
from _typing import _idfunc
def foo(a, b, c):
    return sys._getframe(0)

f = foo(3, 4, 5)
_idfunc(f)' failed with exit code 1, expected exit code 0:
stdout='Breakpoint 1 at 0x43c9e0: file ./Include/object.h, line 270.\n[Thread debugging using libthread_db enabled]\nUsing host libthread_db library "/lib64/libthread_db.so.1".\n\nBreakpoint 1, _typing__idfunc () at ./Include/object.h:270\n270\t        return ob->ob_type;\n'
stderr='No symbol "x" in current context.\n'

@pablogsal
Copy link
Member

How this was not detected in the CI?

I will take a look later today

@pablogsal
Copy link
Member

I cannot reproduce this locally:

❯ ./python -m test test_gdb -m test_frames -v
== CPython 3.14.0a5+ (heads/main:19081158713, Mar 11 2025, 14:17:12) [GCC 14.2.1 20250207]
== Linux-6.13.6-arch1-1-x86_64-with-glibc2.41 little-endian
== Python build: debug
== cwd: /home/pablogsal/github/python/main/build/test_python_worker_653652æ
== CPU count: 36
== encodings: locale=UTF-8 FS=utf-8
== resources: all test resources are disabled, use -u option to unskip tests

Using random seed: 3793625289
0:00:00 load avg: 7.60 Run 5 tests sequentially in a single process
0:00:00 load avg: 7.60 [1/5] test_gdb.test_backtrace

----------------------------------------------------------------------
Ran 0 tests in 0.000s

NO TESTS RAN
0:00:00 load avg: 7.60 [1/5] test_gdb.test_backtrace ran no tests
0:00:00 load avg: 7.60 [2/5] test_gdb.test_cfunction

----------------------------------------------------------------------
Ran 0 tests in 0.000s

NO TESTS RAN
0:00:01 load avg: 7.60 [2/5] test_gdb.test_cfunction ran no tests
0:00:01 load avg: 7.60 [3/5] test_gdb.test_cfunction_full

----------------------------------------------------------------------
Ran 0 tests in 0.000s

NO TESTS RAN
0:00:01 load avg: 7.60 [3/5] test_gdb.test_cfunction_full ran no tests
0:00:01 load avg: 7.60 [4/5] test_gdb.test_misc

----------------------------------------------------------------------
Ran 0 tests in 0.000s

NO TESTS RAN
0:00:01 load avg: 7.60 [4/5] test_gdb.test_misc ran no tests
0:00:01 load avg: 7.60 [5/5] test_gdb.test_pretty_print
gdb version 16.2:
    GNU gdb (GDB) 16.2
    Copyright (C) 2024 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    path: /usr/bin/gdb

test_frames (test.test_gdb.test_pretty_print.PrettyPrintTests.test_frames) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.311s

OK
0:00:02 load avg: 7.60 [5/5] test_gdb.test_pretty_print passed

== Tests result: SUCCESS ==

4 tests run no tests:
    test_gdb.test_backtrace test_gdb.test_cfunction
    test_gdb.test_cfunction_full test_gdb.test_misc

1 test OK.

Total duration: 2.7 sec
Total tests: run=1 (filtered)
Total test files: run=5/5 (filtered) run_no_tests=4
Result: SUCCESS**
```**

@pablogsal
Copy link
Member

Looking at the failures this is super wrong:

Breakpoint 1, _typing__idfunc () at ./Include/object.h:270270	        return ob->ob_type;#0  _typing__idfunc () at ./Include/object.h:270

_typing__idfunc is certainly not at ./Include/object.h:270

@pablogsal
Copy link
Member

@encukou not sure what to do here, this looks like a gdb bug that only happens inAMD64 RHEL8 LTO PR. The output from gdb makes no sense to me. Maybe this only happens under LTO...

pablogsal added a commit to pablogsal/cpython that referenced this issue Mar 11, 2025
pablogsal added a commit to pablogsal/cpython that referenced this issue Mar 11, 2025
pablogsal added a commit to pablogsal/cpython that referenced this issue Mar 11, 2025
…1095)

(cherry picked from commit ebc24d5)

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
pablogsal added a commit that referenced this issue Mar 11, 2025
@encukou
Copy link
Member

encukou commented Mar 12, 2025

@vstinner, do you have any ideas?

vstinner added a commit to vstinner/cpython that referenced this issue Mar 12, 2025
@vstinner
Copy link
Member

tl; dr I created PR gh-131143 to skip test_gdb if Python is built with LTO.


There are two important things on the AMD64 RHEL 8 buildbot:

  • It uses an old GCC version: gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-24)
  • LTO is always used on RHEL buildbots

I already noticed weird gdb behaviors when Python is built with LTO.

@pablogsal:

Looking at the failures this is super wrong: (...)
_typing__idfunc is certainly not at ./Include/object.h:270

It's maybe time to declare game over and skip test_gdb if Python is built with LTO :-( We already skip test_gdb if Python is built with PGO or BOLT:

if support.check_cflags_pgo():
    raise unittest.SkipTest("test_gdb is not reliable on PGO builds")

if support.check_bolt_optimized():
    raise unittest.SkipTest("test_gdb is not reliable on BOLT optimized builds")

By the way, we recently modified test_gdb to also skip it if Python is built with clang -Og (commit 129db32).

@pablogsal
Copy link
Member

tl; dr I created PR gh-131143 to skip test_gdb if Python is built with LTO.


There are two important things on the AMD64 RHEL 8 buildbot:

  • It uses an old GCC version: gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-24)
  • LTO is always used on RHEL buildbots

I already noticed weird gdb behaviors when Python is built with LTO.

@pablogsal:

Looking at the failures this is super wrong: (...)
_typing__idfunc is certainly not at ./Include/object.h:270

It's maybe time to declare game over and skip test_gdb if Python is built with LTO :-( We already skip test_gdb if Python is built with PGO or BOLT:

if support.check_cflags_pgo():
    raise unittest.SkipTest("test_gdb is not reliable on PGO builds")

if support.check_bolt_optimized():
    raise unittest.SkipTest("test_gdb is not reliable on BOLT optimized builds")

By the way, we recently modified test_gdb to also skip it if Python is built with clang -Og (commit 129db32).

Please let's not do it. The whole point of test gdb is to test that gdb works with released versions of CPython. If we skip it then gdb will be broken and we never know that the functionality works with the versions that people use it

@pablogsal
Copy link
Member

I fixed the test failures by using a different function that doesn't get super optimised to wrong DWARF so at least we are fine for now

@vstinner
Copy link
Member

Oh, I didn't see that you already fixed the issue. That's why I was unable to reproduce it :-)

Ok, I abandon my PR skipping test_gdb is Python is built with LTO.

bswck added a commit to bswck/cpython that referenced this issue Mar 28, 2025
Yhg1s pushed a commit that referenced this issue Mar 28, 2025
…havior (follow-up gh-131065) (#131836)

Adapt test to new REPL behavior (follow-up gh-117174)
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Mar 28, 2025
…EPL behavior (follow-up pythongh-131065) (pythonGH-131836)

Adapt test to new REPL behavior (follow-up pythongh-117174)
(cherry picked from commit a6cf827)

Co-authored-by: Bartosz Sławecki <bartosz@ilikepython.com>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Mar 28, 2025
…e have always source (pythonGH-131065)

(cherry picked from commit 4192ce1)

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
Yhg1s pushed a commit that referenced this issue Mar 28, 2025
…we have always source (GH-131065) (#131850)

gh-117174: Adapt test_multiple_statements_fail_early now that we have always source (GH-131065)
(cherry picked from commit 4192ce1)

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
Yhg1s pushed a commit that referenced this issue Mar 28, 2025
…REPL behavior (follow-up gh-131065) (GH-131836) (#131841)

gh-117174: Adapt `test_multiple_statements_fail_early` to new REPL behavior (follow-up gh-131065) (GH-131836)

Adapt test to new REPL behavior (follow-up gh-117174)
(cherry picked from commit a6cf827)

Co-authored-by: Bartosz Sławecki <bartosz@ilikepython.com>
cjwatson added a commit to cjwatson/billiard that referenced this issue Apr 7, 2025
As of the changes to `linecache` in
python/cpython#117174, logging tracebacks
requires code objects to have a `co_qualname` attribute, which is true
for native Python code objects as of 3.11.  Adjust billiard's emulation
of them to match.

Spotted by pagure's tests in https://bugs.debian.org/1101621; analysis
and patch by Rebecca N. Palmer <rebecca_palmer@zoho.com>.
Nusnus pushed a commit to celery/billiard that referenced this issue Apr 7, 2025
As of the changes to `linecache` in
python/cpython#117174, logging tracebacks
requires code objects to have a `co_qualname` attribute, which is true
for native Python code objects as of 3.11.  Adjust billiard's emulation
of them to match.

Spotted by pagure's tests in https://bugs.debian.org/1101621; analysis
and patch by Rebecca N. Palmer <rebecca_palmer@zoho.com>.
seehwan pushed a commit to seehwan/cpython that referenced this issue Apr 16, 2025
seehwan pushed a commit to seehwan/cpython that referenced this issue Apr 16, 2025
seehwan pushed a commit to seehwan/cpython that referenced this issue Apr 16, 2025
…EPL behavior (follow-up pythongh-131065) (python#131836)

Adapt test to new REPL behavior (follow-up pythongh-117174)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

5 participants