| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
…is fixed Some iterator checks are added for _FOR_ITER, _FOR_ITER_TIER_TWO and INSTRUMENTED_FOR_ITER bytecode implementations. TypeError is raised in case of tp_iternext == NULL. Tests on generator modifying through gi_frame.f_locals are added, both to genexpr generators and function generators.
…e-125038.ffSLCz.rst Co-authored-by: Kirill Podoprigora <kirill.bast9@mail.ru>
| if (iternext == NULL) { | ||
| _PyErr_Format(tstate, PyExc_TypeError, | ||
| "'for' requires an object with " | ||
| "__iter__ method, got %.100s", |
There was a problem hiding this comment.
This error is incorrect as it's looking for __next__, not __iter__. I think we can use the same error as builtins.next():
PyErr_Format(PyExc_TypeError,
"'%.200s' object is not an iterator",
Py_TYPE(it)->tp_name);
Sorry, something went wrong.
|
I think this is wrong approach. Rather than modifying FOR_ITER, etc to handle an incorrect VM state, we should generate code that cannot get into that state, in the bytecode compiler. Currently, the bytecode compiler assumes that local variables in generator expressions cannot be modified in The bytecode for (x for x in ...) starts with: 0 RETURN_GENERATOR
2 POP_TOP
4 RESUME 0
6 LOAD_FAST 0 (.0)
>> 8 FOR_ITER 6 (to 24)
Compare that with the code for for x in ....: 0 RESUME 0
2 LOAD_FAST 0 (...)
4 GET_ITER
>> 6 FOR_ITER 4 (to 18)
10 STORE_FAST 1 (x)
Note the additional GET_ITER as the bytecode compiler doesn't know what ... is. I think adding the GET_ITER before the FOR_ITER would gain the robustness required in this case, without complicating the iteration code. |
Sorry, something went wrong.
|
So, basically, we want to provide equal results for such code fragments:
g = (x for x in range(10)) g.gi_frame.f_locals['.0'] = range(20)
g = (x for x in range(10)) g.gi_frame.f_locals['.0'] = iter(range(20)) Have I got your idea correct on high level? IMHO, this will change be convenient. |
Sorry, something went wrong.
Yes, I think those should provide the same results. |
Sorry, something went wrong.
|
We also want to avoid emitting GET_ITER twice. It is currently emitted in the scope surrounding the generator expression. In other words, this function: def f(s):
return (x for x in s)currently generates the following bytecode: 1 RESUME 0
2 LOAD_CONST 1 (<code object <genexpr> at 0x7fc1c815e6a0, file "<python-input-0>", line 2>)
MAKE_FUNCTION
LOAD_FAST_TEMP 0 (s)
GET_ITER
CALL 0
RETURN_VALUE
Disassembly of <code object <genexpr> at 0x7fc1c815e6a0, file "<python-input-0>", line 2>:
2 RETURN_GENERATOR
POP_TOP
L1: RESUME 0
LOAD_FAST 0 (.0)
L2: FOR_ITER 6 (to L3)
...
I think it should generate: 1 RESUME 0
2 LOAD_CONST 1 (<code object <genexpr> at 0x7fc1c815e6a0, file "<python-input-0>", line 2>)
MAKE_FUNCTION
LOAD_FAST_TEMP 0 (s)
CALL 0
RETURN_VALUE
Disassembly of <code object <genexpr> at 0x7fc1c815e6a0, file "<python-input-0>", line 2>:
2 RETURN_GENERATOR
POP_TOP
L1: RESUME 0
LOAD_FAST 0 (.0)
GET_ITER
L2: FOR_ITER 6 (to L3)
...
This is a behavior change, but I think a correct one. If f(2) is called, we get a TypeError, but this seems wrong. The error should occur once the generator expression is iterated over. |
Sorry, something went wrong.
Agree.
Could you please give some additional information about this behavior change? |
Sorry, something went wrong.
Currently, this happens: >>> (x for x in 1)
Traceback (most recent call last):
File "<python-input-0>", line 1, in <module>
(x for x in 1)
^
TypeError: 'int' object is not iterableWith Mark's proposed change, this error would be thrown only when the generator is iterated over. I think either behavior is fine and I agree with Mark that the proposed change makes for a more elegant implementation, but it is a change in core language behavior that I could see impacting some users (e.g., if you put a try-except around the construction of the genexp), so I wouldn't feel comfortable making that change in a bugfix release. @markshannon what do you think of applying this PR's approach in the 3.13 branch, and your idea on main only? |
Sorry, something went wrong.
Understood. Thx for the clarification.
It looks like a good idea for me, since crash through f_locals modification is already fixed. There will be no problem in backports in the case of two separate PRs? |
Sorry, something went wrong.
|
My proposed fix should have no performance impact (it does no extra work), so I'd definitely prefer that. If you think that is too intrusive for 3.13, we could leave the GET_ITER in the enclosing scope and add a new CHECK_ITER instruction to be inserted before the FOR_ITER. |
Sorry, something went wrong.
|
I like the idea of new CHECK_ITER bytecode. What do you think about this plan:
And what about Python 3.12 and below? |
Sorry, something went wrong.
|
That sounds like a good plan. If the fix is simple enough, then we can backport to 3.12. |
Sorry, something went wrong.
New tests are moved back to test_generators.py. Tests on generator creation via FunctionType from gi_code are added.
It seems like this solution will be best tradeoff. |
Sorry, something went wrong.
Sorry, something went wrong.
|
This might introduce a small slowdown in 3.13, but it isn't in the loop, so shouldn't be an issue. I'm not worried about that though, as we can remove the other GET_ITER and thus the overhead in 3.14. |
Sorry, something went wrong.
There was a problem hiding this comment.
Looks good me once you've agreed on a better name for ModifyTest.
Sorry, something went wrong.
|
It's okay for me to change name from "ModifyTest" to something. |
Sorry, something went wrong.
|
Either of those names works for me. |
Sorry, something went wrong.
There was a problem hiding this comment.
Thank you!
Sorry, something went wrong.
It seems we have agreed. Anything else, or it could be merged? |
Sorry, something went wrong.
|
Thanks @efimov-mikhail for the PR, and @JelleZijlstra for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13. |
Sorry, something went wrong.
|
Sorry, @efimov-mikhail and @JelleZijlstra, I could not cleanly backport this to 3.13 due to a conflict. cherry_picker 079875e39589eb0628b5883f7ffa387e7476ec06 3.13 |
Sorry, something went wrong.
…ipulations (pythonGH-125178) (cherry picked from commit 079875e) Co-authored-by: Mikhail Efimov <efimov.mikhail@gmail.com>
|
GH-125846 is a backport of this pull request to the 3.13 branch. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Some iterator checks are added for _FOR_ITER, _FOR_ITER_TIER_TWO and INSTRUMENTED_FOR_ITER bytecode implementations. TypeError is raised in case of tp_iternext == NULL. Tests on generator modifying through gi_frame.f_locals are added, both to genexpr generators and function generators.
I've added some test cases to emphasize current code behavior and save it explicitly in tests.
IMHO, there is still a room to improvement.
Previous PR becomes incorrect (#125051) after my typo in git commands.
I'm sorry about that.
@JelleZijlstra, Could you please review this again?
Moreover, label "needs backport to 3.13" should be added once again.