Skip to content

test_strftime_y2k fails on embedded Linux #123681

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
picnixz opened this issue Sep 4, 2024 · 16 comments · Fixed by #128106
Open

test_strftime_y2k fails on embedded Linux #123681

picnixz opened this issue Sep 4, 2024 · 16 comments · Fixed by #128106
Labels
tests Tests in the Lib/test dir type-bug An unexpected behavior, bug, or error

Comments

@picnixz
Copy link
Member

picnixz commented Sep 4, 2024

Bug report

Bug description:

This is an issue to track the progress of fixing the JIT. Branches that trigger the embedded Linux tests fail on test_strftime_y2k, e.g. https://github.com/python/cpython/actions/runs/10655985897/job/29534232332?pr=123546. As a side-effect this causes users getting notification that their JIT workflow failed.

Both the C and F specifiers are failing for this specific test:

test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) ... 
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=1, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=1, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=48, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=48, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=70, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=70, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=99, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=99, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=99, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=99, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=999, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDate_Fast.test_strftime_y2k) (year=999, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) ... 
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=1, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=1, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=48, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=48, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=70, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=70, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=99, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=99, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=99, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=99, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=999, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTime_Fast.test_strftime_y2k) (year=999, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) ... 
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=1, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=1, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=48, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=48, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=70, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=70, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=99, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=99, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=99, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=99, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=999, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k) (year=999, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) ... 
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=1, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=1, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=48, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=48, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=70, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=70, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=99, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=99, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=99, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=99, specifier='C') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=999, specifier='F') ... FAIL
test_strftime_y2k (test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k) (year=999, specifier='C') ... FAIL

A solution is to add this test to the ignored tests or to fix the detection algorithm.

cc @blhsing @serhiy-storchaka

CPython versions tested on:

CPython main branch

Operating systems tested on:

Other

Linked PRs

Related and proposed PR: gh-128106

@picnixz picnixz added type-bug An unexpected behavior, bug, or error tests Tests in the Lib/test dir labels Sep 4, 2024
@serhiy-storchaka
Copy link
Member

It seems that only tests for the C implementation fail.

We can try to move the detection from the compile time to the run time, like for Python implementation. But it is a puzzle why it does not work here.

@picnixz
Copy link
Member Author

picnixz commented Sep 4, 2024

(Forget my comment that I deleted, I looked at the wrong commit).

I think we can improve the configure.ac detection by including the weird cases. Or maybe the test is broken because of this:

if _time.strftime('%F', (1900, 1, 1, 0, 0, 0, 0, 1, 0)) == '1900-01-01':
    specifiers += 'FC'

(Those are the two failing specifiers)

@brandtbucher
Copy link
Member

This particular test failure could be a bit weird, since this CI job involves cross-compiling Python and then running the test suite under emulation. Not sure if it's a "real" issue, or if we should just skip the test (like we do for others).

@picnixz
Copy link
Member Author

picnixz commented Sep 7, 2024

since this CI job involves cross-compiling Python and then running the test suite under emulation

Does it mean that the configure tests might not be correct (or inconsistent because they were done using cross-compilation?) If so, we can just outright ignore the test I guess?

@serhiy-storchaka
Copy link
Member

If these configure tests are not correct, then other configure tests can be not correct too.

@picnixz
Copy link
Member Author

picnixz commented Sep 8, 2024

Ha! I knew I should have looked more in detail the logs:

On aarch64-unknown-linux-gnu (failing tests), we have two different configure:

https://github.com/python/cpython/commit/e565b87d9dafe89bf262e3ab46edb9c9c8618b27/checks/29534232332/logs

2024-09-01T17:33:50.4537429Z checking whether year with century should be normalized for strftime... yes
2024-09-01T17:33:50.4888417Z checking whether C99-specific strftime specifiers are supported... yes

2024-09-01T17:35:47.4696152Z checking whether year with century should be normalized for strftime... yes
2024-09-01T17:35:47.4698164Z checking whether C99-specific strftime specifiers are supported... >> no <<

So it appears we have different ./configure. I don't know which one is actually being used at runtime in the emulated environment. Now, both '%F' and '%C' are C99-specific (https://en.cppreference.com/w/c/chrono/strftime) so the following

if _time.strftime('%F', (1900, 1, 1, 0, 0, 0, 0, 1, 0)) == '1900-01-01':
    specifiers += 'FC'

is perhaps a too broad check. Since specifiers 'F' and 'C' are used, we actually expect to support %F, but this contradicts the fact that "C99-specific strftime specifiers are supported" (at least for the second one which seems the most recent and probably the one being used under the hood). The configure check is:

