Repository: PlasmaPy/PlasmaPy

Issue:
Change `Particle.is_category.valid_categories` to `plasmapy.particles.particle_class.valid_categories`
Following a discussion on #1710.

Discussion:
review-notebook-app[bot]: Check out this pull request on&nbsp; <a href="https://app.reviewnb.com/PlasmaPy/PlasmaPy/pull/1720"><img align="absmiddle"  alt="ReviewNB" height="28" class="BotMessageButtonImage" src="https://raw.githubusercontent.com/ReviewNB/support/master/images/button_reviewnb.png"/></a> 

 See visual diffs & provide feedback on Jupyter Notebooks. 

---

 <i>Powered by <a href='https://www.reviewnb.com/?utm_source=gh'>ReviewNB</a></i>
codecov[bot]: # [Codecov](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1720?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PlasmaPy) Report
> Merging [#1720](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1720?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PlasmaPy) (1070964) into [main](https://codecov.io/gh/PlasmaPy/PlasmaPy/commit/eceb9c17ecafd705d69c276b84691c3909fe3a1c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PlasmaPy) (eceb9c1) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head 1070964 differs from pull request most recent head 0abf088. Consider uploading reports for the commit 0abf088 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main    #1720      +/-   ##
==========================================
+ Coverage   98.08%   98.09%   +0.01%     
==========================================
  Files          87       87              
  Lines        8102     8099       -3     
==========================================
- Hits         7947     7945       -2     
+ Misses        155      154       -1     
```


| [Impacted Files](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1720?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PlasmaPy) | Coverage Δ | |
|---|---|---|
| [plasmapy/particles/particle\_class.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1720/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PlasmaPy#diff-cGxhc21hcHkvcGFydGljbGVzL3BhcnRpY2xlX2NsYXNzLnB5) | `99.33% <100.00%> (-0.01%)` | :arrow_down: |
| [plasmapy/particles/particle\_collections.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1720/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PlasmaPy#diff-cGxhc21hcHkvcGFydGljbGVzL3BhcnRpY2xlX2NvbGxlY3Rpb25zLnB5) | `100.00% <0.00%> (+0.83%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PlasmaPy). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PlasmaPy)

namurphy: pre-commit.ci autofix
namurphy: Before merging, need to make sure that the `valid_categories` link in the example notebook shows up okay.

Patch:
diff --git a/changelog/1720.breaking.rst b/changelog/1720.breaking.rst
new file mode 100644
index 00000000..427cc848
--- /dev/null
+++ b/changelog/1720.breaking.rst
@@ -0,0 +1,3 @@
+Moved the ``valid_categories`` attribute of
+`~plasmapy.particles.particle_class.AbstractPhysicalParticle.is_category`
+to `plasmapy.particles.particle_class.valid_categories`.
diff --git a/docs/notebooks/getting_started/particles.ipynb b/docs/notebooks/getting_started/particles.ipynb
index 818d64db..b9d9815d 100644
--- a/docs/notebooks/getting_started/particles.ipynb
+++ b/docs/notebooks/getting_started/particles.ipynb
@@ -25,7 +25,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "from plasmapy.particles import *"
+    "from plasmapy.particles import *\n",
+    "from plasmapy.particles.particle_class import valid_categories"
    ]
   },
   {
@@ -748,10 +749,9 @@
    "id": "a03c8153",
    "metadata": {},
    "source": [
-    "[is_category()]: ../../api/plasmapy.particles.particle_class.Particle.rst#plasmapy.particles.particle_class.Particle.is_category\n",
-    "[Particle]: ../../api/plasmapy.particles.particle_class.Particle.rst\n",
+    "[valid_categories]: ../../api/plasmapy.particles.particle_class.valid_categories.rst\n",
     "\n",
-    "The `valid_categories` attribute of [is_category()] for any [Particle] gives a set containing all valid categories."
+    "All valid particle categories are included in [valid_categories]."
    ]
   },
   {
@@ -761,7 +761,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "print(electron.is_category.valid_categories)"
+    "print(valid_categories)"
    ]
   },
   {
diff --git a/docs/particles/particle_class.rst b/docs/particles/particle_class.rst
index 29fa6079..838b63a7 100644
--- a/docs/particles/particle_class.rst
+++ b/docs/particles/particle_class.rst
@@ -101,8 +101,8 @@ the particle.
 >>> sorted(electron.categories)
 ['charged', 'electron', 'fermion', 'lepton', 'matter', 'stable']
 
-Membership of a particle within a category may be checked using the
-`~plasmapy.particles.particle_class.Particle.is_category` method.
+Membership of a particle within a category may be checked using
+|is_category|.
 
 >>> alpha.is_category('lepton')
 False
@@ -123,18 +123,7 @@ False
 >>> alpha.is_category(exclude='lepton')
 True
 
-Calling the `~plasmapy.particles.particle_class.Particle.is_category`
-method with no arguments returns a set containing all of the valid
-categories for any particle.  Valid categories include:
-``"actinide"``, ``"alkali metal"``, ``"alkaline earth metal"``,
-``"antibaryon"``, ``"antilepton"``, ``"antimatter"``,
-``"antineutrino"``, ``"baryon"``, ``"boson"``, ``"charged"``,
-``"custom"``, ``"electron"``, ``"element"``, ``"fermion"``,
-``"halogen"``, ``"ion"``, ``"isotope"``, ``"lanthanide"``, ``"lepton"``,
-``"matter"``, ``"metal"``, ``"metalloid"``, ``"neutrino"``,
-``"neutron"``, ``"noble gas"``, ``"nonmetal"``, ``"positron"``,
-``"post-transition metal"``, ``"proton"``, ``"stable"``,
-``"transition metal"``, ``"uncharged"``, and ``"unstable"``.
+Valid particle categories are listed in the docstring for |is_category|.
 
 .. _particle-class-conditionals-equality:
 
@@ -193,3 +182,5 @@ of a |Particle| object.
 Particle("e+")
 >>> antimuon.antiparticle
 Particle("mu-")
+
+.. |is_category| replace:: `~plasmapy.particles.particle_class.AbstractPhysicalParticle.is_category`
diff --git a/plasmapy/particles/particle_class.py b/plasmapy/particles/particle_class.py
index c55cd596..dd0f7b1b 100644
--- a/plasmapy/particles/particle_class.py
+++ b/plasmapy/particles/particle_class.py
@@ -10,6 +10,7 @@
     "Particle",
     "ParticleLike",
     "molecule",
+    "valid_categories",
 ]
 
 import astropy.constants as const
