Skip to content

Commit

Permalink
Drop python 3.9 support (PlasmaPy#2501)
Browse files Browse the repository at this point in the history
* Drop support for Python 3.9

* Add strict=False to zip calls

* Replace typing.Union with | operator

* Add changelog entry

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Enable type union syntax in annotations

* Apply UP006 & UP007 rules from ruff for annotations

* Upgrade more type hints

* Revert "Upgrade more type hints"

This reverts commit 9313eb1.

* Revert "Apply UP006 & UP007 rules from ruff for annotations"

This reverts commit d81b066.

* Revert "Enable type union syntax in annotations"

This reverts commit 7c0cbe4.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
namurphy and pre-commit-ci[bot] authored Feb 6, 2024
1 parent 7a2b715 commit bf6b6d8
Show file tree
Hide file tree
Showing 43 changed files with 88 additions and 85 deletions.
5 changes: 0 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ jobs:
python: '3.10'
toxenv: py310-all

- name: Python 3.9 (Windows), minimal dependencies
os: windows-latest
python: 3.9
toxenv: py39-all-minimal

- name: mypy
os: ubuntu-latest
python: '3.12'
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ very welcome!

## Installation

PlasmaPy requires Python 3.9 or newer. If you do not have Python
PlasmaPy requires Python 3.10 or newer. If you do not have Python
installed already, here are the instructions to [download and install
Python].

Expand All @@ -104,14 +104,14 @@ To install PlasmaPy on macOS or Linux, open a terminal and run:
python -m pip install plasmapy
```
On some systems, it might be necessary to specify the Python version
number, for example by using `python3` or `python3.11` instead of
number, for example by using `python3` or `python3.12` instead of
`python`.

To install PlasmaPy on Windows, open a terminal and run
```Shell
py -3.11 -m pip install plasmapy
py -3.12 -m pip install plasmapy
```
The `3.11` may be replaced by any version of Python that is supported by
The `3.12` may be replaced by any version of Python that is supported by
PlasmaPy.

If you have [installed Conda], then you can also install PlasmaPy into
Expand Down
1 change: 1 addition & 0 deletions changelog/2501.breaking.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Changed the minimum required version of Python from 3.9 to 3.10.
4 changes: 2 additions & 2 deletions docs/_global_substitutions.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@
"keyword-only": r":term:`keyword-only`\ ",
"lite-function": r":term:`lite-function`\ ",
"lite-functions": r":term:`lite-functions`\ ",
"maxpython": "3.11",
"minpython": "3.9",
"maxpython": "3.12",
"minpython": "3.10",
"Open a terminal": r":ref:`Open a terminal <opening-a-terminal>`\ ",
"parameter": r":term:`parameter`\ ",
"parameters": r":term:`parameters <parameter>`\ ",
Expand Down
2 changes: 1 addition & 1 deletion docs/notebooks/analysis/fit_functions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@
"plt.legend(fontsize=14, loc=\"upper left\")\n",
"\n",
"txt = f\"$f(x) = {explin.latex_str}$\\n$r^2 = {explin.rsq:.3f}$\\n\"\n",
"for name, param, err in zip(explin.param_names, explin.params, explin.param_errors):\n",
"for name, param, err in zip(explin.param_names, explin.params, explin.param_errors, strict=False):\n",
" txt += f\"{name} = {param:.3f} $\\\\pm$ {err:.3f}\\n\"\n",
"txt_loc = [-13.0, ax.get_ylim()[1]]\n",
"txt_loc = ax.transAxes.inverted().transform(ax.transData.transform(txt_loc))\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@
"axs[0].legend(fontsize=12)\n",
"\n",
"# zoom on fit\n",
"for ii, label, rtn in zip([1, 2], [\"Exponential\", \"Linear\"], [results, results_lin]):\n",
"for ii, label, rtn in zip([1, 2], [\"Exponential\", \"Linear\"], [results, results_lin], strict=False):\n",
" vf = rtn[0]\n",
" extras = rtn[1]\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion docs/notebooks/particles/ace.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@
"source": [
"fig, axes = plt.subplots(1, 3, figsize=(10, 3), tight_layout=True)\n",
"\n",
"for state, ax in zip(states, axes):\n",
"for state, ax in zip(states, axes, strict=False):\n",
" ax.bar(state.charge_numbers, state.ionic_fractions)\n",
" ax.set_title(f\"Ionization state for {state.base_particle}\")\n",
" ax.set_xlabel(\"ionic level\")\n",
Expand Down
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[mypy]
python_version = 3.9
python_version = '3.10'
mypy_path = ./type_stubs
exclude = (?x)(
docs|
Expand Down
4 changes: 2 additions & 2 deletions plasmapy/analysis/fit_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def params(self) -> Optional[tuple]:
@params.setter
def params(self, val) -> None:
if isinstance(val, self.FitParamTuple) or (
isinstance(val, (tuple, list))
isinstance(val, tuple | list)
and len(val) == len(self.param_names)
and all(isinstance(vv, numbers.Real) for vv in val)
):
Expand All @@ -257,7 +257,7 @@ def param_errors(self) -> Optional[tuple]:
@param_errors.setter
def param_errors(self, val) -> None:
if isinstance(val, self.FitParamTuple) or (
isinstance(val, (tuple, list))
isinstance(val, tuple | list)
and len(val) == len(self.param_names)
and all(isinstance(vv, numbers.Real) for vv in val)
):
Expand Down
4 changes: 2 additions & 2 deletions plasmapy/analysis/swept_langmuir/floating_potential.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def find_floating_potential( # noqa: C901, PLR0912, PLR0915
# condition min_points
if min_points is None:
min_points = int(np.max([5, np.around(min_point_factor * voltage.size)]))
elif not isinstance(min_points, (float, np.floating, int, np.integer)):
elif not isinstance(min_points, float | np.floating | int | np.integer):
raise TypeError(
f"Argument 'min_points' is wrong type '{type(min_points)}', expecting "
f"an int or float."
Expand Down Expand Up @@ -265,7 +265,7 @@ def find_floating_potential( # noqa: C901, PLR0912, PLR0915
(cp_candidates[threshold_indices] + 1, [cp_candidates[-1] + 1])
)
rtn_extras["islands"] = [
slice(start, stop) for start, stop in zip(isl_start, isl_stop)
slice(start, stop) for start, stop in zip(isl_start, isl_stop, strict=False)
]

# do islands fall within the min_points window?
Expand Down
4 changes: 2 additions & 2 deletions plasmapy/analysis/swept_langmuir/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def check_sweep( # noqa: C901, PLR0912
# check type
if isinstance(voltage, np.ndarray):
pass
elif isinstance(voltage, (list, tuple)):
elif isinstance(voltage, list | tuple):
voltage = np.array(voltage)
else:
raise TypeError(
Expand Down Expand Up @@ -102,7 +102,7 @@ def check_sweep( # noqa: C901, PLR0912
# check type
if isinstance(current, np.ndarray):
pass
elif isinstance(current, (list, tuple)):
elif isinstance(current, list | tuple):
current = np.array(current)
else:
raise TypeError(
Expand Down
7 changes: 5 additions & 2 deletions plasmapy/analysis/time_series/conditional_averaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,13 +423,16 @@ def is_middle_value_highest(sequence):

if self._reference_signal_provided:
for event, peak, reference_event in zip(
conditional_events, peak_indices, conditional_events_reference_signal
conditional_events,
peak_indices,
conditional_events_reference_signal,
strict=False,
):
if is_middle_value_highest(reference_event):
checked_conditional_events.append(event)
checked_peak_indices.append(peak)
else:
for event, peak in zip(conditional_events, peak_indices):
for event, peak in zip(conditional_events, peak_indices, strict=False):
if is_middle_value_highest(event):
checked_conditional_events.append(event)
checked_peak_indices.append(peak)
Expand Down
2 changes: 1 addition & 1 deletion plasmapy/diagnostics/thomson.py
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ def spectral_density_model( # noqa: C901, PLR0912, PLR0915
# **********************
# Required settings and parameters per population
# **********************
for p, nums in zip(["T_e", "T_i"], [num_e, num_i]):
for p, nums in zip(["T_e", "T_i"], [num_e, num_i], strict=False):
for num in range(nums):
key = f"{p}_{num!s}"
if key not in params:
Expand Down
8 changes: 4 additions & 4 deletions plasmapy/dispersion/analytical/stix_.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,15 @@ def stix( # noqa: C901, PLR0912, PLR0915
"""

# Validate ions argument
if not isinstance(ions, (list, tuple, ParticleList)):
if not isinstance(ions, list | tuple | ParticleList):
ions = [ions]
ions = ParticleList(ions)

if not all(failed := [ion.is_ion and ion.charge_number > 0 for ion in ions]):
raise ValueError(
"Particle(s) passed to 'ions' must be a positively charged"
" ion. The following particle(s) is(are) not allowed "
f"{[ion for ion, fail in zip(ions, failed) if not fail]}"
f"{[ion for ion, fail in zip(ions, failed, strict=False) if not fail]}"
)

# Validate n_i argument
Expand Down Expand Up @@ -244,15 +244,15 @@ def stix( # noqa: C901, PLR0912, PLR0915
# Generate the plasma parameters needed
wps = []
wcs = []
for par, dens in zip(species, densities):
for par, dens in zip(species, densities, strict=False):
wps.append(plasma_frequency(n=dens * u.m**-3, particle=par).value)
wcs.append(gyrofrequency(B=B, particle=par, signed=True).value)

# Stix method implemented
S = np.ones_like(w, dtype=np.float64)
P = np.ones_like(S)
D = np.zeros_like(S)
for wc, wp in zip(wcs, wps):
for wc, wp in zip(wcs, wps, strict=False):
S -= (wp**2) / (w**2 - wc**2)
P -= (wp / w) ** 2
D += ((wp**2) / (w**2 - wc**2)) * (wc / w)
Expand Down
4 changes: 2 additions & 2 deletions plasmapy/dispersion/analytical/tests/test_stix_.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ def spd(B, w, ions, n_i):
# Generate the plasma parameters needed
wps = []
wcs = []
for par, dens in zip(species, densities):
for par, dens in zip(species, densities, strict=False):
wps.append(plasma_frequency(n=dens * u.m**-3, particle=par).value)
wcs.append(gyrofrequency(B=B, particle=par, signed=True).value)

# Stix method implemented
S = np.ones_like(w, dtype=np.float64)
P = np.ones_like(S)
D = np.zeros_like(S)
for wc, wp in zip(wcs, wps):
for wc, wp in zip(wcs, wps, strict=False):
S -= (wp**2) / (w**2 - wc**2)
P -= (wp / w) ** 2
D += ((wp**2) / (w**2 - wc**2)) * (wc / w)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def temp_ratio( # noqa: C901
"""

# Validate ions argument
if not isinstance(ions, (list, tuple, ParticleList)):
if not isinstance(ions, list | tuple | ParticleList):
ions = [ions]
ions = ParticleList(ions)

Expand Down
2 changes: 1 addition & 1 deletion plasmapy/formulary/collisions/tests/test_frequencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ def test_limit_values(
) ** 2

for attribute_name, expected_limit_value in zip(
self.return_values_to_test, expected_limit_values
self.return_values_to_test, expected_limit_values, strict=False
):
calculated_limit_value = getattr(value_test_case, attribute_name).value
# Energy loss limit value is already in units of
Expand Down
2 changes: 1 addition & 1 deletion plasmapy/formulary/densities.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def mass_density(
f"(not type {type(particle)}) to calculate the mass density!"
) from e

if not isinstance(z_ratio, (float, np.floating, int, np.integer)):
if not isinstance(z_ratio, float | np.floating | int | np.integer):
raise TypeError(
f"Expected type int or float for keyword z_ratio, got type {type(z_ratio)}."
)
Expand Down
4 changes: 2 additions & 2 deletions plasmapy/formulary/dielectric.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def cold_plasma_permittivity_SDP(
"""
S, D, P = 1, 0, 1

for s, n_s in zip(species, n):
for s, n_s in zip(species, n, strict=False):
omega_c = gyrofrequency(B=B, particle=s, signed=True)
omega_p = plasma_frequency(n=n_s, particle=s)

Expand Down Expand Up @@ -207,7 +207,7 @@ def cold_plasma_permittivity_LRP(
"""
L, R, P = 1, 1, 1

for s, n_s in zip(species, n):
for s, n_s in zip(species, n, strict=False):
omega_c = gyrofrequency(B=B, particle=s, signed=True)
omega_p = plasma_frequency(n=n_s, particle=s)

Expand Down
2 changes: 1 addition & 1 deletion plasmapy/formulary/mathematics.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def Fermi_integral(
>>> Fermi_integral(1, 1)
(1.8062860704447743-0j)
"""
if isinstance(x, (numbers.Integral, numbers.Real, numbers.Complex)):
if isinstance(x, numbers.Integral | numbers.Real | numbers.Complex):
arg = -np.exp(x)
return -1 * complex(polylog(j + 1, arg))
elif isinstance(x, np.ndarray):
Expand Down
2 changes: 1 addition & 1 deletion plasmapy/formulary/relativity.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ def velocity(self, V: u.Quantity[u.m / u.s]) -> None:

@lorentz_factor.setter
def lorentz_factor(self, γ: Union[Real, u.Quantity]):
if not isinstance(γ, (Real, u.Quantity)):
if not isinstance(γ, Real | u.Quantity):
raise TypeError("Invalid type for Lorentz factor")

if isinstance(γ, u.Quantity):
Expand Down
2 changes: 1 addition & 1 deletion plasmapy/formulary/speeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def ion_sound_speed(
<Quantity 229585... m / s>
"""
for gamma, species in zip([gamma_e, gamma_i], ["electrons", "ions"]):
for gamma, species in zip([gamma_e, gamma_i], ["electrons", "ions"], strict=False):
if not isinstance(gamma, Real):
raise TypeError(
f"The adiabatic index gamma for {species} must be a positive "
Expand Down
4 changes: 2 additions & 2 deletions plasmapy/particles/_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def _make_custom_particle_with_real_charge_number(
CustomParticle(mass=6.64511...e-27 kg, charge=2.40326...e-19 C)
"""

if not isinstance(Z, (Real, u.Quantity)) and Z is not None:
if not isinstance(Z, Real | u.Quantity) and Z is not None:
raise ChargeError("The charge number must be a real number.")

base_particle = Particle(arg, mass_numb=mass_numb, Z=0)
Expand Down Expand Up @@ -191,7 +191,7 @@ def _physical_particle_factory(
with contextlib.suppress(ChargeError, InvalidParticleError, TypeError):
return constructor(*args, **kwargs)

if args and not isinstance(args[0], (str, Integral, u.Quantity)):
if args and not isinstance(args[0], str | Integral | u.Quantity):
raise TypeError("Invalid type for particle.")

raise InvalidParticleError(_generate_particle_factory_error_message(args, kwargs))
2 changes: 1 addition & 1 deletion plasmapy/particles/_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ def reconstruct_ion_symbol(

return ion

if not isinstance(argument, (str, Integral)):
if not isinstance(argument, str | Integral):
raise TypeError(f"The argument {argument} is not an integer or string.")

arg = dealias_particle_aliases(argument)
Expand Down
16 changes: 10 additions & 6 deletions plasmapy/particles/atomic.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,9 @@ def known_isotopes_for_element(argument):
mass_numbers = [mass_number(isotope) for isotope in isotopes]
return [
mass_number
for (isotope, mass_number) in sorted(zip(mass_numbers, isotopes))
for (isotope, mass_number) in sorted(
zip(mass_numbers, isotopes, strict=False)
)
]

if argument is not None:
Expand Down Expand Up @@ -674,7 +676,9 @@ def common_isotopes_for_element(

sorted_isotopes = [
iso_comp
for (isotope, iso_comp) in sorted(zip(isotopic_abundances, CommonIsotopes))
for (isotope, iso_comp) in sorted(
zip(isotopic_abundances, CommonIsotopes, strict=False)
)
]

sorted_isotopes.reverse()
Expand Down Expand Up @@ -902,7 +906,7 @@ def periodic_table_period(argument: Union[str, Integral]) -> Integral:
2
"""
# TODO: Implement @particle_input
if not isinstance(argument, (str, Integral)):
if not isinstance(argument, str | Integral):
raise TypeError(
"The argument to periodic_table_period must be either a "
"string representing the element or its symbol, or an "
Expand Down Expand Up @@ -950,7 +954,7 @@ def periodic_table_group(argument: Union[str, Integral]) -> Integral:
2
"""
# TODO: Implement @particle_input
if not isinstance(argument, (str, Integral)):
if not isinstance(argument, str | Integral):
raise TypeError(
"The argument to periodic_table_group must be "
"either a string representing the element or its "
Expand Down Expand Up @@ -1001,7 +1005,7 @@ def periodic_table_block(argument: Union[str, Integral]) -> str:
's'
"""
# TODO: Implement @particle_input
if not isinstance(argument, (str, Integral)):
if not isinstance(argument, str | Integral):
raise TypeError(
"The argument to periodic_table_block must be "
"either a string representing the element or its "
Expand Down Expand Up @@ -1047,7 +1051,7 @@ def periodic_table_category(argument: Union[str, Integral]) -> str:
'transition metal'
"""
# TODO: Implement @particle_input
if not isinstance(argument, (str, Integral)):
if not isinstance(argument, str | Integral):
raise TypeError(
"The argument to periodic_table_category must be "
"either a string representing the element or its "
Expand Down
2 changes: 1 addition & 1 deletion plasmapy/particles/ionization_state_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ def _sort_entries_by_atomic_and_mass_numbers(k):

self._pars["abundances"] = new_abundances

elif isinstance(inputs, (list, tuple)):
elif isinstance(inputs, list | tuple):
try:
_particle_instances = [Particle(particle) for particle in inputs]
except (InvalidParticleError, TypeError) as exc:
Expand Down
2 changes: 1 addition & 1 deletion plasmapy/particles/nuclear.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def process_particles_list(
if isinstance(unformatted_particles_list, str):
unformatted_particles_list = [unformatted_particles_list]

if not isinstance(unformatted_particles_list, (list, tuple)):
if not isinstance(unformatted_particles_list, list | tuple):
raise TypeError(
"The input to process_particles_list should be a "
"string, list, or tuple."
Expand Down
Loading

0 comments on commit bf6b6d8

Please sign in to comment.