Skip to content
Merged
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
7 changes: 5 additions & 2 deletions can/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,11 @@ class Bus(BusABC): # pylint: disable=abstract-method
"""

@staticmethod
@util.deprecated_args_alias( # Deprecated since python-can 4.2
bustype="interface", context="config_context"
@util.deprecated_args_alias(
deprecation_start="4.2.0",
deprecation_end="5.0.0",
bustype="interface",
context="config_context",
)
def __new__( # type: ignore
cls: Any,
Expand Down
2 changes: 2 additions & 0 deletions can/interfaces/ixxat/canlib_vcinpl.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ class IXXATBus(BusABC):
}

@deprecated_args_alias(
deprecation_start="4.0.0",
deprecation_end="5.0.0",
UniqueHardwareId="unique_hardware_id",
rxFifoSize="rx_fifo_size",
txFifoSize="tx_fifo_size",
Expand Down
2 changes: 2 additions & 0 deletions can/interfaces/ixxat/canlib_vcinpl2.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ class IXXATBus(BusABC):
"""

@deprecated_args_alias(
deprecation_start="4.0.0",
deprecation_end="5.0.0",
UniqueHardwareId="unique_hardware_id",
rxFifoSize="rx_fifo_size",
txFifoSize="tx_fifo_size",
Expand Down
6 changes: 5 additions & 1 deletion can/interfaces/vector/canlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ class VectorBus(BusABC):
tseg2Dbr="tseg2_dbr",
)

@deprecated_args_alias(**deprecated_args)
@deprecated_args_alias(
deprecation_start="4.0.0",
deprecation_end="5.0.0",
**deprecated_args,
)
def __init__(
self,
channel: Union[int, Sequence[int], str],
Expand Down
53 changes: 44 additions & 9 deletions can/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,26 +295,47 @@ def channel2int(channel: Optional[typechecking.Channel]) -> Optional[int]:
return None


def deprecated_args_alias(**aliases):
def deprecated_args_alias( # type: ignore
deprecation_start: str, deprecation_end: Optional[str] = None, **aliases
):
"""Allows to rename/deprecate a function kwarg(s) and optionally
have the deprecated kwarg(s) set as alias(es)

Example::

@deprecated_args_alias(oldArg="new_arg", anotherOldArg="another_new_arg")
@deprecated_args_alias("1.2.0", oldArg="new_arg", anotherOldArg="another_new_arg")
def library_function(new_arg, another_new_arg):
pass

@deprecated_args_alias(oldArg="new_arg", obsoleteOldArg=None)
@deprecated_args_alias(
deprecation_start="1.2.0",
deprecation_end="3.0.0",
oldArg="new_arg",
obsoleteOldArg=None,
)
def library_function(new_arg):
pass

:param deprecation_start:
The *python-can* version, that introduced the :class:`DeprecationWarning`.
:param deprecation_end:
The *python-can* version, that marks the end of the deprecation period.
:param aliases:
keyword arguments, that map the deprecated argument names
to the new argument names or ``None``.

"""

def deco(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
_rename_kwargs(f.__name__, kwargs, aliases)
_rename_kwargs(
func_name=f.__name__,
start=deprecation_start,
end=deprecation_end,
kwargs=kwargs,
aliases=aliases,
)
return f(*args, **kwargs)

return wrapper
Expand All @@ -323,21 +344,35 @@ def wrapper(*args, **kwargs):


def _rename_kwargs(
func_name: str, kwargs: Dict[str, str], aliases: Dict[str, str]
func_name: str,
start: str,
end: Optional[str],
kwargs: Dict[str, str],
aliases: Dict[str, str],
) -> None:
"""Helper function for `deprecated_args_alias`"""
for alias, new in aliases.items():
if alias in kwargs:
deprecation_notice = (
f"The '{alias}' argument is deprecated since python-can v{start}"
)
if end:
deprecation_notice += (
f", and scheduled for removal in python-can v{end}"
)
deprecation_notice += "."

value = kwargs.pop(alias)
if new is not None:
warnings.warn(f"{alias} is deprecated; use {new}", DeprecationWarning)
deprecation_notice += f" Use '{new}' instead."

if new in kwargs:
raise TypeError(
f"{func_name} received both {alias} (deprecated) and {new}"
f"{func_name} received both '{alias}' (deprecated) and '{new}'."
)
kwargs[new] = value
else:
warnings.warn(f"{alias} is deprecated", DeprecationWarning)

warnings.warn(deprecation_notice, DeprecationWarning)


def time_perfcounter_correlation() -> Tuple[float, float]:
Expand Down
95 changes: 86 additions & 9 deletions test/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,26 @@
import unittest
import warnings

from can.util import _create_bus_config, _rename_kwargs, channel2int
import pytest

from can.util import (
_create_bus_config,
_rename_kwargs,
channel2int,
deprecated_args_alias,
)


class RenameKwargsTest(unittest.TestCase):
expected_kwargs = dict(a=1, b=2, c=3, d=4)

def _test(self, kwargs, aliases):
def _test(self, start: str, end: str, kwargs, aliases):

# Test that we do get the DeprecationWarning when called with deprecated kwargs
with self.assertWarnsRegex(DeprecationWarning, "is deprecated"):
_rename_kwargs("unit_test", kwargs, aliases)
with self.assertWarnsRegex(
DeprecationWarning, "is deprecated.*?" + start + ".*?" + end
):
_rename_kwargs("unit_test", start, end, kwargs, aliases)

# Test that the aliases contains the deprecated values and
# the obsolete kwargs have been removed
Expand All @@ -25,30 +34,98 @@ def _test(self, kwargs, aliases):
# Cause all warnings to always be triggered.
warnings.simplefilter("error", DeprecationWarning)
try:
_rename_kwargs("unit_test", kwargs, aliases)
_rename_kwargs("unit_test", start, end, kwargs, aliases)
finally:
warnings.resetwarnings()

def test_rename(self):
kwargs = dict(old_a=1, old_b=2, c=3, d=4)
aliases = {"old_a": "a", "old_b": "b"}
self._test(kwargs, aliases)
self._test("1.0", "2.0", kwargs, aliases)

def test_obsolete(self):
kwargs = dict(a=1, b=2, c=3, d=4, z=10)
aliases = {"z": None}
self._test(kwargs, aliases)
self._test("1.0", "2.0", kwargs, aliases)

def test_rename_and_obsolete(self):
kwargs = dict(old_a=1, old_b=2, c=3, d=4, z=10)
aliases = {"old_a": "a", "old_b": "b", "z": None}
self._test(kwargs, aliases)
self._test("1.0", "2.0", kwargs, aliases)

def test_with_new_and_alias_present(self):
kwargs = dict(old_a=1, a=1, b=2, c=3, d=4, z=10)
aliases = {"old_a": "a", "old_b": "b", "z": None}
with self.assertRaises(TypeError):
self._test(kwargs, aliases)
self._test("1.0", "2.0", kwargs, aliases)


class DeprecatedArgsAliasTest(unittest.TestCase):
def test_decorator(self):
@deprecated_args_alias("1.0.0", old_a="a")
def _test_func1(a):
pass

with pytest.warns(DeprecationWarning) as record:
_test_func1(old_a=1)
assert len(record) == 1
assert (
record[0].message.args[0]
== "The 'old_a' argument is deprecated since python-can v1.0.0. Use 'a' instead."
)

@deprecated_args_alias("1.6.0", "3.4.0", old_a="a", old_b=None)
def _test_func2(a):
pass

with pytest.warns(DeprecationWarning) as record:
_test_func2(old_a=1, old_b=2)
assert len(record) == 2
assert record[0].message.args[0] == (
"The 'old_a' argument is deprecated since python-can v1.6.0, and scheduled for "
"removal in python-can v3.4.0. Use 'a' instead."
)
assert record[1].message.args[0] == (
"The 'old_b' argument is deprecated since python-can v1.6.0, and scheduled for "
"removal in python-can v3.4.0."
)

@deprecated_args_alias("1.6.0", "3.4.0", old_a="a")
@deprecated_args_alias("2.0.0", "4.0.0", old_b=None)
def _test_func3(a):
pass

with pytest.warns(DeprecationWarning) as record:
_test_func3(old_a=1, old_b=2)
assert len(record) == 2
assert record[0].message.args[0] == (
"The 'old_a' argument is deprecated since python-can v1.6.0, and scheduled "
"for removal in python-can v3.4.0. Use 'a' instead."
)
assert record[1].message.args[0] == (
"The 'old_b' argument is deprecated since python-can v2.0.0, and scheduled "
"for removal in python-can v4.0.0."
)

with pytest.warns(DeprecationWarning) as record:
_test_func3(old_a=1)
assert len(record) == 1
assert record[0].message.args[0] == (
"The 'old_a' argument is deprecated since python-can v1.6.0, and scheduled "
"for removal in python-can v3.4.0. Use 'a' instead."
)

with pytest.warns(DeprecationWarning) as record:
_test_func3(a=1, old_b=2)
assert len(record) == 1
assert record[0].message.args[0] == (
"The 'old_b' argument is deprecated since python-can v2.0.0, and scheduled "
"for removal in python-can v4.0.0."
)

with warnings.catch_warnings():
warnings.simplefilter("error")
_test_func3(a=1)


class TestBusConfig(unittest.TestCase):
Expand Down