From 2bd36c30eefdf9d5580ad81b169fe92652b98051 Mon Sep 17 00:00:00 2001 From: sn0wle0pard Date: Mon, 14 Aug 2017 06:55:46 +0900 Subject: [PATCH 1/4] Fix #26669 --- Lib/test/test_time.py | 4 ++++ Python/pytime.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 810ec37b1113f9f..1e8b3ef339637fc 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -489,6 +489,10 @@ def test_localtime_failure(self): self.assertRaises(OSError, time.localtime, invalid_time_t) self.assertRaises(OSError, time.ctime, invalid_time_t) + # Issue #26669: check for localtime() failure + self.assertRaises(ValueError, time.localtime, float("nan")) + self.assertRaises(ValueError, time.ctime, float("nan")) + def test_get_clock_info(self): clocks = ['clock', 'perf_counter', 'process_time', 'time'] if hasattr(time, 'monotonic'): diff --git a/Python/pytime.c b/Python/pytime.c index 8979adc2191267f..7ee0ca8985fc244 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -155,6 +155,10 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) volatile double d; d = PyFloat_AsDouble(obj); + if (isnan(d)) { + PyErr_SetString(PyExc_ValueError, "Invalid Value"); + return -1; + } d = _PyTime_Round(d, round); (void)modf(d, &intpart); From 9738f51b118f3d7cd6a948a87d9c4b8945c901e6 Mon Sep 17 00:00:00 2001 From: sn0wle0pard Date: Fri, 18 Aug 2017 14:51:13 +0900 Subject: [PATCH 2/4] Modify NaN check function and error message --- Python/pytime.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Python/pytime.c b/Python/pytime.c index 7ee0ca8985fc244..9d43ea059b5baec 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -155,10 +155,11 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) volatile double d; d = PyFloat_AsDouble(obj); - if (isnan(d)) { - PyErr_SetString(PyExc_ValueError, "Invalid Value"); + if (Py_IS_NAN(d)) { + PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)"); return -1; } + d = _PyTime_Round(d, round); (void)modf(d, &intpart); From 5812e36f59ab090226800d972ffd19ddee34db5a Mon Sep 17 00:00:00 2001 From: sn0wle0pard Date: Fri, 8 Sep 2017 15:20:17 +0900 Subject: [PATCH 3/4] Fix pytime.c when arg is nan --- Lib/test/test_time.py | 27 ++++++++++++++++++++++++++- Python/pytime.c | 9 +++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 1e8b3ef339637fc..d84cd8aef295a22 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -792,7 +792,7 @@ def convert_values(ns_timestamps): debug_info = {'value': value, 'rounding': time_rnd} with self.assertRaises(OverflowError, msg=debug_info): pytime_converter(value, time_rnd) - + def check_int_rounding(self, pytime_converter, expected_func, unit_to_sec=1, value_filter=None): self._check_rounding(pytime_converter, expected_func, @@ -827,6 +827,11 @@ def c_int_filter(secs): lambda secs: secs * SEC_TO_NS, value_filter=c_int_filter) + # test nan + for time_rnd, _ in ROUNDING_MODES: + with self.assertRaises(TypeError): + PyTime_FromSeconds(float('nan')) + def test_FromSecondsObject(self): from _testcapi import PyTime_FromSecondsObject @@ -837,6 +842,11 @@ def test_FromSecondsObject(self): self.check_float_rounding( PyTime_FromSecondsObject, lambda ns: self.decimal_round(ns * SEC_TO_NS)) + + # test nan + for time_rnd, _ in ROUNDING_MODES: + with self.assertRaises(ValueError): + PyTime_FromSecondsObject(float('nan'), time_rnd) def test_AsSecondsDouble(self): from _testcapi import PyTime_AsSecondsDouble @@ -851,6 +861,11 @@ def float_converter(ns): float_converter, NS_TO_SEC) + # test nan + for time_rnd, _ in ROUNDING_MODES: + with self.assertRaises(TypeError): + PyTime_AsSecondsDouble(float('nan')) + def create_decimal_converter(self, denominator): denom = decimal.Decimal(denominator) @@ -956,6 +971,11 @@ def test_object_to_timeval(self): self.create_converter(SEC_TO_US), value_filter=self.time_t_filter) + # test nan + for time_rnd, _ in ROUNDING_MODES: + with self.assertRaises(ValueError): + pytime_object_to_timeval(float('nan'), time_rnd) + def test_object_to_timespec(self): from _testcapi import pytime_object_to_timespec @@ -967,6 +987,11 @@ def test_object_to_timespec(self): self.create_converter(SEC_TO_NS), value_filter=self.time_t_filter) + # test nan + for time_rnd, _ in ROUNDING_MODES: + with self.assertRaises(ValueError): + pytime_object_to_timespec(float('nan'), time_rnd) + if __name__ == "__main__": unittest.main() diff --git a/Python/pytime.c b/Python/pytime.c index 9d43ea059b5baec..938f361c004625c 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -134,6 +134,11 @@ _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, if (PyFloat_Check(obj)) { double d = PyFloat_AsDouble(obj); + if (Py_IS_NAN(d)) { + *numerator = 0; + PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)"); + return -1; + } return _PyTime_DoubleToDenominator(d, sec, numerator, denominator, round); } @@ -306,6 +311,10 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round, if (PyFloat_Check(obj)) { double d; d = PyFloat_AsDouble(obj); + if (Py_IS_NAN(d)) { + PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)"); + return -1; + } return _PyTime_FromFloatObject(t, d, round, unit_to_ns); } else { From eb06ed0c74da0a6d88251bf55862058701732215 Mon Sep 17 00:00:00 2001 From: sn0wle0pard Date: Fri, 8 Sep 2017 15:46:24 +0900 Subject: [PATCH 4/4] fix whitespace --- Lib/test/test_time.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index d84cd8aef295a22..7093fc6a2ec1729 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -792,7 +792,7 @@ def convert_values(ns_timestamps): debug_info = {'value': value, 'rounding': time_rnd} with self.assertRaises(OverflowError, msg=debug_info): pytime_converter(value, time_rnd) - + def check_int_rounding(self, pytime_converter, expected_func, unit_to_sec=1, value_filter=None): self._check_rounding(pytime_converter, expected_func, @@ -842,7 +842,7 @@ def test_FromSecondsObject(self): self.check_float_rounding( PyTime_FromSecondsObject, lambda ns: self.decimal_round(ns * SEC_TO_NS)) - + # test nan for time_rnd, _ in ROUNDING_MODES: with self.assertRaises(ValueError):