From e4dd665d11659d6e114aad630cd2e49265e5a8ba Mon Sep 17 00:00:00 2001 From: Stijn de Gooijer Date: Fri, 28 Jul 2023 17:49:01 +0200 Subject: [PATCH] refactor(python): Add a simple util `issue_deprecation_warning` (#10146) --- py-polars/polars/dataframe/frame.py | 10 +-- py-polars/polars/expr/datetime.py | 2 +- py-polars/polars/expr/expr.py | 2 +- py-polars/polars/expr/list.py | 2 +- py-polars/polars/expr/string.py | 8 +-- py-polars/polars/expr/whenthen.py | 10 +-- .../polars/functions/aggregation/vertical.py | 46 +++++-------- py-polars/polars/functions/as_datatype.py | 9 +-- py-polars/polars/functions/lazy.py | 64 ++++++------------- py-polars/polars/functions/range.py | 34 ++++------ py-polars/polars/functions/repeat.py | 9 +-- py-polars/polars/functions/whenthen.py | 2 +- py-polars/polars/io/database.py | 2 +- .../polars/io/pyarrow_dataset/functions.py | 9 +-- py-polars/polars/lazyframe/frame.py | 16 ++--- py-polars/polars/lazyframe/groupby.py | 9 +-- py-polars/polars/series/datetime.py | 2 +- py-polars/polars/series/list.py | 2 +- py-polars/polars/series/series.py | 22 ++----- py-polars/polars/series/string.py | 10 +-- py-polars/polars/sql/context.py | 2 +- py-polars/polars/string_cache.py | 11 ++-- py-polars/polars/utils/_parse_expr_input.py | 15 ++--- .../utils/{decorators.py => deprecation.py} | 53 ++++++++------- py-polars/polars/utils/meta.py | 9 +-- .../tests/unit/datatypes/test_temporal.py | 10 ++- py-polars/tests/unit/functions/test_range.py | 4 +- py-polars/tests/unit/operations/test_pivot.py | 5 +- py-polars/tests/unit/utils/test_utils.py | 2 +- 29 files changed, 136 insertions(+), 245 deletions(-) rename py-polars/polars/utils/{decorators.py => deprecation.py} (90%) diff --git a/py-polars/polars/dataframe/frame.py b/py-polars/polars/dataframe/frame.py index d223d847d242..af88ba1ea27b 100644 --- a/py-polars/polars/dataframe/frame.py +++ b/py-polars/polars/dataframe/frame.py @@ -4,7 +4,6 @@ import contextlib import os import random -import warnings from collections import defaultdict from collections.abc import Sized from io import BytesIO, StringIO, TextIOWrapper @@ -87,12 +86,11 @@ from polars.utils._parse_expr_input import parse_as_expression from polars.utils._wrap import wrap_expr, wrap_ldf, wrap_s from polars.utils.convert import _timedelta_to_pl_duration -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias, issue_deprecation_warning from polars.utils.various import ( _prepare_row_count_args, _process_null_values, can_create_dicts_with_pyarrow, - find_stacklevel, handle_projection_columns, is_bool_sequence, is_int_sequence, @@ -6550,12 +6548,10 @@ def pivot( columns = [columns] if aggregate_function is no_default: - warnings.warn( + issue_deprecation_warning( "In a future version of polars, the default `aggregate_function` " "will change from `'first'` to `None`. Please pass `'first'` to keep the " - "current behaviour, or `None` to accept the new one.", - DeprecationWarning, - stacklevel=find_stacklevel(), + "current behaviour, or `None` to accept the new one." ) aggregate_function = "first" diff --git a/py-polars/polars/expr/datetime.py b/py-polars/polars/expr/datetime.py index 8c8de12c980d..d0a9248ebd7c 100644 --- a/py-polars/polars/expr/datetime.py +++ b/py-polars/polars/expr/datetime.py @@ -9,7 +9,7 @@ from polars.utils._parse_expr_input import parse_as_expression from polars.utils._wrap import wrap_expr from polars.utils.convert import _timedelta_to_pl_duration -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias if TYPE_CHECKING: from datetime import timedelta diff --git a/py-polars/polars/expr/expr.py b/py-polars/polars/expr/expr.py index 18714d8fbae5..c314a4b08959 100644 --- a/py-polars/polars/expr/expr.py +++ b/py-polars/polars/expr/expr.py @@ -50,7 +50,7 @@ parse_as_list_of_expressions, ) from polars.utils.convert import _timedelta_to_pl_duration -from polars.utils.decorators import ( +from polars.utils.deprecation import ( deprecated, deprecated_alias, warn_closed_future_change, diff --git a/py-polars/polars/expr/list.py b/py-polars/polars/expr/list.py index 1053b56cf523..ce1e1d6a3da3 100644 --- a/py-polars/polars/expr/list.py +++ b/py-polars/polars/expr/list.py @@ -7,7 +7,7 @@ from polars import functions as F from polars.utils._parse_expr_input import parse_as_expression from polars.utils._wrap import wrap_expr -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias if TYPE_CHECKING: from datetime import date, datetime, time diff --git a/py-polars/polars/expr/string.py b/py-polars/polars/expr/string.py index a9e8c24f99fd..224e6960bd63 100644 --- a/py-polars/polars/expr/string.py +++ b/py-polars/polars/expr/string.py @@ -7,7 +7,7 @@ from polars.exceptions import ChronoFormatWarning from polars.utils._parse_expr_input import parse_as_expression from polars.utils._wrap import wrap_expr -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias, issue_deprecation_warning from polars.utils.various import find_stacklevel if TYPE_CHECKING: @@ -136,14 +136,12 @@ def to_datetime( """ _validate_format_argument(format) if utc is not None: - warnings.warn( + issue_deprecation_warning( "The `utc` argument is now a no-op and has no effect. " "You can safely remove it. " "Offset-naive strings are parsed as ``pl.Datetime(time_unit)``, " "and offset-aware strings are converted to " - '``pl.Datetime(time_unit, "UTC")``.', - DeprecationWarning, - stacklevel=find_stacklevel(), + '``pl.Datetime(time_unit, "UTC")``.' ) return wrap_expr( self._pyexpr.str_to_datetime( diff --git a/py-polars/polars/expr/whenthen.py b/py-polars/polars/expr/whenthen.py index 897b6987f905..23faa1717ef0 100644 --- a/py-polars/polars/expr/whenthen.py +++ b/py-polars/polars/expr/whenthen.py @@ -1,14 +1,12 @@ from __future__ import annotations -import warnings from typing import TYPE_CHECKING, Any import polars.functions as F from polars.expr.expr import Expr from polars.utils._parse_expr_input import parse_as_expression from polars.utils._wrap import wrap_expr -from polars.utils.decorators import deprecated_alias -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import deprecated_alias, issue_deprecation_warning if TYPE_CHECKING: from polars.polars import PyExpr @@ -182,9 +180,7 @@ def otherwise(self, statement: IntoExpr) -> Expr: def _warn_for_deprecated_string_input_behavior(input: str) -> None: - warnings.warn( + issue_deprecation_warning( "in a future version, string input will be parsed as a column name rather than a string literal." - f" To silence this warning, pass the input as an expression instead: `pl.lit({input!r})`", - DeprecationWarning, - stacklevel=find_stacklevel(), + f" To silence this warning, pass the input as an expression instead: `pl.lit({input!r})`" ) diff --git a/py-polars/polars/functions/aggregation/vertical.py b/py-polars/polars/functions/aggregation/vertical.py index 85b3cc698dac..402c83863b2a 100644 --- a/py-polars/polars/functions/aggregation/vertical.py +++ b/py-polars/polars/functions/aggregation/vertical.py @@ -1,12 +1,10 @@ from __future__ import annotations -import warnings from typing import TYPE_CHECKING, Any, Iterable, overload import polars._reexport as pl import polars.functions as F -from polars.utils.decorators import deprecated_alias -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import deprecated_alias, issue_deprecation_warning if TYPE_CHECKING: from polars import Expr, Series @@ -91,10 +89,8 @@ def all( if exprs is None: return F.col("*") elif isinstance(exprs, pl.Series): - warnings.warn( - "passing a Series to `all` is deprecated. Use `Series.all()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `all` is deprecated. Use `Series.all()` instead." ) return exprs.all() elif isinstance(exprs, str): @@ -163,10 +159,8 @@ def any( """ if not more_exprs: if isinstance(exprs, pl.Series): - warnings.warn( - "passing a Series to `any` is deprecated. Use `Series.any()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `any` is deprecated. Use `Series.any()` instead." ) return exprs.any() elif isinstance(exprs, str): @@ -257,10 +251,8 @@ def max(exprs: IntoExpr | Iterable[IntoExpr], *more_exprs: IntoExpr) -> Expr | A """ if not more_exprs: if isinstance(exprs, pl.Series): - warnings.warn( - "passing a Series to `max` is deprecated. Use `Series.max()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `max` is deprecated. Use `Series.max()` instead." ) return exprs.max() elif isinstance(exprs, str): @@ -353,10 +345,8 @@ def min( """ if not more_exprs: if isinstance(exprs, pl.Series): - warnings.warn( - "passing a Series to `min` is deprecated. Use `Series.min()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `min` is deprecated. Use `Series.min()` instead." ) return exprs.min() elif isinstance(exprs, str): @@ -450,10 +440,8 @@ def sum( """ if not more_exprs: if isinstance(exprs, pl.Series): - warnings.warn( - "passing a Series to `sum` is deprecated. Use `Series.sum()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `sum` is deprecated. Use `Series.sum()` instead." ) return exprs.sum() elif isinstance(exprs, str): @@ -524,10 +512,8 @@ def cumsum( """ if not more_exprs: if isinstance(exprs, pl.Series): - warnings.warn( - "passing a Series to `cumsum` is deprecated. Use `Series.cumsum()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `cumsum` is deprecated. Use `Series.cumsum()` instead." ) return exprs.cumsum() elif isinstance(exprs, str): @@ -538,8 +524,6 @@ def cumsum( def _warn_for_deprecated_horizontal_use(name: str) -> None: - warnings.warn( - f"using `{name}` for horizontal computation is deprecated. Use `{name}_horizontal` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + f"using `{name}` for horizontal computation is deprecated. Use `{name}_horizontal` instead." ) diff --git a/py-polars/polars/functions/as_datatype.py b/py-polars/polars/functions/as_datatype.py index 2c4cafe88a2f..23b2771a50bc 100644 --- a/py-polars/polars/functions/as_datatype.py +++ b/py-polars/polars/functions/as_datatype.py @@ -1,7 +1,6 @@ from __future__ import annotations import contextlib -import warnings from typing import TYPE_CHECKING, Iterable, overload from polars import functions as F @@ -11,7 +10,7 @@ parse_as_list_of_expressions, ) from polars.utils._wrap import wrap_expr -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import issue_deprecation_warning with contextlib.suppress(ImportError): # Module not available when building docs import polars.polars as plr @@ -376,11 +375,9 @@ def struct( """ if "exprs" in named_exprs: - warnings.warn( + issue_deprecation_warning( "passing expressions to `struct` using the keyword argument `exprs` is" - " deprecated. Use positional syntax instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + " deprecated. Use positional syntax instead." ) first_input = named_exprs.pop("exprs") pyexprs = parse_as_list_of_expressions(first_input, *exprs, **named_exprs) diff --git a/py-polars/polars/functions/lazy.py b/py-polars/polars/functions/lazy.py index 9ac37b0ab735..5a8d728c010f 100644 --- a/py-polars/polars/functions/lazy.py +++ b/py-polars/polars/functions/lazy.py @@ -1,7 +1,6 @@ from __future__ import annotations import contextlib -import warnings from datetime import date, datetime, time, timedelta from typing import TYPE_CHECKING, Any, Callable, Iterable, Sequence, overload @@ -27,8 +26,7 @@ _time_to_pl_time, _timedelta_to_pl_timedelta, ) -from polars.utils.decorators import deprecated_alias -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import deprecated_alias, issue_deprecation_warning with contextlib.suppress(ImportError): # Module not available when building docs import polars.polars as plr @@ -323,10 +321,8 @@ def count(column: str | Series | None = None) -> Expr | int: return wrap_expr(plr.count()) if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `count` is deprecated. Use `Series.len()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `count` is deprecated. Use `Series.len()` instead." ) return column.len() return col(column).count() @@ -385,10 +381,8 @@ def std(column: str | Series, ddof: int = 1) -> Expr | float | None: """ if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `std` is deprecated. Use `Series.std()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `std` is deprecated. Use `Series.std()` instead." ) return column.std(ddof) return col(column).std(ddof) @@ -434,10 +428,8 @@ def var(column: str | Series, ddof: int = 1) -> Expr | float | None: """ if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `var` is deprecated. Use `Series.var()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `var` is deprecated. Use `Series.var()` instead." ) return column.var(ddof) return col(column).var(ddof) @@ -472,10 +464,8 @@ def mean(column: str | Series) -> Expr | float | None: """ if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `mean` is deprecated. Use `Series.mean()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `mean` is deprecated. Use `Series.mean()` instead." ) return column.mean() return col(column).mean() @@ -541,10 +531,8 @@ def median(column: str | Series) -> Expr | float | int | None: """ if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `median` is deprecated. Use `Series.median()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `median` is deprecated. Use `Series.median()` instead." ) return column.median() return col(column).median() @@ -579,10 +567,8 @@ def n_unique(column: str | Series) -> Expr | int: """ if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `n_unique` is deprecated. Use `Series.n_unique()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `n_unique` is deprecated. Use `Series.n_unique()` instead." ) return column.n_unique() return col(column).n_unique() @@ -674,10 +660,8 @@ def first(column: str | Series | None = None) -> Expr | Any: return wrap_expr(plr.first()) if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `first` is deprecated. Use `series[0]` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `first` is deprecated. Use `series[0]` instead." ) if column.len() > 0: return column[0] @@ -740,10 +724,8 @@ def last(column: str | Series | None = None) -> Expr: return wrap_expr(plr.last()) if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `last` is deprecated. Use `series[-1]` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `last` is deprecated. Use `series[-1]` instead." ) if column.len() > 0: return column[-1] @@ -800,10 +782,8 @@ def head(column: str | Series, n: int = 10) -> Expr | Series: """ if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `head` is deprecated. Use `Series.head()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `head` is deprecated. Use `Series.head()` instead." ) return column.head(n) return col(column).head(n) @@ -857,10 +837,8 @@ def tail(column: str | Series, n: int = 10) -> Expr | Series: """ if isinstance(column, pl.Series): - warnings.warn( - "passing a Series to `tail` is deprecated. Use `Series.tail()` instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "passing a Series to `tail` is deprecated. Use `Series.tail()` instead." ) return column.tail(n) return col(column).tail(n) diff --git a/py-polars/polars/functions/range.py b/py-polars/polars/functions/range.py index 908116dd8fd0..f26aa70f69a0 100644 --- a/py-polars/polars/functions/range.py +++ b/py-polars/polars/functions/range.py @@ -1,7 +1,6 @@ from __future__ import annotations import contextlib -import warnings from datetime import time, timedelta from typing import TYPE_CHECKING, overload @@ -12,8 +11,7 @@ from polars.utils.convert import ( _timedelta_to_pl_duration, ) -from polars.utils.decorators import deprecated_alias -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import deprecated_alias, issue_deprecation_warning with contextlib.suppress(ImportError): # Module not available when building docs import polars.polars as plr @@ -119,14 +117,12 @@ def arange( """ # This check is not water-proof, but we cannot check for literal expressions here if not (isinstance(start, int) and isinstance(end, int)): - warnings.warn( + issue_deprecation_warning( " `arange` has been replaced by two new functions:" " `int_range` for generating a single range," " and `int_ranges` for generating a list column with multiple ranges." " `arange` will remain available as an alias for `int_range`, which means its behaviour will change." - " To silence this warning, use either of the new functions.", - DeprecationWarning, - stacklevel=find_stacklevel(), + " To silence this warning, use either of the new functions." ) start = parse_as_expression(start) @@ -534,10 +530,8 @@ def date_range( """ if name is not None: - warnings.warn( - "the `name` argument is deprecated. Use the `alias` method instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "the `name` argument is deprecated. Use the `alias` method instead." ) interval = _parse_interval_argument(interval) @@ -795,10 +789,8 @@ def time_range( """ if name is not None: - warnings.warn( - "the `name` argument is deprecated. Use the `alias` method instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "the `name` argument is deprecated. Use the `alias` method instead." ) interval = _parse_interval_argument(interval) @@ -958,20 +950,16 @@ def _parse_interval_argument(interval: str | timedelta) -> str: def _warn_for_deprecation_date_range() -> None: - warnings.warn( + issue_deprecation_warning( "behavior of `date_range` will change in a future version." " The result will be a single range of type Date or Datetime instead of List." - " Use the new `date_ranges` function to retain the old functionality and silence this warning.", - DeprecationWarning, - stacklevel=find_stacklevel(), + " Use the new `date_ranges` function to retain the old functionality and silence this warning." ) def _warn_for_deprecation_time_range() -> None: - warnings.warn( + issue_deprecation_warning( "behavior of `time_range` will change in a future version." " The result will be a single range of type Time instead of List." - " Use the new `date_ranges` function to retain the old functionality and silence this warning.", - DeprecationWarning, - stacklevel=find_stacklevel(), + " Use the new `date_ranges` function to retain the old functionality and silence this warning." ) diff --git a/py-polars/polars/functions/repeat.py b/py-polars/polars/functions/repeat.py index 2431590f4659..4e7da2a6436a 100644 --- a/py-polars/polars/functions/repeat.py +++ b/py-polars/polars/functions/repeat.py @@ -1,14 +1,13 @@ from __future__ import annotations import contextlib -import warnings from typing import TYPE_CHECKING, overload from polars import functions as F from polars.datatypes import Float64 from polars.utils._parse_expr_input import parse_as_expression from polars.utils._wrap import wrap_expr -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import issue_deprecation_warning with contextlib.suppress(ImportError): # Module not available when building docs import polars.polars as plr @@ -122,10 +121,8 @@ def repeat( """ if name is not None: - warnings.warn( - "the `name` argument is deprecated. Use the `alias` method instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "the `name` argument is deprecated. Use the `alias` method instead." ) if isinstance(n, int): diff --git a/py-polars/polars/functions/whenthen.py b/py-polars/polars/functions/whenthen.py index 620ae6b5e526..e32a9ff72739 100644 --- a/py-polars/polars/functions/whenthen.py +++ b/py-polars/polars/functions/whenthen.py @@ -5,7 +5,7 @@ import polars._reexport as pl from polars.utils._parse_expr_input import parse_as_expression -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias with contextlib.suppress(ImportError): # Module not available when building docs import polars.polars as plr diff --git a/py-polars/polars/io/database.py b/py-polars/polars/io/database.py index 510d1aeca6af..474db87d168f 100644 --- a/py-polars/polars/io/database.py +++ b/py-polars/polars/io/database.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any from polars.convert import from_arrow -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias if TYPE_CHECKING: from polars import DataFrame diff --git a/py-polars/polars/io/pyarrow_dataset/functions.py b/py-polars/polars/io/pyarrow_dataset/functions.py index 9fed5ee4b136..930116692f69 100644 --- a/py-polars/polars/io/pyarrow_dataset/functions.py +++ b/py-polars/polars/io/pyarrow_dataset/functions.py @@ -1,10 +1,9 @@ from __future__ import annotations -import warnings from typing import TYPE_CHECKING from polars.io.pyarrow_dataset.anonymous_scan import _scan_pyarrow_dataset -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import issue_deprecation_warning if TYPE_CHECKING: from polars import LazyFrame @@ -96,10 +95,8 @@ def scan_ds(ds: pa.dataset.Dataset, *, allow_pyarrow_filter: bool = True) -> Laz └───────┴────────┴────────────┘ """ - warnings.warn( + issue_deprecation_warning( "`scan_ds` has been renamed; this" - " redirect is temporary, please use `scan_pyarrow_dataset` instead", - category=DeprecationWarning, - stacklevel=find_stacklevel(), + " redirect is temporary, please use `scan_pyarrow_dataset` instead" ) return scan_pyarrow_dataset(ds, allow_pyarrow_filter=allow_pyarrow_filter) diff --git a/py-polars/polars/lazyframe/frame.py b/py-polars/polars/lazyframe/frame.py index 69bc6657acf6..d31cde3f72e9 100644 --- a/py-polars/polars/lazyframe/frame.py +++ b/py-polars/polars/lazyframe/frame.py @@ -2,7 +2,6 @@ import contextlib import os -import warnings from datetime import date, datetime, time, timedelta from io import BytesIO, StringIO from pathlib import Path @@ -55,12 +54,11 @@ ) from polars.utils._wrap import wrap_df, wrap_expr from polars.utils.convert import _timedelta_to_pl_duration -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias, issue_deprecation_warning from polars.utils.various import ( _in_notebook, _prepare_row_count_args, _process_null_values, - find_stacklevel, normalise_filepath, ) @@ -2089,11 +2087,9 @@ def select( structify = bool(int(os.environ.get("POLARS_AUTO_STRUCTIFY", 0))) if "exprs" in named_exprs: - warnings.warn( + issue_deprecation_warning( "passing expressions to `select` using the keyword argument `exprs` is" - " deprecated. Use positional syntax instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + " deprecated. Use positional syntax instead." ) first_input = named_exprs.pop("exprs") pyexprs = parse_as_list_of_expressions( @@ -3216,11 +3212,9 @@ def with_columns( structify = bool(int(os.environ.get("POLARS_AUTO_STRUCTIFY", 0))) if "exprs" in named_exprs: - warnings.warn( + issue_deprecation_warning( "passing expressions to `with_columns` using the keyword argument" - " `exprs` is deprecated. Use positional syntax instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + " `exprs` is deprecated. Use positional syntax instead." ) first_input = named_exprs.pop("exprs") pyexprs = parse_as_list_of_expressions( diff --git a/py-polars/polars/lazyframe/groupby.py b/py-polars/polars/lazyframe/groupby.py index a75726cde9ec..65d4292f65b6 100644 --- a/py-polars/polars/lazyframe/groupby.py +++ b/py-polars/polars/lazyframe/groupby.py @@ -1,12 +1,11 @@ from __future__ import annotations -import warnings from typing import TYPE_CHECKING, Callable, Iterable from polars import functions as F from polars.utils._parse_expr_input import parse_as_list_of_expressions from polars.utils._wrap import wrap_ldf -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import issue_deprecation_warning if TYPE_CHECKING: from polars import DataFrame, LazyFrame @@ -142,11 +141,9 @@ def agg( ) if "aggs" in named_aggs: - warnings.warn( + issue_deprecation_warning( "passing expressions to `agg` using the keyword argument `aggs` is" - " deprecated. Use positional syntax instead.", - DeprecationWarning, - stacklevel=find_stacklevel(), + " deprecated. Use positional syntax instead." ) first_input = named_aggs.pop("aggs") pyexprs = parse_as_list_of_expressions(first_input, *aggs, **named_aggs) diff --git a/py-polars/polars/series/datetime.py b/py-polars/polars/series/datetime.py index 6034ae91e199..2b0620d1b4eb 100644 --- a/py-polars/polars/series/datetime.py +++ b/py-polars/polars/series/datetime.py @@ -6,7 +6,7 @@ from polars.series.utils import expr_dispatch from polars.utils._wrap import wrap_s from polars.utils.convert import _to_python_date, _to_python_datetime -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias if TYPE_CHECKING: import datetime as dt diff --git a/py-polars/polars/series/list.py b/py-polars/polars/series/list.py index 6c65570d51c3..037484705204 100644 --- a/py-polars/polars/series/list.py +++ b/py-polars/polars/series/list.py @@ -5,7 +5,7 @@ from polars import functions as F from polars.series.utils import expr_dispatch from polars.utils._wrap import wrap_s -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias if TYPE_CHECKING: from datetime import date, datetime, time diff --git a/py-polars/polars/series/series.py b/py-polars/polars/series/series.py index 07f861f7b473..cda1c4e8c6e2 100644 --- a/py-polars/polars/series/series.py +++ b/py-polars/polars/series/series.py @@ -3,7 +3,6 @@ import contextlib import math import os -import warnings from datetime import date, datetime, time, timedelta from typing import ( TYPE_CHECKING, @@ -89,11 +88,10 @@ _datetime_to_pl_timestamp, _time_to_pl_time, ) -from polars.utils.decorators import deprecated_alias +from polars.utils.deprecation import deprecated_alias, issue_deprecation_warning from polars.utils.meta import get_index_type from polars.utils.various import ( _is_generator, - find_stacklevel, parse_version, range_to_series, range_to_slice, @@ -412,11 +410,9 @@ def shape(self) -> tuple[int]: @property def time_unit(self) -> TimeUnit | None: """Get the time unit of underlying Datetime Series as {"ns", "us", "ms"}.""" - warnings.warn( + issue_deprecation_warning( "`Series.time_unit` is deprecated and will be removed in a future version," - " please use `Series.dtype.time_unit` instead", - category=DeprecationWarning, - stacklevel=find_stacklevel(), + " please use `Series.dtype.time_unit` instead" ) return self._s.time_unit() @@ -2156,12 +2152,10 @@ def rename(self, name: str, *, in_place: bool | None = None) -> Series: # if 'in_place' is not None, this indicates that the parameter was # explicitly set by the caller, and we should warn against it (use # of NoDefault only applies when one of the valid values is None). - warnings.warn( + issue_deprecation_warning( "the `in_place` parameter is deprecated and will be removed in a future" " version; note that renaming is a shallow-copy operation with" - " essentially zero cost.", - category=DeprecationWarning, - stacklevel=find_stacklevel(), + " essentially zero cost." ) if in_place: self._s.rename(name) @@ -2410,12 +2404,10 @@ def append(self, other: Series, *, append_chunks: bool | None = None) -> Self: """ if append_chunks is not None: - warnings.warn( + issue_deprecation_warning( "the `append_chunks` argument will be removed and `append` will change" " to always behave like `append_chunks=True` (the previous default)." - " For the behavior of `append_chunks=False`, use `Series.extend`.", - DeprecationWarning, - stacklevel=find_stacklevel(), + " For the behavior of `append_chunks=False`, use `Series.extend`." ) else: append_chunks = True diff --git a/py-polars/polars/series/string.py b/py-polars/polars/series/string.py index f4f98a91bd1d..d940cfb99d01 100644 --- a/py-polars/polars/series/string.py +++ b/py-polars/polars/series/string.py @@ -1,13 +1,11 @@ from __future__ import annotations -import warnings from typing import TYPE_CHECKING from polars import functions as F from polars.series.utils import expr_dispatch from polars.utils._wrap import wrap_s -from polars.utils.decorators import deprecated_alias -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import deprecated_alias, issue_deprecation_warning if TYPE_CHECKING: from polars import Expr, Series @@ -262,14 +260,12 @@ def strptime( ] """ if utc is not None: - warnings.warn( + issue_deprecation_warning( "The `utc` argument is now a no-op and has no effect. " "You can safely remove it. " "Offset-naive strings are parsed as ``pl.Datetime(time_unit)``, " "and offset-aware strings are converted to " - '``pl.Datetime(time_unit, "UTC")``.', - DeprecationWarning, - stacklevel=find_stacklevel(), + '``pl.Datetime(time_unit, "UTC")``.' ) s = wrap_s(self._s) return ( diff --git a/py-polars/polars/sql/context.py b/py-polars/polars/sql/context.py index 5ec88b408007..486405b4af32 100644 --- a/py-polars/polars/sql/context.py +++ b/py-polars/polars/sql/context.py @@ -13,7 +13,7 @@ from polars.lazyframe import LazyFrame from polars.type_aliases import FrameType from polars.utils._wrap import wrap_ldf -from polars.utils.decorators import deprecated_alias, redirect +from polars.utils.deprecation import deprecated_alias, redirect from polars.utils.various import _get_stack_locals with contextlib.suppress(ImportError): # Module not available when building docs diff --git a/py-polars/polars/string_cache.py b/py-polars/polars/string_cache.py index 928c6d22a0d3..7fe31caaf26a 100644 --- a/py-polars/polars/string_cache.py +++ b/py-polars/polars/string_cache.py @@ -1,10 +1,9 @@ from __future__ import annotations import contextlib -import warnings from typing import TYPE_CHECKING -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import issue_deprecation_warning with contextlib.suppress(ImportError): # Module not available when building docs from polars.polars import enable_string_cache as _enable_string_cache @@ -129,11 +128,9 @@ def toggle_string_cache(toggle: bool) -> None: .. deprecated:: 0.17.0 """ - warnings.warn( - "`toggle_string_cache` has been renamed; this" - " redirect is temporary, please use `enable_string_cache` instead", - category=DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + "`toggle_string_cache` has been renamed;" + " this redirect is temporary, please use `enable_string_cache` instead" ) enable_string_cache(toggle) diff --git a/py-polars/polars/utils/_parse_expr_input.py b/py-polars/polars/utils/_parse_expr_input.py index 15e0243601d5..87f95efa1831 100644 --- a/py-polars/polars/utils/_parse_expr_input.py +++ b/py-polars/polars/utils/_parse_expr_input.py @@ -1,13 +1,12 @@ from __future__ import annotations -import warnings from datetime import date, datetime, time, timedelta from typing import TYPE_CHECKING, Iterable import polars._reexport as pl from polars import functions as F from polars.exceptions import ComputeError -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import issue_deprecation_warning if TYPE_CHECKING: from polars import Expr @@ -55,12 +54,10 @@ def _parse_regular_inputs( and isinstance(inputs[0], Iterable) and not isinstance(inputs[0], (str, pl.Series)) ): - warnings.warn( + issue_deprecation_warning( "In the next breaking release, combining list input and positional input will result in an error." " To silence this warning, either unpack the list , or append the positional inputs to the list first." - " The resulting behavior will be identical", - DeprecationWarning, - stacklevel=find_stacklevel(), + " The resulting behavior will be identical" ) input_list = _first_input_to_list(inputs[0]) @@ -72,13 +69,11 @@ def _first_input_to_list( inputs: IntoExpr | Iterable[IntoExpr], ) -> list[IntoExpr]: if inputs is None: - warnings.warn( + issue_deprecation_warning( "In the next breaking release, passing `None` as the first expression input will evaluate to `lit(None)`," " rather than be ignored." " To silence this warning, either pass no arguments or an empty list to retain the current behavior," - " or pass `lit(None)` to opt into the new behavior.", - DeprecationWarning, - stacklevel=find_stacklevel(), + " or pass `lit(None)` to opt into the new behavior." ) return [] elif not isinstance(inputs, Iterable) or isinstance(inputs, (str, pl.Series)): diff --git a/py-polars/polars/utils/decorators.py b/py-polars/polars/utils/deprecation.py similarity index 90% rename from py-polars/polars/utils/decorators.py rename to py-polars/polars/utils/deprecation.py index a128f10e810b..1aa0dc506ab9 100644 --- a/py-polars/polars/utils/decorators.py +++ b/py-polars/polars/utils/deprecation.py @@ -19,21 +19,25 @@ T = TypeVar("T") -def deprecated_alias(**aliases: str) -> Callable[[Callable[P, T]], Callable[P, T]]: +def issue_deprecation_warning(message: str) -> None: + """Issue a DeprecationWarning.""" + warnings.warn(message, DeprecationWarning, stacklevel=find_stacklevel()) + + +def deprecated(reason: str) -> Callable[[Callable[P, T]], Callable[P, T]]: """ - Deprecate a function or method argument. + Decorator which can be used to mark functions as deprecated. - Decorator for deprecated function and method arguments. Use as follows: + It will result in a warning being emitted when the function is used. - @deprecated_alias(old_arg='new_arg') - def myfunc(new_arg): - ... """ def deco(function: Callable[P, T]) -> Callable[P, T]: @wraps(function) def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: - _rename_kwargs(function.__name__, kwargs, aliases) + issue_deprecation_warning( + message=f"Call to deprecated function {function.__name__}. ({reason})" + ) return function(*args, **kwargs) return wrapper @@ -41,22 +45,21 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: return deco -def deprecated(reason: str) -> Callable[[Callable[P, T]], Callable[P, T]]: +def deprecated_alias(**aliases: str) -> Callable[[Callable[P, T]], Callable[P, T]]: """ - Decorator which can be used to mark functions as deprecated. + Deprecate a function or method argument. - It will result in a warning being emitted when the function is used. + Decorator for deprecated function and method arguments. Use as follows: + @deprecated_alias(old_arg='new_arg') + def myfunc(new_arg): + ... """ def deco(function: Callable[P, T]) -> Callable[P, T]: @wraps(function) def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: - warnings.warn( - message=f"Call to deprecated function {function.__name__}. ({reason})", - category=DeprecationWarning, - stacklevel=find_stacklevel(), - ) + _rename_kwargs(function.__name__, kwargs, aliases) return function(*args, **kwargs) return wrapper @@ -113,13 +116,9 @@ def _rename_kwargs( f"{func_name} received both {alias} and {new} as arguments!" f" {alias} is deprecated, use {new} instead." ) - warnings.warn( - message=( - f"`{alias}` is deprecated as an argument to `{func_name}`; use" - f" `{new}` instead." - ), - category=DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + f"`{alias}` is deprecated as an argument to `{func_name}`;" + f" use `{new}` instead." ) kwargs[new] = kwargs.pop(alias) @@ -181,7 +180,7 @@ def decorate(function: Callable[P, T]) -> Callable[P, T]: @wraps(function) def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: if len(args) > num_allowed_args: - warnings.warn(msg, DeprecationWarning, stacklevel=find_stacklevel()) + issue_deprecation_warning(msg) return function(*args, **kwargs) wrapper.__signature__ = new_sig # type: ignore[attr-defined] @@ -222,11 +221,9 @@ def _redirecting_getattr_(obj: T, item: Any) -> Any: if isinstance(item, str) and item in from_to: new_item = from_to[item] new_item_name = new_item if isinstance(new_item, str) else new_item[0] - warnings.warn( - f"`{type(obj).__name__}.{item}` has been renamed; this" - f" redirect is temporary, please use `.{new_item_name}` instead", - category=DeprecationWarning, - stacklevel=find_stacklevel(), + issue_deprecation_warning( + f"`{type(obj).__name__}.{item}` has been renamed;" + f" this redirect is temporary, please use `.{new_item_name}` instead" ) item = new_item_name diff --git a/py-polars/polars/utils/meta.py b/py-polars/polars/utils/meta.py index 155fd8ecb527..beca8518c0d9 100644 --- a/py-polars/polars/utils/meta.py +++ b/py-polars/polars/utils/meta.py @@ -2,10 +2,9 @@ from __future__ import annotations import contextlib -import warnings from typing import TYPE_CHECKING -from polars.utils.various import find_stacklevel +from polars.utils.deprecation import issue_deprecation_warning with contextlib.suppress(ImportError): # Module not available when building docs from polars.polars import get_index_type as _get_index_type @@ -30,11 +29,9 @@ def get_index_type() -> DataTypeClass: def get_idx_type() -> DataTypeClass: """Get the datatype used for Polars indexing.""" - warnings.warn( + issue_deprecation_warning( "`get_idx_type` has been renamed; this" - " redirect is temporary, please use `get_index_type` instead", - category=DeprecationWarning, - stacklevel=find_stacklevel(), + " redirect is temporary, please use `get_index_type` instead" ) return get_index_type() diff --git a/py-polars/tests/unit/datatypes/test_temporal.py b/py-polars/tests/unit/datatypes/test_temporal.py index 8c354719b2c1..d514a6c41a31 100644 --- a/py-polars/tests/unit/datatypes/test_temporal.py +++ b/py-polars/tests/unit/datatypes/test_temporal.py @@ -1957,16 +1957,14 @@ def test_strptime_with_invalid_tz() -> None: def test_utc_deprecation() -> None: - with pytest.warns( - DeprecationWarning, - match="The `utc` argument is now a no-op and has no effect. You can safely remove it", + with pytest.deprecated_call( + match="The `utc` argument is now a no-op and has no effect. You can safely remove it" ): pl.Series(["2020-01-01 03:00:00"]).str.strptime( pl.Datetime("us"), "%Y-%m-%d %H:%M:%S", utc=True ) - with pytest.warns( - DeprecationWarning, - match="The `utc` argument is now a no-op and has no effect. You can safely remove it", + with pytest.deprecated_call( + match="The `utc` argument is now a no-op and has no effect. You can safely remove it" ): pl.Series(["2020-01-01 03:00:00"]).str.to_datetime( "%Y-%m-%d %H:%M:%S", utc=True diff --git a/py-polars/tests/unit/functions/test_range.py b/py-polars/tests/unit/functions/test_range.py index b9fcfd648f54..bf9fb9de0819 100644 --- a/py-polars/tests/unit/functions/test_range.py +++ b/py-polars/tests/unit/functions/test_range.py @@ -171,9 +171,7 @@ def test_date_range() -> None: time_unit=time_unit, eager=True, ) - with pytest.warns( - DeprecationWarning, match="`Series.time_unit` is deprecated.*" - ): + with pytest.deprecated_call(match="`Series.time_unit` is deprecated.*"): assert rng.time_unit == time_unit assert rng.shape == (13,) assert rng.dt[0] == datetime(2020, 1, 1) diff --git a/py-polars/tests/unit/operations/test_pivot.py b/py-polars/tests/unit/operations/test_pivot.py index 5f5ca96b2062..26c8ea277fc0 100644 --- a/py-polars/tests/unit/operations/test_pivot.py +++ b/py-polars/tests/unit/operations/test_pivot.py @@ -302,9 +302,8 @@ def test_pivot_negative_duration() -> None: def test_aggregate_function_deprecation_warning() -> None: df = pl.DataFrame({"a": [1, 2], "b": ["foo", "foo"], "c": ["x", "x"]}) - with pytest.warns( - DeprecationWarning, - match="the default `aggregate_function` will change from `'first'` to `None`", + with pytest.deprecated_call( + match="the default `aggregate_function` will change from `'first'` to `None`" ): df.pivot("a", "b", "c") diff --git a/py-polars/tests/unit/utils/test_utils.py b/py-polars/tests/unit/utils/test_utils.py index e49412b70fef..44793fb799a8 100644 --- a/py-polars/tests/unit/utils/test_utils.py +++ b/py-polars/tests/unit/utils/test_utils.py @@ -15,7 +15,7 @@ _timedelta_to_pl_duration, _timedelta_to_pl_timedelta, ) -from polars.utils.decorators import deprecate_nonkeyword_arguments, redirect +from polars.utils.deprecation import deprecate_nonkeyword_arguments, redirect from polars.utils.meta import get_idx_type from polars.utils.various import _in_notebook, parse_version