File: C:/Users/fred/anaconda3/Lib/site-packages/skimage/_shared/tests/test_utils.py
import sys
import warnings
import numpy as np
import pytest
from skimage._shared import testing
from skimage._shared.utils import (
_supported_float_type,
_validate_interpolation_order,
change_default_value,
channel_as_last_axis,
check_nD,
deprecate_func,
deprecate_parameter,
DEPRECATED,
)
complex_dtypes = [np.complex64, np.complex128]
if hasattr(np, 'complex256'):
complex_dtypes += [np.complex256]
have_numpydoc = False
try:
import numpydoc # noqa: F401
have_numpydoc = True
except ImportError:
pass
def test_change_default_value():
@change_default_value('arg1', new_value=-1, changed_version='0.12')
def foo(arg0, arg1=0, arg2=1):
"""Expected docstring"""
return arg0, arg1, arg2
@change_default_value(
'arg1',
new_value=-1,
changed_version='0.12',
warning_msg="Custom warning message",
)
def bar(arg0, arg1=0, arg2=1):
"""Expected docstring"""
return arg0, arg1, arg2
# Assert warning messages
with pytest.warns(FutureWarning) as record:
assert foo(0) == (0, 0, 1)
assert bar(0) == (0, 0, 1)
expected_msg = (
"The new recommended value for arg1 is -1. Until "
"version 0.12, the default arg1 value is 0. From "
"version 0.12, the arg1 default value will be -1. "
"To avoid this warning, please explicitly set arg1 value."
)
assert str(record[0].message) == expected_msg
assert str(record[1].message) == "Custom warning message"
# Assert that nothing happens if arg1 is set
with warnings.catch_warnings(record=True) as recorded:
# No kwargs
assert foo(0, 2) == (0, 2, 1)
assert foo(0, arg1=0) == (0, 0, 1)
# Function name and doc is preserved
assert foo.__name__ == 'foo'
if sys.flags.optimize < 2:
# if PYTHONOPTIMIZE is set to 2, docstrings are stripped
assert foo.__doc__ == 'Expected docstring'
# Assert no warnings were raised
assert len(recorded) == 0
def test_check_nD():
z = np.random.random(200**2).reshape((200, 200))
x = z[10:30, 30:10]
with testing.raises(ValueError):
check_nD(x, 2)
@pytest.mark.parametrize(
'dtype', [bool, int, np.uint8, np.uint16, float, np.float32, np.float64]
)
@pytest.mark.parametrize('order', [None, -1, 0, 1, 2, 3, 4, 5, 6])
def test_validate_interpolation_order(dtype, order):
if order is None:
# Default order
assert _validate_interpolation_order(dtype, None) == 0 if dtype == bool else 1
elif order < 0 or order > 5:
# Order not in valid range
with testing.raises(ValueError):
_validate_interpolation_order(dtype, order)
elif dtype == bool and order != 0:
# Deprecated order for bool array
with pytest.raises(ValueError):
_validate_interpolation_order(bool, order)
else:
# Valid use case
assert _validate_interpolation_order(dtype, order) == order
@pytest.mark.parametrize(
'dtype',
[
bool,
np.float16,
np.float32,
np.float64,
np.uint8,
np.uint16,
np.uint32,
np.uint64,
np.int8,
np.int16,
np.int32,
np.int64,
],
)
def test_supported_float_dtype_real(dtype):
float_dtype = _supported_float_type(dtype)
if dtype in [np.float16, np.float32]:
assert float_dtype == np.float32
else:
assert float_dtype == np.float64
@pytest.mark.parametrize('dtype', complex_dtypes)
@pytest.mark.parametrize('allow_complex', [False, True])
def test_supported_float_dtype_complex(dtype, allow_complex):
if allow_complex:
float_dtype = _supported_float_type(dtype, allow_complex=allow_complex)
if dtype == np.complex64:
assert float_dtype == np.complex64
else:
assert float_dtype == np.complex128
else:
with testing.raises(ValueError):
_supported_float_type(dtype, allow_complex=allow_complex)
@pytest.mark.parametrize('dtype', ['f', 'float32', np.float32, np.dtype(np.float32)])
def test_supported_float_dtype_input_kinds(dtype):
assert _supported_float_type(dtype) == np.float32
@pytest.mark.parametrize(
'dtypes, expected',
[
((np.float16, np.float64), np.float64),
((np.float32, np.uint16, np.int8), np.float64),
((np.float32, np.float16), np.float32),
],
)
def test_supported_float_dtype_sequence(dtypes, expected):
float_dtype = _supported_float_type(dtypes)
assert float_dtype == expected
@channel_as_last_axis(multichannel_output=False)
def _decorated_channel_axis_size(x, *, channel_axis=None):
if channel_axis is None:
return None
assert channel_axis == -1
return x.shape[-1]
@testing.parametrize('channel_axis', [None, 0, 1, 2, -1, -2, -3])
def test_decorated_channel_axis_shape(channel_axis):
# Verify that channel_as_last_axis modifies the channel_axis as expected
# need unique size per axis here
x = np.zeros((2, 3, 4))
size = _decorated_channel_axis_size(x, channel_axis=channel_axis)
if channel_axis is None:
assert size is None
else:
assert size == x.shape[channel_axis]
@deprecate_func(
deprecated_version="x", removed_version="y", hint="You are on your own."
)
def _deprecated_func():
"""Dummy function used in `test_deprecate_func`.
The decorated function must be outside the test function, otherwise it
seems that the warning does not point at the calling location.
"""
def test_deprecate_func():
with pytest.warns(FutureWarning) as record:
_deprecated_func()
testing.assert_stacklevel(record)
assert len(record) == 1
assert record[0].message.args[0] == (
"`_deprecated_func` is deprecated since version x and will be removed in "
"version y. You are on your own."
)
@deprecate_parameter("old1", start_version="0.10", stop_version="0.12")
@deprecate_parameter("old0", start_version="0.10", stop_version="0.12")
def _func_deprecated_params(arg0, old0=DEPRECATED, old1=DEPRECATED, arg1=None):
"""Expected docstring.
Parameters
----------
arg0 : int
First unchanged parameter.
arg1 : int, optional
Second unchanged parameter.
"""
return arg0, old0, old1, arg1
@deprecate_parameter("old1", new_name="new0", start_version="0.10", stop_version="0.12")
@deprecate_parameter("old0", new_name="new1", start_version="0.10", stop_version="0.12")
def _func_replace_params(
arg0, old0=DEPRECATED, old1=DEPRECATED, new0=None, new1=None, arg1=None
):
"""Expected docstring.
Parameters
----------
arg0 : int
First unchanged parameter.
new0 : int, optional
First new parameter.
.. versionadded:: 0.10
new1 : int, optional
Second new parameter.
.. versionadded:: 0.10
arg1 : int, optional
Second unchanged parameter.
"""
return arg0, old0, old1, new0, new1, arg1
class Test_deprecate_parameter:
@pytest.mark.skipif(not have_numpydoc, reason="requires numpydoc")
def test_docstring_removed_param(self):
# function name and doc are preserved
assert _func_deprecated_params.__name__ == "_func_deprecated_params"
if sys.flags.optimize < 2:
# if PYTHONOPTIMIZE is set to 2, docstrings are stripped
assert (
_func_deprecated_params.__doc__
== """Expected docstring.
Parameters
----------
arg0 : int
First unchanged parameter.
arg1 : int, optional
Second unchanged parameter.
Other Parameters
----------------
old0 : DEPRECATED
`old0` is deprecated.
.. deprecated:: 0.10
old1 : DEPRECATED
`old1` is deprecated.
.. deprecated:: 0.10
"""
)
@pytest.mark.skipif(not have_numpydoc, reason="requires numpydoc")
def test_docstring_replaced_param(self):
assert _func_replace_params.__name__ == "_func_replace_params"
if sys.flags.optimize < 2:
# if PYTHONOPTIMIZE is set to 2, docstrings are stripped
assert (
_func_replace_params.__doc__
== """Expected docstring.
Parameters
----------
arg0 : int
First unchanged parameter.
new0 : int, optional
First new parameter.
.. versionadded:: 0.10
new1 : int, optional
Second new parameter.
.. versionadded:: 0.10
arg1 : int, optional
Second unchanged parameter.
Other Parameters
----------------
old0 : DEPRECATED
Deprecated in favor of `new1`.
.. deprecated:: 0.10
old1 : DEPRECATED
Deprecated in favor of `new0`.
.. deprecated:: 0.10
"""
)
def test_warning_removed_param(self):
match = (
r".*`old[01]` is deprecated since version 0\.10 and will be removed "
r"in 0\.12.* see the documentation of .*_func_deprecated_params`."
)
with pytest.warns(FutureWarning, match=match):
assert _func_deprecated_params(1, 2) == (1, DEPRECATED, DEPRECATED, None)
with pytest.warns(FutureWarning, match=match):
assert _func_deprecated_params(1, 2, 3) == (1, DEPRECATED, DEPRECATED, None)
with pytest.warns(FutureWarning, match=match):
assert _func_deprecated_params(1, old0=2) == (
1,
DEPRECATED,
DEPRECATED,
None,
)
with pytest.warns(FutureWarning, match=match):
assert _func_deprecated_params(1, old1=2) == (
1,
DEPRECATED,
DEPRECATED,
None,
)
with warnings.catch_warnings(record=True) as record:
assert _func_deprecated_params(1, arg1=3) == (1, DEPRECATED, DEPRECATED, 3)
assert len(record) == 0
def test_warning_replaced_param(self):
match = (
r".*`old[0,1]` is deprecated since version 0\.10 and will be removed "
r"in 0\.12.* see the documentation of .*_func_replace_params`."
)
with pytest.warns(FutureWarning, match=match):
assert _func_replace_params(1, 2) == (
1,
DEPRECATED,
DEPRECATED,
None,
2,
None,
)
with pytest.warns(FutureWarning, match=match) as records:
assert _func_replace_params(1, 2, 3) == (
1,
DEPRECATED,
DEPRECATED,
3,
2,
None,
)
assert len(records) == 2
assert "`old1` is deprecated" in records[0].message.args[0]
assert "`old0` is deprecated" in records[1].message.args[0]
with pytest.warns(FutureWarning, match=match):
assert _func_replace_params(1, old0=2) == (
1,
DEPRECATED,
DEPRECATED,
None,
2,
None,
)
with pytest.warns(FutureWarning, match=match):
assert _func_replace_params(1, old1=3) == (
1,
DEPRECATED,
DEPRECATED,
3,
None,
None,
)
# Otherwise, no warnings are emitted!
with warnings.catch_warnings(record=True) as record:
assert _func_replace_params(1, new0=2, new1=3) == (
1,
DEPRECATED,
DEPRECATED,
2,
3,
None,
)
assert len(record) == 0
def test_missing_DEPRECATED(self):
decorate = deprecate_parameter(
"old", start_version="0.10", stop_version="0.12", stacklevel=2
)
def foo(arg0, old=None):
return arg0, old
with pytest.raises(RuntimeError, match="Expected .* <DEPRECATED>"):
decorate(foo)
def bar(arg0, old=DEPRECATED):
return arg0
assert decorate(bar)(1) == 1
def test_new_keyword_only(self):
@deprecate_parameter(
"old",
new_name="new",
start_version="0.19",
stop_version="0.21",
)
def foo(arg0, old=DEPRECATED, *, new=1, arg3=None):
"""Expected docstring"""
return arg0, new, arg3
# Assert that nothing happens when the function is called with the
# new API
with warnings.catch_warnings(record=True) as recorded:
# No kwargs
assert foo(0) == (0, 1, None)
# Kwargs without deprecated argument
assert foo(0, new=1, arg3=2) == (0, 1, 2)
assert foo(0, new=2) == (0, 2, None)
assert foo(0, arg3=2) == (0, 1, 2)
assert len(recorded) == 0
def test_conflicting_old_and_new(self):
match = r".*`old[0,1]` is deprecated"
with pytest.warns(FutureWarning, match=match):
with pytest.raises(ValueError, match=".* avoid conflicting values"):
_func_replace_params(1, old0=2, new1=2)
with pytest.warns(FutureWarning, match=match):
with pytest.raises(ValueError, match=".* avoid conflicting values"):
_func_replace_params(1, old1=2, new0=2)
with pytest.warns(FutureWarning, match=match):
with pytest.raises(ValueError, match=".* avoid conflicting values"):
_func_replace_params(1, old0=1, old1=1, new0=1, new1=1)
def test_wrong_call_signature(self):
"""Check that normal errors for faulty calls are unchanged."""
with pytest.raises(
TypeError, match=r".* required positional argument\: 'arg0'"
):
_func_replace_params()
with pytest.warns(FutureWarning, match=r".*`old[0,1]` is deprecated"):
with pytest.raises(
TypeError, match=".* multiple values for argument 'old0'"
):
_func_deprecated_params(1, 2, old0=2)
def test_wrong_param_name(self):
with pytest.raises(ValueError, match="'old' is not in list"):
@deprecate_parameter("old", start_version="0.10", stop_version="0.12")
def foo(arg0):
pass
with pytest.raises(ValueError, match="'new' is not in list"):
@deprecate_parameter(
"old", new_name="new", start_version="0.10", stop_version="0.12"
)
def bar(arg0, old, arg1):
pass
def test_warning_location(self):
with pytest.warns(FutureWarning) as records:
_func_deprecated_params(1, old0=2, old1=2)
testing.assert_stacklevel(records)
assert len(records) == 2
def test_stacklevel(self):
@deprecate_parameter(
"old",
start_version="0.19",
stop_version="0.21",
)
def foo(arg0, old=DEPRECATED):
pass
with pytest.raises(RuntimeError, match="Set stacklevel manually"):
foo(0, 1)
@deprecate_parameter(
"old",
start_version="0.19",
stop_version="0.21",
stacklevel=2,
)
def bar(arg0, old=DEPRECATED):
pass
with pytest.warns(FutureWarning, match="`old` is deprecated") as records:
bar(0, 1)
testing.assert_stacklevel(records)