From 7f6b4f86e305ec63441657c57f5f546d15114ca0 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 30 Mar 2009 21:30:26 +0000 Subject: Make sure time.strptime only accepts strings (and document the fact like strftime). Already didn't accept bytes but make the check earlier. This also lifts the limitation of requiring ASCII. Closes issue #5236. Thanks Tennessee Leeuwenburg. --- Doc/library/time.rst | 14 ++++++++------ Lib/_strptime.py | 9 ++++++++- Lib/test/test_time.py | 5 +++++ Misc/ACKS | 1 + Misc/NEWS | 3 +++ 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Doc/library/time.rst b/Doc/library/time.rst index 46d972a..cdc623d 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 896c798..9ff29bc 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 4c89f03..c2dfaca 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 85bc8a1..85a443b 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 f0dafcb..f2888be 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. -- cgit v0.12