diff --git a/brainglobe_utils/general/list.py b/brainglobe_utils/general/list.py index fc72508..a2faaa7 100644 --- a/brainglobe_utils/general/list.py +++ b/brainglobe_utils/general/list.py @@ -1,3 +1,5 @@ +from natsort import natsorted + def remove_empty_string(str_list): """ Removes any empty strings from a list of strings @@ -10,3 +12,50 @@ def remove_empty_string(str_list): def unique_elements_lists(list_in): """return the unique elements in a list""" return list(dict.fromkeys(list_in)) + +def check_unique_list(in_list, natural_sort=True): + """ + Checks if all the items in a list are unique or not + :param list in_list: Input list + :param bool natural_sort: Sort the resulting items naturally + (default: True) + :return: True/False and a list of any repeated values + """ + unique = set(in_list) + repeated_items = [] + + for item in unique: + count = in_list.count(item) + if count > 1: + repeated_items.append(item) + + if repeated_items: + if natural_sort: + repeated_items = natsorted(repeated_items) + return False, repeated_items + else: + return True, [] + + +def common_member(a, b, natural_sort=True): + """ + Checks if two lists (or sets) have a common member, and if so, returns + the common members. + :param a: First list (or set) + :param b: Second list (or set) + :param bool natural_sort: Sort the resulting items naturally + (default: True) + :return: True/False and the list of values + """ + a_set = set(a) + b_set = set(b) + intersection = list(a_set.intersection(b_set)) + if len(intersection) > 0: + result = True + else: + result = False + + if natural_sort: + intersection = natsorted(intersection) + + return result, intersection diff --git a/brainglobe_utils/general/system.py b/brainglobe_utils/general/system.py index d84f56b..e2fea8c 100644 --- a/brainglobe_utils/general/system.py +++ b/brainglobe_utils/general/system.py @@ -13,6 +13,7 @@ from tqdm import tqdm from brainglobe_utils.general.string import get_text_lines +from brainglobe_utils.general.exceptions import CommandLineInputError # On Windows, max_workers must be less than or equal to 61 # https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor @@ -349,3 +350,31 @@ def delete_directory_contents(directory, progress=False): else: for f in files: os.remove(os.path.join(directory, f)) + +def check_path_exists(file): + """ + Returns True is a file exists, otherwise throws a FileNotFoundError + :param file: Input file + :return: True, if the file exists + """ + file = Path(file) + if file.exists(): + return True + else: + raise FileNotFoundError + + +def catch_input_file_error(path): + """ + Catches if an input path doesn't exist, and returns an informative error + :param path: Input file path + default) + """ + try: + check_path_exists(path) + except FileNotFoundError: + message = ( + "File path: '{}' cannot be found. Please check your input " + "arguments.".format(path) + ) + raise CommandLineInputError(message) diff --git a/tests/tests/test_general/test_list.py b/tests/tests/test_general/test_list.py index 5efae3d..3dad0ac 100644 --- a/tests/tests/test_general/test_list.py +++ b/tests/tests/test_general/test_list.py @@ -1,10 +1,13 @@ from brainglobe_utils.general import list as list_tools +a = [1, "a", 10, 30] +b = [30, 10, "c", "d"] -list_with_empty = ["test1", "test 2", " ", "", "test4", ""] +list_with_empty = ["test1", "test 2", " ", "", "test4", ""] list_without_empty = ["test1", "test 2", " ", "test4"] + def test_remove_empty_string(): assert ( list_tools.remove_empty_string(list_with_empty) == list_without_empty @@ -15,3 +18,12 @@ def test_unique_elements_list(): list_in = [1, 2, 2, "a", "b", 1, "a", "dog"] unique_list = [1, 2, "a", "b", "dog"] assert list_tools.unique_elements_lists(list_in) == unique_list + +def test_check_unique_list(): + a = [1, "a", 10, 30] + assert (True, []) == list_tools.check_unique_list(a) + repeating_list = [1, 2, 3, 3, "dog", "cat", "dog"] + assert (False, [3, "dog"]) == list_tools.check_unique_list(repeating_list) + +def test_common_member(): + assert (True, [10, 30]) == list_tools.common_member(a, b) \ No newline at end of file diff --git a/tests/tests/test_general/test_system.py b/tests/tests/test_general/test_system.py index 7feb3f2..77909e4 100644 --- a/tests/tests/test_general/test_system.py +++ b/tests/tests/test_general/test_system.py @@ -9,6 +9,7 @@ from brainglobe_utils.general import system from brainglobe_utils.general.string import get_text_lines +from brainglobe_utils.general.exceptions import CommandLineInputError data_dir = Path("tests", "data") cubes_dir = data_dir / "cubes" @@ -326,3 +327,31 @@ def test_delete_directory_contents(tmp_path): system.delete_directory_contents(delete_dir, progress=False) assert os.listdir(delete_dir) == [] + +def write_file_single_size(directory, file_size): + with open(os.path.join(directory, str(file_size)), "wb") as fout: + fout.write(os.urandom(file_size)) + +def test_check_path_exists(tmpdir): + num = 10 + tmpdir = str(tmpdir) + + assert system.check_path_exists(os.path.join(tmpdir)) + no_exist_dir = os.path.join(tmpdir, "i_dont_exist") + with pytest.raises(FileNotFoundError): + assert system.check_path_exists(no_exist_dir) + + write_file_single_size(tmpdir, num) + assert system.check_path_exists(os.path.join(tmpdir, str(num))) + with pytest.raises(FileNotFoundError): + assert system.check_path_exists(os.path.join(tmpdir, "20")) + + +def test_catch_input_file_error(tmpdir): + tmpdir = str(tmpdir) + # check no error is raised: + system.catch_input_file_error(tmpdir) + + no_exist_dir = os.path.join(tmpdir, "i_dont_exist") + with pytest.raises(CommandLineInputError): + system.catch_input_file_error(no_exist_dir)