Syntax error for naked trailing comma for 1-tuple

That’s a straw man as far as I can tell? Your example is about a purely stylistic choice, while what I’m talking about is a foot gun.

The empty tuple is the special case. Every other tuple is consistent:

>>> one = 1,
>>> two = 1,2,
>>> three = 1,2,3,
>>> four = 1,2,3,4,
>>> five = 1,2,3,4,5,

The only thing special about the single-element tuple is that the last comma is mandatory (since it’s the ONLY comma). Machine-generated code - and a lot of human-generated code - can simply have the trailing comma on everything, and then special-case “oh hey, I didn’t have any elements, stick a couple of parens in there”.

That can happen with MANY bugs. Part of the experience you gain as a programmer is learning how to read an error message and diagnose a problem. While I’m sympathetic in theory to this argument, it is not strong enough to break valid code.

No it’s really not. It’s perfectly reasonable. Simply means the author wants a tuple with one element. Nothing unusual to want. In contrast, please explain why a book author would want to end a book chapter with a comma.

Btw both the Python tutorial and the Python wiki teach to build 1-tuples with just the comma, without parentheses. (The wiki afterwards adds that you can optionally add parentheses, the tutorial doesn’t mention that at all).

Emitting unparenthesised tuples is more work for the code generator - parenthesised tuples are universally unambiguous. It’s not just an unparenthesised 1-tuple that might trip people up:

lambda: True, False

Could you clarify what point you are trying to make with your example?

Because if I don’t know if this expression evaluates to a 2-tuple or it evaluates to a lambda:

lambda: True, False

Then adding parenthesis around this doesn’t help me (it is going to evaluate to whatever the non-parenthesis version evaluated to):

(lambda: True, False)

Well, if you were to move the opening bracket slightly to the right…

lambda: (True, False)

But that isn’t equivalent to the expression you listed before, your new expressions evaluates to a lambda where as your original expression evaluates to a 2-tuple. If a developer was looking to write a 2-tuple adding parenthesis or not doesn’t help make the expression easier to understand.

So I’m still missing clarity on the point you are making with your example, are you saying that if a developer was looking to implement the second expression but accidentally implemented the first expression because even in a (>1)-tuple case it can be non-obvious?

Yes, unparenthesised tuples are context-sensitive, and what may seem obvious to one programmer might not be at all apparent to another programmer (that the lambda expression has lower precedence).

Right, but don’t paranthesised tuples have the same problem? E.g.

(lambda: (True, False),)

Does this expression evaluate to:

  • A lambda that returns 1-tuple whose element is a 2-tuple of True and False
  • Or: A 1-tuple whose element is a lambda that returns a 2-tuple of True and False

?

Wouldn’t this expression equally trip someone up who would have been tripped up by the unparenthesised examples?

1 Like

I don’t think so. The ambiguity arises only if you consider the return value to be an unparenthesised tuple. But if you were to begin with lambda: True, False and were forced to wrap the return value in parentheses, then you would have hopefully realised that the lambda has lower precedence, the comma cannot belong to the return value, and that lambdas would be inexpressible in tuples, save for wrapping the entire lambda expression in parentheses.

1 Like

On the contrary; if I see that with parentheses, my first assumption is that the author expected the parentheses to be necessary. Be very very careful of code that makes people think you don’t know how the language works.

You can put the parenthesis like (lambda True), False too, and that’s something very different. Which kinda reinforces the point of naked tuples being hard to read and error prone. But that’s a totally different conversation I think :stuck_out_tongue:

I disagree. Just shows that it depends on the case. Your (lambda: True), False is totally clear, doesn’t need extra parentheses around. The lambda: True, False is … well, I don’t even know how that gets parsed, and I don’t intend to memorize it. I would always use parentheses there.

I read that as “I disagree. I agree”. I’m confused now. Anyway, that’s getting off the rails…

You said “hard to read”, without restrictions, i.e., generally. I’m saying it depends. Some cases are hard to read, others are not. That example doesn’t show that all cases are hard to read, only that some are.

By the way, Discourse doesn’t recognise “py” as a known language for code blocks. You have to spell it “python”.

It is not our place to force the writers of code generators to write it one way or another way. Maybe they should always emit tuples grouped in parentheses. But they aren’t required to, and if you have a code generator that emits non-empty tuples without parens, that is perfectly fine under many circumstances, and it is not up to us to sy they must not.

If we change the rules, we don’t just break code using such 1-tuples, we also break the code generators that emits such code.

