From e2e9a02f9c98768b3b7c609afc8d40928e5ded01 Mon Sep 17 00:00:00 2001 From: Chsudeepta Date: Mon, 18 Aug 2025 12:00:36 +0100 Subject: [PATCH 01/16] Added schema changes for configuring alarms --- src/server_common/schema/blocks.xsd | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/server_common/schema/blocks.xsd b/src/server_common/schema/blocks.xsd index fea0e51..86f1f92 100644 --- a/src/server_common/schema/blocks.xsd +++ b/src/server_common/schema/blocks.xsd @@ -26,6 +26,13 @@ + + + + + + + @@ -42,4 +49,10 @@ + + + + + + From d9cb590958556da5da1ce01aaaf3073e0620b256 Mon Sep 17 00:00:00 2001 From: Chsudeepta Date: Mon, 8 Sep 2025 15:48:50 +0100 Subject: [PATCH 02/16] Ticket8780: Added methdo to dehexed PV value --- src/server_common/utilities.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index d31d3ab..dcb98dd 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -371,3 +371,14 @@ def parse_date_time_arg_exit_on_fail(date_arg, error_code=1): except (ValueError, TypeError) as ex: print(f"Can not interpret date '{date_arg}' error: {ex}") exit(error_code) + +def dehex_and_decompress_waveform_value(value): + """Decompresses the inputted waveform, assuming it is available as string. + + Args: + value (str): The string to be decompressed + + Returns: + str : A decompressed and unhexed version of the input string + """ + return zlib.decompress(binascii.unhexlify(value)) From 54f7e2a3b8fa37345781780e883fcfd0f2487ec0 Mon Sep 17 00:00:00 2001 From: Chsudeepta Date: Thu, 11 Sep 2025 11:20:08 +0100 Subject: [PATCH 03/16] Modified the XSD --- src/server_common/schema/blocks.xsd | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/server_common/schema/blocks.xsd b/src/server_common/schema/blocks.xsd index 86f1f92..6b8f7d0 100644 --- a/src/server_common/schema/blocks.xsd +++ b/src/server_common/schema/blocks.xsd @@ -28,8 +28,6 @@ - - @@ -51,8 +49,6 @@ - - From 118dcd02f6a717251dc02db9242c089c3ce89214 Mon Sep 17 00:00:00 2001 From: Chsudeepta Date: Thu, 20 Nov 2025 15:44:57 +0000 Subject: [PATCH 04/16] Fixes ruff stuffs --- src/server_common/utilities.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index dcb98dd..98d72e8 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -29,6 +29,7 @@ from server_common.common_exceptions import MaxAttemptsExceededException from server_common.loggers.logger import Logger +# ruff: noqa: ANN001, ANN201, ANN002, ANN003, ANN202, E721 # Default to base class - does not actually log anything LOGGER = Logger() _LOGGER_LOCK = threading.RLock() # To prevent message interleaving between different threads. @@ -72,8 +73,8 @@ def print_and_log(message, severity=SEVERITY.INFO, src="BLOCKSVR"): Args: message (string|exception): The message to log - severity (string, optional): Gives the severity of the message. Expected serverities are MAJOR, MINOR and INFO. - Default severity is INFO. + severity (string, optional): Gives the severity of the message. Expected serverities are + MAJOR, MINOR and INFO. Default severity is INFO. src (string, optional): Gives the source of the message. Default source is BLOCKSVR. """ with _LOGGER_LOCK: @@ -115,7 +116,8 @@ def dehex_and_decompress(value): def dehex_and_decompress_waveform(value): - """Decompresses the inputted waveform, assuming it is a array of integers representing characters (null terminated). + """Decompresses the inputted waveform, assuming it is a array of integers + representing characters (null terminated). Args: value (list[int]): The string to be decompressed @@ -181,11 +183,11 @@ def value_list_to_xml(value_list, grp, group_tag, item_tag): """Converts a list of values to corresponding xml. Args: - value_list (dist[str, dict[object, object]]): The dictionary of names and their values, values are in turn a - dictonary of names and value {name: {parameter : value, parameter : value}} + value_list (dist[str, dict[object, object]]): The dictionary of names and their values, + values are in turn a dictonary of names and value {name:{parameter:value,parameter:value}} grp (ElementTree.SubElement): The SubElement object to append the list on to - group_tag (string): The tag that corresponds to the group for the items given in the list e.g. macros - item_tag (string): The tag that corresponds to each item in the list e.g. macro + group_tag (string): The tag that corresponds to the group for the items given in the list + e.g. macros item_tag (string): The tag that corresponds to each item in the list e.g. macro """ xml_list = ElementTree.SubElement(grp, group_tag) if len(value_list) > 0: @@ -218,7 +220,7 @@ def create_pv_name(name, current_pvs, default_pv, limit=6, allow_colon=False): current_pvs (list): List of already allocated pvs default_pv (string): Basis for the PV if name is unreasonable, must be a valid PV name limit (integer): Character limit for the PV - allow_colon (bool): If True, pv name is allowed to contain colons; if False, remove the colons + allow_colon (bool): If True,pv name is allowed to contain colons;if False,remove the colons Returns: string : A valid PV @@ -344,7 +346,8 @@ def remove_from_end(string, text_to_remove): def lowercase_and_make_unique(in_list): """ - Takes a collection of strings, and returns it with all strings lowercased and with duplicates removed. + Takes a collection of strings, and returns it with all strings lowercased and with duplicates + removed. Args: in_list (List[str]): the collection of strings to operate on @@ -372,6 +375,7 @@ def parse_date_time_arg_exit_on_fail(date_arg, error_code=1): print(f"Can not interpret date '{date_arg}' error: {ex}") exit(error_code) + def dehex_and_decompress_waveform_value(value): """Decompresses the inputted waveform, assuming it is available as string. From 37309aebffcff74842923b1817bae0e3c484b4ff Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 6 Jul 2026 17:19:31 +0100 Subject: [PATCH 05/16] Added logic to convert byte[] to string --- src/server_common/utilities.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index 98d72e8..4bdd785 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -385,4 +385,5 @@ def dehex_and_decompress_waveform_value(value): Returns: str : A decompressed and unhexed version of the input string """ - return zlib.decompress(binascii.unhexlify(value)) + + return zlib.decompress(binascii.unhexlify(value)).decode("utf-8") From 081b11f0e3de06c2dc479e28c8338338d9119d46 Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 16:05:37 +0100 Subject: [PATCH 06/16] implemented review comments --- src/server_common/utilities.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index 4bdd785..554450a 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -29,7 +29,6 @@ from server_common.common_exceptions import MaxAttemptsExceededException from server_common.loggers.logger import Logger -# ruff: noqa: ANN001, ANN201, ANN002, ANN003, ANN202, E721 # Default to base class - does not actually log anything LOGGER = Logger() _LOGGER_LOCK = threading.RLock() # To prevent message interleaving between different threads. @@ -380,10 +379,13 @@ def dehex_and_decompress_waveform_value(value): """Decompresses the inputted waveform, assuming it is available as string. Args: - value (str): The string to be decompressed + value: The string to be decompressed Returns: str : A decompressed and unhexed version of the input string """ + if value and len(value) % 2 == 0: + return zlib.decompress(binascii.unhexlify(value)).decode("utf-8") + else: + raise ValueError(f"Invalid hex string: odd length ({len(value)})") - return zlib.decompress(binascii.unhexlify(value)).decode("utf-8") From 23ac5b5261c304307abc966c6699a60df6e841c4 Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 16:10:38 +0100 Subject: [PATCH 07/16] implemented review comments --- src/server_common/utilities.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index 554450a..d684ddd 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -388,4 +388,3 @@ def dehex_and_decompress_waveform_value(value): return zlib.decompress(binascii.unhexlify(value)).decode("utf-8") else: raise ValueError(f"Invalid hex string: odd length ({len(value)})") - From b2fd124caf077cfe5badf2ba3d8c62cd1558a3b5 Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 16:24:15 +0100 Subject: [PATCH 08/16] Implemented review comments --- src/server_common/utilities.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index d684ddd..bc95af5 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -1,4 +1,4 @@ -# This file is part of the ISIS IBEX application. +e is part of the ISIS IBEX application. # Copyright (C) 2012-2016 Science & Technology Facilities Council. # All rights reserved. # @@ -72,8 +72,8 @@ def print_and_log(message, severity=SEVERITY.INFO, src="BLOCKSVR"): Args: message (string|exception): The message to log - severity (string, optional): Gives the severity of the message. Expected serverities are - MAJOR, MINOR and INFO. Default severity is INFO. + severity (string, optional): Gives the severity of the message. Expected serverities are MAJOR, MINOR and INFO. + Default severity is INFO. src (string, optional): Gives the source of the message. Default source is BLOCKSVR. """ with _LOGGER_LOCK: @@ -115,8 +115,7 @@ def dehex_and_decompress(value): def dehex_and_decompress_waveform(value): - """Decompresses the inputted waveform, assuming it is a array of integers - representing characters (null terminated). + """Decompresses the inputted waveform, assuming it is a array of integers representing characters (null terminated). Args: value (list[int]): The string to be decompressed @@ -182,11 +181,11 @@ def value_list_to_xml(value_list, grp, group_tag, item_tag): """Converts a list of values to corresponding xml. Args: - value_list (dist[str, dict[object, object]]): The dictionary of names and their values, - values are in turn a dictonary of names and value {name:{parameter:value,parameter:value}} + value_list (dist[str, dict[object, object]]): The dictionary of names and their values, values are in turn a + dictonary of names and value {name: {parameter : value, parameter : value}} grp (ElementTree.SubElement): The SubElement object to append the list on to - group_tag (string): The tag that corresponds to the group for the items given in the list - e.g. macros item_tag (string): The tag that corresponds to each item in the list e.g. macro + group_tag (string): The tag that corresponds to the group for the items given in the list e.g. macros + item_tag (string): The tag that corresponds to each item in the list e.g. macro """ xml_list = ElementTree.SubElement(grp, group_tag) if len(value_list) > 0: @@ -219,7 +218,7 @@ def create_pv_name(name, current_pvs, default_pv, limit=6, allow_colon=False): current_pvs (list): List of already allocated pvs default_pv (string): Basis for the PV if name is unreasonable, must be a valid PV name limit (integer): Character limit for the PV - allow_colon (bool): If True,pv name is allowed to contain colons;if False,remove the colons + allow_colon (bool): If True, pv name is allowed to contain colons; if False, remove the colons Returns: string : A valid PV @@ -345,8 +344,7 @@ def remove_from_end(string, text_to_remove): def lowercase_and_make_unique(in_list): """ - Takes a collection of strings, and returns it with all strings lowercased and with duplicates - removed. + Takes a collection of strings, and returns it with all strings lowercased and with duplicates removed. Args: in_list (List[str]): the collection of strings to operate on @@ -374,7 +372,6 @@ def parse_date_time_arg_exit_on_fail(date_arg, error_code=1): print(f"Can not interpret date '{date_arg}' error: {ex}") exit(error_code) - def dehex_and_decompress_waveform_value(value): """Decompresses the inputted waveform, assuming it is available as string. @@ -383,6 +380,9 @@ def dehex_and_decompress_waveform_value(value): Returns: str : A decompressed and unhexed version of the input string + + Raises: + ValueError : If the supplied string is not valid hexadecimal characters """ if value and len(value) % 2 == 0: return zlib.decompress(binascii.unhexlify(value)).decode("utf-8") From 1f76b299dcd1cc08d0553c2fd11daf81ac24eeff Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 16:27:41 +0100 Subject: [PATCH 09/16] Some Fixes --- src/server_common/utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index bc95af5..92ce3eb 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -1,4 +1,4 @@ -e is part of the ISIS IBEX application. +# This file is part of the ISIS IBEX application. # Copyright (C) 2012-2016 Science & Technology Facilities Council. # All rights reserved. # From 20d4145a3b63601bbf50b60fcf78b56aa627b246 Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 16:35:35 +0100 Subject: [PATCH 10/16] Some Fixes --- src/server_common/utilities.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index 92ce3eb..de31310 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -372,6 +372,7 @@ def parse_date_time_arg_exit_on_fail(date_arg, error_code=1): print(f"Can not interpret date '{date_arg}' error: {ex}") exit(error_code) +# fmt: off def dehex_and_decompress_waveform_value(value): """Decompresses the inputted waveform, assuming it is available as string. @@ -388,3 +389,4 @@ def dehex_and_decompress_waveform_value(value): return zlib.decompress(binascii.unhexlify(value)).decode("utf-8") else: raise ValueError(f"Invalid hex string: odd length ({len(value)})") +# fmt: on From 19d02f1a4d1e88ff97c49897b5e905fe9701b685 Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 16:43:39 +0100 Subject: [PATCH 11/16] My tryst with ruff --- src/server_common/utilities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index de31310..ae617db 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -29,6 +29,7 @@ from server_common.common_exceptions import MaxAttemptsExceededException from server_common.loggers.logger import Logger +# ruff: noqa: ANN001, ANN201, ANN002, ANN003, ANN202, E721 # Default to base class - does not actually log anything LOGGER = Logger() _LOGGER_LOCK = threading.RLock() # To prevent message interleaving between different threads. @@ -372,7 +373,7 @@ def parse_date_time_arg_exit_on_fail(date_arg, error_code=1): print(f"Can not interpret date '{date_arg}' error: {ex}") exit(error_code) -# fmt: off + def dehex_and_decompress_waveform_value(value): """Decompresses the inputted waveform, assuming it is available as string. @@ -389,4 +390,3 @@ def dehex_and_decompress_waveform_value(value): return zlib.decompress(binascii.unhexlify(value)).decode("utf-8") else: raise ValueError(f"Invalid hex string: odd length ({len(value)})") -# fmt: on From 983aa9a66aa73b9a2a593927d8a30a83b51e4152 Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 16:50:35 +0100 Subject: [PATCH 12/16] The tryst continues --- src/server_common/utilities.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index ae617db..f8df324 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -29,7 +29,6 @@ from server_common.common_exceptions import MaxAttemptsExceededException from server_common.loggers.logger import Logger -# ruff: noqa: ANN001, ANN201, ANN002, ANN003, ANN202, E721 # Default to base class - does not actually log anything LOGGER = Logger() _LOGGER_LOCK = threading.RLock() # To prevent message interleaving between different threads. From f4cbd873ad41ece900806cd21ae761a44689e1aa Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 18:37:18 +0100 Subject: [PATCH 13/16] The usual boring ruff fixes --- src/server_common/utilities.py | 68 +++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index f8df324..23dabab 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -24,6 +24,7 @@ import threading import time import zlib +from typing import Any from xml.etree import ElementTree from server_common.common_exceptions import MaxAttemptsExceededException @@ -44,7 +45,7 @@ class SEVERITY(object): MAJOR = "MAJOR" -def char_waveform(length): +def char_waveform(length: object) -> dict[str, str | list[int] | Any]: """ Helper function for creating a char waveform PV. @@ -57,7 +58,7 @@ def char_waveform(length): return {"type": "char", "count": length, "value": [0]} -def set_logger(logger): +def set_logger(logger: Logger) -> None: """Sets the logger used by the print_and_log function. Args: @@ -67,13 +68,13 @@ def set_logger(logger): LOGGER = logger -def print_and_log(message, severity=SEVERITY.INFO, src="BLOCKSVR"): +def print_and_log(message: str | Any, severity=SEVERITY.INFO, src="BLOCKSVR") -> None: """Prints the specified message to the console and writes it to the log. Args: message (string|exception): The message to log - severity (string, optional): Gives the severity of the message. Expected serverities are MAJOR, MINOR and INFO. - Default severity is INFO. + severity (string, optional): Gives the severity of the message. Expected severities are + MAJOR, MINOR and INFO. Default severity is INFO. src (string, optional): Gives the source of the message. Default source is BLOCKSVR. """ with _LOGGER_LOCK: @@ -82,7 +83,7 @@ def print_and_log(message, severity=SEVERITY.INFO, src="BLOCKSVR"): LOGGER.write_to_log(message, severity, src) -def compress_and_hex(value): +def compress_and_hex(value: str) -> bytes: """Compresses the inputted string and encodes it as hex. Args: @@ -90,15 +91,18 @@ def compress_and_hex(value): Returns: bytes : A compressed and hexed version of the inputted string """ - assert type(value) == str, ( - "Non-str argument passed to compress_and_hex, maybe Python 2/3 compatibility issue\n" - "Argument was type {} with value {}".format(value.__class__.__name__, value) + ( + isinstance(value, str), + ( + "Non-str argument passed to compress_and_hex, maybe Python 2/3 compatibility issue\n" + "Argument was type {} with value {}".format(value.__class__.__name__, value) + ), ) compr = zlib.compress(bytes(value, "utf-8")) return binascii.hexlify(compr) -def dehex_and_decompress(value): +def dehex_and_decompress(value: bytes) -> bytes | Any: """Decompresses the inputted string, assuming it is in hex encoding. Args: @@ -107,14 +111,14 @@ def dehex_and_decompress(value): Returns: bytes : A decompressed version of the inputted string """ - assert type(value) == bytes, ( + assert isinstance(value, bytes), ( "Non-bytes argument passed to dehex_and_decompress, maybe Python 2/3 compatibility issue\n" "Argument was type {} with value {}".format(value.__class__.__name__, value) ) return zlib.decompress(binascii.unhexlify(value)) -def dehex_and_decompress_waveform(value): +def dehex_and_decompress_waveform(value: list) -> bytes | Any: """Decompresses the inputted waveform, assuming it is a array of integers representing characters (null terminated). Args: @@ -123,7 +127,7 @@ def dehex_and_decompress_waveform(value): Returns: bytes : A decompressed version of the inputted string """ - assert type(value) == list, ( + assert isinstance(value, list), ( "Non-list argument passed to dehex_and_decompress_waveform\n" "Argument was type {} with value {}".format(value.__class__.__name__, value) ) @@ -133,7 +137,7 @@ def dehex_and_decompress_waveform(value): return dehex_and_decompress(bytes_rep) -def convert_to_json(value): +def convert_to_json(value: object) -> str: """Converts the inputted object to JSON format. Args: @@ -145,7 +149,7 @@ def convert_to_json(value): return json.dumps(value) -def convert_from_json(value): +def convert_from_json(value: str) -> Any: """Converts the inputted string into a JSON object. Args: @@ -157,7 +161,7 @@ def convert_from_json(value): return json.loads(value) -def parse_boolean(string): +def parse_boolean(string: str) -> bool: """Parses an xml true/false value to boolean Args: @@ -177,12 +181,14 @@ def parse_boolean(string): raise ValueError(str(string) + ': Attribute must be "true" or "false"') -def value_list_to_xml(value_list, grp, group_tag, item_tag): +def value_list_to_xml( + value_list: dict, grp: ElementTree.Element, group_tag: str, item_tag: str +) -> None: """Converts a list of values to corresponding xml. Args: - value_list (dist[str, dict[object, object]]): The dictionary of names and their values, values are in turn a - dictonary of names and value {name: {parameter : value, parameter : value}} + value_list (dict[str, dict[object, object]]): The dictionary of names and their values, values are in turn a + dictionary of names and value {name: {parameter : value, parameter : value}} grp (ElementTree.SubElement): The SubElement object to append the list on to group_tag (string): The tag that corresponds to the group for the items given in the list e.g. macros item_tag (string): The tag that corresponds to each item in the list e.g. macro @@ -196,7 +202,7 @@ def value_list_to_xml(value_list, grp, group_tag, item_tag): xml_item.set(str(cn), str(cv)) -def check_pv_name_valid(name): +def check_pv_name_valid(name: str) -> bool: """Checks that text conforms to the ISIS PV naming standard Args: @@ -210,7 +216,9 @@ def check_pv_name_valid(name): return True -def create_pv_name(name, current_pvs, default_pv, limit=6, allow_colon=False): +def create_pv_name( + name: str, current_pvs: list, default_pv: str, limit: int = 6, allow_colon: bool = False +) -> str: """Uses the given name as a basis for a valid PV. Args: @@ -249,7 +257,7 @@ def create_pv_name(name, current_pvs, default_pv, limit=6, allow_colon=False): return pv -def parse_xml_removing_namespace(file_path): +def parse_xml_removing_namespace(file_path: str) -> Any: """Creates an Element object from a given xml file, removing the namespace. Args: @@ -265,7 +273,7 @@ def parse_xml_removing_namespace(file_path): return it.root -def waveform_to_string(data): +def waveform_to_string(data: Any) -> str: """ Args: data: waveform as null terminated string @@ -281,7 +289,7 @@ def waveform_to_string(data): return output -def ioc_restart_pending(ioc_pv, channel_access): +def ioc_restart_pending(ioc_pv: Any, channel_access: Any) -> Any: """Check if a particular IOC is restarting. Assumes it has suitable restart PV Args: @@ -294,7 +302,7 @@ def ioc_restart_pending(ioc_pv, channel_access): return channel_access.caget(ioc_pv + ":RESTART", as_string=True) == "Busy" -def retry(max_attempts, interval, exception): +def retry(max_attempts: int, interval: int, exception: Any): """ Attempt to perform a function a number of times in specified intervals before failing. @@ -327,7 +335,7 @@ def _wrapper(*args, **kwargs): return _tags_decorator -def remove_from_end(string, text_to_remove): +def remove_from_end(string: str | None, text_to_remove: str) -> str | None: """ Remove a String from the end of a string if it exists Args: @@ -342,7 +350,7 @@ def remove_from_end(string, text_to_remove): return string -def lowercase_and_make_unique(in_list): +def lowercase_and_make_unique(in_list: list[str]) -> set[str]: """ Takes a collection of strings, and returns it with all strings lowercased and with duplicates removed. @@ -355,7 +363,7 @@ def lowercase_and_make_unique(in_list): return {x.lower() for x in in_list} -def parse_date_time_arg_exit_on_fail(date_arg, error_code=1): +def parse_date_time_arg_exit_on_fail(date_arg: str, error_code: int = 1) -> datetime.datetime: """ Parse a date argument and exit the program with an error code if that argument is not a date Args: @@ -373,7 +381,7 @@ def parse_date_time_arg_exit_on_fail(date_arg, error_code=1): exit(error_code) -def dehex_and_decompress_waveform_value(value): +def dehex_and_decompress_waveform_value(value: str) -> str: """Decompresses the inputted waveform, assuming it is available as string. Args: @@ -388,4 +396,4 @@ def dehex_and_decompress_waveform_value(value): if value and len(value) % 2 == 0: return zlib.decompress(binascii.unhexlify(value)).decode("utf-8") else: - raise ValueError(f"Invalid hex string: odd length ({len(value)})") + raise ValueError(f"Invalid hex string: value is empty or has odd length ({len(value)})") From a455347231eac0adb216a32267c79d664d62f72d Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 19:02:37 +0100 Subject: [PATCH 14/16] The usual boring ruff fixes --- src/server_common/utilities.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index 23dabab..e4abf41 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -30,6 +30,7 @@ from server_common.common_exceptions import MaxAttemptsExceededException from server_common.loggers.logger import Logger +# ruff: noqa: ANN401 # Default to base class - does not actually log anything LOGGER = Logger() _LOGGER_LOCK = threading.RLock() # To prevent message interleaving between different threads. @@ -68,7 +69,9 @@ def set_logger(logger: Logger) -> None: LOGGER = logger -def print_and_log(message: str | Any, severity=SEVERITY.INFO, src="BLOCKSVR") -> None: +def print_and_log( + message: str | object, severity: str = SEVERITY.INFO, src: str = "BLOCKSVR" +) -> None: """Prints the specified message to the console and writes it to the log. Args: @@ -102,7 +105,7 @@ def compress_and_hex(value: str) -> bytes: return binascii.hexlify(compr) -def dehex_and_decompress(value: bytes) -> bytes | Any: +def dehex_and_decompress(value: bytes) -> bytes: """Decompresses the inputted string, assuming it is in hex encoding. Args: @@ -118,8 +121,9 @@ def dehex_and_decompress(value: bytes) -> bytes | Any: return zlib.decompress(binascii.unhexlify(value)) -def dehex_and_decompress_waveform(value: list) -> bytes | Any: - """Decompresses the inputted waveform, assuming it is a array of integers representing characters (null terminated). +def dehex_and_decompress_waveform(value: list) -> bytes: + """Decompresses the inputted waveform, assuming it is an array of integers + representing characters (null terminated). Args: value (list[int]): The string to be decompressed @@ -149,7 +153,7 @@ def convert_to_json(value: object) -> str: return json.dumps(value) -def convert_from_json(value: str) -> Any: +def convert_from_json(value: str) -> object: """Converts the inputted string into a JSON object. Args: @@ -187,10 +191,12 @@ def value_list_to_xml( """Converts a list of values to corresponding xml. Args: - value_list (dict[str, dict[object, object]]): The dictionary of names and their values, values are in turn a - dictionary of names and value {name: {parameter : value, parameter : value}} + value_list (dict[str, dict[object, object]]): The dictionary of names and their values, + values are in turn a dictionary of names and value {name: {parameter : value, + parameter : value}} grp (ElementTree.SubElement): The SubElement object to append the list on to - group_tag (string): The tag that corresponds to the group for the items given in the list e.g. macros + group_tag (string): The tag that corresponds to the group for the items given in the list + e.g. macros item_tag (string): The tag that corresponds to each item in the list e.g. macro """ xml_list = ElementTree.SubElement(grp, group_tag) @@ -302,7 +308,7 @@ def ioc_restart_pending(ioc_pv: Any, channel_access: Any) -> Any: return channel_access.caget(ioc_pv + ":RESTART", as_string=True) == "Busy" -def retry(max_attempts: int, interval: int, exception: Any): +def retry(max_attempts: int, interval: int, exception: Any) -> Any: """ Attempt to perform a function a number of times in specified intervals before failing. @@ -316,8 +322,8 @@ def retry(max_attempts: int, interval: int, exception: Any): """ - def _tags_decorator(func): - def _wrapper(*args, **kwargs): + def _tags_decorator(func: Any) -> Any: + def _wrapper(*args: Any, **kwargs: Any) -> Any: attempts = 0 last_exception = ValueError("Max attempts should be > 0, it is {}".format(max_attempts)) while attempts < max_attempts: @@ -352,7 +358,8 @@ def remove_from_end(string: str | None, text_to_remove: str) -> str | None: def lowercase_and_make_unique(in_list: list[str]) -> set[str]: """ - Takes a collection of strings, and returns it with all strings lowercased and with duplicates removed. + Takes a collection of strings, and returns it with all strings lowercased and with duplicates + removed. Args: in_list (List[str]): the collection of strings to operate on From e40b5b6063a0bf17228c10fd0a32cf6120b5319d Mon Sep 17 00:00:00 2001 From: vux62295 Date: Mon, 20 Jul 2026 19:07:10 +0100 Subject: [PATCH 15/16] The usual boring ruff fixes --- src/server_common/utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index e4abf41..7f47aa2 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -232,7 +232,7 @@ def create_pv_name( current_pvs (list): List of already allocated pvs default_pv (string): Basis for the PV if name is unreasonable, must be a valid PV name limit (integer): Character limit for the PV - allow_colon (bool): If True, pv name is allowed to contain colons; if False, remove the colons + allow_colon (bool): If True, pv name is allowed to contain colons; else, remove the colons Returns: string : A valid PV From ad290244788d8fee6523694178c86a53a130fe70 Mon Sep 17 00:00:00 2001 From: vux62295 Date: Tue, 21 Jul 2026 14:11:36 +0100 Subject: [PATCH 16/16] Updated type annotations --- src/server_common/utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server_common/utilities.py b/src/server_common/utilities.py index 7f47aa2..90abe34 100644 --- a/src/server_common/utilities.py +++ b/src/server_common/utilities.py @@ -388,7 +388,7 @@ def parse_date_time_arg_exit_on_fail(date_arg: str, error_code: int = 1) -> date exit(error_code) -def dehex_and_decompress_waveform_value(value: str) -> str: +def dehex_and_decompress_waveform_value(value: str | bytes) -> str: """Decompresses the inputted waveform, assuming it is available as string. Args: