From 64440922f032bcd2ba647041934f4228e8651f08 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sat, 22 Feb 2025 20:12:06 +0100
Subject: [PATCH 01/35] Add basic implementation for can bridge
---
can/bridge.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++
pyproject.toml | 1 +
2 files changed, 48 insertions(+)
create mode 100644 can/bridge.py
diff --git a/can/bridge.py b/can/bridge.py
new file mode 100644
index 000000000..0a5921e0b
--- /dev/null
+++ b/can/bridge.py
@@ -0,0 +1,47 @@
+"""
+Creates a bridge between two CAN busses.
+
+This will connect to two CAN busses. Messages received on one
+bus will be sent to the other bus and vice versa.
+"""
+
+import argparse
+import errno
+import sys
+from datetime import datetime
+
+from .logger import _create_base_argument_parser, _create_bus, _parse_additional_config
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description="Bridge two CAN busses.")
+
+ _create_base_argument_parser(parser)
+
+ parser.add_argument(
+ "--error-frames",
+ help="Also send error frames to the interface.",
+ action="store_true",
+ )
+
+ # print help message when no arguments were given
+ if len(sys.argv) < 2:
+ parser.print_help(sys.stderr)
+ raise SystemExit(errno.EINVAL)
+
+ results, unknown_args = parser.parse_known_args()
+ additional_config = _parse_additional_config([*results.extra_args, *unknown_args])
+
+ verbosity = results.verbosity
+
+ error_frames = results.error_frames
+
+ with _create_bus(results, **additional_config) as bus_a
+ with _create_bus(results, **additional_config) as bus_b
+ print(f"CAN Bridge (Started on {datetime.now()})")
+
+ print(f"CAN Bridge (Stopped on {datetime.now()})")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/pyproject.toml b/pyproject.toml
index a6a7f38c4..18217ac4b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -49,6 +49,7 @@ can_logconvert = "can.logconvert:main"
can_logger = "can.logger:main"
can_player = "can.player:main"
can_viewer = "can.viewer:main"
+can_bridge = "can.bridge:main"
[project.urls]
homepage = "https://github.com/hardbyte/python-can"
From 83083315269f062da10ede819557793108f1496d Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sat, 22 Feb 2025 21:16:54 +0100
Subject: [PATCH 02/35] Implement basic configuration parsing
---
can/bridge.py | 80 ++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 66 insertions(+), 14 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 0a5921e0b..3c6709876 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -8,36 +8,88 @@
import argparse
import errno
import sys
+import logging
from datetime import datetime
from .logger import _create_base_argument_parser, _create_bus, _parse_additional_config
-def main() -> None:
- parser = argparse.ArgumentParser(description="Bridge two CAN busses.")
+LOG = logging.getLogger(__name__)
+
+
+def get_config_list(it, separator, conf):
+ while True:
+ el = next(it)
+ if el == separator:
+ break
+ else:
+ conf.append(el)
+
+
+def split_configurations(arg_list, separator='--'):
+ general = []
+ conf_a = []
+ conf_b = []
+
+ it = iter(arg_list)
+ try:
+ get_config_list(it, separator, conf_a)
+ get_config_list(it, separator, conf_b)
+
+ # When we reached this point we found two separators so we have
+ # a general config. We will treate the first config as general
+ general = conf_a
+ conf_a = conf_b
+ get_config_list(it, separator, conf_b)
- _create_base_argument_parser(parser)
+ # When we reached this point we found three separators so this is
+ # an error.
+ raise Exception("To many configurations")
+ except StopIteration:
+ LOG.debug("All configurations were split")
- parser.add_argument(
- "--error-frames",
- help="Also send error frames to the interface.",
- action="store_true",
+ return general, conf_a, conf_b
+
+
+def main() -> None:
+ general_parser = argparse.ArgumentParser()
+ general_parser.add_argument(
+ "-v",
+ action="count",
+ dest="verbosity",
+ help="""How much information do you want to see at the command line?
+ You can add several of these e.g., -vv is DEBUG""",
+ default=2,
)
+ bus_parser = argparse.ArgumentParser(description="Bridge two CAN busses.")
+
+ _create_base_argument_parser(bus_parser)
+
+ parser = argparse.ArgumentParser(description="Bridge two CAN busses.")
+ parser.add_argument('configs', nargs=argparse.REMAINDER)
+
# print help message when no arguments were given
if len(sys.argv) < 2:
- parser.print_help(sys.stderr)
+ bus_parser.print_help(sys.stderr)
raise SystemExit(errno.EINVAL)
- results, unknown_args = parser.parse_known_args()
- additional_config = _parse_additional_config([*results.extra_args, *unknown_args])
+ args = sys.argv[1:]
+ general, conf_a, conf_b = split_configurations(args)
+
+ g_results = general_parser.parse_args(general)
+ verbosity = g_results.verbosity
- verbosity = results.verbosity
+ a_results, a_unknown_args = bus_parser.parse_known_args(conf_a)
+ a_additional_config = _parse_additional_config([*a_results.extra_args, *a_unknown_args])
+ a_results.__dict__['verbosity'] = verbosity
- error_frames = results.error_frames
+ b_results, b_unknown_args = bus_parser.parse_known_args(conf_b)
+ b_additional_config = _parse_additional_config([*b_results.extra_args, *b_unknown_args])
+ b_results.__dict__['verbosity'] = verbosity
- with _create_bus(results, **additional_config) as bus_a
- with _create_bus(results, **additional_config) as bus_b
+ with _create_bus(a_results, **a_additional_config) as bus_a:
+ with _create_bus(b_results, **b_additional_config) as bus_b:
print(f"CAN Bridge (Started on {datetime.now()})")
print(f"CAN Bridge (Stopped on {datetime.now()})")
From c2b7413b36fb8913f4c038ee0c338b935c897ada Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sat, 22 Feb 2025 21:25:11 +0100
Subject: [PATCH 03/35] Add implementation for bridge
---
can/bridge.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/can/bridge.py b/can/bridge.py
index 3c6709876..cb957f0db 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -8,11 +8,14 @@
import argparse
import errno
import sys
+import time
import logging
from datetime import datetime
from .logger import _create_base_argument_parser, _create_bus, _parse_additional_config
+import can
+
LOG = logging.getLogger(__name__)
@@ -90,7 +93,13 @@ def main() -> None:
with _create_bus(a_results, **a_additional_config) as bus_a:
with _create_bus(b_results, **b_additional_config) as bus_b:
+ reader_a = can.RedirectReader(bus_b)
+ reader_b = can.RedirectReader(bus_a)
+ notifier_a = can.Notifier(bus_a, [reader_a])
+ notifier_b = can.Notifier(bus_b, [reader_b])
print(f"CAN Bridge (Started on {datetime.now()})")
+ while True:
+ time.sleep(1)
print(f"CAN Bridge (Stopped on {datetime.now()})")
From 12f037d181c8b19933c8fd5deab503de6fdb4db4 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sat, 22 Feb 2025 21:26:15 +0100
Subject: [PATCH 04/35] Improve exit handling
---
can/bridge.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index cb957f0db..057813bef 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -98,8 +98,11 @@ def main() -> None:
notifier_a = can.Notifier(bus_a, [reader_a])
notifier_b = can.Notifier(bus_b, [reader_b])
print(f"CAN Bridge (Started on {datetime.now()})")
- while True:
- time.sleep(1)
+ try:
+ while True:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ pass
print(f"CAN Bridge (Stopped on {datetime.now()})")
From 21ac3a0dae946fd35cb0e37db78a9c613897cda1 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sat, 22 Feb 2025 21:35:01 +0100
Subject: [PATCH 05/35] Add debug logging
---
can/bridge.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/can/bridge.py b/can/bridge.py
index 057813bef..24f4f0e20 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -80,6 +80,9 @@ def main() -> None:
args = sys.argv[1:]
general, conf_a, conf_b = split_configurations(args)
+ LOG.debug("General configuration: %s", general)
+ LOG.debug("Bus A configuration: %s", conf_a)
+ LOG.debug("Bus B configuration: %s", conf_b)
g_results = general_parser.parse_args(general)
verbosity = g_results.verbosity
@@ -91,6 +94,11 @@ def main() -> None:
b_additional_config = _parse_additional_config([*b_results.extra_args, *b_unknown_args])
b_results.__dict__['verbosity'] = verbosity
+ LOG.debug("General configuration results: %s", g_results)
+ LOG.debug("Bus A configuration results: %s", a_results)
+ LOG.debug("Bus A additional configuration results: %s", a_additional_config)
+ LOG.debug("Bus B configuration results: %s", b_results)
+ LOG.debug("Bus B additional configuration results: %s", b_additional_config)
with _create_bus(a_results, **a_additional_config) as bus_a:
with _create_bus(b_results, **b_additional_config) as bus_b:
reader_a = can.RedirectReader(bus_b)
From 59e64f6f3555a0fed2842009270f2f87f7e23209 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sat, 22 Feb 2025 21:50:12 +0100
Subject: [PATCH 06/35] Add error handling for wrong arguments
---
can/bridge.py | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 24f4f0e20..3d17faa2e 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -20,6 +20,10 @@
LOG = logging.getLogger(__name__)
+class UserError(Exception):
+ pass
+
+
def get_config_list(it, separator, conf):
while True:
el = next(it)
@@ -34,9 +38,11 @@ def split_configurations(arg_list, separator='--'):
conf_a = []
conf_b = []
+ found_sep = False
it = iter(arg_list)
try:
get_config_list(it, separator, conf_a)
+ found_sep = True
get_config_list(it, separator, conf_b)
# When we reached this point we found two separators so we have
@@ -47,9 +53,11 @@ def split_configurations(arg_list, separator='--'):
# When we reached this point we found three separators so this is
# an error.
- raise Exception("To many configurations")
+ raise UserError("To many configurations")
except StopIteration:
LOG.debug("All configurations were split")
+ if not found_sep:
+ raise UserError("Missing separator")
return general, conf_a, conf_b
@@ -78,7 +86,13 @@ def main() -> None:
raise SystemExit(errno.EINVAL)
args = sys.argv[1:]
- general, conf_a, conf_b = split_configurations(args)
+ try:
+ general, conf_a, conf_b = split_configurations(args)
+ except UserError as exc:
+ print("Error while processing arguments:")
+ print(exc)
+ print("")
+ raise SystemExit(errno.EINVAL)
LOG.debug("General configuration: %s", general)
LOG.debug("Bus A configuration: %s", conf_a)
From 07b6e37dd6c53380af4c1f658ab2915fd5e2c8f3 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sat, 22 Feb 2025 21:54:07 +0100
Subject: [PATCH 07/35] Use stderr
---
can/bridge.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 3d17faa2e..10ee9bd88 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -89,9 +89,8 @@ def main() -> None:
try:
general, conf_a, conf_b = split_configurations(args)
except UserError as exc:
- print("Error while processing arguments:")
- print(exc)
- print("")
+ print(f"Error while processing arguments: {exc}",
+ file=sys.stderr)
raise SystemExit(errno.EINVAL)
LOG.debug("General configuration: %s", general)
From 3dce956cae3596ff6b5c78a7058788dc41767481 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sat, 22 Feb 2025 22:03:35 +0100
Subject: [PATCH 08/35] Add custom usage
---
can/bridge.py | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/can/bridge.py b/can/bridge.py
index 10ee9bd88..e40c3f0b3 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -17,6 +17,23 @@
import can
+USAGE = """
+usage: can_bridge [{general config} --] {can A config} -- {can B config}
+
+Bridge two CAN busses.
+
+Both can busses will be connected so that messages from bus A will be sent on bus B and messages on bus B will be sent to bus A. The busses are separated by a `--`
+
+positional arguments:
+ {general config} The configuration for this program excluding the config for each bus. Can be omitted
+ {can A config} The configuration for the first bus
+ {can B config} The configuration for the second bus
+
+Example usage:
+ can_bridge -i socketcan -c can0 -- -i socketcan can1
+ can_bridge -vvv -- -i socketcan -c can0 -- -i socketcan can1
+"""
+
LOG = logging.getLogger(__name__)
@@ -82,7 +99,7 @@ def main() -> None:
# print help message when no arguments were given
if len(sys.argv) < 2:
- bus_parser.print_help(sys.stderr)
+ print(USAGE, file=sys.stderr)
raise SystemExit(errno.EINVAL)
args = sys.argv[1:]
From 6016ae7c3a6e3ec83b514bbc2fca393be4ff68e4 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sat, 22 Feb 2025 22:07:36 +0100
Subject: [PATCH 09/35] Add bus configuration info
---
can/bridge.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index e40c3f0b3..8e0ee40e1 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -32,6 +32,8 @@
Example usage:
can_bridge -i socketcan -c can0 -- -i socketcan can1
can_bridge -vvv -- -i socketcan -c can0 -- -i socketcan can1
+
+Type `can_bridge help_bus` for information about single bus configuration.
"""
LOG = logging.getLogger(__name__)
@@ -106,8 +108,11 @@ def main() -> None:
try:
general, conf_a, conf_b = split_configurations(args)
except UserError as exc:
- print(f"Error while processing arguments: {exc}",
- file=sys.stderr)
+ if len(args) == 1 and args[0] == 'help_bus':
+ bus_parser.print_help(sys.stderr)
+ else:
+ print(f"Error while processing arguments: {exc}",
+ file=sys.stderr)
raise SystemExit(errno.EINVAL)
LOG.debug("General configuration: %s", general)
From b0e75826b9c7071c27a1597cc92c6c5f11892722 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 10:54:47 +0100
Subject: [PATCH 10/35] Code format
---
can/bridge.py | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 8e0ee40e1..8a6b270c8 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -52,7 +52,7 @@ def get_config_list(it, separator, conf):
conf.append(el)
-def split_configurations(arg_list, separator='--'):
+def split_configurations(arg_list, separator="--"):
general = []
conf_a = []
conf_b = []
@@ -97,7 +97,7 @@ def main() -> None:
_create_base_argument_parser(bus_parser)
parser = argparse.ArgumentParser(description="Bridge two CAN busses.")
- parser.add_argument('configs', nargs=argparse.REMAINDER)
+ parser.add_argument("configs", nargs=argparse.REMAINDER)
# print help message when no arguments were given
if len(sys.argv) < 2:
@@ -108,11 +108,10 @@ def main() -> None:
try:
general, conf_a, conf_b = split_configurations(args)
except UserError as exc:
- if len(args) == 1 and args[0] == 'help_bus':
+ if len(args) == 1 and args[0] == "help_bus":
bus_parser.print_help(sys.stderr)
else:
- print(f"Error while processing arguments: {exc}",
- file=sys.stderr)
+ print(f"Error while processing arguments: {exc}", file=sys.stderr)
raise SystemExit(errno.EINVAL)
LOG.debug("General configuration: %s", general)
@@ -122,12 +121,16 @@ def main() -> None:
verbosity = g_results.verbosity
a_results, a_unknown_args = bus_parser.parse_known_args(conf_a)
- a_additional_config = _parse_additional_config([*a_results.extra_args, *a_unknown_args])
- a_results.__dict__['verbosity'] = verbosity
+ a_additional_config = _parse_additional_config(
+ [*a_results.extra_args, *a_unknown_args]
+ )
+ a_results.__dict__["verbosity"] = verbosity
b_results, b_unknown_args = bus_parser.parse_known_args(conf_b)
- b_additional_config = _parse_additional_config([*b_results.extra_args, *b_unknown_args])
- b_results.__dict__['verbosity'] = verbosity
+ b_additional_config = _parse_additional_config(
+ [*b_results.extra_args, *b_unknown_args]
+ )
+ b_results.__dict__["verbosity"] = verbosity
LOG.debug("General configuration results: %s", g_results)
LOG.debug("Bus A configuration results: %s", a_results)
From 78787d9e1daa0dfa5a236a4de7b0d7ed33a04b0f Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 11:03:07 +0100
Subject: [PATCH 11/35] Add exception for prints for can_bridge
---
pyproject.toml | 1 +
1 file changed, 1 insertion(+)
diff --git a/pyproject.toml b/pyproject.toml
index 18217ac4b..8fd77de21 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -187,6 +187,7 @@ ignore = [
"can/cli.py" = ["T20"] # flake8-print
"can/logger.py" = ["T20"] # flake8-print
"can/player.py" = ["T20"] # flake8-print
+"can/bridge.py" = ["T20"] # flake8-print
"can/viewer.py" = ["T20"] # flake8-print
"examples/*" = ["T20"] # flake8-print
From d9391f7eb1c98f594425adf456108205f4215ca0 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 11:05:02 +0100
Subject: [PATCH 12/35] Add from to exception in exception
---
can/bridge.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 8a6b270c8..65bd25664 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -76,7 +76,7 @@ def split_configurations(arg_list, separator="--"):
except StopIteration:
LOG.debug("All configurations were split")
if not found_sep:
- raise UserError("Missing separator")
+ raise UserError("Missing separator") from None
return general, conf_a, conf_b
@@ -112,7 +112,7 @@ def main() -> None:
bus_parser.print_help(sys.stderr)
else:
print(f"Error while processing arguments: {exc}", file=sys.stderr)
- raise SystemExit(errno.EINVAL)
+ raise SystemExit(errno.EINVAL) from exc
LOG.debug("General configuration: %s", general)
LOG.debug("Bus A configuration: %s", conf_a)
From aca4798c52be1417902ed722d63d41f8f5aa3908 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 11:05:19 +0100
Subject: [PATCH 13/35] Remove assignment to unused variable
---
can/bridge.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 65bd25664..992c96ee5 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -141,8 +141,8 @@ def main() -> None:
with _create_bus(b_results, **b_additional_config) as bus_b:
reader_a = can.RedirectReader(bus_b)
reader_b = can.RedirectReader(bus_a)
- notifier_a = can.Notifier(bus_a, [reader_a])
- notifier_b = can.Notifier(bus_b, [reader_b])
+ can.Notifier(bus_a, [reader_a])
+ can.Notifier(bus_b, [reader_b])
print(f"CAN Bridge (Started on {datetime.now()})")
try:
while True:
From 46434925b960b6e53fa9d44a9b81fdd671838ba1 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 11:06:45 +0100
Subject: [PATCH 14/35] Shorten line length
---
can/bridge.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 992c96ee5..5a4a16d21 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -22,10 +22,12 @@
Bridge two CAN busses.
-Both can busses will be connected so that messages from bus A will be sent on bus B and messages on bus B will be sent to bus A. The busses are separated by a `--`
+Both can busses will be connected so that messages from bus A will be sent on
+bus B and messages on bus B will be sent to bus A. The busses are separated by a `--`
positional arguments:
- {general config} The configuration for this program excluding the config for each bus. Can be omitted
+ {general config} The configuration for this program excluding
+ the config for each bus. Can be omitted
{can A config} The configuration for the first bus
{can B config} The configuration for the second bus
From e117b38c51d857e9352af5ca1d31acf23677aaeb Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 11:10:33 +0100
Subject: [PATCH 15/35] Organize imports
---
can/bridge.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 5a4a16d21..e9f51c1d4 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -7,15 +7,14 @@
import argparse
import errno
+import logging
import sys
import time
-import logging
from datetime import datetime
-from .logger import _create_base_argument_parser, _create_bus, _parse_additional_config
-
import can
+from .logger import _create_base_argument_parser, _create_bus, _parse_additional_config
USAGE = """
usage: can_bridge [{general config} --] {can A config} -- {can B config}
From e40ee3370f9c4b7de07d6365f5700d1c6c9231fc Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 11:12:35 +0100
Subject: [PATCH 16/35] Remove unnecessary else
---
can/bridge.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index e9f51c1d4..6833af8af 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -49,8 +49,8 @@ def get_config_list(it, separator, conf):
el = next(it)
if el == separator:
break
- else:
- conf.append(el)
+
+ conf.append(el)
def split_configurations(arg_list, separator="--"):
From 166b8826cf8a22535e2b70140e56091908206620 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 11:18:58 +0100
Subject: [PATCH 17/35] Add documentation for new script
---
doc/scripts.rst | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/doc/scripts.rst b/doc/scripts.rst
index 2d59b7528..281391162 100644
--- a/doc/scripts.rst
+++ b/doc/scripts.rst
@@ -57,6 +57,21 @@ The full usage page can be seen below:
:shell:
+can.bridge
+----------
+
+A small application that can be used to connect two can busses:
+
+.. command-output:: python -m can.bridge -h
+ :shell:
+
+
+Example call:
+::
+
+ python -m can.bridge -i socketcan -c can0 -- -i socketcan -c can1
+
+
can.logconvert
--------------
From 0ef6ca237b61c48afabfb7283f2d4e7cf455ef54 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 11:25:08 +0100
Subject: [PATCH 18/35] Add handling for -h and help sub command
Necessary for generation of documentation
---
can/bridge.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 6833af8af..a2cbfd4aa 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -109,8 +109,12 @@ def main() -> None:
try:
general, conf_a, conf_b = split_configurations(args)
except UserError as exc:
- if len(args) == 1 and args[0] == "help_bus":
- bus_parser.print_help(sys.stderr)
+ if len(args) >= 1:
+ if args[0] == "-h" or args[0] == "help":
+ print(USAGE)
+ raise SystemExit()
+ elif args[0] == "help_bus":
+ bus_parser.print_help(sys.stderr)
else:
print(f"Error while processing arguments: {exc}", file=sys.stderr)
raise SystemExit(errno.EINVAL) from exc
From f1401ab4b0ad4961e11e66b0194accff6f2d0d5d Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 11:27:24 +0100
Subject: [PATCH 19/35] Add from none to exception
---
can/bridge.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/can/bridge.py b/can/bridge.py
index a2cbfd4aa..130ed46c6 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -112,7 +112,7 @@ def main() -> None:
if len(args) >= 1:
if args[0] == "-h" or args[0] == "help":
print(USAGE)
- raise SystemExit()
+ raise SystemExit() from None
elif args[0] == "help_bus":
bus_parser.print_help(sys.stderr)
else:
From 7057f69635490a47d6a75ce08306362a74dde30e Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 18:34:02 +0100
Subject: [PATCH 20/35] Fix typo busses to bus
---
can/bridge.py | 14 +++++++-------
doc/scripts.rst | 2 +-
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 130ed46c6..0b42d525f 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -1,7 +1,7 @@
"""
-Creates a bridge between two CAN busses.
+Creates a bridge between two CAN buses.
-This will connect to two CAN busses. Messages received on one
+This will connect to two CAN buses. Messages received on one
bus will be sent to the other bus and vice versa.
"""
@@ -19,10 +19,10 @@
USAGE = """
usage: can_bridge [{general config} --] {can A config} -- {can B config}
-Bridge two CAN busses.
+Bridge two CAN buses.
-Both can busses will be connected so that messages from bus A will be sent on
-bus B and messages on bus B will be sent to bus A. The busses are separated by a `--`
+Both can buses will be connected so that messages from bus A will be sent on
+bus B and messages on bus B will be sent to bus A. The buses are separated by a `--`
positional arguments:
{general config} The configuration for this program excluding
@@ -93,11 +93,11 @@ def main() -> None:
default=2,
)
- bus_parser = argparse.ArgumentParser(description="Bridge two CAN busses.")
+ bus_parser = argparse.ArgumentParser(description="Bridge two CAN buses.")
_create_base_argument_parser(bus_parser)
- parser = argparse.ArgumentParser(description="Bridge two CAN busses.")
+ parser = argparse.ArgumentParser(description="Bridge two CAN buses.")
parser.add_argument("configs", nargs=argparse.REMAINDER)
# print help message when no arguments were given
diff --git a/doc/scripts.rst b/doc/scripts.rst
index 281391162..47555bf87 100644
--- a/doc/scripts.rst
+++ b/doc/scripts.rst
@@ -60,7 +60,7 @@ The full usage page can be seen below:
can.bridge
----------
-A small application that can be used to connect two can busses:
+A small application that can be used to connect two can buses:
.. command-output:: python -m can.bridge -h
:shell:
From 91ffbdbf738b79249802be3e89992ed47fc44017 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 18:48:55 +0100
Subject: [PATCH 21/35] Add type annotations
---
can/bridge.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 0b42d525f..12d04ca9d 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -11,6 +11,9 @@
import sys
import time
from datetime import datetime
+from typing import (
+ Iterator,
+)
import can
@@ -44,7 +47,7 @@ class UserError(Exception):
pass
-def get_config_list(it, separator, conf):
+def get_config_list(it: Iterator[str], separator: str, conf) -> None:
while True:
el = next(it)
if el == separator:
@@ -53,7 +56,9 @@ def get_config_list(it, separator, conf):
conf.append(el)
-def split_configurations(arg_list, separator="--"):
+def split_configurations(
+ arg_list: list[str], separator: str = "--"
+) -> (list, list, list):
general = []
conf_a = []
conf_b = []
From e0b536835bcfe4a706ceb598b4729f539258052c Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 18:52:36 +0100
Subject: [PATCH 22/35] Fix type annotations
---
can/bridge.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 12d04ca9d..3300a50a7 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -13,6 +13,7 @@
from datetime import datetime
from typing import (
Iterator,
+ List,
)
import can
@@ -47,7 +48,7 @@ class UserError(Exception):
pass
-def get_config_list(it: Iterator[str], separator: str, conf) -> None:
+def get_config_list(it: Iterator[str], separator: str, conf: list) -> None:
while True:
el = next(it)
if el == separator:
@@ -57,7 +58,7 @@ def get_config_list(it: Iterator[str], separator: str, conf) -> None:
def split_configurations(
- arg_list: list[str], separator: str = "--"
+ arg_list: List[str], separator: str = "--"
) -> (list, list, list):
general = []
conf_a = []
From deb06fdae40775049eb496b47becb605a45796c8 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 18:55:41 +0100
Subject: [PATCH 23/35] Fix type annotations again
---
can/bridge.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index 3300a50a7..50da299a3 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -14,6 +14,7 @@
from typing import (
Iterator,
List,
+ Tuple,
)
import can
@@ -59,10 +60,10 @@ def get_config_list(it: Iterator[str], separator: str, conf: list) -> None:
def split_configurations(
arg_list: List[str], separator: str = "--"
-) -> (list, list, list):
+) -> Tuple[list, list, list]:
general = []
- conf_a = []
- conf_b = []
+ conf_a: List[str] = []
+ conf_b: List[str] = []
found_sep = False
it = iter(arg_list)
From c7222b1a930d1b9b1dd01c402fc57a58220476b4 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 19:47:03 +0100
Subject: [PATCH 24/35] Add --help to get help
---
can/bridge.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/can/bridge.py b/can/bridge.py
index 50da299a3..bbae0a3e1 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -117,7 +117,7 @@ def main() -> None:
general, conf_a, conf_b = split_configurations(args)
except UserError as exc:
if len(args) >= 1:
- if args[0] == "-h" or args[0] == "help":
+ if args[0] == "-h" or args[0] == "--help" or args[0] == "help":
print(USAGE)
raise SystemExit() from None
elif args[0] == "help_bus":
From 69c039a63ebd82ba3eb0f3b7a6fcb1c8852fcbdb Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 19:47:17 +0100
Subject: [PATCH 25/35] Add basic print help test
---
test/test_scripts.py | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/test/test_scripts.py b/test/test_scripts.py
index 9d8c059cf..c1a6c082d 100644
--- a/test/test_scripts.py
+++ b/test/test_scripts.py
@@ -98,6 +98,20 @@ def _import(self):
return module
+class TestBridgeScript(CanScriptTest):
+ def _commands(self):
+ commands = [
+ "python -m can.bridge --help",
+ "can_bridge --help",
+ ]
+ return commands
+
+ def _import(self):
+ import can.bridge as module
+
+ return module
+
+
class TestLogconvertScript(CanScriptTest):
def _commands(self):
commands = [
From 037eb23c38c5472034ce647b0d4ebd129f69da92 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 19:52:55 +0100
Subject: [PATCH 26/35] Add basic test file for bridge script
---
test/test_bridge.py | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 test/test_bridge.py
diff --git a/test/test_bridge.py b/test/test_bridge.py
new file mode 100644
index 000000000..43fb96e06
--- /dev/null
+++ b/test/test_bridge.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+"""
+This module tests the functions inside of bridge.py
+"""
+
+import unittest
+from unittest import mock
+from unittest.mock import Mock
+
+import can
+import can.bridge
+
+
+class TestBridgeScriptModule(unittest.TestCase):
+ def setUp(self) -> None:
+ # Patch VirtualBus object
+ patcher_virtual_bus = mock.patch("can.interfaces.virtual.VirtualBus", spec=True)
+ self.MockVirtualBus = patcher_virtual_bus.start()
+ self.addCleanup(patcher_virtual_bus.stop)
+ self.mock_virtual_bus = self.MockVirtualBus.return_value
+ self.mock_virtual_bus.shutdown = Mock()
+
+ self.testmsg = can.Message(
+ arbitration_id=0xC0FFEE, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=True
+ )
+
+ def assert_successfull_cleanup(self):
+ self.MockVirtualBus.assert_called_once()
+ self.mock_virtual_bus.shutdown.assert_called_once()
+
+
+if __name__ == "__main__":
+ unittest.main()
From 991aa05a1f556f2ddb7c6b1f58bb1f189a17f92a Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 20:16:02 +0100
Subject: [PATCH 27/35] Add very basic test
---
test/test_bridge.py | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/test/test_bridge.py b/test/test_bridge.py
index 43fb96e06..bd857b071 100644
--- a/test/test_bridge.py
+++ b/test/test_bridge.py
@@ -4,6 +4,7 @@
This module tests the functions inside of bridge.py
"""
+import sys
import unittest
from unittest import mock
from unittest.mock import Mock
@@ -21,13 +22,26 @@ def setUp(self) -> None:
self.mock_virtual_bus = self.MockVirtualBus.return_value
self.mock_virtual_bus.shutdown = Mock()
+ # Patch time sleep object
+ patcher_sleep = mock.patch("can.io.player.time.sleep", spec=True)
+ self.MockSleep = patcher_sleep.start()
+ self.addCleanup(patcher_sleep.stop)
+
self.testmsg = can.Message(
arbitration_id=0xC0FFEE, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=True
)
+ self.busargs = ["-i", "virtual"]
+
def assert_successfull_cleanup(self):
- self.MockVirtualBus.assert_called_once()
- self.mock_virtual_bus.shutdown.assert_called_once()
+ self.MockVirtualBus.assert_called()
+
+ def test_bridge_no_config(self):
+ self.MockSleep.side_effect = KeyboardInterrupt
+ sys.argv = [sys.argv[0], *self.busargs, "--", *self.busargs]
+ can.bridge.main()
+
+ self.assert_successfull_cleanup()
if __name__ == "__main__":
From d2227d7519df846e75f2178317614547e332617f Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 20:20:56 +0100
Subject: [PATCH 28/35] Add different channels for virtual bus
---
test/test_bridge.py | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/test/test_bridge.py b/test/test_bridge.py
index bd857b071..3189d18b7 100644
--- a/test/test_bridge.py
+++ b/test/test_bridge.py
@@ -38,7 +38,16 @@ def assert_successfull_cleanup(self):
def test_bridge_no_config(self):
self.MockSleep.side_effect = KeyboardInterrupt
- sys.argv = [sys.argv[0], *self.busargs, "--", *self.busargs]
+ sys.argv = [
+ sys.argv[0],
+ *self.busargs,
+ "-c",
+ "can_a",
+ "--",
+ *self.busargs,
+ "-c",
+ "can_b",
+ ]
can.bridge.main()
self.assert_successfull_cleanup()
From 3974c512c6465de24be150f67a364c6dd10284ff Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 20:28:39 +0100
Subject: [PATCH 29/35] Add assert for call to exit
---
test/test_bridge.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/test/test_bridge.py b/test/test_bridge.py
index 3189d18b7..b17030aff 100644
--- a/test/test_bridge.py
+++ b/test/test_bridge.py
@@ -20,7 +20,7 @@ def setUp(self) -> None:
self.MockVirtualBus = patcher_virtual_bus.start()
self.addCleanup(patcher_virtual_bus.stop)
self.mock_virtual_bus = self.MockVirtualBus.return_value
- self.mock_virtual_bus.shutdown = Mock()
+ self.mock_virtual_bus.__enter__ = Mock(return_value=self.mock_virtual_bus)
# Patch time sleep object
patcher_sleep = mock.patch("can.io.player.time.sleep", spec=True)
@@ -35,6 +35,7 @@ def setUp(self) -> None:
def assert_successfull_cleanup(self):
self.MockVirtualBus.assert_called()
+ self.assertEqual(2, len(self.mock_virtual_bus.__exit__.mock_calls))
def test_bridge_no_config(self):
self.MockSleep.side_effect = KeyboardInterrupt
From 8f6ae52082b6d835210bd9c742722c3a8747f735 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Sun, 23 Feb 2025 20:33:11 +0100
Subject: [PATCH 30/35] Patch correct function
---
test/test_bridge.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/test_bridge.py b/test/test_bridge.py
index b17030aff..c57b5768b 100644
--- a/test/test_bridge.py
+++ b/test/test_bridge.py
@@ -23,7 +23,7 @@ def setUp(self) -> None:
self.mock_virtual_bus.__enter__ = Mock(return_value=self.mock_virtual_bus)
# Patch time sleep object
- patcher_sleep = mock.patch("can.io.player.time.sleep", spec=True)
+ patcher_sleep = mock.patch("can.bridge.time.sleep", spec=True)
self.MockSleep = patcher_sleep.start()
self.addCleanup(patcher_sleep.stop)
From e86814f3b4f6935b14d2781b0370c3c37a2428f9 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Mon, 24 Feb 2025 21:20:16 +0100
Subject: [PATCH 31/35] test
---
test/test_bridge.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/test_bridge.py b/test/test_bridge.py
index c57b5768b..973f98e93 100644
--- a/test/test_bridge.py
+++ b/test/test_bridge.py
@@ -49,9 +49,9 @@ def test_bridge_no_config(self):
"-c",
"can_b",
]
- can.bridge.main()
+ #can.bridge.main()
- self.assert_successfull_cleanup()
+ #self.assert_successfull_cleanup()
if __name__ == "__main__":
From 6dd6519d12c802421ac20361563665372810f001 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Mon, 24 Feb 2025 21:23:59 +0100
Subject: [PATCH 32/35] fjkdf
---
test/test_bridge.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/test_bridge.py b/test/test_bridge.py
index 973f98e93..df5a10a94 100644
--- a/test/test_bridge.py
+++ b/test/test_bridge.py
@@ -49,7 +49,7 @@ def test_bridge_no_config(self):
"-c",
"can_b",
]
- #can.bridge.main()
+ can.bridge.main()
#self.assert_successfull_cleanup()
From c3a69065f1d5db66549395ed89884de29a3dbfa6 Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Mon, 24 Feb 2025 21:27:42 +0100
Subject: [PATCH 33/35] once again -.-
---
test/test_bridge.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/test_bridge.py b/test/test_bridge.py
index df5a10a94..c57b5768b 100644
--- a/test/test_bridge.py
+++ b/test/test_bridge.py
@@ -51,7 +51,7 @@ def test_bridge_no_config(self):
]
can.bridge.main()
- #self.assert_successfull_cleanup()
+ self.assert_successfull_cleanup()
if __name__ == "__main__":
From 9f0950ed446c2a4b4652a9118b5ac2123a853c4d Mon Sep 17 00:00:00 2001
From: Peter Kessen
Date: Mon, 24 Feb 2025 21:32:40 +0100
Subject: [PATCH 34/35] Try snakecase
---
test/test_bridge.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/test_bridge.py b/test/test_bridge.py
index c57b5768b..11e645ede 100644
--- a/test/test_bridge.py
+++ b/test/test_bridge.py
@@ -33,7 +33,7 @@ def setUp(self) -> None:
self.busargs = ["-i", "virtual"]
- def assert_successfull_cleanup(self):
+ def assertSuccessfullCleanup(self):
self.MockVirtualBus.assert_called()
self.assertEqual(2, len(self.mock_virtual_bus.__exit__.mock_calls))
@@ -51,7 +51,7 @@ def test_bridge_no_config(self):
]
can.bridge.main()
- self.assert_successfull_cleanup()
+ self.assertSuccessfullCleanup()
if __name__ == "__main__":
From 4e063c3877e5a24524babd01e92683a68ceb27ec Mon Sep 17 00:00:00 2001
From: zariiii9003 <52598363+zariiii9003@users.noreply.github.com>
Date: Thu, 24 Jul 2025 13:03:58 +0200
Subject: [PATCH 35/35] use new api to create cli args for bus1 and bus2
---
can/bridge.py | 161 ++++++++------------------------------------
doc/scripts.rst | 6 --
test/test_bridge.py | 138 +++++++++++++++++++++++++++----------
3 files changed, 132 insertions(+), 173 deletions(-)
diff --git a/can/bridge.py b/can/bridge.py
index bbae0a3e1..57ebb368d 100644
--- a/can/bridge.py
+++ b/can/bridge.py
@@ -7,154 +7,51 @@
import argparse
import errno
-import logging
import sys
import time
from datetime import datetime
-from typing import (
- Iterator,
- List,
- Tuple,
-)
+from typing import Final
-import can
-
-from .logger import _create_base_argument_parser, _create_bus, _parse_additional_config
-
-USAGE = """
-usage: can_bridge [{general config} --] {can A config} -- {can B config}
+from can.cli import add_bus_arguments, create_bus_from_namespace
+from can.listener import RedirectReader
+from can.notifier import Notifier
+BRIDGE_DESCRIPTION: Final = """\
Bridge two CAN buses.
-Both can buses will be connected so that messages from bus A will be sent on
-bus B and messages on bus B will be sent to bus A. The buses are separated by a `--`
-
-positional arguments:
- {general config} The configuration for this program excluding
- the config for each bus. Can be omitted
- {can A config} The configuration for the first bus
- {can B config} The configuration for the second bus
-
-Example usage:
- can_bridge -i socketcan -c can0 -- -i socketcan can1
- can_bridge -vvv -- -i socketcan -c can0 -- -i socketcan can1
-
-Type `can_bridge help_bus` for information about single bus configuration.
+Both can buses will be connected so that messages from bus1 will be sent on
+bus2 and messages from bus2 will be sent to bus1.
"""
-
-LOG = logging.getLogger(__name__)
-
-
-class UserError(Exception):
- pass
-
-
-def get_config_list(it: Iterator[str], separator: str, conf: list) -> None:
- while True:
- el = next(it)
- if el == separator:
- break
-
- conf.append(el)
+BUS_1_PREFIX: Final = "bus1"
+BUS_2_PREFIX: Final = "bus2"
-def split_configurations(
- arg_list: List[str], separator: str = "--"
-) -> Tuple[list, list, list]:
- general = []
- conf_a: List[str] = []
- conf_b: List[str] = []
+def _parse_bridge_args(args: list[str]) -> argparse.Namespace:
+ """Parse command line arguments for bridge script."""
- found_sep = False
- it = iter(arg_list)
- try:
- get_config_list(it, separator, conf_a)
- found_sep = True
- get_config_list(it, separator, conf_b)
+ parser = argparse.ArgumentParser(description=BRIDGE_DESCRIPTION)
+ add_bus_arguments(parser, prefix=BUS_1_PREFIX, group_title="Bus 1 arguments")
+ add_bus_arguments(parser, prefix=BUS_2_PREFIX, group_title="Bus 2 arguments")
- # When we reached this point we found two separators so we have
- # a general config. We will treate the first config as general
- general = conf_a
- conf_a = conf_b
- get_config_list(it, separator, conf_b)
-
- # When we reached this point we found three separators so this is
- # an error.
- raise UserError("To many configurations")
- except StopIteration:
- LOG.debug("All configurations were split")
- if not found_sep:
- raise UserError("Missing separator") from None
+ # print help message when no arguments were given
+ if not args:
+ parser.print_help(sys.stderr)
+ raise SystemExit(errno.EINVAL)
- return general, conf_a, conf_b
+ results, _unknown_args = parser.parse_known_args(args)
+ return results
def main() -> None:
- general_parser = argparse.ArgumentParser()
- general_parser.add_argument(
- "-v",
- action="count",
- dest="verbosity",
- help="""How much information do you want to see at the command line?
- You can add several of these e.g., -vv is DEBUG""",
- default=2,
- )
-
- bus_parser = argparse.ArgumentParser(description="Bridge two CAN buses.")
-
- _create_base_argument_parser(bus_parser)
-
- parser = argparse.ArgumentParser(description="Bridge two CAN buses.")
- parser.add_argument("configs", nargs=argparse.REMAINDER)
-
- # print help message when no arguments were given
- if len(sys.argv) < 2:
- print(USAGE, file=sys.stderr)
- raise SystemExit(errno.EINVAL)
-
- args = sys.argv[1:]
- try:
- general, conf_a, conf_b = split_configurations(args)
- except UserError as exc:
- if len(args) >= 1:
- if args[0] == "-h" or args[0] == "--help" or args[0] == "help":
- print(USAGE)
- raise SystemExit() from None
- elif args[0] == "help_bus":
- bus_parser.print_help(sys.stderr)
- else:
- print(f"Error while processing arguments: {exc}", file=sys.stderr)
- raise SystemExit(errno.EINVAL) from exc
-
- LOG.debug("General configuration: %s", general)
- LOG.debug("Bus A configuration: %s", conf_a)
- LOG.debug("Bus B configuration: %s", conf_b)
- g_results = general_parser.parse_args(general)
- verbosity = g_results.verbosity
-
- a_results, a_unknown_args = bus_parser.parse_known_args(conf_a)
- a_additional_config = _parse_additional_config(
- [*a_results.extra_args, *a_unknown_args]
- )
- a_results.__dict__["verbosity"] = verbosity
-
- b_results, b_unknown_args = bus_parser.parse_known_args(conf_b)
- b_additional_config = _parse_additional_config(
- [*b_results.extra_args, *b_unknown_args]
- )
- b_results.__dict__["verbosity"] = verbosity
-
- LOG.debug("General configuration results: %s", g_results)
- LOG.debug("Bus A configuration results: %s", a_results)
- LOG.debug("Bus A additional configuration results: %s", a_additional_config)
- LOG.debug("Bus B configuration results: %s", b_results)
- LOG.debug("Bus B additional configuration results: %s", b_additional_config)
- with _create_bus(a_results, **a_additional_config) as bus_a:
- with _create_bus(b_results, **b_additional_config) as bus_b:
- reader_a = can.RedirectReader(bus_b)
- reader_b = can.RedirectReader(bus_a)
- can.Notifier(bus_a, [reader_a])
- can.Notifier(bus_b, [reader_b])
+ results = _parse_bridge_args(sys.argv[1:])
+
+ with (
+ create_bus_from_namespace(results, prefix=BUS_1_PREFIX) as bus1,
+ create_bus_from_namespace(results, prefix=BUS_2_PREFIX) as bus2,
+ ):
+ reader1_to_2 = RedirectReader(bus2)
+ reader2_to_1 = RedirectReader(bus1)
+ with Notifier(bus1, [reader1_to_2]), Notifier(bus2, [reader2_to_1]):
print(f"CAN Bridge (Started on {datetime.now()})")
try:
while True:
diff --git a/doc/scripts.rst b/doc/scripts.rst
index 47555bf87..1d730a74b 100644
--- a/doc/scripts.rst
+++ b/doc/scripts.rst
@@ -66,12 +66,6 @@ A small application that can be used to connect two can buses:
:shell:
-Example call:
-::
-
- python -m can.bridge -i socketcan -c can0 -- -i socketcan -c can1
-
-
can.logconvert
--------------
diff --git a/test/test_bridge.py b/test/test_bridge.py
index 11e645ede..ee41bd949 100644
--- a/test/test_bridge.py
+++ b/test/test_bridge.py
@@ -4,54 +4,122 @@
This module tests the functions inside of bridge.py
"""
+import random
+import string
import sys
-import unittest
-from unittest import mock
-from unittest.mock import Mock
+import threading
+import time
+from time import sleep as real_sleep
+import unittest.mock
import can
import can.bridge
+from can.interfaces import virtual
+from .message_helper import ComparingMessagesTestCase
+
+
+class TestBridgeScriptModule(unittest.TestCase, ComparingMessagesTestCase):
+
+ TIMEOUT = 3.0
+
+ def __init__(self, *args, **kwargs):
+ unittest.TestCase.__init__(self, *args, **kwargs)
+ ComparingMessagesTestCase.__init__(
+ self,
+ allowed_timestamp_delta=None,
+ preserves_channel=False,
+ )
-class TestBridgeScriptModule(unittest.TestCase):
def setUp(self) -> None:
- # Patch VirtualBus object
- patcher_virtual_bus = mock.patch("can.interfaces.virtual.VirtualBus", spec=True)
- self.MockVirtualBus = patcher_virtual_bus.start()
- self.addCleanup(patcher_virtual_bus.stop)
- self.mock_virtual_bus = self.MockVirtualBus.return_value
- self.mock_virtual_bus.__enter__ = Mock(return_value=self.mock_virtual_bus)
-
- # Patch time sleep object
- patcher_sleep = mock.patch("can.bridge.time.sleep", spec=True)
- self.MockSleep = patcher_sleep.start()
- self.addCleanup(patcher_sleep.stop)
+ self.stop_event = threading.Event()
+
+ self.channel1 = "".join(random.choices(string.ascii_letters, k=8))
+ self.channel2 = "".join(random.choices(string.ascii_letters, k=8))
+
+ self.cli_args = [
+ "--bus1-interface",
+ "virtual",
+ "--bus1-channel",
+ self.channel1,
+ "--bus2-interface",
+ "virtual",
+ "--bus2-channel",
+ self.channel2,
+ ]
self.testmsg = can.Message(
arbitration_id=0xC0FFEE, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=True
)
- self.busargs = ["-i", "virtual"]
-
- def assertSuccessfullCleanup(self):
- self.MockVirtualBus.assert_called()
- self.assertEqual(2, len(self.mock_virtual_bus.__exit__.mock_calls))
-
- def test_bridge_no_config(self):
- self.MockSleep.side_effect = KeyboardInterrupt
- sys.argv = [
- sys.argv[0],
- *self.busargs,
- "-c",
- "can_a",
- "--",
- *self.busargs,
- "-c",
- "can_b",
- ]
- can.bridge.main()
+ def fake_sleep(self, duration):
+ """A fake replacement for time.sleep that checks periodically
+ whether self.stop_event is set, and raises KeyboardInterrupt
+ if so.
+
+ This allows tests to simulate an interrupt (like Ctrl+C)
+ during long sleeps, in a controlled and responsive way.
+ """
+ interval = 0.05 # Small interval for responsiveness
+ t_wakeup = time.perf_counter() + duration
+ while time.perf_counter() < t_wakeup:
+ if self.stop_event.is_set():
+ raise KeyboardInterrupt("Simulated interrupt from fake_sleep")
+ real_sleep(interval)
+
+ def test_bridge(self):
+ with (
+ unittest.mock.patch("can.bridge.time.sleep", new=self.fake_sleep),
+ unittest.mock.patch("can.bridge.sys.argv", [sys.argv[0], *self.cli_args]),
+ ):
+ # start script
+ thread = threading.Thread(target=can.bridge.main)
+ thread.start()
+
+ # wait until script instantiates virtual buses
+ t0 = time.perf_counter()
+ while True:
+ with virtual.channels_lock:
+ if (
+ self.channel1 in virtual.channels
+ and self.channel2 in virtual.channels
+ ):
+ break
+ if time.perf_counter() > t0 + 2.0:
+ raise TimeoutError("Bridge script did not create virtual buses")
+ real_sleep(0.2)
+
+ # create buses with the same channels as in scripts
+ with (
+ can.interfaces.virtual.VirtualBus(self.channel1) as bus1,
+ can.interfaces.virtual.VirtualBus(self.channel2) as bus2,
+ ):
+ # send test message to bus1, it should be received on bus2
+ bus1.send(self.testmsg)
+ recv_msg = bus2.recv(self.TIMEOUT)
+ self.assertMessageEqual(self.testmsg, recv_msg)
+
+ # assert that both buses are empty
+ self.assertIsNone(bus1.recv(0))
+ self.assertIsNone(bus2.recv(0))
+
+ # send test message to bus2, it should be received on bus1
+ bus2.send(self.testmsg)
+ recv_msg = bus1.recv(self.TIMEOUT)
+ self.assertMessageEqual(self.testmsg, recv_msg)
+
+ # assert that both buses are empty
+ self.assertIsNone(bus1.recv(0))
+ self.assertIsNone(bus2.recv(0))
+
+ # stop the bridge script
+ self.stop_event.set()
+ thread.join()
- self.assertSuccessfullCleanup()
+ # assert that the virtual buses were closed
+ with virtual.channels_lock:
+ self.assertNotIn(self.channel1, virtual.channels)
+ self.assertNotIn(self.channel2, virtual.channels)
if __name__ == "__main__":