In the following, the question of which parameters
foo accepts is ignored, the point is only about the syntax of the call.
As far as I know,
foo(kw=1, "pos") was never allowed in Python. The call syntax always rejected positional arguments following keyword arguments, and it’s exactly what the error says in that case in 3.11 : “SyntaxError: positional argument follows keyword argument”.
The order in which positional unpacking (*args) and keyword unpacking (**kwargs) can be passed in a call basically respects that : even though you can have several of those in a single call since PEP 448, the limitations on what argument ordering is allowed rely on the logic of “positional first, keyword after”.
Except that there is one counter-example : for some reason, Python implementations before and after PEP 448 allowed positional unpackings after keyword arguments, as long as they were placed before keyword unpackings.
In other words,
foo(kw=1, "pos") and
foo(**d, *t) are forbidden, but
foo(kw=1, *t) is accepted.
It seems to be done on purpose, but no rationale is given about it in 448. The text and explanations legalize it quite clearly, and the pseudocode sort-of features it, but it’s not dwelled on much more than that (the PEP itself is not very long).
The “Disadvantages” section even says, quote, “The allowable orders for arguments in a function call are more complicated than before.” and goes on to give a quite complex explanation of the new rule, when “positionals come first and keywords later” would be much simpler.
As far as I can see, that syntax was made legal by PEP 448 and was untouched ever since.
EDIT : not true, it actually predated 448.
My question is, since it appears it was done on purpose, what is or was the rationale to allow this particular syntax ?