if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) {
    return 0;
}

In other words, either strftime(full_date, sizeof(full_date), "%F", &date) fails or full_date != 1900-01-01. So maybe %F does not fail. We can probably debug the tests to check whether it _time.strftime that bugs, or if it is the configure test that bugs. I don't know if it's possible for _time.strftime to support %F (which does not directly call strftime()) but at the same time fail the strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01") test.


I'm tired so what I wrote here might be wrong but I'll try to investigate tomorrow.

@blhsing
Copy link
Contributor

blhsing commented Sep 9, 2024

2024-09-08T16:49:27.5943515Z checking whether year with century should be normalized for strftime... yes
2024-09-08T16:49:27.6286869Z checking whether C99-specific strftime specifiers are supported... yes

2024-09-08T16:51:33.7240722Z checking whether year with century should be normalized for strftime... yes
2024-09-08T16:51:33.7242911Z checking whether C99-specific strftime specifiers are supported... >> no <<

Ahh how could I have not noticed the second configure in the logs! Was scratching my head over why the detection of C99 support got a yes (in the first configure) but the compiled code ended up not handling C99. It makes total sense now.

The real problem is simply that autoconf does not run the test for the AC_RUN_IFELSE macro at all when cross-compiling, but directly performs action-if-cross-compiling as specified, which I thought should be made a no (to be pessimistic) when it should really be a yes because we should make sure to handle %F and %C ourselves when we are unsure if the target platform can handle them.

@picnixz
Copy link
Member Author

picnixz commented Sep 9, 2024

Ahh how could I have not noticed the second configure in the logs!

Well, isn't it trivial to spot it among 11985 lines 😄? Actually, I found the duplicate configure in the logs by a CTRL+F and... saw that I had two results.

@blhsing
Copy link
Contributor

blhsing commented Sep 9, 2024

Ahh how could I have not noticed the second configure in the logs!

Well, isn't it trivial to spot it among 11985 lines 😄? Actually, I found the duplicate configure in the logs by a CTRL+F and... saw that I had two results.

I used Ctrl-F too, but the browser limits the search to only what's loaded in the memory, when the output of the second configure is hidden because it is loaded on demand by an AJAX call only if I scroll to it. I should've used the "Search Logs" text box in the page to perform the search for the keyword on the server side instead.

Thanks again!

@picnixz
Copy link
Member Author

picnixz commented Sep 9, 2024

I used Ctrl-F too, but the browser limits the search to only what's loaded in the memory, when the output of the second configure is hidden because it is loaded on demand by an AJAX call only if I scroll to it. I should've used the "Search Logs" text box in the page to perform the search for the keyword on the server side instead.

Actually, the trick is to "view raw logs". You'll get the entire logs without buffering (easier to read IMO).

@picnixz
Copy link
Member Author

picnixz commented Sep 27, 2024

FTR: #124466 was merged as a temporary workaround so we need to keep the issue opened even though the job looks successful (it's just that we skip it).

@picnixz
Copy link
Member Author

picnixz commented Dec 21, 2024

Closing this one in favor of gh-128104 which has a better description and an alternative PR.

@picnixz picnixz closed this as completed Dec 21, 2024
@picnixz picnixz closed this as not planned Won't fix, can't repro, duplicate, stale Dec 21, 2024
@serhiy-storchaka
Copy link
Member

Why not test this at runtime instead of compile time, like in the Python implementation?

serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Jan 3, 2025
…at the compile time

It is needed to support cross-compiling.
Remove macros Py_NORMALIZE_CENTURY and Py_STRFTIME_C99_SUPPORT.
@serhiy-storchaka
Copy link
Member

We still need to fix a check for Py_NORMALIZE_CENTURY which does not work in cross-compiler. It is only possible at run time.

If we require C99 compliant strftime (it is not on Linux for %C), we need to make the following changes:

  • Remove the runtime check from the Python implementation.
  • Update the documentation (it still refers to C89). Document all C99 codes, including the E and O modifiers.

@erlend-aasland
Copy link
Contributor

it is not on Linux for %C

%C works swell for me on Debian and Ubuntu. Please specify where it does not work instead of putting out such a broad claim. We also explicitly test the %C specifier in our test suite. If it did not work on Linux, CI would be broken.

@serhiy-storchaka
Copy link
Member

>>> time.strftime("%C", (99, 1, 1, 0, 0, 0, 0, 1, 0))
'0'

In C99 it should be 2 digits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tests Tests in the Lib/test dir type-bug An unexpected behavior, bug, or error
Projects
Development

Successfully merging a pull request may close this issue.

5 participants