Skip to content

Adding plugin support to can.io Reader/Writer #783

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
Mar 2, 2020
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
30 changes: 21 additions & 9 deletions can/io/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pathlib
import typing

from pkg_resources import iter_entry_points
import can.typechecking

from ..listener import Listener
Expand Down Expand Up @@ -38,6 +39,16 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method
arguments are passed on to the returned instance.
"""

fetched_plugins = False
message_writers = {
".asc": ASCWriter,
".blf": BLFWriter,
".csv": CSVWriter,
".db": SqliteWriter,
".log": CanutilsLogWriter,
".txt": Printer,
}

@staticmethod
def __new__(
cls, filename: typing.Optional[can.typechecking.StringPathLike], *args, **kwargs
Expand All @@ -51,17 +62,18 @@ def __new__(
if filename is None:
return Printer(*args, **kwargs)

lookup = {
".asc": ASCWriter,
".blf": BLFWriter,
".csv": CSVWriter,
".db": SqliteWriter,
".log": CanutilsLogWriter,
".txt": Printer,
}
if not Logger.fetched_plugins:
Logger.message_writers.update(
{
writer.name: writer.load()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@felixdivo @sou1hacker
This is quite old, but is this load method actually documented anywhere?

for writer in iter_entry_points("can.io.message_writer")
}
)
Logger.fetched_plugins = True

suffix = pathlib.PurePath(filename).suffix
try:
return lookup[suffix](filename, *args, **kwargs)
return Logger.message_writers[suffix](filename, *args, **kwargs)
except KeyError:
raise ValueError(
f'No write support for this unknown log format "{suffix}"'
Expand Down
29 changes: 20 additions & 9 deletions can/io/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from time import time, sleep
import typing

from pkg_resources import iter_entry_points

if typing.TYPE_CHECKING:
import can

Expand Down Expand Up @@ -44,24 +46,33 @@ class LogReader(BaseIOHandler):
arguments are passed on to the returned instance.
"""

fetched_plugins = False
message_readers = {
".asc": ASCReader,
".blf": BLFReader,
".csv": CSVReader,
".db": SqliteReader,
".log": CanutilsLogReader,
}

@staticmethod
def __new__(cls, filename: "can.typechecking.StringPathLike", *args, **kwargs):
"""
:param filename: the filename/path of the file to read from
:raises ValueError: if the filename's suffix is of an unknown file type
"""
suffix = pathlib.PurePath(filename).suffix
if not LogReader.fetched_plugins:
LogReader.message_readers.update(
{
reader.name: reader.load()
for reader in iter_entry_points("can.io.message_reader")
}
)
LogReader.fetched_plugins = True

lookup = {
".asc": ASCReader,
".blf": BLFReader,
".csv": CSVReader,
".db": SqliteReader,
".log": CanutilsLogReader,
}
suffix = pathlib.PurePath(filename).suffix
try:
return lookup[suffix](filename, *args, **kwargs)
return LogReader.message_readers[suffix](filename, *args, **kwargs)
except KeyError:
raise ValueError(
f'No read support for this unknown log format "{suffix}"'
Expand Down
18 changes: 18 additions & 0 deletions doc/listeners.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ readers are also documented here.
completely unchanged message again, since some properties are not (yet)
supported by some file formats.

.. note ::

Additional file formats for both reading/writing log files can be added via
a plugin reader/writer. An external package can register a new reader
by using the ``can.io.message_reader`` entry point. Similarly, a writer can
be added using the ``can.io.message_writer`` entry point.

The format of the entry point is ``reader_name=module:classname`` where ``classname``
is a :class:`can.io.generic.BaseIOHandler` concrete implementation.

::

entry_points={
'can.io.message_reader': [
'.asc = my_package.io.asc:ASCReader'
]
},


BufferedReader
--------------
Expand Down