From 0d28e76e81fc88696936d19398c253f4015a8bcf Mon Sep 17 00:00:00 2001 From: fujiaxiang Date: Tue, 4 Feb 2020 23:32:22 +0800 Subject: [PATCH 1/3] ENH: Timestamp constructor now raises more explanatory error message (GH31200) Timestamp constructor now raises explanatory error message when year, month or day is missing --- doc/source/whatsnew/v1.1.0.rst | 2 +- pandas/_libs/tslibs/timestamps.pyx | 11 +++++++++++ pandas/tests/tslibs/test_timestamps.py | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 pandas/tests/tslibs/test_timestamps.py diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index dc7edd8db662e..c5090fa26737f 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -42,7 +42,7 @@ Other enhancements ^^^^^^^^^^^^^^^^^^ - :class:`Styler` may now render CSS more efficiently where multiple cells have the same styling (:issue:`30876`) -- +- :func:`Timestamp` now raises more explanatory error message when year, month or day is missing (:issue:`31200`) - .. --------------------------------------------------------------------------- diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 4915671aa6512..5dcc58865d799 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -412,6 +412,17 @@ class Timestamp(_Timestamp): ) elif ts_input is _no_input: + # GH 31200 + # When year, month or day is not given, we call the datetime + # constructor to make sure we get the same error message + # since Timestamp inherits datetime + if year is None: + datetime(month=month, day=day) + elif month is None: + datetime(year=year, day=day) + elif day is None: + datetime(year=year, month=month) + # User passed keyword arguments. ts_input = datetime(year, month, day, hour or 0, minute or 0, second or 0, diff --git a/pandas/tests/tslibs/test_timestamps.py b/pandas/tests/tslibs/test_timestamps.py new file mode 100644 index 0000000000000..b14926fb9b13f --- /dev/null +++ b/pandas/tests/tslibs/test_timestamps.py @@ -0,0 +1,11 @@ +import pytest + +from pandas import Timestamp + + +@pytest.mark.parametrize("kwargs", [{}, {"year": 2020}, {"year": 2020, "month": 1}]) +def test_timestamp_constructor_missing_keyword(kwargs): + # GH 31200 + msg = r"function missing required argument .* \(pos .\)" + with pytest.raises(TypeError, match=msg): + Timestamp(**kwargs) From 82892a8ff4bae5e2eb609214ea078c3d789768ca Mon Sep 17 00:00:00 2001 From: fujiaxiang Date: Wed, 5 Feb 2020 10:27:47 +0800 Subject: [PATCH 2/3] ENH: updated whatsnew, test and implementation (GH31200) --- doc/source/whatsnew/v1.1.0.rst | 2 +- pandas/_libs/tslibs/timestamps.pyx | 26 +++++++++++-------- .../scalar/timestamp/test_constructors.py | 16 ++++++++++++ pandas/tests/tslibs/test_timestamps.py | 11 -------- 4 files changed, 32 insertions(+), 23 deletions(-) delete mode 100644 pandas/tests/tslibs/test_timestamps.py diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index c5090fa26737f..59f9670902c98 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -42,7 +42,7 @@ Other enhancements ^^^^^^^^^^^^^^^^^^ - :class:`Styler` may now render CSS more efficiently where multiple cells have the same styling (:issue:`30876`) -- :func:`Timestamp` now raises more explanatory error message when year, month or day is missing (:issue:`31200`) +- :class:`Timestamp` now raises more explanatory error message when year, month or day is missing (:issue:`31200`) - .. --------------------------------------------------------------------------- diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 5dcc58865d799..ef2652e843a16 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -416,17 +416,21 @@ class Timestamp(_Timestamp): # When year, month or day is not given, we call the datetime # constructor to make sure we get the same error message # since Timestamp inherits datetime - if year is None: - datetime(month=month, day=day) - elif month is None: - datetime(year=year, day=day) - elif day is None: - datetime(year=year, month=month) - - # User passed keyword arguments. - ts_input = datetime(year, month, day, hour or 0, - minute or 0, second or 0, - microsecond or 0) + datetime_kwargs = { + "hour": hour or 0, + "minute": minute or 0, + "second": second or 0, + "microsecond": microsecond or 0 + } + if year is not None: + datetime_kwargs["year"] = year + if month is not None: + datetime_kwargs["month"] = month + if day is not None: + datetime_kwargs["day"] = day + + ts_input = datetime(**datetime_kwargs) + elif is_integer_object(freq): # User passed positional arguments: # Timestamp(year, month, day[, hour[, minute[, second[, diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 737a85faa4c9b..c6a9a0a5be8a2 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -1,5 +1,6 @@ import calendar from datetime import datetime, timedelta +import re import dateutil.tz from dateutil.tz import tzutc @@ -550,3 +551,18 @@ def test_timestamp_constructor_identity(): expected = Timestamp("2017-01-01T12") result = Timestamp(expected) assert result is expected + + +@pytest.mark.parametrize("kwargs", [{}, {"year": 2020}, {"year": 2020, "month": 1}]) +def test_constructor_missing_keyword(kwargs): + # GH 31200 + + # The exact error message of datetime() depends on its version + # We use this instead of explicit message for backward and forward compatibility + try: + datetime(**kwargs) + except TypeError as e: + msg = str(e) + + with pytest.raises(TypeError, match=re.escape(msg)): + Timestamp(**kwargs) diff --git a/pandas/tests/tslibs/test_timestamps.py b/pandas/tests/tslibs/test_timestamps.py deleted file mode 100644 index b14926fb9b13f..0000000000000 --- a/pandas/tests/tslibs/test_timestamps.py +++ /dev/null @@ -1,11 +0,0 @@ -import pytest - -from pandas import Timestamp - - -@pytest.mark.parametrize("kwargs", [{}, {"year": 2020}, {"year": 2020, "month": 1}]) -def test_timestamp_constructor_missing_keyword(kwargs): - # GH 31200 - msg = r"function missing required argument .* \(pos .\)" - with pytest.raises(TypeError, match=msg): - Timestamp(**kwargs) From d3a25816edcd127f2c35e17551d0953d3b9eb78d Mon Sep 17 00:00:00 2001 From: fujiaxiang Date: Mon, 10 Feb 2020 21:01:32 +0800 Subject: [PATCH 3/3] updated test and whatsnew (GH31200) --- doc/source/whatsnew/v1.1.0.rst | 2 +- pandas/tests/scalar/timestamp/test_constructors.py | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 59f9670902c98..6e169de29f90d 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -42,7 +42,6 @@ Other enhancements ^^^^^^^^^^^^^^^^^^ - :class:`Styler` may now render CSS more efficiently where multiple cells have the same styling (:issue:`30876`) -- :class:`Timestamp` now raises more explanatory error message when year, month or day is missing (:issue:`31200`) - .. --------------------------------------------------------------------------- @@ -110,6 +109,7 @@ Datetimelike - Bug in :class:`Timestamp` where constructing :class:`Timestamp` with dateutil timezone less than 128 nanoseconds before daylight saving time switch from winter to summer would result in nonexistent time (:issue:`31043`) - Bug in :meth:`DataFrame.reindex` and :meth:`Series.reindex` when reindexing with a tz-aware index (:issue:`26683`) - Bug in :meth:`Period.to_timestamp`, :meth:`Period.start_time` with microsecond frequency returning a timestamp one nanosecond earlier than the correct time (:issue:`31475`) +- :class:`Timestamp` raising confusing error message when year, month or day is missing (:issue:`31200`) Timedelta ^^^^^^^^^ diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index c6a9a0a5be8a2..7ff8a757b2197 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -1,6 +1,5 @@ import calendar from datetime import datetime, timedelta -import re import dateutil.tz from dateutil.tz import tzutc @@ -558,11 +557,9 @@ def test_constructor_missing_keyword(kwargs): # GH 31200 # The exact error message of datetime() depends on its version - # We use this instead of explicit message for backward and forward compatibility - try: - datetime(**kwargs) - except TypeError as e: - msg = str(e) + msg1 = r"function missing required argument '(year|month|day)' \(pos [123]\)" + msg2 = r"Required argument '(year|month|day)' \(pos [123]\) not found" + msg = "|".join([msg1, msg2]) - with pytest.raises(TypeError, match=re.escape(msg)): + with pytest.raises(TypeError, match=msg): Timestamp(**kwargs)