-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
gh-127527: Improve error handling in time_strftime1 #127528
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
base: main
Are you sure you want to change the base?
Conversation
Can you add tests and a news entry? |
I can add a news entry. I'll have to think about tests. There's already coverage for this in any job that uses Musl such as the wasi job. Hopefully if I read the glibc implementation of strftime / wcsftime I can find a format string that will hit the new codepath on glibc as well as musl. |
Well, in any case these tests in
======================================================================
ERROR: test_date_locale (test.test_strptime.StrptimeTests.test_date_locale) (locale='fr_FR')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/runner/work/cpython/cpython/Lib/test/support/__init__.py", line 968, in wrapper
func(self, *args, **kwargs)
~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 542, in test_date_locale
self.roundtrip('%x', slice(0, 3), time.localtime(now))
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 306, in roundtrip
strp_output = _strptime._strptime_time(strf_output, fmt)
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 670, in _strptime_time
tt = _strptime(data_string, format)[0]
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 431, in _strptime
_TimeRE_cache = TimeRE()
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 285, in __init__
self.locale_time = LocaleTime()
~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 86, in __init__
self.__calc_am_pm()
~~~~~~~~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 119, in __calc_am_pm
am_pm.append(time.strftime("%p", time_tuple).lower().strip())
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
ValueError: Invalid format string
======================================================================
ERROR: test_date_locale2 (test.test_strptime.StrptimeTests.test_date_locale2) (locale='fr_FR')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/runner/work/cpython/cpython/Lib/test/support/__init__.py", line 968, in wrapper
func(self, *args, **kwargs)
~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 565, in test_date_locale2
self.roundtrip('%x', slice(0, 3), (1900, 1, 1, 0, 0, 0, 0, 1, 0))
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 306, in roundtrip
strp_output = _strptime._strptime_time(strf_output, fmt)
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 670, in _strptime_time
tt = _strptime(data_string, format)[0]
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 431, in _strptime
_TimeRE_cache = TimeRE()
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 285, in __init__
self.locale_time = LocaleTime()
~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 86, in __init__
self.__calc_am_pm()
~~~~~~~~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 119, in __calc_am_pm
am_pm.append(time.strftime("%p", time_tuple).lower().strip())
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
ValueError: Invalid format string
======================================================================
ERROR: test_date_time_locale (test.test_strptime.StrptimeTests.test_date_time_locale) (locale='fr_FR')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/runner/work/cpython/cpython/Lib/test/support/__init__.py", line 968, in wrapper
func(self, *args, **kwargs)
~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 502, in test_date_time_locale
self.roundtrip('%c', slice(0, 6), time.localtime(now))
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 306, in roundtrip
strp_output = _strptime._strptime_time(strf_output, fmt)
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 670, in _strptime_time
tt = _strptime(data_string, format)[0]
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 431, in _strptime
_TimeRE_cache = TimeRE()
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 285, in __init__
self.locale_time = LocaleTime()
~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 86, in __init__
self.__calc_am_pm()
~~~~~~~~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 119, in __calc_am_pm
am_pm.append(time.strftime("%p", time_tuple).lower().strip())
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
ValueError: Invalid format string
======================================================================
ERROR: test_date_time_locale2 (test.test_strptime.StrptimeTests.test_date_time_locale2) (locale='fr_FR')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/runner/work/cpython/cpython/Lib/test/support/__init__.py", line 968, in wrapper
func(self, *args, **kwargs)
~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 529, in test_date_time_locale2
self.roundtrip('%c', slice(0, 6), (1900, 1, 1, 0, 0, 0, 0, 1, 0))
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 306, in roundtrip
strp_output = _strptime._strptime_time(strf_output, fmt)
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 670, in _strptime_time
tt = _strptime(data_string, format)[0]
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 431, in _strptime
_TimeRE_cache = TimeRE()
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 285, in __init__
self.locale_time = LocaleTime()
~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 86, in __init__
self.__calc_am_pm()
~~~~~~~~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 119, in __calc_am_pm
am_pm.append(time.strftime("%p", time_tuple).lower().strip())
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
ValueError: Invalid format string
======================================================================
ERROR: test_month_locale (test.test_strptime.StrptimeTests.test_month_locale) (locale='fr_FR')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/runner/work/cpython/cpython/Lib/test/support/__init__.py", line 968, in wrapper
func(self, *args, **kwargs)
~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 335, in test_month_locale
self.roundtrip('%B', 1)
~~~~~~~~~~~~~~^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 306, in roundtrip
strp_output = _strptime._strptime_time(strf_output, fmt)
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 670, in _strptime_time
tt = _strptime(data_string, format)[0]
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 431, in _strptime
_TimeRE_cache = TimeRE()
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 285, in __init__
self.locale_time = LocaleTime()
~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 86, in __init__
self.__calc_am_pm()
~~~~~~~~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 119, in __calc_am_pm
am_pm.append(time.strftime("%p", time_tuple).lower().strip())
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
ValueError: Invalid format string
======================================================================
ERROR: test_time_locale (test.test_strptime.StrptimeTests.test_time_locale) (locale='fr_FR')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/runner/work/cpython/cpython/Lib/test/support/__init__.py", line 968, in wrapper
func(self, *args, **kwargs)
~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 590, in test_time_locale
self.roundtrip('%X', pos, time.localtime(now))
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 306, in roundtrip
strp_output = _strptime._strptime_time(strf_output, fmt)
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 670, in _strptime_time
tt = _strptime(data_string, format)[0]
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 431, in _strptime
_TimeRE_cache = TimeRE()
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 285, in __init__
self.locale_time = LocaleTime()
~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 86, in __init__
self.__calc_am_pm()
~~~~~~~~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 119, in __calc_am_pm
am_pm.append(time.strftime("%p", time_tuple).lower().strip())
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
ValueError: Invalid format string
======================================================================
ERROR: test_weekday_locale (test.test_strptime.StrptimeTests.test_weekday_locale) (locale='fr_FR')
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/runner/work/cpython/cpython/Lib/test/support/__init__.py", line 968, in wrapper
func(self, *args, **kwargs)
~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 378, in test_weekday_locale
self.roundtrip('%A', 6)
~~~~~~~~~~~~~~^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/test/test_strptime.py", line 306, in roundtrip
strp_output = _strptime._strptime_time(strf_output, fmt)
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 670, in _strptime_time
tt = _strptime(data_string, format)[0]
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 431, in _strptime
_TimeRE_cache = TimeRE()
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 285, in __init__
self.locale_time = LocaleTime()
~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 86, in __init__
self.__calc_am_pm()
~~~~~~~~~~~~~~~~~^^
File "/Users/runner/work/cpython/cpython/Lib/_strptime.py", line 119, in __calc_am_pm
am_pm.append(time.strftime("%p", time_tuple).lower().strip())
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
ValueError: Invalid format string |
Or maybe it's a sign that the patch is wrong... |
Hmm so the failures are all of the French locale? |
81e12cd
to
d3b0056
Compare
If something is wrong with the format string, on Windows it raises a ValueError and on all other platforms it returns an empty string. I think ideally it we should always raise a |
Please don't force push (https://devguide.python.org/getting-started/pull-request-lifecycle):
|
Handling of format strings containing Unicode code points that can't be | ||
represented in the charset of the current locale is also platform-dependent. On | ||
some platforms such code points are preserved intact in the output, while on | ||
others ``strftime`` may raise :exc:`UnicodeError` or return an empty string | ||
instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this still true? According to test_strftime_special
, these should be always preserved.
Could you add tests for the newly documented behaviour? (Let me know if you'd like me to continue this.) |
What platforms with musl or BSD libc are supported? MacOS uses BSD and wasi uses musl and they both have buildbots. Then we could also use windows and glibc linux and add tests for the edge cases for each of these? |
Think of the tests as a tool for maintainers of platforms that CPython doesn't support. If they fail, they'll know that they need a patch -- even if it's just for the docs. |
Well we already have divergent behavior in the platforms that we do support. One reasonable approach would be to classify unsupported platforms as glibc-like, musl-like, bsd-like, or windows-like and assert that they agree in behavior with the most similar supported platform... |
Maybe assert that unknown platforms behave like any known platform? |
Okay. I will look into that. |
This fixes
python -m test test_datetime -mtest_strftime_trailing_percent
on Emscripten. On other musl platforms it will:HAVE_WCSFTIME
is true, changestrftime("format string ending in %")
from returning empty string to raisingValueError("Invalid format string")
HAVE_WCSFTIME
is false, changestrftime("format string ending in %")
from raisingValueError("embedded null byte")
toValueError("invalid format string")
.It also should change the behavior for other unsupported behaviors from returning empty string to raising
Invalid format string
. Also, if the result is actually empty, the exit condition(*outbuf)[buflen] == '\0'
is always true the first time and allows us to avoid resizing the buffer and callingformat_time
again.