Breaking backwards compatibility for syntax that works all the way back to Python 1.5 and beyond such require a correspondingly large benefit, not merely “some people occasionally typo a comma and get a tuple they weren’t expecting”.

It might be annoying to have to debug, but in general this is no worse to debug than any other typo – and probably less so than many.

 x = 2,5096282746310002  # oops, should be dot not comma
 y = 2.5096382746310002  # oops, should be 9628 not 9638

Debugging the x bug is likely to be trivially easy compared to debugging the y bug.

I expect that this discussion about deprecating bare 1-tuples has probably consumed more intellectual energy in a just a few days than most Python coders will consume debugging accidental bare 1-tuples in their professional lifetime.

I suspect there is probably a long-tail here. Most coders don’t randomly insert trailing commas at the end of assignments, so they feel that this entire discussion is a storm in a teacup. Consider the real-world example:

url = invoice.url[len(prefix):],

How can you typo the comma? It’s not near the closing square bracket on my keyboard, or the Enter key. To me, that looks as likely a typo as

url = invoice.url[len(prefix):]i

so the idea that many people do this regularly seems unlikely.

2 Likes

I had this happen to me once when I tried to refactor a project which used a pre-3.10 dictionary way of using switch-case in Python to a more simplified multi-line instruction. Had to scratch my head with PyCharm’s debugger for a couple of minutes before I ran Black on the code base, which highlighted that I accidentally declared a 1-tuple because I forgot to erase a comma when I got rid of the dictionary. Now, this is all n=1, and I am not a Python guru by any means, but if this has happened to me once then I imagine that this would happen even more frequently to less skilled Python coders - write code that uses a comma inside some multi-line data structure, refactor it for code quality reasons, miss out on a small little comma by sheer chance.

1 Like

I’m not saying it can’t happen, I’m saying that it seems unlikely to happen regularly enough to conclude that bare 1-tuples are a bug magnet that needs the heavy hand of deprecation followed by SyntaxError to fix it.

I was replying to Maxime, who was referring to stylistic issues, for example:

“I know it’s a tuple, yet, I cannot help but feel that this is unfinished matter. It is a comma at the end of a book chapter”

and describes it as “asking more questions than giving answers”.

Those objections are figurative descriptions of Maxime’s distaste for bare 1-tuples. As such, commenting on style is relevent and certainly not a strawman.

(Aside: I don’t know whether it ever ends a chapter with a comma, but arguably the greatest novel in English literature – or at least the most pretentious and difficult to read – “Finnegans Wake”, begins and ends in the middle of a sentence. The same sentence.)

Describing bare tuples as “a foot gun” is, I think, a grossly exaggerated position to take. We’ve established that bare 1-tuples are fine when you intend for them to be 1-tuples. Of course people can say that they don’t like the look of bare 1-tuples, and prefer them when they are parenthesised – but that’s just a matter of taste and style, like indent width and whether you bracket expressions for clarity even when the syntax doesn’t require brackets:

if (arg is not None) and (arg < 1): ...

What we haven’t established is that this is a problem that occurs so often and is so hard to debug that we need the heavy hammer of prohibiting bare 1-tuples to solve it. You say:

You are using catastrophizing language here, describing a regular exception as a “crash”, which is normally used by computer-savvy coders to describe a serious event where the interpreter fails and dumps core, has a segmentation fault, or the equivalent. We don’t normally describe an exception as a crash. An exception is the interpreter running as it is designed to do.

In any case, any exception can be separated from its cause. That alone is not an argument either for or against the proposed change.

As for the obscure error message, here is your real world example:

url = invoice.url[len(prefix):],  # Oops a tuple.

Any bug, no matter how obvious in hindsight, could be a nightmare to debug under the right circumstances. But this error looks like it will usually give a straight-forward and obvious type error (TypeError or AttributeError) most of the time.

For example, if we go on to treat the tuple as if it were a URL, and do this:

with urllib.request.urlopen(url) as f:
    data = f.read()

the exception we get from urlopen() is not very obscure:

AttributeError: 'tuple' object has no attribute 'timeout'

It even tells you that you have a tuple! As a Django expert, I expect that you are much more familiar with urlopen than I am, and knew without having to look it up that it can accept either strings or Request objects but not tuples.

So no, I don’t accept your claim that this error is usually especially hard to debug.

And of course if you are using static typing, the error will be picked up before you even run the code.

1 Like