Skip to content

API: make some Timestamp args keyword-only #49416

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

Merged
merged 4 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v2.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ See :ref:`install.dependencies` and :ref:`install.optional_dependencies` for mor

Other API changes
^^^^^^^^^^^^^^^^^
- The ``freq``, ``tz``, ``nanosecond``, and ``unit`` keywords in the :class:`Timestamp` constructor are now keyword-only (:issue:`45307`)
- Passing ``nanoseconds`` greater than 999 or less than 0 in :class:`Timestamp` now raises a ``ValueError`` (:issue:`48538`, :issue:`48255`)
- :func:`read_csv`: specifying an incorrect number of columns with ``index_col`` of now raises ``ParserError`` instead of ``IndexError`` when using the c parser.
- Default value of ``dtype`` in :func:`get_dummies` is changed to ``bool`` from ``uint8`` (:issue:`45848`)
Expand Down
8 changes: 4 additions & 4 deletions pandas/_libs/tslibs/timestamps.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ class Timestamp(datetime):
def __new__( # type: ignore[misc]
cls: type[_DatetimeT],
ts_input: np.integer | float | str | _date | datetime | np.datetime64 = ...,
freq: int | None | str | BaseOffset = ...,
tz: str | _tzinfo | None | int = ...,
unit: str | int | None = ...,
year: int | None = ...,
month: int | None = ...,
day: int | None = ...,
hour: int | None = ...,
minute: int | None = ...,
second: int | None = ...,
microsecond: int | None = ...,
nanosecond: int | None = ...,
tzinfo: _tzinfo | None = ...,
*,
nanosecond: int | None = ...,
freq: int | None | str | BaseOffset = ...,
tz: str | _tzinfo | None | int = ...,
unit: str | int | None = ...,
fold: int | None = ...,
) -> _DatetimeT | NaTType: ...
def _set_freq(self, freq: BaseOffset | None) -> None: ...
Expand Down
52 changes: 17 additions & 35 deletions pandas/_libs/tslibs/timestamps.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,10 @@ class Timestamp(_Timestamp):
----------
ts_input : datetime-like, str, int, float
Value to be converted to Timestamp.
year, month, day : int
hour, minute, second, microsecond : int, optional, default 0
tzinfo : datetime.tzinfo, optional, default None
nanosecond : int, optional, default 0
freq : str, DateOffset
Offset which Timestamp will have.
tz : str, pytz.timezone, dateutil.tz.tzfile or None
Expand All @@ -1250,10 +1254,6 @@ class Timestamp(_Timestamp):
Unit used for conversion if ts_input is of type int or float. The
valid values are 'D', 'h', 'm', 's', 'ms', 'us', and 'ns'. For
example, 's' means seconds and 'ms' means milliseconds.
year, month, day : int
hour, minute, second, microsecond : int, optional, default 0
nanosecond : int, optional, default 0
tzinfo : datetime.tzinfo, optional, default None
fold : {0, 1}, default None, keyword-only
Due to daylight saving time, one wall clock time can occur twice
when shifting from summer to winter time; fold describes whether the
Expand Down Expand Up @@ -1480,19 +1480,19 @@ class Timestamp(_Timestamp):
def __new__(
cls,
object ts_input=_no_input,
object freq=None,
tz=None,
unit=None,
year=None,
month=None,
day=None,
hour=None,
minute=None,
second=None,
microsecond=None,
nanosecond=None,
tzinfo_type tzinfo=None,
*,
nanosecond=None,
object freq=None,
tz=None,
unit=None,
fold=None,
):
# The parameter list folds together legacy parameter names (the first
Expand Down Expand Up @@ -1527,27 +1527,6 @@ class Timestamp(_Timestamp):
# GH#17690 tzinfo must be a datetime.tzinfo object, ensured
# by the cython annotation.
if tz is not None:
if (is_integer_object(tz)
and is_integer_object(ts_input)
and is_integer_object(freq)
):
# GH#31929 e.g. Timestamp(2019, 3, 4, 5, 6, tzinfo=foo)
# TODO(GH#45307): this will still be fragile to
# mixed-and-matched positional/keyword arguments
ts_input = datetime(
ts_input,
freq,
tz,
unit or 0,
year or 0,
month or 0,
day or 0,
fold=fold or 0,
)
nanosecond = hour
tz = tzinfo
return cls(ts_input, nanosecond=nanosecond, tz=tz)

