diff --git a/Doc/library/time.rst b/Doc/library/time.rst index 46d972a3f0e..cdc623d60d1 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -358,15 +358,17 @@ The module defines the following functions and data items: .. function:: strptime(string[, format]) - Parse a string representing a time according to a format. The return value is - a :class:`struct_time` as returned by :func:`gmtime` or :func:`localtime`. + Parse a string representing a time according to a format. The return value + is a :class:`struct_time` as returned by :func:`gmtime` or + :func:`localtime`. The *format* parameter uses the same directives as those used by :func:`strftime`; it defaults to ``"%a %b %d %H:%M:%S %Y"`` which matches the - formatting returned by :func:`ctime`. If *string* cannot be parsed according to - *format*, or if it has excess data after parsing, :exc:`ValueError` is raised. - The default values used to fill in any missing data when more accurate values - cannot be inferred are ``(1900, 1, 1, 0, 0, 0, 0, 1, -1)``. + formatting returned by :func:`ctime`. If *string* cannot be parsed according + to *format*, or if it has excess data after parsing, :exc:`ValueError` is + raised. The default values used to fill in any missing data when more + accurate values cannot be inferred are ``(1900, 1, 1, 0, 0, 0, 0, 1, -1)``. + Both *string* and *format* must be strings. For example: diff --git a/Lib/_strptime.py b/Lib/_strptime.py index 896c7986c4e..9ff29bce8f7 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -262,7 +262,7 @@ class TimeRE(dict): def compile(self, format): """Return a compiled re object for the format string.""" - return re_compile(self.pattern(format), IGNORECASE | ASCII) + return re_compile(self.pattern(format), IGNORECASE) _cache_lock = _thread_allocate_lock() # DO NOT modify _TimeRE_cache or _regex_cache without acquiring the cache lock @@ -294,8 +294,15 @@ def _calc_julian_from_U_or_W(year, week_of_year, day_of_week, week_starts_Mon): def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): """Return a time struct based on the input string and the format string.""" + + for index, arg in enumerate([data_string, format]): + if not isinstance(arg, str): + msg = "strptime() argument {} must be str, not {}" + raise TypeError(msg.format(arg, index)) + global _TimeRE_cache, _regex_cache with _cache_lock: + if _getlang() != _TimeRE_cache.locale_time.lang: _TimeRE_cache = TimeRE() _regex_cache.clear() diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 4c89f03bab3..c2dfaca968c 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -116,6 +116,11 @@ class TimeTestCase(unittest.TestCase): self.fail("conversion specifier %r failed with '%s' input." % (format, strf_output)) + def test_strptime_bytes(self): + # Make sure only strings are accepted as arguments to strptime. + self.assertRaises(TypeError, time.strptime, b'2009', "%Y") + self.assertRaises(TypeError, time.strptime, '2009', b'%Y') + def test_asctime(self): time.asctime(time.gmtime(self.t)) self.assertRaises(TypeError, time.asctime, 0) diff --git a/Misc/ACKS b/Misc/ACKS index 85bc8a12794..85a443bba7d 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -411,6 +411,7 @@ John J. Lee Inyeol Lee Thomas Lee Christopher Lee +Tennessee Leeuwenburg Luc Lefebvre Kip Lehman Joerg Lehmann diff --git a/Misc/NEWS b/Misc/NEWS index f0dafcb8caf..f2888be1f3b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -43,6 +43,9 @@ Core and Builtins Library ------- +- Issue #5236: Change time.strptime() to only take strings. Didn't work with + bytes already but the failure was non-obvious. + - Issue #5177: Multiprocessing's SocketListener class now uses socket.SO_REUSEADDR on all connections so that the user no longer needs to wait 120 seconds for the socket to expire.