Skip to content

Commit

Permalink
depr(api): deprecate type coercion to_* methods in favor of as_*
Browse files Browse the repository at this point in the history
…methods

Closes #9788

Notes:
- for `to_unit` and `to_interval` I check the raise of the deprecation
warning in the existing tests.
- for `to_timestamp` and `to_date` I added to tests outside the
`test/backends/temportal.py` to check for the warning raise, to avoid
the resource warning socket issue in pyspark when checking for warnings
in the backends/tests. (see
#9843 (comment))
  • Loading branch information
ncclementi authored Aug 16, 2024
1 parent 6780a6b commit fb22e20
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 90 deletions.
6 changes: 3 additions & 3 deletions ibis/backends/bigquery/tests/unit/test_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def test_hashbytes(case, how, dtype, snapshot):
),
)
def test_integer_to_timestamp(case, unit, snapshot):
expr = ibis.literal(case, type=dt.int64).to_timestamp(unit=unit).name("tmp")
expr = ibis.literal(case, type=dt.int64).as_timestamp(unit=unit).name("tmp")
snapshot.assert_match(to_sql(expr), "out.sql")


Expand Down Expand Up @@ -424,12 +424,12 @@ def test_identical_to(alltypes, snapshot):


def test_to_timestamp_no_timezone(alltypes, snapshot):
expr = alltypes.date_string_col.to_timestamp("%F")
expr = alltypes.date_string_col.as_timestamp("%F")
snapshot.assert_match(to_sql(expr), "out.sql")


def test_to_timestamp_timezone(alltypes, snapshot):
expr = (alltypes.date_string_col + " America/New_York").to_timestamp("%F %Z")
expr = (alltypes.date_string_col + " America/New_York").as_timestamp("%F %Z")
snapshot.assert_match(to_sql(expr), "out.sql")


Expand Down
2 changes: 1 addition & 1 deletion ibis/backends/clickhouse/tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ def test_literal_none_to_nullable_column(alltypes):

def test_timestamp_from_integer(con, alltypes, assert_sql):
# timestamp_col has datetime type
expr = alltypes.int_col.to_timestamp()
expr = alltypes.int_col.as_timestamp()
assert_sql(expr, "out.sql")
assert len(con.execute(expr))

Expand Down
2 changes: 1 addition & 1 deletion ibis/backends/druid/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def functional_alltypes(self) -> ir.Table:
# tool that calls itself a time series database or "good for
# working with time series", that lacks a first-class timestamp
# type.
timestamp_col=t.timestamp_col.to_timestamp(unit="ms"),
timestamp_col=t.timestamp_col.as_timestamp(unit="ms"),
)

@property
Expand Down
2 changes: 1 addition & 1 deletion ibis/backends/flink/tests/test_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_count_star(simple_table, assert_sql):
],
)
def test_timestamp_from_unix(simple_table, unit, assert_sql):
expr = simple_table.d.to_timestamp(unit=unit)
expr = simple_table.d.as_timestamp(unit=unit)
assert_sql(expr)


Expand Down
14 changes: 7 additions & 7 deletions ibis/backends/impala/tests/test_exprs.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ def test_builtins(con, alltypes):
i1.fill_null(0),
i4.fill_null(0),
i8.fill_null(0),
i4.to_timestamp("s"),
i4.to_timestamp("ms"),
i4.to_timestamp("us"),
i8.to_timestamp(),
i4.as_timestamp("s"),
i4.as_timestamp("ms"),
i4.as_timestamp("us"),
i8.as_timestamp(),
d.abs(),
d.cast("decimal(12, 2)"),
d.cast("int32"),
Expand Down Expand Up @@ -191,9 +191,9 @@ def test_column_types(alltypes_df, col, expected):
@pytest.mark.parametrize(
("expr", "expected"),
[
(L(50000).to_timestamp("s"), pd.to_datetime(50000, unit="s")),
(L(50000).to_timestamp("ms"), pd.to_datetime(50000, unit="ms")),
(L(5 * 10**8).to_timestamp(), pd.to_datetime(5 * 10**8, unit="s")),
(L(50000).as_timestamp("s"), pd.to_datetime(50000, unit="s")),
(L(50000).as_timestamp("ms"), pd.to_datetime(50000, unit="ms")),
(L(5 * 10**8).as_timestamp(), pd.to_datetime(5 * 10**8, unit="s")),
(
ibis.timestamp("2009-05-17 12:34:56").truncate("y"),
pd.Timestamp("2009-01-01"),
Expand Down
6 changes: 3 additions & 3 deletions ibis/backends/impala/tests/test_value_exprs.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,9 @@ def test_timestamp_day_of_week(method_name, snapshot):
@pytest.mark.parametrize(
"expr_fn",
[
lambda col: col.to_timestamp(),
lambda col: col.to_timestamp("ms"),
lambda col: col.to_timestamp("us"),
lambda col: col.as_timestamp(),
lambda col: col.as_timestamp("ms"),
lambda col: col.as_timestamp("us"),
],
ids=["default", "ms", "us"],
)
Expand Down
13 changes: 7 additions & 6 deletions ibis/backends/tests/test_temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ def test_date_truncate(backend, alltypes, df, unit):
def test_integer_to_interval_timestamp(
backend, con, alltypes, df, unit, displacement_type
):
interval = alltypes.int_col.to_interval(unit=unit)
interval = alltypes.int_col.as_interval(unit=unit)
expr = (alltypes.timestamp_col + interval).name("tmp")

def convert_to_offset(offset, displacement_type=displacement_type):
Expand Down Expand Up @@ -663,7 +663,7 @@ def convert_to_offset(offset, displacement_type=displacement_type):
@pytest.mark.notimpl(["datafusion", "druid"], raises=com.OperationNotDefinedError)
@pytest.mark.notimpl(["exasol"], raises=com.OperationNotDefinedError)
def test_integer_to_interval_date(backend, con, alltypes, df, unit):
interval = alltypes.int_col.to_interval(unit=unit)
interval = alltypes.int_col.as_interval(unit=unit)
month = alltypes.date_string_col[:2]
day = alltypes.date_string_col[3:5]
year = alltypes.date_string_col[6:8]
Expand Down Expand Up @@ -1182,8 +1182,9 @@ def test_integer_to_timestamp(backend, con, unit):

# convert the timestamp to the input unit being tested
int_expr = ibis.literal(pandas_ts // factor)
expr = int_expr.to_timestamp(unit).name("tmp")
result = con.execute(expr)
expr_as = int_expr.as_timestamp(unit).name("tmp")
result = con.execute(expr_as)

expected = pd.Timestamp(pandas_ts, unit="ns").floor(backend_unit)

assert result == expected
Expand Down Expand Up @@ -1260,7 +1261,7 @@ def test_integer_to_timestamp(backend, con, unit):
@pytest.mark.notimpl(["exasol"], raises=com.OperationNotDefinedError)
def test_string_to_timestamp(alltypes, fmt):
table = alltypes
result = table.mutate(date=table.date_string_col.to_timestamp(fmt)).execute()
result = table.mutate(date=table.date_string_col.as_timestamp(fmt)).execute()

# TEST: do we get the same date out, that we put in?
# format string assumes that we are using pandas' strftime
Expand Down Expand Up @@ -1339,7 +1340,7 @@ def test_string_to_timestamp(alltypes, fmt):
@pytest.mark.notimpl(["exasol"], raises=com.OperationNotDefinedError)
def test_string_to_date(alltypes, fmt):
table = alltypes
result = table.mutate(date=table.date_string_col.to_date(fmt)).execute()
result = table.mutate(date=table.date_string_col.as_date(fmt)).execute()

# TEST: do we get the same date out, that we put in?
# format string assumes that we are using pandas' strftime
Expand Down
2 changes: 1 addition & 1 deletion ibis/expr/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ def timestamp(
)
return ops.TimestampFromYMDHMS(*args).to_expr()
elif isinstance(value_or_year, (numbers.Real, ir.IntegerValue)):
raise TypeError("Use ibis.literal(...).to_timestamp() instead")
raise TypeError("Use ibis.literal(...).as_timestamp() instead")
elif isinstance(value_or_year, ir.Expr):
return value_or_year.cast(dt.Timestamp(timezone=timezone))
else:
Expand Down
19 changes: 17 additions & 2 deletions ibis/expr/types/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ibis.common.exceptions import IbisTypeError
from ibis.expr.types.core import _binop
from ibis.expr.types.generic import Column, Scalar, Value
from ibis.util import deprecated

if TYPE_CHECKING:
from collections.abc import Iterable, Sequence
Expand Down Expand Up @@ -1013,7 +1014,7 @@ def histogram(

@public
class IntegerValue(NumericValue):
def to_timestamp(
def as_timestamp(
self,
unit: Literal["s", "ms", "us"] = "s",
) -> ir.TimestampValue:
Expand All @@ -1031,7 +1032,7 @@ def to_timestamp(
"""
return ops.TimestampFromUNIX(self, unit).to_expr()

def to_interval(
def as_interval(
self,
unit: Literal["Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns"] = "s",
) -> ir.IntervalValue:
Expand All @@ -1049,6 +1050,20 @@ def to_interval(
"""
return ops.IntervalFromInteger(self, unit).to_expr()

@deprecated(as_of="10.0", instead="use as_timestamp() instead")
def to_timestamp(
self,
unit: Literal["s", "ms", "us"] = "s",
) -> ir.TimestampValue:
return self.as_timestamp(unit=unit)

@deprecated(as_of="10.0", instead="use as_interval() instead")
def to_interval(
self,
unit: Literal["Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns"] = "s",
) -> ir.IntervalValue:
return self.as_interval(unit=unit)

def convert_base(
self,
from_base: IntegerValue,
Expand Down
17 changes: 13 additions & 4 deletions ibis/expr/types/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ibis import util
from ibis.expr.types.core import _binop
from ibis.expr.types.generic import Column, Scalar, Value
from ibis.util import deprecated

if TYPE_CHECKING:
from collections.abc import Iterable, Sequence
Expand Down Expand Up @@ -1269,7 +1270,7 @@ def replace(
"""
return ops.StringReplace(self, pattern, replacement).to_expr()

def to_timestamp(self, format_str: str) -> ir.TimestampValue:
def as_timestamp(self, format_str: str) -> ir.TimestampValue:
"""Parse a string and return a timestamp.
Parameters
Expand All @@ -1287,7 +1288,7 @@ def to_timestamp(self, format_str: str) -> ir.TimestampValue:
>>> import ibis
>>> ibis.options.interactive = True
>>> t = ibis.memtable({"ts": ["20170206"]})
>>> t.ts.to_timestamp("%Y%m%d")
>>> t.ts.as_timestamp("%Y%m%d")
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ StringToTimestamp(ts, '%Y%m%d') ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
Expand All @@ -1298,7 +1299,11 @@ def to_timestamp(self, format_str: str) -> ir.TimestampValue:
"""
return ops.StringToTimestamp(self, format_str).to_expr()

def to_date(self, format_str: str) -> ir.DateValue:
@deprecated(as_of="10.0", instead="use as_timestamp() instead")
def to_timestamp(self, format_str: str) -> ir.TimestampValue:
return self.as_timestamp(format_str=format_str)

def as_date(self, format_str: str) -> ir.DateValue:
"""Parse a string and return a date.
Parameters
Expand All @@ -1316,7 +1321,7 @@ def to_date(self, format_str: str) -> ir.DateValue:
>>> import ibis
>>> ibis.options.interactive = True
>>> t = ibis.memtable({"ts": ["20170206"]})
>>> t.ts.to_date("%Y%m%d")
>>> t.ts.as_date("%Y%m%d")
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ StringToDate(ts, '%Y%m%d') ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
Expand All @@ -1327,6 +1332,10 @@ def to_date(self, format_str: str) -> ir.DateValue:
"""
return ops.StringToDate(self, format_str).to_expr()

@deprecated(as_of="10.0", instead="use as_date() instead")
def to_date(self, format_str: str) -> ir.DateValue:
return self.as_date(format_str=format_str)

def protocol(self):
"""Parse a URL and extract protocol.
Expand Down
31 changes: 18 additions & 13 deletions ibis/expr/types/temporal.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from ibis.common.temporal import IntervalUnit
from ibis.expr.types.core import _binop
from ibis.expr.types.generic import Column, Scalar, Value
from ibis.util import deprecated

if TYPE_CHECKING:
import datetime
Expand Down Expand Up @@ -822,7 +823,7 @@ class TimestampColumn(Column, TimestampValue):

@public
class IntervalValue(Value):
def to_unit(self, target_unit: str) -> IntervalValue:
def as_unit(self, target_unit: str) -> IntervalValue:
"""Convert this interval to units of `target_unit`."""
# TODO(kszucs): should use a separate operation for unit conversion
# which we can rewrite/simplify to integer multiplication/division
Expand All @@ -839,62 +840,66 @@ def to_unit(self, target_unit: str) -> IntervalValue:
value = util.convert_unit(
self.cast(dt.int64), current_unit.short, target_unit.short
)
return value.to_interval(target_unit)
return value.as_interval(target_unit)

@deprecated(as_of="10.0", instead="use as_unit() instead")
def to_unit(self, target_unit: str) -> IntervalValue:
return self.as_unit(target_unit=target_unit)

@property
def years(self) -> ir.IntegerValue:
"""The number of years (IntegerValue)."""
return self.to_unit("Y")
return self.as_unit("Y")

@property
def quarters(self) -> ir.IntegerValue:
"""The number of quarters (IntegerValue)."""
return self.to_unit("Q")
return self.as_unit("Q")

@property
def months(self) -> ir.IntegerValue:
"""The number of months (IntegerValue)."""
return self.to_unit("M")
return self.as_unit("M")

@property
def weeks(self) -> ir.IntegerValue:
"""The number of weeks (IntegerValue)."""
return self.to_unit("W")
return self.as_unit("W")

@property
def days(self) -> ir.IntegerValue:
"""The number of days (IntegerValue)."""
return self.to_unit("D")
return self.as_unit("D")

@property
def hours(self) -> ir.IntegerValue:
"""The number of hours (IntegerValue)."""
return self.to_unit("h")
return self.as_unit("h")

@property
def minutes(self) -> ir.IntegerValue:
"""The number of minutes (IntegerValue)."""
return self.to_unit("m")
return self.as_unit("m")

@property
def seconds(self) -> ir.IntegerValue:
"""The number of seconds (IntegerValue)."""
return self.to_unit("s")
return self.as_unit("s")

@property
def milliseconds(self) -> ir.IntegerValue:
"""The number of milliseconds (IntegerValue)."""
return self.to_unit("ms")
return self.as_unit("ms")

@property
def microseconds(self) -> ir.IntegerValue:
"""The number of microseconds (IntegerValue)."""
return self.to_unit("us")
return self.as_unit("us")

@property
def nanoseconds(self) -> ir.IntegerValue:
"""The number of nanoseconds (IntegerValue)."""
return self.to_unit("ns")
return self.as_unit("ns")

def __add__(
self,
Expand Down
Loading

0 comments on commit fb22e20

Please sign in to comment.