-
-
Notifications
You must be signed in to change notification settings - Fork 136
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
Infinite Recursion possible in 2.4 #220
Comments
@stuaxo, @schinckel, any idea about how to avoid that regression? |
I think I’ve had similar issues. I’ll try to have a look later tonight to see how I got around it. |
Okay, I've found the similar problem I came across. I think it's the "same" problem, just a different approach. I had code in My workaround was to get the raw data instead, and build up that form and get the cleaned data that way. We should think about either/both a way to handle this (getting cleaned data from other steps in condition_dict callable), and/or documenting the problem. |
Ah, looking at the code, it seems that whilst As an aside, I think for consistency, But that doesn't solve this problem (it just moves it to a different place). |
I wonder if it's possible to use a generator that retains knowledge of previous steps: it should be possible for condition_dict to refer to previous steps, but not subsequent steps. Let me see what I can get to work. |
This shows a possible approach we can use to fix the infinite recursion that can be triggered by referring to step data from condition_dict callables.
Maybe get_cleaned_data_step could be behind lru_cache with maxsize None. |
Issue with infinite recursion in version 2.4, see jazzband/django-formtools#220
We're also having the same issue |
Experiencing the same issue upon update from 2.3 > 2.4. Reverting back to 2.3 for now. Following for updates... |
The fix of #168 has caused this problem. My understanding is that you should be using the My opinion is that 533a830 should be reverted as it tries to allow the same problem to be solved in a new, previously-not-possible, way and breaks the original way of solving the same problem that's been part of this great package for years. Thanks for your help, we've also had to pin 2.3 due to this being a breaking change for our use of the package. |
I have a different opinion on this. By being able to override the get_form_list you have complete freedom on the list of forms you're going to display in your wizard. See here some example code from a project where a wizard is created dynamically based on some configuration (taken from the PR_ACTION list of dics):
So I can do something like
in my urls and create different wizards. I really don't think that something like that would possible using the condition_dict. |
Yeah, the original use case for my patch (and what I've been using as a subclass for many years) was where I needed to add steps dynamically, based upon the submitted data from an earlier step. Specifically, there was a form that needed to be re-used N times, with different initial data. |
So the fix I have (linked in this thread), it "works", but there's not a real way to detect that we have attempted to reference data from a subsequent step (and are inside an infinite recursion), until the Python infinite recursion protection kicks in by running out of stack: at that point we can't recover because, well, we are out of stack. If there was a neat way of detecting we are in a recursive call, then we could try that - but without sniffing the stack, I wasn't able to detect that. |
It probably makes sense to step back and list the places ways the data is referenced at different steps. |
This removes the undocumented `get_form_list` since the process of repeatedly and dynamically generating an OrderedDict of steps/forms pairs leads to infinite recursion. This is replaced with a new method `process_condition_dict` which directly modifies `form_list` in place, in a single step executed prior to dispatching to `get` or `post`. This eliminates infinite recursion since the `form_list` is calculated exactly once prior to any other attempts to access and process forms. Removed `test_form_condition_unstable` since this is an odd test (why would a condition_dict return value suddenly change in the middle of a request/response cycle?) that was attempting to deal with invalid steps (invalid steps are better handled through actual validation, see jazzband#224). For users who need to dynamically _add_ forms, this can be handled by overriding `process_condition_dict`.
We also have this issue that blocks us from upgrading. |
@schinckel if you want to check if we're in a recursive call you can have a counter starting at 0. If, on the way in the counter is not 0 then you are in a recursive call. |
* greenlightgo/fix-infinite-recursion-220: Fix infinite recursion error jazzband#220
Please revert 533a830, which introduced a major bug and was not a proper solution for the new feature request (to dynamically add forms that aren't in the initial |
@claudep, @felixxm and @hramezani (I believe you're the Jazzband leads on this project) would it be possible for someone to review this thread? There was a major breaking change in 2.4 and a solution needs to be found if possible ASAP. Thank you. |
Maybe we should revert the change, and work to find a solution that works later. I just don’t have the time to spend on it now. |
I'm confused about why people are advocating for this change to be reverted "ASAP". Is there a reason why affected people can't stick with 2.3 until a comprehensive solution for this issue is found? |
@martey, it should be reverted because 2.4 is a broken release that breaks real projects and costs people valuable time and money. It's not very reasonable to expect everyone to find out the hard way that 2.4 is broken, find this issue, and then downgrade to 2.3, when reverting could avoid all of that. To be clear, we don't need to come up with a comprehensive solution for the infinite recursion bug. The changeset that introduced the bug simply needs to be reverted. Once that is done, a comprehensive solution to the feature request (to be able to dynamically add forms that are not present in @schinckel, are you saying you don't have time for reverting the commit or just that you do not have time to work on a proper solution for the feature request? There is already #222 for reverting the change. In my opinion, 2.4 should be yanked from PyPI in favor of the next release. I could submit a PR to update the changelog. Let us know how we can help. Thanks! |
The latter - to come up with a proper solution. |
@jsma I don't think it's fair to assume that everyone uses django-formtools in the same way, or that everyone is affected by this regression. I think people using
This already happened (see #62, #168, #209). The "future release" was 2.4. Calling this a "new feature" or "feature request" is inaccurate, especially since people have been confused by the fact that
Removing 2.4 from PyPI and releasing a new version would prevent people using dynamic forms with |
I'll have a stare at this later + try and understand it (and invite everyone else to) - I (or someone else), should add a test to this for modifying get_form_list() as well so we have both cases. EDIT: Thanks for making an actual test; I'm pressed for free time, so it's really helpful to have a working example to start from. |
Gave feedback on the PR - since it involves a few different test improvements; it would be make it easier to merge if the fixes for different things are split into small standalone PRs. It'll make it easier for people here evaluate the tests that is just for this issue. |
I think test improvements are dearly needed in this project; my PR is meant as a general improvement of things not just this issue. Anyway, for the issue at hand you could pull just this commit: c6d9eda ... remove the |
I would like to use a What are my options right now? I've thought of:
Are there other options for being able to use this feature that I'm not thinking of? |
@neilmb the other option is to override
|
A reason could be that version 2.4.0 added official support for django v4 Maybe a version 2.3.1 could be released with the old behaviour and official django v4 support? |
And in an ideal world, SemVer would be used so people could understand version numbers and use modern package managers like Poetry properly. This being a breaking change could then be safely introduced using version 3.0.0 instead of 2.4.0, if it was wanted (not that I'm sure it should be tbh). Having come from other languages to Python in the last few years, the Python community seems quite out of sync with the rest on the SemVer front and SemVer is really damn handy when managing dependencies. |
OK, so I had a look back at the original PR and a bit of a think - There are two use cases, one is an API for callers outside the wizard to get a list of forms, and the case where inside get_form we need a list. If these have separate names we can avoid the issue. |
The original use case was to dynamically inject forms: specifically given the input from step A, run n instances of step B with different data, as “sub-steps”.
|
In that case is it enough to keep it and people use the solution @spapas proposes if people want to overide get_form ? |
Hi folks! We are also being bitten by this infinite recursion bug. There was an ask for examples of forms with this issue. I don't have the simplest example (as was the ask), but you can take a look at our usage case in the Outreachy website repository. I've documented steps to reproduce the issue in the Outreachy website issue tracker Outreachy is an internship program that supports diversity in open source. Outreachy uses django-formtools to have potential interns fill out an application. We receive over 3,000 applications per internship cohort, so we really appreciate this Django package! ❤️ We'll pin our django-formtools version to 2.3 until this issue can be fixed. I'll also take a look at the other solutions posted here. I don't know if I'll have time to make them work before our applications open on January 16, 2023, but I'll try. 😅 |
I can't currently update to Django 4.0 because django-formtools 2.4 includes a bug that breaks our usage case of conditional form steps on the Outreachy initial application: #537 jazzband/django-formtools#220 I also ran into an odd issue with the home/test_eligibility.py unit test. The work-around is documented in the code, although Jamey Sharp and I still have no clue as to what the root cause is. All the other unit tests use the home/scenarios.py code, and none of them seem to have issues. The unit tests are now more verbose, showing the permission errors that are uncaught, and 404 not found messages. In those cases, we're actually trying to test that errors are thrown when pages aren't accessed properly. I could add more code to catch those permission errors, but I'm too tired after wrestling with updates. That's future work. At least we're running a Django version that is receiving security updates now! Django 3.2 is marked as being a long-term support release, until early 2024: https://www.djangoproject.com/download/ However, it will be really important to upgrade to 4.2 in mid-2023, so that we can be on the next LTS release before 3.2 is deprecated in early 2023.
I can't currently update to Django 4.0 because django-formtools 2.4 includes a bug that breaks our usage case of conditional form steps on the Outreachy initial application: #537 jazzband/django-formtools#220 I also ran into an odd issue with the home/test_eligibility.py unit test. The work-around is documented in the code, although Jamey Sharp and I still have no clue as to what the root cause is. All the other unit tests use the home/scenarios.py code, and none of them seem to have issues. The unit tests are now more verbose, showing the permission errors that are uncaught, and 404 not found messages. In those cases, we're actually trying to test that errors are thrown when pages aren't accessed properly. I could add more code to catch those permission errors, but I'm too tired after wrestling with updates. That's future work. At least we're running a Django version that is receiving security updates now! Django 3.2 is marked as being a long-term support release, until early 2024: https://www.djangoproject.com/download/ However, it will be really important to upgrade to 4.2 in mid-2023, so that we can be on the next LTS release before 3.2 is deprecated in early 2024.
@schinckel, any opinion about the possible fix in #239 ? |
Yeah, that looks okay to me. |
I've just submitted a PR that attempts a more complete fix for this problem. Please have a look and give it a test if you've got time. |
django-formtools committed a fix in version 2.4.1 for the regression causing our initial application form to not work. Original bug: jazzband/django-formtools#220 Fix merged into 2.4.1: jazzband/django-formtools#239
django-formtools committed a fix in version 2.4.1 for the regression causing our initial application form to not work. Original bug: jazzband/django-formtools#220 Fix merged into 2.4.1: jazzband/django-formtools#239
We have a
condition_dict
containing the following function:The call to
WizardView.get_cleaned_data_for_step
then callsWizardView.get_form
, which used to referenceself.form_list
, but the following commit changes this toself.get_form_list()
, which then calls the user-suppliedcondition_dict
functions, which then calls ourneed_further_steps
function above, which callsWizardView.get_cleaned_data_for_step
, and so on ad infinitum.533a830
Perhaps our custom condition function is bad, but it doesn't seem too unreasonable?
The text was updated successfully, but these errors were encountered: