diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8ac6b8c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..419c4d9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,106 @@ +name: CI + +on: + push: + branches: + - 'main' + - '*.*' + - '!*backport*' + tags: + - 'v*' + - '!*dev*' + - '!*pre*' + - '!*post*' + pull_request: + # Allow manual runs through the web UI + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + core: + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main + with: + submodules: false + coverage: codecov + toxdeps: tox-pypi-filter + posargs: -n auto + envs: | + - linux: py311 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + test: + needs: [core] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main + with: + submodules: false + coverage: codecov + toxdeps: tox-pypi-filter + posargs: -n auto + envs: | + - windows: py310 + - macos: py39 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + docs: + needs: [core] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main + with: + default_python: '3.9' + submodules: false + pytest: false + toxdeps: tox-pypi-filter + cache-path: | + docs/_build/ + docs/generated/ + .tox/sample_data/ + libraries: | + apt: + - graphviz + envs: | + - linux: build_docs + + online: + if: "!startsWith(github.event.ref, 'refs/tags/v')" + needs: [docs] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main + with: + default_python: '3.9' + submodules: false + coverage: codecov + toxdeps: tox-pypi-filter + posargs: -n auto --dist loadgroup + libraries: | + apt: + - libopenjp2-7 + envs: | + - linux: py39-online + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + publish: + # Build wheels when pushing to any branch except main + # publish.yml will only publish if tagged ^v.* + if: | + ( + github.event_name != 'pull_request' && ( + github.ref_name != 'main' || + github.event_name == 'workflow_dispatch' + ) + ) || ( + github.event_name == 'pull_request' && + contains(github.event.pull_request.labels.*.name, 'Run publish') + ) + needs: [test] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish_pure_python.yml@main + with: + python-version: "3.10" + test_extras: 'all,tests' + test_command: 'pytest -p no:warnings --doctest-rst -m "not mpl_image_compare" --pyargs sunpy' + submodules: false + secrets: + pypi_token: ${{ secrets.pypi_token }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 5c15e70..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,71 +0,0 @@ -image: python:3.10 - -before_script: - - pip install "tox<4.0" tox-pypi-filter - -stages: - - Base - - Extras - - Release - -codestyle: - stage: Base - script: - - tox -e codestyle - -py310: - stage: Base - script: - - tox -e py310 - -py310-online: - stage: Extras - script: - - tox -e py310-online - - pip install --upgrade codecov - - codecov - -py38: - stage: Extras - image: python:3.8 - script: - - tox -e py38 - -py311-devdeps: - allow_failure: true - stage: Extras - image: python:3.11 - before_script: - # Need graphviz binary for generating inheritance diagrams - - apt-get update -qq && apt-get install -y -qq libhdf5-dev - - pip install "tox<4.0" tox-pypi-filter - script: - - tox -e py311-devdeps - -#py310-rc-online: -# stage: Extras -# image: python:3.10 -# script: -# - tox -e py310-rc-online - -build_docs: - stage: Extras - before_script: - # Need graphviz binary for generating inheritance diagrams - - apt-get update -qq && apt-get install -y -qq graphviz - - pip install "tox<4.0" tox-pypi-filter - script: - - tox -e build_docs - -pypi_upload: - stage: Release - rules: - - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+/' # A specific tag with 'vX.Y.Z' pattern is created - before_script: - - pip install --upgrade twine pep517 - script: - - python -m pep517.build --source --out-dir dist . - - twine upload dist/aiapy*.tar.gz - -.common: - interruptible: true diff --git a/.rtd-environment.yml b/.rtd-environment.yml index a27c7b7..309e230 100644 --- a/.rtd-environment.yml +++ b/.rtd-environment.yml @@ -2,6 +2,6 @@ name: rtd_aiapy channels: - conda-forge dependencies: - - python=3.10 + - python=3.11 - pip - graphviz!=2.42.*,!=2.43.* diff --git a/.sunpy-template.yml b/.sunpy-template.yml deleted file mode 100644 index 0326416..0000000 --- a/.sunpy-template.yml +++ /dev/null @@ -1,13 +0,0 @@ -default_context: - package_name: aiapy - module_name: aiapy - short_description: Python package for AIA analysis. - author_name: AIA Instrument Team - author_email: cheung@lmsal.com - license: BSD 3-Clause - project_url: https://gitlab.com/LMSAL_HUB/aia_hub/aiapy - github_repo: sunpy/aiapy - minimum_python_version: 3.7 - use_compiled_extensions: n - include_example_code: n - _sphinx_theme: sphinx_rtd_theme diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 12a56f0..7208b88 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,18 +1,18 @@ -# Contributor Covenant Code of Conduct +# Contributor Covenant Code of Conduct # -## Our Pledge +## Our Pledge ## We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual identity -and orientation. +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. -## Our Standards +## Our Standards ## Examples of behavior that contributes to a positive environment for our community include: @@ -22,21 +22,21 @@ community include: * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community +* Focusing on what is best not just for us as individuals, but for the overall + community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or - advances of any kind +* The use of sexualized language or imagery, and sexual attention or advances of + any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission +* Publishing others' private information, such as a physical or email address, + without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting -## Enforcement Responsibilities +## Enforcement Responsibilities ## Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in @@ -48,7 +48,7 @@ comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. -## Scope +## Scope ## This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. @@ -56,7 +56,63 @@ Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. -## Attribution +## Enforcement ## + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines ## + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction ## + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning ## + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban ## + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban ## + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution ## This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at @@ -66,8 +122,8 @@ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available -at [https://www.contributor-covenant.org/translations][translations]. +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html diff --git a/LICENSE.rst b/LICENSE.rst index fb3c530..27ff4a7 100644 --- a/LICENSE.rst +++ b/LICENSE.rst @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021, AIA Instrument Team +Copyright (c) 2020-2023, AIA Instrument Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/aiapy/__init__.py b/aiapy/__init__.py index b2aa681..1eb0a5c 100644 --- a/aiapy/__init__.py +++ b/aiapy/__init__.py @@ -1,8 +1,8 @@ from pathlib import Path -from .version import version as __version__ - from itertools import compress +from .version import version as __version__ + _SSW_MIRROR = "https://sohoftp.nascom.nasa.gov/solarsoft/" diff --git a/aiapy/calibrate/meta.py b/aiapy/calibrate/meta.py index 8b5167e..b7f7fe4 100644 --- a/aiapy/calibrate/meta.py +++ b/aiapy/calibrate/meta.py @@ -4,10 +4,9 @@ import copy import warnings -import numpy as np - import astropy.time import astropy.units as u +import numpy as np from astropy.coordinates import CartesianRepresentation, HeliocentricMeanEcliptic, SkyCoord from sunpy.map import contains_full_disk diff --git a/aiapy/calibrate/prep.py b/aiapy/calibrate/prep.py index 8079484..501578b 100644 --- a/aiapy/calibrate/prep.py +++ b/aiapy/calibrate/prep.py @@ -3,9 +3,8 @@ """ import warnings -import numpy as np - import astropy.units as u +import numpy as np from sunpy.map import contains_full_disk from sunpy.map.sources.sdo import AIAMap, HMIMap from sunpy.util.decorators import add_common_docstring diff --git a/aiapy/calibrate/spikes.py b/aiapy/calibrate/spikes.py index 058f476..63999c4 100644 --- a/aiapy/calibrate/spikes.py +++ b/aiapy/calibrate/spikes.py @@ -1,10 +1,9 @@ import copy import warnings +import astropy.units as u import drms import numpy as np - -import astropy.units as u from astropy.io import fits from astropy.wcs.utils import pixel_to_pixel from sunpy.map.mapbase import PixelPair @@ -110,7 +109,7 @@ def respike(smap, spikes=None): ) -def fetch_spikes(smap, as_coords=False): +def fetch_spikes(smap, *, as_coords=False): """ Returns coordinates and values of removed spikes. diff --git a/aiapy/calibrate/tests/test_meta.py b/aiapy/calibrate/tests/test_meta.py index ba30611..d6ffb43 100644 --- a/aiapy/calibrate/tests/test_meta.py +++ b/aiapy/calibrate/tests/test_meta.py @@ -1,7 +1,6 @@ +import astropy.units as u import numpy as np import pytest - -import astropy.units as u from astropy.coordinates.sky_coordinate import SkyCoord from astropy.table import QTable from astropy.time import Time, TimeDelta diff --git a/aiapy/calibrate/tests/test_prep.py b/aiapy/calibrate/tests/test_prep.py index 2874fdb..848007b 100644 --- a/aiapy/calibrate/tests/test_prep.py +++ b/aiapy/calibrate/tests/test_prep.py @@ -1,11 +1,10 @@ import copy import tempfile -import numpy as np -import pytest - import astropy.time import astropy.units as u +import numpy as np +import pytest import sunpy.data.test from astropy.io.fits.verify import VerifyWarning from sunpy.map import Map @@ -121,7 +120,8 @@ def test_correct_degradation(aia_171_map, correction_table, version): correction_table=correction_table, calibration_version=version, ) - uncorrected_over_corrected = aia_171_map.data / original_corrected.data + with np.errstate(divide="ignore", invalid="ignore"): + uncorrected_over_corrected = aia_171_map.data / original_corrected.data # If intensity is zero, ratio will be NaN/infinite i_valid = aia_171_map.data > 0.0 assert np.allclose(uncorrected_over_corrected[i_valid], d) diff --git a/aiapy/calibrate/tests/test_spikes.py b/aiapy/calibrate/tests/test_spikes.py index dc30941..d95205c 100644 --- a/aiapy/calibrate/tests/test_spikes.py +++ b/aiapy/calibrate/tests/test_spikes.py @@ -1,9 +1,8 @@ import copy +import astropy.units as u import numpy as np import pytest - -import astropy.units as u import sunpy.map from astropy.coordinates import SkyCoord from sunpy.map.mapbase import PixelPair diff --git a/aiapy/calibrate/tests/test_uncertainty.py b/aiapy/calibrate/tests/test_uncertainty.py index 5762784..0643d21 100644 --- a/aiapy/calibrate/tests/test_uncertainty.py +++ b/aiapy/calibrate/tests/test_uncertainty.py @@ -1,12 +1,11 @@ from pathlib import Path from contextlib import nullcontext +import astropy.units as u import numpy as np import pytest from numpy.random import default_rng -import astropy.units as u - from aiapy.calibrate import estimate_error from aiapy.calibrate.util import get_error_table from aiapy.tests.data import get_test_filepath diff --git a/aiapy/calibrate/tests/test_util.py b/aiapy/calibrate/tests/test_util.py index 0a0d889..77ecd86 100644 --- a/aiapy/calibrate/tests/test_util.py +++ b/aiapy/calibrate/tests/test_util.py @@ -1,8 +1,7 @@ -import pytest - import astropy.table import astropy.time import astropy.units as u +import pytest from aiapy.calibrate.util import ( _select_epoch_from_correction_table, diff --git a/aiapy/calibrate/uncertainty.py b/aiapy/calibrate/uncertainty.py index bdafa13..79f0fbd 100644 --- a/aiapy/calibrate/uncertainty.py +++ b/aiapy/calibrate/uncertainty.py @@ -1,12 +1,12 @@ """ Estimate uncertainty on intensities. """ -import numpy as np - import astropy.units as u +import numpy as np from aiapy.util import telescope_number from aiapy.util.decorators import validate_channel + from .util import get_error_table __all__ = ["estimate_error"] @@ -17,6 +17,7 @@ def estimate_error( counts: u.ct / u.pix, channel: u.angstrom, + *, n_sample=1, include_preflight=False, include_eve=False, diff --git a/aiapy/calibrate/util.py b/aiapy/calibrate/util.py index 676c66c..54a2d43 100644 --- a/aiapy/calibrate/util.py +++ b/aiapy/calibrate/util.py @@ -1,15 +1,13 @@ """ Utilities for computing intensity corrections. """ -import os import pathlib import warnings from urllib.parse import urljoin -import numpy as np - import astropy.io.ascii import astropy.units as u +import numpy as np from astropy.table import QTable from astropy.time import Time from sunpy.data import manager @@ -218,10 +216,7 @@ def get_pointing_table(start, end): def get_error_table(error_table=None): if error_table is None: - # This is to work around a parfive bug - os.environ["PARFIVE_DISABLE_RANGE"] = "1" error_table = fetch_error_table() - os.environ.pop("PARFIVE_DISABLE_RANGE") if isinstance(error_table, (str, pathlib.Path)): table = astropy.io.ascii.read(error_table) elif isinstance(error_table, QTable): diff --git a/aiapy/conftest.py b/aiapy/conftest.py index f5c6a38..492fa45 100644 --- a/aiapy/conftest.py +++ b/aiapy/conftest.py @@ -1,6 +1,5 @@ -import pytest - import astropy.units as u +import pytest import sunpy.data.test import sunpy.map diff --git a/aiapy/data/_sample.py b/aiapy/data/_sample.py index bd4c47d..5b1a300 100644 --- a/aiapy/data/_sample.py +++ b/aiapy/data/_sample.py @@ -2,7 +2,6 @@ from urllib.parse import urljoin from parfive import SessionConfig - from sunpy import log from sunpy.util.config import get_and_create_sample_dir from sunpy.util.parfive_helpers import Downloader @@ -66,7 +65,7 @@ def _handle_final_errors(results): log.error(f"Failed to download {_SAMPLE_FILES[file_name]} from all mirrors," "the file will not be available.") -def download_sample_data(overwrite=False): +def download_sample_data(*, overwrite=False): """ Download all sample data at once. This will overwrite any existing files. diff --git a/aiapy/psf/deconvolve.py b/aiapy/psf/deconvolve.py index a825073..c1abf07 100644 --- a/aiapy/psf/deconvolve.py +++ b/aiapy/psf/deconvolve.py @@ -5,7 +5,6 @@ import warnings import numpy as np - from sunpy import log try: @@ -16,12 +15,13 @@ HAS_CUPY = False from aiapy.util import AiapyUserWarning + from .psf import psf as calculate_psf __all__ = ["deconvolve"] -def deconvolve(smap, psf=None, iterations=25, clip_negative=True, use_gpu=True): +def deconvolve(smap, *, psf=None, iterations=25, clip_negative=True, use_gpu=True): """ Deconvolve an AIA image with the point spread function. diff --git a/aiapy/psf/psf.py b/aiapy/psf/psf.py index a856eb2..c7e75d9 100644 --- a/aiapy/psf/psf.py +++ b/aiapy/psf/psf.py @@ -1,9 +1,8 @@ """ Calculate the point spread function (PSF) for the AIA telescopes. """ -import numpy as np - import astropy.units as u +import numpy as np from sunpy import log from aiapy.util.decorators import validate_channel @@ -18,7 +17,7 @@ __all__ = ["psf", "filter_mesh_parameters", "_psf"] -def filter_mesh_parameters(use_preflightcore=False): +def filter_mesh_parameters(*, use_preflightcore=False): """ Geometric parameters for meshes in AIA filters used to calculate the point spread function. @@ -179,7 +178,7 @@ def filter_mesh_parameters(use_preflightcore=False): @u.quantity_input @validate_channel("channel", valid_channels=[94, 131, 171, 193, 211, 304, 335] * u.angstrom) -def psf(channel: u.angstrom, use_preflightcore=False, diffraction_orders=None, use_gpu=True): +def psf(channel: u.angstrom, *, use_preflightcore=False, diffraction_orders=None, use_gpu=True): r""" Calculate the composite PSF for a given channel, including diffraction and core effects. @@ -300,7 +299,7 @@ def psf(channel: u.angstrom, use_preflightcore=False, diffraction_orders=None, u return psf -def _psf(meshinfo, angles, diffraction_orders, focal_plane=False, use_gpu=True): +def _psf(meshinfo, angles, diffraction_orders, *, focal_plane=False, use_gpu=True): psf = np.zeros((4096, 4096), dtype=float) if use_gpu and not HAS_CUPY: log.info("cupy not installed or working, falling back to CPU") diff --git a/aiapy/psf/tests/conftest.py b/aiapy/psf/tests/conftest.py index ba01b06..1677ab7 100644 --- a/aiapy/psf/tests/conftest.py +++ b/aiapy/psf/tests/conftest.py @@ -1,9 +1,8 @@ """ Shared fixtures for PSF tests. """ -import pytest - import astropy.units as u +import pytest import aiapy.psf diff --git a/aiapy/psf/tests/test_deconvolve.py b/aiapy/psf/tests/test_deconvolve.py index a0448d4..eeefedc 100644 --- a/aiapy/psf/tests/test_deconvolve.py +++ b/aiapy/psf/tests/test_deconvolve.py @@ -1,6 +1,5 @@ import numpy as np import pytest - import sunpy.data.test import sunpy.map diff --git a/aiapy/response/channel.py b/aiapy/response/channel.py index 88bb021..29d2150 100644 --- a/aiapy/response/channel.py +++ b/aiapy/response/channel.py @@ -4,10 +4,9 @@ import collections from urllib.parse import urljoin -import numpy as np - import astropy.constants as const import astropy.units as u +import numpy as np from sunpy.data import manager from sunpy.io.special import read_genx from sunpy.util.metadata import MetaDict @@ -373,6 +372,7 @@ def gain( @u.quantity_input def wavelength_response( self, + *, obstime=None, include_eve_correction=False, include_crosstalk=True, diff --git a/aiapy/response/tests/test_channel.py b/aiapy/response/tests/test_channel.py index b86e939..f0183c8 100644 --- a/aiapy/response/tests/test_channel.py +++ b/aiapy/response/tests/test_channel.py @@ -1,10 +1,9 @@ import collections from pathlib import Path -import pytest - import astropy.time import astropy.units as u +import pytest from sunpy.util.metadata import MetaDict from aiapy.calibrate.util import get_correction_table diff --git a/aiapy/util/tests/test_util.py b/aiapy/util/tests/test_util.py index 03477e7..82fb573 100644 --- a/aiapy/util/tests/test_util.py +++ b/aiapy/util/tests/test_util.py @@ -1,5 +1,4 @@ import pytest - from astropy.tests.helper import assert_quantity_allclose import aiapy.util diff --git a/aiapy/util/util.py b/aiapy/util/util.py index 1d68e10..304f8b2 100644 --- a/aiapy/util/util.py +++ b/aiapy/util/util.py @@ -1,10 +1,9 @@ """ Miscellaneous utility functions. """ +import astropy.units as u import drms import numpy as np - -import astropy.units as u from astropy.coordinates import SkyCoord from astropy.time import Time from sunpy.time import parse_time diff --git a/docs/conf.py b/docs/conf.py index 5296ab8..a0484cc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,16 +1,15 @@ # Configuration file for the Sphinx documentation builder. # -- Project information ----------------------------------------------------- -from aiapy import __version__ -from astropy.utils.exceptions import AstropyDeprecationWarning -from datetime import datetime import os import warnings -from sunpy.util.exceptions import ( - SunpyDeprecationWarning, - SunpyPendingDeprecationWarning, -) from pathlib import Path +from datetime import datetime + +from astropy.utils.exceptions import AstropyDeprecationWarning from packaging.version import Version +from sunpy.util.exceptions import SunpyDeprecationWarning, SunpyPendingDeprecationWarning + +from aiapy import __version__ os.environ["JSOC_EMAIL"] = "jsoc@sunpy.org" os.environ["HIDE_PARFIVE_PROGESS"] = "True" diff --git a/examples/calculate_response_function.py b/examples/calculate_response_function.py index 3de8bc2..f69da19 100644 --- a/examples/calculate_response_function.py +++ b/examples/calculate_response_function.py @@ -8,10 +8,9 @@ well as explore the different properties of the telescope channels. """ -import matplotlib.pyplot as plt - import astropy.time import astropy.units as u +import matplotlib.pyplot as plt from aiapy.response import Channel diff --git a/examples/download_specific_data.py b/examples/download_specific_data.py index 9ba44d7..8ff121c 100644 --- a/examples/download_specific_data.py +++ b/examples/download_specific_data.py @@ -14,10 +14,9 @@ import os from pathlib import Path +import astropy.units as u import drms import matplotlib.pyplot as plt - -import astropy.units as u import sunpy.map from aiapy.calibrate import correct_degradation, register, update_pointing diff --git a/examples/instrument_degradation.py b/examples/instrument_degradation.py index 7b38e85..d7d5ea6 100644 --- a/examples/instrument_degradation.py +++ b/examples/instrument_degradation.py @@ -8,11 +8,10 @@ lifetime of the instrument. """ -import matplotlib.pyplot as plt -import numpy as np - import astropy.time import astropy.units as u +import matplotlib.pyplot as plt +import numpy as np from astropy.visualization import time_support from aiapy.calibrate import degradation diff --git a/examples/replace_hot_pixels.py b/examples/replace_hot_pixels.py index 01bb79b..c61ea9a 100644 --- a/examples/replace_hot_pixels.py +++ b/examples/replace_hot_pixels.py @@ -6,9 +6,8 @@ This example demonstrates how to "re-spike" AIA level 1 images """ -import matplotlib.pyplot as plt - import astropy.units as u +import matplotlib.pyplot as plt import sunpy.map from astropy.coordinates import SkyCoord @@ -99,7 +98,7 @@ lon, lat = ax.coords lon.set_axislabel("HPC Longitude") lat.set_axislabel(" ") -lat.set_ticklabel_visible(False) +lat.set_ticklabel_visible(visible=False) plt.show() ########################################################### diff --git a/examples/skip_correct_degradation.py b/examples/skip_correct_degradation.py index b6ba371..2c57e0e 100644 --- a/examples/skip_correct_degradation.py +++ b/examples/skip_correct_degradation.py @@ -6,10 +6,9 @@ This example demonstrates the degradation of the filters on AIA over time. """ -import matplotlib.pyplot as plt - import astropy.time import astropy.units as u +import matplotlib.pyplot as plt from astropy.visualization import quantity_support, time_support from sunpy.net import Fido from sunpy.net import attrs as a diff --git a/examples/skip_psf_deconvolution.py b/examples/skip_psf_deconvolution.py index 22dcd4f..455b9f4 100644 --- a/examples/skip_psf_deconvolution.py +++ b/examples/skip_psf_deconvolution.py @@ -6,9 +6,8 @@ This example demonstrates how to deconvolve an AIA image with the instrument point spread function (PSF). """ -import matplotlib.pyplot as plt - import astropy.units as u +import matplotlib.pyplot as plt import sunpy.map from astropy.coordinates import SkyCoord from astropy.visualization import AsinhStretch, ImageNormalize, LogStretch @@ -84,7 +83,7 @@ m_deconvolved.plot(axes=ax, annotate=False, norm=norm) ax.coords[0].set_axislabel(" ") ax.coords[1].set_axislabel(" ") -ax.coords[1].set_ticklabel_visible(False) +ax.coords[1].set_ticklabel_visible(visible=False) plt.show() ################################################# @@ -107,5 +106,5 @@ m_deconvolved_sub.plot(axes=ax, annotate=False, norm=norm) ax.coords[0].set_axislabel(" ") ax.coords[1].set_axislabel(" ") -ax.coords[1].set_ticklabel_visible(False) +ax.coords[1].set_ticklabel_visible(visible=False) plt.show() diff --git a/joss/make_figure.py b/joss/make_figure.py index 30c1665..9fda16e 100644 --- a/joss/make_figure.py +++ b/joss/make_figure.py @@ -2,11 +2,10 @@ Make the figure for the JOSS paper. """ -import matplotlib.pyplot as plt -import numpy as np - import astropy.time import astropy.units as u +import matplotlib.pyplot as plt +import numpy as np import sunpy.map from astropy.coordinates import SkyCoord from astropy.visualization import AsinhStretch, ImageNormalize, time_support diff --git a/licenses/SUNPY.rst b/licenses/SUNPY.rst deleted file mode 100644 index 5b4d954..0000000 --- a/licenses/SUNPY.rst +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2013-2019 The SunPy developers -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pyproject.toml b/pyproject.toml index 871122c..1a02b17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,33 +1,128 @@ [build-system] +requires = [ + "setuptools", + "setuptools_scm[toml]", + "wheel", +] +build-backend = 'setuptools.build_meta' -requires = ["setuptools", - "setuptools_scm", - "wheel"] +[project] +name = "aiapy" +dynamic = ["version"] +description = "Python library for AIA data analysis." +readme = "README.rst" +requires-python = ">=3.9" +license = {file = "LICENSE.txt"} +keywords = ["solar physics", "solar", "science", "NASA", "SDO", "AIA", "solar dynamics observatory", + "atmospheric imaging assembly"] +authors = [ + {email = "freij@baeri.org"}, + {name = "AIA Instrument Team"} +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Astronomy", +] +dependencies = [ + 'sunpy[net,image,map]>=5.0', +] -build-backend = 'setuptools.build_meta' +[project.urls] +homepage = "https://aia.lmsal.com/" +documentation = "https://aiapy.readthedocs.io/en/stable/" +repository = "https://github.com/LM-SAL/aiapy" +changelog = "https://aiapy.readthedocs.io/en/stable/changelog.html" + +[project.optional-dependencies] +cupy = [ + 'cupy', +] +tests = [ + "hissw", + "pytest", + "pytest-astropy", +] +docs = [ + "sphinx", + "sphinx-automodapi", + "sphinx-changelog", + "sphinx-copybutton", + "sphinx-design", + "sphinx-gallery", + "sphinx-hoverxref", + "sphinxext-opengraph", + "sunpy-sphinx-theme", +] +dev = ["aiapy[all,tests,docs]"] + +[tool.setuptools] +packages = ["aiapy"] + +[tool.setuptools_scm] +write_to = "aiapy/_version.py" + +[tool.setuptools.exclude-package-data] +aiapy = ["aiapy._dev"] + +[tool.pytest.ini_options] +testpaths = [ + "aiapy", + "docs", +] +norecursedirs = [ + ".tox", + "build", + '''docs[\/]_build''', + '''docs[\/]generated''', + "*.egg-info", + "examples", + '''aiapy[/\]_dev''', + ".jupyter", + ".history", +] +doctest_plus = "enabled" +doctest_optionflags = "NORMALIZE_WHITESPACE FLOAT_CMP ELLIPSIS" +addopts = "--doctest-rst --doctest-ignore-import-errors -p no:unraisableexception -p no:threadexception" +markers = [ + "remote_data: marks this test function as needing remote data.", +] +remote_data_strict = "True" +filterwarnings = [ + "error", + "always::pytest.PytestConfigWarning", + "ignore:.*deprecated and slated for removal in Python 3.13", + "ignore:numpy.ufunc size changed:RuntimeWarning", + "ignore:numpy.ndarray size changed:RuntimeWarning", + "ignore:.*unitfix.*", + "ignore::erfa.core.ErfaWarning", + "ignore:invalid value encountered in sqrt", +] [tool.black] line-length = 120 target-version = ['py39'] -exclude = ''' -( - /( - \.eggs - | \.git - | \.mypy_cache - | \.tox - | \.venv - | _build - | build - | dist - | docs - | .history - )/ -) -''' + +[tool.isort] +profile = "black" +line_length = 120 +length_sort = "False" +length_sort_sections = "stdlib" [tool.ruff] -fix = true +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +target-version = "py39" +line-length = 120 exclude = [ ".eggs", ".git", @@ -43,14 +138,6 @@ exclude = [ "venv", "joss", ] - -# Same as Black. -line-length = 120 -target-version = "py39" - -# Allow unused variables when underscore-prefixed. -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -ignore = ["E501"] select = [ "E", "F", @@ -74,49 +161,17 @@ select = [ "RSE", "ERA", ] -fixable = [ - "E", - "F", - "W", - "UP", - "PT", - "RET", - "TID", - "PLE", - "NPY", - "RUF", - "PGH", - "PTH", - "BLE", - "FBT", - "B", - "A", - "COM", - "C4", - "T20", - "RSE", - "ERA", -] +ignore = ["E501"] extend-ignore = [ - # pytest (PT) - "PT001", # Always use pytest.fixture() - "PT004", # Fixtures which don't return anything should have leading _ - "PT023", # Always use () on pytest decorators - # General "PGH004", # NOQA IS THE BEST OF ALL TIME - "FBT003", # Boolean positional value in function call - "FBT002", # Boolean default value in function definition ] [tool.ruff.per-file-ignores] "examples/*.py" = [ "T201", # We need print in our examples ] -"setup.py" = ["INP001"] # Part of configuration, not a package. -"conftest.py" = ["INP001"] # Part of configuration, not a package. "docs/*.py" = [ "INP001", # implicit-namespace-package. The examples are not a package. - "F405", # `html_theme_options` may be undefined, or defined from star imports: `sunpy_sphinx_theme.conf` "A001", # Variable `copyright` is shadowing a python builtin ] "aiapy/data/sample.py" = [ @@ -124,11 +179,18 @@ extend-ignore = [ "PLE0604", # Invalid object in `__all__`, must contain only strings ] +[tool.ruff.pydocstyle] +convention = "numpy" + +[tool.codespell] +ignore-words-list = "emiss" + [tool.towncrier] package = "aiapy" filename = "CHANGELOG.rst" directory = "changelog/" - issue_format = "`#{issue} `__" + issue_format = "`#{issue} `__" + title_format = "{version} ({project_date})" [[tool.towncrier.type]] directory = "breaking" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 72ba3b8..0000000 --- a/setup.cfg +++ /dev/null @@ -1,145 +0,0 @@ -[metadata] -name = aiapy -author = AIA Instrument Team -author_email = cheung@lmsal.com -license = BSD 3-Clause -license_files = LICENSE.rst -url = https://gitlab.com/LMSAL_HUB/aia_hub/aiapy -gitlab_project = 'LMSAL_HUB/aia_hub/aiapy' -keywords = solar physics, solar, science, sun, sdo, aia, solar dynamics observatory, atmospheric imaging assembly -classifiers = - Development Status :: 4 - Beta - Intended Audience :: Science/Research - License :: OSI Approved :: BSD License - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Topic :: Scientific/Engineering :: Astronomy -description = Python package for AIA analysis. -long_description = file: README.rst - -[options] -zip_safe = False -packages = find: -python_requires = >=3.8 -setup_requires = setuptools_scm -install_requires = - sunpy[net,image,map]>=4.0.4 - -[options.extras_require] -test = - hissw - pytest - pytest-astropy -docs = - packaging - sphinx - sphinx-automodapi - sphinx-changelog - sphinx-copybutton - sphinx-design - sphinx-gallery - sphinx-hoverxref - sphinxext-opengraph - sunpy-sphinx-theme - # Not a direct dependency, but we need to pin this until sphinx-changelog is updated (see #6668) - towncrier<22.12.0 - -[options.package_data] -aiapy = CITATION.rst, data/* -aiapy.tests = data/* - -[tool:pytest] -testpaths = "aiapy" "docs" -norecursedirs = ".tox" "build" "docs[\/]_build" "docs[\/]generated" "*.egg-info" "examples" ".jupyter" ".history" "tools" "joss" -doctest_plus = enabled -text_file_format = rst -addopts = --doctest-rst --doctest-ignore-import-errors -p no:unraisableexception -p no:threadexception -remote_data_strict = True -filterwarnings = - error - # Do not fail on pytest config issues (i.e. missing plugins) but do show them - always::pytest.PytestConfigWarning - # - # A list of warnings to ignore follows. If you add to this list, you MUST - # add a comment or ideally a link to an issue that explains why the warning - # is being ignored - # - # - ignore:distutils Version classes are deprecated - ignore:The distutils.sysconfig module is deprecated, use sysconfig instead - # This is due to dependencies building with a numpy version different from - # the local installed numpy version, but should be fine - # See https://github.com/numpy/numpy/issues/15748#issuecomment-598584838 - ignore:numpy.ufunc size changed:RuntimeWarning - ignore:numpy.ndarray size changed:RuntimeWarning - # Not sure if this is a problem - ignore:invalid value encountered in divide - # Some tests use data that have dates in the future and ERFA does not like. - ignore:ERFA function "d2dtf"* - ignore:ERFA function "dtf2d"* - ignore:ERFA function "utctai"* - ignore:ERFA function "taiutc"* - # test_degradation[None-10-time_correction_truth0] raises this: - ignore:Multiple valid epochs for .*. Using the most recent one - # From Zeep - ignore:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning - -[pycodestyle] -max_line_length = 110 - -[flake8] -max-line-length = 110 -exclude = - .git, - __pycache__, - docs/conf.py, - build, - aiapy/__init__.py, -rst-directives = - plot - -[isort] -balanced_wrapping = True -skip=docs/conf.py,aiapy/__init__.py -default_section = THIRDPARTY -include_trailing_comma = True -known_astropy = astropy, asdf, sunpy -known_first_party = aiapy -length_sort = False -length_sort_sections=stdlib -line_length = 110 -multi_line_output = 3 -no_lines_before = LOCALFOLDER -sections = STDLIB, THIRDPARTY, ASTROPY, FIRSTPARTY, LOCALFOLDER - -[coverage:run] -omit = - aiapy/conftest.py - aiapy/tests/* - aiapy/*/tests/* - aiapy/extern/* - aiapy/*version* - */aiapy/conftest.py - */aiapy/tests/* - */aiapy/*/tests/* - */aiapy/*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 de596f7..0000000 --- a/setup.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -from setuptools import setup # isort:skip -from pathlib import Path -from itertools import chain - -try: - # Recommended for setuptools 61.0.0+ - # (though may disappear in the future) - from setuptools.config.setupcfg import read_configuration -except ImportError: - from setuptools.config import read_configuration - -################################################################################ -# Programmatically generate some extras combos. -################################################################################ -extras = read_configuration("setup.cfg")["options"]["extras_require"] - -# Dev is everything -extras["dev"] = list(chain(*extras.values())) - -# All is everything but tests and docs -exclude_keys = ("tests", "docs", "dev") -ex_extras = dict(filter(lambda i: i[0] not in exclude_keys, extras.items())) -# Concatenate all the values together for 'all' -extras["all"] = list(chain.from_iterable(ex_extras.values())) - -setup( - extras_require=extras, - use_scm_version={"write_to": Path("aiapy") / Path("_version.py")}, -) diff --git a/tox.ini b/tox.ini index d57a68c..926a0d5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,62 +1,59 @@ [tox] +minversion = 4.0 envlist = - py{38,39,310,311}{,-online,-devdeps,-rc} + py{39,310,311}{,-online,-devdeps,-rc} build_docs codestyle isolated_build = true -requires = - setuptools - pip - tox-pypi-filter [testenv] -# The following option combined with the use of the tox-pypi-filter above allows -# project-wide pinning of dependencies, e.g. if new versions of pytest do not -# work correctly with pytest-astropy plugins. Most of the time the pinnings file -# should be empty. pypi_filter_requirements = https://raw.githubusercontent.com/sunpy/sunpy/main/.test_package_pins.txt -# Pass through the following environemnt variables which may be needed for the CI -passenv = HOME WINDIR LC_ALL LC_CTYPE CC CI TRAVIS +allowlist_externals= + /bin/bash + /usr/bin/bash setenv = - build_docs: HIDE_PARFIVE_PROGESS = True -# Run the tests in a temporary directory to make sure that we don't import -# the package from the source tree + MPLBACKEND = agg + SUNPY_SAMPLEDIR = {env:SUNPY_SAMPLEDIR:{toxinidir}/.tox/sample_data/} + PYTEST_COMMAND = pytest -vvv -r aS --pyargs aiapy --cov-report=xml --cov=aiapy {toxinidir}/docs + devdeps,build_docs,online: HOME = {envtmpdir} + PARFIVE_HIDE_PROGRESS = True + devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple changedir = .tmp/{envname} -# tox environments are constructued with so-called 'factors' (or terms) -# separated by hyphens, e.g. test-devdeps-cov. Lines below starting with factor: -# will only take effect if that factor is included in the environment name. To -# see a list of example environments that can be run, along with a description, -# run: -# -# tox -l -v -# description = run tests - deps = - devdeps: git+https://github.com/sunpy/sunpy.git devdeps: git+https://github.com/astropy/astropy.git + devdeps: git+https://github.com/sunpy/sunpy.git + online: pytest-rerunfailures + online: pytest-timeout rc: sunpy>=0.0.dev0 pytest-cov -# The following indicates which extras_require from setup.cfg will be installed + pytest-xdist extras = - test + all + tests commands = - !online: pytest --pyargs aiapy {toxinidir}/docs --remote-data=none --cov aiapy --cov-report=xml --cov-config={toxinidir}/setup.cfg {posargs} - online: pytest --pyargs aiapy {toxinidir}/docs --remote-data=any --cov aiapy --cov-report=xml --cov-config={toxinidir}/setup.cfg {posargs} + pip freeze --all --no-input + !online: {env:PYTEST_COMMAND} {posargs} + online: {env:PYTEST_COMMAND} --reruns 2 --reruns-delay 15 --timeout=30 --remote-data=any {posargs} [testenv:build_docs] changedir = docs -description = invoke sphinx-build to build the HTML docs -extras = docs +description = Invoke sphinx-build to build the HTML docs +extras = + all + docs commands = - sphinx-build -W -b html . _build/html {posargs} + pip freeze --all --no-input + gallery: sphinx-build -j auto --color -W --keep-going -b html -d _build/.doctrees . _build/html {posargs} + python -c 'import pathlib; print("Documentation available under file://\{0\}".format(pathlib.Path(r"{toxinidir}") / "docs" / "_build" / "index.html"))' [testenv:codestyle] +pypi_filter = skip_install = true description = Run all style and file checks with pre-commit deps = pre-commit commands = pre-commit install-hooks - pre-commit run --all-files + pre-commit run --color always --all-files --show-diff-on-failure