PEP 736: Shorthand syntax for keyword arguments at invocation

I already think black changes too much. I would be against black changing one to the other.

At the very least it shouldn’t do it until all Python versions that don’t have this feature are end of life.

5 Likes

Black supports specifying supported Python versions. If we apply this change, we’d do it only in projects that declare they only support Python 3.13 and higher.

Black could wait for refactoring tools to update themselves, though I would not want to block this forever on changes to third-party tools. In practice this might be fine since we’ll only ever apply the new syntax in Python 3.13-only code, and it will take some time for that to become common.

1 Like

I expect to see this behavior with the shorthand syntax:

# module.py
def example_function(name):
    print(name)

def main():
    example_function(name=)
# main.py
import module
module.name = "Alice"
module.main()

Why shouldn’t a newbie use it?

I don’t know if it really matters what linters do in this case. It’s a language feature first and foremost. We should just consider if the benefits of adding it outweigh the cons.

Linters will evolve, as they always have. If at some point this is such a widely accepted practice, I’m sure some might choose to do the auto-conversion. However, that’s clearly not a given - just consider := for a moment.

IMO the PEP should clearly state the benefits, e.g. being able to quickly see changed variables in a long list of arguments. It could just make sense to maybe state the obvious:

As with any new feature, it's up to the developer to decide
if it's the right place to use it / worth it.
This does **NOT** in itself replace existing practices.

–
Side note reg. black: Personally I’d be surprised if it were to start removing the variables form call expressions automatically. It should just be able to parse / format the new syntax. The code change is more something I would expect (optionally) from ruff or pyupgrade. But in the end that’s up to the devs / users to decide that.

5 Likes

Perhaps too much of a tangent, but doesn’t black aim to preserve AST?

I would want the AST to be different between these two usages (probably in the way that @ncoghlan suggested) – not because it would impact black but because it makes writing AST-based linters for this possible.

Does that mean that black would be disallowed from rewriting this?


I’m mostly curious to hear from a black maintainer if I’m wrong about it wanting to be AST-preserving. And if that is an explicit goal (it at least used to be, right?), it may impact the way that PEPs are written, since we’ll have some common expectation that specifying AST details has implications for black.

1 Like

Yes, this is a concern. (Didn’t want to get into this above.) Black does already change the AST in a few rare cases (example: del (a, b) and del a, b have a different AST, but Black will change from one to the other in some circumstances; see The Black code style - Black 23.12.1 documentation for more). I’d say our aim isn’t to preserve the AST as such, but to be a safe formatter. AST equivalence is a good tool to enforce safety, but we can deviate from it where it makes sense. If PEP 736 is accepted and the syntactic difference is reflected in the AST, I’d be comfortable relaxing the AST check in this context, since we’d know the code is semantically equivalent.

That said, whether or not to enforce PEP 736 style in Black is something we’d have to discuss within the Black project (if the PEP is accepted). Maybe @cdce8p is right and we should leave the change to Ruff and pyupgrade instead.

You didn’t show any behavior.

I think it is the behaviour of users that is referred to, in writing such stuff, and we’re invited to think the PEP encourages it.

The PEP 736 behaviour of the code is surely to print Alice.

The point seems worth consideration as an argument in favour of the implied variable having to be local.

@elis.byberi As foot-guns go, for me this one has relatively small calibre. You have to load it in one place (write the module.py like that) and fire it in another (in main.py). At first glance I thought it might catch on as a way to supply defaults to a library, but it can’t work that way unless the library writer makes calls that won’t work any other way. module.name = "Alice" is questionable PEP 736 or no.

1 Like

On the topic of whether formatters should automatically apply this feature universally, the first draft of the PEP includes this quote which hasn’t proven controversial as far as I can tell:

We do not recommend enforcing a rule to use the feature in all cases where it may be applicable.

Following to my suggestion in the description in this thread I’ve expanded this a little in the latest draft to:

We do not recommend enforcing a rule to use the feature in all cases where it may be applicable.

As described above, we propose that a reasonable rule of thumb would be to use this in cases where a parameter and its argument have the same semantics in order to reduce unintentional desynchronisation without causing inappropriate coupling.

As several have pointed out, I think that this is a language feature and what formatters do is not for me, or this PEP, to decide. Arguably, the PEP should leave this entirely open and ‘Recommendations’ could be deleted.

3 Likes

That’s an interesting case I hadn’t considered. That said, I don’t think it’s intrinsically any more confusing with this syntax than if it were just:

# module.py
def example_function(name):
    print(name)

def main():
    example_function(name=name)  # using current syntax

Any reason PEP 736 in particular would make this trickier?

2 Likes

The code example shown would be universally seen as horribly bad before and after the proposed syntax change I would think. Linters and static type checkers would assume it’s incorrect, and so would a human reviewer. The syntax isn’t the problem.

1 Like

I don’t think the PEP should try to make recommendations to third party linters/formatters. That is for them and their communities to decide (and why I prefer configurable formatters/linters so I can have some say about their behavior).

But I think the PEP authors, who have thought about this a lot, can make recommendations to python language users about when this feature is valuable and should be considered versus when this feature could make code worse (introduce unnecessary coupling).

I think the language in that quoted segment is very good.

1 Like

It will not make it trickier but, instead, easier.

Old users are going to use the new syntax the same as keyword arguments, but we can’t prevent new users from ‘misusing’ it if it makes coding easier.

The question is, does it make the code easier to maintain? Maybe we are trading code maintainability and code readability for ease of code writing.

PEP does not encourage it, but I think it would be a decent precedent for ‘magic’. It seems too dynamic to me.

That would make it scope-aware, making it no longer a shorthand syntax but a new syntax.