@@ -74,12 +75,19 @@
 
 _specific_particle_categories = {"electron", "positron", "proton", "neutron"}
 
-_valid_categories = (
+valid_categories = (
     _periodic_table_categories
     | _classification_categories
     | _atomic_property_categories
     | _specific_particle_categories
 )
+r"""
+A `set` containing all valid particle categories.
+
+See Also
+--------
+:py:meth:`~plasmapy.particles.particle_class.AbstractPhysicalParticle.is_category`
+"""
 
 
 def _category_errmsg(particle, category: str) -> str:
@@ -218,8 +226,7 @@ def is_category(
         any_of: Union[str, Iterable[str]] = None,
         exclude: Union[str, Iterable[str]] = None,
     ) -> bool:
-        """
-        Determine if the particle meets categorization criteria.
+        """Determine if the particle meets categorization criteria.
 
         Return `True` if the particle is consistent with the provided
         categories, and `False` otherwise.
@@ -230,23 +237,39 @@ def is_category(
             Required categories in the form of one or more `str` objects
             or an iterable.
 
-        require : `str` or iterable providing `str` objects, optional, keyword-only
-            One or more categories.  This method will return `False` if
-            the particle does not belong to all of these categories.
-
-        any_of : `str` or iterable providing `str` objects, optional, keyword-only
-            One or more categories. This method will return `False` if
-            the particle does not belong to at least one of these
+        require : `str` or iterable of `str`, optional, |keyword-only|
+            One or more particle categories. This method will return
+            `False` if the particle does not belong to all of these
             categories.
 
-        exclude : `str` or iterable providing `str` objects, optional, keyword-only
-            One or more categories.  This method will return `False` if
-            the particle belongs to any of these categories.
+        any_of : `str` or iterable of `str`, optional, |keyword-only|
+            One or more particle categories. This method will return
+            `False` if the particle does not belong to at least one of
+            these categories.
+
+        exclude : `str` or iterable of `str`, optional, |keyword-only|
+            One or more particle categories.  This method will return
+            `False` if the particle belongs to any of these categories.
+
+        See Also
+        --------
+        ~plasmapy.particles.particle_class.valid_categories :
+            A `set` containing all valid particle categories.
 
         Notes
         -----
-        A `set` containing all valid categories may be accessed by the
-        ``valid_categories`` attribute of ``is_category``.
+        Valid particle categories are given in
+        `~plasmapy.particles.particle_class.valid_categories` and
+        include: ``"actinide"``, ``"alkali metal"``, ``"alkaline earth
+        metal"``, ``"antibaryon"``, ``"antilepton"``, ``"antimatter"``,
+        ``"antineutrino"``, ``"baryon"``, ``"boson"``, ``"charged"``,
+        ``"custom"``, ``"electron"``, ``"element"``, ``"fermion"``,
+        ``"halogen"``, ``"ion"``, ``"isotope"``, ``"lanthanide"``,
+        ``"lepton"``, ``"matter"``, ``"metal"``, ``"metalloid"``,
+        ``"neutrino"``, ``"neutron"``, ``"noble gas"``, ``"nonmetal"``,
+        ``"positron"``, ``"post-transition metal"``, ``"proton"``,
+        ``"stable"``, ``"transition metal"``, ``"uncharged"``, and
+        ``"unstable"``.
 
         Examples
         --------
@@ -316,7 +339,7 @@ def become_set(arg: Union[str, Set, Sequence]) -> Set[str]:
         exclude = become_set(exclude)
         any_of = become_set(any_of)
 
-        invalid_categories = (require | exclude | any_of) - _valid_categories
+        invalid_categories = (require | exclude | any_of) - valid_categories
 
         duplicate_categories = require & exclude | exclude & any_of | require & any_of
 
@@ -356,10 +379,6 @@ def __gt__(self, other):
         return self._as_particle_list.__gt__(other)
 
 
-AbstractPhysicalParticle.is_category.valid_categories = _valid_categories
-"""All valid particle categories."""
-
-
 class Particle(AbstractPhysicalParticle):
     """
     A class for an individual particle or antiparticle.
@@ -413,6 +432,20 @@ class Particle(AbstractPhysicalParticle):
     CustomParticle
     DimensionlessParticle
     ~plasmapy.particles.particle_collections.ParticleList
+    ~plasmapy.particles.particle_class.valid_categories
+
+    Notes
+    -----
+    Valid particle categories include: ``"actinide"``, ``"alkali
+    metal"``, ``"alkaline earth metal"``, ``"antibaryon"``,
+    ``"antilepton"``, ``"antimatter"``, ``"antineutrino"``,
+    ``"baryon"``, ``"boson"``, ``"charged"``, ``"custom"``,
+    ``"electron"``, ``"element"``, ``"fermion"``, ``"halogen"``,
+    ``"ion"``, ``"isotope"``, ``"lanthanide"``, ``"lepton"``,
+    ``"matter"``, ``"metal"``, ``"metalloid"``, ``"neutrino"``,
+    ``"neutron"``, ``"noble gas"``, ``"nonmetal"``, ``"positron"``,
+    ``"post-transition metal"``, ``"proton"``, ``"stable"``,
+    ``"transition metal"``, ``"uncharged"``, and ``"unstable"``.
 
     Examples
     --------
@@ -534,17 +567,6 @@ class Particle(AbstractPhysicalParticle):
     The `~plasmapy.particles.particle_class.Particle.categories` attribute
     and `~plasmapy.particles.particle_class.Particle.is_category` method
     may be used to find and test particle membership in categories.
-
-    Valid particle categories include: ``'actinide'``, ``'alkali
-    metal'``, ``'alkaline earth metal'``, ``'antibaryon'``,
-    ``'antilepton'``, ``'antimatter'``, ``'antineutrino'``,
-    ``'baryon'``, ``'boson'``, ``'charged'``, ``'electron'``,
-    ``'element'``, ``'fermion'``, ``'halogen'``, ``'ion'``,
-    ``'isotope'``, ``'lanthanide'``, ``'lepton'``, ``'matter'``,
-    ``'metal'``, ``'metalloid'``, ``'neutrino'``, ``'neutron'``,
-    ``'noble gas'``, ``'nonmetal'``, ``'positron'``,
-    ``'post-transition metal'``, ``'proton'``, ``'stable'``,
-    ``'transition metal'``, ``'uncharged'``, and ``'unstable'``.
     """
 
     def __init__(


Test patch:
diff --git a/plasmapy/particles/tests/test_particle_class.py b/plasmapy/particles/tests/test_particle_class.py
index ce631054..0ae8a83d 100644
--- a/plasmapy/particles/tests/test_particle_class.py
+++ b/plasmapy/particles/tests/test_particle_class.py
@@ -28,10 +28,10 @@
     CustomParticle,
     DimensionlessParticle,
     Particle,
+    valid_categories,
 )
 from plasmapy.utils import roman
 from plasmapy.utils.code_repr import call_string
-from plasmapy.utils.exceptions import PlasmaPyFutureWarning
 from plasmapy.utils.pytest_helpers import run_test_equivalent_calls
 
 # (arg, kwargs, results_dict)
@@ -1451,19 +1451,19 @@ def test_particle_to_json_file(cls, kwargs, expected_repr):
 
 def test_particle_is_category_valid_categories():
     """Test the location where valid categories may be accessed."""
-    assert hasattr(Particle.is_category, "valid_categories")
     some_valid_categories = {
-        "lepton",
-        "fermion",
-        "matter",
-        "nonmetal",
+        "charged",
+        "custom",
         "electron",
+        "fermion",
         "ion",
         "isotope",
-        "charged",
+        "lepton",
+        "matter",
+        "nonmetal",
         "uncharged",
     }
-    assert some_valid_categories.issubset(Particle.is_category.valid_categories)
+    assert some_valid_categories.issubset(valid_categories)
 
 
 def test_CustomParticle_cmp():