Skip to content

Commit

Permalink
Merge branch 'develop' into feature/add_event_based_reception_for_pca…
Browse files Browse the repository at this point in the history
…n_interface_on_linux

# Conflicts:
#	can/interfaces/pcan/pcan.py
  • Loading branch information
zariiii9003 committed Jan 22, 2023
2 parents ef09567 + 35de98e commit b0796b8
Show file tree
Hide file tree
Showing 70 changed files with 1,071 additions and 676 deletions.
21 changes: 17 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,18 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install tox
- name: Setup SocketCAN
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
sudo apt-get -y install linux-modules-extra-$(uname -r)
sudo ./test/open_vcan.sh
- name: Test with pytest via tox
run: |
tox -e gh
env:
# SocketCAN tests currently fail with PyPy because it does not support raw CAN sockets
# See: https://foss.heptapod.net/pypy/pypy/-/issues/3809
TEST_SOCKETCAN: "${{ matrix.os == 'ubuntu-latest' && ! startsWith(matrix.python-version, 'pypy' ) }}"
- name: Coveralls Parallel
uses: coverallsapp/github-action@master
with:
Expand Down Expand Up @@ -91,10 +99,12 @@ jobs:
run: |
pylint --rcfile=.pylintrc \
can/**.py \
can/io \
setup.py \
doc.conf \
doc/conf.py \
scripts/**.py \
examples/**.py
examples/**.py \
can/interfaces/socketcan
format:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -127,7 +137,10 @@ jobs:
pip install -r doc/doc-requirements.txt
- name: Build documentation
run: |
python -m sphinx -an doc build
python -m sphinx -Wan --keep-going doc build
- name: Run doctest
run: |
python -m sphinx -b doctest -W --keep-going doc build
- uses: actions/upload-artifact@v3
with:
name: sphinx-out
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ htmlcov/
.cache
nosetests.xml
coverage.xml
coverage.lcov
*,cover
.hypothesis/
test.*
Expand Down
3 changes: 2 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ disable=invalid-name,
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member
enable=c-extension-no-member,
useless-suppression,


[REPORTS]
Expand Down
14 changes: 9 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
python-can
==========

|release| |python_implementation| |downloads| |downloads_monthly| |formatter|
|pypi| |conda| |python_implementation| |downloads| |downloads_monthly|

|docs| |github-actions| |build_travis| |coverage| |mergify|
|docs| |github-actions| |build_travis| |coverage| |mergify| |formatter|

.. |release| image:: https://img.shields.io/pypi/v/python-can.svg
.. |pypi| image:: https://img.shields.io/pypi/v/python-can.svg
:target: https://pypi.python.org/pypi/python-can/
:alt: Latest Version on PyPi

.. |conda| image:: https://img.shields.io/conda/v/conda-forge/python-can
:target: https://github.com/conda-forge/python-can-feedstock
:alt: Latest Version on conda-forge

.. |python_implementation| image:: https://img.shields.io/pypi/implementation/python-can
:target: https://pypi.python.org/pypi/python-can/
:alt: Supported Python implementations
Expand All @@ -29,8 +33,8 @@ python-can
:target: https://python-can.readthedocs.io/en/stable/
:alt: Documentation

.. |github-actions| image:: https://github.com/hardbyte/python-can/actions/workflows/build.yml/badge.svg?branch=develop
:target: https://github.com/hardbyte/python-can/actions/workflows/build.yml
.. |github-actions| image:: https://github.com/hardbyte/python-can/actions/workflows/ci.yml/badge.svg
:target: https://github.com/hardbyte/python-can/actions/workflows/ci.yml
:alt: Github Actions workflow status

.. |build_travis| image:: https://img.shields.io/travis/hardbyte/python-can/develop.svg?label=Travis%20CI
Expand Down
14 changes: 7 additions & 7 deletions can/bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,10 @@ def stop_all_periodic_tasks(self, remove_tasks: bool = True) -> None:
def __iter__(self) -> Iterator[Message]:
"""Allow iteration on messages as they are received.
>>> for msg in bus:
... print(msg)
.. code-block:: python
for msg in bus:
print(msg)
:yields:
Expand Down Expand Up @@ -352,9 +354,9 @@ def set_filters(
:param filters:
A iterable of dictionaries each containing a "can_id",
a "can_mask", and an optional "extended" key.
a "can_mask", and an optional "extended" key::
>>> [{"can_id": 0x11, "can_mask": 0x21, "extended": False}]
[{"can_id": 0x11, "can_mask": 0x21, "extended": False}]
A filter matches, when
``<received_can_id> & can_mask == can_id & can_mask``.
Expand Down Expand Up @@ -464,7 +466,5 @@ class _SelfRemovingCyclicTask(CyclicSendTaskABC, ABC):
Only needed for typing :meth:`Bus._periodic_tasks`. Do not instantiate.
"""

def stop( # pylint: disable=arguments-differ
self, remove_task: bool = True
) -> None:
def stop(self, remove_task: bool = True) -> None:
raise NotImplementedError()
24 changes: 15 additions & 9 deletions can/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,21 @@ class CanError(Exception):
If specified, the error code is automatically appended to the message:
>>> # With an error code (it also works with a specific error):
>>> error = CanOperationError(message="Failed to do the thing", error_code=42)
>>> str(error)
'Failed to do the thing [Error Code 42]'
>>>
>>> # Missing the error code:
>>> plain_error = CanError(message="Something went wrong ...")
>>> str(plain_error)
'Something went wrong ...'
.. testsetup:: canerror
from can import CanError, CanOperationError
.. doctest:: canerror
>>> # With an error code (it also works with a specific error):
>>> error = CanOperationError(message="Failed to do the thing", error_code=42)
>>> str(error)
'Failed to do the thing [Error Code 42]'
>>>
>>> # Missing the error code:
>>> plain_error = CanError(message="Something went wrong ...")
>>> str(plain_error)
'Something went wrong ...'
:param error_code:
An optional error code to narrow down the cause of the fault
Expand Down
49 changes: 30 additions & 19 deletions can/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import logging
from typing import Any, cast, Iterable, Type, Optional, Union, List

from . import util
from .bus import BusABC
from .util import load_config
from .interfaces import BACKENDS
from .exceptions import CanInterfaceNotImplementedError
from .typechecking import AutoDetectedConfig, Channel
Expand Down Expand Up @@ -61,6 +61,13 @@ class Bus(BusABC): # pylint: disable=abstract-method
Instantiates a CAN Bus of the given ``interface``, falls back to reading a
configuration file from default locations.
.. note::
Please note that while the arguments provided to this class take precedence
over any existing values from configuration, it is possible that other parameters
from the configuration may be added to the bus instantiation.
This could potentially have unintended consequences. To prevent this,
you may use the *ignore_config* parameter to ignore any existing configurations.
:param channel:
Channel identification. Expected type is backend dependent.
Set to ``None`` to let it be resolved automatically from the default
Expand All @@ -71,8 +78,13 @@ class Bus(BusABC): # pylint: disable=abstract-method
Set to ``None`` to let it be resolved automatically from the default
:ref:`configuration`.
:param args:
``interface`` specific positional arguments.
:param config_context:
Extra 'context', that is passed to config sources.
This can be used to select a section other than 'default' in the configuration file.
:param ignore_config:
If ``True``, only the given arguments will be used for the bus instantiation. Existing
configuration sources will be ignored.
:param kwargs:
``interface`` specific keyword arguments.
Expand All @@ -88,24 +100,28 @@ class Bus(BusABC): # pylint: disable=abstract-method
"""

@staticmethod
def __new__( # type: ignore # pylint: disable=keyword-arg-before-vararg
@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,
channel: Optional[Channel] = None,
interface: Optional[str] = None,
*args: Any,
config_context: Optional[str] = None,
ignore_config: bool = False,
**kwargs: Any,
) -> BusABC:
# figure out the rest of the configuration; this might raise an error
if interface is not None:
kwargs["interface"] = interface
if channel is not None:
kwargs["channel"] = channel
if "context" in kwargs:
context = kwargs["context"]
del kwargs["context"]
else:
context = None
kwargs = load_config(config=kwargs, context=context)

if not ignore_config:
kwargs = util.load_config(config=kwargs, context=config_context)

# resolve the bus class to use for that interface
cls = _get_class_for_interface(kwargs["interface"])
Expand All @@ -114,17 +130,12 @@ def __new__( # type: ignore # pylint: disable=keyword-arg-before-vararg
del kwargs["interface"]

# make sure the bus can handle this config format
if "channel" not in kwargs:
raise ValueError("'channel' argument missing")
else:
channel = kwargs["channel"]
del kwargs["channel"]

channel = kwargs.pop("channel", channel)
if channel is None:
# Use the default channel for the backend
bus = cls(*args, **kwargs)
bus = cls(**kwargs)
else:
bus = cls(channel, *args, **kwargs)
bus = cls(channel, **kwargs)

return cast(BusABC, bus)

Expand Down
29 changes: 21 additions & 8 deletions can/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"""

import sys
from typing import Dict, Tuple
from typing import cast, Dict, Tuple

# interface_name => (module, classname)
BACKENDS: Dict[str, Tuple[str, ...]] = {
BACKENDS: Dict[str, Tuple[str, str]] = {
"kvaser": ("can.interfaces.kvaser", "KvaserBus"),
"socketcan": ("can.interfaces.socketcan", "SocketcanBus"),
"serial": ("can.interfaces.serial.serial_can", "SerialBus"),
Expand Down Expand Up @@ -35,18 +35,31 @@
if sys.version_info >= (3, 8):
from importlib.metadata import entry_points

entries = entry_points().get("can.interface", ())
BACKENDS.update(
{interface.name: tuple(interface.value.split(":")) for interface in entries}
)
# See https://docs.python.org/3/library/importlib.metadata.html#entry-points, "Compatibility Note".
if sys.version_info >= (3, 10):
BACKENDS.update(
{
interface.name: (interface.module, interface.attr)
for interface in entry_points(group="can.interface")
}
)
else:
# The entry_points().get(...) causes a deprecation warning on Python >= 3.10.
BACKENDS.update(
{
interface.name: cast(
Tuple[str, str], tuple(interface.value.split(":", maxsplit=1))
)
for interface in entry_points().get("can.interface", [])
}
)
else:
from pkg_resources import iter_entry_points

entries = iter_entry_points("can.interface")
BACKENDS.update(
{
interface.name: (interface.module_name, interface.attrs[0])
for interface in entries
for interface in iter_entry_points("can.interface")
}
)

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
15 changes: 12 additions & 3 deletions can/interfaces/kvaser/canlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,9 +662,18 @@ def get_stats(self) -> structures.BusStatistics:
Use like so:
>>> stats = bus.get_stats()
>>> print(stats)
std_data: 0, std_remote: 0, ext_data: 0, ext_remote: 0, err_frame: 0, bus_load: 0.0%, overruns: 0
.. testsetup:: kvaser
from unittest.mock import Mock
from can.interfaces.kvaser.structures import BusStatistics
bus = Mock()
bus.get_stats = Mock(side_effect=lambda: BusStatistics())
.. doctest:: kvaser
>>> stats = bus.get_stats()
>>> print(stats)
std_data: 0, std_remote: 0, ext_data: 0, ext_remote: 0, err_frame: 0, bus_load: 0.0%, overruns: 0
:returns: bus statistics.
"""
Expand Down
Loading

0 comments on commit b0796b8

Please sign in to comment.