You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
3.12 setrecursionlimit is ignored in connection with @functools.cache #112215
We added the C recursion limit for safety reasons. We don't want to give that up.
We also opted for portability of code by setting the C recursion limit to a single value for all platforms.
Perhaps that was a mistake and we should choose different limits at build time depending on the platform and the build, improving backwards compatibility at the expense of portability.
@dimpase: your tone is aggressive. that's uncalled for. please keep it civil.
Apologies. In my defense, I can only say that I am speaking from one of the pitfalls wisely predicted by the SC here.
* The benefit of PEP 651 is negated as soon as a non-Python function is involved in the
recursion, making the likelihood of it being useful even smaller. It also creates
easy pitfalls for users who do end up relying on recursion.
@dimpase: your tone is aggressive. that's uncalled for. please keep it civil.
Apologies. In my defense, I can only say that I am speaking from one of the pitfalls wisely predicted by the SC here.
* The benefit of PEP 651 is negated as soon as a non-Python function is involved in the
recursion, making the likelihood of it being useful even smaller. It also creates
easy pitfalls for users who do end up relying on recursion.
And, by the way, limiting the C stack cannot prevent all stack overflows.
500, 1000, or 1500 are just rough estimates. We can still create stack overflows under these conditions.
The idiomatic way is to register a signal handler (on UNIX) / VectoredExceptionHandler (on Windows) to check if the stack pointer is out of the boundary while not recoverable.
I am fine with having these limits, but please provide a way to circumvent them.
Why are users forbidden to use large stack memory even if they provide one?
I think it would be more productive to come up with a way that we can calculate the C stack size accurately rather than continue to explain why it's not great (we know, we know).
getrlimit(RLIMIT_STACK only returns the size of the main process thread, not the running thread. There is no posix API to get a given threads stack size. It can only be controlled at thread creation time and CPython does not control creation of all threads. It is common for C/C++ applications to launch threads with a different stack size than the overly large main thread default.
getrlimit(RLIMIT_STACK only returns the size of the main process thread, not the running thread. There is no posix API to get a given threads stack size. It can only be controlled at thread creation time and CPython does not control creation of all threads. It is common for C/C++ applications to launch threads with a different stack size than the overly large main thread default.
Yeah, you are right. It's just too niche (too target-specific).
I also understand Python would prefer resumeable error to just aborting the program.
Then, how about providing the user an option to disable this C recursion limit check? Let the user manage themselves under this mode.
getrlimit(RLIMIT_STACK only returns the size of the main process thread, not the running thread. There is no posix API to get a given threads stack size. It can only be controlled at thread creation time and CPython does not control creation of all threads. It is common for C/C++ applications to launch threads with a different stack size than the overly large main thread default.
Isn't it possible to get C stack size (at thread creation time) directly via pthread_attr_getstacksize (which is POSIX)
instead of a GNU extension? These give you the minimal stack size. (Not sure what minimal stands for here).
One way or another, knowing C stack limit does not give one complete knowledge on the maximal C recursion depth, as different functions place different amount of data on stack.
Anyhow, for the issue at hand, the C version _functools._lru_cache_wrapper(), behind all this, is an implementation of a Python wrapper, what it produces has to be compatible with the Python version functools._lru_cache_wrapper(), recursion-depth-wise too.
Isn't it possible to get C stack size (at thread creation time) directly via pthread_attr_getstacksize (which is POSIX)
instead of a GNU extension?
Nope. That is merely a function for accessing the opaque pthread_attr_t structure data.
the code you posted in #112215 (comment) is basically a small wrapper around pthread_attr_getstacksize, isn't it?
Just copy it all if you must - there is nothing magical about a GNU extension there.
no it isn't. pthread_getattr_np() fetches the thread information.
OK, sorry, I misunderstood that. It seems that musl has similar functions implemented, so it doesn't look impossible to have this for all platforms. (I don't know anything about Windows, though).
Starting about mid-week, I always see 14 test failures on Windows 10 (Pro), seemingly due to this. Current main branch, release build, Visual Studio 2022, nothing customized:
Sometimes there's an explicit message about Windows stack overflow. Historically, as I recall we always needed a lower recursion limit on Windows (than on Linux).
Note: 13 of the 14 tests named above still pass in a debug build. As I recall, test_importlib (which still fails in a debug build) started flaking out before the other ones:
Traceback (most recent call last):
File "C:\Code\Python\Lib\test\test_importlib\test_main.py", line 424, in test_packages_distributions_symlinked_top_level
fixtures.build_files(files, self.site_dir)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
File "C:\Code\Python\Lib\test\test_importlib\_path.py", line 70, in build
create(contents, _ensure_tree_maker(prefix) / name)
~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Code\Python\Lib\functools.py", line 911, in wrapper
return dispatch(args[0].__class__)(*args, **kw)
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "C:\Code\Python\Lib\test\test_importlib\_path.py", line 91, in _
path.symlink_to(content)
~~~~~~~~~~~~~~~^^^^^^^^^
File "C:\Code\Python\Lib\pathlib\__init__.py", line 441, in symlink_to
os.symlink(target, self, target_is_directory)
~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: [WinError 1314] A required privilege is not held by the client: '.symlink.target' -> 'C:\\Users\\Tim\\AppData\\Local\\Temp\\test_python_zgi02bbl\\tmp8n3vtxjo\\symlinked'
Marking this as release blocker since we discussed this off-line. The plan is to restore 3.12 to the 3.11 behavior (which is a little tricky since various tests were added/updated, but it's doable). For 3.13 we will do per-platform things (and per-config-setting, e.g. debug) by default and add an API to change the C recursion limit.
Starting about mid-week, I always see 14 test failures on Windows 10 (Pro), seemingly due to this. Current main branch, release build, Visual Studio 2022, nothing customized:
What's the current state of this issue? I know some of the test failures were worked around, but do we need anything to address user concerns that hasn't already been done? Should this block today's 3.12 release?
#115083 ports the various C recursion limit changes from main, but it is unlikely to be ready until tomorrow as I need to get the limits and tests fixed for wasi.
Marking this as release blocker since we discussed this off-line. The plan is to restore 3.12 to the 3.11 behavior (which is a little tricky since various tests were added/updated, but it's doable). For 3.13 we will do per-platform things (and per-config-setting, e.g. debug) by default and add an API to change the C recursion limit.
Do I understand correctly that the plan for 3.12 changed to backporting various changes from main?
Is there anything left to do for this? A fix went in for the next 3.12 (scheduled in two months). Is there anything that still needs to be done for 3.13, before or after today's 3.13 alpha 4?
We might change the implementation for 3.13, to use the actual stack depth (in bytes) rather than a count.
What we have now works well enough though, and better than what we had historically.
For the particular case of cache() and lru_cache(), we may need to provide an easy way to get to the pure python version that works as expected. Perhaps something like from functools import py_lru_cache as lru_cache. Without such an option, users would need to code their own caching decorator to be able to run dynamic programming like that found in the StackOverflow question. That is what the user had to do in #127346 . I'm pretty sure that is not what we want.
I think the solution to this is to implement the wrapper function in Python, but implement the cache management in C.
That will fix the recursion issue, with little or no performance impact.
The wrapper function would look something like this:
Activity
gvanrossum commentedon Nov 20, 2023
@markshannon ^^
markshannon commentedon Nov 20, 2023
We added the C recursion limit for safety reasons. We don't want to give that up.
We also opted for portability of code by setting the C recursion limit to a single value for all platforms.
Perhaps that was a mistake and we should choose different limits at build time depending on the platform and the build, improving backwards compatibility at the expense of portability.
dimpase commentedon Nov 20, 2023
I don't understand how you can nuke all the recursive uses of Python C extensions in the name of "safety", sorry.
For a small sampling, have a look how many code chunks on GitHub have
@cacheandsetrecursionlimitin one place. I counted over 400.gpshead commentedon Nov 20, 2023
@dimpase: your tone is aggressive. that's uncalled for. please keep it civil.
dimpase commentedon Nov 20, 2023
Apologies. In my defense, I can only say that I am speaking from one of the pitfalls wisely predicted by the SC here.
in the rejection of PEP 651 – Robust Stack Overflow Handling
sunmy2019 commentedon Nov 21, 2023
I agree with this. It causes large inconvenience for users who do end up relying on recursion.
Their right to use C recursion is deprived, even without notice.
sunmy2019 commentedon Nov 21, 2023
And, by the way, limiting the C stack cannot prevent all stack overflows.
500, 1000, or 1500 are just rough estimates. We can still create stack overflows under these conditions.
The idiomatic way is to register a signal handler (on UNIX) / VectoredExceptionHandler (on Windows) to check if the stack pointer is out of the boundary while not recoverable.
I am fine with having these limits, but please provide a way to circumvent them.
Why are users forbidden to use large stack memory even if they provide one?
gvanrossum commentedon Nov 21, 2023
I think it would be more productive to come up with a way that we can calculate the C stack size accurately rather than continue to explain why it's not great (we know, we know).
sunmy2019 commentedon Nov 21, 2023
Yes, we can estimate it better. We don't need to be that accurate. We can make sure the user can make it larger.
For example on Linux, we can query the stack limit at the
programthread start. https://linux.die.net/man/2/getrlimit Then set the C recursion limit.When the user sets the stack limit of 1MB, he gets
1MB / 2KB = 500.When he needs 2000, all he needs to do is set the stack limit to
1 MB * 2000 / 500 = 4 MB. The main conflict will be resolved.gpshead commentedon Nov 21, 2023
getrlimit(RLIMIT_STACKonly returns the size of the main process thread, not the running thread. There is no posix API to get a given threads stack size. It can only be controlled at thread creation time and CPython does not control creation of all threads. It is common for C/C++ applications to launch threads with a different stack size than the overly large main thread default.gpshead commentedon Nov 21, 2023
Note that there is a Linux only GNU extension that works if you
#define _GNU_SOURCEbefore the#include <pthread.h>:macOS appears to have its own non-standard
pthread_get_stacksize_np()function. It's bit of a mess of non-standard APIs.(my best guess is that the
_npsuffix in both worlds means "non-posix")sunmy2019 commentedon Nov 21, 2023
Yeah, you are right. It's just too niche (too target-specific).
I also understand Python would prefer resumeable error to just aborting the program.
Then, how about providing the user an option to disable this C recursion limit check? Let the user manage themselves under this mode.
dimpase commentedon Nov 21, 2023
Isn't it possible to get C stack size (at thread creation time) directly via
pthread_attr_getstacksize(which is POSIX)instead of a GNU extension? These give you the minimal stack size. (Not sure what
minimalstands for here).One way or another, knowing C stack limit does not give one complete knowledge on the maximal C recursion depth, as different functions place different amount of data on stack.
Anyhow, for the issue at hand, the C version
_functools._lru_cache_wrapper(), behind all this, is an implementation of a Python wrapper, what it produces has to be compatible with the Python versionfunctools._lru_cache_wrapper(), recursion-depth-wise too.gpshead commentedon Nov 21, 2023
Nope. That is merely a function for accessing the opaque
pthread_attr_tstructure data.dimpase commentedon Nov 21, 2023
the code you posted in #112215 (comment) is basically a small wrapper around
pthread_attr_getstacksize, isn't it?Just copy it all if you must - there is nothing magical about a GNU extension there.
gpshead commentedon Nov 21, 2023
no it isn't.
pthread_getattr_np()fetches the thread information.dimpase commentedon Nov 21, 2023
OK, sorry, I misunderstood that. It seems that
muslhas similar functions implemented, so it doesn't look impossible to have this for all platforms. (I don't know anything about Windows, though).rhettinger commentedon Nov 25, 2023
Python 3.12 should not be left in its current state. The OP's example demonstrates a major regression.
GH-112215: Increase C recursion limit for non debug builds (GH-113397)
pythonGH-112215: Increase C recursion limit for non debug builds (pyt…
pythonGH-112215: Increase C recursion limit for non debug builds (pyt…
tim-one commentedon Dec 25, 2023
Starting about mid-week, I always see 14 test failures on Windows 10 (Pro), seemingly due to this. Current main branch, release build, Visual Studio 2022, nothing customized:
Sometimes there's an explicit message about Windows stack overflow. Historically, as I recall we always needed a lower recursion limit on Windows (than on Linux).
tim-one commentedon Dec 25, 2023
Note: 13 of the 14 tests named above still pass in a debug build. As I recall,
test_importlib(which still fails in a debug build) started flaking out before the other ones:gvanrossum commentedon Dec 25, 2023
Hm, maybe the CI uses debug build.
pythonGH-112215: Increase C recursion limit for non debug builds (pyt…
gvanrossum commentedon Jan 3, 2024
Marking this as release blocker since we discussed this off-line. The plan is to restore 3.12 to the 3.11 behavior (which is a little tricky since various tests were added/updated, but it's doable). For 3.13 we will do per-platform things (and per-config-setting, e.g. debug) by default and add an API to change the C recursion limit.
See also #113403 (comment) for some measurements of what we can actually support without crashing (on Mac, but apparently Linux is similar, and Windows is dramatically different).
gvanrossum commentedon Jan 8, 2024
I get this too. Windows 11, main branch. We also see it in the Azure Pipelines CI build, e.g.
https://dev.azure.com/Python/cpython/_build/results?buildId=147048&view=results
(requires login?)
pythonGH-112215: Increase C recursion limit for non debug builds (pyt…
Yhg1s commentedon Feb 6, 2024
What's the current state of this issue? I know some of the test failures were worked around, but do we need anything to address user concerns that hasn't already been done? Should this block today's 3.12 release?
encukou commentedon Feb 6, 2024
Do you need to delegate the reverts to a developer-in-residence?
markshannon commentedon Feb 6, 2024
#115083 ports the various C recursion limit changes from main, but it is unlikely to be ready until tomorrow as I need to get the limits and tests fixed for wasi.
I don't think it should block the release.
encukou commentedon Feb 8, 2024
@guido said:
Do I understand correctly that the plan for 3.12 changed to backporting various changes from main?
pythonGH-112215: Increase C recursion limit for non debug builds (pyt…
[3.12] GH-112215: Backport C recursion changes (GH-115083)
Yhg1s commentedon Feb 13, 2024
Is there anything left to do for this? A fix went in for the next 3.12 (scheduled in two months). Is there anything that still needs to be done for 3.13, before or after today's 3.13 alpha 4?
encukou commentedon Feb 13, 2024
Like Mark, I don't think this issue should block alpha 4.
markshannon commentedon Feb 14, 2024
We might change the implementation for 3.13, to use the actual stack depth (in bytes) rather than a count.
What we have now works well enough though, and better than what we had historically.
Add test_long_kernel
Add test_long_kernel
Add test_long_kernel
pythonGH-112215: Increase C recursion limit for non debug builds (pyt…
Eclips4 commentedon Nov 27, 2024
What's the plan for 3.13? It looks like it still doesn't behave as expectedI cannot reproduce error from #127346 on my macos, so it's probably a Windows issue?
gpshead commentedon Nov 27, 2024
beware that C stack size issues are typically local platform & build config dependent as to what numbers you need to use to bump into a given issue.
rhettinger commentedon Jan 12, 2025
Another user reported this as a bug: #127346
Also there is the question on StackOverflow: https://stackoverflow.com/questions/78389885/why-lru-cache-doesnt-work-on-desktops-but-only-works-in-the-online-python-int
For the particular case of
cache()andlru_cache(), we may need to provide an easy way to get to the pure python version that works as expected. Perhaps something likefrom functools import py_lru_cache as lru_cache. Without such an option, users would need to code their own caching decorator to be able to run dynamic programming like that found in the StackOverflow question. That is what the user had to do in #127346 . I'm pretty sure that is not what we want.markshannon commentedon Feb 12, 2025
I think the solution to this is to implement the wrapper function in Python, but implement the cache management in C.
That will fix the recursion issue, with little or no performance impact.
The wrapper function would look something like this:
markshannon commentedon May 6, 2025
This is fixed in 3.14.
encukou commentedon Nov 20, 2025
The fix is backported to 3.13 & 3.12.