My understanding is that the responder is saying that "That test does not mean what you are intending it to mean, it means something else that we've documented. We won't make it mean what you would like it to take make your incorrect code work, at the expense of breaking code that is using it in the documented way".
I started on the side of the original person but was persuaded round by the responder.
"if x:" means something completely different than "if x is not None", adn that needs to be understood. The fact that they might evaluate to the same result a lot of the time is luck, and is how people have got into this mess. Changing the boolean evaluation in this case is actually making the mess even worse.
One of the most basic pages of python documentation [1] says this:
Any object can be tested for truth value, for use in an if or while
condition or as operand of the Boolean operations below.
The following values are considered false:
None
False
zero of any numeric type, for example, 0, 0L, 0.0, 0j.
any empty sequence, for example, '', (), [].
any empty mapping, for example, {}.
instances of user-defined classes, if the class defines a __nonzero__()
or __len__() method, when that method returns the integer zero or bool value False. [1]
All other values are considered true — so objects of many types are always true.
To me, that makes it clear that no valid time could be False. It isn't on that list, so it should be True. Is there any controversy about that?
in Boolean contexts, a time object is considered to be true if and only if, after converting it to minutes and subtracting utcoffset() (or 0 if that’s None), the result is non-zero.
But the only reason I know that is because I read a substantial part of the exchange where Mr. Paul Moore quotes it.
My initial reaction was to think "How the heck would you deduce midnight from this?" and I think arguing that the behavior is fully documented from this line alone is a bit of a stretch. It's true that it is documented, but not in a manner that is immediately obvious. Worse, there are 87 instances of "None" on that page, which makes searching for a specific issue somewhat daunting.
The irony (in terms of an unexpected side effect) is that by having the discussion they did, even if nothing comes of it in terms of fixes or changes, future developers bit by this behavior will be able to readily find it via a search for midnight, None values from time objects, etc.
For the purposes of that document, datetime is not a builtin type but a user-defined class (since it's written in a Python package and not the interpreter directly). It has a __nonzero__ method defined.
You're correct, it falls under that last rule of user-defined class. They certainly have a right to define it that way, in some sense.
But it just goes entirely against the spirit of that documentation to do so. I would be fairly shocked if there are many other examples of exceptions to this rule in the standard library. That's just not how it is supposed to work. I'm primarily a python developer, and I have that list of False things very deeply internalized. They are False, other things are True. I'm sure most others devs have as well.
It is right at the top of the page on the documentation of the standard types.
I'm sure there could other classes where the truthiness had some obvious physical meaning, and objects could be either True or False in a meaninful way. But midnight is not one of those.
The issue I take with this behavior, or at least the justification for closing discussion of whether or not it's applicable (though I do agree it's not a "bug" per se, simply on the merit that it's documented--even if the documentation is unclear), is this notion that it would break code where this behavior is relied upon. First, you're assuming that someone understands the behavior clearly enough to exploit it (while there's obviously a non-zero population who aren't aware enough to avoid it). Second, you're assuming that they're not inclined to realize what an outrageously stupid idea it is to rely on midnight == False. If someone who's attempting to use truthiness as a means of determining if a time object (say, from a database) is None or falsey isn't aware of this behavior enough to not get bitten by it, what makes us think that the percentage of people who would be exploiting this behavior is somewhat higher? It's insanity. More so when each of the examples presented in the discussion for how it might be used are outrageous edge cases.
Now, would it be possible to workaround this by using datetimes, which as far as I can tell cannot be made falsey, then extract the time component later when needed after validating that the datetime is indeed not None? I can't think of any real world circumstances where you're not going to need to be aware of the date, timezone, and therefore DST when extracting times except for profiling or one-off quicky applications that don't need TZ awareness.
> Changing the boolean evaluation in this case is actually making the mess even worse.
I don't see how that's possible. Assuming that using "if x:" is wrong (for some value of wrong) here, this issue only affects people who are using it wrong anyway. So all fixing it does is make the consequences of doing bad stuff less painful. I can't see how that would hurt things. Even if you think "if x:" should never be used on non-booleans, leaving land mines in there to hurt newbie (or lazy) developers is not a good way to enforce that convention. It just creates pain.
Or more simply: whether having boolean coercion for conditionals is a good idea is orthogonal to how such a coercion should work. It sounds like Python should deprecate truthiness altogether. But it hasn't, so in the meantime truthiness should work in a reasonable way, and falsy midnights are not reasonable.
Pointing out that it's documented is unhelpful (the documentation is just the wrongness restated in a different language), as is "That test does not mean what you are intending it to mean" (Tautological. The OP was suggesting changing what the test means).
This is a nice way of putting things. There's a school of thought that says if things work how it says somewhere that they should work, then no part of the system is exhibiting a problem. But just in this thread we can see Python's insane treatment of default function parameters called a "bug in the language". I've long been mad about what I can only think of as a bug in the Java spec: bit shifts use the base-32 modulus of their actual operand. Sure, the specification says that that's what they're supposed to do, but when I ask the JVM to fill a 32-bit value with 40 zeroes from the right, I expect one of the only two sane results -- an exception or whatever value 32 zero bits represents. I'm pretty sure there is no circumstance where shifting in 8 zeroes could be correct or useful.
I understand the distinction between the two ways of writing the conditional. The point is that the documented way is surprising, and as far as I can tell everyone agrees that it is wrong. It's really not a matter of making the original poster's code work, it's a matter of making the language constructs have the expected behavior. Everyone seems to agree that code that used this behavior in the documented way would be bad code. That being the case, I see no way in which changing the behavior to the expected behavior would make anything worse.
I started on the side of the original person but was persuaded round by the responder.
"if x:" means something completely different than "if x is not None", adn that needs to be understood. The fact that they might evaluate to the same result a lot of the time is luck, and is how people have got into this mess. Changing the boolean evaluation in this case is actually making the mess even worse.