diff --git a/.codecov.yaml b/.codecov.yaml
new file mode 100644
index 0000000..8fe09b7
--- /dev/null
+++ b/.codecov.yaml
@@ -0,0 +1,11 @@
+comment: off
+coverage:
+ status:
+ project:
+ default:
+ threshold: 0.2%
+
+codecov:
+ require_ci_to_pass: false
+ notify:
+ wait_for_ci: true
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..21f6880
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,30 @@
+[run]
+omit =
+ glue_solar/conftest.py
+ glue_solar/*setup_package*
+ glue_solar/extern/*
+ glue_solar/version*
+ */glue_solar/conftest.py
+ */glue_solar/*setup_package*
+ */glue_solar/extern/*
+ */glue_solar/version*
+
+[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_
+ # typing.TYPE_CHECKING is False at runtime
+ if TYPE_CHECKING:
+ # Ignore typing overloads
+ @overload
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..a2c6683
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+# https://editorconfig.org
+root=true
+# utf, UNIX-style new line
+
+[*]
+charset=utf-8
+end_of_line=lf
+insert_final_newline=true
+trim_trailing_whitespace=true
+
+[*.{py,rst,md}]
+indent_style=space
+indent_size=4
+
+[*.yml]
+indent_style=space
+indent_size=2
diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml
index 019d6b7..c962515 100644
--- a/.github/workflows/ci_workflows.yml
+++ b/.github/workflows/ci_workflows.yml
@@ -29,7 +29,7 @@ jobs:
- linux: codestyle
libraries:
coverage: false
- - linux: py39
+ - linux: py312
test:
needs: [core]
@@ -51,8 +51,7 @@ jobs:
brew:
- enchant
envs: |
- - linux: py310-devdeps
- - linux: py39-docs
+ - linux: py312-docs
coverage: false
- - macos: py38
- - windows: py39
+ - macos: py312
+ - windows: py312
diff --git a/.gitignore b/.gitignore
index c59d279..8dc01ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,3 +47,4 @@ pip-wheel-metadata
glue_solar/_version.py
.tmp
.qt_for_python
+docs/generated/
diff --git a/.isort.cfg b/.isort.cfg
new file mode 100644
index 0000000..5b3f02a
--- /dev/null
+++ b/.isort.cfg
@@ -0,0 +1,14 @@
+[settings]
+balanced_wrapping = true
+skip =
+ docs/conf.py
+default_section = THIRDPARTY
+include_trailing_comma = true
+known_astropy = astropy, asdf, sunpy
+known_first_party = glue_solar
+length_sort = false
+length_sort_sections = stdlib
+line_length = 110
+multi_line_output = 3
+no_lines_before = LOCALFOLDER
+sections = STDLIB, THIRDPARTY, ASTROPY, FIRSTPARTY, LOCALFOLDER
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index bac4175..8a65d0e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,31 +1,32 @@
+exclude: ".*(.csv|.fits|.fts|.fit|.header|.txt|tca.*|.json|.asdf)$|^CITATION.rst"
repos:
- - repo: https://github.com/myint/autoflake
- rev: v1.4
+ # This should be before any formatting hooks like isort
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: "v0.6.5"
hooks:
- - id: autoflake
- args: ['--in-place', '--remove-all-unused-imports', '--remove-unused-variable']
- exclude: ".*(.fits|.fts|.fit|.txt|tca.*|extern.*|.rst|.md|__init__.py|docs/conf.py)$"
- - repo: https://github.com/ambv/black
- rev: 22.6.0
- hooks:
- - id: black
- - repo: https://github.com/timothycrosley/isort
- rev: 5.10.1
+ - id: ruff
+ args: ["--fix"]
+ - repo: https://github.com/PyCQA/isort
+ rev: 5.13.2
hooks:
- id: isort
- args: ['--sp','setup.cfg']
- exclude: ".*(.fits|.fts|.fit|.txt|tca.*|extern.*|.rst|.md|docs/conf.py)$"
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.3.0
+ rev: v4.6.0
hooks:
- id: check-ast
- id: check-case-conflict
- id: trailing-whitespace
- exclude: ".*(.fits|.fts|.fit|.txt)$"
- id: check-yaml
- id: debug-statements
- id: check-added-large-files
+ args: ["--enforce-all", "--maxkb=1054"]
+ exclude: ""
- id: end-of-file-fixer
- exclude: ".*(.fits|.fts|.fit|.txt|tca.*)$"
- id: mixed-line-ending
- exclude: ".*(.fits|.fts|.fit|.txt|tca.*)$"
+ - repo: https://github.com/crate-ci/typos
+ rev: v1.22.9
+ hooks:
+ - id: typos
+ci:
+ autofix_prs: false
+ autoupdate_schedule: "quarterly"
diff --git a/.readthedocs.yml b/.readthedocs.yml
index a8a72e6..9152ff1 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -1,8 +1,9 @@
version: 2
+
build:
- os: ubuntu-20.04
+ os: ubuntu-lts-latest
tools:
- python: "3.9"
+ python: "mambaforge-latest"
apt_packages:
- libxkbcommon-x11-0
- libxcb-icccm4
@@ -12,6 +13,14 @@ build:
- libxcb-render-util0
- libxcb-xfixes0
- libxcb-xinerama0
+ jobs:
+ post_checkout:
+ - git fetch --unshallow || true
+ pre_install:
+ - git update-index --assume-unchanged .rtd-environment.yml docs/conf.py
+
+conda:
+ environment: .rtd-environment.yml
sphinx:
builder: html
@@ -19,9 +28,9 @@ sphinx:
fail_on_warning: false
python:
- install:
- - method: pip
- extra_requirements:
+ install:
+ - method: pip
+ extra_requirements:
- all
- docs
- path: .
+ path: .
diff --git a/.rtd-environment.yml b/.rtd-environment.yml
new file mode 100644
index 0000000..fd564f2
--- /dev/null
+++ b/.rtd-environment.yml
@@ -0,0 +1,7 @@
+name: rtd_glue_solar
+channels:
+ - conda-forge
+dependencies:
+ - python=3.12
+ - pip
+ - graphviz!=2.42.*,!=2.43.*
diff --git a/.ruff.toml b/.ruff.toml
new file mode 100644
index 0000000..26ff6aa
--- /dev/null
+++ b/.ruff.toml
@@ -0,0 +1,37 @@
+target-version = "py310"
+line-length = 110
+exclude = [
+ ".git,",
+ "__pycache__",
+ "build",
+ "*version.py",
+]
+
+[lint]
+select = ["E", "F", "W", "UP", "PT"]
+extend-ignore = [
+ # pycodestyle (E, W)
+ "E501", # LineTooLong # TODO! fix
+ # pytest (PT)
+ "PT001", # Always use pytest.fixture()
+ "PT004", # Fixtures which don't return anything should have leading _
+ "PT007", # Parametrize should be lists of tuples # TODO! fix
+ "PT011", # Too broad exception assert # TODO! fix
+ "PT023", # Always use () on pytest decorators
+]
+
+[lint.per-file-ignores]
+# Part of configuration, not a package.
+"setup.py" = ["INP001"]
+"conftest.py" = ["INP001"]
+"docs/conf.py" = [
+ "E402" # Module imports not at top of file
+]
+"docs/*.py" = [
+ "INP001", # Implicit-namespace-package. The examples are not a package.
+]
+"__init__.py" = ["E402", "F401", "F403"]
+"test_*.py" = ["B011", "D", "E402", "PGH001", "S101"]
+
+[lint.pydocstyle]
+convention = "numpy"
diff --git a/LICENSE.rst b/LICENSE.rst
index 255e106..6e1bcb5 100644
--- a/LICENSE.rst
+++ b/LICENSE.rst
@@ -1,4 +1,4 @@
-Copyright (c) 2019-2022, Glue developers
+Copyright (c) 2019-2024, SunPy and Glue developers
All rights reserved.
diff --git a/_typos.toml b/_typos.toml
new file mode 100644
index 0000000..6b141ba
--- /dev/null
+++ b/_typos.toml
@@ -0,0 +1,3 @@
+default.extend-ignore-identifiers-re = [
+ "NDCube",
+]
diff --git a/docs/api_reference.rst b/docs/api_reference.rst
index 78b1278..007e5e5 100644
--- a/docs/api_reference.rst
+++ b/docs/api_reference.rst
@@ -1,4 +1,4 @@
-.. _api_reference:
+.. _glue_solar_api_reference:
=============
API Reference
@@ -12,6 +12,3 @@ API Reference
.. automodapi:: glue_solar.sources.maps
:no-inheritance-diagram:
-
-.. automodapi:: glue_solar.pixel_extraction
- :no-inheritance-diagram:
diff --git a/docs/conf.py b/docs/conf.py
index 7b3b702..e890fd9 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,46 +1,189 @@
+"""
+Configuration file for the Sphinx documentation builder.
+"""
+# -- stdlib imports ------------------------------------------------------------
+import os
+import datetime
+
+
+# -- Read the Docs Specific Configuration --------------------------------------
+# This needs to be done before sunpy is imported
+on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+if on_rtd:
+ os.environ['SUNPY_CONFIGDIR'] = '/home/docs/'
+ os.environ['HOME'] = '/home/docs/'
+ os.environ['LANG'] = 'C'
+ os.environ['LC_ALL'] = 'C'
+ os.environ['PARFIVE_HIDE_PROGRESS'] = 'True'
+
# -- Project information -----------------------------------------------------
-project = "Glue Solar"
-copyright = "2020-2022, The SunPy Developers and The Glue Developers"
-author = "The SunPy Developers and The Glue Developers"
-# -- General configuration ---------------------------------------------------
+project = 'glue-solar'
+author = 'The SunPy Developers and The Glue Developers'
+copyright = f'{datetime.datetime.now().year}, {author}'
+
+# sphinxext-opengraph
+ogp_image = "https://raw.githubusercontent.com/sunpy/sunpy-logo/master/generated/sunpy_logo_word.png"
+ogp_use_first_image = True
+ogp_description_length = 160
+ogp_custom_meta_tags = [
+ '',
+]
+
+# Suppress warnings about overriding directives as we overload some of the
+# doctest extensions.
+suppress_warnings = ['app.add_directive', ]
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named "sphinx.ext.*") or your custom
+# ones.
extensions = [
- "sphinx_automodapi.automodapi",
- "sphinx_automodapi.smart_resolver",
- "sphinx.ext.autodoc",
- "sphinx.ext.coverage",
- "sphinx.ext.doctest",
- "sphinx.ext.inheritance_diagram",
- "sphinx.ext.intersphinx",
- "sphinx.ext.mathjax",
- "sphinx.ext.napoleon",
- "sphinx.ext.todo",
- "sphinx.ext.viewcode",
+ 'matplotlib.sphinxext.plot_directive',
+ 'sphinx_automodapi.automodapi',
+ 'sphinx_automodapi.smart_resolver',
+ 'sphinx_changelog',
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.coverage',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.inheritance_diagram',
+ 'sphinx.ext.intersphinx',
+ 'sphinx.ext.mathjax',
+ 'sphinx.ext.napoleon',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.viewcode',
+ "sphinxext.opengraph",
+ 'sphinx_design',
+ 'sphinx_copybutton',
+ 'hoverxref.extension',
]
-numpydoc_show_class_members = False
-templates_path = ["_templates"]
+
+# Set automodapi to generate files inside the generated directory
+automodapi_toctreedirnm = "generated/api"
+
+# Add any paths that contain templates here, relative to this directory.
+# templates_path = ["_templates"]
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+html_extra_path = ['robots.txt']
+
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
source_suffix = ".rst"
+
+# The master toctree document.
+master_doc = 'index'
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents. Set to the "smart" one.
+default_role = 'obj'
+
+# Disable having a separate return type row
+napoleon_use_rtype = False
+
+# Disable google style docstrings
+napoleon_google_docstring = False
+
+# Disable the use of param, which prevents a distinct "Other Parameters" section
+napoleon_use_param = False
+
+# Enable nitpicky mode, which forces links to be non-broken
+nitpicky = True
+# This is not used. See docs/nitpick-exceptions file for the actual listing.
+nitpick_ignore = []
+for line in open('nitpick-exceptions'):
+ if line.strip() == "" or line.startswith("#"):
+ continue
+ dtype, target = line.split(None, 1)
+ target = target.strip()
+ nitpick_ignore.append((dtype, target))
+
+
# -- Options for intersphinx extension ---------------------------------------
+
+# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
"python": (
"https://docs.python.org/3/",
- (None, "http://data.astropy.org/intersphinx/python3.inv"),
+ (None, "http://www.astropy.org/astropy-data/intersphinx/python3.inv"),
),
"numpy": (
"https://numpy.org/doc/stable/",
- (None, "http://data.astropy.org/intersphinx/numpy.inv"),
+ (None, "http://www.astropy.org/astropy-data/intersphinx/numpy.inv"),
),
"scipy": (
"https://docs.scipy.org/doc/scipy/reference/",
- (None, "http://data.astropy.org/intersphinx/scipy.inv"),
- ),
- "matplotlib": (
- "https://matplotlib.org/",
- (None, "http://data.astropy.org/intersphinx/matplotlib.inv"),
+ (None, "http://www.astropy.org/astropy-data/intersphinx/scipy.inv"),
),
+ "aiapy": ("https://aiapy.readthedocs.io/en/stable/", None),
"astropy": ("https://docs.astropy.org/en/stable/", None),
- "sunpy": ("https://docs.sunpy.org/en/stable/", None),
"glueviz": ("http://docs.glueviz.org/en/stable/", None),
+ "irispy": ("https://irispy-lmsal.readthedocs.io/en/stable/", None),
+ "matplotlib": ("https://matplotlib.org/stable", None),
+ "reproject": ("https://reproject.readthedocs.io/en/stable/", None),
+ "skimage": ("https://scikit-image.org/docs/stable/", None),
+ "sunkit_image": ("https://docs.sunpy.org/projects/sunkit-image/en/stable/", None),
+ "sunkit_instruments": ("https://docs.sunpy.org/projects/sunkit-instruments/en/stable/", None),
+ "sunpy": ("https://docs.sunpy.org/en/stable/", None),
+}
+
+# -- Options for hoverxref -----------------------------------------------------
+if os.environ.get("READTHEDOCS"):
+ hoverxref_api_host = "https://readthedocs.org"
+
+ if os.environ.get("PROXIED_API_ENDPOINT"):
+ # Use the proxied API endpoint
+ # A RTD thing to avoid a CSRF block when docs are using a custom domain
+ hoverxref_api_host = "/_"
+
+hoverxref_auto_ref = False
+hoverxref_domains = ["py"]
+hoverxref_mathjax = True
+hoverxref_modal_hover_delay = 500
+hoverxref_tooltip_maxwidth = 600 # RTD main window is 696px
+hoverxref_intersphinx = list(intersphinx_mapping.keys())
+hoverxref_role_types = {
+ # Roles within the py domain
+ "attr": "tooltip",
+ "class": "tooltip",
+ "const": "tooltip",
+ "data": "tooltip",
+ "exc": "tooltip",
+ "func": "tooltip",
+ "meth": "tooltip",
+ "mod": "tooltip",
+ "obj": "tooltip",
+ # Roles within the std domain
+ "confval": "tooltip",
+ "hoverxref": "tooltip",
+ "ref": "tooltip", # Would be used by hoverxref_auto_ref if we set it to True
+ "term": "tooltip",
}
-# -- Options for HTML output -------------------------------------------------
-from sunpy_sphinx_theme.conf import *
+
+# -- Options for HTML output ---------------------------------------------------
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = "sunpy"
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+# html_static_path = ["_static"]
+
+# Render inheritance diagrams in SVG
+graphviz_output_format = "svg"
+
+graphviz_dot_args = [
+ '-Nfontsize=10',
+ '-Nfontname=Helvetica Neue, Helvetica, Arial, sans-serif',
+ '-Efontsize=10',
+ '-Efontname=Helvetica Neue, Helvetica, Arial, sans-serif',
+ '-Gfontsize=10',
+ '-Gfontname=Helvetica Neue, Helvetica, Arial, sans-serif'
+]
diff --git a/docs/dev_guide/index.rst b/docs/dev_guide/index.rst
index 7dd2e8b..a6c7aa3 100644
--- a/docs/dev_guide/index.rst
+++ b/docs/dev_guide/index.rst
@@ -1,3 +1,6 @@
+.. _glue_solar_dev_docs_index:
+
+=======================
Developer Documentation
=======================
diff --git a/docs/dev_guide/loader-customization.rst b/docs/dev_guide/loader-customization.rst
index 4e2842a..7effd8d 100644
--- a/docs/dev_guide/loader-customization.rst
+++ b/docs/dev_guide/loader-customization.rst
@@ -1,4 +1,4 @@
-.. _loader_customization_guide:
+.. _glue_solar_dev_docs_loader_customization:
===============================
Data Loader Customization Guide
diff --git a/docs/index.rst b/docs/index.rst
index bcbf412..cdd8505 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,5 +1,6 @@
-.. _glue-solar:
+.. _glue-solar-index:
+========================
glue-solar documentation
========================
diff --git a/docs/nitpick-exceptions b/docs/nitpick-exceptions
new file mode 100644
index 0000000..9d9486a
--- /dev/null
+++ b/docs/nitpick-exceptions
@@ -0,0 +1,6 @@
+# Prevents sphinx nitpicky mode picking up on optional
+# (see https://github.com/sphinx-doc/sphinx/issues/6861)
+# Even if it was "fixed", still broken
+py:class optional
+# See https://github.com/numpy/numpy/issues/10039
+py:obj numpy.datetime64
diff --git a/docs/robots.txt b/docs/robots.txt
new file mode 100644
index 0000000..df810cf
--- /dev/null
+++ b/docs/robots.txt
@@ -0,0 +1,6 @@
+User-agent: *
+Allow: /*/latest/
+Allow: /en/latest/ # Fallback for bots that don't understand wildcards
+Allow: /*/stable/
+Allow: /en/stable/ # Fallback for bots that don't understand wildcards
+Disallow: /
diff --git a/docs/user_guide/guide-to-glue-1dprofile-viewer-for-iris-data.rst b/docs/user_guide/guide-to-glue-1dprofile-viewer-for-iris-data.rst
index 6b28113..fc58330 100644
--- a/docs/user_guide/guide-to-glue-1dprofile-viewer-for-iris-data.rst
+++ b/docs/user_guide/guide-to-glue-1dprofile-viewer-for-iris-data.rst
@@ -1,4 +1,4 @@
-.. _guide_to_1dprofile_viewer_for_iris_data:
+.. _glue_solar_user_guide_1dprofile_viewer_for_iris_data:
=============================================================================
A guide to using ``glue``'s 1D profile viewer to probe IRIS Level 2 data sets
@@ -37,7 +37,7 @@ Now we are ready to visualize the 2D slices of the imported data cubes.
Using ``glue``'s 2D image viewer to get the indices and slices needed for the 1D spectrum plot
----------------------------------------------------------------------------------------------
-To plot 2D slices of the ND data cubes, we will need to drag the data ("C_II_1336" in this
+To plot 2D slices of the AND data cubes, we will need to drag the data ("C_II_1336" in this
example) concerned from the "Data Collection" area and drop it at the large plotting window
to the right, as shown in the following image:
diff --git a/docs/user_guide/index.rst b/docs/user_guide/index.rst
index f8624ec..68966aa 100644
--- a/docs/user_guide/index.rst
+++ b/docs/user_guide/index.rst
@@ -1,5 +1,6 @@
-.. _users_guide:
+.. _glue_solar_users_guide_index:
+==========
User Guide
==========
diff --git a/docs/user_guide/loading-aia-and-hmi.rst b/docs/user_guide/loading-aia-and-hmi.rst
index ae786df..849aded 100644
--- a/docs/user_guide/loading-aia-and-hmi.rst
+++ b/docs/user_guide/loading-aia-and-hmi.rst
@@ -1,4 +1,4 @@
-.. _loading_aia_and_hmi_files:
+.. _glue_solar_users_guide_loading_aia_and_hmi_files:
=========================================================
Loading and over-plotting AIA and HMI files as sunpy Maps
diff --git a/docs/user_guide/loading-iris-level-2-raster-and-sji-data.rst b/docs/user_guide/loading-iris-level-2-raster-and-sji-data.rst
index 59ce4e4..25b6e8e 100644
--- a/docs/user_guide/loading-iris-level-2-raster-and-sji-data.rst
+++ b/docs/user_guide/loading-iris-level-2-raster-and-sji-data.rst
@@ -1,4 +1,4 @@
-.. _loading_iris_level_2_raster_and_sji_files:
+.. _glue_solar_users_guide_loading_iris_level_2_raster_and_sji_files:
=======================================================
Loading IRIS Level 2 Raster and SJI Data Files Together
diff --git a/glue_solar/__init__.py b/glue_solar/__init__.py
index 368309c..0bcf857 100644
--- a/glue_solar/__init__.py
+++ b/glue_solar/__init__.py
@@ -1,16 +1,13 @@
from glue.config import colormaps
-from glue.viewers.image.qt import ImageViewer
from sunpy.visualization.colormaps import cmlist
-from glue_solar.pixel_extraction.pixel_extraction import PixelExtractionTool # NOQA
-from glue_solar.sources import iris, maps, sst # NOQA
+from glue_solar.sources import iris, maps, sst
from glue_solar.version import version as __version__
+__all__ = ["setup", "__version__", "iris", "maps", "sst"]
def setup():
- # List of all plugins to enable to the default ImageViewer
- ImageViewer.tools.append("solar:pixel_extraction")
# Enables sunpy colormaps to be used in glueviz
for _, ctable in sorted(cmlist.items()):
colormaps.add(ctable.name, ctable)
diff --git a/glue_solar/pixel_extraction/__init__.py b/glue_solar/pixel_extraction/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/glue_solar/pixel_extraction/pixel_extraction.py b/glue_solar/pixel_extraction/pixel_extraction.py
deleted file mode 100644
index ed58620..0000000
--- a/glue_solar/pixel_extraction/pixel_extraction.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from glue.config import viewer_tool
-from glue.core.data_derived import IndexedData
-from glue.viewers.matplotlib.toolbar_mode import ToolbarModeBase
-
-__all__ = ["PixelExtractionTool"]
-
-
-@viewer_tool
-class PixelExtractionTool(ToolbarModeBase):
- """
- Create a "dervied dataset" corresponding to the selected pixel.
- """
-
- icon = "pencil"
- tool_id = "solar:pixel_extraction"
- action_text = "Pixel"
- tool_tip = "Extract data for a single pixel based on mouse location"
- status_tip = "CLICK to select a point, CLICK and DRAG to update the extracted dataset in real time"
- _pressed = False
-
- def __init__(self, *args, **kwargs):
- super(PixelExtractionTool, self).__init__(*args, **kwargs)
- self._move_callback = self._extract_pixel
- self._press_callback = self._on_press
- self._release_callback = self._on_release
- self._derived = None
- self._line_x = self.viewer.axes.axvline(0, color="orange")
- self._line_x.set_visible(False)
- self._line_y = self.viewer.axes.axhline(0, color="orange")
- self._line_y.set_visible(False)
-
- def _on_press(self, mode):
- self._pressed = True
- self._extract_pixel(mode)
-
- def _on_release(self, mode):
- self._pressed = False
-
- def _extract_pixel(self, mode):
- if not self._pressed:
- return
- x, y = self._event_xdata, self._event_ydata
- if x is None or y is None:
- return None
- xi = int(round(x))
- yi = int(round(y))
- indices = [None] * self.viewer.state.reference_data.ndim
- indices[self.viewer.state.x_att.axis] = xi
- indices[self.viewer.state.y_att.axis] = yi
- self._line_x.set_data([x, x], [0, 1])
- self._line_x.set_visible(True)
- self._line_y.set_data([0, 1], [y, y])
- self._line_y.set_visible(True)
- self.viewer.axes.figure.canvas.draw()
- if self._derived is None:
- self._derived = IndexedData(self.viewer.state.reference_data, indices)
- self.viewer.session.data_collection.append(self._derived)
- else:
- try:
- self._derived.indices = indices
- except TypeError:
- self.viewer.session.data_collection.remove(self._derived)
- self._derived = IndexedData(self.viewer.state.reference_data, indices)
- self.viewer.session.data_collection.append(self._derived)
diff --git a/glue_solar/pixel_extraction/tests/__init__.py b/glue_solar/pixel_extraction/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/glue_solar/pixel_extraction/tests/test_pixel_extraction.py b/glue_solar/pixel_extraction/tests/test_pixel_extraction.py
deleted file mode 100644
index 044247e..0000000
--- a/glue_solar/pixel_extraction/tests/test_pixel_extraction.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import numpy as np
-from glue.app.qt import GlueApplication
-from glue.core.data import Data
-from glue.utils.qt import process_events
-from glue.viewers.image.qt import ImageViewer
-from numpy.testing import assert_allclose
-
-
-class TestPixelExtraction:
- def setup_method(self, method):
- self.data = Data(label="d1")
- self.data["x"] = np.arange(24).reshape((3, 4, 2)).astype(float)
-
- self.app = GlueApplication()
- self.session = self.app.session
- self.hub = self.session.hub
-
- self.data_collection = self.session.data_collection
- self.data_collection.append(self.data)
-
- self.viewer = self.app.new_data_viewer(ImageViewer)
-
- def teardown_method(self, method):
- self.viewer.close(warn=False)
- self.viewer = None
- self.app.close()
- self.app = None
-
- def test_navigate_sync_image(self):
- self.viewer.add_data(self.data)
- self.viewer.toolbar.active_tool = "solar:pixel_extraction"
-
- self.viewer.axes.figure.canvas.draw()
- process_events()
-
- x, y = self.viewer.axes.transData.transform([[1, 2]])[0]
- self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
- self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
- assert len(self.data_collection) == 2
-
- derived1 = self.data_collection[1]
- assert derived1.label == "d1[:,2,1]"
- assert derived1.shape == (3,)
- assert_allclose(derived1["x"], self.data["x"][:, 2, 1])
-
- x, y = self.viewer.axes.transData.transform([[1, 1]])[0]
- self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
- self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
- assert len(self.data_collection) == 2
-
- derived2 = self.data_collection[1]
- assert derived2 is derived1
- assert derived2.label == "d1[:,1,1]"
- assert derived2.shape == (3,)
- assert_allclose(derived2["x"], self.data["x"][:, 1, 1])
-
- self.viewer.state.x_att = self.data.pixel_component_ids[0]
-
- self.viewer.axes.figure.canvas.draw()
- process_events()
-
- x, y = self.viewer.axes.transData.transform([[1, 0]])[0]
- self.viewer.axes.figure.canvas.button_press_event(x, y, 1)
- self.viewer.axes.figure.canvas.button_release_event(x, y, 1)
- assert len(self.data_collection) == 2
-
- derived3 = self.data_collection[1]
- assert derived3 is not derived1
- assert derived3.label == "d1[1,0,:]"
- assert derived3.shape == (2,)
- assert_allclose(derived3["x"], self.data["x"][1, 0, :])
diff --git a/glue_solar/sources/iris.py b/glue_solar/sources/iris.py
index 62d0f69..cfbe5d0 100644
--- a/glue_solar/sources/iris.py
+++ b/glue_solar/sources/iris.py
@@ -9,8 +9,8 @@
from glue.core.data_factories import is_fits
from glue.core.visual import VisualAttributes
from irispy.io import read_files
-from irispy.sji import IRISMapCube, IRISMapCubeSequence
-from irispy.spectrograph import IRISCollection
+from irispy.sji import SJICube
+from irispy.spectrograph import SpectrogramCube
from qtpy import QtWidgets
from astropy.io import fits
@@ -20,7 +20,7 @@
__all__ = ["import_iris", "read_iris_files"]
-@qglue_parser(IRISCollection)
+@qglue_parser(SpectrogramCube)
def _parse_iris_raster(data):
"""
Parse IRIS Level 2 raster files so that it can be loaded by glue.
@@ -41,7 +41,7 @@ def _parse_iris_raster(data):
return w_data
-@qglue_parser(IRISMapCube)
+@qglue_parser(SJICube)
def _parse_iris_sji(data, file_header):
"""
Parse IRIS Level 2 SJI files so that it can be loaded by glue.
@@ -68,9 +68,9 @@ def read_iris_files(file_path):
"""
# TODO: Memmap in future.
data = read_files(file_path, uncertainty=False, memmap=False)
- if isinstance(data, IRISMapCubeSequence):
+ if isinstance(data, SJICube):
return _parse_iris_sji(data, fits.getheader(file_path))
- elif isinstance(data, IRISCollection):
+ elif isinstance(data, SpectrogramCube):
return _parse_iris_raster(data)
else:
raise ValueError(f"Unrecognised IRIS file type for {file_path}")
diff --git a/glue_solar/sources/loaders/iris.py b/glue_solar/sources/loaders/iris.py
index 4c3e665..379a394 100644
--- a/glue_solar/sources/loaders/iris.py
+++ b/glue_solar/sources/loaders/iris.py
@@ -5,8 +5,7 @@
from glue.core.coordinates import WCSCoordinates
from glue.core.data import Data
from glue.core.visual import VisualAttributes
-from glue.utils.qt import get_qapp
-from glue.utils.qt.helpers import load_ui
+from glue_qt.utils import get_qapp, load_ui
from irispy.io import read_files
from qtpy import QtWidgets
from qtpy.QtCore import Qt
@@ -69,28 +68,31 @@ def populate_table_sji(self):
self.sjis.resizeColumnToContents(1)
def get_raster_windows(self):
+ if not self.raster_files:
+ return []
with fits.open(self.raster_files[0]) as hdulist:
return list(
- hdulist[0].header["TDESC{0}".format(i)]
+ hdulist[0].header[f"TDESC{i}"]
for i in range(1, hdulist[0].header["NWIN"] + 1)
)
def get_sji_windows(self):
windows = {}
+ if not self.sji_files:
+ return windows
for sji in self.sji_files:
with fits.open(sji) as hdul:
windows[hdul[0].header["TDESC1"]] = sji
-
return windows
def load_sji(self, sji):
with fits.open(sji) as hdul:
- hdul.verify("fix")
+ hdul.verify("silentfix+ignore")
label = hdul[0].header["TDESC1"] + hdul[0].header["OBSID"]
data = Data(label=label)
data.coords = WCSCoordinates(hdul[0].header)
data.meta = hdul[0].header
- preferred_cmap_name = "IRIS " + hdul[0].header["TDESC1"].replace("_", " ")
+ preferred_cmap_name = f"iris{hdul[0].header["TDESC1"].replace("_","").lower()}"
data.style = VisualAttributes(preferred_cmap=preferred_cmap_name)
data.add_component(Component(hdul[0].data), label)
diff --git a/glue_solar/sources/loaders/maps.py b/glue_solar/sources/loaders/maps.py
index aa304df..650d88e 100644
--- a/glue_solar/sources/loaders/maps.py
+++ b/glue_solar/sources/loaders/maps.py
@@ -4,8 +4,7 @@
from glue.core.component import Component
from glue.core.data import Data
from glue.core.visual import VisualAttributes
-from glue.utils.qt import get_qapp
-from glue.utils.qt.helpers import load_ui
+from glue_qt.utils import get_qapp, load_ui
from qtpy import QtWidgets
from qtpy.QtCore import Qt
diff --git a/pyproject.toml b/pyproject.toml
index 349a6be..ee72b0d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,62 +1,79 @@
[build-system]
requires = [
- "setuptools>=56,!=61.0.0",
- "setuptools_scm[toml]>=6.2",
- "wheel",
+ "setuptools>=62.1",
+ "setuptools_scm[toml]>=6.2",
+ "wheel",
]
-build-backend = 'setuptools.build_meta'
-
-[ tool.gilesbot ]
- [ tool.gilesbot.pull_requests ]
- enabled = true
-
- [ tool.gilesbot.towncrier_changelog ]
- enabled = true
- verify_pr_number = true
- changelog_skip_label = "No Changelog Entry Needed"
- help_url = "https://github.com/glueviz/glue-solar/blob/main/changelog/README.rst"
- changelog_missing_long = "There isn't a changelog file in this pull request. Please add a changelog file to the `changelog/` directory following the instructions in the changelog [README](https://github.com/glueviz/glue-solar/blob/main/changelog/README.rst)."
- type_incorrect_long = "The changelog file you added is not one of the allowed types. Please use one of the types described in the changelog [README](https://github.com/glueviz/glue-solar/blob/main/changelog/README.rst)"
- number_incorrect_long = "The number in the changelog file you added does not match the number of this pull request. Please rename the file."
-
-[tool.towncrier]
- package = "glue-solar"
- filename = "CHANGELOG.rst"
- directory = "changelog/"
- issue_format = "`#{issue} `__"
- title_format = "{version} ({project_date})"
-
- [[tool.towncrier.type]]
- directory = "breaking"
- name = "Breaking Changes"
- showcontent = true
-
- [[tool.towncrier.type]]
- directory = "deprecation"
- name = "Deprecations"
- showcontent = true
-
- [[tool.towncrier.type]]
- directory = "removal"
- name = "Removals"
- showcontent = true
-
- [[tool.towncrier.type]]
- directory = "feature"
- name = "New Features"
- showcontent = true
-
- [[tool.towncrier.type]]
- directory = "bugfix"
- name = "Bug Fixes"
- showcontent = true
-
- [[tool.towncrier.type]]
- directory = "doc"
- name = "Documentation"
- showcontent = true
-
- [[tool.towncrier.type]]
- directory = "trivial"
- name = "Internal Changes"
- showcontent = true
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "glue-solar"
+description = "Solar Physics plugin for GlueViz"
+requires-python = ">=3.10"
+readme = { file = "README.rst", content-type = "text/x-rst" }
+license = { file = "LICENSE.rst" }
+authors = [
+ { name = "The SunPy Community", email = "sunpy@googlegroups.com" },
+]
+dependencies = [
+ "glue-core[all]>=1.2.0",
+ "glue-qt[qt]>=0.3.0",
+ "sunpy[map,net,coordinates]>=6.0.0",
+ "irispy-lmsal[all]>=0.2.0rc1",
+]
+dynamic = ["version"]
+keywords = ["solar physics", "solar", "science", "sun", "visualization", "coordinates", "glueviz"]
+classifiers = [
+ "Development Status :: 2 - Pre-Alpha",
+ "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.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Topic :: Scientific/Engineering :: Physics",
+]
+
+[project.optional-dependencies]
+tests = [
+ "pytest-astropy",
+]
+docs = [
+ "sphinx>=5.0.0",
+ "sphinx-automodapi>=0.14.1",
+ "sphinx-changelog>=1.5.0",
+ "sphinx-copybutton>=0.5.0",
+ "sphinx-design>=0.2.0",
+ "sphinxext-opengraph>=0.6.0",
+ "sunpy-sphinx-theme>=2.0.3",
+ "sphinx-hoverxref>=1.3.0",
+]
+
+[project.urls]
+Homepage = "https://glueviz.org"
+Download = "https://pypi.org/project/glue-solar/"
+"Source Code" = "https://github.com/glue-viz/glue-solar"
+Documentation = "https://glue-solar.readthedocs.io/en/latest/"
+Changelog = "https://glue-solar.readthedocs.io/en/latest/"
+"Issue Tracker" = "https://github.com/glue-viz/glue-solar/issues"
+
+[tool.setuptools]
+zip-safe = true
+include-package-data = true
+platforms = ["any"]
+provides = ["glue_solar"]
+license-files = ["LICENSE.rst"]
+
+[tool.setuptools.packages.find]
+include = ["glue_solar*"]
+exclude = ["glue_solar._dev*"]
+
+
+[tool.setuptools_scm]
+write_to = "glue_solar/_version.py"
+
+[project.entry-points."glue.plugins"]
+glue_solar = "glue_solar:setup"
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 0000000..2e1d125
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,46 @@
+[pytest]
+minversion = 7.0
+testpaths =
+ glue_solar
+ docs
+norecursedirs =
+ .tox
+ build
+ docs/_build
+ docs/generated
+ *.egg-info
+ examples
+ glue_solar/_dev
+ .history
+ tools
+ glue_solar/extern
+ benchmarks
+doctest_plus = enabled
+doctest_optionflags =
+ NORMALIZE_WHITESPACE
+ FLOAT_CMP
+ ELLIPSIS
+addopts =
+ --doctest-rst
+ -p no:unraisableexception
+ -p no:theadexception
+ --arraydiff
+ --doctest-ignore-import-errors
+ --doctest-continue-on-failure
+remote_data_strict = true
+junit_family = xunit1
+filterwarnings =
+ # Turn all warnings into errors so they do not pass silently.
+ 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
+ # These should have been fixed by numpy 2.0 but possible its due to the
+ # other packages not building agasint it yet?
+ # This should be at the top of the list as well.
+ ignore:.*may indicate binary incompatibility.*
+ ignore:.*Jupyter is migrating its paths to use standard platformdirs.*
+ ignore:.*could not determine irispy package.*
+ ignore:.*Setting colormap using "color" key is deprecated.*
diff --git a/setup.cfg b/setup.cfg
index 7ed3896..6147ff7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,71 +1,3 @@
-[metadata]
-name = glue-solar
-provides = glue_solar
-description = Solar physics focused plugin for glueviz
-long_description = file: README.rst
-author = Nabil Freij
-author_email = freij@baeri.org
-license = BSD 3-Clause License
-license_file = LICENSE.rst
-url = https://github.com/glue-viz/glue-solar
-edit_on_github = True
-github_project = glueviz/glue-solar
-platform = any
-keywords = solar physics, solar, science, sun, wcs, coordinates, visualization, glue, glueviz
-classifiers =
- Development Status :: 2 - Pre-Alpha
- 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.8
- Programming Language :: Python :: 3.9
- Programming Language :: Python :: 3.10
- Topic :: Scientific/Engineering :: Physics
-
-[options]
-zip_safe = True
-python_requires = >=3.8
-packages = find:
-include_package_data = True
-setup_requires =
- setuptools_scm
-install_requires =
- glue-core[all,qt]>=1.2.2
- irispy-lmsal>=0.2.0rc1
-
-[options.extras_require]
-tests =
- pytest
-docs =
- sphinx
- sphinx-automodapi
- sunpy-sphinx-theme
-
-[options.entry_points]
-glue.plugins =
- glue_solar=glue_solar:setup
-
-[tool:pytest]
-testpaths = "glue_solar" "docs"
-norecursedirs = ".tox" "build" "docs[\/]_build" "docs[\/]generated" "*.egg-info" "examples" ".history"
-addopts = -p no:unraisableexception -p no:threadexception
-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:the imp module is deprecated in favour of importlib
- ignore:numpy.ndarray size changed:RuntimeWarning
- ignore:distutils Version classes are deprecated. Use packaging.version instead.:DeprecationWarning
- ignore:numpy.ufunc size changed:RuntimeWarning
[pycodestyle]
max_line_length = 110
diff --git a/setup.py b/setup.py
index 968f552..2d4f85f 100755
--- a/setup.py
+++ b/setup.py
@@ -1,25 +1,5 @@
-#!/usr/bin/env python
-from setuptools import setup # isort:skip
-import os
-from itertools import chain
+# We can remove this file when the default pip version
+# that Python installs is pip >= 21.3
+from setuptools import setup
-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": os.path.join("glue_solar", "_version.py")},
-)
+setup()
diff --git a/tox.ini b/tox.ini
index 41d078b..b72befb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,50 +1,88 @@
[tox]
+min_version = 4.0
+requires =
+ tox-pypi-filter>=0.14
envlist =
- py{38,39,310}{,-devdeps,-docs}
+ py{310,311,312}
+ build_docs
+ linkcheck
codestyle
-requires =
- setuptools >= 30.3.0
- pip >= 19.3.1
-isolated_build = true
[testenv]
+allowlist_externals=
+ /bin/sh
+# Run the tests in a temporary directory to make sure that we don't import anything from the source tree
changedir = .tmp/{envname}
description =
run tests
-passenv =
+pass_env =
+ # A variable to tell tests we are on a CI system
+ CI
+ # Custom compiler locations (such as ccache)
+ CC
+ # Location of locales (needed by sphinx on some systems)
+ LOCALE_ARCHIVE
+ # If the user has set a LC override we should follow it
+ LC_ALL
+ # TBD
DISPLAY
+ # TBD
HOME
setenv =
- QT_DEBUG_PLUGINS = 0
- HIDE_PARFIVE_PROGESS = True
- PYTEST_COMMAND = pytest -vvv -s -raR --pyargs glue_solar --cov-report=xml --cov=glue_solar --cov-config={toxinidir}/setup.cfg {toxinidir}/docs
+ devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple https://pypi.anaconda.org/liberfa/simple
deps =
- pytest-cov
- # The devdeps factor is intended to be used to install the latest developer version.
- # of key dependencies.
- devdeps: git+https://github.com/glue-viz/glue.git
-# The following indicates which extras_require from setup.cfg will be installed
-# We use test and all so we don't install docs when we don't need it (as
-# opposed to dev).
extras =
- all
tests
+commands_pre =
+ pip freeze --all --no-input
commands =
- {env:PYTEST_COMMAND} {posargs}
+ pytest \
+ -vvv \
+ -r fEs \
+ --pyargs glue_solar \
+ --cov-report=xml \
+ --cov=glue_solar \
+ --cov-config={toxinidir}/.coveragerc \
+ {toxinidir}/docs \
+ {posargs}
-[testenv:docs]
+[testenv:build_docs]
changedir = docs
description = Invoke sphinx-build to build the HTML docs
-# Be verbose about the extras rather than using dev for clarity
extras =
all
docs
commands =
- sphinx-build -j auto --color -W --keep-going -b html -d _build/.doctrees . _build/html {posargs}
+ pip freeze --all --no-input
+ 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:linkcheck]
+changedir = docs
+description = Invoke sphinx-build to check the URLS within the docs
+extras =
+ all
+ docs
+commands =
+ pip freeze --all --no-input
+ sphinx-build \
+ -qqq \
+ --color \
+ -b linkcheck \
+ . \
+ _build/html \
+ {posargs}
+
[testenv:codestyle]
-skipsdist = true
skip_install = true
description = Run all style and file checks with pre-commit
deps =