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

Internal error due to infinite recursion #13156

Closed
rr- opened this issue Jan 22, 2025 · 5 comments
Closed

Internal error due to infinite recursion #13156

rr- opened this issue Jan 22, 2025 · 5 comments
Labels
status: needs information reporter needs to provide more information; can be closed after 2 or more weeks of inactivity

Comments

@rr-
Copy link

rr- commented Jan 22, 2025

Error description

We are experiencing an internal error that enters an infinite loop – from what I can see, during reporting another error.

pip list

Package                    Version
-------------------------- ------------
alabaster                  0.7.10
amqp                       5.2.0
argh                       0.26.2
argon2-cffi                21.3.0
argon2-cffi-bindings       21.2.0
asgiref                    3.4.1
asn1                       2.7.0
async-timeout              4.0.2
attrs                      22.2.0
autopep8                   1.3.5
Babel                      2.11.0
BabelDjango                0.2.2
bandit                     1.6.2
bandit-teamcity            0.2.0
bcrypt                     3.2.0
beautifulsoup4             4.7.1
billiard                   3.6.4.0
cached-property            1.5.1
celery                     5.1.2
certifi                    2020.6.20
cffi                       1.14.3
chargebee                  2.33.0
charset-normalizer         2.0.12
click                      7.1.2
click-didyoumean           0.3.0
click-plugins              1.1.1
click-repl                 0.3.0
coverage                   4.4.2
cron-descriptor            1.4.0
cryptography               40.0.2
cssutils                   1.0.2
dataclasses                0.8
defusedxml                 0.6.0
Deprecated                 1.2.14
dj-database-url            2.1.0
Django                     3.2.23
django-braces              1.15.0
django-cors-headers        3.10.1
django-crispy-forms        1.13.0
django-csp                 3.7
django-datatable-view      0.8.3
django-extensions          2.1.0
django-filter              21.1
django-formtools           2.3
django-internationalflavor 0.4.3
django-modeltranslation    0.15.2
django-oauth-toolkit       2.3.0
django-redis               5.4.0
django-silk                2.0.0
django-zxcvbn-password     2.1.1
djangorestframework        3.14.0
dnspython                  2.2.1
docker                     5.0.3
docopt                     0.6.2
docutils                   0.14
enum-compat                0.0.3
Fabric3                    1.14.post1
factory-boy                2.11.1
Faker                      4.1.3
flake8                     3.5.0
flake8-quotes              1.0.0
flake8-teamcity            0.0.5
flake8-tofix               0.0.2
flower                     1.2.0
gitdb2                     2.0.3
GitPython                  2.1.8
gprof2dot                  2016.10.13
greenlet                   2.0.2
html-linter                0.4.1
humanize                   2.6.0
idna                       2.10
imagesize                  0.7.1
importlib-metadata         4.8.3
importlib-resources        5.3.0
iniconfig                  1.1.1
inlinestyler               0.2.5
isodate                    0.6.0
Jinja2                     3.0.3
jwcrypto                   1.5.0
kombu                      5.1.0
lxml                       4.9.3
Markdown                   3.3.7
MarkupSafe                 2.0.1
mccabe                     0.6.1
newrelic                   7.16.0.178
oauthlib                   3.2.2
packaging                  20.4
paramiko                   2.7.2
parsimonious               0.10.0
patchman-django-sharding   2.0.0
patchman-selenium          1.3.7
pathtools                  0.1.2
pbr                        3.1.1
pep517                     0.13.1
phonenumberslite           8.13.26
Pillow                     8.4.0
pip                        21.2.4
pip-tools                  6.4.0
platformdirs               2.4.0
pluggy                     1.0.0
polib                      1.0.8
progressbar2               3.55.0
prometheus-client          0.8.0
prompt-toolkit             3.0.36
psycopg2-binary            2.9.5
py                         1.11.0
pycodestyle                2.3.1
pycountry                  22.1.10
pycparser                  2.20
pyflakes                   1.5.0
Pygments                   2.2.0
PyNaCl                     1.4.0
pyparsing                  2.4.7
pystache                   0.6.4
pytest                     7.0.1
pytest-custom-exit-code    0.3.0
pytest-django              4.5.0
python-dateutil            2.8.2
python-http-client         3.3.7
python-utils               2.4.0
pytz                       2023.3.post1
PyYAML                     6.0.1
redis                      4.3.6
regex                      2022.8.17
requests                   2.27.1
requests-file              1.5.1
requests-toolbelt          0.9.1
rfeed                      1.1.1
selenium                   3.141.0
sendgrid                   6.10.0
sentry-sdk                 2.19.1
setuptools                 57.5.0
six                        1.15.0
smmap2                     2.0.3
snowballstemmer            1.2.1
soupsieve                  1.9.1
Sphinx                     1.7.4
sphinx-rtd-theme           0.3.1
sphinxcontrib-websupport   1.0.1
splinter                   0.15.0
SQLAlchemy                 1.4.50
sqlparse                   0.3.1
starkbank-ecdsa            2.2.0
stevedore                  1.28.0
tabulate                   0.8.10
tblib                      1.7.0
teamcity-messages          1.21
template-remover           0.1.9
text-unidecode             1.3
toml                       0.10.2
tomli                      1.2.3
tornado                    6.0.4
typing_extensions          4.1.1
Unidecode                  1.3.7
urllib3                    1.26.18
vine                       5.1.0
vulture                    2.8
watchdog                   0.8.3
wcwidth                    0.2.12
websocket-client           1.2.3
wheel                      0.37.1
wrapt                      1.16.0
zeep                       4.1.0
zipp                       3.1.0
zxcvbn                     4.4.28

