Skip to content
Open
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
15 changes: 15 additions & 0 deletions chainladder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

import copy
import warnings
import numpy as np
import pandas as pd
from importlib.metadata import version
Expand Down Expand Up @@ -95,6 +96,20 @@ def set_option(

"""
self._validate_option(option)
if option == "ARRAY_BACKEND" and value == "cupy":
warnings.warn(
"The 'cupy' array backend is deprecated and will be removed in a "
"future release. See https://github.com/casact/chainladder-python/issues/843.",
DeprecationWarning,
stacklevel=2,
)
elif option == "ARRAY_PRIORITY" and "cupy" in value:
warnings.warn(
"The 'cupy' array backend is deprecated and will be removed in a "
"future release. See https://github.com/casact/chainladder-python/issues/843.",
DeprecationWarning,
stacklevel=2,
)
setattr(self, option, value)

def reset_option(self, option: str | None = None) -> None:
Expand Down
18 changes: 15 additions & 3 deletions chainladder/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
from __future__ import annotations

import warnings

import numpy as np
import pandas as pd

Expand Down Expand Up @@ -147,7 +149,7 @@ def pipe(self, func, *args, **kwargs):
return func(self, *args, **kwargs)

def set_backend(
self, backend: str, inplace: bool = False, deep: bool = False, **kwargs
self, backend: str, inplace: bool = False, deep: bool = False, _warn: bool = True, **kwargs
):
"""
Converts triangle array_backend.
Expand All @@ -164,6 +166,16 @@ def set_backend(
-------
Triangle with updated array_backend
"""
# Warn once, at the public entry point, so stacklevel=2 points at the
# user's call site rather than an internal recursive call. The _warn
# flag suppresses duplicate warnings from internal recursion below.
if _warn and backend == "cupy":
warnings.warn(
"The 'cupy' array backend is deprecated and will be removed in a "
"future release. See https://github.com/casact/chainladder-python/issues/843.",
DeprecationWarning,
stacklevel=2,
)
if hasattr(self, "array_backend"):
old_backend: str = self.array_backend
else:
Expand Down Expand Up @@ -205,15 +217,15 @@ def set_backend(
if deep:
for k, v in vars(self).items():
if isinstance(v, Common):
v.set_backend(backend, inplace=True, deep=True)
v.set_backend(backend, inplace=True, deep=True, _warn=False)
if hasattr(self, "array_backend"):
self.array_backend = backend
else:
raise AttributeError(backend, "backend is not supported.")
return self
else:
obj = self.copy()
return obj.set_backend(backend=backend, inplace=True, deep=deep, **kwargs)
return obj.set_backend(backend=backend, inplace=True, deep=deep, _warn=False, **kwargs)

@staticmethod
def _validate_assumption(
Expand Down
71 changes: 70 additions & 1 deletion chainladder/utils/tests/test_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,73 @@ def test_reset_option_invalid() -> None:
None
"""
with pytest.raises(ValueError):
cl.options.reset_option('NOT_A_REAL_OPTION')
cl.options.reset_option('NOT_A_REAL_OPTION')


def test_set_option_cupy_backend_deprecated() -> None:
"""
Setting ARRAY_BACKEND to 'cupy' should emit a DeprecationWarning. See issue #843.

Returns
-------
None
"""
try:
with pytest.warns(DeprecationWarning, match="cupy"):
cl.options.set_option('ARRAY_BACKEND', 'cupy')
finally:
cl.options.reset_option('ARRAY_BACKEND')


def test_set_option_cupy_priority_deprecated() -> None:
"""
Setting ARRAY_PRIORITY to a list containing 'cupy' should emit a
DeprecationWarning. See issue #843.

Returns
-------
None
"""
try:
with pytest.warns(DeprecationWarning, match="cupy"):
cl.options.set_option('ARRAY_PRIORITY', ['dask', 'sparse', 'cupy', 'numpy'])
finally:
cl.options.reset_option('ARRAY_PRIORITY')


def test_set_option_non_cupy_no_warning(recwarn) -> None:
"""
Setting backends other than 'cupy' should not emit a DeprecationWarning.

Returns
-------
None
"""
try:
cl.options.set_option('ARRAY_BACKEND', 'sparse')
cl.options.set_option('ARRAY_PRIORITY', ['dask', 'sparse', 'numpy'])
assert not [w for w in recwarn if issubclass(w.category, DeprecationWarning)]
finally:
cl.options.reset_option('ARRAY_BACKEND')
cl.options.reset_option('ARRAY_PRIORITY')


def test_set_backend_cupy_deprecated(clrd) -> None:
"""
Triangle.set_backend('cupy') should emit exactly one DeprecationWarning,
pointing at the caller. See issue #843.

Returns
-------
None
"""
with pytest.warns(DeprecationWarning, match="cupy") as record:
clrd.set_backend('cupy', deep=True)
cupy_warnings = [
w for w in record
if issubclass(w.category, DeprecationWarning) and "cupy" in str(w.message)
]
# A single warning should fire at the user's call site, not once per
# internal recursive / deep child conversion.
assert len(cupy_warnings) == 1
assert cupy_warnings[0].filename == __file__
Loading