It would be very useful for setting configuration variables. For example, you can read the configuration file and set the configuration variables in any module you want using just a function. You don’t need a config variable. If any config variable is erroneously not set, it will raise a NameError, and it seems pretty good to me, rather than having an invalid value, e.g., None, used instead.

That [referencing a non-existing variable] is considered bad code today, but I don’t see any reason to call it such with the new syntax. With the new syntax, the argument is literally missing. It would be technically correct to not define the variable in the code because it will be defined at runtime.

Just to bring it to mind, we usually define whole classes at runtime; that is not anything new in Python. Think of pickle . There are systems that never go down but still need to be updated.


What PEP is trying to normalize is implicit referencing or using non-existing variables. There is no variable being passed as an argument. Regardless of the syntax naming or the PEP background, that is what is really happening. That pretty much seems like defining a Person() object with just person = . I think that will be the next PEP.

This forum is no place for conspiracy theories.

2 Likes

These are just my opinions on possible aftereffects.

I don’t believe this kind of comment moves the conversation forward. If you think someone is being hyperbolic, please call it out, but do so in a way which deescalates.


I have mixed feelings about how the PEP should talk about linters/fixers. Basically, I have several disparate but vaguely related thoughts:

  • In theory, I don’t think PEPs should need or want to talk about the appropriate choices for developer tools to make. That should generally be left up to the tools.

  • My only concerns about this PEP are about those exact choices.

  • The authors of a PEP introducing new syntax probably have a good feeling for where the syntax is most helpful. That seems useful for linters/fixers.

I don’t know if that’s helpful or not. This one feels different from other syntactic changes. I don’t think anyone considered it likely or possible that tools would start to consider the following code “wrong” with the introduction of assignment expressions:

m = re.match(...).group(1)
if m:
    ...

But that was also a different era (I know, it’s recent!) – before black and ruff existed or had so much mindshare. yapf usage was fairly niche in my experience.

4 Likes

I agree with these positions. The current status of the pep is that it makes a recommendation for when we think this syntax will/won’t be helpful but without stating what formatters should do. This seems like it should address all of the above but do you think it’s lacking in some way?

1 Like

I remain concerned about this comment above.

I would like to see a statement in the PEP that explicitly recommends that formatters do not automatically change the current style to the new form. To me that’s a change in how the code author expresses their intent, not just a reformatting.

I don’t know why this particular PEP has resulted in this line of thinking - we’ve had PEPs that say “this new construct is equivalent to this old one” before without getting this sort of reaction - but I’d like the PEP to make it explicit that the “equivalence” is about behaviour, and not about expression of intent.

I agree in general that PEPs shouldn’t need to discuss what tools choose to do with the proposal, but IMO this is a special case because of the direction the discussion has gone in, and it should be addressed.

6 Likes

If the PEP recommend what third party tools should do then, if, in the future, it is discovered most or many people (at least in some community) like this feature and would prefer to have transformation made by default then a tool implementing that would have to be in opposition to the PEP.

I wish I understood this sentence better. Could you please clarify? I think I understand “the equivalence is about behavior” but I don’t understand what you mean be expression of intent.

Can you be more specific about “the direction the discussion has gone in” that makes this a special case?

I think it’s clear that this feature would be helpful in some cases and there are some cases where it could be detrimental. Whether a tool wants to make transformations based on this PEP should be up to the tool. If you have strong opinions against ever using this feature why isn’t it sufficient to bring those opinions up with the tool?

And to be clear, we’re talking about the formatter making a transformation like

def foo(bar):
    print(bar)

bar = 42
foo(bar=bar)

# >>>> Transformed to

def foo(bar):
    print(bar)

bar = 42
foo(bar=)

NOT

def foo(bar):
    print(bar)

baz = 42
foo(bar=baz)

# >>>> Transformed to

def foo(bar):
    print(bar)

bar = 42
foo(bar=)

I’m out of arguments here. I’m disappointed that I’m going to end up in opposition to the PEP over a matter of presentation, rather than because of any technical issues, but I don’t seem able to make my point in a way that persuades you.

Precisely. And yet, a lot of the discussion here from supporters of the proposal seems to suggest that switching to use the new form is always the right thing to do. The idea that black might auto-format code to the new form is simply the purest form of that suggestion.

I’m not trying to get the PEP to make demands on black. What I am trying to do, is to ask that the PEP be clearer that the choice of which form to use should be based on the programmer taking a view on which form expresses their intent best. Both forms are valid, and in any given situation, both will do the same thing. But as you said yourself, which is the right choice in any given situation is a matter of judgement.

Yes, exactly. My point being, if we accept your comment that “this feature would be helpful in some cases and there are some cases where it could be detrimental”, then how can a formatter validly decide that this transformation is acceptable? The fact that black is considering doing this, suggests that whether it’s helpful is nowhere near as clear (from the PEP) as you seem to think.

Because they are my opinions. I understand that some people like the new feature, and some (like me) do not. That’s normal, and normally I’d simply not use the feature[1]. But the discussion here, and the comment from black, suggests that social pressure will make this a lot harder than it should be for what is frankly a fairly minor feature. It feels a lot like the “typing is optional” idea - and anyone who’s tried to resist adding typing can attest that it’s a lot less optional than people claim :slightly_frowning_face:

Anyway, I have nothing more to add. I repeat my request - please try to emphasise in the PEP that the forms f(x=x) and f(x=) are different but equivalent forms, and it should be the programmer’s choice which is appropriate in any given situation[2]. I’ll leave it to you to decide how to respond.


  1. and maybe moan that “Python isn’t as simple a language as it used to be” :wink: ↩︎

  2. and yes, that means automated tools shouldn’t take that choice away from the programmer ↩︎

3 Likes