From e4cb48eb6dea93ed7834b72d476da2726df5ecb4 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Tue, 28 Oct 2025 17:20:40 +0000 Subject: [PATCH] Stricter rules on pytest.raises, requiring match for generic error types Closes #560 --- pyproject.toml | 2 -- tests/test_scan_planners.py | 11 ++++++----- tests/test_stack.py | 36 ++++++++++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cfef2a23..706c95fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -136,8 +136,6 @@ ignore = [ "PLR2004", # Ignore magic values in comparison for now. It doesn't seem we can set # allowed magic values and a number of our magic values are not really # magic (such as 255 when doing uint8 maths) - "PT011", # Ignore pytest.raises being used on too general expectations without a - # match. We may need to revisit this. "SIM105", # Not enforcing use of `contextlib.suppress(NotConnectedToServerError)` # instead of `try`-`except`-`pass` ] diff --git a/tests/test_scan_planners.py b/tests/test_scan_planners.py index f18d6c81..4d87a0d6 100644 --- a/tests/test_scan_planners.py +++ b/tests/test_scan_planners.py @@ -15,7 +15,7 @@ def test_enforce_xy_tuple(): """Check that 2 value tuples (or ValueErrors) are always returned.""" bad_len_vals = [[1], [], (1,), (2, 4, 4), [1, 4, 5, 7]] for value in bad_len_vals: - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="2 value tuple expected"): scan_planners.enforce_xy_tuple(value) bad_type_vals = ["hi", 1, {"this": "that"}, {1, 2}] @@ -31,7 +31,7 @@ def test_enforce_xyz_tuple(): """Check that 3 value tuples (or ValueErrors) are always returned.""" bad_len_vals = [[1], [], (1,), (2, 4), [1, 4, 5, 7]] for value in bad_len_vals: - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="3 value tuple expected"): scan_planners.enforce_xyz_tuple(value) bad_type_vals = ["hi!", 1, {"this": "that"}, {1, 2, 3}] @@ -66,7 +66,7 @@ def test_v_basic_smart_spiral(): assert z_pos is None # Try to mark location as imaged with only xy_position - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="3 value tuple expected"): planner.mark_location_visited(xy_pos, imaged=False, focused=False) # scan still not complete assert not planner.scan_complete @@ -87,7 +87,8 @@ def test_bad_smart_spiral_settings(): initial_position = (100, 50) # Class init should raise error if no planner_settings dictionary set - with pytest.raises(ValueError): + msg = "SmartSpiral requires a planner_settings dictionary with keys" + with pytest.raises(ValueError, match=msg): scan_planners.SmartSpiral(initial_position=initial_position) planner_settings = {"dx": 50, "dy": 50, "max_dist": 10000} @@ -108,7 +109,7 @@ def test_bad_smart_spiral_settings(): for badkey in keys: bad_planner_settings = copy(planner_settings) bad_planner_settings[badkey] = "I can't be converted to an int" - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="invalid literal for int()"): scan_planners.SmartSpiral( initial_position=initial_position, planner_settings=bad_planner_settings ) diff --git a/tests/test_stack.py b/tests/test_stack.py index 912e588d..747e2eb1 100644 --- a/tests/test_stack.py +++ b/tests/test_stack.py @@ -86,7 +86,12 @@ def test_stack_params_not_enough_test_images(save_ims, extra_ims): For arguments see test_stack_params_validation """ - with pytest.raises(ValueError): + # Depending on the values multiple messages are possible + match = ( + "(Can't test for focus with fewer than 3 images|" + "Can't save more images than the minimum number tested)" + ) + with pytest.raises(ValueError, match=match): StackParams( stack_dz=50, images_to_save=save_ims, @@ -106,7 +111,12 @@ def test_stack_params_negative_images_to_save(save_ims, extra_ims): For arguments see test_stack_params_validation """ - with pytest.raises(ValueError): + # Depending on the values multiple messages are possible + match = ( + "(Can't test for focus with fewer than 3 images|" + "Images to save must be positive and odd)" + ) + with pytest.raises(ValueError, match=match): StackParams( stack_dz=50, images_to_save=save_ims, @@ -126,7 +136,13 @@ def test_even_min_images_to_test(save_ims, extra_ims): For arguments see test_stack_params_validation """ - with pytest.raises(ValueError): + # Depending on the values multiple messages are possible + match = ( + "(Can't test for focus with fewer than 3 images|" + "Testing with more than 9 images is|" # may give more than 9 which errors first + "Minimum number of images to test should be positive and odd)" + ) + with pytest.raises(ValueError, match=match): StackParams( stack_dz=50, images_to_save=save_ims, @@ -146,7 +162,11 @@ def test_even_images_to_save(save_ims, extra_ims): For arguments see test_stack_params_validation """ - with pytest.raises(ValueError): + match = ( + "(Can't test for focus with fewer than 3 images|" + "Images to save must be positive and odd)" + ) + with pytest.raises(ValueError, match=match): StackParams( stack_dz=50, images_to_save=save_ims, @@ -232,13 +252,13 @@ def test_retrieval_of_captures(start): assert _get_capture_by_id(captures, buffer_id) is capture # Check errors are raised when supplying ids that aren't in the list - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="No capture has a buffer id of"): _get_capture_index_by_id(captures, start - 1) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="No capture has a buffer id of"): _get_capture_index_by_id(captures, start + 21) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="No capture has a buffer id of"): _get_capture_by_id(captures, start - 1) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="No capture has a buffer id of"): _get_capture_by_id(captures, start + 21)