Pytest and OS versions

pytest 7.0.1
Python 3.6.15
Linux 70819cd62512 6.12.9-arch1-1 #1 SMP PREEMPT_DYNAMIC Fri, 10 Jan 2025 00:39:41 +0000 x86_64 GNU/Linux

Detailed description

Unfortunately, the details how to replicate this issue are a bit elusive to me still and as such I can't provide a minimal example at this time. Hopefully, the stack trace contains enough information. Additionally, we heavily use selenium in the affected tests, and the app is built on Django. What I tried:

  • Muting the failing test. It does not help – the failure occurs right after, with a different test.
  • Using --tb=no, --tb=native, -p no:warnings does not help.
  • Removing the pytest-custom-exit-code package does not help.

I run the tests with the following invocation:

pytest -v --junit-xml=/build/test_report.xml --suppress-no-test-exit-code -m "selenium"

I understand that this is about an obsolete Python version and as such the issue will be likely closed.

portal_sharding/tests/test_frontend.py::ConnectionExceptionViewFrontEndTestCase::test PASSED [ 45%]
search/tests/staff/test_frontend.py::LoggedInPermissionSeleniumTestCase::test_signin_accepted_without_permission SKIPPED [ 45%]
search/tests/staff/test_frontend.py::StaffSearchViewTestCase::test SKIPPED [ 46%]
search/tests/staff/test_frontend.py::StaffSearchViewTestCase::test_progress_bar SKIPPED [ 46%]
search/tests/staff/test_frontend.py::StaffSearchViewTestCase::test_signin_accepted_without_permission SKIPPED [ 46%]
servers/tests/frontend/test_server.py::LoggedInPermissionSeleniumTestCase::test_signin_accepted_without_permission SKIPPED [ 46%]
servers/tests/frontend/test_server.py::ServerBasicSettingsUpdateTestCase::test_reapply_policies_current_time SKIPPED [ 47%]
servers/tests/frontend/test_server.py::ServerBasicSettingsUpdateTestCase::test_reapply_policies_detection_time SKIPPED [ 47%]
servers/tests/frontend/test_server.py::ServerBasicSettingsUpdateTestCase::test_reapply_policies_dont Destroying test database for alias 'default'...

INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/main.py", line 268, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/main.py", line 322, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_hooks.py", line 265, in __call__
INTERNALERROR>     return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 60, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/main.py", line 347, in pytest_runtestloop
INTERNALERROR>     item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_hooks.py", line 265, in __call__
INTERNALERROR>     return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 60, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/runner.py", line 113, in pytest_runtest_protocol
INTERNALERROR>     runtestprotocol(item, nextitem=nextitem)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/runner.py", line 126, in runtestprotocol
INTERNALERROR>     rep = call_and_report(item, "setup", log)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/runner.py", line 223, in call_and_report
INTERNALERROR>     report: TestReport = hook.pytest_runtest_makereport(item=item, call=call)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_hooks.py", line 265, in __call__
INTERNALERROR>     return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 55, in _multicall
INTERNALERROR>     gen.send(outcome)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/skipping.py", line 265, in pytest_runtest_makereport
INTERNALERROR>     rep = outcome.get_result()
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/runner.py", line 367, in pytest_runtest_makereport
INTERNALERROR>     return TestReport.from_item_and_call(item, call)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/reports.py", line 347, in from_item_and_call
INTERNALERROR>     excinfo, style=item.config.getoption("tbstyle", "auto")
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/nodes.py", line 481, in _repr_failure_py
INTERNALERROR>     truncate_locals=truncate_locals,
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/_code/code.py", line 666, in getrepr
INTERNALERROR>     return fmt.repr_excinfo(self)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/_code/code.py", line 926, in repr_excinfo
INTERNALERROR>     reprtraceback = self.repr_traceback(excinfo_)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/_code/code.py", line 867, in repr_traceback
INTERNALERROR>     reprentry = self.repr_traceback_entry(entry, einfo)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/_code/code.py", line 818, in repr_traceback_entry
INTERNALERROR>     s = self.get_source(source, line_index, excinfo, short=short)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/_code/code.py", line 756, in get_source
INTERNALERROR>     lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/_code/code.py", line 768, in get_exconly
INTERNALERROR>     exlines = excinfo.exconly(tryshort=True).split("\n")
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/_code/code.py", line 585, in exconly
INTERNALERROR>     lines = format_exception_only(self.type, self.value)
INTERNALERROR>   File "/usr/local/lib/python3.6/traceback.py", line 140, in format_exception_only
INTERNALERROR>     return list(TracebackException(etype, value, None).format_exception_only())
INTERNALERROR>   File "/usr/local/lib/python3.6/traceback.py", line 498, in __init__
INTERNALERROR>     _seen=_seen)
INTERNALERROR>   File "/usr/local/lib/python3.6/traceback.py", line 498, in __init__
INTERNALERROR>     _seen=_seen)
INTERNALERROR>   File "/usr/local/lib/python3.6/traceback.py", line 498, in __init__
INTERNALERROR>     _seen=_seen)
INTERNALERROR>   [Previous line repeated 234 more times]
INTERNALERROR> RecursionError: maximum recursion depth exceeded

= 50 passed, 134 skipped, 4923 deselected, 18811 warnings in 819.16s (0:13:39) =
@The-Compiler
Copy link
Member

This looks very similar to #3163, #4648 and #8957 (none of which have a reproducer either, unfortunately).

Given that the infinite recursion happens inside Python's own traceback.py module (and not pytest), indeed the first thing you should do is probably upgrade to a supported Python version. I couldn't find much at https://github.com/python/cpython/issues or https://docs.python.org/3/whatsnew/changelog.html from a quick search, but this still seems like a possible CPython rather than pytest bug (even more so if it also happens with --tb=native).

Without a reproducer, I don't think there is much that can be done from pytest's side. Could you perhaps show the stacktrace with --tb=native?

@The-Compiler The-Compiler added the status: needs information reporter needs to provide more information; can be closed after 2 or more weeks of inactivity label Jan 22, 2025
@The-Compiler
Copy link
Member

The-Compiler commented Jan 22, 2025

Here is the code where the loop occurs:

https://github.com/python/cpython/blob/v3.6.15/Lib/traceback.py#L489-L498

So for that to happen, there needs to be an exception that:

  • Has either a cycle in its __context__, or a very long chain of __context__ exceptions
  • If it's a cycle, that cycle works in a way that the id(exc_value.__context__) not in _seen prevention for exactly that doesn't work properly

For the "very long chain of exceptions" scenario, I found python/cpython#87014 which seems similar enough to your stacktrace, and that has been fixed in Python 3.10: python/cpython@6dfd173

So unless there is a reproducer that shows the contrary, this is a CPython bug that's been fixed already.

@rr-
Copy link
Author

rr- commented Jan 22, 2025

This looks very similar to #3163, #4648 and #8957 (none of which have a reproducer either, unfortunately).

