Skip to content

Commit 8599f2d

Browse files
committed
feat: add deprecation warning for Expr in date functions
- Added a deprecation warning helper in `functions.py` to warn users when an `Expr` is passed to: - `date_part` - `datepart` - `extract` - `date_trunc` - `datetrunc` - Kept plain string paths unchanged. test: enhance tests for deprecation warnings - Added tests in `test_functions.py` to check for deprecation warnings. - Implemented no-warning checks for native strings. - Updated an existing temporal test to use strings to reduce warning noise.
1 parent 43df9f7 commit 8599f2d

2 files changed

Lines changed: 69 additions & 10 deletions

File tree

python/datafusion/functions.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from __future__ import annotations
4040

4141
import inspect
42+
import warnings
4243
from typing import TYPE_CHECKING, Any
4344

4445
import pyarrow as pa
@@ -60,6 +61,16 @@
6061
sort_or_default,
6162
)
6263

64+
65+
def _warn_expr_for_literal_arg(function_name: str, arg_name: str) -> None:
66+
warnings.warn(
67+
f"Passing Expr for {function_name}() argument {arg_name!r} is deprecated; "
68+
"pass a Python literal instead.",
69+
DeprecationWarning,
70+
stacklevel=3,
71+
)
72+
73+
6374
__all__ = [
6475
"abs",
6576
"acos",
@@ -2575,7 +2586,7 @@ def datepart(part: Expr | str, date: Expr) -> Expr:
25752586
See Also:
25762587
This is an alias for :py:func:`date_part`.
25772588
"""
2578-
return date_part(part, date)
2589+
return _date_part(part, date, "datepart")
25792590

25802591

25812592
def date_part(part: Expr | str, date: Expr) -> Expr:
@@ -2595,6 +2606,12 @@ def date_part(part: Expr | str, date: Expr) -> Expr:
25952606
>>> result.collect_column("y")[0].as_py()
25962607
2021
25972608
"""
2609+
return _date_part(part, date, "date_part")
2610+
2611+
2612+
def _date_part(part: Expr | str, date: Expr, function_name: str) -> Expr:
2613+
if isinstance(part, Expr):
2614+
_warn_expr_for_literal_arg(function_name, "part")
25982615
part = coerce_to_expr(part)
25992616
return Expr(f.date_part(part.expr, date.expr))
26002617

@@ -2605,7 +2622,7 @@ def extract(part: Expr | str, date: Expr) -> Expr:
26052622
See Also:
26062623
This is an alias for :py:func:`date_part`.
26072624
"""
2608-
return date_part(part, date)
2625+
return _date_part(part, date, "extract")
26092626

26102627

26112628
def date_trunc(part: Expr | str, date: Expr) -> Expr:
@@ -2626,6 +2643,12 @@ def date_trunc(part: Expr | str, date: Expr) -> Expr:
26262643
>>> str(result.collect_column("t")[0].as_py())
26272644
'2021-07-01 00:00:00'
26282645
"""
2646+
return _date_trunc(part, date, "date_trunc")
2647+
2648+
2649+
def _date_trunc(part: Expr | str, date: Expr, function_name: str) -> Expr:
2650+
if isinstance(part, Expr):
2651+
_warn_expr_for_literal_arg(function_name, "part")
26292652
part = coerce_to_expr(part)
26302653
return Expr(f.date_trunc(part.expr, date.expr))
26312654

@@ -2636,7 +2659,7 @@ def datetrunc(part: Expr | str, date: Expr) -> Expr:
26362659
See Also:
26372660
This is an alias for :py:func:`date_trunc`.
26382661
"""
2639-
return date_trunc(part, date)
2662+
return _date_trunc(part, date, "datetrunc")
26402663

26412664

26422665
def date_bin(stride: Expr, source: Expr, origin: Expr) -> Expr:

python/tests/test_functions.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717
import math
18+
import warnings
1819
from datetime import date, datetime, time, timezone
1920

2021
import numpy as np
@@ -1086,10 +1087,10 @@ def test_hash_functions(df):
10861087

10871088
def test_temporal_functions(df):
10881089
df = df.select(
1089-
f.date_part(literal("month"), column("d")),
1090-
f.datepart(literal("year"), column("d")),
1091-
f.date_trunc(literal("month"), column("d")),
1092-
f.datetrunc(literal("day"), column("d")),
1090+
f.date_part("month", column("d")),
1091+
f.datepart("year", column("d")),
1092+
f.date_trunc("month", column("d")),
1093+
f.datetrunc("day", column("d")),
10931094
f.date_bin(
10941095
literal("15 minutes").cast(pa.string()),
10951096
column("d"),
@@ -1100,7 +1101,7 @@ def test_temporal_functions(df):
11001101
f.to_timestamp_seconds(literal("2023-09-07 05:06:14.523952")),
11011102
f.to_timestamp_millis(literal("2023-09-07 05:06:14.523952")),
11021103
f.to_timestamp_micros(literal("2023-09-07 05:06:14.523952")),
1103-
f.extract(literal("day"), column("d")),
1104+
f.extract("day", column("d")),
11041105
f.to_timestamp(
11051106
literal("2023-09-07 05:06:14.523952000"), literal("%Y-%m-%d %H:%M:%S.%f")
11061107
),
@@ -2160,16 +2161,51 @@ def test_date_part_native_str(self):
21602161
ctx = SessionContext()
21612162
df = ctx.from_pydict({"a": ["2021-07-15T00:00:00"]})
21622163
df = df.select(f.to_timestamp(column("a")).alias("a"))
2163-
result = df.select(f.date_part("year", column("a")).alias("y")).collect()
2164+
with warnings.catch_warnings():
2165+
warnings.simplefilter("error", DeprecationWarning)
2166+
result = df.select(f.date_part("year", column("a")).alias("y")).collect()
21642167
assert result[0].column(0)[0].as_py() == 2021
21652168

2169+
@pytest.mark.parametrize(
2170+
("func", "name"),
2171+
[
2172+
pytest.param(f.date_part, "date_part", id="date_part"),
2173+
pytest.param(f.datepart, "datepart", id="datepart"),
2174+
pytest.param(f.extract, "extract", id="extract"),
2175+
],
2176+
)
2177+
def test_date_part_expr_part_warns_deprecated(self, func, name):
2178+
with pytest.warns(
2179+
DeprecationWarning,
2180+
match=rf"Passing Expr for {name}\(\) argument 'part' is deprecated",
2181+
):
2182+
expr = func(literal("year"), column("a"))
2183+
assert expr is not None
2184+
21662185
def test_date_trunc_native_str(self):
21672186
ctx = SessionContext()
21682187
df = ctx.from_pydict({"a": ["2021-07-15T12:34:56"]})
21692188
df = df.select(f.to_timestamp(column("a")).alias("a"))
2170-
result = df.select(f.date_trunc("month", column("a")).alias("t")).collect()
2189+
with warnings.catch_warnings():
2190+
warnings.simplefilter("error", DeprecationWarning)
2191+
result = df.select(f.date_trunc("month", column("a")).alias("t")).collect()
21712192
assert str(result[0].column(0)[0].as_py()) == "2021-07-01 00:00:00"
21722193

2194+
@pytest.mark.parametrize(
2195+
("func", "name"),
2196+
[
2197+
pytest.param(f.date_trunc, "date_trunc", id="date_trunc"),
2198+
pytest.param(f.datetrunc, "datetrunc", id="datetrunc"),
2199+
],
2200+
)
2201+
def test_date_trunc_expr_part_warns_deprecated(self, func, name):
2202+
with pytest.warns(
2203+
DeprecationWarning,
2204+
match=rf"Passing Expr for {name}\(\) argument 'part' is deprecated",
2205+
):
2206+
expr = func(literal("month"), column("a"))
2207+
assert expr is not None
2208+
21732209
def test_left_native_int(self):
21742210
ctx = SessionContext()
21752211
df = ctx.from_pydict({"a": ["the cat"]})

0 commit comments

Comments
 (0)