Skip to content

Use dependency groups for docs/lint/test #1945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 30, 2025
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 .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 .
Expand All @@ -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
Expand All @@ -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 .
Expand Down
8 changes: 6 additions & 2 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ build:
os: ubuntu-22.04
tools:
python: "3.12"
jobs:
post_install:
- pip install --group docs

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand All @@ -23,10 +26,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
4 changes: 2 additions & 2 deletions can/interfaces/udp_multicast/bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -104,7 +104,7 @@ def __init__(
fd: bool = True,
**kwargs,
) -> None:
check_msgpack_installed()
is_msgpack_installed()

if receive_own_messages:
raise can.CanInterfaceNotImplementedError(
Expand Down
22 changes: 17 additions & 5 deletions can/interfaces/udp_multicast/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion can/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion doc/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 0 additions & 5 deletions doc/doc-requirements.txt

This file was deleted.

9 changes: 9 additions & 0 deletions doc/interfaces/udp_multicast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------------------

Expand Down
35 changes: 27 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down Expand Up @@ -58,20 +57,14 @@ 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"]
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"]
Expand All @@ -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" }
Expand Down
12 changes: 9 additions & 3 deletions test/back2back_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand Down Expand Up @@ -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
Expand All @@ -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"

Expand Down
7 changes: 6 additions & 1 deletion test/listener_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
27 changes: 11 additions & 16 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down