Repository: skypyproject/skypy
Pull request #646 (2025-02-09 09:11:11)

Release v0.5.5
## Description
Updates for release v0.5.5

## Checklist
- [x] Follow the [Contributor Guidelines](https://github.com/skypyproject/skypy/blob/main/CONTRIBUTING.rst)
- [ ] Write unit tests
- [ ] Write documentation strings
- [ ] Assign someone from your working team to review this pull request
- [x] Assign someone from the infrastructure team to review this pull request



Patch:
diff --git a/.github/workflows/compatibility.yaml b/.github/workflows/compatibility.yaml
index 984ac74..6346d59 100644
--- a/.github/workflows/compatibility.yaml
+++ b/.github/workflows/compatibility.yaml
@@ -9,8 +9,8 @@ jobs:
       fail-fast: false
       matrix:
         os: [ubuntu-latest]
-        python: ['3.12']
-        toxenv: [py312-test, py312-test-dev]
+        python: ['3.13']
+        toxenv: [py313-test, py313-test-dev]
         release: [main, latest]
     steps:
     - name: Checkout Repository
diff --git a/.readthedocs.yml b/.readthedocs.yml
index d6ba808..4fa0e9b 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -3,11 +3,12 @@ version: 2
 build:
   os: ubuntu-20.04
   tools:
-    python: '3.8'
+    python: '3.13'
   apt_packages:
     - graphviz
 
 sphinx:
+  configuration: docs/conf.py
   fail_on_warning: true
 
 python:
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index fddce44..40dcaa1 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -129,7 +129,7 @@ Before your pull request can be merged into the codebase, it will be reviewed by
 General Guidelines
 ^^^^^^^^^^^^^^^^^^
 
-- SkyPy is compatible with Python>=3.8 (see `setup.cfg <https://github.com/skypyproject/skypy/blob/main/setup.cfg>`_). SkyPy *does not* support backwards compatibility with Python 2.x; `six`, `__future__` and `2to3` should not be used.
+- SkyPy is compatible with Python>=3.9 (see `setup.cfg <https://github.com/skypyproject/skypy/blob/main/setup.cfg>`_). SkyPy *does not* support backwards compatibility with Python 2.x; `six`, `__future__` and `2to3` should not be used.
 - All contributions should follow the `PEP8 Style Guide for Python Code <https://www.python.org/dev/peps/pep-0008/>`_. We recommend using `flake8 <https://flake8.pycqa.org/>`__ to check your code for PEP8 compliance.
 - Importing SkyPy should only depend on having `NumPy <https://www.numpy.org>`_, `SciPy <https://www.scipy.org/>`_ and `Astropy <https://www.astropy.org/>`__ installed.
 - Code is grouped into submodules based on broad science areas e.g. `galaxies <https://skypy.readthedocs.io/en/stable/galaxies.html>`_. There is also a `utils <https://skypy.readthedocs.io/en/stable/utils/index.html>`_ submodule for general utility functions.
diff --git a/docs/install.rst b/docs/install.rst
index 7203271..4d368c6 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -40,7 +40,7 @@ can be installed directly from GitHub using a recent version of pip:
 Dependencies
 ------------
 
-SkyPy is compatble with Python versions 3.8 or later on Ubuntu, macOS and
+SkyPy is compatble with Python versions 3.9 or later on Ubuntu, macOS and
 Windows operating systems. It has the following core dependencies:
 
 - `astropy <https://www.astropy.org/>`__
diff --git a/setup.cfg b/setup.cfg
index 0235fd8..f827e7c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -14,7 +14,7 @@ github_project = skypyproject/skypy
 [options]
 zip_safe = False
 packages = find:
-python_requires = >=3.8
+python_requires = >=3.9
 setup_requires = setuptools_scm
 install_requires =
     astropy>=4
diff --git a/skypy/galaxies/spectrum.py b/skypy/galaxies/spectrum.py
index 3d0722e..8a552f9 100644
--- a/skypy/galaxies/spectrum.py
+++ b/skypy/galaxies/spectrum.py
@@ -4,8 +4,8 @@ r"""Galaxy spectrum module.
 
 from astropy import units
 from astropy.io import fits
+from importlib import resources
 import numpy as np
-from pkg_resources import resource_filename
 from skypy.utils.photometry import SpectrumTemplates
 
 
@@ -107,7 +107,7 @@ class KCorrectTemplates(SpectrumTemplates):
     '''
 
     def __init__(self, hdu=1):
-        filename = resource_filename('skypy', 'data/kcorrect/k_nmf_derived.default.fits')
+        filename = resources.files('skypy') / 'data/kcorrect/k_nmf_derived.default.fits'
         with fits.open(filename) as hdul:
             self.templates = hdul[hdu].data * units.Unit('erg s-1 cm-2 angstrom-1')
             self.wavelength = hdul[11].data * units.Unit('angstrom')
diff --git a/skypy/utils/special.py b/skypy/utils/special.py
index a2fae69..1766a05 100644
--- a/skypy/utils/special.py
+++ b/skypy/utils/special.py
@@ -10,10 +10,22 @@ This module computes useful special functions.
 
 """
 
+from importlib.metadata import version
+from packaging.version import parse
+
 import numpy as np
 import scipy.special as sc
 
 
+def _is_gamma_pole(a):
+    '''Utility function that returns True if a is a pole of the gamma function'''
+    s = sc.gammasgn(a)
+    if parse(version('scipy')) < parse('1.15'):
+        return s == 0
+    else:
+        return np.logical_or(a == 0, np.isnan(s))
+
+
 def _gammaincc(a, x):
     '''gammaincc for positive or negative indices and scalar inputs'''
     if x < 0:
@@ -21,10 +33,9 @@ def _gammaincc(a, x):
     if x == 0:
         if a > 0:
             return 1
-        s = sc.gammasgn(a)
-        if s == 0:
+        if _is_gamma_pole(a):
             return 0
-        return s*np.inf
+        return sc.gammasgn(a) * np.inf
     if np.isinf(x):
         return 0
     if a < 0:
@@ -95,7 +106,7 @@ def gammaincc(a, x):
         # do x = 0 for nonpositive a; depends on Gamma(a)
         i = i & (x == 0)
         s = sc.gammasgn(a[i])
-        g[i] = np.where(s == 0, 0, np.copysign(np.inf, s))
+        g[i] = np.where(_is_gamma_pole(a[i]), 0, np.copysign(np.inf, s))
         n[i] = 0
 
         # these are still to do
diff --git a/tox.ini b/tox.ini
index 78766b4..ef5f437 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,9 +1,9 @@
 [tox]
 envlist =
-    py{38,39,310,311,312}-test{,-all}{,-dev,-latest,-oldest}{,-cov}
-    py{38,39,310,311,312}-test-numpy{117,118,119,120,121,122,123,124,125,126,20,21}
-    py{38,39,310,311,312}-test-scipy{16,17,18,19,110,111,112,113,114}
-    py{38,39,310,311,312}-test-astropy{40,41,42,43,50,51,52,53,60,61}
+    py{39,310,311,312,313}-test{,-all}{,-dev,-latest,-oldest}{,-cov}
+    py{39,310,311,312,313}-test-numpy{119,120,121,122,123,124,125,126,20,21,22}
+    py{39,310,311,312,313}-test-scipy{16,17,18,19,110,111,112,113,114,115}
+    py{39,310,311,312,313}-test-astropy{43,50,51,52,53,60,61,70}
     build_docs
     linkcheck
     codestyle
@@ -36,8 +36,6 @@ description =
     latest: with the latest supported version of key dependencies
     oldest: with the oldest supported version of key dependencies
     cov: and test coverage
-    numpy117: with numpy 1.17.*
-    numpy118: with numpy 1.18.*
     numpy119: with numpy 1.19.*
     numpy120: with numpy 1.20.*
     numpy121: with numpy 1.21.*
@@ -48,6 +46,7 @@ description =
     numpy125: with numpy 1.26.*
     numpy20: with numpy 2.0.*
     numpy21: with numpy 2.1.*
+    numpy22: with numpy 2.2.*
     scipy16: with scipy 1.6.*
     scipy17: with scipy 1.7.*
     scipy18: with scipy 1.8.*
@@ -57,9 +56,7 @@ description =
     scipy112: with scipy 1.12.*
     scipy113: with scipy 1.13.*
     scipy114: with scipy 1.14.*
-    astropy40: with astropy 4.0.*
-    astropy41: with astropy 4.1.*
-    astropy42: with astropy 4.2.*
+    scipy114: with scipy 1.15.*
     astropy43: with astropy 4.3.*
     astropy50: with astropy 5.0.*
     astropy51: with astropy 5.1.*
@@ -67,12 +64,11 @@ description =
     astropy53: with astropy 5.3.*
     astropy60: with astropy 6.0.*
     astropy61: with astropy 6.1.*
+    astropy70: with astropy 7.0.*
 
 # The following provides some specific pinnings for key packages
 deps =
 
-    numpy117: numpy==1.17.*
-    numpy118: numpy==1.18.*
     numpy119: numpy==1.19.*
     numpy120: numpy==1.20.*
     numpy121: numpy==1.21.*
@@ -83,6 +79,7 @@ deps =
     numpy125: numpy==1.26.*
     numpy20: numpy==2.0.*
     numpy21: numpy==2.1.*
+    numpy22: numpy==2.2.*
 
     scipy16: scipy==1.6.*
     scipy17: scipy==1.7.*
@@ -93,10 +90,8 @@ deps =
     scipy112: scipy==1.12.*
     scipy113: scipy==1.13.*
     scipy114: scipy==1.14.*
+    scipy114: scipy==1.15.*
 
-    astropy40: astropy==4.0.*
-    astropy41: astropy==4.1.*
-    astropy42: astropy==4.2.*
     astropy43: astropy==4.3.*
     astropy50: astropy==5.0.*
     astropy51: astropy==5.1.*
@@ -104,17 +99,18 @@ deps =
     astropy53: astropy==5.3.*
     astropy60: astropy==6.0.*
     astropy61: astropy==6.1.*
+    astropy70: astropy==7.0.*
 
     dev: numpy
     dev: scipy
     dev: git+https://github.com/astropy/astropy.git#egg=astropy
 
-    latest: astropy==6.1.*
-    latest: numpy==2.1.*
-    latest: scipy==1.14.*
+    latest: astropy==7.0.*
+    latest: numpy==2.2.*
+    latest: scipy==1.15.*
 
-    oldest: astropy==4.0.*
-    oldest: numpy==1.17.*
+    oldest: astropy==4.3.*
+    oldest: numpy==1.19.*
     oldest: scipy==1.6.*
 
 # The following indicates which extras_require from setup.cfg will be installed


Test patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 2800cb6..6546eee 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -23,24 +23,24 @@ jobs:
 
           - name: latest supported versions
             os: ubuntu-latest
-            python: '3.12'
-            toxenv: py312-test-all-latest-cov
+            python: '3.13'
+            toxenv: py313-test-all-latest-cov
             toxposargs: --cov-report=xml:${GITHUB_WORKSPACE}/coverage.xml
 
           - name: oldest supported versions
             os: ubuntu-latest
-            python: '3.8'
-            toxenv: py38-test-oldest
+            python: '3.9'
+            toxenv: py39-test-oldest
 
           - name: macOS latest supported
             os: macos-latest
-            python: '3.12'
-            toxenv: py312-test-latest
+            python: '3.13'
+            toxenv: py313-test-latest
 
           - name: Windows latest supported
             os: windows-latest
-            python: '3.12'
-            toxenv: py312-test-latest
+            python: '3.13'
+            toxenv: py313-test-latest
 
     steps:
     - name: Checkout Repository
diff --git a/skypy/pipeline/tests/test_config.py b/skypy/pipeline/tests/test_config.py
index 16b0b03..f8cfe8b 100644
--- a/skypy/pipeline/tests/test_config.py
+++ b/skypy/pipeline/tests/test_config.py
@@ -3,7 +3,7 @@ import pytest
 from skypy.pipeline import load_skypy_yaml
 from skypy.pipeline._items import Call
 from astropy import units
-from astropy.cosmology.core import Cosmology
+from astropy.cosmology import Cosmology
 
 
 def test_load_skypy_yaml():
diff --git a/skypy/pipeline/tests/test_pipeline.py b/skypy/pipeline/tests/test_pipeline.py
index 3b6151f..924a9e5 100644
--- a/skypy/pipeline/tests/test_pipeline.py
+++ b/skypy/pipeline/tests/test_pipeline.py
@@ -1,5 +1,4 @@
-from astropy.cosmology import FlatLambdaCDM, default_cosmology
-from astropy.cosmology.core import Cosmology
+from astropy.cosmology import Cosmology, FlatLambdaCDM, default_cosmology
 from astropy.io import fits
 from astropy.io.misc.hdf5 import read_table_hdf5
 from astropy.table import Table, vstack
diff --git a/skypy/utils/tests/test_special.py b/skypy/utils/tests/test_special.py
index 92b7f10..c228d98 100644
--- a/skypy/utils/tests/test_special.py
+++ b/skypy/utils/tests/test_special.py
@@ -3,7 +3,7 @@ import numpy.testing as npt
 from astropy.utils.data import get_pkg_data_filename
 import pytest
 
-from skypy.utils.special import gammaincc
+from skypy.utils.special import gammaincc, _is_gamma_pole
 
 
 def test_gammaincc_scalar():
@@ -90,3 +90,25 @@ def test_gammaincc_neg_x_array():
     # negative x in array raises an exception
     with pytest.raises(ValueError):
         gammaincc(0.5, [3.0, 2.0, 1.0, 0.0, -1.0])
+
+
+gamma_poles = np.array([-2.0, -1.0, 0.0])
+not_gamma_poles = np.array([-2.5, 0.5, 1.0])
+
+
+@pytest.mark.parametrize('z', gamma_poles)
+def test_is_gamma_pole_scalar(z):
+    assert _is_gamma_pole(z)
+
+
+def test_is_gamma_pole_array():
+    assert np.all(_is_gamma_pole(gamma_poles))
+
+
+@pytest.mark.parametrize('z', not_gamma_poles)
+def test_is_not_gamma_pole_scalar(z):
+    assert not _is_gamma_pole(z)
+
+
+def test_is_not_gamma_pole_array():
+    assert not np.any(_is_gamma_pole(not_gamma_poles))