From 9c9418d9f0e5d3f17bcf35153e0b35db7092d4da Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:55:10 -0500 Subject: [PATCH 1/3] MNT: Infrastructure and other updates. Replace broken URL and deprecated np.trapz usage. Ignore link that is blocking CI check. --- .flake8 | 2 + .github/workflows/cron-tests.yml | 59 ++++------ .github/workflows/tox-tests.yml | 88 ++++++-------- .readthedocs.yml => .readthedocs.yaml | 4 +- CHANGES.rst | 31 +++-- MANIFEST.in | 7 +- README.rst | 21 ++-- conftest.py | 25 ++++ docs/conf.py | 45 +++----- pyproject.toml | 105 ++++++++++++++++- setup.cfg | 107 ------------------ setup.py | 66 ----------- specreduce/background.py | 2 +- specreduce/calibration_data.py | 12 +- specreduce/conftest.py | 78 ++++++++++++- specreduce/core.py | 4 +- specreduce/extract.py | 6 +- specreduce/fluxcal.py | 16 ++- specreduce/table_utils.py | 7 +- ...det_image_seq5_MIRIMAGE_P750Lexp1_s2d.fits | Bin 0 -> 72000 bytes specreduce/tests/test_background.py | 29 ++--- specreduce/tests/test_extinction.py | 8 +- specreduce/tests/test_extract.py | 19 +--- .../tests/test_get_reference_file_path.py | 2 +- specreduce/tests/test_image_parsing.py | 51 +++------ specreduce/tests/test_linelists.py | 41 +++---- specreduce/tests/test_specphot_stds.py | 5 +- specreduce/tests/test_synth_data.py | 8 +- specreduce/tests/test_tracing.py | 2 +- .../tests/test_wavelength_calibration.py | 9 +- specreduce/tracing.py | 4 +- specreduce/utils/synth_data.py | 16 +-- specreduce/wavelength_calibration.py | 12 +- tox.ini | 63 +++++------ 34 files changed, 433 insertions(+), 521 deletions(-) create mode 100644 .flake8 rename .readthedocs.yml => .readthedocs.yaml (87%) create mode 100644 conftest.py delete mode 100644 setup.cfg delete mode 100755 setup.py create mode 100644 specreduce/tests/data/transposed_det_image_seq5_MIRIMAGE_P750Lexp1_s2d.fits diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..7da1f960 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 100 diff --git a/.github/workflows/cron-tests.yml b/.github/workflows/cron-tests.yml index 84530154..2bb4da17 100644 --- a/.github/workflows/cron-tests.yml +++ b/.github/workflows/cron-tests.yml @@ -5,48 +5,31 @@ name: Weekly Tests on: + pull_request: + # We also want this workflow triggered if the 'Extra CI' label is added + # or present when PR is updated + types: + - synchronize + - labeled schedule: # run every Monday at 6am UTC - cron: '0 6 * * 1' -env: - TOXARGS: '-v' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: - # Set up matrix to run tox tests across lists of os, python version, and tox environment - matrix_tests: - runs-on: ${{ matrix.os }} - strategy: - matrix: - # Github actions supports ubuntu, windows, and macos virtual environments: - # https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners - # - # Only run on ubuntu by default, but can add other os's to the test matrix here. - # For example -- os: [ubuntu-latest, macos-latest, windows-latest] - include: - - os: ubuntu-latest - python: '3.11' - tox_env: 'linkcheck' - - os: ubuntu-latest - python: '3.12' - tox_env: 'py312-test-devdeps' - - os: ubuntu-latest - python: '3.12' - tox_env: 'py312-test-predeps' + tests: + if: (github.repository == 'astropy/specreduce' && (github.event_name == 'schedule' || github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'Extra CI'))) + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 + with: + submodules: false + coverage: '' + envs: | + - name: Check URLs in docs + linux: linkcheck - steps: - - name: Check out repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Set up python ${{ matrix.python }} with tox environment ${{ matrix.tox_env }} on ${{ matrix.os }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python }} - - name: Install base dependencies - run: | - python -m pip install --upgrade pip - python -m pip install tox - - name: Test with tox - run: | - tox -e ${{ matrix.tox_env }} + - name: Python 3.12 on Linux with pre-releases + linux: py312-test-alldeps-predeps + toxargs: -v diff --git a/.github/workflows/tox-tests.yml b/.github/workflows/tox-tests.yml index 3005622d..cc2ee2fa 100644 --- a/.github/workflows/tox-tests.yml +++ b/.github/workflows/tox-tests.yml @@ -11,64 +11,44 @@ on: tags: - '*' pull_request: + schedule: + # run every Monday at 6am UTC + - cron: '0 6 * * 1' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -env: - TOXARGS: '-v' - jobs: - # Set up matrix to run tox tests across lists of os, python version, and tox environment - matrix_tests: - runs-on: ${{ matrix.os }} - strategy: - matrix: - # Github actions supports ubuntu, windows, and macos virtual environments: - # https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners - # - # Only run on ubuntu by default, but can add other os's to the test matrix here. - # For example -- os: [ubuntu-latest, macos-latest, windows-latest] - include: - - os: ubuntu-latest - python: '3.10' - tox_env: 'py310-test-cov' - - os: ubuntu-latest - python: '3.11' - tox_env: 'py311-test' - - os: ubuntu-latest - python: '3.12' - tox_env: 'py312-test' - - os: macos-latest - python: '3.12' - tox_env: 'py312-test-devdeps' - - os: ubuntu-latest - python: '3.12' - tox_env: 'codestyle' - - os: ubuntu-latest - python: '3.10' - tox_env: 'py310-test-oldestdeps' + tests: + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV }} + with: + submodules: false + coverage: '' + envs: | + - name: Codestyle + linux: codestyle + + - name: Python 3.8 on Linux with oldest supported dependencies + linux: py38-test-alldeps-oldestdeps + toxargs: -v + + - name: Python 3.9 on Windows with minimal dependencies + windows: py39-test + toxargs: -v + + - name: Python 3.10 on OSX with minimal dependencies + macos: py310-test + toxargs: -v + + - name: Python 3.11 on Linux with all dependencies, remote data, and coverage + linux: py311-test-alldeps-cov + coverage: codecov + toxargs: -v + posargs: --remote-data=any - steps: - - name: Check out repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Set up python ${{ matrix.python }} with tox environment ${{ matrix.tox_env }} on ${{ matrix.os }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python }} - - name: Install base dependencies - run: | - python -m pip install --upgrade pip - python -m pip install tox - - name: Test with tox - run: | - tox -e ${{ matrix.tox_env }} - - name: Upload coverage to codecov - if: "contains(matrix.tox_env, '-cov')" - uses: codecov/codecov-action@v3 - with: - file: ./coverage.xml - verbose: true + - name: (Allowed Failure) Python 3.12 on Linux with dev dependencies + linux: py312-test-devdeps + toxargs: -v diff --git a/.readthedocs.yml b/.readthedocs.yaml similarity index 87% rename from .readthedocs.yml rename to .readthedocs.yaml index b9cf0085..16476ed2 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yaml @@ -1,11 +1,11 @@ version: 2 build: - os: ubuntu-20.04 + os: ubuntu-22.04 apt_packages: - graphviz tools: - python: "3.10" + python: "3.11" sphinx: builder: html diff --git a/CHANGES.rst b/CHANGES.rst index 9d005521..4ce17f25 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,30 +5,37 @@ New Features ^^^^^^^^^^^^ - Added 'interpolated_profile' option for HorneExtract. If The ``interpolated_profile`` option -is used, the image will be sampled in various wavelength bins (set by -``n_bins_interpolated_profile``), averaged in those bins, and samples are then -interpolated between (linear by default, interpolation degree can be set with -the ``interp_degree_interpolated_profile`` parameter) to generate a continuously varying -spatial profile that can be evaluated at any wavelength.[ #173] + is used, the image will be sampled in various wavelength bins (set by + ``n_bins_interpolated_profile``), averaged in those bins, and samples are then + interpolated between (linear by default, interpolation degree can be set with + the ``interp_degree_interpolated_profile`` parameter) to generate a continuously varying + spatial profile that can be evaluated at any wavelength. [#173] API Changes ^^^^^^^^^^^ -- Fit residuals exposed for wavelength calibration in WavelengthCalibration1D.fit_residuals. [#446] +- Fit residuals exposed for wavelength calibration in ``WavelengthCalibration1D.fit_residuals``. [#446] Bug Fixes ^^^^^^^^^ - Output 1D spectra from Background no longer include NaNs. Output 1D -spectra from BoxcarExtract no longer include NaNs when none are present -in the extraction window. NaNs in the window will still propagate to -BoxcarExtract's extracted 1D spectrum. [#159] + spectra from BoxcarExtract no longer include NaNs when none are present + in the extraction window. NaNs in the window will still propagate to + BoxcarExtract's extracted 1D spectrum. [#159] -- Backgrounds using median statistic properly ignore zero-weighted pixels -[#159] +- Backgrounds using median statistic properly ignore zero-weighted pixels. + [#159] -- HorneExtract now accepts 'None' as a vaild option for bkgrd_prof [#171] +- HorneExtract now accepts 'None' as a vaild option for ``bkgrd_prof``. [#171] +Other changes +^^^^^^^^^^^^^ + +- The following packages are now optional dependencies because they are not + required for core functionality: ``matplotlib``, ``photutils``, ``synphot``. + To install them anyway, use the ``[all]`` specifier when you install specreduce; e.g.: + ``pip install specreduce[all]`` [#202] 1.3.0 (2022-12-05) ------------------ diff --git a/MANIFEST.in b/MANIFEST.in index 8c13ad01..a43828c6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,16 +1,11 @@ include README.rst include CHANGES.rst -include setup.cfg -include LICENSE.rst include pyproject.toml -recursive-include specreduce *.pyx *.c *.pxd recursive-include docs * recursive-include licenses * -recursive-include scripts * +prune notebook_sandbox prune build prune docs/_build prune docs/api - -global-exclude *.pyc *.o diff --git a/README.rst b/README.rst index f58f086d..b13adbf3 100644 --- a/README.rst +++ b/README.rst @@ -1,22 +1,27 @@ Specreduce ========== -.. image:: https://github.com/astropy/specreduce/workflows/Python%20Tests/badge.svg - :target: https://github.com/astropy/specreduce/actions +.. image:: https://github.com/astropy/specreduce/actions/workflows/tox-tests.yml/badge.svg?branch=main + :target: https://github.com/astropy/specreduce/actions/workflows/tox-tests.yml :alt: CI Status +.. image:: https://codecov.io/gh/astropy/specreduce/graph/badge.svg?token=3fLGjZ2Pe0 + :target: https://codecov.io/gh/astropy/specreduce + :alt: Coverage + .. image:: https://readthedocs.org/projects/specreduce/badge/?version=latest - :target: http://specreduce.readthedocs.io/en/latest/?badge=latest + :target: http://specreduce.readthedocs.io/en/latest/ :alt: Documentation Status -.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.6608788.svg - :target: https://doi.org/10.5281/zenodo.6608788 - :alt: Zenodo DOI 10.5281/zenodo.6608788 +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.6608787.svg + :target: https://zenodo.org/doi/10.5281/zenodo.6608787 + :alt: Zenodo DOI 10.5281/zenodo.6608787 .. image:: http://img.shields.io/badge/powered%20by-AstroPy-orange.svg?style=flat - :target: http://www.astropy.org/ + :target: http://www.astropy.org/ + :alt: Powered by Astropy -Specreduce is an Astropy affiliated package with the goal of providing a shared +Specreduce is an Astropy coordinated package with the goal of providing a shared set of Python utilities that can be used to reduce and calibrate spectroscopic data. License diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..4dd5b004 --- /dev/null +++ b/conftest.py @@ -0,0 +1,25 @@ +"""Need to repeat the astropy header config here for tox.""" + +try: + from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS # noqa: E501 + ASTROPY_HEADER = True +except ImportError: + ASTROPY_HEADER = False + + +def pytest_configure(config): + + if ASTROPY_HEADER: + + config.option.astropy_header = True + + # Customize the following lines to add/remove entries from the list of + # packages for which version numbers are displayed when running the tests. # noqa: E501 + PYTEST_HEADER_MODULES.pop('Pandas', None) + PYTEST_HEADER_MODULES['scikit-image'] = 'skimage' + PYTEST_HEADER_MODULES['astropy'] = 'astropy' + PYTEST_HEADER_MODULES['photutils'] = 'photutils' + PYTEST_HEADER_MODULES['synphot'] = 'synphot' + + from specreduce import __version__ + TESTED_VERSIONS["specreduce"] = __version__ diff --git a/docs/conf.py b/docs/conf.py index ea4f7f0b..0468ad32 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,10 +25,10 @@ # Thus, any C-extensions that are needed to build the documentation will *not* # be accessible, and the documentation will not build correctly. -import os import sys import datetime -from importlib import import_module + +from specreduce import __version__ try: from sphinx_astropy.conf.v1 import * # noqa @@ -36,13 +36,6 @@ print('ERROR: the documentation requires the sphinx-astropy package to be installed') sys.exit(1) -# Get configuration information from setup.cfg -from configparser import ConfigParser -conf = ConfigParser() - -conf.read([os.path.join(os.path.dirname(__file__), '..', 'setup.cfg')]) -setup_cfg = dict(conf.items('metadata')) - # -- General configuration ---------------------------------------------------- # By default, highlight as Python 3. @@ -67,22 +60,19 @@ # -- Project information ------------------------------------------------------ # This does not *have* to match the package name, but typically does -project = setup_cfg['name'] -author = setup_cfg['author'] +project = "specreduce" +author = "Astropy Specreduce contributors" copyright = '{0}, {1}'.format( - datetime.datetime.now().year, setup_cfg['author']) + datetime.datetime.now().year, author) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -import_module(setup_cfg['name']) -package = sys.modules[setup_cfg['name']] - # The short X.Y version. -version = package.__version__.split('-', 1)[0] +version = __version__.split('-', 1)[0] # The full version, including alpha/beta/rc tags. -release = package.__version__ +release = __version__ # -- Options for HTML output -------------------------------------------------- @@ -154,19 +144,6 @@ # -- Options for the edit_on_github extension --------------------------------- -if setup_cfg.get('edit_on_github').lower() == 'true': - - extensions += ['sphinx_astropy.ext.edit_on_github'] - - edit_on_github_project = setup_cfg['github_project'] - edit_on_github_branch = "main" - - edit_on_github_source_root = "" - edit_on_github_doc_root = "docs" - -# -- Resolving issue number to links in changelog ----------------------------- -github_issues_url = 'https://github.com/{0}/issues/'.format(setup_cfg['github_project']) - # -- Turn on nitpicky mode for sphinx (to warn about references not found) ---- # nitpicky = True @@ -199,3 +176,11 @@ # dtype, target = line.split(None, 1) # target = target.strip() # nitpick_ignore.append((dtype, six.u(target))) + +# -- Options for linkcheck output ------------------------------------------- +linkcheck_retry = 5 +linkcheck_ignore = [ + "https://www.aanda.org/", +] +linkcheck_timeout = 180 +linkcheck_anchors = False diff --git a/pyproject.toml b/pyproject.toml index c83a0e56..7d46af60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,110 @@ -[build-system] +[project] +name = "specreduce" +dynamic = ["version"] +authors = [ + { name = "Astropy Specreduce contributors", email = "astropy-dev@googlegroups.com" } +] +license = {file = "licenses/LICENSE.rst"} +description = "Astropy coordinated package for Spectroscopic Reductions" +readme = "README.rst" +requires-python = ">=3.8" +dependencies = [ + "numpy", + "astropy", + "scipy", + "specutils>=1.9.1", +] + +[project.optional-dependencies] +test = [ + "pytest-astropy", + "photutils", +] +docs = [ + "sphinx-astropy", + "matplotlib", + "photutils", + "synphot", +] +all = [ + "matplotlib", + "photutils", + "synphot", +] + +[project.urls] +Homepage = "http://astropy.org/" +Repository = "https://github.com/astropy/specreduce.git" +Documentation = "https://specreduce.readthedocs.io/" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.packages] +find = {} # Scanning implicit namespaces is active by default +[tool.setuptools.package-data] +"specreduce.tests" = ["data/*.fits"] + +[tool.setuptools_scm] +version_file = "specreduce/version.py" + +[build-system] requires = ["setuptools", "setuptools_scm", ] - build-backend = 'setuptools.build_meta' [tool.pytest.ini_options] +minversion = 7.0 +testpaths = [ + "specreduce", + "docs", +] +astropy_header = true +doctest_plus = "enabled" +text_file_format = "rst" +addopts = [ + "--color=yes", + "--doctest-rst", +] +xfail_strict = true +filterwarnings = [ + "error", + "ignore:numpy\\.ufunc size changed:RuntimeWarning", + "ignore:numpy\\.ndarray size changed:RuntimeWarning", + "ignore:Can\\'t import specreduce_data package", + # Python 3.12 warning from dateutil imported by matplotlib + "ignore:.*utcfromtimestamp:DeprecationWarning", +] + +[tool.coverage] + + [tool.coverage.run] + omit = [ + "specreduce/_astropy_init*", + "specreduce/conftest.py", + "specreduce/tests/*", + "specreduce/version*", + "*/specreduce/_astropy_init*", + "*/specreduce/conftest.py", + "*/specreduce/tests/*", + "*/specreduce/version*", + ] -filterwarnings = ["ignore::DeprecationWarning:datetime",] + [tool.coverage.report] + exclude_lines = [ + # Have to re-enable the standard pragma + "pragma: no cover", + # Don't complain about packages we have installed + "except ImportError", + # Don't complain if tests don't hit defensive assertion code: + "raise AssertionError", + "raise NotImplementedError", + # Don't complain about script hooks + "'def main(.*):'", + # Ignore branches that don't pertain to this version of Python + "pragma: py{ignore_python_version}", + # Don't complain about IPython completion helper + "def _ipython_key_completions_", + ] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 3f0fd7d5..00000000 --- a/setup.cfg +++ /dev/null @@ -1,107 +0,0 @@ -[metadata] -name = specreduce -author = Astropy Specreduce contributors -author_email = astropy-dev@googlegroups.com -license = BSD 3-Clause -license_file = licenses/LICENSE.rst -url = http://astropy.org/ -description = Astropy affiliated package for Spectroscopic Reductions -long_description = file: README.rst -long_description_content_type = text/x-rst -edit_on_github = False -github_project = astropy/specreduce - -[options] -zip_safe = False -packages = find: -python_requires = >=3.8 -setup_requires = setuptools_scm -install_requires = - astropy - specutils>=1.9.1 - synphot - matplotlib - photutils - pyparsing - -[options.entry_points] - -[options.extras_require] -test = - pytest-astropy -docs = - sphinx-astropy - -[options.package_data] -specreduce = - datasets/* - datasets/line_lists/* - datasets/line_lists/NIST/* - datasets/line_lists/iraf/* - datasets/line_lists/iraf/air/* - datasets/line_lists/iraf/vacuum/* - datasets/onedstds/* - datasets/onedstds/blackbody/* - datasets/onedstds/bstdscal/* - datasets/onedstds/ctio/* - datasets/onedstds/ctiocal/* - datasets/onedstds/ctionewcal/* - datasets/onedstds/iidscal/* - datasets/onedstds/irscal/* - datasets/onedstds/oke1990/* - datasets/onedstds/redcal/* - datasets/onedstds/spec16cal/* - datasets/onedstds/spec50cal/* - datasets/onedstds/spechayescal/* - datasets/onedstds/snfactory/* - datasets/onedstds/gemini/* - datasets/onedstds/eso/* - datasets/onedstds/eso/Xshooter/* - datasets/onedstds/eso/ctiostan/* - datasets/onedstds/eso/hststan/* - datasets/onedstds/eso/okestan/* - datasets/onedstds/eso/wdstan/* - -[tool:pytest] -testpaths = "specreduce" "docs" -astropy_header = true -doctest_plus = enabled -text_file_format = rst -addopts = --doctest-rst -filterwarnings = - error - ignore:Can\'t import specreduce_data package: - ignore:numpy.ndarray size changed: - -[coverage:run] -omit = - specreduce/_astropy_init* - specreduce/conftest.py - specreduce/*setup_package* - specreduce/tests/* - specreduce/*/tests/* - specreduce/extern/* - specreduce/version* - */specreduce/_astropy_init* - */specreduce/conftest.py - */specreduce/*setup_package* - */specreduce/tests/* - */specreduce/*/tests/* - */specreduce/extern/* - */specreduce/version* - -[coverage:report] -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - # Don't complain about packages we have installed - except ImportError - # Don't complain if tests don't hit assertions - raise AssertionError - raise NotImplementedError - # Don't complain about script hooks - def main\(.*\): - # Ignore branches that don't pertain to this version of Python - pragma: py{ignore_python_version} - # Don't complain about IPython completion helper - def _ipython_key_completions_ diff --git a/setup.py b/setup.py deleted file mode 100755 index 7d32a44d..00000000 --- a/setup.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -# NOTE: The configuration for the package, including the name, version, and -# other information are set in the setup.cfg file. - -import os -import sys - -from setuptools import setup - - -# First provide helpful messages if contributors try and run legacy commands -# for tests or docs. - -TEST_HELP = """ -Note: running tests is no longer done using 'python setup.py test'. Instead -you will need to run: - - tox -e test - -If you don't already have tox installed, you can install it with: - - pip install tox - -If you only want to run part of the test suite, you can also use pytest -directly with:: - - pip install -e .[test] - pytest - -For more information, see: - - http://docs.astropy.org/en/latest/development/testguide.html#running-tests -""" - -if 'test' in sys.argv: - print(TEST_HELP) - sys.exit(1) - -DOCS_HELP = """ -Note: building the documentation is no longer done using -'python setup.py build_docs'. Instead you will need to run: - - tox -e build_docs - -If you don't already have tox installed, you can install it with: - - pip install tox - -You can also build the documentation with Sphinx directly using:: - - pip install -e .[docs] - cd docs - make html - -For more information, see: - - http://docs.astropy.org/en/latest/install.html#builddocs -""" - -if 'build_docs' in sys.argv or 'build_sphinx' in sys.argv: - print(DOCS_HELP) - sys.exit(1) - -setup(use_scm_version={'write_to': os.path.join('specreduce', 'version.py')}) diff --git a/specreduce/background.py b/specreduce/background.py index 526f46c6..77c6a980 100644 --- a/specreduce/background.py +++ b/specreduce/background.py @@ -4,9 +4,9 @@ from dataclasses import dataclass, field import numpy as np +from astropy import units as u from astropy.nddata import NDData from astropy.utils.decorators import deprecated_attribute -from astropy import units as u from specutils import Spectrum1D from specreduce.core import _ImageParser diff --git a/specreduce/calibration_data.py b/specreduce/calibration_data.py index 339feb89..f90a4a46 100644 --- a/specreduce/calibration_data.py +++ b/specreduce/calibration_data.py @@ -6,13 +6,11 @@ import warnings import numpy as np - -import astropy.units as u +from astropy import units as u from astropy.table import Table, vstack, QTable from astropy.utils.data import download_file from astropy.utils.exceptions import AstropyUserWarning -import synphot from specutils import Spectrum1D from specutils.utils.wcs_utils import vac_to_air @@ -111,7 +109,7 @@ def get_reference_file_path( cache : bool (default: False) Set whether file is cached if file is downloaded. - repo_url : str (default: https://raw.githubusercontent.com/astropy/specreduce-data) + repo_url : str Base repository URL for the reference data. repo_branch : str (default: main) @@ -298,6 +296,8 @@ def load_MAST_calspec(filename, cache=True, show_progress=False): If ``remote`` is True, the spectrum will be downloaded from MAST. Set ``remote`` to False to load a local file. + .. note:: This function requires ``synphot`` to be installed separately. + Parameters ---------- filename : str @@ -333,8 +333,10 @@ def load_MAST_calspec(filename, cache=True, show_progress=False): if file_path is None: return None else: + import synphot _, wave, flux = synphot.specio.read_fits_spec(file_path) + # DEV: pllim does not think this is necessary at all but whatever. # the calspec data stores flux in synphot's FLAM units. convert to flux units # supported directly by astropy.units. mJy is chosen since it's the JWST # standard and can easily be converted to/from AB magnitudes. @@ -524,7 +526,7 @@ class AtmosphericTransmission(AtmosphericExtinction): two columns, wavelength and transmission (unscaled dimensionless). If this isn't provided, a model is built from a pre-calculated table of values from 0.9 to 5.6 microns. The values were generated by the ATRAN model, - https://atran.arc.nasa.gov/cgi-bin/atran/atran.cgi (Lord, S. D., 1992, NASA + https://ntrs.nasa.gov/citations/19930010877 (Lord, S. D., 1992, NASA Technical Memorandum 103957). The extinction is given as a linear transmission fraction at an airmass of 1 and 1 mm of precipitable water. diff --git a/specreduce/conftest.py b/specreduce/conftest.py index 559241af..e69a7295 100644 --- a/specreduce/conftest.py +++ b/specreduce/conftest.py @@ -3,10 +3,13 @@ # get picked up when running the tests inside an interpreter using # packagename.test -import astropy.units as u import numpy as np import pytest -from specutils import Spectrum1D +from astropy import units as u +from astropy.io import fits +from astropy.nddata import CCDData, NDData, VarianceUncertainty +from astropy.utils.data import get_pkg_data_filename +from specutils import Spectrum1D, SpectralAxis try: from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS # noqa: E501 @@ -15,6 +18,72 @@ ASTROPY_HEADER = False +# Test image is comprised of 30 rows with 10 columns each. Row content +# is row index itself. This makes it easy to predict what should be the +# value extracted from a region centered at any arbitrary Y position. +def _mk_test_data(imgtype, nrows=30, ncols=10): + image_ones = np.ones(shape=(nrows, ncols)) + image = image_ones.copy() + for j in range(nrows): + image[j, ::] *= j + if imgtype == "raw": + pass # no extra processing + elif imgtype == "ccddata": + image = CCDData(image, unit=u.Jy) + else: # spectrum + flux = image * u.DN + uncert = VarianceUncertainty(image_ones) + if imgtype == "spec_no_axis": + image = Spectrum1D(flux, uncertainty=uncert) + else: # "spec" + image = Spectrum1D(flux, spectral_axis=np.arange(ncols) * u.um, uncertainty=uncert) + return image + + +@pytest.fixture +def mk_test_img_raw(): + return _mk_test_data("raw") + + +@pytest.fixture +def mk_test_img(): + return _mk_test_data("ccddata") + + +@pytest.fixture +def mk_test_spec_no_spectral_axis(): + return _mk_test_data("spec_no_axis") + + +@pytest.fixture +def mk_test_spec_with_spectral_axis(): + return _mk_test_data("spec") + + +# Test data file already transposed like this: +# fn = download_file('https://stsci.box.com/shared/static/exnkul627fcuhy5akf2gswytud5tazmw.fits', cache=True) # noqa: E501 +# img = fits.getdata(fn).T +@pytest.fixture +def all_images(): + np.random.seed(7) + + filename = get_pkg_data_filename( + "data/transposed_det_image_seq5_MIRIMAGE_P750Lexp1_s2d.fits", package="specreduce.tests") + img = fits.getdata(filename) + flux = img * (u.MJy / u.sr) + sax = SpectralAxis(np.linspace(14.377, 3.677, flux.shape[-1]) * u.um) + unc = VarianceUncertainty(np.random.rand(*flux.shape)) + + all_images = {} + all_images['arr'] = img + all_images['s1d'] = Spectrum1D(flux, spectral_axis=sax, uncertainty=unc) + all_images['s1d_pix'] = Spectrum1D(flux, uncertainty=unc) + all_images['ccd'] = CCDData(img, uncertainty=unc, unit=flux.unit) + all_images['ndd'] = NDData(img, uncertainty=unc, unit=flux.unit) + all_images['qnt'] = img * flux.unit + return all_images + + @pytest.fixture def spec1d(): np.random.seed(7) @@ -56,6 +125,9 @@ def pytest_configure(config): # packages for which version numbers are displayed when running the tests. # noqa: E501 PYTEST_HEADER_MODULES.pop('Pandas', None) PYTEST_HEADER_MODULES['scikit-image'] = 'skimage' + PYTEST_HEADER_MODULES['astropy'] = 'astropy' + PYTEST_HEADER_MODULES['photutils'] = 'photutils' + PYTEST_HEADER_MODULES['synphot'] = 'synphot' - from . import __version__ + from specreduce import __version__ TESTED_VERSIONS["specreduce"] = __version__ diff --git a/specreduce/core.py b/specreduce/core.py index 69fe01ca..a6ed2a29 100644 --- a/specreduce/core.py +++ b/specreduce/core.py @@ -1,11 +1,11 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import inspect -import numpy as np +from dataclasses import dataclass +import numpy as np from astropy import units as u from astropy.nddata import VarianceUncertainty -from dataclasses import dataclass from specutils import Spectrum1D __all__ = ['SpecreduceOperation'] diff --git a/specreduce/extract.py b/specreduce/extract.py index 07b74c7e..cabe856f 100644 --- a/specreduce/extract.py +++ b/specreduce/extract.py @@ -4,15 +4,15 @@ from dataclasses import dataclass, field import numpy as np - from astropy import units as u from astropy.modeling import Model, models, fitting from astropy.nddata import NDData, VarianceUncertainty +from scipy.integrate import trapezoid from scipy.interpolate import RectBivariateSpline +from specutils import Spectrum1D from specreduce.core import SpecreduceOperation from specreduce.tracing import Trace, FlatTrace -from specutils import Spectrum1D __all__ = ['BoxcarExtract', 'HorneExtract', 'OptimalExtract'] @@ -795,7 +795,7 @@ def __call__(self, image=None, trace_object=None, else: # interpolated_profile fitted_col = interp_spatial_prof(col_pix, xd_pixels) kernel_vals.append(fitted_col) - norms.append(np.trapz(fitted_col, dx=1)[0]) + norms.append(trapezoid(fitted_col, dx=1)[0]) # transform fit-specific information kernel_vals = np.vstack(kernel_vals).T diff --git a/specreduce/fluxcal.py b/specreduce/fluxcal.py index 02d98fd9..aee181fb 100644 --- a/specreduce/fluxcal.py +++ b/specreduce/fluxcal.py @@ -1,16 +1,12 @@ import os import numpy as np - -import matplotlib.pyplot as plt - +from astropy import units as u from astropy.constants import c as cc from astropy.table import Table -import astropy.units as u - from scipy.interpolate import UnivariateSpline - from specutils import Spectrum1D + from specreduce.core import SpecreduceOperation @@ -151,8 +147,8 @@ def onedstd(self, stdstar): subdirectory and file name. For example: - >>> standard_sensfunc(obj_wave, obj_flux, stdstar='spec50cal/bd284211.dat', \ - mode='spline') # doctest: +SKIP + + >>> standard_sensfunc(obj_wave, obj_flux, stdstar='spec50cal/bd284211.dat', mode='spline') # doctest: +SKIP If no std is supplied, or an improper path is given, raises a ValueError. @@ -160,7 +156,7 @@ def onedstd(self, stdstar): ------- standard: astropy.talbe.Table A table with the onedstd data. - """ + """ # noqa: E501 std_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'datasets', 'onedstds') @@ -198,6 +194,7 @@ def standard_sensfunc(self, standard, mode='linear', polydeg=9, (Default is 9.) display : bool, optional If True, plot the sensfunc. (Default is False.) + This requires ``matplotlib`` to be installed. badlines : array-like list A list of values (lines) to mask-out of when generating sensfunc. @@ -260,6 +257,7 @@ def standard_sensfunc(self, standard, mode='linear', polydeg=9, sensfunc_spec = Spectrum1D(spectral_axis=obj_wave, flux=sensfunc_out) if display is True: + import matplotlib.pyplot as plt plt.figure() plt.plot(obj_wave, obj_flux * sensfunc_out, c="C0", label="Observed x sensfunc", alpha=0.5) diff --git a/specreduce/table_utils.py b/specreduce/table_utils.py index 118bc552..76fa5346 100644 --- a/specreduce/table_utils.py +++ b/specreduce/table_utils.py @@ -1,8 +1,9 @@ -"""Utility functions to parse main NIST table. -""" +"""Utility functions to parse main NIST table.""" -from astropy.table import Table, vstack import numpy as np +from astropy.table import Table, vstack + +__all__ = [] def sort_table_by_element(table, elem_list): diff --git a/specreduce/tests/data/transposed_det_image_seq5_MIRIMAGE_P750Lexp1_s2d.fits b/specreduce/tests/data/transposed_det_image_seq5_MIRIMAGE_P750Lexp1_s2d.fits new file mode 100644 index 0000000000000000000000000000000000000000..5a7fb248d0a78c391ea3950cfac0f1b000c8305b GIT binary patch literal 72000 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 J{(rgx{{?@yCEfr4 literal 0 HcmV?d00001 diff --git a/specreduce/tests/test_background.py b/specreduce/tests/test_background.py index 6302facd..11095d59 100644 --- a/specreduce/tests/test_background.py +++ b/specreduce/tests/test_background.py @@ -1,29 +1,16 @@ -import pytest import numpy as np - -import astropy.units as u -from astropy.nddata import VarianceUncertainty +import pytest from specutils import Spectrum1D from specreduce.background import Background from specreduce.tracing import FlatTrace, ArrayTrace -# NOTE: same test image as in test_extract.py -# Test image is comprised of 30 rows with 10 columns each. Row content -# is row index itself. This makes it easy to predict what should be the -# value extracted from a region centered at any arbitrary Y position. -img = np.ones(shape=(30, 10)) -for j in range(img.shape[0]): - img[j, ::] *= j -image = Spectrum1D(img * u.DN, - uncertainty=VarianceUncertainty(np.ones_like(img))) -image_um = Spectrum1D(image.flux, - spectral_axis=np.arange(image.data.shape[1]) * u.um, - uncertainty=VarianceUncertainty(np.ones_like(image.data))) - - -def test_background(): +def test_background(mk_test_img_raw, mk_test_spec_no_spectral_axis, + mk_test_spec_with_spectral_axis): + img = mk_test_img_raw + image = mk_test_spec_no_spectral_axis + image_um = mk_test_spec_with_spectral_axis # # Try combinations of extraction center, and even/odd # extraction aperture sizes. @@ -92,7 +79,9 @@ def test_background(): assert np.isnan(bg.sub_spectrum().flux).sum() == 0 -def test_warnings_errors(): +def test_warnings_errors(mk_test_spec_no_spectral_axis): + image = mk_test_spec_no_spectral_axis + # image.shape (30, 10) with pytest.warns(match="background window extends beyond image boundaries"): Background.two_sided(image, 25, 4, width=3) diff --git a/specreduce/tests/test_extinction.py b/specreduce/tests/test_extinction.py index 000587c4..53b6b1a0 100644 --- a/specreduce/tests/test_extinction.py +++ b/specreduce/tests/test_extinction.py @@ -1,11 +1,9 @@ -import pytest - import numpy as np - -import astropy.units as u +import pytest +from astropy import units as u from astropy.utils.exceptions import AstropyUserWarning -from ..calibration_data import ( +from specreduce.calibration_data import ( AtmosphericExtinction, AtmosphericTransmission, SUPPORTED_EXTINCTION_MODELS diff --git a/specreduce/tests/test_extract.py b/specreduce/tests/test_extract.py index da3d4c88..4465a1bf 100644 --- a/specreduce/tests/test_extract.py +++ b/specreduce/tests/test_extract.py @@ -1,9 +1,8 @@ import numpy as np import pytest - -import astropy.units as u +from astropy import units as u from astropy.modeling import models -from astropy.nddata import CCDData, VarianceUncertainty, UnknownUncertainty +from astropy.nddata import VarianceUncertainty, UnknownUncertainty from astropy.tests.helper import assert_quantity_allclose from specreduce.extract import ( @@ -12,20 +11,6 @@ from specreduce.tracing import FlatTrace, ArrayTrace -# Test image is comprised of 30 rows with 10 columns each. Row content -# is row index itself. This makes it easy to predict what should be the -# value extracted from a region centered at any arbitrary Y position. - - -@pytest.fixture -def mk_test_img(nrows=30, ncols=10): - image = np.ones(shape=(nrows, ncols)) - for j in range(image.shape[0]): - image[j, ::] *= j - image = CCDData(image, unit=u.Jy) - return image - - def add_gaussian_source(image, amps=2, stddevs=2, means=None): """ Modify `image.data` to add a horizontal spectrum across the image. diff --git a/specreduce/tests/test_get_reference_file_path.py b/specreduce/tests/test_get_reference_file_path.py index bea0d823..d619f2c5 100644 --- a/specreduce/tests/test_get_reference_file_path.py +++ b/specreduce/tests/test_get_reference_file_path.py @@ -1,6 +1,6 @@ import pytest -from ..calibration_data import get_reference_file_path, get_pypeit_data_path +from specreduce.calibration_data import get_reference_file_path, get_pypeit_data_path @pytest.mark.remote_data diff --git a/specreduce/tests/test_image_parsing.py b/specreduce/tests/test_image_parsing.py index 8f83210f..39765f37 100644 --- a/specreduce/tests/test_image_parsing.py +++ b/specreduce/tests/test_image_parsing.py @@ -1,42 +1,18 @@ import numpy as np - from astropy import units as u -from astropy.io import fits -from astropy.nddata import CCDData, NDData, VarianceUncertainty -from astropy.utils.data import download_file +from specutils import Spectrum1D from specreduce.extract import HorneExtract from specreduce.tracing import FlatTrace -from specutils import Spectrum1D, SpectralAxis - - -# fetch test image -fn = download_file('https://stsci.box.com/shared/static/exnkul627fcuhy5akf2gswytud5tazmw.fits', - cache=True) - -# duplicate image in all accepted formats -# (one Spectrum1D variant has a physical spectral axis; the other is in pixels) -img = fits.getdata(fn).T -flux = img * u.MJy / u.sr -sax = SpectralAxis(np.linspace(14.377, 3.677, flux.shape[-1]) * u.um) -unc = VarianceUncertainty(np.random.rand(*flux.shape)) - -all_images = {} -all_images['arr'] = img -all_images['s1d'] = Spectrum1D(flux, spectral_axis=sax, uncertainty=unc) -all_images['s1d_pix'] = Spectrum1D(flux, uncertainty=unc) -all_images['ccd'] = CCDData(img, uncertainty=unc, unit=flux.unit) -all_images['ndd'] = NDData(img, uncertainty=unc, unit=flux.unit) -all_images['qnt'] = img * flux.unit - -# save default values used for spectral axis and uncertainty when they are not -# available from the image object or provided by the user -sax_def = np.arange(img.shape[1]) * u.pix -unc_def = np.ones_like(img) # (for use inside tests) -def compare_images(key, collection, compare='s1d'): +def compare_images(all_images, key, collection, compare='s1d'): + # save default values used for spectral axis and uncertainty when they are not + # available from the image object or provided by the user + unc_def = np.ones_like(all_images['arr']) + sax_def = np.arange(unc_def.shape[1]) * u.pix + # was input converted to Spectrum1D? assert isinstance(collection[key], Spectrum1D), (f"image '{key}' not " "of type Spectrum1D") @@ -71,16 +47,19 @@ def compare_images(key, collection, compare='s1d'): # test consistency of general image parser results -def test_parse_general(): +def test_parse_general(all_images): all_images_parsed = {k: FlatTrace._parse_image(object, im) for k, im in all_images.items()} - for key in all_images_parsed.keys(): - compare_images(key, all_images_parsed) + compare_images(all_images, key, all_images_parsed) # use verified general image parser results to check HorneExtract's image parser -def test_parse_horne(): +def test_parse_horne(all_images): + # save default values used for uncertainty when it is + # available from the image object or provided by the user + unc_def = np.ones_like(all_images['arr']) + # HorneExtract's parser is more stringent than the general one, hence the # separate test. Given proper inputs, both should produce the same results. images_collection = {k: {} for k in all_images.keys()} @@ -102,4 +81,4 @@ def test_parse_horne(): col[key] = HorneExtract._parse_image(object, img, **defaults) - compare_images(key, col, compare='general') + compare_images(all_images, key, col, compare='general') diff --git a/specreduce/tests/test_linelists.py b/specreduce/tests/test_linelists.py index 61e1ca45..70b89f66 100644 --- a/specreduce/tests/test_linelists.py +++ b/specreduce/tests/test_linelists.py @@ -1,6 +1,6 @@ import pytest -from ..calibration_data import load_pypeit_calibration_lines +from specreduce.calibration_data import load_pypeit_calibration_lines @pytest.mark.remote_data @@ -10,16 +10,15 @@ def test_pypeit_single(): """ line_tab = load_pypeit_calibration_lines('HeI', cache=True, show_progress=False) assert line_tab is not None - if line_tab is not None: - assert "HeI" in line_tab['ion'] - assert sorted(list(line_tab.columns)) == [ - 'Instr', - 'NIST', - 'Source', - 'amplitude', - 'ion', - 'wave' - ] + assert "HeI" in line_tab['ion'] + assert sorted(list(line_tab.columns)) == [ + 'Instr', + 'NIST', + 'Source', + 'amplitude', + 'ion', + 'wave' + ] @pytest.mark.remote_data @@ -29,9 +28,8 @@ def test_pypeit_list(): """ line_tab = load_pypeit_calibration_lines(['HeI', 'NeI'], cache=True, show_progress=False) assert line_tab is not None - if line_tab is not None: - assert "HeI" in line_tab['ion'] - assert "NeI" in line_tab['ion'] + assert "HeI" in line_tab['ion'] + assert "NeI" in line_tab['ion'] @pytest.mark.remote_data @@ -39,10 +37,9 @@ def test_pypeit_empty(): """ Test to make sure None is returned if an empty list is passed. """ - with pytest.warns() as record: + with pytest.warns(UserWarning, match='No calibration lines'): line_tab = load_pypeit_calibration_lines([], cache=True, show_progress=False) - assert line_tab is None - assert 'No calibration lines' in record[0].message.args[0] + assert line_tab is None @pytest.mark.remote_data @@ -50,10 +47,8 @@ def test_pypeit_input_validation(): """ Check that bad inputs for ``pypeit`` linelists raise the appropriate warnings and exceptions """ - with pytest.raises(ValueError, match=r'.*Invalid calibration lamps specification.*'): - _ = load_pypeit_calibration_lines({}, cache=True, show_progress=False) + with pytest.raises(ValueError, match='.*Invalid calibration lamps specification.*'): + load_pypeit_calibration_lines({}, cache=True, show_progress=False) - with pytest.warns() as record: - _ = load_pypeit_calibration_lines(['HeI', 'ArIII'], cache=True, show_progress=False) - if not record: - pytest.fails("Expected warning about nonexistant linelist for ArIII.") + with pytest.warns(UserWarning, match="ArIII not in the list of supported calibration line lists"): # noqa: E501 + load_pypeit_calibration_lines(['HeI', 'ArIII'], cache=True, show_progress=False) diff --git a/specreduce/tests/test_specphot_stds.py b/specreduce/tests/test_specphot_stds.py index 868315ba..ea153264 100644 --- a/specreduce/tests/test_specphot_stds.py +++ b/specreduce/tests/test_specphot_stds.py @@ -1,9 +1,6 @@ import pytest -from ..calibration_data import ( - load_MAST_calspec, - load_onedstds -) +from specreduce.calibration_data import load_MAST_calspec, load_onedstds @pytest.mark.remote_data diff --git a/specreduce/tests/test_synth_data.py b/specreduce/tests/test_synth_data.py index 276be972..a90efe15 100644 --- a/specreduce/tests/test_synth_data.py +++ b/specreduce/tests/test_synth_data.py @@ -1,10 +1,10 @@ import pytest - -from specreduce.utils.synth_data import make_2d_trace_image, make_2d_arc_image, make_2d_spec_image -from astropy.nddata import CCDData +from astropy import units as u from astropy.modeling import models +from astropy.nddata import CCDData from astropy.wcs import WCS -import astropy.units as u + +from specreduce.utils.synth_data import make_2d_trace_image, make_2d_arc_image, make_2d_spec_image def test_make_2d_trace_image(): diff --git a/specreduce/tests/test_tracing.py b/specreduce/tests/test_tracing.py index a609677b..63bcabfe 100644 --- a/specreduce/tests/test_tracing.py +++ b/specreduce/tests/test_tracing.py @@ -1,7 +1,7 @@ import numpy as np import pytest - from astropy.modeling import models + from specreduce.utils.synth_data import make_2d_trace_image from specreduce.tracing import Trace, FlatTrace, ArrayTrace, FitTrace diff --git a/specreduce/tests/test_wavelength_calibration.py b/specreduce/tests/test_wavelength_calibration.py index 7accf9dd..539e2ea3 100644 --- a/specreduce/tests/test_wavelength_calibration.py +++ b/specreduce/tests/test_wavelength_calibration.py @@ -1,12 +1,11 @@ -from numpy.testing import assert_allclose import numpy as np import pytest - -from astropy.table import QTable -import astropy.units as u -from astropy.modeling.models import Polynomial1D +from astropy import units as u from astropy.modeling.fitting import LinearLSQFitter +from astropy.modeling.models import Polynomial1D +from astropy.table import QTable from astropy.tests.helper import assert_quantity_allclose +from numpy.testing import assert_allclose from specreduce import WavelengthCalibration1D diff --git a/specreduce/tracing.py b/specreduce/tracing.py index eadbbf37..a508dd20 100644 --- a/specreduce/tracing.py +++ b/specreduce/tracing.py @@ -1,14 +1,14 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst +import warnings from copy import deepcopy from dataclasses import dataclass, field -import warnings +import numpy as np from astropy.modeling import Model, fitting, models from astropy.nddata import NDData from astropy.stats import gaussian_sigma_to_fwhm from astropy.utils.decorators import deprecated -import numpy as np from specreduce.core import _ImageParser diff --git a/specreduce/utils/synth_data.py b/specreduce/utils/synth_data.py index cf5b73fd..06dc199e 100644 --- a/specreduce/utils/synth_data.py +++ b/specreduce/utils/synth_data.py @@ -1,14 +1,11 @@ # Licensed under a 3-clause BSD style license - see ../../licenses/LICENSE.rst import numpy as np - -from photutils.datasets import apply_poisson_noise - -import astropy.units as u +from astropy import units as u from astropy.modeling import models from astropy.nddata import CCDData -from astropy.wcs import WCS from astropy.stats import gaussian_fwhm_to_sigma +from astropy.wcs import WCS from specreduce.calibration_data import load_pypeit_calibration_lines @@ -54,7 +51,7 @@ def make_2d_trace_image( Power index of the source's Moffat profile. Use small number here to emulate extended source. add_noise : bool (default=True) - If True, add Poisson noise to the image + If True, add Poisson noise to the image; requires ``photutils`` to be installed. Returns ------- ccd_im : `~astropy.nddata.CCDData` @@ -76,6 +73,7 @@ def make_2d_trace_image( z = background + profile(trace) if add_noise: + from photutils.datasets import apply_poisson_noise trace_image = apply_poisson_noise(z) else: trace_image = z @@ -135,7 +133,7 @@ def make_2d_arc_image( The tilt function to apply along the cross-dispersion axis to simulate tilted or curved emission lines. add_noise : bool (default=True) - If True, add Poisson noise to the image + If True, add Poisson noise to the image; requires ``photutils`` to be installed. Returns ------- @@ -315,6 +313,7 @@ def make_2d_arc_image( z += line_mod(yy) if add_noise: + from photutils.datasets import apply_poisson_noise arc_image = apply_poisson_noise(z) else: arc_image = z @@ -387,7 +386,7 @@ def make_2d_spec_image( Power index of the source's Moffat profile. Use small number here to emulate extended source. add_noise : bool (default=True) - If True, add Poisson noise to the image + If True, add Poisson noise to the image; requires ``photutils`` to be installed. """ arc_image = make_2d_arc_image( nx=nx, @@ -419,6 +418,7 @@ def make_2d_spec_image( spec_image = arc_image.data + trace_image.data + background if add_noise: + from photutils.datasets import apply_poisson_noise spec_image = apply_poisson_noise(spec_image) ccd_im = CCDData(spec_image, unit=u.count, wcs=arc_image.wcs) diff --git a/specreduce/wavelength_calibration.py b/specreduce/wavelength_calibration.py index 43696c92..e52ec206 100644 --- a/specreduce/wavelength_calibration.py +++ b/specreduce/wavelength_calibration.py @@ -1,14 +1,14 @@ -from astropy.modeling.models import Linear1D +from functools import cached_property + +import numpy as np +from astropy import units as u from astropy.modeling.fitting import LMLSQFitter, LinearLSQFitter +from astropy.modeling.models import Linear1D from astropy.table import QTable, hstack -import astropy.units as u -from functools import cached_property -from gwcs import wcs from gwcs import coordinate_frames as cf -import numpy as np +from gwcs import wcs from specutils import Spectrum1D - __all__ = ['WavelengthCalibration1D'] diff --git a/tox.ini b/tox.ini index 29a45ded..e4798867 100644 --- a/tox.ini +++ b/tox.ini @@ -1,21 +1,16 @@ [tox] envlist = - py{310,311,312}-test{,-devdeps,-predeps}{,-cov} - build_docs + py{38,39,310,311,312}-test{,-alldeps}{,-oldestdeps,-devdeps,-predeps}{,-cov} + linkcheck codestyle -requires = - setuptools - pip >= 19.3.1 -isolated_build = true [testenv] # Pass through the following environment variables which may be needed for the CI -passenv = HOME,WINDIR,LC_ALL,LC_CTYPE,CC,CI +passenv = HOME,WINDIR,CI setenv = - devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple - py312: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple + devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/liberfa/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple # Run the tests in a temporary directory to make sure that we don't import # this package from the source tree @@ -31,56 +26,50 @@ changedir = .tmp/{envname} # description = run tests - devdeps: with the latest developer version of key dependencies + alldeps: with optional dependencies oldestdeps: with the oldest supported version of key dependencies - cov: enable remote data and measure test coverage + devdeps: with the latest developer version of key dependencies + predeps: with pre-releases of key dependencies + cov: with test coverage # The following provides some specific pinnings for key packages deps = devdeps: numpy>=0.0.dev0 devdeps: scipy>=0.0.dev0 + devdeps: pyerfa>=0.0.dev0 devdeps: astropy>=0.0.dev0 devdeps: git+https://github.com/astropy/specutils.git#egg=specutils devdeps: git+https://github.com/astropy/photutils.git#egg=photutils + devdeps: git+https://github.com/spacetelescope/synphot_refactor.git#egg=synphot - oldestdeps: numpy==1.22.4 - oldestdeps: astropy==5.1 - oldestdeps: scipy==1.8.0 - oldestdeps: matplotlib==3.5 - oldestdeps: photutils==1.0.0 - oldestdeps: specutils==1.9.1 - - # Currently need dev astropy with python 3.12 as well - py312: astropy>=0.0.dev0 + oldestdeps: numpy==1.22.* + oldestdeps: astropy==5.1.* + oldestdeps: scipy==1.8.* + oldestdeps: matplotlib==3.5.* + oldestdeps: photutils==1.0.* + oldestdeps: specutils==1.9.* # The following indicates which extras_require from setup.cfg will be installed extras = - test: test - build_docs: docs + test + alldeps: all + +install_command = + !devdeps: python -I -m pip install + # Force dev dependency with C-extension (synphot) to also build with numpy-dev + devdeps: python -I -m pip install -v --pre commands = - # Force numpy-dev after matplotlib downgrades it (https://github.com/matplotlib/matplotlib/issues/26847) - devdeps: python -m pip install --pre --upgrade --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy - # Maybe we also have to do this for scipy? - devdeps: python -m pip install --pre --upgrade --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple scipy pip freeze !cov: pytest --pyargs specreduce {toxinidir}/docs {posargs} - cov: pytest --pyargs specreduce {toxinidir}/docs --cov specreduce --cov-config={toxinidir}/setup.cfg --remote-data {posargs} + cov: pytest --pyargs specreduce {toxinidir}/docs --cov specreduce --cov-config={toxinidir}/pyproject.toml {posargs} cov: coverage xml -o {toxinidir}/coverage.xml pip_pre = predeps: true !predeps: false -[testenv:build_docs] -changedir = docs -description = invoke sphinx-build to build the HTML docs -extras = docs -commands = - pip freeze - sphinx-build -W -b html . _build/html - [testenv:linkcheck] changedir = docs description = check the links in the HTML docs @@ -92,6 +81,6 @@ commands = [testenv:codestyle] skip_install = true changedir = . -description = check code style, e.g. with flake8 +description = check code style, e.g., with flake8 deps = flake8 -commands = flake8 specreduce --count --max-line-length=100 +commands = flake8 specreduce --count From 8508eeb787eeab0d279f19535718e3f0f9d56959 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 5 Dec 2023 17:38:03 -0500 Subject: [PATCH 2/3] Clean up test header --- conftest.py | 7 ++++--- specreduce/conftest.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/conftest.py b/conftest.py index 4dd5b004..3d6b45de 100644 --- a/conftest.py +++ b/conftest.py @@ -1,7 +1,7 @@ """Need to repeat the astropy header config here for tox.""" try: - from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS # noqa: E501 + from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS ASTROPY_HEADER = True except ImportError: ASTROPY_HEADER = False @@ -14,10 +14,11 @@ def pytest_configure(config): config.option.astropy_header = True # Customize the following lines to add/remove entries from the list of - # packages for which version numbers are displayed when running the tests. # noqa: E501 + # packages for which version numbers are displayed when running the tests. PYTEST_HEADER_MODULES.pop('Pandas', None) - PYTEST_HEADER_MODULES['scikit-image'] = 'skimage' + PYTEST_HEADER_MODULES.pop('h5py', None) PYTEST_HEADER_MODULES['astropy'] = 'astropy' + PYTEST_HEADER_MODULES['specutils'] = 'specutils' PYTEST_HEADER_MODULES['photutils'] = 'photutils' PYTEST_HEADER_MODULES['synphot'] = 'synphot' diff --git a/specreduce/conftest.py b/specreduce/conftest.py index e69a7295..4bbe96f5 100644 --- a/specreduce/conftest.py +++ b/specreduce/conftest.py @@ -12,7 +12,7 @@ from specutils import Spectrum1D, SpectralAxis try: - from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS # noqa: E501 + from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS ASTROPY_HEADER = True except ImportError: ASTROPY_HEADER = False @@ -122,10 +122,11 @@ def pytest_configure(config): config.option.astropy_header = True # Customize the following lines to add/remove entries from the list of - # packages for which version numbers are displayed when running the tests. # noqa: E501 + # packages for which version numbers are displayed when running the tests. PYTEST_HEADER_MODULES.pop('Pandas', None) - PYTEST_HEADER_MODULES['scikit-image'] = 'skimage' + PYTEST_HEADER_MODULES.pop('h5py', None) PYTEST_HEADER_MODULES['astropy'] = 'astropy' + PYTEST_HEADER_MODULES['specutils'] = 'specutils' PYTEST_HEADER_MODULES['photutils'] = 'photutils' PYTEST_HEADER_MODULES['synphot'] = 'synphot' From bbf377d6f99b784c6dc36518be8c116064cf4765 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:51:39 -0500 Subject: [PATCH 3/3] DOC: Style logo like specutils --- docs/_static/logo_icon.ico | Bin 0 -> 4414 bytes docs/_static/logo_icon.png | Bin 0 -> 62134 bytes docs/_static/specreduce.css | 15 +++++++++++++++ docs/conf.py | 13 +++++++++---- 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 docs/_static/logo_icon.ico create mode 100644 docs/_static/logo_icon.png create mode 100644 docs/_static/specreduce.css diff --git a/docs/_static/logo_icon.ico b/docs/_static/logo_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..468670e7f96d34c4d3b28c1e352eb8fce97f545c GIT binary patch literal 4414 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 Ci5Fu4 literal 0 HcmV?d00001 diff --git a/docs/_static/logo_icon.png b/docs/_static/logo_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7ea90d6ed24a52a62bf89067ca4f9e1d7858881f GIT binary patch literal 62134 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 literal 0 HcmV?d00001 diff --git a/docs/_static/specreduce.css b/docs/_static/specreduce.css new file mode 100644 index 00000000..229d8787 --- /dev/null +++ b/docs/_static/specreduce.css @@ -0,0 +1,15 @@ +@import url("bootstrap-astropy.css"); + +div.topbar a.brand { + background: transparent url("logo_icon.png") no-repeat 8px 3px; + background-image: url("logo_icon.png"), none; + background-size: 32px 32px; +} + +#logotext1 { + color: #519EA8; +} + +#logotext2 { + color: #FF5000; +} diff --git a/docs/conf.py b/docs/conf.py index 0468ad32..6649e7f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -93,17 +93,20 @@ # a list of builtin themes. To override the custom theme, set this to the # name of a builtin theme or the name of a custom theme in html_theme_path. #html_theme = None +html_static_path = ['_static'] # html_theme = None +html_style = 'specreduce.css' html_theme_options = { - 'logotext1': 'specreduce', # white, semi-bold - 'logotext2': '', # orange, light + 'logotext1': 'spec', # white, semi-bold + 'logotext2': 'reduce', # orange, light 'logotext3': ':docs' # white, light } - # Custom sidebar templates, maps document names to template names. #html_sidebars = {} +html_sidebars['**'] = ['localtoc.html'] +html_sidebars['index'] = ['globaltoc.html', 'localtoc.html'] # The name of an image file (relative to this directory) to place at the top # of the sidebar. @@ -112,7 +115,7 @@ # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = '' +html_favicon = '_static/logo_icon.ico' # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. @@ -125,6 +128,8 @@ # Output file base name for HTML help builder. htmlhelp_basename = project + 'doc' +# Prefixes that are ignored for sorting the Python module index +modindex_common_prefix = ["specreduce."] # -- Options for LaTeX output -------------------------------------------------