raise ValueError('Can provide at most one of tz, tzinfo')

# User passed tzinfo instead of tz; avoid silently ignoring
Expand Down Expand Up @@ -1596,7 +1575,7 @@ class Timestamp(_Timestamp):
if any(arg is not None for arg in _date_attributes):
raise ValueError(
"Cannot pass a date attribute keyword "
"argument when passing a date string"
"argument when passing a date string; 'tz' is keyword-only"
)

elif ts_input is _no_input:
Expand All @@ -1620,17 +1599,20 @@ class Timestamp(_Timestamp):

ts_input = datetime(**datetime_kwargs)

elif is_integer_object(freq):
elif is_integer_object(year):
# User passed positional arguments:
# Timestamp(year, month, day[, hour[, minute[, second[,
# microsecond[, nanosecond[, tzinfo]]]]]])
ts_input = datetime(ts_input, freq, tz, unit or 0,
year or 0, month or 0, day or 0, fold=fold or 0)
nanosecond = hour
tz = minute
ts_input = datetime(ts_input, year, month, day or 0,
hour or 0, minute or 0, second or 0, fold=fold or 0)
freq = None
unit = None

if nanosecond is None:
# nanosecond was not passed as a keyword, but may have been
# passed positionally see test_constructor_nanosecond
nanosecond = microsecond

if getattr(ts_input, 'tzinfo', None) is not None and tz is not None:
raise ValueError("Cannot pass a datetime or Timestamp with tzinfo with "
"the tz parameter. Use tz_convert instead.")
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/indexes/datetimes/test_scalar_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def test_dti_timestamp_freq_fields(self):

msg = "The 'freq' argument in Timestamp is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
ts = Timestamp(idx[-1], idx.freq)
ts = Timestamp(idx[-1], freq=idx.freq)

msg2 = "Timestamp.freq is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg2):
Expand Down
14 changes: 8 additions & 6 deletions pandas/tests/scalar/timestamp/test_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def test_constructor_invalid_tz(self):
with pytest.raises(ValueError, match=msg):
Timestamp("2017-10-22", tzinfo=pytz.utc, tz="UTC")

msg = "Invalid frequency:"
msg = "Cannot pass a date attribute keyword argument when passing a date string"
msg2 = "The 'freq' argument"
with pytest.raises(ValueError, match=msg):
# GH#5168
Expand Down Expand Up @@ -273,11 +273,15 @@ def test_constructor_positional_with_tzinfo(self):
expected = Timestamp("2020-12-31", tzinfo=timezone.utc)
assert ts == expected

@pytest.mark.xfail(reason="GH#45307")
@pytest.mark.parametrize("kwd", ["nanosecond", "microsecond", "second", "minute"])
def test_constructor_positional_keyword_mixed_with_tzinfo(self, kwd):
def test_constructor_positional_keyword_mixed_with_tzinfo(self, kwd, request):
# TODO: if we passed microsecond with a keyword we would mess up
# xref GH#45307
if kwd != "nanosecond":
# nanosecond is keyword-only as of 2.0, others are not
mark = pytest.mark.xfail(reason="GH#45307")
request.node.add_marker(mark)

kwargs = {kwd: 4}
ts = Timestamp(2020, 12, 31, tzinfo=timezone.utc, **kwargs)

Expand Down Expand Up @@ -399,9 +403,7 @@ def test_constructor_fromordinal(self):
tz="UTC",
),
Timestamp(2000, 1, 2, 3, 4, 5, 6, 1, None),
# error: Argument 9 to "Timestamp" has incompatible type "_UTCclass";
# expected "Optional[int]"
Timestamp(2000, 1, 2, 3, 4, 5, 6, 1, pytz.UTC), # type: ignore[arg-type]
Timestamp(2000, 1, 2, 3, 4, 5, 6, 1, pytz.UTC),
],
)
def test_constructor_nanosecond(self, result):
Expand Down