Please reconsider the Boolean evaluation of midnight
Hi all, This is my first post here, following a recommendation from Alexander Belopolsky to use this list, to try to convince the Python developers to reopen a ticket. I am a long-time Python user, and a Django committer. http://bugs.python.org/issue13936 is a complaint about the fact that midnight -- datetime.time(0,0,0) -- is evaluated as False in Boolean contexts. It was closed as invalid, under the claim that """It is odd, but really no odder than "zero values" of other types evaluating to false in Boolean contexts""". I would like to ask for this to be reconsidered; since the ticket was closed, two main arguments were given for this: 1) The practical argument (paraphrasing Danilo Bergen and Andreas Pelme): The current semantics is surprising and useless; users do not expect valid times to be falsey, and do not normally want code to have special behavior on midnight. Users who ask for Boolean evaluation of a variable that's supposed to hold a time value, usually write "if var" as shorthand for "if var is not None". 2) The principled argument (which I think is at the root of the practical argument); quoting myself from the ticket: """Midnight is not a "zero value", it is just a value. It does not have any special qualities analogous to those of 0, "", or the empty set. ... Midnight evaluting to false makes as much sense as date(1,1,1) -- the minimal valid date value -- evaluating to false""". Thanks, Shai.
On 5 March 2014 10:16, Shai Berger <shai@platonix.com> wrote:
http://bugs.python.org/issue13936 is a complaint about the fact that midnight -- datetime.time(0,0,0) -- is evaluated as False in Boolean contexts. It was closed as invalid, under the claim that """It is odd, but really no odder than "zero values" of other types evaluating to false in Boolean contexts""".
I would like to ask for this to be reconsidered; since the ticket was closed, two main arguments were given for this:
Why on earth would anyone check the boolean value of a time??? Before looking at whether midnight should be false or true, I'd like to be clear on why any real life code would care one way or the other. It seems to me that the correct answer would be to change the code to simply not rely on the boolean value of a time. Paul
On 2014-03-05, at 11:23 , Paul Moore <p.f.moore@gmail.com> wrote:
On 5 March 2014 10:16, Shai Berger <shai@platonix.com> wrote:
http://bugs.python.org/issue13936 is a complaint about the fact that midnight -- datetime.time(0,0,0) -- is evaluated as False in Boolean contexts. It was closed as invalid, under the claim that """It is odd, but really no odder than "zero values" of other types evaluating to false in Boolean contexts""".
I would like to ask for this to be reconsidered; since the ticket was closed, two main arguments were given for this:
Why on earth would anyone check the boolean value of a time???
Side-effect of e.g. an object with an optional (nullable) time field, the developer didn't realise a time could be false-ish (because seriously?) and then it turns out code along the lines of if event.start_time: # stuff unexpectedly fails if start_time is midnight. I've actually done something close recently, didn't expect more_itertool's peekable iterator could be false-ish[0] because I'd expect empty sequences to be falsy but not empty iterators. It turned out to be useful and to simplify my code, but I can see developers not considering such edge cases unless there's a clear warning (as in ElementTree which IIRC clearly warns that an element with no children is false-ish) especially for objects for which false-ish-ness makes so little sense as a time. [0] even though the behavior is documented.
On 5 March 2014 10:31, Masklinn <masklinn@masklinn.net> wrote:
Side-effect of e.g. an object with an optional (nullable) time field, the developer didn't realise a time could be false-ish (because seriously?) and then it turns out code along the lines of
if event.start_time: # stuff
unexpectedly fails if start_time is midnight.
Yeah, I was classing that as "application bug" and it's easy to fix with an "is not None". Agreed that the odd behaviour of time in a boolean context is why this whole class of bugs exists, but it's only a subclass if the wider problem that people shouldn't truth-test values without thinking - an explicit test is always better (explicit is better than implicit and all that). I actually find the idea of truth-testing a value that's expected to have a type that is always true *more* unintuitive than an explicit "is not None", so I'd fix the above code regardless of what the truth value of midnight is. Paul
On Wednesday 05 March 2014 12:44:07 Paul Moore wrote:
I actually find the idea of truth-testing a value that's expected to have a type that is always true *more* unintuitive than an explicit "is not None", so I'd fix the above code regardless of what the truth value of midnight is.
I can appreciate this point of view, but I think it is not the position generally taken by Python (if it were, Boolean evaluation of an object of a type that is always true would have raised an exception). Shai.
On 5 March 2014 10:59, Shai Berger <shai@platonix.com> wrote:
On Wednesday 05 March 2014 12:44:07 Paul Moore wrote:
I actually find the idea of truth-testing a value that's expected to have a type that is always true *more* unintuitive than an explicit "is not None", so I'd fix the above code regardless of what the truth value of midnight is.
I can appreciate this point of view, but I think it is not the position generally taken by Python (if it were, Boolean evaluation of an object of a type that is always true would have raised an exception).
I think it's clear that if this were new code being added to the stdlib then the consensus would be that having midnight evaluate as False is ridiculous. The question is surely whether the issue is worth a backwards compatibility break not whether the current behaviour is a good idea (it clearly isn't). Oscar
On 5 March 2014 12:34, Oscar Benjamin <oscar.j.benjamin@gmail.com> wrote:
On 5 March 2014 10:59, Shai Berger <shai@platonix.com> wrote:
On Wednesday 05 March 2014 12:44:07 Paul Moore wrote:
I actually find the idea of truth-testing a value that's expected to have a type that is always true *more* unintuitive than an explicit "is not None", so I'd fix the above code regardless of what the truth value of midnight is.
I can appreciate this point of view, but I think it is not the position generally taken by Python (if it were, Boolean evaluation of an object of a type that is always true would have raised an exception).
I think it's clear that if this were new code being added to the stdlib then the consensus would be that having midnight evaluate as False is ridiculous.
The question is surely whether the issue is worth a backwards compatibility break not whether the current behaviour is a good idea (it clearly isn't).
Precisely. Sorry I wasn't clear enough. And FWIW, my opinion is that the problem is not worth a compatibility break, because it's so easily solved (and the fixed code is probably an improvement in any case). Paul
On Wednesday 05 March 2014 14:34:09 Oscar Benjamin wrote:
I think it's clear that if this were new code being added to the stdlib then the consensus would be that having midnight evaluate as False is ridiculous.
The question is surely whether the issue is worth a backwards compatibility break not whether the current behaviour is a good idea (it clearly isn't).
If this is the case, then we should be asking a variant of Paul's question: Why would anyone check if a time is equal to midnight, by Boolean evaluation rather than comparison to midnight? Or, rather, how many people do this? We have several reports of the current behavior causing surprising, hard-to- detect bugs (a co-worker spent a couple of hours on one such bug yesterday; frankly, I'm impressed he found it that fast). Unless we have reason to think people are using this on purpose, the effect of the change will probably be to fix many more bugs than it causes. I suggest to handle the backwards-compatibility issue with a deprecation cycle: Python 3.4 is already feature frozen, so make 3.5 raise a PendingDeprecationWarning when datetime.time.__nonzero__ returns False; 3.6 a DeprecationWarning; and 3.7 return True. That's what we would do in Django. On Wednesday 05 March 2014 14:54:27 Paul Moore wrote:
And FWIW, my opinion is that the problem is not worth a compatibility break, because it's so easily solved (and the fixed code is probably an improvement in any case).
Ah, but it is only easily solved once it is detected; the behavior causes bugs that surface once in a blue moon (in production, and then they cannot be reproduced by developers who, usually, do not work at midnight). So, on a second thought -- an alternative solution is to make any Boolean evaluation of time objects warn the user about midnight. Thanks for your attention, Shai.
On 5 March 2014 13:10, Shai Berger <shai@platonix.com> wrote:
On Wednesday 05 March 2014 14:54:27 Paul Moore wrote:
And FWIW, my opinion is that the problem is not worth a compatibility break, because it's so easily solved (and the fixed code is probably an improvement in any case).
Ah, but it is only easily solved once it is detected; the behavior causes bugs that surface once in a blue moon (in production, and then they cannot be reproduced by developers who, usually, do not work at midnight).
That's a fair point. But your deprecation suggestion doesn't seem to help this, unless you expect (in a realistic timescale) to be able to drop support for Python <3.7. Your users (on say 2.7 or 3.3) will still get bugs, and now your developers (using 3.7) won't even be able to reproduce the issue by staying up till midnight. And once they do find the bug they will still have to fix it by an explicit test. Again, I'm not saying the current behaviour is sensible, but I doubt the work to fix it will benefit anyone in practice. Paul.
I agree this change would cause far more good than harm - I see that it implies an incompatible change, but lets think on "Although practicality beats purity" terms here: the only instance I see this could possibly be used by purpose is in a code to that would increase a "day counter" if the time.now() would evaluate to false. Such a piece of code would be broken in many other levels to start with. A critical session in any piece of code that has to check if "at this second now we are at midnight" should be using one of the specialized timers to take care of borderline cases, such as start and ending of D.S.T. and such. It would have to be orders of magnitude better written than "if not current_time: ... " And even in the improbable case of such a piece of code exists, the changing of behavior would make it break in the first 24 hours, and the problem would not be that hard to find out. In this light, I think having to wait for 3 major release cycles until that is gone is a lot of overkill. On 5 March 2014 10:26, Paul Moore <p.f.moore@gmail.com> wrote:
On 5 March 2014 13:10, Shai Berger <shai@platonix.com> wrote:
On Wednesday 05 March 2014 14:54:27 Paul Moore wrote:
And FWIW, my opinion is that the problem is not worth a compatibility break, because it's so easily solved (and the fixed code is probably an improvement in any case).
Ah, but it is only easily solved once it is detected; the behavior causes bugs that surface once in a blue moon (in production, and then they cannot be reproduced by developers who, usually, do not work at midnight).
That's a fair point. But your deprecation suggestion doesn't seem to help this, unless you expect (in a realistic timescale) to be able to drop support for Python <3.7. Your users (on say 2.7 or 3.3) will still get bugs, and now your developers (using 3.7) won't even be able to reproduce the issue by staying up till midnight. And once they do find the bug they will still have to fix it by an explicit test.
Again, I'm not saying the current behaviour is sensible, but I doubt the work to fix it will benefit anyone in practice.
Paul. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Wednesday 05 March 2014 15:26:38 Paul Moore wrote:
On 5 March 2014 13:10, Shai Berger <shai@platonix.com> wrote:
On Wednesday 05 March 2014 14:54:27 Paul Moore wrote:
And FWIW, my opinion is that the problem is not worth a compatibility break, because it's so easily solved (and the fixed code is probably an improvement in any case).
Ah, but it is only easily solved once it is detected; the behavior causes bugs that surface once in a blue moon (in production, and then they cannot be reproduced by developers who, usually, do not work at midnight).
That's a fair point. But your deprecation suggestion doesn't seem to help this, unless you expect (in a realistic timescale) to be able to drop support for Python <3.7.
I expect to drop support for Python<3.7 sometime...
Your users (on say 2.7 or 3.3) will still get bugs, and now your developers (using 3.7) won't even be able to reproduce the issue by staying up till midnight.
If my developers are using 3.7 to debug problems that user encounter with 3.3, they deserve to stay up till midnight and beyond.
And once they do find the bug they will still have to fix it by an explicit test.
The process I want to see is, that Boolean evaluations of time objects is slowly, but more-or-less completely removed from code that targets Python<3.7; then, in a few years, people can go back to writing "if event.start_time" like they do with dates and datetimes.
Again, I'm not saying the current behaviour is sensible, but I doubt the work to fix it will benefit anyone in practice.
So, you support my other suggestion -- a warning on every use of bool(time)? Also, the work to fix it is probably less than the work invested in this discussion so far... In any case, if we all agree that http://bugs.python.org/issue13936 is a valid problem -- can we re-open it, and then discuss how (or if) we solve it? Thanks, Shai.
On 5 March 2014 13:53, Shai Berger <shai@platonix.com> wrote:
then, in a few years, people can go back to writing "if event.start_time" like they do with dates and datetimes.
Why on earth would they do that? It's still bad practice. If you're using None as a sentinel, you should test for it explicitly. Nobody has yet suggested any other use case where this matters.
Again, I'm not saying the current behaviour is sensible, but I doubt the work to fix it will benefit anyone in practice.
So, you support my other suggestion -- a warning on every use of bool(time)?
No. Good programming practice should cover that. We don't warn in other cases where programmers make silly coding errors. Look at Skip's message - should we also warn on uses of bool(int) because people can write bad code that fails to work properly with zero, as well?
Also, the work to fix it is probably less than the work invested in this discussion so far...
That is not obvious (given documentation, release management, etc, costs).
In any case, if we all agree that http://bugs.python.org/issue13936 is a valid problem -- can we re-open it, and then discuss how (or if) we solve it?
We don't. Can we agree that it's not a bug and abandon this fruitless discussion? Paul
On 05/03/2014 15:01, Paul Moore wrote:
On 5 March 2014 13:53, Shai Berger <shai@platonix.com> wrote:
then, in a few years, people can go back to writing "if event.start_time" like they do with dates and datetimes.
Why on earth would they do that? It's still bad practice. If you're using None as a sentinel, you should test for it explicitly. Nobody has yet suggested any other use case where this matters.
Again, I'm not saying the current behaviour is sensible, but I doubt the work to fix it will benefit anyone in practice.
So, you support my other suggestion -- a warning on every use of bool(time)?
No. Good programming practice should cover that. We don't warn in other cases where programmers make silly coding errors. Look at Skip's message - should we also warn on uses of bool(int) because people can write bad code that fails to work properly with zero, as well?
Also, the work to fix it is probably less than the work invested in this discussion so far...
That is not obvious (given documentation, release management, etc, costs).
In any case, if we all agree that http://bugs.python.org/issue13936 is a valid problem -- can we re-open it, and then discuss how (or if) we solve it?
We don't. Can we agree that it's not a bug and abandon this fruitless discussion?
Paul
I wouldn't agree that this has been a fruitless discussion. I would agree that this is not a bug, that the code needs changing, and that at least one new test needs to be added to the unit test suite(s) for the buggy application(s). -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
On Wednesday 05 March 2014 17:01:33 you wrote:
On 5 March 2014 13:53, Shai Berger <shai@platonix.com> wrote:
Again, I'm not saying the current behaviour is sensible, but I doubt the work to fix it will benefit anyone in practice.
[...]
In any case, if we all agree that http://bugs.python.org/issue13936 is a valid problem
We don't. Can we agree that it's not a bug and abandon this fruitless discussion?
What, other than "bug", do you call behavior that isn't sensible?
On 5 March 2014 15:19, Shai Berger <shai@platonix.com> wrote:
What, other than "bug", do you call behavior that isn't sensible?
"Unfortunate". I'm not arguing that the behaviour is useful. Or sensible. What I'm arguing, and you're missing, is that the behaviour is as documented, and so can't be changed without backward compatibility implications. Specifically, see http://docs.python.org/3.4/library/datetime.html#time-objects: """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.""" Your code is ignoring documented behaviour. Worse, your code can clearly be improved by being explicit in your test, and yet you'd rather argue that the Python developers implement a backward compatibility break which you won't even be able to take advantage of until you drop support of versions of Python before 3.7. Paul.
On Wed, Mar 5, 2014 at 9:37 AM, Paul Moore <p.f.moore@gmail.com> wrote:
On 5 March 2014 15:19, Shai Berger <shai@platonix.com> wrote:
What, other than "bug", do you call behavior that isn't sensible?
"Unfortunate". I'm not arguing that the behaviour is useful. Or sensible. What I'm arguing, and you're missing, is that the behaviour is as documented, and so can't be changed without backward compatibility implications. Specifically, see http://docs.python.org/3.4/library/datetime.html#time-objects:
"""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."""
Your code is ignoring documented behaviour. Worse, your code can clearly be improved by being explicit in your test, and yet you'd rather argue that the Python developers implement a backward compatibility break which you won't even be able to take advantage of until you drop support of versions of Python before 3.7.
Or, perhaps, even though he wouldn't be able to use this change right away, he'd just like to see Python be better. I can't see that there's going to come a better time to discuss this issue than the present, thus it seems to me that you're suggesting that this odd and illogical behavior continue forever, simply because it's been documented.
The backward compatibility and migration path still need to be addressed, but it seems that we are disagreeing on whether this strange behavior should ever change. I think that, at some point, it should. If we ever agree on that, then we can start thinking about when.
Ryan Hiebert writes:
he'd just like to see Python be better.
We all agree that we'd like Python to be better. But many of us feel that the proposed change does not make Python better. It merely makes it more comfortable for what we (many of us, anyway) consider to be badly written code, and encourages people to write more code in poor style as we see it.
it seems that we are disagreeing on whether this strange behavior should ever change.
Well, yes, for Python 2 (obviously) and Python 3. I don't think anybody would object to changing it in Python 4. (Honestly, I would, on the principle above -- it really is only useful if you want to write code in bad style -- but it doesn't take a French soldier on the parapet to let you know in which general direction the wind blows.)
I think that, at some point, it should. If we ever agree on that, then we can start thinking about when.
I really don't think you'll achieve consensus before we start discussing Python 4 seriously. That's when the hunting season on backward incompatible changes (that don't correct showstoppers) opens.
On Wednesday 05 March 2014 17:37:14 Paul Moore wrote:
On 5 March 2014 15:19, Shai Berger <shai@platonix.com> wrote:
What, other than "bug", do you call behavior that isn't sensible?
"Unfortunate".
That's unfortunate. You miss a lot of bugs.
I'm not arguing that the behaviour is useful. Or sensible. What I'm arguing, and you're missing, is that the behaviour is as documented, and so can't be changed without backward compatibility implications.
I think I have given ample room to backwards-compatibility considerations.
Specifically, see http://docs.python.org/3.4/library/datetime.html#time-objects:
"""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."""
Yes, I know. It's mentioned in the bug.
Your code is ignoring documented behaviour. Worse, your code can clearly be improved by being explicit in your test,
We disagree. I don't see adding "is not None" as an improvement at all. I see it as adding unnecessary ceremony. If you really like that much explicitness, use Java.
and yet you'd rather argue that the Python developers implement a backward compatibility break which you won't even be able to take advantage of until you drop support of versions of Python before 3.7.
What Ryan said: The code in question will not benefit from this change; it is Python 2.7, and will probably be rewritten from scratch before anything comes out of this. This is about fixing my projects a decade from now. Shai.
On Thu, Mar 6, 2014 at 3:05 AM, Shai Berger <shai@platonix.com> wrote:
On Wednesday 05 March 2014 17:37:14 Paul Moore wrote:
On 5 March 2014 15:19, Shai Berger <shai@platonix.com> wrote:
What, other than "bug", do you call behavior that isn't sensible?
"Unfortunate".
That's unfortunate. You miss a lot of bugs.
A bug is something where behaviour differs from documentation or intent. Differing from expectation isn't necessarily a bug (although in the absence of clear docs or statement of intent, it could be called one), partly because expectations vary. When the bulk of programmers expect one thing and the code does another, it's probably a wart or misfeature, but if it's properly documented, it can't really be called a bug. In this case, I would say that it's a wart. The fact that one timestamp happens to be treated as zero is odd, and I wouldn't advocate it, but now that it's there and documented, it's not a bug. ChrisA
On Wednesday 05 March 2014 18:20:08 Chris Angelico wrote:
On Thu, Mar 6, 2014 at 3:05 AM, Shai Berger <shai@platonix.com> wrote:
On Wednesday 05 March 2014 17:37:14 Paul Moore wrote:
On 5 March 2014 15:19, Shai Berger <shai@platonix.com> wrote:
What, other than "bug", do you call behavior that isn't sensible?
"Unfortunate".
That's unfortunate. You miss a lot of bugs.
A bug is something where behaviour differs from documentation or intent. Differing from expectation isn't necessarily a bug (although in the absence of clear docs or statement of intent, it could be called one), partly because expectations vary. When the bulk of programmers expect one thing and the code does another, it's probably a wart or misfeature, but if it's properly documented, it can't really be called a bug.
In this case, I would say that it's a wart.
Fine. That is, I'm not too interested in the semantics of English, but rather in the semantics of Python. So, I rephrase my request: Would anybody object to a silent warning issued whenever a time-object is evaluated as Boolean? Something along the lines of, "Evaluating time object as Boolean: Note that time(0,0,0) evaluates to False" A silent warning, IIUC, is only printed when requested -- so it won't get in anybody's way, but still provide a tool for avoiding the bug. It can be added into Python 3.5. It could probably be added to earlier versions, but for that we'd need to consider this a bug :) Shai.
On Wed, Mar 5, 2014 at 11:38 AM, Shai Berger <shai@platonix.com> wrote:
So, I rephrase my request: Would anybody object to a silent warning issued whenever a time-object is evaluated as Boolean?
I would. Users should not be penalized for using a documented behavior. There are legitimate uses for bool(midnight) being False. Midnight is special in many contexts. For example, it is uncertain whether midnight belongs to the previous or next day. If your application wants to group midnight differently from other times - it is perfectly fine to use "if dt.time()" instead of a more verbose "if dt.time() != datetime.time(0, 0)".
On Wednesday 05 March 2014 19:27:21 Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Wed, Mar 5, 2014 at 11:38 AM, Shai Berger <shai@platonix.com> wrote:
So, I rephrase my request: Would anybody object to a silent warning issued whenever a time-object is evaluated as Boolean?
I would. Users should not be penalized for using a documented behavior.
Fair enough. I would also add the warning to the documentation, and call the behavior "deprecated" (even if no steps will be taken to actually remove it).
There are legitimate uses for bool(midnight) being False. Midnight is special in many contexts. For example, it is uncertain whether midnight belongs to the previous or next day. If your application wants to group midnight differently from other times - it is perfectly fine to use "if dt.time()" instead of a more verbose "if dt.time() != datetime.time(0, 0)".
The question is: Of all the Python lines in existence, where a time is evaluated as Boolean, what percentage intends to capture midnight, and what percentage intends to capture an empty field; the latter is a (user) bug, and I suspect it is the much larger part. So even though there are legitimate uses for midnight being false, keeping things as they are is setting a trap for users. It is documented, but highly unintuitive (as evidenced by many messages on this thread). I think it best if the gotcha is removed, but if not, I ask at least to have a way to mitigate the problem. Thanks, Shai.
On 05/03/2014 17:27, Alexander Belopolsky wrote:
On Wed, Mar 5, 2014 at 11:38 AM, Shai Berger <shai@platonix.com <mailto:shai@platonix.com>> wrote:
So, I rephrase my request: Would anybody object to a silent warning issued whenever a time-object is evaluated as Boolean?
I would. Users should not be penalized for using a documented behavior.
We hope that the Python documentation describes the semantics of the language and packages as completely as is reasonably possible. So you are effectively saying that no backwards-incompatible change should ever be made. If we accepted that, this discussion would end now.
There are legitimate uses for bool(midnight) being False. Midnight is special in many contexts. For example, it is uncertain whether midnight belongs to the previous or next day. If your application wants to group midnight differently from other times - it is perfectly fine to use "if dt.time()" instead of a more verbose "if dt.time() != datetime.time(0, 0)".
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
No virus found in this message. Checked by AVG - www.avg.com <http://www.avg.com> Version: 2012.0.2247 / Virus Database: 3705/6655 - Release Date: 03/05/14
On Wed, Mar 5, 2014 at 7:21 PM, Rob Cliffe <rob.cliffe@btinternet.com>wrote:
So you are effectively saying that no backwards-incompatible change should ever be made. If we accepted that, this discussion would end now.
This was the rule for the datetime module for many years and we were able to put in many nice enhancements in a fully backward compatible way.
[Rob Cliffe <rob.cliffe@btinternet.com>]
We hope that the Python documentation describes the semantics of the language and packages as completely as is reasonably possible. So you are effectively saying that no backwards-incompatible change should ever be made. If we accepted that, this discussion would end now.
There should be a very high bias against changing documented behavior that is in fact working as documented. The document is a contract with users. Contracts shouldn't be violated without truly compelling cause. "I am altering the deal. Pray I don't alter it any further." There's a reason Darth Vader wasn't proclaimed Python's BDFL ;-)
On Mar 5, 2014, at 8:34 PM, Tim Peters <tim.peters@gmail.com> wrote:
[Rob Cliffe <rob.cliffe@btinternet.com>]
We hope that the Python documentation describes the semantics of the language and packages as completely as is reasonably possible. So you are effectively saying that no backwards-incompatible change should ever be made. If we accepted that, this discussion would end now.
There should be a very high bias against changing documented behavior that is in fact working as documented. The document is a contract with users. Contracts shouldn't be violated without truly compelling cause.
"I am altering the deal. Pray I don't alter it any further." There's a reason Darth Vader wasn't proclaimed Python's BDFL ;-) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
When the documented behavior is both nonsensical and the cause of hard to debug bugs that is a pretty compelling use case to me, unless you actively enjoy being user hostile. ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
[Donald Stufft]
When the documented behavior is both nonsensical and the cause of hard to debug bugs that is a pretty compelling use case to me, unless you actively enjoy being user hostile.
Breaking code that currently works is as hostile as hostile gets. Where is the evidence that no code relies on the current behavior? For that matter, where is the evidence that the current behavior is a significant cause of bugs? I know I've used "if some_time_object:", but it did exactly what I expected it to do.
On Mar 5, 2014, at 9:15 PM, Tim Peters <tim.peters@gmail.com> wrote:
[Donald Stufft]
When the documented behavior is both nonsensical and the cause of hard to debug bugs that is a pretty compelling use case to me, unless you actively enjoy being user hostile.
Breaking code that currently works is as hostile as hostile gets. Where is the evidence that no code relies on the current behavior? For that matter, where is the evidence that the current behavior is a significant cause of bugs? I know I've used "if some_time_object:", but it did exactly what I expected it to do.
Forgive me if I’m wrong, but aren’t you the author of the date time module? If that’s the case what you expect it to do isn’t particularly relevant as you’re intimately aware of it’s implementation. It’s hard to do any sort of search for this, however in an informal poll where I’ve shown people this code not a single person thought it made sense, and most of them responded with “wtf?”. I’ve also seen first hand through Django (which is why the person who started this thread) get caught by this bug and have to spend hours trying to figure out why it’s behaving that way. ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
Forgive me if I'm wrong, but aren't you the author of the date time module?
Yes.
If that's the case what you expect it to do isn't particularly relevant as you're intimately aware of it's implementation.
Drat! Checkmated again! I get tired of putting smileys after everything ;-)
It's hard to do any sort of search for this, however in an informal poll where I've shown people this code
Precisely which code did you show them? What were the backgrounds of these people? Etc etc etc.
not a single person thought it made sense, and most of them responded with "wtf?"
Which, via amazing coincidence or foresight, is exactly what I bet you were expecting ;-)
. I've also seen first hand through Django (which is why the person who started this thread) get caught by this bug and have to spend hours trying to figure out why it's behaving that way.
Too sketchy to say much about. If this is a variation of testing "if object:" as a shorthand for testing "if object is None:", little sympathy from me. In any case, this took more of my time so far than any possible outcome would be worth to me, so I'll just close with -0 on changing it (would rather not bother, but won't complain if it is changed).
On Wed, Mar 05, 2014 at 09:23:37PM -0500, Donald Stufft wrote:
It’s hard to do any sort of search for this, however in an informal poll where I’ve shown people this code not a single person thought it made sense, and most of them responded with “wtf?”.
Well I don't know who these people are, what their background is, or exactly how you phrased the question. But in my experience, most programmers have extremely strongly held opinions about the sensibility of certain features that have little or nothing with any rational analysis of the pros and cons and far more likely to be "that's different from the first language I learned, therefore it's rubbish". Having read Tim's explanation for why time() is falsey, I no longer think it's a misfeature, and am now -0.5 on changing it at all.
I’ve also seen first hand through Django (which is why the person who started this thread) get caught by this bug and have to spend hours trying to figure out why it’s behaving that way.
Again, I don't know the background here, and we all know that sometimes tracking down bugs can be difficult, but if somebody is spending "hours" trying to debug why "if time_value" sometimes does the wrong thing, *without* bothering to read the documentation for time objects to find out how they're supposed to behave, then I don't have a lot of sympathy. Also, if you write "if spam" as a short-cut for "if spam is not None", then again any bugs you experience are self-inflicted. -- Steven
On Thursday 06 March 2014 14:00:02 Steven D'Aprano wrote:
tracking down bugs can be difficult, but if somebody is spending "hours" trying to debug why "if time_value" sometimes does the wrong thing, *without* bothering to read the documentation for time objects to find out how they're supposed to behave, then I don't have a lot of sympathy.
This argument keeps repeating. It is a gross misunderstanding of the problem. The hours are not spent trying to debug why an if went the wrong way. The hours are spent trying to understand why some feature of a program doesn't work as expected -- *sometimes*. It can easily take hours to try and reproduce the wrong behavior, in vain, because it only shows up on unique data; and the wrong behavior will often not show itself as "an if went the wrong way", but, for example, "sometimes a number here is wrong" -- which happens when the calculation of the number goes over a collection of objects and does complex things including decisions based on time. Of course, once you get to the "if" it's relatively easy (surprising behavior is still surprising, but less mystifying). It's getting to it that's the issue. Thanks, Shai.
On 03/05/2014 07:00 PM, Steven D'Aprano wrote:
On Wed, Mar 05, 2014 at 09:23:37PM -0500, Donald Stufft wrote:
It’s hard to do any sort of search for this, however in an informal poll where I’ve shown people this code not a single person thought it made sense, and most of them responded with “wtf?”.
Well I don't know who these people are, what their background is, or exactly how you phrased the question. But in my experience, most programmers have extremely strongly held opinions about the sensibility of certain features that have little or nothing with any rational analysis of the pros and cons and far more likely to be "that's different from the first language I learned, therefore it's rubbish".
As part of learning Python, I learned one of its core tenets was the divide between something and nothing (some of that from you, I believe ;), midnight is most certainly something, so I was very surprised to learn that bool(midnight) is False. -- ~Ethan~
On 06.03.2014 03:23, Donald Stufft wrote:
On Mar 5, 2014, at 9:15 PM, Tim Peters <tim.peters@gmail.com> wrote:
[Donald Stufft]
When the documented behavior is both nonsensical and the cause of hard to debug bugs that is a pretty compelling use case to me, unless you actively enjoy being user hostile.
Breaking code that currently works is as hostile as hostile gets. Where is the evidence that no code relies on the current behavior? For that matter, where is the evidence that the current behavior is a significant cause of bugs? I know I've used "if some_time_object:", but it did exactly what I expected it to do.
Forgive me if I’m wrong, but aren’t you the author of the date time module? If that’s the case what you expect it to do isn’t particularly relevant as you’re intimately aware of it’s implementation.
Just in case you're looking for a relevant use case: Having bool(time(0,0,0)) allows you to quickly check whether you are dealing with a datetime value with (non-trivial) time part or not. You run into situations where you have to test for this in date/time conversions, arithmetics, etc. e.g. if you need to convert a datetime value to a date and want to prevent truncation of information, or if you want to format a datetime value in a user friendly way by omitting the zero time part, or if you're doing datetime arithmetic that has to follow special requirements w/r to days (think interest or insurance math) and you want to quickly check whether you can use the fast path simple calculation, or you need to do the full blown complicated part. Now, those use cases may still not appear relevant to you, but then the argument that "if x:" should not trigger for time(0,0,0) because x might actually be None, is also not relevant to me, since it masks a bug in the example code and those should never go undetected :-) -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On Mar 6, 2014, at 3:35 AM, M.-A. Lemburg <mal@egenix.com> wrote:
On 06.03.2014 03:23, Donald Stufft wrote:
On Mar 5, 2014, at 9:15 PM, Tim Peters <tim.peters@gmail.com> wrote:
[Donald Stufft]
When the documented behavior is both nonsensical and the cause of hard to debug bugs that is a pretty compelling use case to me, unless you actively enjoy being user hostile.
Breaking code that currently works is as hostile as hostile gets. Where is the evidence that no code relies on the current behavior? For that matter, where is the evidence that the current behavior is a significant cause of bugs? I know I've used "if some_time_object:", but it did exactly what I expected it to do.
Forgive me if I’m wrong, but aren’t you the author of the date time module? If that’s the case what you expect it to do isn’t particularly relevant as you’re intimately aware of it’s implementation.
Just in case you're looking for a relevant use case:
Having bool(time(0,0,0)) allows you to quickly check whether you are dealing with a datetime value with (non-trivial) time part or not.
I don’t see how midnight is any more or less trivial than 12:01.
You run into situations where you have to test for this in date/time conversions, arithmetics, etc. e.g. if you need to convert a datetime value to a date and want to prevent truncation of information, or if you want to format a datetime value in a user friendly way by omitting the zero time part, or if you're doing datetime arithmetic that has to follow special requirements w/r to days (think interest or insurance math) and you want to quickly check whether you can use the fast path simple calculation, or you need to do the full blown complicated part.
I think these would be better served by actually checking for what you mean. For the record I also think that checking for a date time to not be None is also better served by actually checking for what you mean. The difference is in intent and likelihood of confusion. For all of those you’re going to be explicitly testing and working with midnight objects, so you’re going to test them, you’re going to exercise that code. In the ``is None`` case, Midnight is just another value so that bug will lay dormant and undetected until someone just happens to hit an unlucky time. And once you get that it’s likely to be difficult to reproduce. Further more this only ever works reliably on naive times and doesn’t realistically work “sanely” (if you consider this behavior sane) on aware times at all. So to be specific, I think using ``if time_or_something_that_may_be_none:`` is always wrong, but I think the like hood for user confusion and hard to reproduce bugs are far greater when a more or less arbitrary time (especially in the sense of aware times) evaluates as false when a user omits the ``is None`` than when someone expects ``if time:`` to be false at midnight.
Now, those use cases may still not appear relevant to you, but then the argument that "if x:" should not trigger for time(0,0,0) because x might actually be None, is also not relevant to me, since it masks a bug in the example code and those should never go undetected :-)
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On Mar 6, 2014, at 3:46 AM, Donald Stufft <donald@stufft.io> wrote:
So to be specific, I think using ``if time_or_something_that_may_be_none:`` is always wrong, but I think the like hood for user confusion and hard to reproduce bugs are far greater when a more or less arbitrary time (especially in the sense of aware times) evaluates as false when a user omits the ``is None`` than when someone expects ``if time:`` to be false at midnight.
I realized this was ambiguous, I always consider directly checking the boolean value of a time object as wrong, as I don’t believe time has any True/False implications in it. This includes both using ``if thing:`` to detect None and using ``if thing:`` to detect midnight. I just think the ``if thing:`` to detect None case is more common and also more difficult to detect when you have a bug because of it and more surprising behavior than the other case. ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
2014-03-06 6:34 GMT+01:00 Greg Ewing <greg.ewing@canterbury.ac.nz>:
The point of having time objects as an abstraction is so that you *don't* have to think of them as an offset from anything. You simply think of them as times.
but times *are* offsets from midnight (24h clock) or midnight and noon (12h clock) 1 o’clock = 1 hour after midnight 12 o’clock pm = 12 hours after noon they are essentially radial coordinates – and radial coordinates do have a 0 point.
On 06.03.2014 09:46, Donald Stufft wrote:
On Mar 6, 2014, at 3:35 AM, M.-A. Lemburg <mal@egenix.com> wrote:
On 06.03.2014 03:23, Donald Stufft wrote:
On Mar 5, 2014, at 9:15 PM, Tim Peters <tim.peters@gmail.com> wrote:
[Donald Stufft]
When the documented behavior is both nonsensical and the cause of hard to debug bugs that is a pretty compelling use case to me, unless you actively enjoy being user hostile.
Breaking code that currently works is as hostile as hostile gets. Where is the evidence that no code relies on the current behavior? For that matter, where is the evidence that the current behavior is a significant cause of bugs? I know I've used "if some_time_object:", but it did exactly what I expected it to do.
Forgive me if I’m wrong, but aren’t you the author of the date time module? If that’s the case what you expect it to do isn’t particularly relevant as you’re intimately aware of it’s implementation.
Just in case you're looking for a relevant use case:
Having bool(time(0,0,0)) allows you to quickly check whether you are dealing with a datetime value with (non-trivial) time part or not.
I don’t see how midnight is any more or less trivial than 12:01.
You run into situations where you have to test for this in date/time conversions, arithmetics, etc. e.g. if you need to convert a datetime value to a date and want to prevent truncation of information, or if you want to format a datetime value in a user friendly way by omitting the zero time part, or if you're doing datetime arithmetic that has to follow special requirements w/r to days (think interest or insurance math) and you want to quickly check whether you can use the fast path simple calculation, or you need to do the full blown complicated part.
I think these would be better served by actually checking for what you mean. For the record I also think that checking for a date time to not be None is also better served by actually checking for what you mean. The difference is in intent and likelihood of confusion.
FWIW, I expect bool(time(0,0,0)) == False, just like I expect bool(0.0) == False. I would find it confusing to have bool(zero_element) == True.
For all of those you’re going to be explicitly testing and working with midnight objects, so you’re going to test them, you’re going to exercise that code. In the ``is None`` case, Midnight is just another value so that bug will lay dormant and undetected until someone just happens to hit an unlucky time. And once you get that it’s likely to be difficult to reproduce.
datetime values with zero time are *very* common in practice (you run into them whenever you convert a date value into a datetime value), so the "is None" case is easy to reproduce and will hit you more often than you like. BTW: Not using "is None" will bite you in many other ways as well. None it typically used as value for "not initialized" or "no value provided". This meaning is completely different from "empty selection" or "empty string", so in order to address all those cases as well, you'd have to change: bool(()) == True bool('') == True etc. The "if x is None:" test is also faster than "if x:", so actually win something by coding correctly ;-) Please provide a stronger argument for needing to change bool(time(0,0,0)) than one which is built on buggy code :-) (I'm running out of smileys...) -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On 6 March 2014 09:32, M.-A. Lemburg <mal@egenix.com> wrote:
On 06.03.2014 09:46, Donald Stufft wrote:
On Mar 6, 2014, at 3:35 AM, M.-A. Lemburg <mal@egenix.com> wrote:
On 06.03.2014 03:23, Donald Stufft wrote:
On Mar 5, 2014, at 9:15 PM, Tim Peters <tim.peters@gmail.com> wrote:
[Donald Stufft]
When the documented behavior is both nonsensical and the cause of
hard to debug bugs that
is a pretty compelling use case to me, unless you actively enjoy being user hostile.
Breaking code that currently works is as hostile as hostile gets. Where is the evidence that no code relies on the current behavior? For that matter, where is the evidence that the current behavior is a significant cause of bugs? I know I've used "if some_time_object:", but it did exactly what I expected it to do.
Forgive me if I'm wrong, but aren't you the author of the date time module? If that's the case what you expect it to do isn't particularly relevant as you're intimately aware of it's implementation.
Just in case you're looking for a relevant use case:
Having bool(time(0,0,0)) allows you to quickly check whether you are dealing with a datetime value with (non-trivial) time part or not.
I don't see how midnight is any more or less trivial than 12:01.
You run into situations where you have to test for this in date/time conversions, arithmetics, etc. e.g. if you need to convert a datetime value to a date and want to prevent truncation of information, or if you want to format a datetime value in a user friendly way by omitting the zero time part, or if you're doing datetime arithmetic that has to follow special requirements w/r to days (think interest or insurance math) and you want to quickly check whether you can use the fast path simple calculation, or you need to do the full blown complicated part.
I think these would be better served by actually checking for what you mean. For the record I also think that checking for a date time to not be None is also better served by actually checking for what you mean. The difference is in intent and likelihood of confusion.
FWIW, I expect bool(time(0,0,0)) == False, just like I expect bool(0.0) == False. I would find it confusing to have bool(zero_element) == True.
For all of those you're going to be explicitly testing and working with midnight objects, so you're going to test them, you're going to exercise that code. In the ``is None`` case, Midnight is just another value so that bug will lay dormant and undetected until someone just happens to hit an unlucky time. And once you get that it's likely to be difficult to reproduce.
datetime values with zero time are *very* common in practice (you run into them whenever you convert a date value into a datetime value), so the "is None" case is easy to reproduce and will hit you more often than you like.
BTW: Not using "is None" will bite you in many other ways as well. None it typically used as value for "not initialized" or "no value provided". This meaning is completely different from "empty selection" or "empty string", so in order to address all those cases as well, you'd have to change:
bool(()) == True bool('') == True etc.
The "if x is None:" test is also faster than "if x:", so actually win something by coding correctly ;-)
Please provide a stronger argument for needing to change bool(time(0,0,0)) than one which is built on buggy code :-) (I'm running out of smileys...)
I'm very surprised that "midnight evaluates to False is insane" is not in and of itself a strong enough argument to change it. :-/ Michael
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
I feel zero value of non abelian group should not mean False in bool context. () + () == () "" + "" == "" 0 + 0 == 0 timedelta() + timedelta() == timedelta() time() + time() => TypeError On Thu, Mar 6, 2014 at 6:32 PM, M.-A. Lemburg <mal@egenix.com> wrote:
On 06.03.2014 09:46, Donald Stufft wrote:
On Mar 6, 2014, at 3:35 AM, M.-A. Lemburg <mal@egenix.com> wrote:
On 06.03.2014 03:23, Donald Stufft wrote:
On Mar 5, 2014, at 9:15 PM, Tim Peters <tim.peters@gmail.com> wrote:
[Donald Stufft]
When the documented behavior is both nonsensical and the cause of hard to debug bugs that is a pretty compelling use case to me, unless you actively enjoy being user hostile.
Breaking code that currently works is as hostile as hostile gets. Where is the evidence that no code relies on the current behavior? For that matter, where is the evidence that the current behavior is a significant cause of bugs? I know I've used "if some_time_object:", but it did exactly what I expected it to do.
Forgive me if I'm wrong, but aren't you the author of the date time module? If that's the case what you expect it to do isn't particularly relevant as you're intimately aware of it's implementation.
Just in case you're looking for a relevant use case:
Having bool(time(0,0,0)) allows you to quickly check whether you are dealing with a datetime value with (non-trivial) time part or not.
I don't see how midnight is any more or less trivial than 12:01.
You run into situations where you have to test for this in date/time conversions, arithmetics, etc. e.g. if you need to convert a datetime value to a date and want to prevent truncation of information, or if you want to format a datetime value in a user friendly way by omitting the zero time part, or if you're doing datetime arithmetic that has to follow special requirements w/r to days (think interest or insurance math) and you want to quickly check whether you can use the fast path simple calculation, or you need to do the full blown complicated part.
I think these would be better served by actually checking for what you mean. For the record I also think that checking for a date time to not be None is also better served by actually checking for what you mean. The difference is in intent and likelihood of confusion.
FWIW, I expect bool(time(0,0,0)) == False, just like I expect bool(0.0) == False. I would find it confusing to have bool(zero_element) == True.
For all of those you're going to be explicitly testing and working with midnight objects, so you're going to test them, you're going to exercise that code. In the ``is None`` case, Midnight is just another value so that bug will lay dormant and undetected until someone just happens to hit an unlucky time. And once you get that it's likely to be difficult to reproduce.
datetime values with zero time are *very* common in practice (you run into them whenever you convert a date value into a datetime value), so the "is None" case is easy to reproduce and will hit you more often than you like.
BTW: Not using "is None" will bite you in many other ways as well. None it typically used as value for "not initialized" or "no value provided". This meaning is completely different from "empty selection" or "empty string", so in order to address all those cases as well, you'd have to change:
bool(()) == True bool('') == True etc.
The "if x is None:" test is also faster than "if x:", so actually win something by coding correctly ;-)
Please provide a stronger argument for needing to change bool(time(0,0,0)) than one which is built on buggy code :-) (I'm running out of smileys...)
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- INADA Naoki <songofacandy@gmail.com>
Le 06/03/2014 09:35, M.-A. Lemburg a écrit :
Just in case you're looking for a relevant use case:
Having bool(time(0,0,0)) allows you to quickly check whether you are dealing with a datetime value with (non-trivial) time part or not.
This sounds like the kind of shortcut that only an expert would know to take, not something that's actually a good design decision. It's as though pathlib.Path('') evaluated to false for some obscure use case that I cared about. It is not a coincidence, IMO, that the three persons arguing that the current behaviour is reasonable are implementors of datetime modules: you, Tim and Alexander. All other people find the behaviour baffling. Regards Antoine.
On 06.03.2014 10:52, Antoine Pitrou wrote:
Le 06/03/2014 09:35, M.-A. Lemburg a écrit :
Just in case you're looking for a relevant use case:
Having bool(time(0,0,0)) allows you to quickly check whether you are dealing with a datetime value with (non-trivial) time part or not.
This sounds like the kind of shortcut that only an expert would know to take, not something that's actually a good design decision. It's as though pathlib.Path('') evaluated to false for some obscure use case that I cared about.
It doesn't ? :-)
It is not a coincidence, IMO, that the three persons arguing that the current behaviour is reasonable are implementors of datetime modules: you, Tim and Alexander. All other people find the behaviour baffling.
I'm not so sure. Of course, people who have been doing date/time stuff will jump into discussions that deal with them, but the same argument triggering this thread could have applied to other Python types and you would then have a different group of people argue for plausibility of the choice. I find it strange that the only argument against having time(0,0,0) evaluate to False in a boolean context is a wrongly used None test. This argument can be applied to lots of other types as well, where the buggy None test masks errors. Perhaps someone could present at least one compelling argument that is not based on wrong usage of None in if statements. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
Le 06/03/2014 11:33, M.-A. Lemburg a écrit :
I find it strange that the only argument against having time(0,0,0) evaluate to False in a boolean context is a wrongly used None test.
It's only "wrongly used" because of that particular wart. In other words, the "wrongly used" None test *exposes* the wart, it is not its *cause*. There are other contexts with implicit boolean conversion, such as any() and all(). Regards Antoine.
On 06.03.2014 11:46, Antoine Pitrou wrote:
Le 06/03/2014 11:33, M.-A. Lemburg a écrit :
I find it strange that the only argument against having time(0,0,0) evaluate to False in a boolean context is a wrongly used None test.
It's only "wrongly used" because of that particular wart. In other words, the "wrongly used" None test *exposes* the wart, it is not its *cause*.
Wait. Let's be clear on this: Writing if x: print ('x is None') or if x == None: print ('x is None') is wrong code. None is a singleton, so you have to use the "is" operator, i.e. if x is None: print ('x is None') is the only correct way of testing for None. Adding a test to pylint for this would probably be more helpful and address the real problem in a more productive way, than the current discussion to making the above problematic code work in the single use case of x being a datetime.time object.
There are other contexts with implicit boolean conversion, such as any() and all().
If you expect those to work correctly for None testing, then you're writing wrong code as well. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
Le 06/03/2014 12:04, M.-A. Lemburg a écrit :
Writing
if x: print ('x is None')
or
if x == None: print ('x is None')
is wrong code.
No it isn't.
None is a singleton, so you have to use the "is" operator, i.e.
if x is None: print ('x is None')
is the only correct way of testing for None.
No it isn't. x == None is a perfectly well-defined way of doing it, even if it isn't the stylistically preferred one. Regards Antoine.
On 06.03.2014 12:09, Antoine Pitrou wrote:
Le 06/03/2014 12:04, M.-A. Lemburg a écrit :
Writing
if x: print ('x is None')
or
if x == None: print ('x is None')
is wrong code.
No it isn't.
None is a singleton, so you have to use the "is" operator, i.e.
if x is None: print ('x is None')
is the only correct way of testing for None.
No it isn't. x == None is a perfectly well-defined way of doing it, even if it isn't the stylistically preferred one.
Depends on what kind of type x is and how that type implements the comparison slots. It is perfectly well possible to define a type that returns True for (x == None), even though x is not None :-)
class C: ... def __eq__(self, other): ... return (other is None) ... o = C() o == None True
-- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On 06.03.2014 12:04, M.-A. Lemburg wrote:
On 06.03.2014 11:46, Antoine Pitrou wrote:
Le 06/03/2014 11:33, M.-A. Lemburg a écrit :
I find it strange that the only argument against having time(0,0,0) evaluate to False in a boolean context is a wrongly used None test.
It's only "wrongly used" because of that particular wart. In other words, the "wrongly used" None test *exposes* the wart, it is not its *cause*.
Wait. Let's be clear on this:
Writing
if x: print ('x is None')
The above is obviously wrong :-). Let's try again: if not x: print ('x is None')
or
if x == None: print ('x is None')
is wrong code.
None is a singleton, so you have to use the "is" operator, i.e.
if x is None: print ('x is None')
is the only correct way of testing for None.
Adding a test to pylint for this would probably be more helpful and address the real problem in a more productive way, than the current discussion to making the above problematic code work in the single use case of x being a datetime.time object.
There are other contexts with implicit boolean conversion, such as any() and all().
If you expect those to work correctly for None testing, then you're writing wrong code as well.
-- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On 6 March 2014 21:04, M.-A. Lemburg <mal@egenix.com> wrote:
Wait. Let's be clear on this:
Writing
if x: print ('x is None')
or
if x == None: print ('x is None')
The case in question is essentially this one: if x: assert x is not None # Always valid! .... else: assert x is None # Valid for most user defined types There is a learned intuition that people naturally acquire when learning Python: - numbers may be false (it means zero) - containers may be false (it means empty) - everything else is always true (as that's the default for user defined classes) Where datetime().time() is confusing is the fact the expected behaviour changes based on *which* category you put the number in. The vast majority of Python's users will place structured date and time objects in the "arbitrary object" category, and expect them to always be true. When using None as a sentinel for such types, writing "if x:" when you really mean "if x is not None:" is a harmless style error, with no practical ill-effects. When dealing with time objects, such users are unlikely to be crafting test cases to ensure that "midnight UTC" is handled correctly, so their "harmless style error" is in fact a subtle data driven bug waiting to bite them. It is *really* hard for a static analyser to pick this up, because at point of use "if x:" gives no information about the type of "x", and hence any such alert would have an unacceptably high false positive rate. Now, let's consider the time with the *best* possible claim to being false: timestamp zero. How does that behave? Python 3.3:
bool(dt.datetime.fromtimestamp(0)) True bool(dt.datetime.fromtimestamp(0).date()) True bool(dt.datetime.fromtimestamp(0).time()) True
Huh, if times are supposed to be valid as truth values, that looks rather weird. What's going on?
dt.datetime.fromtimestamp(0).time() datetime.time(10, 0)
Oh, I'm in Brisbane - *of course* the truthiness of timestamps should depend on my timezone! Clearly, what I really meant was timestamp -36000, or perhaps 50400:
bool(dt.datetime.fromtimestamp(-36000).time()) False bool(dt.datetime.fromtimestamp(50400).time()) False
So, unless I happen to live in UTC, it's highly unlikely that I'm going to infer from Python's *behaviour* that datetime.time() (unlike datetime.date() and datetime.datetime()) belong in the "number" category, rather than the "arbitrary object" category. Perhaps it behaves like a number in other ways:
utcmidnight = dt.datetime.fromtimestamp(50400).time() utcmidnight + 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'datetime.time' and 'int' utcmidnight * 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for *: 'datetime.time' and 'int' int(utcmidnight) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: int() argument must be a string or a number, not 'datetime.time'
Hmm, nope. And that last one *explicitly* tells me it's not a number! There's a great saying in the usability world: "You can't document your way out of a usability problem". What it means is that if all the affordances of your application (or programming language!) push users towards a particular logical conclusion (in this case, "datetime.time values are not numbers"), having a caveat in your documentation isn't going to help, because people aren't even going to think to ask the question. It doesn't matter if you originally had a good reason for the behaviour, you've ended up in a place where your behaviour is confusing and inconsistent, because there is one piece of behaviour that is out of line with an otherwise consistent mental model. But perhaps I've been told "midnight is false in boolean context". But which midnight? There are three that apply to me:
naivemidnight datetime.time(0, 0) utcmidnight datetime.time(0, 0, tzinfo=datetime.timezone.utc) localmidnight datetime.time(0, 0, tzinfo=datetime.timezone(datetime.timedelta(0, 36000)))
Are they all False? No, no they're not (unless your local timezone is UTC):
bool(utcmidnight) False bool(naivemidnight) False bool(localmidnight) True
There's a phrase for APIs like this one: "expert friendly". Experts like a particular behaviour because it lets them do advanced things (like leave the door open for modular arithmetic on timestamp.time() values). However, that's not normally something we see as a virtue when designing APIs for Python - instead, we generally aim for layered complexity, where simple things are simple, and we provide power tools for advanced users that want them. Now, suppose this boolean behaviour went away. How would I detect if a value was midnight or not? Well, first, I would need to clarify my question. Do I mean midnight UTC? Or do I mean midnight local time? It makes a difference for aware objects, after all. Or perhaps I'm not interested in aware objects at all, and only care about naive midnight. Using appropriately named values like those I defined above, those questions are all very easy to express explicitly in ways that don't rely on readers understanding that "bool(x)" on a datetime.time object means the same thing as "x is naive midnight or UTC midnight": if x not in (naivemidnight, utcmidnight): # This is the current bool(x) .... if x != naivemidnight: # This has no current shorthand .... if x != utcmidnight: # This has no current shorthand .... if x != localmidnight: # This has no current shorthand .... And just to demonstrate that equivalence is accurate:
local_10am = localmidnight.replace(hour=10) local_10am datetime.time(10, 0, tzinfo=datetime.timezone(datetime.timedelta(0, 36000))) bool(local_10am) False local_10am != utcmidnight False local_10am not in (naivemidnight, utcmidnight) False
While it was originally the desire to reduce the impact of a very common bug that prompted me to reopen the issue, it is the improved consistency in the behaviour presented to users of Python 3.6+ that makes me belief this is actually worth fixing. We embarked on the whole Python 3 exercise in the name of making the language easier to learn by removing legacy features and fixing some problematic defaults and design warts - this is a tiny tweak by comparison, and will still go through the full normal deprecation cycle (warning in 3.5, behavioural change in 3.6) Regards, Nick.
On 06.03.2014 13:08, Nick Coghlan wrote:
But perhaps I've been told "midnight is false in boolean context". But which midnight? There are three that apply to me:
naivemidnight datetime.time(0, 0) utcmidnight datetime.time(0, 0, tzinfo=datetime.timezone.utc) localmidnight datetime.time(0, 0, tzinfo=datetime.timezone(datetime.timedelta(0, 36000)))
Are they all False? No, no they're not (unless your local timezone is UTC):
bool(utcmidnight) False bool(naivemidnight) False bool(localmidnight) True
Now this is a what I consider a valid argument for making a change. Thanks, Nick. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On Mar 6, 2014, at 8:21 AM, M.-A. Lemburg <mal@egenix.com> wrote:
On 06.03.2014 13:08, Nick Coghlan wrote:
But perhaps I've been told "midnight is false in boolean context". But which midnight? There are three that apply to me:
naivemidnight datetime.time(0, 0) utcmidnight datetime.time(0, 0, tzinfo=datetime.timezone.utc) localmidnight datetime.time(0, 0, tzinfo=datetime.timezone(datetime.timedelta(0, 36000)))
Are they all False? No, no they're not (unless your local timezone is UTC):
bool(utcmidnight) False bool(naivemidnight) False bool(localmidnight) True
Now this is a what I consider a valid argument for making a change.
Thanks, Nick.
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Yea Nick put into words what I’ve been attempting to say better than I ever could have :) ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
M.-A. Lemburg writes:
Are they all False? No, no they're not (unless your local timezone is UTC):
bool(utcmidnight) False bool(naivemidnight) False bool(localmidnight) True
Now this is a what I consider a valid argument for making a change.
Thanks, Nick.
I don't understand this turnabout. After all, time with time zones is the temporal equivalent of ISO 2022. It's just madness encapsulated in a formal data type definition.[1] Nobody should expect to get sane results from manipulating that stuff, only hope to get lucky. The only argument I've seen that corresponds to sane programming practice is Nick's statement that in Python, None, zeros, and empty containers are naturally false-ys, and all other objects are expected to be truth-ys. I find *that* compelling, even though it is backward incompatible with the documented behavior of time.time, and would not affect code using time.time objects that was written in good style. But I still don't like the way actually making the change would validate using "if var:" where "if var is not None:" is appropriate. I don't care if the change is made, but I hope it will be made absolutely clear that it's purely to work around *old* bugs that may never get fixed, and that "if var:" which is actually testing for identity with a false-y sentinel is still strongly discouraged. Footnotes: [1] Time counted in seconds from an epoch is hard enough, just Google for "monotonic clock". :-(
Le 06/03/2014 15:33, Stephen J. Turnbull a écrit :
I don't care if the change is made, but I hope it will be made absolutely clear that it's purely to work around *old* bugs that may never get fixed, and that "if var:" which is actually testing for identity with a false-y sentinel is still strongly discouraged.
Why "strongly discouraged"? This is more of a style issue than anything else? Regards Antoine.
Antoine Pitrou writes:
Le 06/03/2014 15:33, Stephen J. Turnbull a écrit :
never get fixed, and that "if var:" which is actually testing for identity with a false-y sentinel is still strongly discouraged.
Why "strongly discouraged"? This is more of a style issue than anything else?
No, it's not just a style issue. As Skip points out, it's a *semantic* issue as well. You're really testing for not for the boolean value of values of one type, but for a difference of type ("initialized whatever" vs "uninitialized variable"). Pragmatically, this would be half a dozen of one, six of the other, except that there are two more possibilities: a false-y of a different type, and a truth-y of a different type. I don't think the spurious truth-y matters very much ... you'd expect a TypeError to be raised pretty quickly, and if not, it's a bad bug (by the time you notice the bad value, it's really hard to work back to where and why it's been injected). The spurious false-y, on the other hand, seems quite likely to lead to spurious values *of the expected type*. To restate my earlier example: my_time = None if not my_time and x: my_time = x.expected_to_be_parsed_time_but_parse_bug_returns_0 if not my_time: my_time = time.now() You may never catch *that* parser bug, because the value of 'my_time' always looks right (ie, like a datetime.time value). But in my_time = None if my_time is None and x: my_time = x.expected_to_be_parsed_time_but_parse_bug_returns_0 if my_time is None: my_time = time.now() you're on the same footing as with the truth-y -- just a bit lucky and you get a quick TypeError. This is an objective benefit (although you can say "the probability that there will be a bug to catch is too low to care about" if you like, it's obviously bigger than zero). I also value the psychological benefit of being precise here. I'm not even sure it's possible to fool 'is' into thinking some other object is None, but people do a great job of convincing themselves of things like "datetime.time objects can't be false" all the time. Use the "is None" idiom and you know exactly what you're doing. This latter is close to "just style", of course. Steve
On Mar 07, 2014, at 02:40 AM, Stephen J. Turnbull wrote:
I also value the psychological benefit of being precise here. I'm not even sure it's possible to fool 'is' into thinking some other object is None, but people do a great job of convincing themselves of things like "datetime.time objects can't be false" all the time. Use the "is None" idiom and you know exactly what you're doing. This latter is close to "just style", of course.
Not just "you" (i.e. the author of the code), but everyone who comes after you and reads it. Being more explicit with identity tests against None assists future minions in understanding the intent of the code. When using the Socratic Method Of Code Reviews, I always ask about whether the author really expects the 'var' in "if var:" to be any truth-y value. The answer is usually "no", in which case I encourage tightening up the conditional. There are valid cases where a test is really trying to catch any truth-y or false-y value. I usually encourage a comment to clarify the author's intent in those cases. -Barry
On Thursday 06 March 2014 15:21:36 Barry Warsaw wrote:
On Mar 07, 2014, at 02:40 AM, Stephen J. Turnbull wrote:
I also value the psychological benefit of being precise here. I'm not even sure it's possible to fool 'is' into thinking some other object is None, but people do a great job of convincing themselves of things like "datetime.time objects can't be false" all the time. Use the "is None" idiom and you know exactly what you're doing. This latter is close to "just style", of course.
Not just "you" (i.e. the author of the code), but everyone who comes after you and reads it. Being more explicit with identity tests against None assists future minions in understanding the intent of the code. When using the Socratic Method Of Code Reviews, I always ask about whether the author really expects the 'var' in "if var:" to be any truth-y value. The answer is usually "no", in which case I encourage tightening up the conditional.
Wait -- you just argued for replacing if time_obj: not by if time_obj is not None: -- which is a more relaxed conditional -- but rather by if isinstance(time_obj, datetime.time): That seems quite un-pythonic, and *that* - from you - is quite unlikely, so I probably misunderstood. Do you mind clarifying? [A guess: The argument you intended to make was about an "if not var:" test changed to "if var is None:". It does not survive inversion of sense well.] Thanks, Shai.
On 03/06/2014 09:40 AM, Stephen J. Turnbull wrote:
Antoine Pitrou writes:
Le 06/03/2014 15:33, Stephen J. Turnbull a écrit :
never get fixed, and that "if var:" which is actually testing for identity with a false-y sentinel is still strongly discouraged.
Why "strongly discouraged"? This is more of a style issue than anything else?
No, it's not just a style issue. As Skip points out, it's a *semantic* issue as well.
Indeed, I have been bitten by using the lazy method of `if var` instead of `if var is not None`, and then been briefly puzzled as why my empty string | container | whatever that I passed in did not give me the correct results. I'm still all for having truthy midnights. :) -- ~Ethan~
On Thu, Mar 6, 2014 at 8:21 AM, M.-A. Lemburg <mal@egenix.com> wrote:
Are they all False? No, no they're not (unless your local timezone is UTC):
bool(utcmidnight) False bool(naivemidnight) False bool(localmidnight) True
Now this is a what I consider a valid argument for making a change.
FWIW, I would be +0 for making a change so that t.tzinfo is not None implies bool(t.timetz()) is True. My intuition goes like this. Given
from datetime import * t1 = datetime(2014, 1, 1) t2 = datetime(2014, 1, 1, 12) t3 = datetime(2014, 1, 1, tzinfo=timezone.utc)
Instance t1 has no time information, so t1.time() is false-y. Instance t2 has time information, so t2.time() is true-y. Instance t3 is such that t3.time() == t1.time(), but t3.timetz() has an additional information
t3.timetz() datetime.time(0, 0, tzinfo=datetime.timezone.utc) t3.time() datetime.time(0, 0)
So t3.timetz() is not the same object as returned by the constructor with no arguments - time(). This is reason enough to make it true-y. This also does not affect my use-case because t3.time() is still false-y in this logic:
[bool(t) for t in [t1.time(), t2.time(), t3.time(), time()]] [False, True, False, False]
Which is consistent with
t3.time() == t1.time() == time() True
On 03/06/2014 08:13 AM, Alexander Belopolsky wrote:
On Thu, Mar 6, 2014 at 8:21 AM, M.-A. Lemburg <mal@egenix.com <mailto:mal@egenix.com>> wrote:
> Are they all False? No, no they're not (unless your local timezone is UTC): > >>>> bool(utcmidnight) > False >>>> bool(naivemidnight) > False >>>> bool(localmidnight) > True
Now this is a what I consider a valid argument for making a change.
FWIW, I would be +0 for making a change so that t.tzinfo is not None implies bool(t.timetz()) is True.
My intuition goes like this. Given
from datetime import * t1 = datetime(2014, 1, 1) t2 = datetime(2014, 1, 1, 12) t3 = datetime(2014, 1, 1, tzinfo=timezone.utc)
Instance t1 has no time information, so t1.time() is false-y.
If datetime actually worked like that I would agree with you; but it doesn't -- it says you have the time of midnight on 2014, 1, 1, not that you don't have a time at all. -- ~Ethan~
On Mar 6, 2014, at 4:08, Nick Coghlan <ncoghlan@gmail.com> wrote:
if x not in (naivemidnight, utcmidnight): # This is the current bool(x) .... if x != naivemidnight: # This has no current shorthand .... if x != utcmidnight: # This has no current shorthand .... if x != localmidnight: # This has no current shorthand ....
This is a great argument. The problem here isn't that we use midnight as 0, but that naive and aware times are the same type and we use UTC midnight as 0 for aware types. The other languages/libraries/apps that treat midnight as 0 are generally either naive-only, or much more explicit about awareness. Since that isn't going to change in Python, maybe it would be better to provide these values (or functions that return them) as names in the module, and explicitly encourage testing against them in place of bool testing, and define bool testing exactly like this, whether or not it's deprecated?
On Thu, Mar 6, 2014 at 12:08 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Now, let's consider the time with the *best* possible claim to being false: timestamp zero. How does that behave?
[... snipped ...]
So, unless I happen to live in UTC, it's highly unlikely that I'm going to infer from Python's *behaviour* that datetime.time() (unlike datetime.date() and datetime.datetime()) belong in the "number" category, rather than the "arbitrary object" category.
This one's even *more* fun if you live in the UK. I live on the Greenwich Meridian (and I mean that quite literally: it goes through the middle of my living room), and since the epoch was in January, well away from British Summer Time, I shouldn't see any of those pesky timezone-related problems that those poor underprivileged people living in non-UTC timezones see. Right? Here's Python 2.7 on my Mac laptop, whose timezone is set to London time:
import datetime as dt bool(dt.datetime.fromtimestamp(0).time()) True
And on Windows (again with timezone set to the UK):
import datetime as dt bool(dt.datetime.fromtimestamp(0).time()) False
Wait, what? It turns out that in January 1970, the UK government was in the middle of an experimental change to a year-round GMT+1 timezone. Some operating systems seem to be aware of that fact; some aren't. Count me in the bool(time(0)) should be True camp, by the way. -- Mark
On 6 March 2014 10:33, M.-A. Lemburg <mal@egenix.com> wrote:
It is not a coincidence, IMO, that the three persons arguing that the current behaviour is reasonable are implementors of datetime modules: you, Tim and Alexander. All other people find the behaviour baffling.
I'm not so sure. Of course, people who have been doing date/time stuff will jump into discussions that deal with them, but the same argument triggering this thread could have applied to other Python types and you would then have a different group of people argue for plausibility of the choice.
I had promised to stay out of this discussion as I no longer thing it adds much value (note that the bug has been reopened long since) and I was getting tired of having people decide that I'd said something I didn't mean. But just to add a perspective from someone who *doesn't* develop a datetime module, I encounter "special" midnight values very often. Databases (at least Oracle, and I believe others) typically store date values ("Thursday 6th March 2014") as a timestamp value with midnight as a time part. So checking the time part of a datetime value for midnight as a "special, means omitted" value is not uncommon in that domain. Paul
On 06/03/2014 10:57, Paul Moore wrote:
I had promised to stay out of this discussion as I no longer thing it adds much value (note that the bug has been reopened long since) and I was getting tired of having people decide that I'd said something I didn't mean.
But just to add a perspective from someone who *doesn't* develop a datetime module, I encounter "special" midnight values very often. Databases (at least Oracle, and I believe others) typically store date values ("Thursday 6th March 2014") as a timestamp value with midnight as a time part. So checking the time part of a datetime value for midnight as a "special, means omitted" value is not uncommon in that domain.
Paul
You're joking, right? How do you distinguish "omitted" from "provided, equals midnight" ? Rob Cliffe
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----- No virus found in this message. Checked by AVG - www.avg.com Version: 2012.0.2247 / Virus Database: 3705/6655 - Release Date: 03/05/14
On 6 March 2014 11:36, Rob Cliffe <rob.cliffe@btinternet.com> wrote:
You're joking, right?
No. You just sent 2 "you're joking" messages. You're joking, right? Any time anyone presents a use case or proposal that contradicts what you want it must be a joke? I really am sick of being misinterprested and misrepresented on this thread.
How do you distinguish "omitted" from "provided, equals midnight" ?
I will not explain all the details of how database applications process dates here. All I offered was a perspective that some application domains, in certain cases, see "midnight" as having a special meaning distinct from the specific point in time. I did so because other people's views were being characterised as irrelevant because they developed datetime modules (as if that made them somehow *less* qualified to have an opinion) and I sympathised with them over the hostile reception they were getting. I didn't comment on whether that meant the Python behaviour was good or bad, or whether the practice of truth-testing time values was good or not. Feel free to investigate the details if you care, or ignore my post if you don't. But I'm not trolling, nor do I appreciate the implication that I am. Paul.
Antoine Pitrou <solipsis@...> writes:
Le 06/03/2014 09:35, M.-A. Lemburg a écrit :
Just in case you're looking for a relevant use case:
Having bool(time(0,0,0)) allows you to quickly check whether you are dealing with a datetime value with (non-trivial) time part or not.
This sounds like the kind of shortcut that only an expert would know to take, not something that's actually a good design decision. It's as though pathlib.Path('') evaluated to false for some obscure use case that I cared about.
On the other hand I would not dare to assume it's True without checking.
It is not a coincidence, IMO, that the three persons arguing that the current behaviour is reasonable are implementors of datetime modules: you, Tim and Alexander. All other people find the behaviour baffling.
Not all. :) I think viewing midnight as (unix_time % 86400) is quite reasonable. Stefan Krah
On Mar 6, 2014, at 3:13, Stefan Krah <stefan@bytereef.org> wrote:
Antoine Pitrou <solipsis@...> writes:
It is not a coincidence, IMO, that the three persons arguing that the current behaviour is reasonable are implementors of datetime modules: you, Tim and Alexander. All other people find the behaviour baffling.
Not all. :) I think viewing midnight as (unix_time % 86400) is quite reasonable.
I agree with Stefan here. Treating midnight as zero is not at all baffling. There's a reason modular arithmetic is taught to kids as "clock arithmetic", after all. And it's not unheard of for other languages/libraries/apps to treat midnight as a 0, or even to use it as a null time. Someone else already mentioned that many RDBMS's don't have a separate date type and store dates as datetimes with midnight time. IEEE datetimes also do this--leaving off any trailing component is equivalent to having 0's there. And this isn't some experts-only thing. Novices regularly ask why we need the date class when a datetime with 0 time does the same thing--often because that's what their first language did, but sometimes just as an a priori expectation. On the other hand, the alternative isn't baffling either. Unlike, say, Oracle, Python has separate types for date, time, and datetime, so we don't need to use 0 for "n/a", and it has tz-aware values for all three types, making midnight much less special, and it doesn't have modular arithmetic on times (and timedeltas), etc. So honestly, if handed the Python time class, I wouldn't know whether to expect midnight to be treated as 0 (and therefore falsey) or not, and would have to read the docs rather than guessing. When a choice really is arbitrary like this, "it does what it's documented to do" is the only notion of correct that makes sense. (As a disclaimer, I have implemented datetime libraries for C and C++, but not for Python.)
Andrew Barnert wrote:
I agree with Stefan here. Treating midnight as zero is not at all baffling. There's a reason modular arithmetic is taught to kids as "clock arithmetic",
I still say this is confusing times with timedeltas. It makes no conceptual sense to add two times of day together. You can only make it work if you abuse one type to mean two different things. We keep datetimes and timedeltas clearly separated. Why shouldn't we do the same for times of day? -- Greg
[Antoine Pitrou <solipsis@pitrou.net>]
... It is not a coincidence, IMO, that the three persons arguing that the current behaviour is reasonable are implementors of datetime modules: you, Tim and Alexander. All other people find the behaviour baffling.
The behavior of "aware" time objects is indeed bizarre, but then aware time objects are bizarre beasts from the start (a time zone attached to a mere time-of-day, divorced from date info, doesn't make any real sense to me). The reasons I already explained (possible extensions to naive time's repertoire, in the areas of type conversions and arithmetic) account for why bool(time()) returns False. _As things turned out_ no such extensions were made, so we're left with a foundation sticking out of the ground with nothing built on top of it. Yawn ;-)
On 3/6/2014 4:52 AM, Antoine Pitrou wrote:
It is not a coincidence, IMO, that the three persons arguing that the current behaviour is reasonable are implementors of datetime modules: you, Tim and Alexander. All other people find the behaviour baffling.
Wrong. I don't. What I find baffling and annoying is the endlessly repeated false claim that there is nothing special about midnight, as if a thousands repetitions will change the fact. Ditto for the claim that there is nothing problematical about using 'a' as a substitute for 'a is not None'. Surely you know that the subset of people who pay attention to an endless drumbeat thread like this is severely biased. -- Terry Jan Reedy
Terry Reedy wrote:
What I find baffling and annoying is the endlessly repeated false claim that there is nothing special about midnight,
I think what people are really saying is that it's not special *enough* to be worth breaking people's intuition about the truthiness of structured data types in Python. Anyhow, it seems that the original motivation for the current truthiness behaviour isn't really "midnight is special", but rather "zero hours is special". In other words, if you turn your head sideways and view a time() object not as a time of day, but as a difference between two times of day, then a time() representing midnight morphs into a timedelta of zero hours, and it kind of sort of makes sense to treat that as false. If that's the intended interpretation of a time() object, the docs do a poor job of conveying it. A time() object is described as representing "a (local) time of day", not "an amount of time since midnight". The "local" even suggests that, if some midnight is to special, it would be *local* midnight -- but this is not the case for time() objects with a timezone. It seems to me that those claiming "midnight is special" are inventing a justification after the fact -- one that is not supported by the actual behaviour. Altogether, the whole thing seems to be a mess. -- Greg
On 03/06/2014 04:08 PM, Tim Peters wrote:
[Greg Ewing]
... Altogether, the whole thing seems to be a mess.
Although a mess that fits in a thimble that successfully hid in a dark corner unnoticed for a decade ;-)
If the decade is now, it didn't go that long. I discovered this irritation within a year of learning Python, and at least three years ago brought it up on one of the mailing lists. I ended up making my own Date, Time, and DateTime classes. -- ~Ethan~
Greg Ewing writes:
It seems to me that those claiming "midnight is special" are inventing a justification after the fact -- one that is not supported by the actual behaviour.
Altogether, the whole thing seems to be a mess.
Mirroring the real world, no?
Le 06/03/2014 03:15, Tim Peters a écrit :
[Donald Stufft]
When the documented behavior is both nonsensical and the cause of hard to debug bugs that is a pretty compelling use case to me, unless you actively enjoy being user hostile.
Breaking code that currently works is as hostile as hostile gets. Where is the evidence that no code relies on the current behavior? For that matter, where is the evidence that the current behavior is a significant cause of bugs? I know I've used "if some_time_object:", but it did exactly what I expected it to do.
The current behaviour being highly unintuitive is a likely source of bugs, IMO. Regards Antoine.
On 06/03/2014 02:15, Tim Peters wrote:
[Donald Stufft]
When the documented behavior is both nonsensical and the cause of hard to debug bugs that is a pretty compelling use case to me, unless you actively enjoy being user hostile. Breaking code that currently works is as hostile as hostile gets. Where is the evidence that no code relies on the current behavior? For that matter, where is the evidence that the current behavior is a significant cause of bugs? I know I've used "if some_time_object:", but it did exactly what I expected it to do. Well, let's start a survey. So far we have:
Instances of existing code that would be broken by the change: 0 Instances of code where the unexpected behaviour caused a bug: 1 (the OP's code). I invite everyone to add instances that they know about.:-) Rob Cliffe
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----- No virus found in this message. Checked by AVG - www.avg.com Version: 2012.0.2247 / Virus Database: 3705/6655 - Release Date: 03/05/14
Barry Warsaw wrote:
On Mar 06, 2014, at 11:56 AM, Rob Cliffe wrote:
Instances of existing code that would be broken by the change: 0
You can't know this because you can't audit all the Python code in the world.
I think he means instances that have been pointed out so far in this discussion. -- Greg
On 06/03/2014 23:51, Greg Ewing wrote:
Barry Warsaw wrote:
On Mar 06, 2014, at 11:56 AM, Rob Cliffe wrote:
Instances of existing code that would be broken by the change: 0
You can't know this because you can't audit all the Python code in the world.
I think he means instances that have been pointed out so far in this discussion.
Quite. If you read my post, I was initialising totals. I invited the universe to add to them.
Greg Ewing writes:
Barry Warsaw wrote:
On Mar 06, 2014, at 11:56 AM, Rob Cliffe wrote:
Instances of existing code that would be broken by the change: 0
You can't know this because you can't audit all the Python code in the world.
I think he means instances that have been pointed out so far in this discussion.
Well, either way it's a pretty careless claim. Alexander and Tim both have stated that they have used "if time:" to test for a zero time (per the documentation) in actual applications. I gather from the fact that Tim is "-0" on the change that his code was never widely distributed, but I don't know about Alexander's. Probably not, as he didn't post a URL, either. AFAIK the code doesn't have to be distributed for gratuitous code breakage to be considered a Very Bad Thing, though. One of Alexander's use cases (changing date in a simulation at midnight using naive times) is particularly salient here. Unlike any of the other cases described so far by either side, it's clear that his simulation would be completely broken by the proposed incompatible change in behavior, as the date would never change. (Granted, it's not clear to me whether he actually implemented this in production, but I also can't say from what I've read that he didn't.)
On 07/03/2014 03:58, Stephen J. Turnbull wrote:
Greg Ewing writes:
Barry Warsaw wrote:
On Mar 06, 2014, at 11:56 AM, Rob Cliffe wrote:
Instances of existing code that would be broken by the change: 0
You can't know this because you can't audit all the Python code in the world.
I think he means instances that have been pointed out so far in this discussion.
Well, either way it's a pretty careless claim.
Please read what I actually said: Let's _start_ a survey.
If I had to do it over again I would *definitely* never make a time value "falsy". The datetime module was conceived long ago, when the dangers of falsy objects that weren't clearly trying to be "like numbers" or "like collections" weren't so clear. Today I would argue that times aren't enough "like numbers" to qualify. The only question left in my mind is how safe it is to change the current behavior, given that it's been documented and implemented for over a decade. If this was for Python 3.0 (or even 3.1) I would definitely vote for fixing it, damn the torpedoes. But given that we're talking about 3.5, I'm more hesitant. It's also not something for which a deprecation warning would work well. (When would it be issued? Whenever bool(<time>) is called? Or only when it's about to return False?) Still, my intuition tells me that it's pretty much a nuisance feature and few people have actually relied on it, so I'd be okay with fixing (and breaking!) this in 3.5, perhaps after a thorough search for how much the feature is actually relied upon and how legitimate or important those uses are. A search should *also* try to unearth code that is technically *broken* by the current behavior. I would expect there to be quite a lot of the latter kind, and very few of the former. Fearing I have said nothing new, -- --Guido van Rossum (python.org/~guido)
On 8 Mar 2014 03:19, "Guido van Rossum" <guido@python.org> wrote:
If I had to do it over again I would *definitely* never make a time value
"falsy". The datetime module was conceived long ago, when the dangers of falsy objects that weren't clearly trying to be "like numbers" or "like collections" weren't so clear. Today I would argue that times aren't enough "like numbers" to qualify.
The only question left in my mind is how safe it is to change the current
behavior, given that it's been documented and implemented for over a decade. If this was for Python 3.0 (or even 3.1) I would definitely vote for fixing it, damn the torpedoes. But given that we're talking about 3.5, I'm more hesitant. It's also not something for which a deprecation warning would work well. (When would it be issued? Whenever bool(<time>) is called? Or only when it's about to return False?) I was proposing a deprecation warning when it would be about to return False, advising the use of an explicit comparison instead. My rationale was that people that actually rely on it are at least somewhat likely to be testing it and would trigger the warning and have a chance to change their code. However, on reflection, just changing with a note in the porting guide would actually have a similar consequence. Regards, Nick.
Guido van Rossum writes:
It's also not something for which a deprecation warning would work well. (When would it be issued? Whenever bool(<time>) is called?
Yes! Yes! Yes! Just joking -- that's got to be the wrong thing to do, because it would warn on precisely the construct (None as sentinel for "uninitialized") that's the only reason anybody wants this.
Or only when it's about to return False?)
That seems reasonable to me. People who are testing for this are presumably *constructing* instances of midnight, either with a crontab of "* * * * *" or in a simulation -- they'll hit it frequently. The other case that seems plausible is using a zero time as a sentinel for "uninitialized" or "unspecified", and that *must* get a warning.
Fearing I have said nothing new,
IIRC, you're the only person to have mentioned the pragmatic issues about the DeprecationWarning.
On Mar 7, 2014, at 12:17 PM, Guido van Rossum <guido@python.org> wrote:
If I had to do it over again I would *definitely* never make a time value "falsy". The datetime module was conceived long ago, when the dangers of falsy objects that weren't clearly trying to be "like numbers" or "like collections" weren't so clear. Today I would argue that times aren't enough "like numbers" to qualify.
The only question left in my mind is how safe it is to change the current behavior, given that it's been documented and implemented for over a decade. If this was for Python 3.0 (or even 3.1) I would definitely vote for fixing it, damn the torpedoes. But given that we're talking about 3.5, I'm more hesitant. It's also not something for which a deprecation warning would work well. (When would it be issued? Whenever bool(<time>) is called? Or only when it's about to return False?)
Still, my intuition tells me that it's pretty much a nuisance feature and few people have actually relied on it, so I'd be okay with fixing (and breaking!) this in 3.5, perhaps after a thorough search for how much the feature is actually relied upon and how legitimate or important those uses are. A search should *also* try to unearth code that is technically *broken* by the current behavior. I would expect there to be quite a lot of the latter kind, and very few of the former.
Hrm, I’m not actually sure how to even achieve such a search. Due to Python’s dynamic nature it’s pretty hard to determine what type is being used in a boolean expression, if that data is even available (It could be coming from a database or some other data source). Do you have any idea how to do such a search?
Fearing I have said nothing new,
-- --Guido van Rossum (python.org/~guido) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On Fri, Mar 7, 2014 at 4:48 PM, Donald Stufft <donald@stufft.io> wrote:
On Mar 7, 2014, at 12:17 PM, Guido van Rossum <guido@python.org> wrote:
If I had to do it over again I would *definitely* never make a time value "falsy". The datetime module was conceived long ago, when the dangers of falsy objects that weren't clearly trying to be "like numbers" or "like collections" weren't so clear. Today I would argue that times aren't enough "like numbers" to qualify.
The only question left in my mind is how safe it is to change the current behavior, given that it's been documented and implemented for over a decade. If this was for Python 3.0 (or even 3.1) I would definitely vote for fixing it, damn the torpedoes. But given that we're talking about 3.5, I'm more hesitant. It's also not something for which a deprecation warning would work well. (When would it be issued? Whenever bool(<time>) is called? Or only when it's about to return False?)
Still, my intuition tells me that it's pretty much a nuisance feature and few people have actually relied on it, so I'd be okay with fixing (and breaking!) this in 3.5, perhaps after a thorough search for how much the feature is actually relied upon and how legitimate or important those uses are. A search should *also* try to unearth code that is technically *broken* by the current behavior. I would expect there to be quite a lot of the latter kind, and very few of the former.
Hrm, I'm not actually sure how to even achieve such a search. Due to Python's dynamic nature it's pretty hard to determine what type is being used in a boolean expression, if that data is even available (It could be coming from a database or some other data source).
Do you have any idea how to do such a search?
I know someone at Sourcegraph who thinks he knows how to modify Sourcegraph to do it. -- --Guido van Rossum (python.org/~guido)
On Mar 5, 2014, at 17:34, Tim Peters <tim.peters@gmail.com> wrote:
"I am altering the deal. Pray I don't alter it any further." There's a reason Darth Vader wasn't proclaimed Python's BDFL ;-)
I always assumed it was because he never contributed even a single patch.
Paul Moore wrote:
"""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."""
Aaaaargh... I didn't know this had since been documented. Wy opinion still stands -- documenting this was the wrong thing to do. I hope it can still be fixed. -- Greg
[Paul Moore]
"""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."""
[Greg Ewing]
Aaaaargh... I didn't know this had since been documented.
Wy opinion still stands -- documenting this was the wrong thing to do. I hope it can still be fixed.
The quoted text has always been there, from datetime's initial release: http://docs.python.org/2.3/lib/datetime-time.html That was a bit over 9 years ago.
On 05/03/2014 15:19, Shai Berger wrote:
On Wednesday 05 March 2014 17:01:33 you wrote:
On 5 March 2014 13:53, Shai Berger <shai@platonix.com> wrote:
Again, I'm not saying the current behaviour is sensible, but I doubt the work to fix it will benefit anyone in practice.
[...]
In any case, if we all agree that http://bugs.python.org/issue13936 is a valid problem
We don't. Can we agree that it's not a bug and abandon this fruitless discussion?
What, other than "bug", do you call behavior that isn't sensible?
Documented behaviour. As opposed to insanity: doing the same thing over and over again and expecting different results. -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
On Wed, Mar 5, 2014 at 9:01 AM, Paul Moore <p.f.moore@gmail.com> wrote:
On 5 March 2014 13:53, Shai Berger <shai@platonix.com> wrote:
We don't. Can we agree that it's not a bug and abandon this fruitless discussion.
Please don't abandon this discussion. The behavior we're discussing is clearly illogical and useless. If we simply removed the __nonzero__ behavior, and it did what user-created types do, then it would perform as I would expect it to. Consider also that this time has no special significance, unlike the special cases of 0, (), {}, etc. This is true also for any arbitrarily zeroed unit, such as temperature (C or F), and including datetime, date, and time. Even zero in those cases (however that is defined) is simply an arbitrary point, and not the same as a nonzero value. This is completely illogical behavior, and, IMO, is a bug. Ryan
Oscar Benjamin wrote:
The question is surely whether the issue is worth a backwards compatibility break not whether the current behaviour is a good idea (it clearly isn't).
The currrent behaviour is sufficiently useless that it seems unlikely there would be much code out there relying on it. -- Greg
On Wed, Mar 5, 2014 at 4:44 AM, Paul Moore <p.f.moore@gmail.com> wrote:
if event.start_time: # stuff
unexpectedly fails if start_time is midnight.
Yeah, I was classing that as "application bug" and it's easy to fix with an "is not None".
+1, +1, +1. This is nothing more than an application bug. Issue 13936 should not be reopened. This is a very common mistake when None is used as a sentinel value (though people normally get away with it), especially by people coming from languages having NULL pointers which shall not be named. After all, "None" and "NULL" both start with the letter "N". Surely, they must represent the same fundamental concept, right? <wink> Let's change the problem slightly. Instead of time objects, let's consider the domain of possible values to be integers. Clearly, None is not a member of that set. You could thus use it as a sentinel. So, tell me, OP. Would this usage be correct? if event.some_id: # stuff I would argue, "no." I would not argue that 0 should not compare as False, however. I would fix my code: if event.some_id is not None: # stuff In contexts where None is an element of the set of possible values (and thus can't be used as a sentinel value itself), it's common to explicitly create such a (unique) value, e.g.: SENTINEL = ["xyz"] # or [None] or {} or set([sys]) or ..., but not () or 1 or True! When you check for the sentinel you have to be explicit (and you MUST check for it using the "is" operator, to drag in another recent thread): if something is SENTINEL: mix_the_special_sauce() Using None as a sentinel value is no different, it just happens to a) already exist, and b) be unique, saving you an exceedingly small amount of typing. If you were trying to be terribly clever, there are lots of other values in Python (at least in CPython) which happen to be unique (the empty tuple, small integers, True and False, ...), and could thus theoretically be used as a sentinel. Someone would eventually slap your wrist for that sort of thing though (one would hope). Two final points. 1. If you wanted to modify the language (or the standard library) in some way, perhaps the more theoretically reasonable request would be to ask that None not be usable in a boolean context. Making that change would certainly break a lot of code though, so it's too late to do that. 2. The more I think about it, the more I think None should not be used as a sentinel. Skip
On 5 March 2014 15:34, Skip Montanaro <skip@pobox.com> wrote:
On Wed, Mar 5, 2014 at 4:44 AM, Paul Moore <p.f.moore@gmail.com> wrote:
if event.start_time: # stuff
unexpectedly fails if start_time is midnight.
Yeah, I was classing that as "application bug" and it's easy to fix with an "is not None".
+1, +1, +1. This is nothing more than an application bug. Issue 13936 should not be reopened.
This is a very common mistake when None is used as a sentinel value (though people normally get away with it), especially by people coming from languages having NULL pointers which shall not be named. After all, "None" and "NULL" both start with the letter "N". Surely, they must represent the same fundamental concept, right? <wink>
Let's change the problem slightly. Instead of time objects, let's consider the domain of possible values to be integers. Clearly, None is not a member of that set. You could thus use it as a sentinel. So, tell me, OP. Would this usage be correct?
if event.some_id: # stuff
I would argue, "no." I would not argue that 0 should not compare as False, however.
You come up with an instance where 0 clearly does not apply as something to incur a different branch, which is very apparent in just reading the line, so very easy to spot as a mistake. But this 0 isn't a real zero. It's just a unique identifier that happens to be implemented as an integer(because it's convenient for your database, iterators and whatnot.) Let me show you an actual zero: if event.num_attendants: prepare_cake() else: cancel_event() When no one is coming to your party, is is clearly a different condition than if any number of people are coming to your event. When you read "if num_attendants", you can clearly tell this is going to do something depending on if people are coming or not. Let's read this line: if not event.date: How would you read it, knowing nothing about date objects? I would read it to mean "has no date been set?" and would naturally continue the code as such: if event.date: schedule_event() I cannot fathom one example where it could read as "does the party start at midnight?". Can you?
On 5 March 2014 15:08, Yann Kaiser <kaiser.yann@gmail.com> wrote:
Let me show you an actual zero:
if event.num_attendants: prepare_cake() else: cancel_event()
When no one is coming to your party, is is clearly a different condition than if any number of people are coming to your event. When you read "if num_attendants", you can clearly tell this is going to do something depending on if people are coming or not.
I'm sorry, are you really trying to say that the above code is better than if event.num_attendants != 0: prepare_cake() else: cancel_event() ? (Personally, I'd actually prefer something like "if event.num_attendants > 0" or switch the order of clauses and test for being equal to 0, but that's a minor issue. The major point is I'd prefer any of these to your version). Paul
On Wed, Mar 5, 2014 at 9:42 AM, Paul Moore <p.f.moore@gmail.com> wrote:
On 5 March 2014 15:08, Yann Kaiser <kaiser.yann@gmail.com> wrote:
Let me show you an actual zero:
if event.num_attendants: prepare_cake() else: cancel_event()
When no one is coming to your party, is is clearly a different condition than if any number of people are coming to your event. When you read "if num_attendants", you can clearly tell this is going to do something depending on if people are coming or not.
I'm sorry, are you really trying to say that the above code is better than
if event.num_attendants != 0: prepare_cake() else: cancel_event()
?
(Personally, I'd actually prefer something like "if event.num_attendants > 0" or switch the order of clauses and test for being equal to 0, but that's a minor issue. The major point is I'd prefer any of these to your version).
I suspect you'd like the whole idea of empty collections and zeroes evaluating false to go away, to force this kind of style (one right way to do it and all). Some languages do that. Python isn't one of them. I can see both sides, but I like how Python can use empty lists and zeros as false values, and I often (but not always) write code that takes advantage of it.
On 5 March 2014 15:55, Ryan Hiebert <ryan@ryanhiebert.com> wrote:
I suspect you'd like the whole idea of empty collections and zeroes evaluating false to go away, to force this kind of style (one right way to do it and all). Some languages do that. Python isn't one of them.
Not really. I just found the particular example given to be confusing. I tend to use boolean tests when I think of something as a yes/no type of thing, and the variable name implies it. If the example had referred to "event.attendees" I would probably have been fine with the check as written. The attribute in that case could be any one of a boolean flag, a count, or a list of attendees. That's actually a reasonably plausible way of writing the test to allow for later changes to the event class API. The point here is that there's more to whether code is good than just a black and white "does it work" question. But we're way off topic now - I don't even have any assurance that the OP's code is trying to test for a None sentinel and hitting a bug because not all time values evaluate as True (but if that's *not* his code, he really should have pointed that out by now!) All I can say is that *if* that is what his code is, then I think that rewriting it with an explicit test is clearer, and I'm not convinced by his assertion that "fixing" Python is a better solution.
I can see both sides, but I like how Python can use empty lists and zeros as false values, and I often (but not always) write code that takes advantage of it.
Agreed, but that doesn't mean that code taking advantage of this is never obfuscated or hard to maintain. Paul. PS One final point, and I'll say this one last time and then shut up. I have no objection to making Python better. Nor do I insist that backward compatibility should never be broken. Nor do I wish Python were Java. Nor do I discount the issue of hard to find bugs. But equally, I don't think Python is perfect, nor do I think that this behaviour is ideal. I also don't think that the core developers have infinite time, nor do I think that every wart in Python needs to be fixed. And I trust the core developers to make the right judgements most of the time. All anyone should infer from this discussion is that I don't think that reopening the bug report referred to by the OP is productive. (You may also assume from the fact that I will drop out of this discussion now, that I no longer think that this thread is particularly productive :-()
On Wed, Mar 5, 2014 at 10:24 AM, Paul Moore <p.f.moore@gmail.com> wrote:
PS One final point, and I'll say this one last time and then shut up. I have no objection to making Python better. Nor do I insist that backward compatibility should never be broken. Nor do I wish Python were Java. Nor do I discount the issue of hard to find bugs. But equally, I don't think Python is perfect, nor do I think that this behaviour is ideal. I also don't think that the core developers have infinite time, nor do I think that every wart in Python needs to be fixed. And I trust the core developers to make the right judgements most of the time. All anyone should infer from this discussion is that I don't think that reopening the bug report referred to by the OP is productive. (You may also assume from the fact that I will drop out of this discussion now, that I no longer think that this thread is particularly productive :-()
Thanks for bringing this up. Even assuming that this was accepted as a bug, it's an entirely separate discussion to decide whether worth fixing or if it's worth the dev's time. I make no assertion on that, as that's their time and not mine. I hold the developers with highest regard, even in my disagreement. Python is my language of choice because of the effort of these folks, and I thank them. This seems like it could be a simple enough bug that were it allowed, it would be a fix that even someone as inexperienced with Python's internals as myself could fix. If that's not true and the issue is that it's simply too much work to change, then it should be marked "wontfix" rather than "invalid", and that's a decision that I would not contest (at least not without a working patch). I, like you, suspect that I will be abandoning further involvement, as simply arguing won't change people's minds.
On 05/03/2014 16:24, Paul Moore wrote:
I can see both sides, but I like how Python can use empty lists and zeros as false values, and I often (but not always) write code that takes advantage of it. Agreed, but that doesn't mean that code taking advantage of this is never obfuscated or hard to maintain.
Paul. I'm with Shai and Ryan on this one. There's no such thing as a perfect language, but that doesn't mean we shouldn't strive towards it.
PS One final point, and I'll say this one last time and then shut up. I have no objection to making Python better. Nor do I insist that backward compatibility should never be broken. Nor do I wish Python were Java. Nor do I discount the issue of hard to find bugs. But equally, I don't think Python is perfect, nor do I think that this behaviour is ideal. I also don't think that the core developers have infinite time, nor do I think that every wart in Python needs to be fixed. Good. In other words, each wart should be considered on its merits, and some may be worth fixing. Changes have been made to Python where the backward compatibility issue is far greater. In this case any code that would be broken by the change is already flawed, if not broken. And I find it hard to imagine that the development effort in making this change is anything other than minimal, _compared with other comparable changes_ (I'm not trying to minimise the undoubted effort required to make _any_ change no matter how small).
Rob Cliffe
And I trust the core developers to make the right judgements most of the time. All anyone should infer from this discussion is that I don't think that reopening the bug report referred to by the OP is productive. (You may also assume from the fact that I will drop out of this discussion now, that I no longer think that this thread is particularly productive :-() _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----- No virus found in this message. Checked by AVG - www.avg.com Version: 2012.0.2247 / Virus Database: 3705/6652 - Release Date: 03/04/14
On Thu, Mar 6, 2014 at 2:55 AM, Ryan Hiebert <ryan@ryanhiebert.com> wrote:
I suspect you'd like the whole idea of empty collections and zeroes evaluating false to go away, to force this kind of style (one right way to do it and all). Some languages do that. Python isn't one of them.
I can see both sides, but I like how Python can use empty lists and zeros as false values, and I often (but not always) write code that takes advantage of it.
The broad concept is: A thing is true, a non-thing is false. The question is, though, which things are actually non-things? Since Python doesn't have a concept of not storing anything into a variable, there has to be a thing that represents the state of not being a thing, so None should be false. Python's made the choice that an empty string is a non-thing, but other languages choose instead to have a separate "Nil" value and all strings are true. Same with lists, tuples, etc. Pike's choice is that the integer 0 is false, and anything else is true, so you distinguish between having a string and not-having a string, rather than between having a non-empty string and having an empty string. Simple design choice, and one that each language follows consistently. But if you truly want to abolish the confusion, you want REXX. In REXX, 0 is false and 1 is true, and everything else is error 34, "Logical value not 0 or 1". Is that really an improvement? Not in my opinion. ChrisA
On 03/05/2014 07:42 AM, Paul Moore wrote:
On 5 March 2014 15:08, Yann Kaiser <kaiser.yann@gmail.com> wrote:
Let me show you an actual zero:
if event.num_attendants: prepare_cake() else: cancel_event()
When no one is coming to your party, is is clearly a different condition than if any number of people are coming to your event. When you read "if num_attendants", you can clearly tell this is going to do something depending on if people are coming or not.
I'm sorry, are you really trying to say that the above code is better than
if event.num_attendants != 0: prepare_cake() else: cancel_event()
?
Yes. This is Python. blah blah blah != 0 is unnecessary boiler-plate [1]. -- ~Ethan~ [1] In most cases. I have used `== 0` or `!= 0` when it makes the code clearer, but that's pretty rare. -- ~Ethan~
The obvious solution is just to fix the Python if statement to remove implicit bool coercion. So they would all evaluate to if isinstance (x, bool) and x. Problem solved. On Mar 6, 2014 5:27 PM, "Ethan Furman" <ethan@stoneleaf.us> wrote:
On 03/05/2014 07:42 AM, Paul Moore wrote:
On 5 March 2014 15:08, Yann Kaiser <kaiser.yann@gmail.com> wrote:
Let me show you an actual zero:
if event.num_attendants: prepare_cake() else: cancel_event()
When no one is coming to your party, is is clearly a different condition than if any number of people are coming to your event. When you read "if num_attendants", you can clearly tell this is going to do something depending on if people are coming or not.
I'm sorry, are you really trying to say that the above code is better than
if event.num_attendants != 0: prepare_cake() else: cancel_event()
?
Yes. This is Python. blah blah blah != 0 is unnecessary boiler-plate [1].
-- ~Ethan~
[1] In most cases. I have used `== 0` or `!= 0` when it makes the code clearer, but that's pretty rare.
-- ~Ethan~ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Ethan Furman writes:
On 03/05/2014 07:42 AM, Paul Moore wrote:
On 5 March 2014 15:08, Yann Kaiser <kaiser.yann@gmail.com> wrote:
Let me show you an actual zero:
if event.num_attendants: prepare_cake() else: cancel_event()
I'm sorry, are you really trying to say that the above code is better than
if event.num_attendants != 0: prepare_cake() else: cancel_event()
?
Yes. This is Python. blah blah blah != 0 is unnecessary boiler-plate.
I wouldn't call that *better*, just a different style. I agree I prefer "if blah.count:" to "if blah.count != 0:", but I might very well write "if blah.count > 0:". I don't see anything "wrong" with using an explicit comparison, but I don't have a problem reading the abbreviated style in this situation.[1] Personally I would *always* use the "implied boolean" style for emptiness tests (I'm an old Lisper), but I might very well use explicit comparisons against zero. For example, I would be likely to write the party example with a reversed test (to keep 'if' and 'else' close together): if event.num_attendants == 0: # nobody's coming cancel_event() else: # several-pages-long suite of preparations prepare_cake() and I think that looks better than if not event.num_attendants: # it's a count, not a boolean # etc But actually I'd probably design the event class differently, and write it as: if not event.list_of_attendants: # attendance list is empty # etc or perhaps if event.list_of_attendants: # attendance list is non-empty # reverse the suites (depending on the phase of the moon and whether the trout are running in Esopus Creek to choose between the last two :-). Footnotes: [1] I do have a real issue with the "if x:" to test for a sentinel meaning "uninitialized" that happens to evaluate to false. There's objectively a probability that it would pass an error that would be caught if the "is None" form were used, but that probability is admittedly low. Tastes may differ regarding "importance" here.
Yann Kaiser writes:
if event.date: schedule_event()
I cannot fathom one example where it could read as "does the party start at midnight?". Can you?
No, because it obviously reads as "... WTF??!?"[1] Of course, that's because I've imbibed of the "Python is not C or C++" Kool-Aid, but I just don't see why we should encourage C idioms in Python when there are plenty of other cases where use of a bare expression rather "... is None" can bite. I find Skip Montanaro's implied issue Please reconsider the Boolean evaluation of None far more plausible.<wink/> Footnotes: [1] if event.date: print("Happy New Year/Century/Millennium!")
On Wednesday 05 March 2014 16:34:16 Skip Montanaro wrote:
Let's change the problem slightly. Instead of time objects, let's consider the domain of possible values to be integers. Clearly, None is not a member of that set. You could thus use it as a sentinel. So, tell me, OP. Would this usage be correct?
if event.some_id: # stuff
I would argue, "no." I would not argue that 0 should not compare as False, however.
What Yann said. Also, when opening this thread, I argued that 0 is special, but midnight is not -- which you completely ignore. Thanks, Shai.
On Wed, Mar 5, 2014 at 9:15 AM, Shai Berger <shai@platonix.com> wrote:
Also, when opening this thread, I argued that 0 is special, but midnight is not -- which you completely ignore.
Zero is not different, at least when considered as an element of a set (of integers, in my example). What's important is that we have a set of values (either time objects or integers in these two examples) and need some way to specify a sentinel value. That sentinel must not be in the set of valid values. In both of these cases, None fills the bill. Note that the only reasonable relationship between None and either the set of all time objects or the set of all integers is that it is not a member of either set. Testing that membership by appealing to their value cast to a boolean is simply wrong. I'll push the argument just a bit further. Suppose you have an attribute which corresponds to the setting of a two-valued sensor external to your system. You choose to represent those two values in your program as True and False. You need a way to tell if any sensor inputs have been detected yet, or not, so you initialize the attribute with a sentinel value. I don't care what value you choose out of all the possible sentinel values available to you (None, [], ["xyz"], {}, 7, 0.0, etc), treating any of them as a boolean will be wrong. If you do, you will erroneously always believe that your sensor has produced a value. I have made this sort of mistake many times over the years, and seen other people do it as well. If what you really want to test is membership of a value in a set, be explicit about it. Skip
On Wednesday 05 March 2014 19:09:11 Skip Montanaro wrote:
On Wed, Mar 5, 2014 at 9:15 AM, Shai Berger <shai@platonix.com> wrote:
Also, when opening this thread, I argued that 0 is special, but midnight is not -- which you completely ignore.
Zero is not different, at least when considered as an element of a set (of integers, in my example). What's important is that we have a set of values (either time objects or integers in these two examples) and need some way to specify a sentinel value.
Your argument has merit, but is besides the point. As noted by many in this thread, the idiom "if obj:" for deciding on the existence of some value is quite common. We all know it doesn't always fit, because Python chose to make some objects falsey; however, when it does fit, it makes for succinct, clear, unceremonious code. If your argument is not that "if obj:" should never be used, except when obj actually is Boolean, please elaborate. At issue here is the fact that the idiom doesn't fit where one would expect that it should. Thanks, Shai.
On Wed, Mar 5, 2014 at 11:24 AM, Shai Berger <shai@platonix.com> wrote:
If your argument is not that "if obj:" should never be used, except when obj actually is Boolean, please elaborate.
My approach continues to be to consider the overall set of objects from which you draw values to assign to obj. Call that set V. If this expression is true: len([bool(x) for x in V]) == 1 then using "if obj:" might be valid (if the one "false" value of that set is some sort of distinguishing characteristic, as it often is). If, however, the length of that set is greater than one, you must be explicit in your conditional expressions. It is unfortunate for your purposes that bool(datetime.time(0, 0, 0)) is False, but it's a documented property of that object, and is highly unlikely to change. In your case V is None plus the set of all time objects. That has two values which evaluate to False, so "if obj:" is not appropriate. If, on the other hand, the set of all values is the set of all strings or the set of all integers or the set of all lists, then maybe the falsity of the zero values in those sets ("", 0, []) has some use. My last comment on this topic. I will invoke the Uncle Timmy rule (which I just made up). Never contradict a decision Uncle Timmy made. He had his reasons. They were very good reasons. They were almost certainly better than any reasons you can come up with as counterarguments. Deal with it. :-) Skip
On Wednesday 05 March 2014 20:33:08 Skip Montanaro wrote:
On Wed, Mar 5, 2014 at 11:24 AM, Shai Berger <shai@platonix.com> wrote:
If your argument is not that "if obj:" should never be used, except when obj actually is Boolean, please elaborate.
My approach continues to be to consider the overall set of objects from which you draw values to assign to obj. Call that set V. If this expression is true:
len([bool(x) for x in V]) == 1
then using "if obj:" might be valid (if the one "false" value of that set is some sort of distinguishing characteristic, as it often is). If, however, the length of that set is greater than one, you must be explicit in your conditional expressions.
No argument so far.
It is unfortunate for your purposes that bool(datetime.time(0, 0, 0)) is False, but it's a documented property of that object, and is highly unlikely to change.
I think this is what's called 'begging the question' -- this discussion was not about the validity of boolean testing in general, but about the validity of this property of time objects. Yes, it's documented. I'm trying to make it change. There is a lot of support for that, and the opposition seems to be centered on "because it's documented" -- with the two exceptions being an edge use-case (actually checking midnight) and your call to authority below. It's a gotcha. Let's remove it. If we don't remove it, let's give ourselves a tool to detect it.
My last comment on this topic. I will invoke the Uncle Timmy rule (which I just made up). Never contradict a decision Uncle Timmy made. He had his reasons. They were very good reasons. They were almost certainly better than any reasons you can come up with as counterarguments. Deal with it. :-)
The reason he actually gave (on the ticket)? "because it's documented". Shai.
On Wed, Mar 05, 2014 at 08:44:55PM +0200, Shai Berger wrote:
It is unfortunate for your purposes that bool(datetime.time(0, 0, 0)) is False, but it's a documented property of that object, and is highly unlikely to change.
I think this is what's called 'begging the question' -- this discussion was not about the validity of boolean testing in general, but about the validity of this property of time objects. Yes, it's documented. I'm trying to make it change. There is a lot of support for that, and the opposition seems to be centered on "because it's documented" -- with the two exceptions being an edge use-case (actually checking midnight) and your call to authority below.
There are some very powerful practical reasons for leaving the status quo, namely backwards-compatibility and code-churn. These are real costs, not for the person who writes the patch, or the people who write the tests, and document the change. But these are potential costs for the millions of people using Python who may be *relying* on midnight being falsey, or nevertheless will have to change their code if this change occurs. There's also the argument from consistency: midnight is the zero point of the day, and zero is falsey. It would be surprising if something which is essentially zero became truthy. Compared to those costs, the benefits are insignificant. For library authors, they cannot take advantage of this new always-truthy time objects for probably eight or ten years, when they drop all support for any prior to 3.6 or 3.7. A tiny benefit, discounted even further because it is so far in the distance, versus significant costs. Application authors, only using a single version of Python, may be able to make use of this rather sooner: perhaps as little as three years from now.
It's a gotcha. Let's remove it. If we don't remove it, let's give ourselves a tool to detect it.
Um, it's easy to detect it. not time_obj returns True if time_obj is midnight and therefore falsey. -- Steven
On Wed, Mar 5, 2014 at 11:24 AM, Steven D'Aprano <steve@pearwood.info>wrote:
There's also the argument from consistency: midnight is the zero point of the day, and zero is falsey. It would be surprising if something which is essentially zero became truthy.
I don't think it's consistent now. In Boolean contexts ... - a timedelta<http://docs.python.org/3.4/library/datetime.html#datetime.timedelta> object is considered to be true if and only if it isn't equal to timedelta(0). - all date<http://docs.python.org/3.4/library/datetime.html#datetime.date> objects are considered to be true. - all datetime<http://docs.python.org/3.4/library/datetime.html#datetime.datetime> objects are considered to be true. - a time <http://docs.python.org/3.4/library/datetime.html#datetime.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. And the documentation is not exactly clear for understanding when a time object is falsey. In particular, the "converting it to minutes" part seems irrelevant as a time of 1 microsecond from midnight is truthy and timedeltas are not numbers and are not in minutes. Perhaps a reasonable improvement would be to change this to: - a time <http://docs.python.org/3.4/library/datetime.html#datetime.time> object is considered to be true unless it represents exactly midnight local time, that is, it's false if subtracting the utcoffset() from the time produces an all-zero result (or it's all-zero to start with if the utcoffset is None). --- Bruce Learn how hackers think: http://j.mp/gruyere-security https://www.linkedin.com/in/bruceleban
[Bruce Leban]
And the documentation is not exactly clear for understanding when a time object is falsey. In particular, the "converting it to minutes" part seems irrelevant
That's there because `utcoffset()` is defined to return minutes. A rule is needed to specify how the naive time object and `utcoffset()` interact to define the result.
as a time of 1 microsecond from midnight is truthy and timedeltas are not numbers and are not in minutes
timedeltas have nothing to do with this. The docs could be expanded to specify precisely how the naive time object is converted to minutes, but I think that's so obvious it would just be tedious to do so.
Perhaps a reasonable improvement would be to change this to:
a time object is considered to be true unless it represents exactly midnight local time, that is, it's false if subtracting the utcoffset() from the time produces an all-zero result (or it's all-zero to start with if the utcoffset is None).
You still need a rule to define _how_ "subtracting the utcoffset() from the time" is intended to work, since arithmetic on naive time objects is not defined. Add "convert it to minutes" ;-)
On Wed, Mar 5, 2014 at 8:53 PM, Tim Peters <tim.peters@gmail.com> wrote:
That's there because `utcoffset()` is defined to return minutes.
More accurately, it returns "a timedelta object representing a whole number of minutes": time.utcoffset() If tzinfo is None, returns None, else returns self.tzinfo.utcoffset(None), and raises an exception if the latter doesn’t return None or a timedelta object representing a whole number of minutes with magnitude less than one day. http://docs.python.org/3/library/datetime.html#datetime.time.utcoffset
On Wed, Mar 5, 2014 at 5:53 PM, Tim Peters <tim.peters@gmail.com> wrote:
That's there because `utcoffset()` is defined to return minutes.
It returns either minutes *or a timedelta*: datetime.utcoffset()¶<http://docs.python.org/2/library/datetime.html#datetime.datetime.utcoffset> If tzinfo <http://docs.python.org/2/library/datetime.html#datetime.tzinfo> is None, returns None, else returns self.tzinfo.utcoffset(self), and raises an exception if the latter doesn't return None, or a timedelta<http://docs.python.org/2/library/datetime.html#datetime.timedelta> object representing a whole number of minutes with magnitude less than one day. tzinfo.utcoffset(*self*, *dt*) Return offset of local time from UTC, in minutes east of UTC. If local time is west of UTC, this should be negative. Note that this is intended to be the total offset from UTC; for example, if atzinfo<http://docs.python.org/2/library/datetime.html#datetime.tzinfo> object represents both time zone and DST adjustments, utcoffset()<http://docs.python.org/2/library/datetime.html#datetime.tzinfo.utcoffset> should return their sum. If the UTC offset isn't known, return None. Else the value returned must be a timedelta<http://docs.python.org/2/library/datetime.html#datetime.timedelta>object specifying a whole number of minutes in the range -1439 to 1439 inclusive (1440 = 24*60; the magnitude of the offset must be less than one day). A
rule is needed to specify how the naive time object and `utcoffset()` interact to define the result.
Not sure why you attach 'naive' to time. Time objects carry units with them, unlike integers. I would say 'naive time value' in reference to an integer. Combining a time and an integer can be done by converting the time to minutes (which is a bit confusing since in most contexts in this class, minutes values are integers) or it can be done by interpreting the integer utcoffset result as minutes (as it is documented).
Perhaps a reasonable improvement would be to change this to:
a time object is considered to be true unless it represents exactly midnight local time,
The above part is the key part of my suggested improvement. Reading the current documentation, the fact that it means midnight local time is falsey is not obvious. --- Bruce Learn how hackers think: http://j.mp/gruyere-security https://www.linkedin.com/in/bruceleban
[Tim]
That's there because `utcoffset()` is defined to return minutes.
[Bruce Leban]
It returns either minutes or a timedelta:
I wrote the code and I wrote the docs. I know what it does ;-) Going into tedious detail (as the docs do) is simply irrelevant to the point here: utcoffset() returns minutes. Whether that's _spelled_ as an integer or as a timedelta, minutes is minutes is minutes.
...
A rule is needed to specify how the naive time object and `utcoffset()` interact to define the result.
Not sure why you attach 'naive' to time. Time objects carry units with them, unlike integers. I would say 'naive time value' in reference to an integer.
Start with the second paragraph of the datetime docs: There are two kinds of date and time objects: "naive" and "aware". Etc. I attached "naive" to "time" to make clear that I mean the time object stripped of its tzinfo attribute. An aware time object is a naive time object with a non-None tzinfo attribute.
.... The above part is the key part of my suggested improvement. Reading the current documentation, the fact that it means midnight local time is falsey is not obvious.
Before this thread, I had never heard anyone express it as "midnight local time" before. Now that I have heard it, eh - I don't find it a helpful way to view it.
On Wed, Mar 5, 2014 at 8:13 PM, Tim Peters <tim.peters@gmail.com> wrote:
[Tim]
That's there because `utcoffset()` is defined to return minutes.
[Bruce Leban]
It returns either minutes or a timedelta:
I wrote the code and I wrote the docs. I know what it does ;-) Going into tedious detail (as the docs do) is simply irrelevant to the point here: utcoffset() returns minutes. Whether that's _spelled_ as an integer or as a timedelta, minutes is minutes is minutes.
Just because you wrote the docs doesn't mean you know what they mean to other readers. The point of documentation is to explain it to someone who doesn't know what it does after all.
Start with the second paragraph of the datetime docs:
There are two kinds of date and time objects: "naive" and "aware".
Thanks. Before this thread, I had never heard anyone express it as "midnight
local time" before. Now that I have heard it, eh - I don't find it a helpful way to view it.
Well, it's not surprising that you don't find it's helpful. In fact, it's wrong, which to me indicates how confusing this. Which of the following are falsey? 00:00:00+01:00 01:00:00+01:00 01:00:00-01:00 07:00:00+07:00 23:00:00-01:00 I find it particularly surprising that the bug that the OP described finding would occur in the Russia and China, but not in the US. Obviously, not an accident. :-) --- Bruce
[Bruce Leban <bruce@leapyear.org>]
... Just because you wrote the docs doesn't mean you know what they mean to other readers. The point of documentation is to explain it to someone who doesn't know what it does after all.
Of course. What's your point here?
... Before this thread, I had never heard anyone express it as "midnight local time" before. Now that I have heard it, eh - I don't find it a helpful way to view it.
Well, it's not surprising that you don't find it's helpful. In fact, it's wrong, which to me indicates how confusing this.
Bruce, you're the one who made up the "midnight local time" business, which has just confused you. Try again to read what the docs actually say, without assuming it must mean something else?
Which of the following are falsey?
It would help a lot if you explained which ones surprised _you_, and how you read the docs in such a way that what they actually say caused your confusion.
00:00:00+01:00
Convert the naive time to minutes: 0. Get the UTC offset in minutes (these offsets are always returned directly as minutes in the code, but there's an "extra" calculation step needed here because you're starting from a string representation that also uses units of hours): 1*60 + 0 = 60. Subtract: 0-60 = -60 Is that zero? No, so this one is true.
01:00:00+01:00
60 - 60 = 0. False.
01:00:00-01:00
60 - -60 = 120. True.
07:00:00+07:00
420 - 420 = 0. False.
23:00:00-01:00
23*60 - -60 = 24*60. True.
I find it particularly surprising that the bug that the OP described finding would occur in the Russia and China, but not in the US. Obviously, not an accident. :-)
Indeed, it is the key to liberating both Ukraine and Tibet ;-)
On Thu, Mar 6, 2014 at 12:53 AM, Tim Peters <tim.peters@gmail.com> wrote:
[Bruce Leban <bruce@leapyear.org>]
... Just because you wrote the docs doesn't mean you know what they mean to other readers. The point of documentation is to explain it to someone who doesn't know what it does after all.
Of course. What's your point here?
I am with Bruce here. I tried to digest your notes on timezone algebra [1] for years and I still don't understand it. [1] http://hg.python.org/cpython/file/b07400659dba/Lib/datetime.py#l1922
[Bruce Leban <bruce@leapyear.org>]
... Just because you wrote the docs doesn't mean you know what they mean to other readers. The point of documentation is to explain it to someone who doesn't know what it does after all.
[Tim]
Of course. What's your point here?
[Alexander Belopolsky <alexander.belopolsky@gmail.com>]
I am with Bruce here. I tried to digest your notes on timezone algebra [1] for years and I still don't understand it.
[1] http://hg.python.org/cpython/file/b07400659dba/Lib/datetime.py#l1922
But those aren't user docs - they're notes for developers who are deeply steeped in internal details. And they're hard! Mucking with "all possible" time zones raises mountains of excruciatingly subtle details and corner cases. The masses of tedious "algebra" were needed to justify how the internals get away with doing "so little" - and to explain why the code still wouldn't work right in all cases if the "standard offset" of a single time zone changed (or changes) over time. Do you claim you still don't understand when bool(time) returns True and False? Those are user docs. I don't claim that reading them will make anyone feel good ;-), but I do believe the computation is described clearly enough to enable a reasonable user to predict the result in all cases.
On Mar 6, 2014, at 11:50 AM, Tim Peters <tim.peters@gmail.com> wrote:
[Bruce Leban <bruce@leapyear.org>]
... Just because you wrote the docs doesn't mean you know what they mean to other readers. The point of documentation is to explain it to someone who doesn't know what it does after all.
[Tim]
Of course. What's your point here?
[Alexander Belopolsky <alexander.belopolsky@gmail.com>]
I am with Bruce here. I tried to digest your notes on timezone algebra [1] for years and I still don't understand it.
[1] http://hg.python.org/cpython/file/b07400659dba/Lib/datetime.py#l1922
But those aren't user docs - they're notes for developers who are deeply steeped in internal details. And they're hard! Mucking with "all possible" time zones raises mountains of excruciatingly subtle details and corner cases. The masses of tedious "algebra" were needed to justify how the internals get away with doing "so little" - and to explain why the code still wouldn't work right in all cases if the "standard offset" of a single time zone changed (or changes) over time.
Do you claim you still don't understand when bool(time) returns True and False? Those are user docs. I don't claim that reading them will make anyone feel good ;-), but I do believe the computation is described clearly enough to enable a reasonable user to predict the result in all cases.
I don’t think a reasonable user would predict that midnight UTC in a timezone with a UTC offset of +5 would be false, while midnight UTC in a timezone with a UTC offset of -5 would not be false.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
[TIm]
... Do you claim you still don't understand when bool(time) returns True and False? Those are user docs. I don't claim that reading them will make anyone feel good ;-), but I do believe the computation is described clearly enough to enable a reasonable user to predict the result in all cases.
[Donald Stufft <donald@stufft.io>]
I don't think a reasonable user would predict that midnight UTC in a timezone with a UTC offset of +5 would be false, while midnight UTC in a timezone with a UTC offset of -5 would not be false.
As I said, I didn't claim a reasonable user would feel good about it, and all you're saying here is that they wouldn't feel good about it. I'm not arguing that they should feel good about it. I said that the docs explained the computation clearly enough that a reasonable user - who read the docs and earnestly tried to apply them - _could_ predict the results. And you just showed that you, for example, can predict them. Since you're the very definition of "reasonable", I close my case for that ;-)
On Mar 6, 2014, at 12:01 PM, Tim Peters <tim.peters@gmail.com> wrote:
[TIm]
... Do you claim you still don't understand when bool(time) returns True and False? Those are user docs. I don't claim that reading them will make anyone feel good ;-), but I do believe the computation is described clearly enough to enable a reasonable user to predict the result in all cases.
[Donald Stufft <donald@stufft.io>]
I don't think a reasonable user would predict that midnight UTC in a timezone with a UTC offset of +5 would be false, while midnight UTC in a timezone with a UTC offset of -5 would not be false.
As I said, I didn't claim a reasonable user would feel good about it, and all you're saying here is that they wouldn't feel good about it. I'm not arguing that they should feel good about it.
I said that the docs explained the computation clearly enough that a reasonable user - who read the docs and earnestly tried to apply them - _could_ predict the results. And you just showed that you, for example, can predict them. Since you're the very definition of "reasonable", I close my case for that ;-)
Actually I wasn’t able to predict it :) Someone else posted a number of times offsets and their boolean values. I was really confused when it turned out that 01:00:00+01:00 (1AM France/Midnight UTC) was False but 19:00:00-05:00 (7PM Boston/Midnight UTC) wasn’t. I only realized that the cause was the specific formula that was used when I reversed it and tried to figure out what time in Boston would be False and realized it was -05:00. I don’t think it’s reasonable by any definition of the word that in order to predict the results a user would have to turn it into an algebraic formula and solve for X to determine that the only way for Boston to have a False time is if time was negative. ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On Thu, Mar 6, 2014 at 11:50 AM, Tim Peters <tim.peters@gmail.com> wrote:
Do you claim you still don't understand when bool(time) returns True and False? Those are user docs. I don't claim that reading them will make anyone feel good ;-), but I do believe the computation is described clearly enough to enable a reasonable user to predict the result in all cases.
I have to admit that before this discussion, I did not know about the following consequence of the documented behavior:
from datetime import * t = datetime(2014, 1, 1, tzinfo=timezone.utc) tz1 = timezone(timedelta(hours=5)) tz2 = timezone(timedelta(hours=-5)) bool(t.timetz()) False bool(t.astimezone(tz1).timetz()) False bool(t.astimezone(tz2).timetz()) True
[Alexander Belopolsky <alexander.belopolsky@gmail.com>]
I have to admit that before this discussion, I did not know about the following consequence of the documented behavior:
from datetime import * t = datetime(2014, 1, 1, tzinfo=timezone.utc) tz1 = timezone(timedelta(hours=5)) tz2 = timezone(timedelta(hours=-5)) bool(t.timetz()) False bool(t.astimezone(tz1).timetz()) False bool(t.astimezone(tz2).timetz()) True
I'm not sure I did either! An "aware" time object is a bizarre beast, and I've never used one outside of writing datetime test cases. I'm not even sure why aware time objects exist - we'd have to dig into Guido's subconscious for that one ;-)
On Thu, Mar 6, 2014 at 12:05 PM, Tim Peters <tim.peters@gmail.com> wrote:
I'm not sure I did either! An "aware" time object is a bizarre beast, and I've never used one outside of writing datetime test cases. I'm not even sure why aware time objects exist - we'd have to dig into Guido's subconscious for that one ;-)
They are occasionally useful if you want to split datetimes into date and time parts, process those separately and recombine in the end without loosing tzinfo. I don't think anything involving time.utcoffset() makes much sense and it is unfortunate that time.utcoffset() can be invoked implicitly when aware time is used in boolean context.
On Thu, Mar 6, 2014 at 12:16 AM, Bruce Leban <bruce@leapyear.org> wrote:
Well, it's not surprising that you don't find it's helpful. In fact, it's wrong, which to me indicates how confusing this. Which of the following are falsey?
00:00:00+01:00 01:00:00+01:00 01:00:00-01:00 07:00:00+07:00 23:00:00-01:00
I find it particularly surprising that the bug that the OP described finding would occur in the Russia and China, but not in the US. Obviously, not an accident. :-)
"Aware" time objects are problematic even before you consider their bool behavior. Fortunately, you don't encounter them too often because dt.time() returns naive time even if dt is an aware datetime instance. Again, the use case that I find compelling is to test dt.time() for falsehood to detect datetime objects that fall on midnight. I don't understand why people are denying that midnights are special points. All real numbers are equal, but integers are more equal than others. When you plot say hourly timeseries it is not uncommon to mark midnight ticks on the time axis with dt.date() and other ticks with dt.hour. Whenever you need to do something special when you start a new date in a simulation of a physical process it is convenient to have a simple test for midnight.
On Wed, Mar 5, 2014 at 10:47 PM, Alexander Belopolsky < alexander.belopolsky@gmail.com> wrote:
it is convenient to have a simple test for midnight.
Except this isn't it. That's only works for naive times. For aware times: 00:00:00+00:00 = midnight England = midnight UTC => False 00:00:00+01:00 = midnight France = 2300 UTC => True 01:00:00+01:00 = 1 am France = midnight UTC => False 19:00:00-05:00 = 1 am Boston = midnight UTC => True Code that relies that bool(time) is False for midnight is broken IMHO. --- Bruce Learn how hackers think: http://j.mp/gruyere-security https://www.linkedin.com/in/bruceleban
On Thu, Mar 6, 2014 at 2:02 AM, Bruce Leban <bruce@leapyear.org> wrote:
On Wed, Mar 5, 2014 at 10:47 PM, Alexander Belopolsky < alexander.belopolsky@gmail.com> wrote:
it is convenient to have a simple test for midnight.
Except this isn't it. That's only works for naive times. For aware times:
00:00:00+00:00 = midnight England = midnight UTC => False 00:00:00+01:00 = midnight France = 2300 UTC => True 01:00:00+01:00 = 1 am France = midnight UTC => False 19:00:00-05:00 = 1 am Boston = midnight UTC => True
Code that relies that bool(time) is False for midnight is broken IMHO.
You keep fighting your own straw-man. My use case is not using an arbitrary time object that someone decided to imbue with a timezone info. What I want is given a *datetime* instance dt to check whether it falls on midnight in whatever timezone dt.tzinfo represent or naively if dt.tzinfo is None. I claim that "if dt.time()" is a perfectly good shorthand for "if dt.time() != datetime.time(0)". I am not trying to argue that "if not dt.timetz()" test is equally useful.
On Mar 6, 2014, at 2:20 AM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Thu, Mar 6, 2014 at 2:02 AM, Bruce Leban <bruce@leapyear.org> wrote:
On Wed, Mar 5, 2014 at 10:47 PM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote: it is convenient to have a simple test for midnight.
Except this isn't it. That's only works for naive times. For aware times:
00:00:00+00:00 = midnight England = midnight UTC => False 00:00:00+01:00 = midnight France = 2300 UTC => True 01:00:00+01:00 = 1 am France = midnight UTC => False 19:00:00-05:00 = 1 am Boston = midnight UTC => True
Code that relies that bool(time) is False for midnight is broken IMHO.
You keep fighting your own straw-man. My use case is not using an arbitrary time object that someone decided to imbue with a timezone info. What I want is given a *datetime* instance dt to check whether it falls on midnight in whatever timezone dt.tzinfo represent or naively if dt.tzinfo is None.
So check for it falling on midnight. It’s not reasonable to expect that midnight will evaluate to false, especially when it doesn’t if you happen to have a tzinfo on the time (sometimes!). That makes the behavior even more surprising and more footgun-ey.
I claim that "if dt.time()" is a perfectly good shorthand for "if dt.time() != datetime.time(0)”.
And I claim that you’re reaching to justify stupid behavior.
I am not trying to argue that "if not dt.timetz()" test is equally useful
And yet you can’t control what people do with it. Consistency is a virtue and how objects act with a tzinfo and how they act without a tzinfo should be as close as possible.
. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On Thu, Mar 6, 2014 at 2:30 AM, Donald Stufft <donald@stufft.io> wrote:
It’s not reasonable to expect that midnight will evaluate to false, ..
Only in the world where it is not reasonable to expect programmers to read library documentation. In my world it is reasonable to expect that the behavior that was documented in 10 major versions and for 10 years can be relied on.
especially when it doesn’t if you happen to have a tzinfo on the time (sometimes!).
As long as tzinfo specifies a fixed offset, there is no problem with the current definition. If you are unfortunate enough to live in a place with semi-annual DST adjustment, aware time objects are problematic for reasons that have nothing to do with the discussion at hand.
On Mar 6, 2014, at 2:40 AM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Thu, Mar 6, 2014 at 2:30 AM, Donald Stufft <donald@stufft.io> wrote: It’s not reasonable to expect that midnight will evaluate to false, ..
Only in the world where it is not reasonable to expect programmers to read library documentation. In my world it is reasonable to expect that the behavior that was documented in 10 major versions and for 10 years can be relied on.
especially when it doesn’t if you happen to have a tzinfo on the time (sometimes!).
As long as tzinfo specifies a fixed offset, there is no problem with the current definition. If you are unfortunate enough to live in a place with semi-annual DST adjustment, aware time objects are problematic for reasons that have nothing to do with the discussion at hand.
Nope! It essentially in an aware time would be “false if whatever time is at the same time as UTC midnight, but only if your UTC offset is positive”. If your UTC offset is negative then you end up with 1440 instead of 0. ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On Mar 6, 2014, at 2:43 AM, Donald Stufft <donald@stufft.io> wrote:
On Mar 6, 2014, at 2:40 AM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Thu, Mar 6, 2014 at 2:30 AM, Donald Stufft <donald@stufft.io> wrote: It’s not reasonable to expect that midnight will evaluate to false, ..
Only in the world where it is not reasonable to expect programmers to read library documentation. In my world it is reasonable to expect that the behavior that was documented in 10 major versions and for 10 years can be relied on.
especially when it doesn’t if you happen to have a tzinfo on the time (sometimes!).
As long as tzinfo specifies a fixed offset, there is no problem with the current definition. If you are unfortunate enough to live in a place with semi-annual DST adjustment, aware time objects are problematic for reasons that have nothing to do with the discussion at hand.
Nope! It essentially in an aware time would be “false if whatever time is at the same time as UTC midnight, but only if your UTC offset is positive”. If your UTC offset is negative then you end up with 1440 instead of 0.
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Actually now that I think about it more, in aware times it’s impossible for it to time objects to ever be false if the tzinfo is negative, because that would require the time to negative. Take -5 for instance: x - (-5 * 60) = 0 x - (-300) = 0 x + 300 = 0 x = -300 So in order for a time with UTC offset of -5 to ever be False, it would have to be -05:00:00 there. ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On 6 March 2014 08:40:24 CET, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Thu, Mar 6, 2014 at 2:30 AM, Donald Stufft <donald@stufft.io> wrote:
It’s not reasonable to expect that midnight will evaluate to false, ..
Only in the world where it is not reasonable to expect programmers to read library documentation.
I disagree. Consider this code: class MyType: def __bool__(self): return bool(random.random()) Even though the behavior in boolean context is documented, it doesn't have to make any sense or be reasonable.
In my world it is reasonable to expect that the behavior that was documented in 10 major versions and for 10 years can be relied on.
It *is* reasonable to expect to be able to rely on such features, yet IMO it is not reasonable to actually rely on that feature, for reasons of readability already mentioned by me in a reply to Steven D'Aprano. Considering that (from my subjective view) many people new to Python expect the kind of behavior proposed by the OP, i think it might be time for a change. Maybe an actual survey is the only way to find out.
especially when it doesn’t if you happen to have a tzinfo on the time (sometimes!).
As long as tzinfo specifies a fixed offset, there is no problem with the current definition. If you are unfortunate enough to live in a place with semi-annual DST adjustment, aware time objects are problematic for reasons that have nothing to do with the discussion at hand.
------------------------------------------------------------------------
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Bruce Leban wrote:
Except this isn't it. That's only works for naive times. For aware times:
00:00:00+00:00 = midnight England = midnight UTC => False 00:00:00+01:00 = midnight France = 2300 UTC => True 01:00:00+01:00 = 1 am France = midnight UTC => False 19:00:00-05:00 = 1 am Boston = midnight UTC => True
That's... I don't know what it is. Is there a rationale for this? I'm totally failing to see it. -- Greg
Le 06/03/2014 07:47, Alexander Belopolsky a écrit :
I don't understand why people are denying that midnights are special points.
Because they are not? Really, this is getting ridiculous. In any intuitive and common notion of time, midnight is not a special point at all. There is a reason most people on this thread disagree with you. (and, no, it's not because they are being dense or ignorant) Regards Antoine.
Steven D'Aprano wrote:
There's also the argument from consistency: midnight is the zero point of the day, and zero is falsey.
Regarding midnight as a "zero point" is an arbitrary convention. The integer zero is not arbitrary, because it's the additive identity. There is no corresponding additive identity for times, because you can't add two times. -- Greg
On Thu, Mar 06, 2014 at 05:41:11PM +1300, Greg Ewing wrote:
Steven D'Aprano wrote:
There's also the argument from consistency: midnight is the zero point of the day, and zero is falsey.
Regarding midnight as a "zero point" is an arbitrary convention.
True, as I mentioned in another post. Nevertheless, we have many arbitrary conventions enshrined in Python. Using base 10 for the representation of numbers, instead of, say, balanced ternary notation, is an arbitrary convention. Numbering sequences from 0 is another. If you would prefer another convention for the zero point of the day (say, midday, dusk, dawn, or 2:15pm) then feel free to subclass datetime.time :-) Or even no zero point at all. Tossing out a wild idea, why not leave datetime.time alone and add a new class which is exactly the same but is always truthy? That doesn't break backward compatibility, and so it could happen immediately (too late for 3.4, but certainly for 3.5). All we need is an appropriate name, and a decision as to which ought to inherit from which. The bike-shedding should be over by 3.5 alpha 1 *wink* Despite my quip about the bike-shedding, I am actually serious. -- Steven
On 06/03/2014 10:52, Steven D'Aprano wrote:
True, as I mentioned in another post.
Nevertheless, we have many arbitrary conventions enshrined in Python. Using base 10 for the representation of numbers, instead of, say, balanced ternary notation, is an arbitrary convention. Numbering sequences from 0 is another.
If you would prefer another convention for the zero point of the day (say, midday, dusk, dawn, or 2:15pm) then feel free to subclass datetime.time :-)
Or even no zero point at all. Tossing out a wild idea, why not leave datetime.time alone and add a new class which is exactly the same but is always truthy? That doesn't break backward compatibility, and so it could happen immediately (too late for 3.4, but certainly for 3.5).
All we need is an appropriate name, and a decision as to which ought to inherit from which. The bike-shedding should be over by 3.5 alpha 1 *wink*
Despite my quip about the bike-shedding, I am actually serious.
You're joking, right? Suppose you were considering taking up a new language and you found something in the docs which said: "Here's a class that does so-and-so. And here's another slightly different one that you can use instead, because we didn't get the first one quite right." Would that be a turn-on? Rob Cliffe
On Thu, Mar 06, 2014 at 11:40:21AM +0000, Rob Cliffe wrote:
On 06/03/2014 10:52, Steven D'Aprano wrote:
Or even no zero point at all. Tossing out a wild idea, why not leave datetime.time alone and add a new class which is exactly the same but is always truthy? That doesn't break backward compatibility, and so it could happen immediately (too late for 3.4, but certainly for 3.5).
All we need is an appropriate name, and a decision as to which ought to inherit from which. The bike-shedding should be over by 3.5 alpha 1 *wink*
Despite my quip about the bike-shedding, I am actually serious.
You're joking, right?
What part of "I am actually serious" suggests to you that I am joking? No, I am not joking. When you have a misdesigned API that cannot be fixed without breaking backwards compatibility, or at least cannot be fixed *quickly*, it may be sensible to leave the old API in place and place a new one side-by-side. If the old one is actively harmful, it can even be deprecated for eventual removal, otherwise it can remain indefinitely. Does the term "legacy API" sound familiar? This is *extremely common* in the software industry, at least in that part that isn't run by cowboy coders who have no concern about breaking their users' code. It's quite common in Python too: consider the legacy API of os.system, the deprecated API of the popen2 module, and the modern subprocess API.
Suppose you were considering taking up a new language and you found something in the docs which said: "Here's a class that does so-and-so. And here's another slightly different one that you can use instead, because we didn't get the first one quite right." Would that be a turn-on?
Yes. This would tell me that the developers of this language took backward compatibility seriously, and that they weren't afraid to admit to mistakes. -- Steven
On Mar 6, 2014, at 7:35 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Thu, Mar 06, 2014 at 11:40:21AM +0000, Rob Cliffe wrote:
On 06/03/2014 10:52, Steven D'Aprano wrote:
Or even no zero point at all. Tossing out a wild idea, why not leave datetime.time alone and add a new class which is exactly the same but is always truthy? That doesn't break backward compatibility, and so it could happen immediately (too late for 3.4, but certainly for 3.5).
All we need is an appropriate name, and a decision as to which ought to inherit from which. The bike-shedding should be over by 3.5 alpha 1 *wink*
Despite my quip about the bike-shedding, I am actually serious.
You're joking, right?
What part of "I am actually serious" suggests to you that I am joking? No, I am not joking.
When you have a misdesigned API that cannot be fixed without breaking backwards compatibility, or at least cannot be fixed *quickly*, it may be sensible to leave the old API in place and place a new one side-by-side. If the old one is actively harmful, it can even be deprecated for eventual removal, otherwise it can remain indefinitely. Does the term "legacy API" sound familiar?
This is *extremely common* in the software industry, at least in that part that isn't run by cowboy coders who have no concern about breaking their users' code. It's quite common in Python too: consider the legacy API of os.system, the deprecated API of the popen2 module, and the modern subprocess API.
Suppose you were considering taking up a new language and you found something in the docs which said: "Here's a class that does so-and-so. And here's another slightly different one that you can use instead, because we didn't get the first one quite right." Would that be a turn-on?
Yes. This would tell me that the developers of this language took backward compatibility seriously, and that they weren't afraid to admit to mistakes.
-- Steven _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
I think the decision to deprecate and change in place or create a new API depends greatly on how much code is going to break as a result of the deprecation, how hard it will be to restore the previous behavior, and how different the new API is. Language designers have to both consider backwards compatibility and the cognitive burden of having two (or more!) ways of doing the same thing. If you don't strike this balance and you never break backwards compatibility you very quickly end up with a massive number of new APIs. Remember that every bug fix is a potential backwards compatibility break for someone (https://xkcd.com/1172/ is relevant). In the case of datetime.time()’s boolean evaluation, I don’t believe a new API is warranted. I don’t believe (but I do not have proof) that there is going to be a lot of code out that that is depending on datetime.time evaluating to False in the cases it does (midnight in naive times, midnight utc when the utc offset is 0 or positive in aware times). I believe it is quite simple to replace the checks with a more explicit check when the user has been warned through the deprecation process. Finally I believe that the difference between the two APIs would be so slight as to increase the cognitive overhead for very little benefit. If we were talking about a major shift in how datetime.time() functions then maybe that would be warranted, however this is a small usability fix in an obscure corner of the API. It's not hardly, in my opinion, worth an entire new API revision. ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On 06/03/2014 12:35, Steven D'Aprano wrote:
On Thu, Mar 06, 2014 at 11:40:21AM +0000, Rob Cliffe wrote:
On 06/03/2014 10:52, Steven D'Aprano wrote:
Or even no zero point at all. Tossing out a wild idea, why not leave datetime.time alone and add a new class which is exactly the same but is always truthy? That doesn't break backward compatibility, and so it could happen immediately (too late for 3.4, but certainly for 3.5).
All we need is an appropriate name, and a decision as to which ought to inherit from which. The bike-shedding should be over by 3.5 alpha 1 *wink*
Despite my quip about the bike-shedding, I am actually serious. You're joking, right? What part of "I am actually serious" suggests to you that I am joking? No, I am not joking.
When you have a misdesigned API that cannot be fixed without breaking backwards compatibility, or at least cannot be fixed *quickly*, it may be sensible to leave the old API in place and place a new one side-by-side. If the old one is actively harmful, it can even be deprecated for eventual removal, otherwise it can remain indefinitely. Does the term "legacy API" sound familiar?
This is *extremely common* in the software industry, at least in that part that isn't run by cowboy coders who have no concern about breaking their users' code. It's quite common in Python too: consider the legacy API of os.system, the deprecated API of the popen2 module, and the modern subprocess API.
Suppose you were considering taking up a new language and you found something in the docs which said: "Here's a class that does so-and-so. And here's another slightly different one that you can use instead, because we didn't get the first one quite right." Would that be a turn-on? Yes. This would tell me that the developers of this language took backward compatibility seriously, and that they weren't afraid to admit to mistakes.
Fair point. (It just feels untidy to me!) At least having two classes would highlight the difference in behaviour. Which makes me think, maybe the right thing to do now is to draw more attention to the behaviour in the docs. Section 8.1.5 time Objects currently states "in Boolean contexts, a time <http://docs.python.org/2/library/datetime.html#datetime.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." This could be expanded, using the sort of examples Nick Coghlan produced, and a warning added that using bool on time objects is discouraged. And section 3.1 Truth Value Testing which claims to explicitly list all the falsey objects includes "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|" I think this could be reworded. "user-defined classes" does not suggest to me that it includes classes in the stdlib. Would it be going too far to include in this section a short warning that using bool (explicitly or implicitly) on time objects is discouraged, or at least may not behave as expected? Rob Cliffe
On Thu, Mar 6, 2014 at 10:40 PM, Rob Cliffe <rob.cliffe@btinternet.com> wrote:
Suppose you were considering taking up a new language and you found something in the docs which said: "Here's a class that does so-and-so. And here's another slightly different one that you can use instead, because we didn't get the first one quite right." Would that be a turn-on?
If the first one is marked deprecated, then I'd just ignore that one. The only (minor) downside is remembering which one's the deprecated one, so I avoid wasting time going to its docs and then going to the other. Compare: http://pike.lysator.liu.se/generated/manual/modref/ex/predef_3A_3A/Sql/postg... http://pike.lysator.liu.se/generated/manual/modref/ex/predef_3A_3A/Sql/pgsql... In new code, which of those would you use? Is it sufficiently clear? I think it is. With Python's time types, though, there may be links with datetime. Which type does datetime.time() return? Will there be two parallel methods to resolve that? It's slightly more complicated. Still, it's not a killer. I've worked with far messier APIs - look at PHP and MySQL and the "real" functions. Which ones do you need _real_ in and which not? ChrisA
Shai Berger writes:
At issue here is the fact that the idiom doesn't fit where one would expect that it should.
This is the real problem. People are programming in un-Pythonic style by analogy to either other languages or to other cases in Python where the __len__ method provides a natural cast to Boolean, and deciding since they like the idiom, the behavior of time.time is "unexpected." But your claim is *not* a "fact". Quite the reverse, I was mildly surprised even the last time around that anybody would argue that it's a good idea to use a sentinel to indicate "uninitialized" and then fail to test explicitly for the sentinel. For example, I wouldn't be surprised to see something like this in an MTA or other program that should not let data out of its sight without a timestamp: my_time = None if not my_time and x: my_time = x.supposed_to_be_time_but_in_fact_sometimes_0 if not my_time: my_time = time.now() What are the odds that you'll detect the bug in initializing 'x' in a timely fashion? OTOH, if you test for "is None" you'll probably get a quick TypeError the first time the bug manifests. (No guarantees, of course, but why trade a potentially easy bug for a probably hard one?) If you asked me to implement a Time class, would I have any false instances? Quite possibly, yes. Of course I might implement it as a namedtuple (h, m, s), in which case it would always be true. OTOH, I might implement it as seconds-since-midnight, and probably not bother to give it a __bool__ method to ensure bool(midnight) == True. So I don't really care *why* Uncle Timmy chose to make midnight be false, I only offer kudos for documenting it. :-)
Let's turn this around. Aside from backwards compatibility, what benefit is there from having midnight evaluate as false? On Wednesday, March 5, 2014 9:09:50 AM, Skip Montanaro <skip@pobox.com> wrote:
On Wed, Mar 5, 2014 at 9:15 AM, Shai Berger <shai@platonix.com> wrote:
Also, when opening this thread, I argued that 0 is special, but midnight is not -- which you completely ignore.
Zero is not different, at least when considered as an element of a set (of integers, in my example). What's important is that we have a set of values (either time objects or integers in these two examples) and need some way to specify a sentinel value. That sentinel must not be in the set of valid values. In both of these cases, None fills the bill. Note that the only reasonable relationship between None and either the set of all time objects or the set of all integers is that it is not a member of either set. Testing that membership by appealing to their value cast to a boolean is simply wrong.
I'll push the argument just a bit further. Suppose you have an attribute which corresponds to the setting of a two-valued sensor external to your system. You choose to represent those two values in your program as True and False. You need a way to tell if any sensor inputs have been detected yet, or not, so you initialize the attribute with a sentinel value. I don't care what value you choose out of all the possible sentinel values available to you (None, [], ["xyz"], {}, 7, 0.0, etc), treating any of them as a boolean will be wrong. If you do, you will erroneously always believe that your sensor has produced a value.
I have made this sort of mistake many times over the years, and seen other people do it as well. If what you really want to test is membership of a value in a set, be explicit about it.
Skip _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 5 March 2014 17:27, Amber Yust <amber.yust@gmail.com> wrote:
Let's turn this around. Aside from backwards compatibility, what benefit is there from having midnight evaluate as false?
Zero cost. Any alternative must justify the cost of making the change. Simple as that. Quantify the cost of making the change (properly!) and describe the benefits in such a way as to justify those costs. Paul
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change. I see a difference between "this could be better, but the core devs aren't going to spend time on it" and "this should not change." It would be nice if those two were not conflated so much. On Mar 5, 2014 9:58 AM, "Paul Moore" <p.f.moore@gmail.com> wrote:
On 5 March 2014 17:27, Amber Yust <amber.yust@gmail.com> wrote:
Let's turn this around. Aside from backwards compatibility, what benefit is there from having midnight evaluate as false?
Zero cost. Any alternative must justify the cost of making the change. Simple as that. Quantify the cost of making the change (properly!) and describe the benefits in such a way as to justify those costs.
Paul
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around? -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
Where did you see an indication that a patch would not be accepted? On Mar 5, 2014 10:24 AM, "Mark Lawrence" <breamoreboy@yahoo.co.uk> wrote:
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around?
-- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language.
Mark Lawrence
--- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 05/03/2014 18:26, Amber Yust wrote:
Where did you see an indication that a patch would not be accepted?
On Mar 5, 2014 10:24 AM, "Mark Lawrence" <breamoreboy@yahoo.co.uk <mailto:breamoreboy@yahoo.co.uk>> wrote:
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around?
-- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language.
Mark Lawrence
---
http://bugs.python.org/issue13936 has been closed as invalid and I don't see a rush of core devs backing this proposal. -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
It was closed by one or two individuals, and there has been significant comment since then. Asking to revisit a decision isn't crazy, and there have been reasonable suggestions made. I don't see a lack of core dev "jumping upon" as an active indication that patches would be rejected; merely that core devs are unlikely to produce such a patch themselves. On Mar 5, 2014 11:00 AM, "Mark Lawrence" <breamoreboy@yahoo.co.uk> wrote:
On 05/03/2014 18:26, Amber Yust wrote:
Where did you see an indication that a patch would not be accepted?
On Mar 5, 2014 10:24 AM, "Mark Lawrence" <breamoreboy@yahoo.co.uk <mailto:breamoreboy@yahoo.co.uk>> wrote:
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around?
-- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language.
Mark Lawrence
---
http://bugs.python.org/issue13936 has been closed as invalid and I don't see a rush of core devs backing this proposal.
-- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language.
Mark Lawrence
--- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 05/03/2014 19:06, Amber Yust wrote:
It was closed by one or two individuals, and there has been significant comment since then. Asking to revisit a decision isn't crazy, and there have been reasonable suggestions made. I don't see a lack of core dev "jumping upon" as an active indication that patches would be rejected; merely that core devs are unlikely to produce such a patch themselves.
On Mar 5, 2014 11:00 AM, "Mark Lawrence" <breamoreboy@yahoo.co.uk <mailto:breamoreboy@yahoo.co.uk>> wrote:
On 05/03/2014 18:26, Amber Yust wrote:
Where did you see an indication that a patch would not be accepted?
On Mar 5, 2014 10:24 AM, "Mark Lawrence" <breamoreboy@yahoo.co.uk <mailto:breamoreboy@yahoo.co.uk> <mailto:breamoreboy@yahoo.co.__uk <mailto:breamoreboy@yahoo.co.uk>>> wrote:
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around?
-- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language.
Mark Lawrence
---
http://bugs.python.org/__issue13936 <http://bugs.python.org/issue13936> has been closed as invalid and I don't see a rush of core devs backing this proposal.
There can be lots of comments by Her Majesty Queen Elizabeth II or President Obama but they don't really count unless they put forward arguments to pursuade the core devs to change their minds about this issue. I see no evidence here that this is likely to happen, or do the core devs all come to life at the same time as they're all in the same time zone? A patch can be placed on the issue but if a core dev reviews and it and decides not to commit it nothing happens? -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
On Wednesday 05 March 2014 20:23:12 Mark Lawrence wrote:
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around?
As noted before, the workaround is trivial once you detect the problem. Detecting the problem is not easy. Shai.
On 05/03/2014 18:33, Shai Berger wrote:
On Wednesday 05 March 2014 20:23:12 Mark Lawrence wrote:
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around?
As noted before, the workaround is trivial once you detect the problem. Detecting the problem is not easy.
Shai.
Use mocks in unit testing? -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
On Wednesday 05 March 2014 20:47:02 Mark Lawrence wrote:
On 05/03/2014 18:33, Shai Berger wrote:
On Wednesday 05 March 2014 20:23:12 Mark Lawrence wrote:
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around?
As noted before, the workaround is trivial once you detect the problem. Detecting the problem is not easy.
Shai.
Use mocks in unit testing?
Unless you're aware of the issue, you are not going to unit-test specifically for midnight. If you are aware, you don't need to detect it, you write "is not None".
On 05/03/2014 19:18, Shai Berger wrote:
On Wednesday 05 March 2014 20:47:02 Mark Lawrence wrote:
On 05/03/2014 18:33, Shai Berger wrote:
On Wednesday 05 March 2014 20:23:12 Mark Lawrence wrote:
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around?
As noted before, the workaround is trivial once you detect the problem. Detecting the problem is not easy.
Shai.
Use mocks in unit testing?
Unless you're aware of the issue, you are not going to unit-test specifically for midnight. If you are aware, you don't need to detect it, you write "is not None".
So you have your solution. Good, now can we please move on as this is getting tedious. -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
Shai Berger writes:
Unless you're aware of the issue, you are not going to unit-test specifically for midnight. If you are aware, you don't need to detect it, you write "is not None".
The issue, as Skip has argued at length, is that by using the implicit cast to Boolean you've changed the nature of the test from "membership in valid values" to "a true-ish value".[1] This can be detected generically with grep, something like: grep -E '^ *if [^<>=]*:' | grep -v '^ *if.* is .*:' with a (possibly large) number of false positives. (I suspect it's not worth worrying about no false negatives; if the 'is' operator is present, the test is boolean except if it's part of an arithmetic expression such as "3 + (2 is not None)".) You might want to include modulo (%) in the list of operators. Or it might be more accurate to check for bare variables only and assume any expression should OK. The false positives can be drastically reduced by checking for a Boolean or container initializer in the case of "if var:". None of pep8, pylint, or pychecker does this now AFAICT, but they could, I think. Footnotes: [1] Implementing a membership test as a test for a sentinel is a bit fishy in this context of arguing about the "nature of the test", but given the much less obvious nature of the conversion to bool in complex types like dates and times, I'll go with Skip any time.
On 03/05/2014 10:23 AM, Mark Lawrence wrote:
On 05/03/2014 18:12, Amber Yust wrote:
I think this is the wrong stage to evaluate cost. After all, one of the tenets of open source is that the core devs don't have to be the source of all change.
But the core devs have to review the change and ultimately decide whether or not to commit it. In this case it looks as if a patch would not be accepted, so why keep going on about it, especially when there's a known work around?
The point of discussion is to find out what all the issues are, not to give up with "oh well, the core devs aren't going to accept it anyway..." -- ~Ethan~
Paul Moore wrote:
Agreed that the odd behaviour of time in a boolean context is why this whole class of bugs exists, but it's only a subclass if the wider problem that people shouldn't truth-test values without thinking
But this bites you even if you *do* think, because there is no reason to suspect that any time value should be regarded as false, especially since the docs make no mention of this. Note that "fix the docs" is *not* the right response to this, IMO -- that would just be enshrining a bad design decision. Better to fix the design while we have the chance. -- Greg
On Mar 5, 2014, at 6:23 PM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Paul Moore wrote:
Agreed that the odd behaviour of time in a boolean context is why this whole class of bugs exists, but it's only a subclass if the wider problem that people shouldn't truth-test values without thinking
But this bites you even if you *do* think, because there is no reason to suspect that any time value should be regarded as false, especially since the docs make no mention of this.
Note that "fix the docs" is *not* the right response to this, IMO -- that would just be enshrining a bad design decision. Better to fix the design while we have the chance.
-- Greg _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
The docs do mention it:
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.
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
Shai Berger <shai@...> writes:
Hi all,
This is my first post here, following a recommendation from Alexander Belopolsky to use this list, to try to convince the Python developers to reopen a ticket. I am a long-time Python user, and a Django committer.
http://bugs.python.org/issue13936 is a complaint about the fact that midnight
I strongly support fixing this issue. This behavior is so fundamentally nonsensical that resolving it is worth the potential compatibility issues. First, I'd like to dispense with the notion that ``if foo`` is somehow a bad practice. This is an EXTREMELY common practice as a shorthand for checking for None, a great many people consider it more idiomatic, this really isn't a place to bikeshed that particlar idiom. Now I'd like to address whether this is correct behavior: midnight is not a special time. It does not differ from other times in magnitutde (as with a container), it is not equivilant to the False value (as with 0). It is simply, sometimes, represented as 00:00:00 (or 12:00:00!). The fact that it's representation is composed of zeros no more makes it false than the point in 3-space at (0, 0, 0) is false. It's still a completely normal time, which in no way distinguishes itself from any other. I don't have a strong perspective on what the exact deprecation or breakage cycle should like, but it's very clear to me that this behavior is nonsensical. Out of a sample of half-a-dozen python programmers I've told about this, the *universal* response was confusion and revulsion. I strongly suspect that the majority of people writing ``if time_object`` have a bug in their code. Alex
On Mar 5, 2014, at 4:10 PM, Alex Gaynor <alex.gaynor@gmail.com> wrote:
Shai Berger <shai@...> writes:
Hi all,
This is my first post here, following a recommendation from Alexander Belopolsky to use this list, to try to convince the Python developers to reopen a ticket. I am a long-time Python user, and a Django committer.
http://bugs.python.org/issue13936 is a complaint about the fact that midnight
I strongly support fixing this issue. This behavior is so fundamentally nonsensical that resolving it is worth the potential compatibility issues.
First, I'd like to dispense with the notion that ``if foo`` is somehow a bad practice. This is an EXTREMELY common practice as a shorthand for checking for None, a great many people consider it more idiomatic, this really isn't a place to bikeshed that particlar idiom.
Now I'd like to address whether this is correct behavior: midnight is not a special time. It does not differ from other times in magnitutde (as with a container), it is not equivilant to the False value (as with 0). It is simply, sometimes, represented as 00:00:00 (or 12:00:00!). The fact that it's representation is composed of zeros no more makes it false than the point in 3-space at (0, 0, 0) is false. It's still a completely normal time, which in no way distinguishes itself from any other.
I don't have a strong perspective on what the exact deprecation or breakage cycle should like, but it's very clear to me that this behavior is nonsensical. Out of a sample of half-a-dozen python programmers I've told about this, the *universal* response was confusion and revulsion. I strongly suspect that the majority of people writing ``if time_object`` have a bug in their code.
Alex
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
I completely agree with Alex here. This is a ridiculous behavior and it should be fixed. I find the argument that it would only be fixed in 3.something and thus not be really fixed for X years disingenuous at best. That fact is true for *ALL* bug fixes in Python so by that logic we should simply give up on fixing anything in Python ever? ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On Wed, Mar 05, 2014 at 04:31:36PM -0500, Donald Stufft wrote:
I find the argument that it would only be fixed in 3.something and thus not be really fixed for X years disingenuous at best. That fact is true for *ALL* bug fixes in Python so by that logic we should simply give up on fixing anything in Python ever?
Not at all. But it does mean that the benefit of fixing it needs to be worth the wait. Suppose we said "Sure, we'll fix it, but you have to pay for the fix." How much would you be willing to pay to have it fixed? ("Pay" doesn't have to mean money. It can also mean, how much pain or inconvenience are you willing to experience, or how much time and effort are you willing to out into this.) If I said, "Oh by the way, although you have to pay right now, you won't see any benefit until 2024", now how much are you willing to pay? Design flaws are more costly to fix than outright bugs, not because they take more time and effort to change, but because you're changing behaviour that *somebody out there is relying on*. What you consider a terrible misfeature is *somebody's* critical feature, and you're proposing that we take that away. That's okay, we can do that after a suitable deprecation period. (You wouldn't want us to break your code, so you ought to extend the same courtesy to other people.) But even if there isn't anyone relying on this, and we fix it as soon as possible (which will be 3.5), your library *still* can't rely on it until it drops all support for versions below 3.5. Since you can't rely on that feature being present, you have to write your library code as if it wasn't present. So there is *absolutely no difference* between your library with this (alleged) bug being fixed, or it not being fixed. The situation for application developers is a bit different. An application developer can simply say, I'm going to immediately migrate to 3.5 to take advantage of this new feature/bug fix. That's okay too. The job of the core developers is to balance all these competing interests: the library developers, the application developers, the users who don't care one way or another but do care about some other bug that's not being fixed because people are arguing about this one, the people (actual or hypothetical) who actually like this (mis)feature the way it is, people who don't like it but have written code that relies on it, and, yes, even the core developers themselves, who are perfectly entitled to say that the effort in fixing this is greater than the benefit so they're not going to do it. If you haven't already read Nick's blog posts on these issues, you should: http://www.curiousefficiency.org/posts/2011/02/status-quo-wins-stalemate.htm... http://www.curiousefficiency.org/posts/2011/04/musings-on-culture-of-python-... For what it's worth, I think the current behaviour is a misfeature, and in a perfect world it would be fixed. But the cost of this is so miniscule, and the work-around so trivial, that although the fix is cheap, the benefit is even lower. I've only spent a few minutes reading and responding to this thread. Those few minutes are already worth far more than any benefit to me in fixing this. That's *my* personal opinion on this, others may differ, so I'm a +0 on deprecating the behaviour in 3.5 and changing it in 3.6 or 3.7, and -0.5 on changing it immediately in 3.5. -- Steven
On Mar 5, 2014, at 9:14 PM, Steven D'Aprano <steve@pearwood.info> wrote:
On Wed, Mar 05, 2014 at 04:31:36PM -0500, Donald Stufft wrote:
I find the argument that it would only be fixed in 3.something and thus not be really fixed for X years disingenuous at best. That fact is true for *ALL* bug fixes in Python so by that logic we should simply give up on fixing anything in Python ever?
Not at all. But it does mean that the benefit of fixing it needs to be worth the wait. Suppose we said "Sure, we'll fix it, but you have to pay for the fix." How much would you be willing to pay to have it fixed?
("Pay" doesn't have to mean money. It can also mean, how much pain or inconvenience are you willing to experience, or how much time and effort are you willing to out into this.)
If I said, "Oh by the way, although you have to pay right now, you won't see any benefit until 2024", now how much are you willing to pay?
Well I’ve offered to not only write the patch (assuming that someone else doesn’t do it before me since my time is limited), but also shepherd it through review and commit it myself. So I’m willing to pay with my free time which is a very limited resource :) In general I agree that everything has to go through a cost/benefit analysis, and in this case the cost and benefit are both fairly low in the grand scheme of things but the benefit does outweigh the cost as far as I’m concerned because the workaround requires you to know that the workaround is even required at all.
Design flaws are more costly to fix than outright bugs, not because they take more time and effort to change, but because you're changing behaviour that *somebody out there is relying on*. What you consider a terrible misfeature is *somebody's* critical feature, and you're proposing that we take that away. That's okay, we can do that after a suitable deprecation period. (You wouldn't want us to break your code, so you ought to extend the same courtesy to other people.)
It’s not obvious to me that design flaws are more costly to fix because people rely on even buggy behavior :) I don’t think i’ve advocated for not sending this through a normal deprecation process at all.
But even if there isn't anyone relying on this, and we fix it as soon as possible (which will be 3.5), your library *still* can't rely on it until it drops all support for versions below 3.5. Since you can't rely on that feature being present, you have to write your library code as if it wasn't present. So there is *absolutely no difference* between your library with this (alleged) bug being fixed, or it not being fixed.
There is a difference in the future when 3.5 (or whatever) is a reasonable minimum for a library. People say the same sort of thing about *anything* that is fixed or added to Python 3.x only that can’t be backported easily. There is a wait time until library authors can depend on anything new or changed in Python and there always has been an always will be. All that means is that a change now won’t be widely useful until some years down the road. But that’s true for *anything*, asyncio, PEP453, yield from, and this change too.
The situation for application developers is a bit different. An application developer can simply say, I'm going to immediately migrate to 3.5 to take advantage of this new feature/bug fix. That's okay too.
The job of the core developers is to balance all these competing interests: the library developers, the application developers, the users who don't care one way or another but do care about some other bug that's not being fixed because people are arguing about this one, the people (actual or hypothetical) who actually like this (mis)feature the way it is, people who don't like it but have written code that relies on it, and, yes, even the core developers themselves, who are perfectly entitled to say that the effort in fixing this is greater than the benefit so they're not going to do it.
If you haven't already read Nick's blog posts on these issues, you should:
http://www.curiousefficiency.org/posts/2011/02/status-quo-wins-stalemate.htm... http://www.curiousefficiency.org/posts/2011/04/musings-on-culture-of-python-...
I’ve read them. I have ~opinions~ about them but I’m not going to derail this thread with them.
For what it's worth, I think the current behaviour is a misfeature, and in a perfect world it would be fixed. But the cost of this is so miniscule, and the work-around so trivial, that although the fix is cheap, the benefit is even lower. I've only spent a few minutes reading and responding to this thread. Those few minutes are already worth far more than any benefit to me in fixing this. That's *my* personal opinion on this, others may differ, so I'm a +0 on deprecating the behaviour in 3.5 and changing it in 3.6 or 3.7, and -0.5 on changing it immediately in 3.5.
As I said, the work around is trivial, *once you know it is needed*. This change is reducing the cognitive burden and chance for mishap for users who aren’t aware of this misfeature. It’s making Python a friendlier language to use.
-- Steven _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On 6 March 2014 03:14:57 CET, Steven D'Aprano <steve@pearwood.info> wrote:
On Wed, Mar 05, 2014 at 04:31:36PM -0500, Donald Stufft wrote:
I find the argument that it would only be fixed in 3.something and thus not be really fixed for X years disingenuous at best. That fact is true for *ALL* bug fixes in Python so by that logic we should simply give up on fixing anything in Python ever?
Not at all. But it does mean that the benefit of fixing it needs to be worth the wait. Suppose we said "Sure, we'll fix it, but you have to pay for the fix." How much would you be willing to pay to have it fixed?
("Pay" doesn't have to mean money. It can also mean, how much pain or inconvenience are you willing to experience, or how much time and effort are you willing to out into this.)
If I said, "Oh by the way, although you have to pay right now, you won't see any benefit until 2024", now how much are you willing to pay?
Design flaws are more costly to fix than outright bugs, not because they take more time and effort to change, but because you're changing behaviour that *somebody out there is relying on*.
Just because "somebody" is relying on a wart in the language it doesn't mean that this person is knowing what they are doing, is representative of the rest of Python users or that the wart shouldn't be removed. In my opinion it even means that the person is a bad programmer in a way, because even though he is relying documented behavior, that behavior is relatively unknown and using it intentionally certainly doesn't help readability. Actually i think that this programmer put extra effort into making their own code less readable. Partially, this point has already been made many times in this thread, but apparently this doesn't suffice to make the opposing side realize or respond to it. What you consider a
terrible misfeature is *somebody's* critical feature, and you're proposing that we take that away. That's okay, we can do that after a suitable deprecation period. (You wouldn't want us to break your code, so you ought to extend the same courtesy to other people.)
But even if there isn't anyone relying on this, and we fix it as soon as possible (which will be 3.5), your library *still* can't rely on it until it drops all support for versions below 3.5.
Arguably many libraries already do rely on this currently nonexistent feature.
Since you can't rely
on that feature being present, you have to write your library code as if it wasn't present. So there is *absolutely no difference* between your library with this (alleged) bug being fixed, or it not being fixed.
The situation for application developers is a bit different. An application developer can simply say, I'm going to immediately migrate to 3.5 to take advantage of this new feature/bug fix. That's okay too.
The job of the core developers is to balance all these competing interests: the library developers, the application developers, the users who don't care one way or another but do care about some other bug that's not being fixed because people are arguing about this one, the people (actual or hypothetical) who actually like this (mis)feature the
way it is, people who don't like it but have written code that relies on it, and, yes, even the core developers themselves, who are perfectly entitled to say that the effort in fixing this is greater than the benefit so they're not going to do it.
If you haven't already read Nick's blog posts on these issues, you should:
http://www.curiousefficiency.org/posts/2011/02/status-quo-wins-stalemate.htm... http://www.curiousefficiency.org/posts/2011/04/musings-on-culture-of-python-...
For what it's worth, I think the current behaviour is a misfeature, and
in a perfect world it would be fixed. But the cost of this is so miniscule, and the work-around so trivial, that although the fix is cheap, the benefit is even lower. I've only spent a few minutes reading
and responding to this thread. Those few minutes are already worth far more than any benefit to me in fixing this. That's *my* personal opinion on this, others may differ, so I'm a +0 on deprecating the behaviour in
3.5 and changing it in 3.6 or 3.7, and -0.5 on changing it immediately in 3.5.
Le 06/03/2014 03:14, Steven D'Aprano a écrit :
For what it's worth, I think the current behaviour is a misfeature, and in a perfect world it would be fixed. But the cost of this is so miniscule, and the work-around so trivial, that although the fix is cheap, the benefit is even lower.
Again, the problem is not the cost of the workaround, but the cost of sporadic unexpected failures when you don't *know* your code is vulnerable to this issue. Regards Antoine.
On Wed, Mar 5, 2014 at 4:10 PM, Alex Gaynor <alex.gaynor@gmail.com> wrote:
I strongly support fixing this issue. This behavior is so fundamentally nonsensical that resolving it is worth the potential compatibility issues.
Does anyone know a language that (a) has a time-of-day class in stdlib; (b) allows instances of that class to be used in boolean context, and (c) has a midnight represented by an instance that is truthy? Here is my example of an entirely independent of Python language [1] that nevertheless has the same behavior for time-of-day objects: q)`boolean$00:00:00.000 0b q)`boolean$00:00:00.001 1b [1] http://code.kx.com/wiki/Reference/Datatypes
On Thu, Mar 6, 2014 at 8:36 AM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Wed, Mar 5, 2014 at 4:10 PM, Alex Gaynor <alex.gaynor@gmail.com> wrote:
I strongly support fixing this issue. This behavior is so fundamentally nonsensical that resolving it is worth the potential compatibility issues.
Does anyone know a language that (a) has a time-of-day class in stdlib; (b) allows instances of that class to be used in boolean context, and (c) has a midnight represented by an instance that is truthy?
Pike fits all three of those, but (c) is trivially true: any object in Pike is truthy. Of course, you can choose to represent time-of-day with an integer, in which case an integer of 0 will be false; but if you use floating-point (for sub-second accuracy), then it's still safe, because any float is truthy (even 0.0 - and if you think that's illogical, consider that expecting 0.0 to be false is effectively making an implicit equality comparison, with all the issues of equality comparisons on floats _plus_ the issues of doing that implicitly). If Python didn't have to worry about backward compatibility (that is, if this were being implemented new now), then I'd say a null time range should be false, but any date, datetime, or time, regardless of what it represents, is true. Maybe there could be a "null date" value that isn't a date, but None should be able to do that. ChrisA
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc. datetime.time(0,0,0) is just another one :-) BTW: If you want to test for None, well, then do test for None and not for some property of None: if x is None: print ("x is None, which implies it's False") print ("Oh, and do note that the inverse is not necessarily true :-)") In logic: A -> B does not imply B -> A; it only implies (not B) -> (not A). in Python again: if bool(x) is not False: print ("x is not None") -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On Mar 5, 2014, at 5:04 PM, M.-A. Lemburg <mal@egenix.com> wrote:
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc.
datetime.time(0,0,0) is just another one :-)
BTW: If you want to test for None, well, then do test for None and not for some property of None:
if x is None: print ("x is None, which implies it's False") print ("Oh, and do note that the inverse is not necessarily true :-)")
In logic:
A -> B does not imply B -> A; it only implies (not B) -> (not A).
in Python again:
if bool(x) is not False: print ("x is not None")
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Most of those are pretty easy to reason about being “False”, they all have some concept of empty, nothing, etc. Midnight however is not any more or less a time than one minute past midnight. Even worse, if you consider not explicitly checking for None a bug in that application code, debugging that code is made much more difficult because of the fact that for some reason "midnight" is considered False. That is the real problem here, sure people should be using ``is None`` checks but until people are perfect and never write buggy code, we have to contend with the fact that sometimes people will not write ``is None``. Making that particular error harder to debug is doing nothing but wasting their time and having Python be punitive for what in many cases may be a mistake. ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On 05.03.2014 23:10, Donald Stufft wrote:
On Mar 5, 2014, at 5:04 PM, M.-A. Lemburg <mal@egenix.com> wrote:
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc.
datetime.time(0,0,0) is just another one :-)
Most of those are pretty easy to reason about being “False”, they all have some concept of empty, nothing, etc. Midnight however is not any more or less a time than one minute past midnight. Even worse, if you consider not explicitly checking for None a bug in that application code, debugging that code is made much more difficult because of the fact that for some reason "midnight" is considered False.
The reasoning becomes clear if you regard a time value as number of seconds since midnight (the time of day is a relative measure). This is 0 for time(0,0,0). -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On Mar 5, 2014, at 5:27 PM, M.-A. Lemburg <mal@egenix.com> wrote:
On 05.03.2014 23:10, Donald Stufft wrote:
On Mar 5, 2014, at 5:04 PM, M.-A. Lemburg <mal@egenix.com> wrote:
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc.
datetime.time(0,0,0) is just another one :-)
Most of those are pretty easy to reason about being “False”, they all have some concept of empty, nothing, etc. Midnight however is not any more or less a time than one minute past midnight. Even worse, if you consider not explicitly checking for None a bug in that application code, debugging that code is made much more difficult because of the fact that for some reason "midnight" is considered False.
The reasoning becomes clear if you regard a time value as number of seconds since midnight (the time of day is a relative measure). This is 0 for time(0,0,0).
Except time isn’t number of seconds since midnight.
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
M.-A. Lemburg wrote:
The reasoning becomes clear if you regard a time value as number of seconds since midnight (the time of day is a relative measure).
But there's no clear reason to regard that particular way of representing a time of day as superior to any other. -- Greg
On 6 Mar 2014 08:11, "Donald Stufft" <donald@stufft.io> wrote:
Most of those are pretty easy to reason about being "False", they all
concept of empty, nothing, etc. Midnight however is not any more or less a time than one minute past midnight. Even worse, if you consider not explicitly checking for None a bug in that application code, debugging that code is made much more difficult because of the fact that for some reason "midnight" is considered False.
That is the real problem here, sure people should be using ``is None`` checks but until people are perfect and never write buggy code, we have to contend with the fact that sometimes people will not write ``is None``. Making
have some that
particular error harder to debug is doing nothing but wasting their time and having Python be punitive for what in many cases may be a mistake.
Right, the core problem here is changing an often harmless style problem into a subtle behavioural bug. I changed the title on http://bugs.python.org/issue13936 accordingly, reopened it and described the appropriate next steps for anyone that is motivated enough about the issue to actually do the work. We'll see if the enthusiasm for changing the behaviour translates into anyone being sufficiently interested to work out the details of the deprecation warning and the associated documentation and test updates. Regards, Nick.
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372
DCFA
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Mar 5, 2014, at 5:43 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On 6 Mar 2014 08:11, "Donald Stufft" <donald@stufft.io> wrote:
Most of those are pretty easy to reason about being “False”, they all have some concept of empty, nothing, etc. Midnight however is not any more or less a time than one minute past midnight. Even worse, if you consider not explicitly checking for None a bug in that application code, debugging that code is made much more difficult because of the fact that for some reason "midnight" is considered False.
That is the real problem here, sure people should be using ``is None`` checks but until people are perfect and never write buggy code, we have to contend with the fact that sometimes people will not write ``is None``. Making that particular error harder to debug is doing nothing but wasting their time and having Python be punitive for what in many cases may be a mistake.
Right, the core problem here is changing an often harmless style problem into a subtle behavioural bug. I changed the title on http://bugs.python.org/issue13936 accordingly, reopened it and described the appropriate next steps for anyone that is motivated enough about the issue to actually do the work.
We'll see if the enthusiasm for changing the behaviour translates into anyone being sufficiently interested to work out the details of the deprecation warning and the associated documentation and test updates.
Regards, Nick.
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Thanks Nick :) ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
"M.-A. Lemburg" <mal@egenix.com> writes:
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc.
datetime.time(0,0,0) is just another one :-)
The salient difference is: With the former set, they all embody concepts of either “empty” or “zero magnitude”. They strongly connote “not there” within their domain of values, hence map well to Boolean false. With ‘datetime.time’ there is no equivalent: midnight is not special in the way “empty” or “zero magnitude” are. Midnight is an arbitrary point on a continuum, and is not a “not there” value. It should not be Boolean false any more than any other time. -- \ “What is it that makes a complete stranger dive into an icy | `\ river to save a solid gold baby? Maybe we'll never know.” —Jack | _o__) Handey | Ben Finney
On 05.03.2014 23:16, Ben Finney wrote:
"M.-A. Lemburg" <mal@egenix.com> writes:
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc.
datetime.time(0,0,0) is just another one :-)
The salient difference is:
With the former set, they all embody concepts of either “empty” or “zero magnitude”. They strongly connote “not there” within their domain of values, hence map well to Boolean false.
With ‘datetime.time’ there is no equivalent: midnight is not special in the way “empty” or “zero magnitude” are. Midnight is an arbitrary point on a continuum, and is not a “not there” value.
This is true in a date/time continuum, but not true if you regard time as "time of day". In the latter interpretation, the day starts at midnight, so the day is "not there" yet at midnight.
It should not be Boolean false any more than any other time.
Just like with most interpretations, you can just as easily find one which implies the exact opposite. It's all smoke and mirrors anyway :-) Perhaps Tim just wanted to be able to make float(time(0,0,0)) return 0.0 seconds some day. That's what mxDateTime does for time values. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On Mar 5, 2014, at 5:35 PM, M.-A. Lemburg <mal@egenix.com> wrote:
On 05.03.2014 23:16, Ben Finney wrote:
"M.-A. Lemburg" <mal@egenix.com> writes:
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc.
datetime.time(0,0,0) is just another one :-)
The salient difference is:
With the former set, they all embody concepts of either “empty” or “zero magnitude”. They strongly connote “not there” within their domain of values, hence map well to Boolean false.
With ‘datetime.time’ there is no equivalent: midnight is not special in the way “empty” or “zero magnitude” are. Midnight is an arbitrary point on a continuum, and is not a “not there” value.
This is true in a date/time continuum, but not true if you regard time as "time of day". In the latter interpretation, the day starts at midnight, so the day is "not there" yet at midnight.
The day is absolutely here at midnight. Midnight isn’t some void where time ceases to exist.
It should not be Boolean false any more than any other time.
Just like with most interpretations, you can just as easily find one which implies the exact opposite.
It's all smoke and mirrors anyway :-)
Perhaps Tim just wanted to be able to make float(time(0,0,0)) return 0.0 seconds some day. That's what mxDateTime does for time values.
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On 05.03.2014 23:38, Donald Stufft wrote:
On Mar 5, 2014, at 5:35 PM, M.-A. Lemburg <mal@egenix.com> wrote:
On 05.03.2014 23:16, Ben Finney wrote:
"M.-A. Lemburg" <mal@egenix.com> writes:
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc.
datetime.time(0,0,0) is just another one :-)
The salient difference is:
With the former set, they all embody concepts of either “empty” or “zero magnitude”. They strongly connote “not there” within their domain of values, hence map well to Boolean false.
With ‘datetime.time’ there is no equivalent: midnight is not special in the way “empty” or “zero magnitude” are. Midnight is an arbitrary point on a continuum, and is not a “not there” value.
This is true in a date/time continuum, but not true if you regard time as "time of day". In the latter interpretation, the day starts at midnight, so the day is "not there" yet at midnight.
The day is absolutely here at midnight. Midnight isn’t some void where time ceases to exist.
No, but midnight starts the day, just like 0 starts the positive real numbers (itself not being positive) :-)
It should not be Boolean false any more than any other time.
Just like with most interpretations, you can just as easily find one which implies the exact opposite.
It's all smoke and mirrors anyway :-)
Perhaps Tim just wanted to be able to make float(time(0,0,0)) return 0.0 seconds some day. That's what mxDateTime does for time values.
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
-- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
On Mar 5, 2014, at 6:08 PM, M.-A. Lemburg <mal@egenix.com> wrote:
On 05.03.2014 23:38, Donald Stufft wrote:
On Mar 5, 2014, at 5:35 PM, M.-A. Lemburg <mal@egenix.com> wrote:
On 05.03.2014 23:16, Ben Finney wrote:
"M.-A. Lemburg" <mal@egenix.com> writes:
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc.
datetime.time(0,0,0) is just another one :-)
The salient difference is:
With the former set, they all embody concepts of either “empty” or “zero magnitude”. They strongly connote “not there” within their domain of values, hence map well to Boolean false.
With ‘datetime.time’ there is no equivalent: midnight is not special in the way “empty” or “zero magnitude” are. Midnight is an arbitrary point on a continuum, and is not a “not there” value.
This is true in a date/time continuum, but not true if you regard time as "time of day". In the latter interpretation, the day starts at midnight, so the day is "not there" yet at midnight.
The day is absolutely here at midnight. Midnight isn’t some void where time ceases to exist.
No, but midnight starts the day, just like 0 starts the positive real numbers (itself not being positive) :-)
And yet 0 is False not because it starts the positive real numbers (not sure what that has to do with a value in an integer type) but because it signifies an empty or zero magnitude object.
It should not be Boolean false any more than any other time.
Just like with most interpretations, you can just as easily find one which implies the exact opposite.
It's all smoke and mirrors anyway :-)
Perhaps Tim just wanted to be able to make float(time(0,0,0)) return 0.0 seconds some day. That's what mxDateTime does for time values.
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On 06.03.2014 00:10, Donald Stufft wrote:
On Mar 5, 2014, at 6:08 PM, M.-A. Lemburg <mal@egenix.com> wrote:
On 05.03.2014 23:38, Donald Stufft wrote:
On Mar 5, 2014, at 5:35 PM, M.-A. Lemburg <mal@egenix.com> wrote:
On 05.03.2014 23:16, Ben Finney wrote:
"M.-A. Lemburg" <mal@egenix.com> writes:
I don't know what all the fuzz is about. There are lots of types in Python that evaluate to False in certain corner cases, e.g. "", (), [], {}, 0, 0.0, 0j, None, etc.
datetime.time(0,0,0) is just another one :-)
The salient difference is:
With the former set, they all embody concepts of either “empty” or “zero magnitude”. They strongly connote “not there” within their domain of values, hence map well to Boolean false.
With ‘datetime.time’ there is no equivalent: midnight is not special in the way “empty” or “zero magnitude” are. Midnight is an arbitrary point on a continuum, and is not a “not there” value.
This is true in a date/time continuum, but not true if you regard time as "time of day". In the latter interpretation, the day starts at midnight, so the day is "not there" yet at midnight.
The day is absolutely here at midnight. Midnight isn’t some void where time ceases to exist.
No, but midnight starts the day, just like 0 starts the positive real numbers (itself not being positive) :-)
And yet 0 is False not because it starts the positive real numbers (not sure what that has to do with a value in an integer type) but because it signifies an empty or zero magnitude object.
Well, the duration between the start of day and midnight is zero and returning to the interpretation of time of day being a measurement relative to midnight, this makes time(0,0,0) a zero magnitude object. q.e.d. Anyway, it's all up in the air for interpretation :-) There are reasons to have time(0,0,0) be False just as there are reasons for it not to be False. There's no clear answer.
It should not be Boolean false any more than any other time.
Just like with most interpretations, you can just as easily find one which implies the exact opposite.
It's all smoke and mirrors anyway :-)
Perhaps Tim just wanted to be able to make float(time(0,0,0)) return 0.0 seconds some day. That's what mxDateTime does for time values.
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 05 2014)
> Python Projects, Consulting and Support ... http://www.egenix.com/ > mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ > mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
-- Marc-Andre Lemburg eGenix.com
Professional Python Services directly from the Source (#1, Mar 05 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 35 days to go
::::: Try our mxODBC.Connect Python Database Interface for free ! ::::::
eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
-- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Mar 06 2014)
Python Projects, Consulting and Support ... http://www.egenix.com/ mxODBC.Zope/Plone.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
2014-04-09: PyCon 2014, Montreal, Canada ... 34 days to go ::::: Try our mxODBC.Connect Python Database Interface for free ! :::::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/
M.-A. Lemburg wrote:
Well, the duration between the start of day and midnight is zero and returning to the interpretation of time of day being a measurement relative to midnight,
The point of having time objects as an abstraction is so that you *don't* have to think of them as an offset from anything. You simply think of them as times. -- Greg
On Wed, Mar 5, 2014 at 6:10 PM, Donald Stufft <donald@stufft.io> wrote:
And yet 0 is False not because it starts the positive real numbers (not sure what that has to do with a value in an integer type) but because it signifies an empty or zero magnitude object.
No. It is False because it equals False:
0 == False True
Numbers are not containers. You cannot tell whether a number is empty or not. Python interpretation of numbers in boolean context is different from that of containers. I think the disagreement stems from different view on what time-of-day is. For me, it is the fraction of the day that passed or distance from midnight and in any case fundamentally some kind of number expressed in a Babylonian base-60 notation. I think proponents of bool(time(0)) == True view it as a container of hours, minutes and seconds.
Alexander Belopolsky wrote:
For me, it is the fraction of the day that passed or distance from midnight and in any case fundamentally some kind of number expressed in a Babylonian base-60 notation.
Yes, and that number is conventionally written as "12:00", which is *obviously* equal to zero... er, what? -- Greg
The 12:00 notation is US-centric. Or, perhaps, common in Anglophone countries. The rest of the world does not use the AM/PM conventions and calls midnight 00:00. On Wed, Mar 5, 2014 at 9:38 PM, Greg Ewing <greg.ewing@canterbury.ac.nz>wrote:
Alexander Belopolsky wrote:
For me, it is the fraction of the day that passed or distance from midnight and in any case fundamentally some kind of number expressed in a Babylonian base-60 notation.
Yes, and that number is conventionally written as "12:00", which is *obviously* equal to zero... er, what?
-- Greg _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
Le 06/03/2014 00:22, Alexander Belopolsky a écrit :
No. It is False because it equals False:
0 == False True
I think this is a misunderstanding on your part. Python considered 0 as false-y before the bool type was introduced in the language. Actually, 0 was *the* spelling of "false" before "False" was introduced.
I think proponents of bool(time(0)) == True view it as a container of hours, minutes and seconds.
No, they view it as an abstraction around the common concept of time. Regards Antoine.
On Thu, Mar 6, 2014 at 5:39 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
I think proponents of bool(time(0)) == True view it as a container of hours, minutes and seconds.
No, they view it as an abstraction around the common concept of time.
OK, it looks like we are getting closer to the root of the disagreement. We are talking about the datetime.time type here. It is not "an abstraction around the common concept of time." That role belongs to the datetime.datetime type. The datetime.time type is an implementation of the concept of the *time of day*. In other words, for a datetime instance dt, dt.time() is the fractional part of dt and dt.date() is the integral part. A test for dt.time() is just as natural for datetime as a test for t % (24*60*60) for posix time.
[M.-A. Lemburg]
... Perhaps Tim just wanted to be able to make float(time(0,0,0)) return 0.0 seconds some day. That's what mxDateTime does for time values.
Now you're getting close :-) There was an endless list of features various people said they wanted in a datetime module. At the time, we picked a large subset we could do in time, and released it. There was every expectation that more features may be added later. Among them were various kinds of conversions and various kinds of arithmetic enhancements. For example, modular arithmetic on time objects was a possibility. In that case, time(0, 0, 0) would become "a zero value" just as much as 0 or 0.0 or 0j or 0x00 are zero values. But if it weren't _treated_ as "a zero value" from the start, backward compatibility would prevent it from being treated as a zero value for the first time later. So, what the heck - like all other zero/empty objects, time(0, 0, 0) was fiddled to evaluate to False in a Boolean context, AND (something nobody seems to have noticed yet) plain time() was fiddled to return it:
bool(int()) is bool(complex()) is bool(float()) is bool(datetime.time()) True
People can say that "midnight" isn't a compelling "sentinel value", and I agree about the "compelling" part, but in all implementation respects it does _act_ as "a sentinel value". If people don't want to use it as one, fine, that's their choice. I've used it as one, and it works fine for that purpose. As things turned out, nobody seemed to care much about modular (are any other kind of) arithmetic on time objects after all. Nevertheless, we went about 9 years before anyone complained about the code added to support the _possibility_ for doing that cleanly. The "seconds from midnight" view was also in play, with a view toward clean conversions to float-ish types.
Tim Peters wrote:
For example, modular arithmetic on time objects was a possibility. In that case, time(0, 0, 0) would become "a zero value"
That only follows if you interpret "modular arithmetic on time objects" as meaning that you can directly add one time object to another. But that would be confusing times with timedeltas. The concept of a zero *timedelta* makes sense, but not a "zero time".
People can say that "midnight" isn't a compelling "sentinel value", and I agree about the "compelling" part, but in all implementation respects it does _act_ as "a sentinel value".
If someone is using midnight to represent an unspecified time, I'd say they have a bug. An appointment scheduled at midnight is not the same as an appointment that's not scheduled at all. -- Greg
[Tim Peters]
For example, modular arithmetic on time objects was a possibility. In that case, time(0, 0, 0) would become "a zero value"
[Greg Ewing <greg.ewing@canterbury.ac.nz>]
That only follows if you interpret "modular arithmetic on time objects" as meaning that you can directly add one time object to another.
Of course.
But that would be confusing times with timedeltas. The concept of a zero *timedelta* makes sense, but not a "zero time".
The only difference between time and timedelta is in implementation details. In effect, time values are (or _can_ be viewed as) a subset of timedelta values, restricted to non-negative durations strictly less than 24 hours. From that point of view, it's obvious how to do modular (mod 24 hours) arithmetic on time values. And some people said they wanted that. It wasn't obviously useful enough to implement at first, but neither did we want to preclude it in the future. I don't personally want it.
People can say that "midnight" isn't a compelling "sentinel value", and I agree about the "compelling" part, but in all implementation respects it does _act_ as "a sentinel value".
If someone is using midnight to represent an unspecified time, I'd say they have a bug. An appointment scheduled at midnight is not the same as an appointment that's not scheduled at all.
Again obviously so. In much the same way, there's nothing forbidden about -1 as an integer, but it's ubiquitously used as an error return in CPython's implementation. That doesn't mean CPython is buggy, it implies that CPython has other ways to distinguish whether a -1 return does or does not mean "error!". For example, in some applications (not all), "exactly midnight" simply can't happen as a legitimate starting or ending time.
Tim Peters wrote:
The only difference between time and timedelta is in implementation details.
Only if you define it that way. To my way of thinking, a time-of-day is like a mod-24-hour datetime, not a mod-24-hour timedelta. It seems you're thinking of it as a mod-24-hour timedelta. Given that, I can see why you think that it's bizarre to attach a timezone to one. In fact, I'd say if that's really how a time object is meant to be interpreted, they shouldn't even be *allowed* to have a timezone, any more than a timedelta can. In any case, I don't see how this interpretation justifies the truthiness behaviour with timezones. From my reading of the docs, the *intention* seems to be for midnight UTC to be false and all other times to be true. I would never have suspected the actual behaviour with negative offsets, which looks like an outright bug to me. Or is there some reason for it that I'm not seeing? -- Greg
On Fri, Mar 7, 2014 at 9:38 AM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
It seems you're thinking of it as a mod-24-hour timedelta. Given that, I can see why you think that it's bizarre to attach a timezone to one. In fact, I'd say if that's really how a time object is meant to be interpreted, they shouldn't even be *allowed* to have a timezone, any more than a timedelta can.
Which is the earlier time: 18:00:00 Australia/Melbourne, or 03:00:00 America/New_York? If you add a date to it, you could get an aware datetime that represents an exact instant, and compare them. Can you compare them, as they are? Is it reliable? This is nothing to do with timedelta versus datetime. It's to do with attaching timezone information to a dateless time. ChrisA
Chris Angelico wrote:
Which is the earlier time: 18:00:00 Australia/Melbourne, or 03:00:00 America/New_York?
If you think of times of day as modular, then it doesn't make sense to ask whether one is earlier than another. But to clarify my position: It's okay to attach a timezone to a *time of day*. It's *not* okay to attach a timezone to a *difference* between times of day. This is one of the reasons it's imortant to keep the two concepts clearly separated, as we do for datetimes and timedeltas. Thinking that time(0, 0, 0) should be false seems to be a result of conflating the two. -- Greg
On Fri, Mar 7, 2014 at 11:04 AM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Chris Angelico wrote:
Which is the earlier time: 18:00:00 Australia/Melbourne, or 03:00:00 America/New_York?
If you think of times of day as modular, then it doesn't make sense to ask whether one is earlier than another.
It should be possible to ask if they're equal, at least. Are they?
But to clarify my position: It's okay to attach a timezone to a *time of day*. It's *not* okay to attach a timezone to a *difference* between times of day.
What operations can you do on a time-of-day-with-timezone? Give me some examples, and I'll show you, with the above two examples, how quirky that can be. ChrisA
On Mar 6, 2014, at 7:13 PM, Chris Angelico <rosuav@gmail.com> wrote:
On Fri, Mar 7, 2014 at 11:04 AM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote: Chris Angelico wrote:
Which is the earlier time: 18:00:00 Australia/Melbourne, or 03:00:00 America/New_York?
If you think of times of day as modular, then it doesn't make sense to ask whether one is earlier than another.
It should be possible to ask if they're equal, at least. Are they?
But to clarify my position: It's okay to attach a timezone to a *time of day*. It's *not* okay to attach a timezone to a *difference* between times of day.
What operations can you do on a time-of-day-with-timezone? Give me some examples, and I'll show you, with the above two examples, how quirky that can be.
It's not an operation but a time object with a timezone is perfect for representing an event which occurs everyday at 11:30 EST
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 7/03/2014 1:13 p.m., Chris Angelico wrote:
What operations can you do on a time-of-day-with-timezone? Give me some examples, and I'll show you, with the above two examples, how quirky that can be.
I'm not particularly attached to the idea of times of day with timezones; I wouldn't mind if they didn't exist. The main point is that they definitely don't make sense for time differences. An hour in New York is the same length as an hour in Melbourne, on any day of the year, as far as I know. (At least in modern times. It seems the Romans divided daytime and nighttime into 12 equal hours each, so that the length of an hour varied with latitude and time of year, and daytime hours were a different length from nighttime hours! I hate to think what a Python datetime module would have looked like back then.) -- Greg
On Fri, Mar 7, 2014 at 1:12 PM, Greg <greg.ewing@canterbury.ac.nz> wrote:
On 7/03/2014 1:13 p.m., Chris Angelico wrote:
What operations can you do on a time-of-day-with-timezone? Give me some examples, and I'll show you, with the above two examples, how quirky that can be.
I'm not particularly attached to the idea of times of day with timezones; I wouldn't mind if they didn't exist.
The main point is that they definitely don't make sense for time differences. An hour in New York is the same length as an hour in Melbourne, on any day of the year, as far as I know.
Sure. So a timedelta with timezone makes no sense. But we're not talking about timedelta here, we're talking about time. (And we're also ostriching leap seconds away. Don't wanna know! Don't wanna know!) A calendar that supports repeating events is going to need some kind of rule system anyway. I don't think a "time with timezone" is sufficient for that. I haven't used that kind of system (ever!), but from Lennart Regebro's PyCon 2013 talk "Blame it on Caesar", I gather that there is such a thing in Python. So... from this thread, it seems like there's only one real use for time-with-timezone: fracturing a datetime-with-timezone into two parts, date and time, and recombining losslessly. Anyone have other uses? ChrisA
[Tim]
The only difference between time and timedelta is in implementation details.
[Greg]
Only if you define it that way.
Sorry, don't know what you mean there. But then I don't draw much distinction between "defining" something and "implementation details". `time` and `timedelta` objects are both exact values with microsecond resolution; the operations each supports is a matter of definition + implementation.
To my way of thinking, a time-of-day is like a mod-24-hour datetime, not a mod-24-hour timedelta.
Heh. I'll ponder that and see if I can make sense of it tomorrow ;-)
It seems you're thinking of it as a mod-24-hour timedelta.
No. I'm saying: (1) it _can_ be thought of that way; and, (2) while that's not how `time` was initially released, there was some effort not to _preclude_ later extensions viewing it that way. As is, the datetime module offers no obvious solution to problems like "the clock reads 6:43 now; what will it read 57 minutes from now?". `time` objects support no arithmetic of any kind. The world wouldn't really collapse if time(6, 43) + time(minute=57), or if time(6, 43) + timedelta(minutes=57), didn't blow up but returned the obvious result as a time object.
Given that, I can see why you think that it's bizarre to attach a timezone to one. In fact, I'd say if that's really how a time object is meant to be interpreted, they shouldn't even be *allowed* to have a timezone, any more than a timedelta can.
No, it's bizarre to attach a timezone to a time object because most tzinfo subclasses don't - and can't - know what to return for the UTC offset in the absence of - at least - month and day info too. Hour, minute, second and microsecond aren't usually enough to determine whether "daylight time" is in effect, and most tzinfo subclasses do attempt to model daylight time. And because daylight time is a political conceit, most tzinfo subclasses also need year info. That has nothing to do with whether times are viewed abstractly, concretely, etc - it has to do with that time zones generally aren't _defined_ in terms of isolated time-of-day info. It's 7:13 PM in US Central. Is that daylight (CDT) or standard (CST) time? It's impossible to answer. What's the corresponding UTC time? Ditto. While time objects don't support arithmetic they do support all comparison operators. And this is another case where the docs explain _how_ comparison of aware time objects works, but a close reading reveals that the result generally makes no sense. In effect, the time objects are converted to UTC before comparison, but correct conversion to UTC is generally impossible without month, day and year info too. A user intimately familiar with how all this works can define tzinfo subclasses that make time object conversions with the kinds of errors they prefer. but that too is ... bizarre.
In any case, I don't see how this interpretation justifies the truthiness behaviour with timezones. From my reading of the docs, the *intention* seems to be for midnight UTC to be false and all other times to be true. I would never have suspected the actual behaviour with negative offsets, which looks like an outright bug to me. Or is there some reason for it that I'm not seeing?
Truthiness would become useful if time() (i.e., the naive time object with all 0 fields) acted as an additive identity in some extension that allowed time object addition. That's all. I put more thought into writing that sentence than into what truthiness of aware time objects returned - and I bet it shows ;-) If more thought had gone into it, and assuming it remained the case that time objects had to support tzinfo members (despite that doing so makes little sense), and assuming it remained the case that we wanted to keep the _possibility_ of later time arithmetic open, I'd amend the truthiness rules to that False was equivalent to that the naive time converted to minutes minus the UTC offset (or 0 if tzinfo=None) is 0 mod 24*60. Which would be a long way of saying "midnight UTC", but a better way because: (1) again, it's not usually possible to do _correct_ conversion of a time object to UTC; and, (2) it's not "midnight" that's important to an additive identity, it's "0".
On Mar 6, 2014, at 8:19 PM, Tim Peters <tim.peters@gmail.com> wrote:
No, it's bizarre to attach a timezone to a time object because most tzinfo subclasses don't - and can't - know what to return for the UTC offset in the absence of - at least - month and day info too.
It’s completely reasonable to attach a timezone to a time object. If I schedule an event in a calendar for Mar 7th, at 12pm Eastern, then absolutely that should be represented as a date time. But what if I want to schedule an event that occurs every day at 11:30AM EST? There’s no date associated with it (except an infinite set, or the span of my life I suppose!) it’s just 11:30AM EST. ----------------- Donald Stufft PGP: 0x6E3CBCE93372DCFA // 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA
On Fri, Mar 7, 2014 at 12:25 PM, Donald Stufft <donald@stufft.io> wrote:
If I schedule an event in a calendar for Mar 7th, at 12pm Eastern, then absolutely that should be represented as a date time. But what if I want to schedule an event that occurs every day at 11:30AM EST? There’s no date associated with it (except an infinite set, or the span of my life I suppose!) it’s just 11:30AM EST.
Repeating events need their own handling. Maybe it's safe to use 11:30AM local time, but what if you specify 1:30AM America/New_York? How can you render that onto a calendar? There'll be times when it breaks - one day a year when it's ambiguous, and one day a year when it doesn't exist. How can you work with a time that might not exist? ChrisA
[Donald Stufft]
If I schedule an event in a calendar for Mar 7th, at 12pm Eastern, then absolutely that should be represented as a date time. But what if I want to schedule an event that occurs every day at 11:30AM EST? There's no date associated with it (except an infinite set, or the span of my life I suppose!) it's just 11:30AM EST.
[Chris Angelico <rosuav@gmail.com>]
Repeating events need their own handling. Maybe it's safe to use 11:30AM local time, but what if you specify 1:30AM America/New_York? How can you render that onto a calendar? There'll be times when it breaks - one day a year when it's ambiguous, and one day a year when it doesn't exist. How can you work with a time that might not exist?
Filling in the blanks, I presume that Donald's "EST" is what the docs call a "fixed-offset" tzinfo subclass: it represents a fixed offset from UTC. It's what a US East Coast clock would look like if the idiotic ;-) "daylight saving" rules were repealed, and the East Coast stayed on "standard" time forever. Such classes are utterly problem-free. No ambiguities, no clock leaping ahead or leaping back, no "missing" hours, no "duplicate" hours, they just march on forever in perfect lockstep with UTC. Alas, such classes are also rare.
From: Tim Peters <tim.peters@gmail.com> Sent: Thursday, March 6, 2014 5:51 PM
Filling in the blanks, I presume that Donald's "EST" is what the docs call a "fixed-offset" tzinfo subclass: it represents a fixed offset from UTC. It's what a US East Coast clock would look like if the idiotic ;-) "daylight saving" rules were repealed, and the East Coast stayed on "standard" time forever.
Except that it still wouldn't work for dates before 2014. Or, of course, if DST were reinstated in the future. If I were dictator of the world, I would ban DST. And 12-hour times. And MDY date formats. In fact, I would probably ban timezones. How hard would it be to learn that on the east coast you work from 0400-1200, not 9:00am-5:00pm?
On Fri, Mar 7, 2014 at 1:19 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
If I were dictator of the world, I would ban DST. And 12-hour times. And MDY date formats. In fact, I would probably ban timezones. How hard would it be to learn that on the east coast you work from 0400-1200, not 9:00am-5:00pm?
Fortunately, you don't have to be dictator of the whole world. As admin of Minstrel Hall (minstrelhall.com), I simply made the declaration that all campaign times would be listed in UTC. That effectively bans DST and timezones. The in-game clock uses 24-hour time, so a lot of people follow that. Most things happen on a weekly cycle, so I haven't done anything about MDY (the in-game clock currently reads "Fri 04:36:05"), but again, I would personally use YMD most likely, and a lot of people will just follow that. Let's start creating pockets like that all over the place. Train people to manage international events in UTC. Eventually the benefits will start to be more visible :) ChrisA
On Fri, Mar 07, 2014 at 03:38:02PM +1100, Chris Angelico wrote:
On Fri, Mar 7, 2014 at 1:19 PM, Andrew Barnert <abarnert@yahoo.com> wrote:
If I were dictator of the world, I would ban DST. And 12-hour times. And MDY date formats. In fact, I would probably ban timezones. How hard would it be to learn that on the east coast you work from 0400-1200, not 9:00am-5:00pm?
Fortunately, you don't have to be dictator of the whole world. As admin of Minstrel Hall (minstrelhall.com), I simply made the declaration that all campaign times would be listed in UTC. That effectively bans DST and timezones.
No it doesn't. People still have to convert from the listed timezone to their own local timezone. The only difference is that instead of privileging some semi-arbitrary timezone on the basis of where your servers are, you're privileging an even more arbitrary timezone in Europe. Depending on the distribution of your users, chances are good that you're actually inconveniencing more people by your decision than if you used the local timezone. [...]
Let's start creating pockets like that all over the place. Train people to manage international events in UTC. Eventually the benefits will start to be more visible :)
There are no benefits, not even to programmers, since they will still have to convert a local time to UTC. The only differences will be: - people whose local time happens to be UTC won't have to do anything; - everyone else will suffer more, as even local (to them) events will need to be converted from UTC. Really, all these (hopefully tongue-in-cheek) suggestions to ban timezones are as useful as those (unfortunately serious) suggestions to avoid all the issues with Unicode by going back to "good ol' ASCII" and insist that everybody write in American English. (Actually a subset of American English, since ASCII is missing some common American symbols.) -- Steven
[Donald Stufft]
It's completely reasonable to attach a timezone to a time object.
If I schedule an event in a calendar for Mar 7th, at 12pm Eastern, then absolutely that should be represented as a date time. But what if I want to schedule an event that occurs every day at 11:30AM EST? There's no date associated with it (except an infinite set, or the span of my life I suppose!) it's just 11:30AM EST.
That's a stretch, Donald. Can you present an example of such an event? For someone living on the US East Coast, some days that event would occur when their clock said 11:30AM, and on other days when it occurred at - figure it out! - 10:30AM or 12:30PM? Depending on whether, in the real world, daylight time wasn't or was in effect. In any case, you keep ignoring the context of what's written to present some caricature to shoot down. Here: [Tim]
No, it's bizarre to attach a timezone to a time object because most tzinfo subclasses don't - and can't - know what to return for the UTC offset in the absence of - at least - month and day info too.
You didn't see the "most"? Or the later repeated elaborations that most tzinfo subclasses (in real life) _do_ attempt to model daylight time? An EST tzinfo subclass is a fixed-offset subclass, making no attempt to model daylight time. Those aren't the problem. But fixed-offset tzinfo subclasses are relatively rare.
On 07/03/2014 01:25, Donald Stufft wrote:
On Mar 6, 2014, at 8:19 PM, Tim Peters <tim.peters@gmail.com> wrote:
No, it's bizarre to attach a timezone to a time object because most tzinfo subclasses don't - and can't - know what to return for the UTC offset in the absence of - at least - month and day info too.
It’s completely reasonable to attach a timezone to a time object.
If I schedule an event in a calendar for Mar 7th, at 12pm Eastern, then absolutely that should be represented as a date time. But what if I want to schedule an event that occurs every day at 11:30AM EST? There’s no date associated with it (except an infinite set, or the span of my life I suppose!) it’s just 11:30AM EST.
----------------- Donald Stufft
Hum, thinking out loud as I don't know the answer. Assume that for your sins in a past life at 11:30AM EST every day you have to talk to me. How do I set my calendar up to have an infinite set of dates, I've never known an application let me do this? How do we sync our calendars so that your EST or (I guess) EDT ties in with my GMT or BST? -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
From: Tim Peters <tim.peters@gmail.com> Sent: Thursday, March 6, 2014 5:19 PM
No, it's bizarre to attach a timezone to a time object because most tzinfo subclasses don't - and can't - know what to return for the UTC offset in the absence of - at least - month and day info too. Hour, minute, second and microsecond aren't usually enough to determine whether "daylight time" is in effect, and most tzinfo subclasses do attempt to model daylight time. And because daylight time is a political conceit, most tzinfo subclasses also need year info. That has nothing to do with whether times are viewed abstractly, concretely, etc - it has to do with that time zones generally aren't _defined_ in terms of isolated time-of-day info. It's 7:13 PM in US Central. Is that daylight (CDT) or standard (CST) time? It's impossible to answer. What's the corresponding UTC time? Ditto.
Well, a time in Central is useless, but a time in CDT or CST is not, and you can design a library that's smart enough to give you a CDT or CST (as appropriate) time from a Central datetime. However, I built something like that in a C++ datetime library, and we never found a use for it anywhere. Instead, we just carried the full datetime or recurring-datetime object around as late as possible, and rendered to naive times for display.
[Tim]
No, it's bizarre to attach a timezone to a time object because most tzinfo subclasses don't - and can't - know what to return for the UTC offset in the absence of - at least - month and day info too. Hour, minute, second and microsecond aren't usually enough to determine whether "daylight time" is in effect, and most tzinfo subclasses do attempt to model daylight time. And because daylight time is a political conceit, most tzinfo subclasses also need year info. That has nothing to do with whether times are viewed abstractly, concretely, etc - it has to do with that time zones generally aren't _defined_ in terms of isolated time-of-day info. It's 7:13 PM in US Central. Is that daylight (CDT) or standard (CST) time? It's impossible to answer. What's the corresponding UTC time? Ditto.
[Andrew Barnert <abarnert@yahoo.com>]
Well, a time in Central is useless, but a time in CDT or CST is not, and you can design a library that's smart enough to give you a CDT or CST (as appropriate) time from a Central datetime.
Tell me how. You have, in context, a Python `time` object with a "Central" tzinfo member. That's all you get from the user. You're given absolutely no information about day, month, or year. What's your algorithm for implementing picking CDT or CST "as appropriate"? Note that we're NOT talking about datetime.datetime objects here. These are datetime.time objects. This isn't a problem for datetime.datetime objects. A "Central" tzinfo timeclass has no problem at all picking CDT or CST as appropriate given all the info in a datetime.datetime object.
From: Tim Peters <tim.peters@gmail.com> Sent: Thursday, March 6, 2014 6:16 PM
Well, a time in Central is useless, but a time in CDT or CST is not, and you can design a library that's smart enough to give you a CDT or CST (as appropriate) time from a Central datetime.
Tell me how. You have, in context, a Python `time` object with a "Central" tzinfo member. That's all you get from the user. You're given absolutely no information about day, month, or year. What's your algorithm for implementing picking CDT or CST "as appropriate"? Note that we're NOT talking about datetime.datetime objects here. These are datetime.time objects.
Of course that's inherently ambiguous and therefore not solvable. If your app wants unambiguous times, it can't allow users to enter times in Central with no additional information. But that's a separate problem from getting a CDT or CST time from a Central datetime (or from other problems, like handling a Central time that's known to be today). A datetime library could solve _that_ problem.
This isn't a problem for datetime.datetime objects. A "Central" tzinfo timeclass has no problem at all picking CDT or CST as appropriate given all the info in a datetime.datetime object.
Right, but if you get its tztime, you get a Central time, and no longer have the information that lets you use it as CDT or CST as appropriate. That is a problem—but, as I said before, it's probably not a problem in practice, and therefore probably not worth solving. (Again, I did solve it in a different timezone library, and never found a use for that feature.)
On 07/03/2014 02:16, Tim Peters wrote:
[Tim]
No, it's bizarre to attach a timezone to a time object because most tzinfo subclasses don't - and can't - know what to return for the UTC offset in the absence of - at least - month and day info too. Hour, minute, second and microsecond aren't usually enough to determine whether "daylight time" is in effect, and most tzinfo subclasses do attempt to model daylight time. And because daylight time is a political conceit, most tzinfo subclasses also need year info. That has nothing to do with whether times are viewed abstractly, concretely, etc - it has to do with that time zones generally aren't _defined_ in terms of isolated time-of-day info. It's 7:13 PM in US Central. Is that daylight (CDT) or standard (CST) time? It's impossible to answer. What's the corresponding UTC time? Ditto. [Andrew Barnert <abarnert@yahoo.com>] Well, a time in Central is useless, but a time in CDT or CST is not, and you can design a library that's smart enough to give you a CDT or CST (as appropriate) time from a Central datetime. Tell me how. You have, in context, a Python `time` object with a "Central" tzinfo member. That's all you get from the user. You're given absolutely no information about day, month, or year. What's your algorithm for implementing picking CDT or CST "as appropriate"? Note that we're NOT talking about datetime.datetime objects here. These are datetime.time objects.
This isn't a problem for datetime.datetime objects. A "Central" tzinfo timeclass has no problem at all picking CDT or CST as appropriate given all the info in a datetime.datetime object. _______________________________________________
Guys, please. This discussion is getting increasingly esoteric and has become irrelevant to the original proposition. What it does show is that the current truthy behaviour of time objects is incomprehensible to the average programmer. And therefore useless to her. Rob Cliffe
On 7 March 2014 11:13, Rob Cliffe <rob.cliffe@btinternet.com> wrote:
Guys, please. This discussion is getting increasingly esoteric and has become irrelevant to the original proposition.
Everything here is irrelevant to the original proposition. The bug report has been reopened. The next (relevant) step is to create a patch and post it to the bug report. Everything else is just people having a chat... Paul
On 07/03/2014 11:13, Rob Cliffe wrote:
On 07/03/2014 02:16, Tim Peters wrote:
[Tim]
No, it's bizarre to attach a timezone to a time object because most tzinfo subclasses don't - and can't - know what to return for the UTC offset in the absence of - at least - month and day info too. Hour, minute, second and microsecond aren't usually enough to determine whether "daylight time" is in effect, and most tzinfo subclasses do attempt to model daylight time. And because daylight time is a political conceit, most tzinfo subclasses also need year info. That has nothing to do with whether times are viewed abstractly, concretely, etc - it has to do with that time zones generally aren't _defined_ in terms of isolated time-of-day info. It's 7:13 PM in US Central. Is that daylight (CDT) or standard (CST) time? It's impossible to answer. What's the corresponding UTC time? Ditto. [Andrew Barnert <abarnert@yahoo.com>] Well, a time in Central is useless, but a time in CDT or CST is not, and you can design a library that's smart enough to give you a CDT or CST (as appropriate) time from a Central datetime. Tell me how. You have, in context, a Python `time` object with a "Central" tzinfo member. That's all you get from the user. You're given absolutely no information about day, month, or year. What's your algorithm for implementing picking CDT or CST "as appropriate"? Note that we're NOT talking about datetime.datetime objects here. These are datetime.time objects.
This isn't a problem for datetime.datetime objects. A "Central" tzinfo timeclass has no problem at all picking CDT or CST as appropriate given all the info in a datetime.datetime object. _______________________________________________
Guys, please. This discussion is getting increasingly esoteric and has become irrelevant to the original proposition. What it does show is that the current truthy behaviour of time objects is incomprehensible to the average programmer. And therefore useless to her. Rob Cliffe
I disagree. People here have stated that they use it. I could use it if I wanted to, having been raised to do some strange things, as in read docs, so I'm firmly in favour of sticking with the status quo. -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
M.-A. Lemburg wrote:
This is true in a date/time continuum, but not true if you regard time as "time of day". In the latter interpretation, the day starts at midnight,
That's not always the most convenient way of looking at it. For example, for booking a stay in a hotel, it may be better to think of "nights" as stretching from midday to midday. -- Greg
On Wed, Mar 5, 2014 at 5:16 PM, Ben Finney <ben+python@benfinney.id.au>wrote:
.. midnight is not special in the way “empty” or “zero magnitude” are. Midnight is an arbitrary point on a continuum, ..
Tell that to people who gather in the Times Square on New Year Eve! It is very common to implement time-of-day in the languages that lack a dedicated class as seconds since midnight or fraction of a 24 hour day or any similar scheme. In all these schemes midnight is false in boolean context. Note that when datetime module was introduced, Python did not have a time class and people were using int or float to denote time-of-day. Compatibility with older schemes could be part of the original motivation. It also made more sense when the dunder method was spelled __nonzero__. Whatever the reason was - this is a well established feature and it won't change.
On 05/03/2014 22:38, Alexander Belopolsky wrote:
On Wed, Mar 5, 2014 at 5:16 PM, Ben Finney <ben+python@benfinney.id.au <mailto:ben+python@benfinney.id.au>> wrote:
.. midnight is not special in the way “empty” or “zero magnitude” are. Midnight is an arbitrary point on a continuum, ..
Tell that to people who gather in the Times Square on New Year Eve!
It is very common to implement time-of-day in the languages that lack a dedicated class as seconds since midnight or fraction of a 24 hour day or any similar scheme. In all these schemes midnight is false in boolean context.
Note that when datetime module was introduced, Python did not have a time class and people were using int or float to denote time-of-day. Compatibility with older schemes could be part of the original motivation. It also made more sense when the dunder method was spelled __nonzero__.
Whatever the reason was - this is a well established feature and it won't change.
Have you looked at the issue recently? :) -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
On Wed, Mar 5, 2014 at 5:47 PM, Mark Lawrence <breamoreboy@yahoo.co.uk>wrote:
Have you looked at the issue recently? :)
Yes, and I added one more myself: http://bugs.python.org/issue20855 let's see which one fares better. :-)
On 05/03/2014 22:55, Alexander Belopolsky wrote:
On Wed, Mar 5, 2014 at 5:47 PM, Mark Lawrence <breamoreboy@yahoo.co.uk <mailto:breamoreboy@yahoo.co.uk>> wrote:
Have you looked at the issue recently? :)
Yes, and I added one more myself:
http://bugs.python.org/issue20855
let's see which one fares better. :-)
As 20855 has already been closed, with a comment that I tend to agree with, I think it's time for me to grab my trenching tool, tin hat, camouflage net and flak jacket and start running before the Batley Townswomen Guild get involved. -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
On Thu, Mar 06, 2014 at 09:16:06AM +1100, Ben Finney wrote:
With ‘datetime.time’ there is no equivalent: midnight is not special in the way “empty” or “zero magnitude” are. Midnight is an arbitrary point on a continuum, and is not a “not there” value. It should not be Boolean false any more than any other time.
It may only be a convention that an instant before midnight is the end of the day and midnight the beginning of the next, but it is a convention: midnight is the origin (i.e. zero point) of the day. That makes it arguably as falsey as 0, 0.0 and 0j. -- Steven
Le 06/03/2014 03:19, Steven D'Aprano a écrit :
It may only be a convention that an instant before midnight is the end of the day and midnight the beginning of the next, but it is a convention: midnight is the origin (i.e. zero point) of the day. That makes it arguably as falsey as 0, 0.0 and 0j.
A zero in algebra has a special meaning with respect to an operator (e.g. addition). Midnight doesn't have such a special meaning. Regards Antoine.
On 3/5/2014 4:10 PM, Alex Gaynor wrote:
First, I'd like to dispense with the notion that ``if foo`` is somehow a bad practice.
It is when one means 'if foo is not None' as it is as fragile, and in the same way, in the same way as using 'a and b or c' as a conditional expression.
This is an EXTREMELY common practice as a shorthand for checking for None, a great many people consider it more idiomatic,
In what universe. I was originally for changing datetime, but seems the issue being used as a vehicle to justify and promote buggy programming has changed my mind. -- Terry Jan Reedy
Le 05/03/2014 22:10, Alex Gaynor a écrit :
First, I'd like to dispense with the notion that ``if foo`` is somehow a bad practice. This is an EXTREMELY common practice as a shorthand for checking for None, a great many people consider it more idiomatic, this really isn't a place to bikeshed that particlar idiom.
Agreed. If we didn't want people to rely on that idiom then __bool__ would have to raise TypeError on most types except a select ones. Regards Antoine.
participants (37)
-
Alex Gaynor -
Alexander Belopolsky -
Amber Yust -
Andrew Barnert -
Antoine Pitrou -
Barry Warsaw -
Ben Finney -
Bruce Leban -
Chris Angelico -
Daniel Holth -
Donald Stufft -
Ethan Furman -
Greg -
Greg Ewing -
Guido van Rossum -
INADA Naoki -
Joao S. O. Bueno -
M.-A. Lemburg -
Mark Dickinson -
Mark Lawrence -
Markus Unterwaditzer -
Masklinn -
Michael Foord -
Nick Coghlan -
Oscar Benjamin -
Paul Moore -
Philipp A. -
Rob Cliffe -
Ryan Hiebert -
Shai Berger -
Skip Montanaro -
Stefan Krah -
Stephen J. Turnbull -
Steven D'Aprano -
Terry Reedy -
Tim Peters -
Yann Kaiser