Given that the infinite recursion happens inside Python's own traceback.py module (and not pytest), indeed the first thing you should do is probably upgrade to a supported Python version. I couldn't find much at https://github.com/python/cpython/issues or https://docs.python.org/3/whatsnew/changelog.html from a quick search, but this still seems like a possible CPython rather than pytest bug (even more so if it also happens with --tb=native).

Without a reproducer, I don't think there is much that can be done from pytest's side. Could you perhaps show the stacktrace with --tb=native?

Thank you for the quick reply. Here's the stack trace produced with --tb=native – unfortunately, I don't see much difference:

INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/main.py", line 268, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/main.py", line 322, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_hooks.py", line 265, in __call__
INTERNALERROR>     return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 60, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/main.py", line 347, in pytest_runtestloop
INTERNALERROR>     item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_hooks.py", line 265, in __call__
INTERNALERROR>     return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 60, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/runner.py", line 113, in pytest_runtest_protocol
INTERNALERROR>     runtestprotocol(item, nextitem=nextitem)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/runner.py", line 126, in runtestprotocol
INTERNALERROR>     rep = call_and_report(item, "setup", log)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/runner.py", line 223, in call_and_report
INTERNALERROR>     report: TestReport = hook.pytest_runtest_makereport(item=item, call=call)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_hooks.py", line 265, in __call__
INTERNALERROR>     return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 55, in _multicall
INTERNALERROR>     gen.send(outcome)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/skipping.py", line 265, in pytest_runtest_makereport
INTERNALERROR>     rep = outcome.get_result()
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/runner.py", line 367, in pytest_runtest_makereport
INTERNALERROR>     return TestReport.from_item_and_call(item, call)
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/reports.py", line 347, in from_item_and_call
INTERNALERROR>     excinfo, style=item.config.getoption("tbstyle", "auto")
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/nodes.py", line 481, in _repr_failure_py
INTERNALERROR>     truncate_locals=truncate_locals,
INTERNALERROR>   File "/usr/local/lib/python3.6/site-packages/_pytest/_code/code.py", line 651, in getrepr
INTERNALERROR>     self.type, self.value, self.traceback[0]._rawentry
INTERNALERROR>   File "/usr/local/lib/python3.6/traceback.py", line 121, in format_exception
INTERNALERROR>     type(value), value, tb, limit=limit).format(chain=chain))
INTERNALERROR>   File "/usr/local/lib/python3.6/traceback.py", line 498, in __init__
INTERNALERROR>     _seen=_seen)
INTERNALERROR>   File "/usr/local/lib/python3.6/traceback.py", line 498, in __init__
INTERNALERROR>     _seen=_seen)
INTERNALERROR>   File "/usr/local/lib/python3.6/traceback.py", line 498, in __init__
INTERNALERROR>     _seen=_seen)
INTERNALERROR>   [Previous line repeated 236 more times]
INTERNALERROR> RecursionError: maximum recursion depth exceeded while calling a Python object

@rr-
Copy link
Author

rr- commented Jan 22, 2025

Here is the code where the loop occurs:

https://github.com/python/cpython/blob/v3.6.15/Lib/traceback.py#L489-L498

So for that to happen, there needs to be an exception that:

  • Has either a cycle in its __context__, or a very long chain of __context__ exceptions
  • If it's a cycle, that cycle works in a way that the id(exc_value.__context__) not in _seen prevention for exactly that doesn't work properly

For the "very long chain of exceptions" scenario, I found python/cpython#87014 which seems similar enough to your stacktrace, and that has been fixed in Python 3.10: python/cpython@6dfd173

So unless there is a reproducer that shows the contrary, this is a CPython bug that's been fixed already.

Thank you for this post, especially the links to cpython.
I managed to roll out a pseudo-solution that looks like this, and helped us find out where the actual problem (with our code) was:

from unittest.mock import patch
from traceback import StackSummary, TracebackException, _some_str, walk_tb
from contextlib import contextmanager

"""
Adopted https://github.com/python/cpython/commit/6dfd1734f5b230bb8fbd2a9df806c1333b6652a8
to prevent MaxRecursionError in tests.
To be removed completely once we use Python 3.10+.
"""


