diff --git a/.github/workflows/codestyle.yaml b/.github/workflows/codestyle.yaml index 2e14de43..8dd0fa39 100644 --- a/.github/workflows/codestyle.yaml +++ b/.github/workflows/codestyle.yaml @@ -4,18 +4,22 @@ on: branches: - main - module/* + - 'v*x' + tags: + - 'v*' pull_request: branches: - main - module/* + - 'v*x' jobs: flake8: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install Python 3.x - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.x - name: Install Dependencies diff --git a/.github/workflows/compatibility.yaml b/.github/workflows/compatibility.yaml index a65e452e..6346d592 100644 --- a/.github/workflows/compatibility.yaml +++ b/.github/workflows/compatibility.yaml @@ -9,12 +9,12 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python: ['3.11'] - toxenv: [py311-test, py311-test-dev] + python: ['3.13'] + toxenv: [py313-test, py313-test-dev] release: [main, latest] steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - if: matrix.release != 'main' @@ -22,7 +22,7 @@ jobs: run: | git checkout tags/$(curl -s https://api.github.com/repos/skypyproject/skypy/releases/${{ matrix.release }} | python -c "import sys, json; print(json.load(sys.stdin)['tag_name'])") - name: Install Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install Dependencies diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 5bc9ca89..6546eeee 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -4,10 +4,14 @@ on: branches: - main - module/* + - 'v*x' + tags: + - 'v*' pull_request: branches: - main - module/* + - 'v*x' jobs: test: name: ${{ matrix.name }} @@ -19,32 +23,32 @@ jobs: - name: latest supported versions os: ubuntu-latest - python: '3.11' - toxenv: py311-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.7 - toxenv: py37-test-oldest + python: '3.9' + toxenv: py39-test-oldest - name: macOS latest supported os: macos-latest - python: '3.11' - toxenv: py311-test-latest + python: '3.13' + toxenv: py313-test-latest - name: Windows latest supported os: windows-latest - python: '3.11' - toxenv: py311-test-latest + python: '3.13' + toxenv: py313-test-latest steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install Dependencies @@ -55,4 +59,4 @@ jobs: tox -e ${{ matrix.toxenv }} ${{ matrix.toxargs }} -- ${{ matrix.toxposargs }} - if: contains(matrix.toxenv, '-cov') name: Report Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 diff --git a/.readthedocs.yml b/.readthedocs.yml index 46f01501..4fa0e9b7 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,13 +1,17 @@ version: 2 build: - image: latest + os: ubuntu-20.04 + tools: + python: '3.13' + apt_packages: + - graphviz sphinx: + configuration: docs/conf.py fail_on_warning: true python: - version: 3.7 install: - method: pip path: . diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d749b965..f0fa350f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -168,7 +168,7 @@ Before your pull request can be merged into the codebase, it will be reviewed by General Guidelines ^^^^^^^^^^^^^^^^^^ -- SkyPy is compatible with Python>=3.7 (see `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 `_). 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 `_. We recommend using `flake8 `__ to check your code for PEP8 compliance. - Importing SkyPy should only depend on having `NumPy `_, `SciPy `_ and `Astropy `__ installed. - Code is grouped into submodules based on broad science areas e.g. `galaxies `_. There is also a `utils `_ submodule for general utility functions. diff --git a/docs/install.rst b/docs/install.rst index 986f8d9f..4d368c63 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.7 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 `__ diff --git a/examples/galaxies/plot_schechter.py b/examples/galaxies/plot_schechter.py index 11b060b4..51501279 100644 --- a/examples/galaxies/plot_schechter.py +++ b/examples/galaxies/plot_schechter.py @@ -47,6 +47,7 @@ from astropy.units import Quantity from matplotlib import pyplot as plt import numpy as np +import scipy.integrate from skypy.galaxies import schechter_lf z_range = np.linspace(0.2, 1.0, 100) @@ -81,7 +82,7 @@ # SkyPy simulated galaxies z_mask = np.logical_and(redshift >= z_min, redshift < z_max) dV_dz = (cosmology.differential_comoving_volume(z) * sky_area).to_value('Mpc3') - dV = np.trapz(dV_dz, z) + dV = scipy.integrate.trapezoid(dV_dz, z) dM = (np.max(bins)-np.min(bins)) / (np.size(bins)-1) phi_skypy = np.histogram(magnitude[z_mask], bins=bins)[0] / dV / dM diff --git a/setup.cfg b/setup.cfg index baeb275f..f827e7ce 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,7 +14,7 @@ github_project = skypyproject/skypy [options] zip_safe = False packages = find: -python_requires = >=3.7 +python_requires = >=3.9 setup_requires = setuptools_scm install_requires = astropy>=4 diff --git a/skypy/_astropy_init.py b/skypy/_astropy_init.py index 2dffe8fd..bd453245 100644 --- a/skypy/_astropy_init.py +++ b/skypy/_astropy_init.py @@ -1,52 +1,13 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst +import os -__all__ = ['__version__'] - -# this indicates whether or not we are in the package's setup.py -try: - _ASTROPY_SETUP_ -except NameError: - import builtins - builtins._ASTROPY_SETUP_ = False +__all__ = ['__version__', 'test'] try: from .version import version as __version__ except ImportError: __version__ = '' - -if not _ASTROPY_SETUP_: # noqa - import os - from warnings import warn - from astropy.config.configuration import ( - update_default_config, - ConfigurationDefaultMissingError, - ConfigurationDefaultMissingWarning) - - # Create the test function for self test - from astropy.tests.runner import TestRunner - test = TestRunner.make_test_runner_in(os.path.dirname(__file__)) - test.__test__ = False - __all__ += ['test'] - - # add these here so we only need to cleanup the namespace at the end - config_dir = None - - if not os.environ.get('ASTROPY_SKIP_CONFIG_UPDATE', False): - config_dir = os.path.dirname(__file__) - config_template = os.path.join(config_dir, __package__ + ".cfg") - if os.path.isfile(config_template): - try: - update_default_config( - __package__, config_dir, version=__version__) - except TypeError as orig_error: - try: - update_default_config(__package__, config_dir) - except ConfigurationDefaultMissingError as e: - wmsg = (e.args[0] + - " Cannot install default profile. If you are " - "importing from source, this is expected.") - warn(ConfigurationDefaultMissingWarning(wmsg)) - del e - except Exception: - raise orig_error +# Create the test function for self test +from astropy.tests.runner import TestRunner +test = TestRunner.make_test_runner_in(os.path.dirname(__file__)) diff --git a/skypy/conftest.py b/skypy/conftest.py index 672b2733..482ddb89 100644 --- a/skypy/conftest.py +++ b/skypy/conftest.py @@ -1,27 +1,29 @@ -# This file is used to configure the behavior of pytest when using the Astropy -# test infrastructure. It needs to live inside the package in order for it to -# get picked up when running the tests inside an interpreter using -# packagename.test +"""Configure Test Suite. -import os +This file is used to configure the behavior of pytest when using the Astropy +test infrastructure. It needs to live inside the package in order for it to +get picked up when running the tests inside an interpreter using +`{{ cookiecutter.module_name }}.test()`. + +""" -from astropy.version import version as astropy_version +import os -# For Astropy 3.0 and later, we can use the standalone pytest plugin -if astropy_version < '3.0': - from astropy.tests.pytest_plugins import * # noqa - del pytest_report_header +try: + from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS ASTROPY_HEADER = True -else: - try: - from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS - ASTROPY_HEADER = True - except ImportError: - ASTROPY_HEADER = False +except ImportError: + ASTROPY_HEADER = False def pytest_configure(config): + """Configure Pytest with Astropy. + Parameters + ---------- + config : pytest configuration + + """ if ASTROPY_HEADER: config.option.astropy_header = True @@ -34,16 +36,3 @@ def pytest_configure(config): from . import __version__ packagename = os.path.basename(os.path.dirname(__file__)) TESTED_VERSIONS[packagename] = __version__ - -# Uncomment the last two lines in this block to treat all DeprecationWarnings as -# exceptions. For Astropy v2.0 or later, there are 2 additional keywords, -# as follow (although default should work for most cases). -# To ignore some packages that produce deprecation warnings on import -# (in addition to 'compiler', 'scipy', 'pygments', 'ipykernel', and -# 'setuptools'), add: -# modules_to_ignore_on_import=['module_1', 'module_2'] -# To ignore some specific deprecation warning messages for Python version -# MAJOR.MINOR or later, add: -# warnings_to_ignore_by_pyver={(MAJOR, MINOR): ['Message to ignore']} -# from astropy.tests.helper import enable_deprecations_as_exceptions # noqa -# enable_deprecations_as_exceptions() diff --git a/skypy/galaxies/redshift.py b/skypy/galaxies/redshift.py index f048a357..3173c63d 100644 --- a/skypy/galaxies/redshift.py +++ b/skypy/galaxies/redshift.py @@ -249,7 +249,7 @@ def redshifts_from_comoving_density(redshift, density, sky_area, cosmology, nois dN_dz *= density # integrate density to get expected number of galaxies - N = np.trapz(dN_dz, redshift) + N = scipy.integrate.trapezoid(dN_dz, redshift) # Poisson sample galaxy number if requested if noise: diff --git a/skypy/galaxies/spectrum.py b/skypy/galaxies/spectrum.py index f5492761..8cdb9e3c 100644 --- a/skypy/galaxies/spectrum.py +++ b/skypy/galaxies/spectrum.py @@ -4,8 +4,8 @@ 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/galaxies/tests/test_redshift.py b/skypy/galaxies/tests/test_redshift.py index d41e92ac..c974bab3 100644 --- a/skypy/galaxies/tests/test_redshift.py +++ b/skypy/galaxies/tests/test_redshift.py @@ -1,5 +1,6 @@ import numpy as np import pytest +import scipy.integrate from scipy.stats import kstest @@ -38,7 +39,7 @@ def test_schechter_lf_redshift(): density *= (sky_area * cosmo.differential_comoving_volume(z)).to_value('Mpc3') # integrate total number - n_gal = np.trapz(density, z, axis=-1) + n_gal = scipy.integrate.trapezoid(density, z, axis=-1) # make sure noise-free sample has right size assert np.isclose(len(z_gal), n_gal, atol=1.0) @@ -85,7 +86,7 @@ def test_schechter_smf_redshift(): density *= (sky_area * cosmo.differential_comoving_volume(z)).to_value('Mpc3') # integrate total number - n_gal = np.trapz(density, z, axis=-1) + n_gal = scipy.integrate.trapezoid(density, z, axis=-1) # make sure noise-free sample has right size assert np.isclose(len(z_gal), n_gal, atol=1.0) diff --git a/skypy/galaxies/tests/test_stellar_mass.py b/skypy/galaxies/tests/test_stellar_mass.py index b8809235..cc03ef10 100644 --- a/skypy/galaxies/tests/test_stellar_mass.py +++ b/skypy/galaxies/tests/test_stellar_mass.py @@ -68,7 +68,7 @@ def calc_cdf(m): mass_min = 10 ** 7 mass_max = 10 ** 13 pdf = calc_pdf(m, alpha, mass_star, mass_min, mass_max) - cdf = scipy.integrate.cumtrapz(pdf, m, initial=0) + cdf = scipy.integrate.cumulative_trapezoid(pdf, m, initial=0) cdf = cdf / cdf[-1] return cdf diff --git a/skypy/galaxies/tests/test_velocity_dispersion.py b/skypy/galaxies/tests/test_velocity_dispersion.py index b5be57ec..a01429de 100644 --- a/skypy/galaxies/tests/test_velocity_dispersion.py +++ b/skypy/galaxies/tests/test_velocity_dispersion.py @@ -1,7 +1,7 @@ import numpy as np from scipy.stats import kstest from scipy.special import gamma -from scipy.integrate import cumtrapz +from scipy.integrate import cumulative_trapezoid def test_schechter_vdf(): @@ -30,7 +30,7 @@ def calc_pdf(vd): def calc_cdf(m): pdf = calc_pdf(m) - cdf = cumtrapz(pdf, m, initial=0) + cdf = cumulative_trapezoid(pdf, m, initial=0) cdf /= cdf[-1] return cdf diff --git a/skypy/utils/photometry.py b/skypy/utils/photometry.py index 2b72cfd7..fad1b47c 100644 --- a/skypy/utils/photometry.py +++ b/skypy/utils/photometry.py @@ -436,7 +436,7 @@ def logistic_completeness_function(magnitude, magnitude_95, magnitude_50): is array_like of shape (nb, ) it returns array_like of shape (nb, ). References - ----------- + ---------- .. [1] López-Sanjuan, C. et al., `2017A&A…599A..62L`_ .. _2017A&A…599A..62L: https://ui.adsabs.harvard.edu/abs/2017A%26A...599A..62L diff --git a/tox.ini b/tox.ini index cbd3bd44..46998fa2 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] envlist = - py{37,38,39,310,311}-test{,-all}{,-dev,-latest,-oldest}{,-cov} - py{37,38,39,310,311}-test-numpy{116,117,118,119,120,121,122,123,124} - py{37,38,39,310,311}-test-scipy{12,13,14,15,16,17,18,19,110} - py{37,38,39,310,311}-test-astropy{40,41,42,43,50,51,52} + 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{40,41,42,43,50,51,52,53,60,61,70} build_docs linkcheck codestyle @@ -11,8 +11,6 @@ requires = setuptools >= 30.3.0 pip >= 19.3.1 isolated_build = true -indexserver = - NIGHTLY = https://pypi.anaconda.org/scipy-wheels-nightly/simple [testenv] @@ -38,24 +36,27 @@ description = latest: with the latest supported version of key dependencies oldest: with the oldest supported version of key dependencies cov: and test coverage - numpy116: with numpy 1.16.* - 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.* numpy122: with numpy 1.22.* numpy123: with numpy 1.23.* numpy124: with numpy 1.24.* - scipy12: with scipy 1.2.* - scipy13: with scipy 1.3.* - scipy14: with scipy 1.4.* - scipy15: with scipy 1.5.* + numpy125: with numpy 1.25.* + 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.* scipy19: with scipy 1.9.* scipy110: with scipy 1.10.* + scipy111: with scipy 1.11.* + scipy112: with scipy 1.12.* + scipy113: with scipy 1.13.* + scipy114: with scipy 1.14.* + scipy114: with scipy 1.15.* astropy40: with astropy 4.0.* astropy41: with astropy 4.1.* astropy42: with astropy 4.2.* @@ -63,29 +64,36 @@ description = astropy50: with astropy 5.0.* astropy51: with astropy 5.1.* astropy52: with astropy 5.2.* + 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 = - numpy116: numpy==1.16.* - numpy117: numpy==1.17.* - numpy118: numpy==1.18.* numpy119: numpy==1.19.* numpy120: numpy==1.20.* numpy121: numpy==1.21.* numpy122: numpy==1.22.* numpy123: numpy==1.23.* numpy124: numpy==1.24.* + numpy125: numpy==1.25.* + numpy125: numpy==1.26.* + numpy20: numpy==2.0.* + numpy21: numpy==2.1.* + numpy22: numpy==2.2.* - scipy12: scipy==1.2.* - scipy13: scipy==1.3.* - scipy14: scipy==1.4.* - scipy15: scipy==1.5.* scipy16: scipy==1.6.* scipy17: scipy==1.7.* scipy18: scipy==1.8.* scipy19: scipy==1.9.* scipy110: scipy==1.10.* + scipy111: scipy==1.11.* + scipy112: scipy==1.12.* + scipy113: scipy==1.13.* + scipy114: scipy==1.14.* + scipy114: scipy==1.15.* astropy40: astropy==4.0.* astropy41: astropy==4.1.* @@ -94,18 +102,22 @@ deps = astropy50: astropy==5.0.* astropy51: astropy==5.1.* astropy52: astropy==5.2.* + astropy53: astropy==5.3.* + astropy60: astropy==6.0.* + astropy61: astropy==6.1.* + astropy70: astropy==7.0.* - dev: :NIGHTLY:numpy - dev: :NIGHTLY:scipy + dev: numpy + dev: scipy dev: git+https://github.com/astropy/astropy.git#egg=astropy - latest: astropy==5.2.* - latest: numpy==1.24.* - latest: scipy==1.10.* + latest: astropy==7.0.* + latest: numpy==2.2.* + latest: scipy==1.15.* oldest: astropy==4.0.* - oldest: numpy==1.16.* - oldest: scipy==1.2.* + oldest: numpy==1.19.* + oldest: scipy==1.6.* # The following indicates which extras_require from setup.cfg will be installed extras = @@ -121,6 +133,13 @@ commands = !cov: pytest --pyargs skypy {toxinidir}/docs {posargs} cov: pytest --pyargs skypy {toxinidir}/docs --cov skypy --cov-config={toxinidir}/setup.cfg {posargs} +# For dev environment, use scipy-nightly-wheels as the default index server (for numpy and scipy) +# and PyPI as the extra index server (for all other dependencies, except astropy which is installed +# directly from GitHub). +setenv = + dev: PIP_INDEX_URL = https://pypi.anaconda.org/scipy-wheels-nightly/simple + dev: PIP_EXTRA_INDEX_URL = https://pypi.org/simple + [testenv:build_docs] changedir = docs description = invoke sphinx-build to build the HTML docs