From 793463ce2d30e9aae3f010b700a31a25e76bcf7f Mon Sep 17 00:00:00 2001 From: zariiii9003 <52598363+zariiii9003@users.noreply.github.com> Date: Fri, 28 Oct 2022 20:18:42 +0200 Subject: [PATCH] update docs --- can/interface.py | 51 ++++++++++++++++------------ can/interfaces/cantact.py | 2 +- doc/api.rst | 2 +- doc/bus.rst | 21 +++--------- doc/conf.py | 1 + doc/configuration.rst | 45 +++++++++++++++++++------ doc/development.rst | 12 ++++--- doc/doc-requirements.txt | 1 + doc/interfaces.rst | 65 ++++++++++++++++++++++++++++++------ doc/interfaces/cantact.rst | 8 +++++ doc/interfaces/neousys.rst | 13 ++++++++ doc/internal-api.rst | 4 +-- examples/receive_all.py | 10 +++--- examples/send_one.py | 10 +++--- examples/serial_com.py | 4 +-- examples/vcan_filtered.py | 2 +- examples/virtual_can_demo.py | 4 +-- 17 files changed, 170 insertions(+), 85 deletions(-) create mode 100644 doc/interfaces/cantact.rst create mode 100644 doc/interfaces/neousys.rst diff --git a/can/interface.py b/can/interface.py index f1a0087e3..527f84d20 100644 --- a/can/interface.py +++ b/can/interface.py @@ -60,37 +60,44 @@ 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. - """ - @staticmethod - def __new__( # type: ignore # pylint: disable=keyword-arg-before-vararg - cls: Any, channel: Optional[Channel] = None, *args: Any, **kwargs: Any - ) -> BusABC: - """ - Takes the same arguments as :class:`can.BusABC.__init__`. - Some might have a special meaning, see below. + :param channel: + Channel identification. Expected type is backend dependent. + Set to ``None`` to let it be resolved automatically from the default + :ref:`configuration`. - :param channel: - Set to ``None`` to let it be resolved automatically from the default - configuration. That might fail, see below. + :param interface: + See :ref:`interface names` for a list of supported interfaces. + Set to ``None`` to let it be resolved automatically from the default + :ref:`configuration`. - Expected type is backend dependent. + :param args: + ``interface`` specific positional arguments. - :param dict kwargs: - Should contain an ``interface`` key with a valid interface name. If not, - it is completed using :meth:`can.util.load_config`. + :param kwargs: + ``interface`` specific keyword arguments. - :raises: can.CanInterfaceNotImplementedError - if the ``interface`` isn't recognized or cannot be loaded + :raises ~can.exceptions.CanInterfaceNotImplementedError: + if the ``interface`` isn't recognized or cannot be loaded - :raises: can.CanInitializationError - if the bus cannot be instantiated + :raises ~can.exceptions.CanInitializationError: + if the bus cannot be instantiated - :raises: ValueError - if the ``channel`` could not be determined - """ + :raises ValueError: + if the ``channel`` could not be determined + """ + @staticmethod + def __new__( # type: ignore # pylint: disable=keyword-arg-before-vararg + cls: Any, + channel: Optional[Channel] = None, + interface: Optional[str] = None, + *args: Any, + **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: diff --git a/can/interfaces/cantact.py b/can/interfaces/cantact.py index 056a64a6b..9ad7fbef8 100644 --- a/can/interfaces/cantact.py +++ b/can/interfaces/cantact.py @@ -59,7 +59,7 @@ def __init__( Bitrate in bits/s :param bool monitor: If true, operate in listen-only monitoring mode - :param BitTiming bit_timing + :param BitTiming bit_timing: Optional BitTiming to use for custom bit timing setting. Overrides bitrate if not None. """ diff --git a/doc/api.rst b/doc/api.rst index 011553b1b..23342f992 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -1,7 +1,7 @@ Library API =========== -The main objects are the :class:`~can.BusABC` and the :class:`~can.Message`. +The main objects are the :class:`~can.Bus` and the :class:`~can.Message`. A form of CAN interface is also required. .. hint:: diff --git a/doc/bus.rst b/doc/bus.rst index 9f7077cc1..4db49ee29 100644 --- a/doc/bus.rst +++ b/doc/bus.rst @@ -15,24 +15,11 @@ and implements the :class:`~can.BusABC` API. A thread safe bus wrapper is also available, see `Thread safe bus`_. -Autoconfig Bus -'''''''''''''' - .. autoclass:: can.Bus + :class-doc-from: class + :show-inheritance: :members: - - -API -''' - -.. autoclass:: can.BusABC - :members: - - .. automethod:: __iter__ - .. automethod:: _recv_internal - .. automethod:: _apply_filters - .. automethod:: _detect_available_configs - .. automethod:: _send_periodic_internal + :inherited-members: .. autoclass:: can.bus.BusState :members: @@ -81,7 +68,7 @@ Example defining two filters, one to pass 11-bit ID ``0x451``, the other to pass See :meth:`~can.BusABC.set_filters` for the implementation. Thread safe bus ---------------- +''''''''''''''' This thread safe version of the :class:`~can.BusABC` class can be used by multiple threads at once. Sending and receiving is locked separately to avoid unnecessary delays. diff --git a/doc/conf.py b/doc/conf.py index 495416078..c1409b8c2 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -48,6 +48,7 @@ "sphinx.ext.viewcode", "sphinx.ext.graphviz", "sphinxcontrib.programoutput", + "sphinx_inline_tabs", "sphinx_rtd_theme", ] diff --git a/doc/configuration.rst b/doc/configuration.rst index 9bda3030f..d92d6164f 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -1,3 +1,5 @@ +.. _configuration: + Configuration ============= @@ -100,6 +102,7 @@ For example: ``CAN_INTERFACE=socketcan CAN_CONFIG={"receive_own_messages": true, "fd": true}`` +.. _interface names: Interface Names --------------- @@ -109,31 +112,51 @@ Lookup table of interface names: +---------------------+-------------------------------------+ | Name | Documentation | +=====================+=====================================+ -| ``"socketcan"`` | :doc:`interfaces/socketcan` | +| ``"canalystii"`` | :doc:`interfaces/canalystii` | +---------------------+-------------------------------------+ -| ``"kvaser"`` | :doc:`interfaces/kvaser` | +| ``"cantact"`` | :doc:`interfaces/cantact` | +---------------------+-------------------------------------+ -| ``"serial"`` | :doc:`interfaces/serial` | +| ``"etas"`` | :doc:`interfaces/etas` | +---------------------+-------------------------------------+ -| ``"slcan"`` | :doc:`interfaces/slcan` | +| ``"gs_usb"`` | :doc:`interfaces/gs_usb` | ++---------------------+-------------------------------------+ +| ``"iscan"`` | :doc:`interfaces/iscan` | +---------------------+-------------------------------------+ | ``"ixxat"`` | :doc:`interfaces/ixxat` | +---------------------+-------------------------------------+ -| ``"pcan"`` | :doc:`interfaces/pcan` | +| ``"kvaser"`` | :doc:`interfaces/kvaser` | +---------------------+-------------------------------------+ -| ``"usb2can"`` | :doc:`interfaces/usb2can` | +| ``"neousys"`` | :doc:`interfaces/neousys` | ++---------------------+-------------------------------------+ +| ``"neovi"`` | :doc:`interfaces/neovi` | +---------------------+-------------------------------------+ | ``"nican"`` | :doc:`interfaces/nican` | +---------------------+-------------------------------------+ -| ``"iscan"`` | :doc:`interfaces/iscan` | +| ``"nixnet"`` | :doc:`interfaces/nixnet` | +---------------------+-------------------------------------+ -| ``"neovi"`` | :doc:`interfaces/neovi` | +| ``"pcan"`` | :doc:`interfaces/pcan` | +---------------------+-------------------------------------+ -| ``"vector"`` | :doc:`interfaces/vector` | +| ``"robotell"`` | :doc:`interfaces/robotell` | +---------------------+-------------------------------------+ -| ``"virtual"`` | :doc:`interfaces/virtual` | +| ``"seeedstudio"`` | :doc:`interfaces/seeedstudio` | +---------------------+-------------------------------------+ -| ``"canalystii"`` | :doc:`interfaces/canalystii` | +| ``"serial"`` | :doc:`interfaces/serial` | ++---------------------+-------------------------------------+ +| ``"slcan"`` | :doc:`interfaces/slcan` | ++---------------------+-------------------------------------+ +| ``"socketcan"`` | :doc:`interfaces/socketcan` | ++---------------------+-------------------------------------+ +| ``"socketcand"`` | :doc:`interfaces/socketcand` | +---------------------+-------------------------------------+ | ``"systec"`` | :doc:`interfaces/systec` | +---------------------+-------------------------------------+ +| ``"udp_multicast"`` | :doc:`interfaces/udp_multicast` | ++---------------------+-------------------------------------+ +| ``"usb2can"`` | :doc:`interfaces/usb2can` | ++---------------------+-------------------------------------+ +| ``"vector"`` | :doc:`interfaces/vector` | ++---------------------+-------------------------------------+ +| ``"virtual"`` | :doc:`interfaces/virtual` | ++---------------------+-------------------------------------+ + +Additional interface types can be added via the :ref:`plugin interface`. \ No newline at end of file diff --git a/doc/development.rst b/doc/development.rst index 03bf9a374..055401bdc 100644 --- a/doc/development.rst +++ b/doc/development.rst @@ -35,8 +35,8 @@ The following assumes that the commands are executed from the root of the reposi The project can be built with:: - pip install wheel - python setup.py sdist bdist_wheel + pipx run build + pipx run twine check dist/* The project can be installed in editable mode with:: @@ -44,8 +44,7 @@ The project can be installed in editable mode with:: The unit tests can be run with:: - pip install tox - tox -e py + pipx run tox -e py The documentation can be built with:: @@ -79,6 +78,11 @@ These steps are a guideline on how to add a new backend to python-can. To get started, have a look at ``back2back_test.py``: Simply add a test case like ``BasicTestSocketCan`` and some basic tests will be executed for the new interface. +.. attention:: + We strongly recommend using the :ref:`plugin interface` to extend python-can. + Publish a python package that contains your :class:`can.BusABC` subclass and use + it within the python-can API. We will mention your package inside this documentation + and add it as an optional dependency. Code Structure -------------- diff --git a/doc/doc-requirements.txt b/doc/doc-requirements.txt index 9732ea4ef..b1c2da632 100644 --- a/doc/doc-requirements.txt +++ b/doc/doc-requirements.txt @@ -1,3 +1,4 @@ sphinx>=5.2.3 sphinxcontrib-programoutput sphinx_rtd_theme +sphinx-inline-tabs diff --git a/doc/interfaces.rst b/doc/interfaces.rst index dbe4ad426..c25ea8bea 100644 --- a/doc/interfaces.rst +++ b/doc/interfaces.rst @@ -1,3 +1,5 @@ +.. _can interface modules: + CAN Interface Modules --------------------- @@ -12,11 +14,13 @@ The available interfaces are: :maxdepth: 1 interfaces/canalystii + interfaces/cantact interfaces/etas interfaces/gs_usb interfaces/iscan interfaces/ixxat interfaces/kvaser + interfaces/neousys interfaces/neovi interfaces/nican interfaces/nixnet @@ -33,19 +37,58 @@ The available interfaces are: interfaces/vector interfaces/virtual -Additional interfaces can be added via a plugin interface. An external package -can register a new interface by using the ``can.interface`` entry point in its setup.py. +The *Interface Names* are listed in :doc:`configuration`. -The format of the entry point is ``interface_name=module:classname`` where -``classname`` is a concrete :class:`can.BusABC` implementation. -:: +.. _plugin interface: - entry_points={ - 'can.interface': [ - "interface_name=module:classname", - ] - }, +Plugin Interface +^^^^^^^^^^^^^^^^ +External packages can register a new interfaces by using the ``can.interface`` entry point +in its project configuration. The format of the entry point depends on your project +configuration format (*pyproject.toml*, *setup.cfg* or *setup.py*). -The *Interface Names* are listed in :doc:`configuration`. +In the following example ``module`` defines the location of your bus class inside your +package e.g. ``my_package.subpackage.bus_module`` and ``classname`` is the name of +your :class:`can.BusABC` subclass. + +.. tab:: pyproject.toml (PEP 621) + + .. code-block:: toml + + # Note the quotes around can.interface in order to escape the dot . + [project.entry-points."can.interface"] + interface_name = "module:classname" + +.. tab:: setup.cfg + + .. code-block:: ini + + [options.entry_points] + can.interface = + interface_name = module:classname + +.. tab:: setup.py + + .. code-block:: python + + from setuptools import setup + + setup( + # ..., + entry_points = { + 'can.interface': [ + 'interface_name = module:classname' + ] + } + ) + +The ``interface_name`` can be used to +create an instance of the bus in the **python-can** API: + +.. code-block:: python + + import can + + bus = can.Bus(interface="interface_name", channel=0) diff --git a/doc/interfaces/cantact.rst b/doc/interfaces/cantact.rst new file mode 100644 index 000000000..dc9667218 --- /dev/null +++ b/doc/interfaces/cantact.rst @@ -0,0 +1,8 @@ +CANtact CAN Interface +===================== + +Interface for CANtact devices from Linklayer Labs + +.. autoclass:: can.interfaces.cantact.CantactBus + :show-inheritance: + :members: diff --git a/doc/interfaces/neousys.rst b/doc/interfaces/neousys.rst new file mode 100644 index 000000000..97a37868c --- /dev/null +++ b/doc/interfaces/neousys.rst @@ -0,0 +1,13 @@ +Neousys CAN Interface +===================== + +This kind of interface can be found for example on Neousys POC-551VTC +One needs to have correct drivers and DLL (Share object for Linux) from +`Neousys `_. + +Beware this is only tested on Linux kernel higher than v5.3. This should be drop in +with Windows but you have to replace with correct named DLL + +.. autoclass:: can.interfaces.neousys.NeousysBus + :show-inheritance: + :members: diff --git a/doc/internal-api.rst b/doc/internal-api.rst index 1367dca50..3ef599598 100644 --- a/doc/internal-api.rst +++ b/doc/internal-api.rst @@ -55,15 +55,15 @@ configuration into account. Bus Internals ~~~~~~~~~~~~~ -Several methods are not documented in the main :class:`can.BusABC` +Several methods are not documented in the main :class:`can.Bus` as they are primarily useful for library developers as opposed to library users. This is the entire ABC bus class with all internal methods: .. autoclass:: can.BusABC + :members: :private-members: :special-members: - :noindex: diff --git a/examples/receive_all.py b/examples/receive_all.py index 7ff532079..7b94d526f 100755 --- a/examples/receive_all.py +++ b/examples/receive_all.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Shows how the receive messages via polling. +Shows how to receive messages via polling. """ import can @@ -11,11 +11,9 @@ def receive_all(): """Receives all messages and prints them to the console until Ctrl+C is pressed.""" - with can.interface.Bus( - bustype="pcan", channel="PCAN_USBBUS1", bitrate=250000 - ) as bus: - # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) - # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) + with can.Bus(interface="pcan", channel="PCAN_USBBUS1", bitrate=250000) as bus: + # bus = can.Bus(interface='ixxat', channel=0, bitrate=250000) + # bus = can.Bus(interface='vector', app_name='CANalyzer', channel=0, bitrate=250000) # set to read-only, only supported on some interfaces bus.state = BusState.PASSIVE diff --git a/examples/send_one.py b/examples/send_one.py index 49a4f1ee1..7e3fb8a4c 100755 --- a/examples/send_one.py +++ b/examples/send_one.py @@ -12,13 +12,13 @@ def send_one(): # this uses the default configuration (for example from the config file) # see https://python-can.readthedocs.io/en/stable/configuration.html - with can.interface.Bus() as bus: + with can.Bus() as bus: # Using specific buses works similar: - # bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000) - # bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000) - # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) - # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) + # bus = can.Bus(interface='socketcan', channel='vcan0', bitrate=250000) + # bus = can.Bus(interface='pcan', channel='PCAN_USBBUS1', bitrate=250000) + # bus = can.Bus(interface='ixxat', channel=0, bitrate=250000) + # bus = can.Bus(interface='vector', app_name='CANalyzer', channel=0, bitrate=250000) # ... msg = can.Message( diff --git a/examples/serial_com.py b/examples/serial_com.py index c57207a77..76b95c3e7 100755 --- a/examples/serial_com.py +++ b/examples/serial_com.py @@ -48,8 +48,8 @@ def receive(bus, stop_event): def main(): """Controls the sender and receiver.""" - with can.interface.Bus(interface="serial", channel="/dev/ttyS10") as server: - with can.interface.Bus(interface="serial", channel="/dev/ttyS11") as client: + with can.Bus(interface="serial", channel="/dev/ttyS10") as server: + with can.Bus(interface="serial", channel="/dev/ttyS11") as client: tx_msg = can.Message( arbitration_id=0x01, diff --git a/examples/vcan_filtered.py b/examples/vcan_filtered.py index fa6c71547..a43fbe821 100755 --- a/examples/vcan_filtered.py +++ b/examples/vcan_filtered.py @@ -11,7 +11,7 @@ def main(): """Send some messages to itself and apply filtering.""" - with can.Bus(bustype="virtual", receive_own_messages=True) as bus: + with can.Bus(interface="virtual", receive_own_messages=True) as bus: can_filters = [{"can_id": 1, "can_mask": 0xF, "extended": True}] bus.set_filters(can_filters) diff --git a/examples/virtual_can_demo.py b/examples/virtual_can_demo.py index d0d6a4a6a..af50a87a7 100755 --- a/examples/virtual_can_demo.py +++ b/examples/virtual_can_demo.py @@ -14,9 +14,9 @@ def producer(thread_id: int, message_count: int = 16) -> None: """Spam the bus with messages including the data id. :param thread_id: the id of the thread/process - :param message_count: the number of messages that shall be send + :param message_count: the number of messages that shall be sent """ - with can.Bus(bustype="socketcan", channel="vcan0") as bus: # type: ignore + with can.Bus(interface="socketcan", channel="vcan0") as bus: # type: ignore for i in range(message_count): msg = can.Message( arbitration_id=0x0CF02200 + thread_id,