@contextmanager
def patch_traceback():
    def _load_lines(self):
        """Private API. force all lines in the stack to be loaded."""
        for frame in self.stack:
            frame.line


    def format(self, *, chain=True):
        """Format the exception.

        If chain is not *True*, *__cause__* and *__context__* will not be formatted.

        The return value is a generator of strings, each ending in a newline and
        some containing internal newlines. `print_exception` is a wrapper around
        this method which just prints the lines to a file.

        The message indicating which exception occurred is always the last
        string in the output.
        """

        output = []
        exc = self
        while exc:
            if chain:
                if exc.__cause__ is not None:
                    chained_msg = _cause_message
                    chained_exc = exc.__cause__
                elif (exc.__context__  is not None and
                      not exc.__suppress_context__):
                    chained_msg = _context_message
                    chained_exc = exc.__context__
                else:
                    chained_msg = None
                    chained_exc = None

                output.append((chained_msg, exc))
                exc = chained_exc
            else:
                output.append((None, exc))
                exc = None

        for msg, exc in reversed(output):
            if msg is not None:
                yield msg
            if exc.stack:
                yield 'Traceback (most recent call last):\n'
                yield from exc.stack.format()
            yield from exc.format_exception_only()

    def __init__(
        self,
        exc_type,
        exc_value,
        exc_traceback,
        *,
        limit=None,
        lookup_lines=True,
        capture_locals=False,
        _seen=None
    ):
        is_recursive_call = _seen is not None

        # NB: we need to accept exc_traceback, exc_value, exc_traceback to
        # permit backwards compat with the existing API, otherwise we
        # need stub thunk objects just to glue it together.
        # Handle loops in __cause__ or __context__.
        if _seen is None:
            _seen = set()
        _seen.add(id(exc_value))
        # TODO: locals.
        self.stack = StackSummary.extract(
            walk_tb(exc_traceback),
            limit=limit,
            lookup_lines=lookup_lines,
            capture_locals=capture_locals,
        )
        self.exc_type = exc_type
        # Capture now to permit freeing resources: only complication is in the
        # unofficial API _format_final_exc_line
        self._str = _some_str(exc_value)
        if exc_type and issubclass(exc_type, SyntaxError):
            # Handle SyntaxError's specially
            self.filename = exc_value.filename
            self.lineno = str(exc_value.lineno)
            self.text = exc_value.text
            self.offset = exc_value.offset
            self.msg = exc_value.msg
        if lookup_lines:
            self._load_lines()

        self.__suppress_context__ = (
            exc_value.__suppress_context__ if exc_value else False
        )
        # Convert __cause__ and __context__ to `TracebackExceptions`s, use a
        # queue to avoid recursion (only the top-level call gets _seen == None)
        if not is_recursive_call:
            queue = [(self, exc_value)]
            while queue:
                te, e = queue.pop()
                if (
                    e
                    and e.__cause__ is not None
                    and id(e.__cause__) not in _seen
                ):
                    cause = TracebackException(
                        type(e.__cause__),
                        e.__cause__,
                        e.__cause__.__traceback__,
                        limit=limit,
                        lookup_lines=lookup_lines,
                        capture_locals=capture_locals,
                        _seen=_seen,
                    )
                else:
                    cause = None
                if (
                    e
                    and e.__context__ is not None
                    and id(e.__context__) not in _seen
                ):
                    context = TracebackException(
                        type(e.__context__),
                        e.__context__,
                        e.__context__.__traceback__,
                        limit=limit,
                        lookup_lines=lookup_lines,
                        capture_locals=capture_locals,
                        _seen=_seen,
                    )
                else:
                    context = None
                te.__cause__ = cause
                te.__context__ = context
                if cause:
                    queue.append((te.__cause__, e.__cause__))
                if context:
                    queue.append((te.__context__, e.__context__))

    with patch('traceback.TracebackException.__init__', __init__), patch(
        'traceback.TracebackException._load_lines', _load_lines
    ), patch(
        'traceback.TracebackException.format', format
    ):
        yield


@pytest.fixture(autouse=True, scope='session')
def patch_traceback_fixture():
    with patch_traceback():
        yield

@The-Compiler
Copy link
Member

Closing this then, as it indeed looks like you're just seeing a very long traceback and this CPython bug.

@The-Compiler The-Compiler closed this as not planned Won't fix, can't repro, duplicate, stale Jan 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs information reporter needs to provide more information; can be closed after 2 or more weeks of inactivity
Projects
None yet
Development

No branches or pull requests

2 participants