From 63c7679fa3feb4d85e8b4c3ef892c2f264dbf4b2 Mon Sep 17 00:00:00 2001 From: Ryan Govostes Date: Mon, 27 Jul 2020 03:11:04 -0400 Subject: [PATCH] Allow omitted rule constraints to match any value (#101) The definition of umodbus.server.route gives default values of None to the arguments, suggesting they can be omitted. However, if you actually provide None, it will fail later when evaluating DataRule.match. This change makes it possible to omit any rule constraint to match on any value. --- dev_requirements.txt | 1 + tests/unit/test_route.py | 36 ++++++++++++++++++++++++++++++++++++ umodbus/route.py | 11 +++++------ umodbus/server/__init__.py | 8 +++++--- 4 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 tests/unit/test_route.py diff --git a/dev_requirements.txt b/dev_requirements.txt index acb5d77..b00073a 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,5 +1,6 @@ -r requirements.txt +mock==3.0.5;python_version<"3.3" pytest==5.3.1;python_version>="3.5" pytest==4.6.6;python_version<"3.5" pytest-cov==2.8.1 diff --git a/tests/unit/test_route.py b/tests/unit/test_route.py new file mode 100644 index 0000000..8c052e8 --- /dev/null +++ b/tests/unit/test_route.py @@ -0,0 +1,36 @@ +import pytest + +from umodbus.route import DataRule + + +endpoint = lambda slave_id, function_code, address: 0 + + +def test_basic_route(): + rule = DataRule(endpoint, slave_ids=[1], function_codes=[1], addresses=[1]) + assert rule.match(slave_id=1, function_code=1, address=1) + assert not rule.match(slave_id=0, function_code=1, address=1) + assert not rule.match(slave_id=1, function_code=0, address=1) + assert not rule.match(slave_id=1, function_code=1, address=0) + + +def test_other_iterables(): + # Other iterable types should work, not just lists + rule = DataRule(endpoint, + slave_ids=set([1]), function_codes=[1], addresses=[1]) + assert rule.match(slave_id=1, function_code=1, address=1) + + +def test_wildcard_slave_id(): + rule = DataRule(endpoint, slave_ids=None, function_codes=[1], addresses=[1]) + assert rule.match(slave_id=1, function_code=1, address=1) + + +def test_wildcard_function_code(): + rule = DataRule(endpoint, slave_ids=[1], function_codes=None, addresses=[1]) + assert rule.match(slave_id=1, function_code=1, address=1) + + +def test_wildcard_address(): + rule = DataRule(endpoint, slave_ids=[1], function_codes=[1], addresses=None) + assert rule.match(slave_id=1, function_code=1, address=1) diff --git a/umodbus/route.py b/umodbus/route.py index b95e6f4..3765e87 100644 --- a/umodbus/route.py +++ b/umodbus/route.py @@ -20,9 +20,8 @@ def __init__(self, endpoint, slave_ids, function_codes, addresses): self.addresses = addresses def match(self, slave_id, function_code, address): - if slave_id in self.slave_ids and\ - function_code in self.function_codes and \ - address in self.addresses: - return True - - return False + # A constraint of None matches any value + matches = lambda values, v: values is None or v in values + return matches(self.slave_ids, slave_id) and \ + matches(self.function_codes, function_code) and \ + matches(self.addresses, address) diff --git a/umodbus/server/__init__.py b/umodbus/server/__init__.py index 03b598b..f9d4093 100644 --- a/umodbus/server/__init__.py +++ b/umodbus/server/__init__.py @@ -19,9 +19,11 @@ def route(self, slave_ids=None, function_codes=None, addresses=None): def read_single_bit_values(slave_id, address): return random.choise([0, 1]) - :param slave_ids: A list or set with slave id's. - :param function_codes: A list or set with function codes. - :param addresses: A list or set with addresses. + Any argument can be omitted to match any value. + + :param slave_ids: A list (or iterable) of slave ids. + :param function_codes: A list (or iterable) of function codes. + :param addresses: A list (or iterable) of addresses. """ def inner(f): self.route_map.add_rule(f, slave_ids, function_codes, addresses)