From ebbbf2eeeb15efcb472ebf9e9cb0f019483b11d0 Mon Sep 17 00:00:00 2001 From: Nicholas Felt Date: Tue, 30 Jan 2024 08:45:50 -0800 Subject: [PATCH 1/2] fix: Update the function that detects a VISA resource expression to work properly for SOCKET resource expressions. --- CHANGELOG.md | 4 ++++ src/tm_devices/device_manager.py | 6 +++++ .../helpers/constants_and_dataclasses.py | 2 +- src/tm_devices/helpers/enums.py | 9 ++++---- src/tm_devices/helpers/functions.py | 22 ++++++++++++------- .../test_alternative_connection_addresses.py | 6 +++++ tests/test_helpers.py | 4 ++++ 7 files changed, 39 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d34b5b4f..7292bfe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ Things to be included in the next release go here. - Updated the version of `python-semantic-release` that is used to avoid needing to store a copy of the previous changelog in the repo. - Pinned the linters (ruff, pyright, pylint, docformatter) to specific versions to reduce failures when updates are released that add new rules or break existing rules. +### Fixed + +- Fixed the code that detects VISA resource expressions to be able to detect SOCKET resource expressions properly. + ______________________________________________________________________ ## v1.1.0 (2023-12-07) diff --git a/src/tm_devices/device_manager.py b/src/tm_devices/device_manager.py index dd016136..5c53ecaa 100644 --- a/src/tm_devices/device_manager.py +++ b/src/tm_devices/device_manager.py @@ -1196,6 +1196,12 @@ def _add_device( # noqa: PLR0913 if (visa_resource_parts := detect_visa_resource_expression(address)) is not None: connection_type = visa_resource_parts[0] address = visa_resource_parts[1] + if ( + connection_type == ConnectionTypes.SOCKET.value + and len(address_parts := address.split(":", maxsplit=1)) > 1 + ): + address = address_parts[0] + port = int(address_parts[1]) # Device Manager uses all caps for key mappings to device drivers and aliases config_dict: dict[str, Optional[Union[str, int, SerialConfig]]] = { diff --git a/src/tm_devices/helpers/constants_and_dataclasses.py b/src/tm_devices/helpers/constants_and_dataclasses.py index a3c7b7f8..32a3cdeb 100644 --- a/src/tm_devices/helpers/constants_and_dataclasses.py +++ b/src/tm_devices/helpers/constants_and_dataclasses.py @@ -430,7 +430,7 @@ def __str__(self) -> str: """str: Constant string with the name of this package.""" VISA_RESOURCE_EXPRESSION_REGEX: Final = re.compile( - r"^(\w+)(?:::0X\w+)?::([-.\w]+)(?:::(\w+))?(?:::INST0?)?::INSTR?$" + r"^(\w+)(?:::0X\w+)?::([-.\w]+)(?:::(\w+))?(?:::INST0?)?::(INSTR?|SOCKET)$" ) """re.Pattern[str]: A regex pattern used to capture pieces of VISA resource expressions.""" diff --git a/src/tm_devices/helpers/enums.py b/src/tm_devices/helpers/enums.py index 710b674b..35e0660d 100644 --- a/src/tm_devices/helpers/enums.py +++ b/src/tm_devices/helpers/enums.py @@ -1,6 +1,5 @@ """Module containing Enums for the tm_devices package.""" from enum import Enum -from types import DynamicClassAttribute from typing import cast, List @@ -10,13 +9,13 @@ class CustomStrEnum(Enum): This class provides better type hinting for the value property. """ - @DynamicClassAttribute - def name(self) -> str: # pylint: disable=function-redefined + @property + def name(self) -> str: # pylint: disable=function-redefined,invalid-overridden-method """Return the name of the Enum member.""" return self._name_ # pylint: disable=no-member - @DynamicClassAttribute - def value(self) -> str: + @property + def value(self) -> str: # pylint: disable=invalid-overridden-method """Return the value of the Enum member.""" return cast(str, self._value_) # pylint: disable=no-member diff --git a/src/tm_devices/helpers/functions.py b/src/tm_devices/helpers/functions.py index 4fd3a791..0a502d36 100644 --- a/src/tm_devices/helpers/functions.py +++ b/src/tm_devices/helpers/functions.py @@ -265,7 +265,8 @@ def detect_visa_resource_expression(input_str: str) -> Optional[Tuple[str, str]] The pieces consist of: - The connection type, e.g. TCPIP. - - The address of the device, an IP address, hostname, or + - The address of the device, an IP address + (with port separated by a colon for SOCKET connections), hostname, or string in the format ``model-serial``. Args: @@ -276,11 +277,11 @@ def detect_visa_resource_expression(input_str: str) -> Optional[Tuple[str, str]] """ retval: Optional[Tuple[str, str]] = None if input_str.upper().startswith("ASRL"): - retval = ("SERIAL", input_str[4:].split("::", 1)[0]) + retval = (ConnectionTypes.SERIAL.value, input_str[4:].split("::", 1)[0]) elif (match := VISA_RESOURCE_EXPRESSION_REGEX.search(input_str.upper())) is not None: match_groups_list = list(filter(None, match.groups())) - for unneeded_part in ("INST", "INST0"): - if unneeded_part in match_groups_list: + for unneeded_part in ("INST", "INST0", "INSTR"): + while unneeded_part in match_groups_list: match_groups_list.remove(unneeded_part) # Check if the model is in the USB model lookup filtered_usb_model_keys = [ @@ -291,12 +292,18 @@ def detect_visa_resource_expression(input_str: str) -> Optional[Tuple[str, str]] if filtered_usb_model_keys: # SMU and PSU need to be removed from the string to prevent issues match_groups_list[1] = filtered_usb_model_keys[0].replace("SMU", "").replace("PSU", "") - retval = (match_groups_list[0].rstrip("0"), "-".join(match_groups_list[1:]).lstrip("0X")) + if match_groups_list[-1] == ConnectionTypes.SOCKET.value: + retval = (match_groups_list[-1], ":".join(match_groups_list[1:3])) + else: + retval = ( + match_groups_list[0].rstrip("0"), + "-".join(match_groups_list[1:]).lstrip("0X"), + ) return retval # pylint: disable=too-many-branches -def get_model_series(model: str) -> str: # noqa: PLR0912,C901,PLR0915 +def get_model_series(model: str) -> str: # noqa: PLR0912, C901 """Get the series string from the full model number. Args: @@ -331,10 +338,9 @@ def get_model_series(model: str) -> str: # noqa: PLR0912,C901,PLR0915 model_beginning = "" model_end = "" if re.search("[0-9]", model): # if the model contains numbers - model_end = re.split(r"\d+", model)[-1] # split on the occurence of each number + model_end = re.split(r"\d+", model)[-1] # split on the occurrence of each number if len(model_end) == 1 and model_end not in valid_model_endings: model_end = "" - model_beginning = "" if model_numbers := re.findall(r"\d+", model): model_number = int(model_numbers[0]) if model.startswith("MODEL") or all(x.isdigit() for x in model.rstrip(model_end)): diff --git a/tests/test_alternative_connection_addresses.py b/tests/test_alternative_connection_addresses.py index a48ce2c5..524e1e94 100644 --- a/tests/test_alternative_connection_addresses.py +++ b/tests/test_alternative_connection_addresses.py @@ -108,6 +108,12 @@ None, ("SERIAL", "SMU2614B", "Model 2614B", "4498311"), ), + ( + "TCPIP::AFG3KC-HOSTNAME::10001::SOCKET", + "AFG", + None, + ("SOCKET", "AFG3KC", "AFG3252C", "SERIAL1"), + ), ], ) def test_alternative_connection_methods( diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 69350e57..916b67b2 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -429,6 +429,10 @@ def test_get_visa_backend() -> None: ("TCPIP0::127.0.0.9::inst0::INSTR", ("TCPIP", "127.0.0.9")), ("TCPIP::127.0.0.9::inst::INST", ("TCPIP", "127.0.0.9")), ("USB0::0x0699::0x0522::SERIAL1::INSTR", ("USB", "MSO5-SERIAL1")), + ("TCPIP0::127.0.0.9::4000::SOCKET", ("SOCKET", "127.0.0.9:4000")), + ("GPIB0::1::INSTR", ("GPIB", "1")), + ("ASRL1::INSTR", ("SERIAL", "1")), + ("MOCK0::127.0.0.9::INSTR", ("MOCK", "127.0.0.9")), ], ) def test_detect_visa_resource_expression( From 32dfe575d150fa863e17fab55a74fe37e900e8af Mon Sep 17 00:00:00 2001 From: Nicholas Felt Date: Tue, 30 Jan 2024 08:50:59 -0800 Subject: [PATCH 2/2] style: Update a noqa comment. --- src/tm_devices/helpers/functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tm_devices/helpers/functions.py b/src/tm_devices/helpers/functions.py index 0a502d36..f08a3386 100644 --- a/src/tm_devices/helpers/functions.py +++ b/src/tm_devices/helpers/functions.py @@ -303,7 +303,7 @@ def detect_visa_resource_expression(input_str: str) -> Optional[Tuple[str, str]] # pylint: disable=too-many-branches -def get_model_series(model: str) -> str: # noqa: PLR0912, C901 +def get_model_series(model: str) -> str: # noqa: PLR0912,C901 """Get the series string from the full model number. Args: