Repository: PlasmaPy/PlasmaPy

Issue:
Pytest deprecation of "message" parameter to raises
Followup to #599. https://github.com/pytest-dev/pytest/issues/3974 is a thing, thus we need to do something like https://github.com/PlasmaPy/PlasmaPy/pull/600/commits/e9cd7c593ed1e766993d3daaf7994e046a768242 except without blowing all of pytest-helpers up.


Discussion:
I believe the way we used the `message` keyword was consistent with the intended use and should not be replaced with the `match` keyword, so I think we need to do something like [this](https://github.com/pytest-dev/pytest/issues/3974#issuecomment-453994294):
```
with pytest.raises(SomeException):
    something_that_raises_that_exception()
    pytest.fail("SomeException should have been raised")
```

Correction: We're using both usages.  I'm attempting to fix them now.

Resolving patch:
diff --git a/plasmapy/utils/checks.py b/plasmapy/utils/checks.py
--- a/plasmapy/utils/checks.py
+++ b/plasmapy/utils/checks.py
@@ -50,7 +50,7 @@ def check_quantity(**validations: Dict[str, bool]):
     astropy.units.core.UnitConversionError: The argument x to func should be a Quantity with the following units: m
 
     >>> import pytest    # to show the UnitsWarning
-    >>> with pytest.warns(u.UnitsWarning, message="Assuming units of m."):
+    >>> with pytest.warns(u.UnitsWarning, match="Assuming units of m."):
     ...     func(1)
     <Quantity 1. m>
 
@@ -106,7 +106,7 @@ def check_quantity(**validations: Dict[str, bool]):
 
     Notes
     -----
-    This functionality may end up becoming deprecated due to 
+    This functionality may end up becoming deprecated due to
     noncompliance with the `IEEE 754 standard
     <https://en.wikipedia.org/wiki/IEEE_754#Exception_handling>`_
     and in favor of `~astropy.units.quantity_input`.


Test patch:
diff --git a/plasmapy/atomic/tests/test_atomic.py b/plasmapy/atomic/tests/test_atomic.py
--- a/plasmapy/atomic/tests/test_atomic.py
+++ b/plasmapy/atomic/tests/test_atomic.py
@@ -632,13 +632,14 @@ def test_half_life_u_220():
 
     isotope_without_half_life_data = "No-248"
 
-    with pytest.raises(MissingAtomicDataError, message=(
+    with pytest.raises(MissingAtomicDataError):
+        half_life(isotope_without_half_life_data)
+        pytest.fail(
             f"This test assumes that {isotope_without_half_life_data} does "
             f"not have half-life data.  If half-life data is added for this "
             f"isotope, then a different isotope that does not have half-life "
-            f"data should be chosen for this test.")):
-
-        half_life(isotope_without_half_life_data)
+            f"data should be chosen for this test."
+        )
 
 
 def test_known_common_stable_isotopes_cases():
@@ -692,9 +693,9 @@ def test_known_common_stable_isotopes_error(func):
     """Test that `known_isotopes`, `common_isotopes`, and
     `stable_isotopes` raise an `~plasmapy.utils.InvalidElementError` for
     neutrons."""
-    with pytest.raises(InvalidElementError, message=(
-            f"{func} is not raising a ElementError for neutrons.")):
+    with pytest.raises(InvalidElementError):
         func('n')
+        pytest.fail(f"{func} is not raising a ElementError for neutrons.")
 
 
 def test_isotopic_abundance():
@@ -708,9 +709,9 @@ def test_isotopic_abundance():
     with pytest.warns(AtomicWarning):
         isotopic_abundance('Og', 294)
 
-    with pytest.raises(InvalidIsotopeError, message="No exception raised for "
-                                                    "neutrons"):
+    with pytest.raises(InvalidIsotopeError):
         isotopic_abundance('neutron')
+        pytest.fail("No exception raised for neutrons.")
 
     with pytest.raises(InvalidParticleError):
         isotopic_abundance('Og-2')
diff --git a/plasmapy/atomic/tests/test_ionization_state.py b/plasmapy/atomic/tests/test_ionization_state.py
--- a/plasmapy/atomic/tests/test_ionization_state.py
+++ b/plasmapy/atomic/tests/test_ionization_state.py
@@ -532,11 +532,11 @@ def test_n_e(self):
             "number_densities was set.")
 
     def test_that_negative_density_raises_error(self):
-        with pytest.raises(AtomicError, message="cannot be negative"):
+        with pytest.raises(AtomicError, match="cannot be negative"):
             self.instance.number_densities = u.Quantity([-0.1, 0.2], unit=u.m**-3)
 
     def test_incorrect_number_of_charge_states_error(self):
-        with pytest.raises(AtomicError, message="Incorrect number of charge states"):
+        with pytest.raises(AtomicError, match="Incorrect number of charge states"):
             self.instance.number_densities = u.Quantity([0.1, 0.2, 0.3], unit=u.m**-3)
 
     def test_incorrect_units_error(self):
diff --git a/plasmapy/atomic/tests/test_ionization_states.py b/plasmapy/atomic/tests/test_ionization_states.py
--- a/plasmapy/atomic/tests/test_ionization_states.py
+++ b/plasmapy/atomic/tests/test_ionization_states.py
@@ -476,8 +476,9 @@ def test_attribute_exceptions(self, attribute, invalid_value, expected_exception
         command = f"self.instance.{attribute} = {invalid_value}"
         errmsg = f"No {expected_exception} was raised for command\n\n: {command}"
 
-        with pytest.raises(expected_exception, message=errmsg):
+        with pytest.raises(expected_exception):
             exec(command)
+            pytest.fail(errmsg)
 
     def test_setting_ionic_fractions_for_single_element(self):
         """
@@ -512,8 +513,9 @@ def test_setting_invalid_ionfracs(self, key, invalid_fracs, expected_exception):
         errmsg = (
             f"No {expected_exception} is raised when trying to assign "
             f"{invalid_fracs} to {key} in an IonizationStates instance.")
-        with pytest.raises(expected_exception, message=errmsg):
+        with pytest.raises(expected_exception):
             self.instance[key] = invalid_fracs
+            pytest.fail(errmsg)
 
     def test_setting_incomplete_abundances(self):
         new_abundances = {'H': 1, 'He': 0.1, 'Fe': 1e-5, 'Au': 1e-8}  # missing lithium
diff --git a/plasmapy/atomic/tests/test_parsing.py b/plasmapy/atomic/tests/test_parsing.py
--- a/plasmapy/atomic/tests/test_parsing.py
+++ b/plasmapy/atomic/tests/test_parsing.py
@@ -254,21 +254,22 @@ def test_parse_InvalidParticleErrors(arg, kwargs):
     r"""Tests that _parse_and_check_atomic_input raises an
     InvalidParticleError when the input does not correspond
     to a real particle."""
-    with pytest.raises(InvalidParticleError, message=(
-            "An InvalidParticleError was expected to be raised by "
-            f"{_call_string(arg, kwargs)}, but no exception was raised.")):
+    with pytest.raises(InvalidParticleError):
         _parse_and_check_atomic_input(arg, **kwargs)
-
+        pytest.fail(
+            "An InvalidParticleError was expected to be raised by "
+            f"{_call_string(arg, kwargs)}, but no exception was raised.")
 
 @pytest.mark.parametrize('arg', ParticleZoo.everything - {'p+'})
 def test_parse_InvalidElementErrors(arg):
     r"""Tests that _parse_and_check_atomic_input raises an
     InvalidElementError when the input corresponds to a valid
     particle but not a valid element, isotope, or ion."""
-    with pytest.raises(InvalidElementError, message=(
-            "An InvalidElementError was expected to be raised by "
-            f"{_call_string(arg)}, but no exception was raised.")):
+    with pytest.raises(InvalidElementError):
         _parse_and_check_atomic_input(arg)
+        pytest.fail(
+            "An InvalidElementError was expected to be raised by "
+            f"{_call_string(arg)}, but no exception was raised.")
 
 
 # (arg, kwargs, num_warnings)
@@ -285,10 +286,15 @@ def test_parse_InvalidElementErrors(arg):
 def test_parse_AtomicWarnings(arg, kwargs, num_warnings):
     r"""Tests that _parse_and_check_atomic_input issues an AtomicWarning
     under the required conditions.  """
-    with pytest.warns(AtomicWarning, message=(
-            f"No AtomicWarning was issued by {_call_string(arg, kwargs)} but "
-            f"the expected number of warnings was {num_warnings}")) as record:
+
+    with pytest.warns(AtomicWarning) as record:
         _parse_and_check_atomic_input(arg, **kwargs)
+        if not record:
+            pytest.fail(
+                f"No AtomicWarning was issued by "
+                f"{_call_string(arg, kwargs)} but the expected number "
+                f"of warnings was {num_warnings}")
+
     assert len(record) == num_warnings, (
         f"The number of AtomicWarnings issued by {_call_string(arg, kwargs)} "
         f"was {len(record)}, which differs from the expected number "
diff --git a/plasmapy/atomic/tests/test_particle_class.py b/plasmapy/atomic/tests/test_particle_class.py
--- a/plasmapy/atomic/tests/test_particle_class.py
+++ b/plasmapy/atomic/tests/test_particle_class.py
@@ -529,12 +529,12 @@ def test_Particle_errors(arg, kwargs, attribute, exception):
     Test that the appropriate exceptions are raised during the creation
     and use of a `~plasmapy.atomic.Particle` object.
     """
-    with pytest.raises(exception, message=(
+    with pytest.raises(exception):
+        exec(f'Particle(arg, **kwargs){attribute}')
+        pytest.fail(
             f"The following command: "
             f"\n\n  {call_string(Particle, arg, kwargs)}{attribute}\n\n"
-            f"did not raise a {exception.__name__} as expected")):
-        exec(f'Particle(arg, **kwargs){attribute}')
-
+            f"did not raise a {exception.__name__} as expected")
 
 # arg, kwargs, attribute, exception
 test_Particle_warning_table = [
@@ -551,12 +551,13 @@ def test_Particle_warnings(arg, kwargs, attribute, warning):
     Test that the appropriate warnings are issued during the creation
     and use of a `~plasmapy.atomic.Particle` object.
     """
-    with pytest.warns(warning, message=(
-            f"The following command: "
-            f"\n\n >>> {call_string(Particle, arg, kwargs)}{attribute}\n\n"
-            f"did not issue a {warning.__name__} as expected")):
+    with pytest.warns(warning) as record:
         exec(f'Particle(arg, **kwargs){attribute}')
-
+        if not record:
+            pytest.fail(
+                f"The following command: "
+                f"\n\n >>> {call_string(Particle, arg, kwargs)}{attribute}\n\n"
+                f"did not issue a {warning.__name__} as expected")
 
 def test_Particle_cmp():
     """Test ``__eq__`` and ``__ne__`` in the Particle class."""
diff --git a/plasmapy/atomic/tests/test_particle_input.py b/plasmapy/atomic/tests/test_particle_input.py
--- a/plasmapy/atomic/tests/test_particle_input.py
+++ b/plasmapy/atomic/tests/test_particle_input.py
@@ -100,9 +100,9 @@ def test_particle_input_errors(func, kwargs, expected_error):
     Test that functions decorated with particle_input raise the
     expected errors.
     """
-    with pytest.raises(expected_error, message=(
-            f"{func} did not raise {expected_error} with kwargs = {kwargs}")):
+    with pytest.raises(expected_error):
         func(**kwargs)
+        pytest.fail(f"{func} did not raise {expected_error} with kwargs = {kwargs}")
 
 
 class Test_particle_input:
@@ -439,20 +439,23 @@ def func_none_shall_not_pass_with_tuple(particles: (Particle, Particle)) -> \
     def func_none_shall_not_pass_with_list(particles: [Particle]) -> [Particle]:
         return particles
 
-    with pytest.raises(TypeError, message=(
-            "The none_shall_pass keyword in the particle_input decorator is "
-            "set to False, but is not raising a TypeError.")):
+    with pytest.raises(TypeError):
         func_none_shall_not_pass(None)
+        pytest.fail(
+            "The none_shall_pass keyword in the particle_input "
+            "decorator is set to False, but is not raising a TypeError.")
 
-    with pytest.raises(TypeError, message=(
-            "The none_shall_pass keyword in the particle_input decorator is "
-            "set to False, but is not raising a TypeError.")):
+    with pytest.raises(TypeError):
         func_none_shall_not_pass_with_tuple(('He', None))
+        pytest.fail(
+            "The none_shall_pass keyword in the particle_input "
+            "decorator is set to False, but is not raising a TypeError.")
 
-    with pytest.raises(TypeError, message=(
-            "The none_shall_pass keyword in the particle_input decorator is "
-            "set to False, but is not raising a TypeError.")):
+    with pytest.raises(TypeError):
         func_none_shall_not_pass(('He', None))
+        pytest.fail(
+            "The none_shall_pass keyword in the particle_input "
+            "decorator is set to False, but is not raising a TypeError.")
 
 
 def test_optional_particle_annotation_argname():
@@ -500,15 +503,20 @@ def func_not_optional_particle_with_tuple(particles: (Particle, Optional[Particl
     def func_not_optional_particle_with_list(particles: [Particle]) -> [Particle]:
         return particles
 
-    with pytest.raises(TypeError, message=(
-            "The particle keyword in the particle_input decorator received a "
-            "None instead of a Particle, but is not raising a TypeError.")):
+    with pytest.raises(TypeError):
         func_not_optional_particle_with_tuple((None, 'He'))
+        pytest.fail(
+            "The particle keyword in the particle_input decorator "
+            "received None instead of a Particle, but is not raising a "
+            "TypeError.")
+
+    with pytest.raises(TypeError):
 
-    with pytest.raises(TypeError, message=(
-            "The particle keyword in the particle_input decorator received a "
-            "None instead of a Particle, but is not raising a TypeError.")):
         func_not_optional_particle_with_list(('He', None))
+        pytest.fail(
+            "The particle keyword in the particle_input decorator "
+            "received None instead of a Particle, but is not raising a "
+            "TypeError.")
 
 
 # TODO: The following tests might be able to be cleaned up and/or
@@ -575,11 +583,12 @@ def test_not_element(particle):
     `~plasmapy.atomic.Particle` is named 'element', but the annotated
     argument ends up not being an element, isotope, or ion.
     """
-    with pytest.raises(InvalidElementError, message=(
-            "@particle_input is not raising an InvalidElementError for "
-            f"{repr(particle)} even though the annotated argument is named "
-            "'element'.")):
+    with pytest.raises(InvalidElementError):
         function_with_element_argument(particle)
+        pytest.fail(
+            "@particle_input is not raising an InvalidElementError for "
+            f"{repr(particle)} even though the annotated argument is "
+            f"named 'element'.")
 
 
 @pytest.mark.parametrize('isotope', is_isotope)