Skip to content

gh-81148: Eliminate unnecessary check in _strptime when determining AM/PM #13428

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

GPHemsley
Copy link
Contributor

@GPHemsley GPHemsley commented May 19, 2019

This also adds additional tests to improve the coverage in this area.

https://bugs.python.org/issue36967

Copy link
Member

@pganssle pganssle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this contribution @GPHemsley, I think it's a definite improvement.

I've left two comments on the tests. I think adding the special case tests is important. Moving your ampm tests into datetimetester is more of a "nice to have" (I have not looked into how difficult it would be, but I'm pretty sure there are strptime tests in datetimetester already, so it's probably pretty easy).

Thanks!

@GPHemsley
Copy link
Contributor Author

@pganssle I have updated the tests. Let me know if that is sufficient.

@pganssle
Copy link
Member

pganssle commented May 28, 2019

Because of the interaction of _strptime.py and _datetimemodule.c in the creation of the datetime modules, I think it's worthwhile to have separate tests for each of them. We may very well want to test this functionality in datetimetester.py as well, but I think we would want to deliberately duplicate the tests in that case (unless we go so far as to remove test_strptime.py altogether).

I disagree here. _strptime.py and _datetimemodule.c are both implementation details of the datetime module. I'm not sure why _strptime.py is being tested directly like this, but I don't think it's a good idea to keep adding direct tests for _strptime.py.

The behavior we want to test is the user-facing behavior, which is the one invoked by calling datetime.strptime. It doesn't really matter if that logic is in _strptime or in datetime.strptime, but it does matter that it works with PEP 399 and that it works with subclasses of datetime, both of which come "for free" when you add the test in datetimetester.py, but that I believe are not handled in test_strptime.py.

@GPHemsley
Copy link
Contributor Author

@pganssle I'm not sure if this is what you were asking me to do, but I've now added a similar test to datetimetester.py (which is actually missing a lot of tests covering the code in _strptime.py).

@pganssle
Copy link
Member

@GPHemsley Thank you for adding the tests in datetimetester.py, they look good to me. However, I think you should drop the tests from test_strptime.py, since this case is already getting proper (and better) coverage in datetimetester.py

I am aware that a lot of strptime is tested in test_strptime.py instead of datetimetester.py, but I think that is a historical accident and not something we should continue doing just for consistency's sake. Maybe @abalkin or @vstinner has a different opinion of this, but I think all new tests for strptime should be hitting the public interface of the module datetime.strptime only. Frankly, I think it would be a good idea to migrate a lot of the tests in test_strptime.py over to datetimetester.py, or at least to switch them over to using the same testing methodology (i.e. testing datetime.strptime rather than _strptime directly, testing with datetime subclasses, and running the tests with and without C extensions), but that's a different issue that can be discussed on its own ticket.

@GPHemsley GPHemsley force-pushed the strptime-am-pm branch 2 times, most recently from 1771b9f to 644aa88 Compare June 16, 2019 23:03
@GPHemsley
Copy link
Contributor Author

GPHemsley commented Jun 16, 2019

@pganssle Removed test from test_strptime.py. Also confirmed that test_strptime.py dates back to 2002, compared to datetimetester.py's 2010.

Note that datetimetester.py currently has poor test coverage of _strptime.py (68%) compared to test_strptime.py (93%).

Copy link
Member

@pganssle pganssle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GPHemsley Yeah, I agree, I am at a conference this weekend, but this came up in another context today, so when I get back home (and have a bit of time to recover from my jet lag), I'm going to open an issue on bpo to propose a strategy going forward with respect to the datetimetester / test_strptime split. Things are mildly complicated by the fact that I didn't fully appreciate the fact that _strptime is in its own module because it's got some shared code between time and datetime (I grepped incorrectly for import _strptime, when in fact timemodule.c uses another mechanism to import it).

Anyway, developing a coherent strategy for how to organize the strptime tests is way outside the scope of this PR, so I don't want that to block your contribution, for which I am grateful.

I have left one final comment suggesting a refactoring of the test, but otherwise this looks good to me.

for hour in range(0, 24):
with self.subTest(hour=hour):
hour_date = (1999, 3, 17, hour, 44, 55, 2, 76, 0)
self.assertEqual(self.theclass.strptime(_time.strftime("%I %p", hour_date), "%I %p").hour, hour)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is I think too long for PEP8. If I may suggest a refactoring for this function that I think makes it a bit cleaner:

    def test_strptime_ampm(self):
        dt = datetime(1999, 3, 17, 0, 44, 55, 2, 76, 0)
        for hour in range(0, 24):
            with self.subTest(hour=hour):
                new_dt = dt.replace(hour=hour)
                dt_str = new_dt.strftime("%I %p")

                self.assertEqual(self.theclass.strptime(dt_str, "%I %p").hour,
                                 hour)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I likely opted to ignore line length, since much of the rest of the file has PEP8 issues, but I can change that.

Out of curiosity, why do you feel that using dt.replace is cleaner than generating a new datetime value directly each iteration?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why do you feel that using dt.replace is cleaner than generating a new datetime value directly each iteration?

Technically replace does generate a new value every iteration (datetime is immutable), but I get what you mean. I mainly thought it was more readable because:

  1. I find the time.strftime interface to be a bit more "low level" and harder to read than an strftime call on a datetime object.
  2. Using replace(hour=hour) makes it more obvious that we didn't have any sort of off-by-one error when constructing the time tuple - it would be hard to notice in a tuple if you were actually cycling through different values for the minute instead of hour.

@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@pganssle
Copy link
Member

@vstinner @pablogsal Do you think this rises to the level of needing a NEWS entry? It's a pretty minor refactoring so I added "skip news", but if you think it would be better to add a changelog I will remove the label and add that to the requested changes.

Other than the one little requested change and possibly the news entry, I think this is ready to merge.

@vstinner
Copy link
Member

@vstinner @pablogsal Do you think this rises to the level of needing a NEWS entry? It's a pretty minor refactoring so I added "skip news", (...)

If the change doesn't modify datetime behavior, there is no need to annoy users reading the Changelog. "skip news" is correct here.

@GPHemsley GPHemsley requested a review from abalkin as a code owner June 24, 2019 12:37
@csabella csabella requested review from pganssle and abalkin and removed request for abalkin February 25, 2020 12:32
@AlexWaygood
Copy link
Member

@pganssle, this old PR is marked as "awaiting changes", but I'm not sure it actually is.

Is this something you'd still be interested in reviewing, and potentially merging?

@StanFromIreland
Copy link
Member

@pganssle Is the only change left to do to the test? I don't the person who originally opened this pr has been active recently, I can finish this off :-)

@StanFromIreland
Copy link
Member

I have made the requested changes; please review again

@StanFromIreland StanFromIreland changed the title bpo-36967: Eliminate unnecessary check in _strptime when determining AM/PM gh-81148: Eliminate unnecessary check in _strptime when determining AM/PM Jul 8, 2025
Copy link
Member

@StanFromIreland StanFromIreland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated and merged @pganssle's Requested Changes (with minor fixup).

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy