From 22f7dc2145ee2d1cf9a7c68dda5e9721184822ab Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Fri, 30 May 2025 14:38:23 +0200 Subject: [PATCH 1/5] use dependency groups for docs/lint/test --- .github/workflows/ci.yml | 7 ++++-- .readthedocs.yml | 7 ++++-- can/interfaces/udp_multicast/bus.py | 4 +-- can/interfaces/udp_multicast/utils.py | 22 +++++++++++++---- can/listener.py | 2 +- doc/development.rst | 2 +- doc/doc-requirements.txt | 5 ---- doc/interfaces/udp_multicast.rst | 9 +++++++ pyproject.toml | 35 +++++++++++++++++++++------ test/back2back_test.py | 12 ++++++--- test/listener_test.py | 7 +++++- tox.ini | 27 +++++++++------------ 12 files changed, 93 insertions(+), 46 deletions(-) delete mode 100644 doc/doc-requirements.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab14b6b6a..f04654f0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,7 +79,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e .[lint] + pip install --group lint -e . - name: mypy 3.9 run: | mypy --python-version 3.9 . @@ -92,6 +92,9 @@ jobs: - name: mypy 3.12 run: | mypy --python-version 3.12 . + - name: mypy 3.13 + run: | + mypy --python-version 3.13 . - name: ruff run: | ruff check can @@ -115,7 +118,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e .[lint] + pip install --group lint - name: Code Format Check with Black run: | black --check --verbose . diff --git a/.readthedocs.yml b/.readthedocs.yml index dad8c28db..ec8557e02 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -10,6 +10,8 @@ build: os: ubuntu-22.04 tools: python: "3.12" + commands: + - pip install -group docs # Build documentation in the docs/ directory with Sphinx sphinx: @@ -23,10 +25,11 @@ formats: # Optionally declare the Python requirements required to build your docs python: install: - - requirements: doc/doc-requirements.txt - method: pip path: . extra_requirements: - canalystii - - gs_usb + - gs-usb - mf4 + - remote + - serial diff --git a/can/interfaces/udp_multicast/bus.py b/can/interfaces/udp_multicast/bus.py index 31744c2b8..ec94e22b5 100644 --- a/can/interfaces/udp_multicast/bus.py +++ b/can/interfaces/udp_multicast/bus.py @@ -11,7 +11,7 @@ from can import BusABC, CanProtocol from can.typechecking import AutoDetectedConfig -from .utils import check_msgpack_installed, pack_message, unpack_message +from .utils import is_msgpack_installed, pack_message, unpack_message ioctl_supported = True @@ -104,7 +104,7 @@ def __init__( fd: bool = True, **kwargs, ) -> None: - check_msgpack_installed() + is_msgpack_installed() if receive_own_messages: raise can.CanInterfaceNotImplementedError( diff --git a/can/interfaces/udp_multicast/utils.py b/can/interfaces/udp_multicast/utils.py index 5c9454ed4..c6b2630a5 100644 --- a/can/interfaces/udp_multicast/utils.py +++ b/can/interfaces/udp_multicast/utils.py @@ -13,10 +13,22 @@ msgpack = None -def check_msgpack_installed() -> None: - """Raises a :class:`can.CanInterfaceNotImplementedError` if `msgpack` is not installed.""" +def is_msgpack_installed(raise_exception: bool = True) -> bool: + """Check whether the ``msgpack`` module is installed. + + :param raise_exception: + If True, raise a :class:`can.CanInterfaceNotImplementedError` when ``msgpack`` is not installed. + If False, return False instead. + :return: + True if ``msgpack`` is installed, False otherwise. + :raises can.CanInterfaceNotImplementedError: + If ``msgpack`` is not installed and ``raise_exception`` is True. + """ if msgpack is None: - raise CanInterfaceNotImplementedError("msgpack not installed") + if raise_exception: + raise CanInterfaceNotImplementedError("msgpack not installed") + return False + return True def pack_message(message: Message) -> bytes: @@ -25,7 +37,7 @@ def pack_message(message: Message) -> bytes: :param message: the message to be packed """ - check_msgpack_installed() + is_msgpack_installed() as_dict = { "timestamp": message.timestamp, "arbitration_id": message.arbitration_id, @@ -58,7 +70,7 @@ def unpack_message( :raise ValueError: if `check` is true and the message metadata is invalid in some way :raise Exception: if there was another problem while unpacking """ - check_msgpack_installed() + is_msgpack_installed() as_dict = msgpack.unpackb(data, raw=False) if replace is not None: as_dict.update(replace) diff --git a/can/listener.py b/can/listener.py index 1e95fa990..b450cf36d 100644 --- a/can/listener.py +++ b/can/listener.py @@ -136,6 +136,7 @@ class AsyncBufferedReader( """ def __init__(self, **kwargs: Any) -> None: + self._is_stopped: bool = False self.buffer: asyncio.Queue[Message] if "loop" in kwargs: @@ -150,7 +151,6 @@ def __init__(self, **kwargs: Any) -> None: return self.buffer = asyncio.Queue() - self._is_stopped: bool = False def on_message_received(self, msg: Message) -> None: """Append a message to the buffer. diff --git a/doc/development.rst b/doc/development.rst index 31a7ae077..074c1318d 100644 --- a/doc/development.rst +++ b/doc/development.rst @@ -52,7 +52,7 @@ The documentation can be built with:: The linters can be run with:: - pip install -e .[lint] + pip install --group lint -e . black --check can mypy can ruff check can diff --git a/doc/doc-requirements.txt b/doc/doc-requirements.txt deleted file mode 100644 index 9a01cf589..000000000 --- a/doc/doc-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -sphinx>=5.2.3 -sphinxcontrib-programoutput -sphinx-inline-tabs -sphinx-copybutton -furo diff --git a/doc/interfaces/udp_multicast.rst b/doc/interfaces/udp_multicast.rst index e41925cb3..b2a049d83 100644 --- a/doc/interfaces/udp_multicast.rst +++ b/doc/interfaces/udp_multicast.rst @@ -22,6 +22,15 @@ sufficiently reliable for this interface to function properly. Please refer to the `Bus class documentation`_ below for configuration options and useful resources for specifying multicast IP addresses. +Installation +------------------- + +The Multicast IP Interface depends on the **msgpack** python library, +which is automatically installed with the `multicast` extra keyword:: + + $ pip install python-can[multicast] + + Supported Platforms ------------------- diff --git a/pyproject.toml b/pyproject.toml index 2e8be3e2a..37abec266 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,6 @@ dependencies = [ "wrapt~=1.10", "packaging >= 23.1", "typing_extensions>=3.10.0.0", - "msgpack~=1.1.0", ] requires-python = ">=3.9" license = { text = "LGPL v3" } @@ -58,12 +57,6 @@ repository = "https://github.com/hardbyte/python-can" changelog = "https://github.com/hardbyte/python-can/blob/develop/CHANGELOG.md" [project.optional-dependencies] -lint = [ - "pylint==3.2.*", - "ruff==0.11.12", - "black==25.1.*", - "mypy==1.16.*", -] pywin32 = ["pywin32>=305; platform_system == 'Windows' and platform_python_implementation == 'CPython'"] seeedstudio = ["pyserial>=3.0"] serial = ["pyserial~=3.0"] @@ -71,7 +64,7 @@ neovi = ["filelock", "python-ics>=2.12"] canalystii = ["canalystii>=0.1.0"] cantact = ["cantact>=0.0.7"] cvector = ["python-can-cvector"] -gs_usb = ["gs_usb>=0.2.1"] +gs-usb = ["gs-usb>=0.2.1"] nixnet = ["nixnet>=0.3.2"] pcan = ["uptime~=3.0.1"] remote = ["python-can-remote"] @@ -82,6 +75,32 @@ viewer = [ "windows-curses; platform_system == 'Windows' and platform_python_implementation=='CPython'" ] mf4 = ["asammdf>=6.0.0"] +multicast = ["msgpack~=1.1.0"] + +[dependency-groups] +docs = [ + "sphinx>=5.2.3", + "sphinxcontrib-programoutput", + "sphinx-inline-tabs", + "sphinx-copybutton", + "furo", +] +lint = [ + "pylint==3.2.*", + "ruff==0.11.12", + "black==25.1.*", + "mypy==1.16.*", +] +test = [ + "pytest==8.3.*", + "pytest-timeout==2.1.*", + "coveralls==3.3.1", + "pytest-cov==4.0.0", + "coverage==6.5.0", + "hypothesis~=6.35.0", + "pyserial~=3.5", + "parameterized~=0.8", +] [tool.setuptools.dynamic] readme = { file = "README.rst" } diff --git a/test/back2back_test.py b/test/back2back_test.py index fc630fb65..a46597ef4 100644 --- a/test/back2back_test.py +++ b/test/back2back_test.py @@ -14,14 +14,12 @@ import can from can import CanInterfaceNotImplementedError from can.interfaces.udp_multicast import UdpMulticastBus +from can.interfaces.udp_multicast.utils import is_msgpack_installed from .config import ( IS_CI, IS_OSX, IS_PYPY, - IS_TRAVIS, - IS_UNIX, - IS_WINDOWS, TEST_CAN_FD, TEST_INTERFACE_SOCKETCAN, ) @@ -307,6 +305,10 @@ class BasicTestSocketCan(Back2BackTestCase): IS_CI and IS_OSX, "not supported for macOS CI", ) +@unittest.skipUnless( + is_msgpack_installed(raise_exception=False), + "msgpack not installed", +) class BasicTestUdpMulticastBusIPv4(Back2BackTestCase): INTERFACE_1 = "udp_multicast" CHANNEL_1 = UdpMulticastBus.DEFAULT_GROUP_IPv4 @@ -324,6 +326,10 @@ def test_unique_message_instances(self): IS_CI and IS_OSX, "not supported for macOS CI", ) +@unittest.skipUnless( + is_msgpack_installed(raise_exception=False), + "msgpack not installed", +) class BasicTestUdpMulticastBusIPv6(Back2BackTestCase): HOST_LOCAL_MCAST_GROUP_IPv6 = "ff11:7079:7468:6f6e:6465:6d6f:6d63:6173" diff --git a/test/listener_test.py b/test/listener_test.py index 54496176b..bbcbed56e 100644 --- a/test/listener_test.py +++ b/test/listener_test.py @@ -159,8 +159,13 @@ def testBufferedListenerReceives(self): def test_deprecated_loop_arg(recwarn): + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + warnings.simplefilter("always") - can.AsyncBufferedReader(loop=asyncio.get_event_loop()) + can.AsyncBufferedReader(loop=loop) assert len(recwarn) > 0 assert recwarn.pop(DeprecationWarning) recwarn.clear() diff --git a/tox.ini b/tox.ini index e112c22b4..c69f541f4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,21 +1,15 @@ [tox] +min_version = 4.22 [testenv] +dependency_groups = + test deps = - pytest==8.3.* - pytest-timeout==2.1.* - coveralls==3.3.1 - pytest-cov==4.0.0 - coverage==6.5.0 - hypothesis~=6.35.0 - pyserial~=3.5 - parameterized~=0.8 - asammdf>=6.0; platform_python_implementation=="CPython" and python_version<"3.13" + asammdf>=6.0; platform_python_implementation=="CPython" and python_version<"3.14" + msgpack~=1.1.0; python_version<"3.14" pywin32>=305; platform_system=="Windows" and platform_python_implementation=="CPython" and python_version<"3.14" - commands = pytest {posargs} - extras = canalystii @@ -30,13 +24,14 @@ passenv = [testenv:docs] description = Build and test the documentation basepython = py312 -deps = - -r doc/doc-requirements.txt - gs-usb - +dependency_groups = + docs extras = canalystii - + gs-usb + mf4 + remote + serial commands = python -m sphinx -b html -Wan --keep-going doc build python -m sphinx -b doctest -W --keep-going doc build From 699b8f45ff130980754d96961e1b0a189abf5985 Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Fri, 30 May 2025 14:51:46 +0200 Subject: [PATCH 2/5] fix --group arg --- .readthedocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index ec8557e02..0e9120349 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -11,7 +11,7 @@ build: tools: python: "3.12" commands: - - pip install -group docs + - pip install --group docs # Build documentation in the docs/ directory with Sphinx sphinx: From 7b582cd0a69914febe3d811c89baafaf6c9815bb Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Fri, 30 May 2025 14:53:00 +0200 Subject: [PATCH 3/5] upgrade pip --- .readthedocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 0e9120349..13e6afe4a 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -11,6 +11,7 @@ build: tools: python: "3.12" commands: + - python -m pip install --upgrade pip - pip install --group docs # Build documentation in the docs/ directory with Sphinx From 682a9fa85e9814e0e7d9a24d5cc904a023b960d2 Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Fri, 30 May 2025 14:58:22 +0200 Subject: [PATCH 4/5] try pre_install --- .readthedocs.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 13e6afe4a..a8d2a4840 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -10,9 +10,10 @@ build: os: ubuntu-22.04 tools: python: "3.12" - commands: - - python -m pip install --upgrade pip - - pip install --group docs + jobs: + pre_install: + - python -m pip install --upgrade pip + - pip install --group docs # Build documentation in the docs/ directory with Sphinx sphinx: From 0ca50a6657c1762628086288b286f0a29207926a Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Fri, 30 May 2025 15:01:37 +0200 Subject: [PATCH 5/5] try post_install --- .readthedocs.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index a8d2a4840..6fe4009e4 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -11,8 +11,7 @@ build: tools: python: "3.12" jobs: - pre_install: - - python -m pip install --upgrade pip + post_install: - pip install --group docs # Build documentation in the docs/ directory with Sphinx