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

inspect.signature.BoundArguments "POSITIONAL_OR_KEYWORD" Arguments are always args #118577

Open
Steffenju95 opened this issue May 4, 2024 · 7 comments
Labels
docs Documentation in the Doc dir stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@Steffenju95
Copy link

Steffenju95 commented May 4, 2024

Feature Request

Feature description:

from inspect import signature
def foo(bar, /, baz, *, bat):
    ...

ba = signature(foo).bind('bla', baz='bli', bat='blub')

In this case the argument baz get returned with ba.args, which returns ('bla', 'bli')
I expected it in ba.kwargs, but that returns only {bat: 'blub'}.

Binding baz positional (bind('bla', 'bli', bat='blub')) works as expected.
Here ba.args returns ('bla', 'bli') again and ba.kwargs {bat: 'blub'}.

Is this behavior wanted like this?
In my perspective it makes more sense when the BoundArguments class returns POSITIONAL_OR_KEYWORD arguments as it got it (positional as args and keyword as kwargs) and not always as args. I think there happens a lost of Information .

Edit:
At least it not cause errors, because in constellations, where it is necessary to be a keyword it is. So it also could argued against the information lost, that it is a build in feature of simplification of unnecessary keywords.

See the following example:

from inspect import signature
def foo(bar, /, new='blub', baz='bla', *, bat='bli'):
    ...

ba = signature(foo).bind('bla', baz='bli', bat='blub')

ba.args => ('bla',)
ba.kwargs => {'baz': 'bli', 'bat': 'blub'}

CPython versions tested on:

3.12

Operating systems tested on:

Windows

@Steffenju95 Steffenju95 added the type-bug An unexpected behavior, bug, or error label May 4, 2024
@terryjreedy terryjreedy added the stdlib Python modules in the Lib dir label May 4, 2024
@terryjreedy
Copy link
Member

Running your copy-pasted code in current 3.11.9, 3.12.3, and 3.13.0a6 on Windows, print(ba.args, ba.kwargs) returns ('bla', 'bli') {'bat': 'blub'}. Note that args is a tuple, not a dict, as one should expect. There must have been a fix since the 3.12 you have.

@Steffenju95 Steffenju95 changed the title inspect.signature.BoundArguments "POSITIONAL_OR_KEYWORD" Arguments are missing as keyword inspect.signature.BoundArguments "POSITIONAL_OR_KEYWORD" Arguments are always args May 5, 2024
@Steffenju95
Copy link
Author

Steffenju95 commented May 5, 2024

Running your copy-pasted code in current 3.11.9, 3.12.3, and 3.13.0a6 on Windows, print(ba.args, ba.kwargs) returns ('bla', 'bli') {'bat': 'blub'}. Note that args is a tuple, not a dict, as one should expect. There must have been a fix since the 3.12 you have.

Thank you for you fast reply. You are absolutely right, It's behaves like you said. I should next time check more carefully and copy paste my tested code.

But one point stays. I still think baz='bli' should be returned as kwargs, because otherwise there is a lost of information. I regarding to that I corrected my initial post.

I'm just not sure if it's still a bug report or more a feature request. I guess it's depends on, if the actual behavior is intended as it is.

@terryjreedy terryjreedy added type-feature A feature request or enhancement and removed type-bug An unexpected behavior, bug, or error labels May 5, 2024
@hauntsaninja
Copy link
Contributor

See also #102551

@Steffenju95
Copy link
Author

Steffenju95 commented May 5, 2024

See also #102551

Okay, so it's definitely intended as it is. (I missed that in the PEP 362)

I agree with this comment and think it would be good to change the actual behavior:
#102551 (comment)

I also don't see so far an argument against it, beside the PEP defines it different and backward compatibility.

For the implementation there is not even a new variable needed as proposed before.
You could just pass the args and kwargs argument in addition to argument and let the property methods assess it instead of extracting it from attributes and signature. So nothing would change, beside two more arguments in __init__ and to private attributes _args and _kwargs that replaced the code to extract it from attributes and signature.

But if there are good arguments against it, I think it would be a good idea to address this unintuitive behaviour beside the one sentence in the PEP also in the code doc and here: https://docs.python.org/3/library/inspect.html#inspect.BoundArguments.args

Edit:
I realized in the meantime that beside the feature to reduce the args and kwargs to the minimum (See my original Post #118577 (comment)), you can change the arguments dict of BoundArguments and it will calculate you the correct args and kwargs for the new binding. So changing this behaviour as suggested could cause problems. So I root for a better documentation of the unintuitive behaviour.

@terryjreedy terryjreedy added the docs Documentation in the Doc dir label May 6, 2024
@terryjreedy
Copy link
Member

Please suggest specific doc changes in a comment.

@Steffenju95
Copy link
Author

Steffenju95 commented May 6, 2024

args
A tuple of positional arguments values. Dynamically computed by applying the arguments attribute to the corresponding signature. Includes ambiguous arguments (See Note).

kwargs
A dict of keyword arguments values. Dynamically computed by applying the arguments attribute to the corresponding signature. Excludes ambiguous arguments (See Note).

Note: The allocation in args and kwargs may not match the inserted args and kwargs in Signature.bind() or Signature.bind_partial(). This concerns argument of the kind POSITIONAL_OR_KEYWORD in cases where they can be passed ambiguous as args or kwargs. In cases of ambiguity the dynamically computation of args and kwargs always simplifies the given arguments as much as possible, by dropping keywords and saving them positional in args. For example:

def test(a=1, b=2, c=3):
    pass

sig = signature(test)
ba = sig.bind(a=10, c=13)

>>> ba.args
(10,)

>>> ba.kwargs:
{'c': 13}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation in the Doc dir stdlib Python modules in the Lib dir type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

4 participants
@hauntsaninja @terryjreedy @Steffenju95 and others