Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-gwcs for openSUSE:Factory checked in at 2023-01-14 00:03:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-gwcs (Old) and /work/SRC/openSUSE:Factory/.python-gwcs.new.32243 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-gwcs" Sat Jan 14 00:03:32 2023 rev:10 rq:1058263 version:0.18.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-gwcs/python-gwcs.changes 2022-10-28 19:30:41.858982562 +0200 +++ /work/SRC/openSUSE:Factory/.python-gwcs.new.32243/python-gwcs.changes 2023-01-14 00:04:18.338091106 +0100 @@ -1,0 +2,16 @@ +Fri Jan 13 13:47:14 UTC 2023 - Ben Greiner <c...@bnavigator.de> + +- Update to 0.18.3 + * Fix pixel scale formula used by iterative inverse by @mcara in + #423 + * refactor for Tox 4 by @zacharyburnett in #428 + * Improve stability of SIP fitting. Fix constant term - CRPIX - + in SIP by @mcara in #427 + * use canonical name of RTD config and enable nitpicky mode to + catch broken links in docs by @zacharyburnett in #430 + * add downstream tests for JWST and Roman calibration pipelines + by @zacharyburnett in #415 + * fix CRDS_PATH in CI when running downstream tests by + @zacharyburnett in #432 + +------------------------------------------------------------------- Old: ---- gwcs-0.18.2.tar.gz New: ---- gwcs-0.18.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-gwcs.spec ++++++ --- /var/tmp/diff_new_pack.m2U3MI/_old 2023-01-14 00:04:18.870094205 +0100 +++ /var/tmp/diff_new_pack.m2U3MI/_new 2023-01-14 00:04:18.878094251 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-gwcs # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,12 +17,13 @@ Name: python-gwcs -Version: 0.18.2 +Version: 0.18.3 Release: 0 Summary: Generalized World Coordinate System License: BSD-3-Clause Group: Productivity/Scientific/Astronomy URL: https://gwcs.readthedocs.io/en/latest/ +# SourceRepository: https://github.com/spacetelescope/gwcs Source: https://files.pythonhosted.org/packages/source/g/gwcs/gwcs-%{version}.tar.gz BuildRequires: %{python_module asdf >= 2.8.1} BuildRequires: %{python_module asdf-astropy >= 0.2.0} @@ -30,9 +31,11 @@ BuildRequires: %{python_module astropy >= 5.1} BuildRequires: %{python_module base >= 3.8} BuildRequires: %{python_module numpy} +BuildRequires: %{python_module pip} BuildRequires: %{python_module scipy} BuildRequires: %{python_module setuptools_scm} BuildRequires: %{python_module setuptools} +BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros Requires: python-asdf >= 2.8.1 @@ -56,10 +59,10 @@ %setup -q -n gwcs-%{version} %build -%python_build +%pyproject_wheel %install -%python_install +%pyproject_install %python_expand %fdupes %{buildroot}%{$python_sitelib} %check ++++++ gwcs-0.18.2.tar.gz -> gwcs-0.18.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/.github/workflows/ci.yml new/gwcs-0.18.3/.github/workflows/ci.yml --- old/gwcs-0.18.2/.github/workflows/ci.yml 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/.github/workflows/ci.yml 2022-12-23 20:01:15.000000000 +0100 @@ -12,66 +12,91 @@ # * is a special character in YAML so you have to quote this string - cron: '0 9 * * 1' - jobs: - tox: - name: ${{ matrix.name }} - runs-on: ${{ matrix.runs-on }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + check: + name: ${{ matrix.toxenv }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + toxenv: [ check-style, check-security, check-build ] + python-version: [ '3.11' ] + os: [ ubuntu-latest ] + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + cache-dependency-path: setup.cfg + - run: pip install "tox>=4.0" + - run: tox -e ${{ matrix.toxenv }} + test: + name: ${{ matrix.toxenv }} (Python ${{ matrix.python-version }}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: + toxenv: [ test ] + python-version: [ '3.8', '3.9', '3.10', '3.11' ] + os: [ ubuntu-latest, macos-latest ] include: - - name: Python 3.10.0 - runs-on: ubuntu-latest - python-version: '3.10.0' - toxenv: py310 - - - name: Python 3.9 - runs-on: ubuntu-latest - python-version: 3.9 - toxenv: py39 - - - name: Python 3.8 with coverage - runs-on: ubuntu-latest - python-version: 3.8 - toxenv: py38-cov - - - name: Code style checks - runs-on: ubuntu-latest - python-version: 3.8 - toxenv: style - - - name: Bandit security audit - runs-on: ubuntu-latest - python-version: 3.8 - toxenv: bandit - - - name: Python 3.8 with dev dependencies - runs-on: ubuntu-latest - python-version: 3.8 - toxenv: py38-dev + - toxenv: test-dev + os: ubuntu-latest + python-version: '3.x' + - toxenv: test-pyargs + os: ubuntu-latest + python-version: '3.11' + - toxenv: test-cov + os: ubuntu-latest + python-version: '3.11' + - toxenv: test-numpy122 + os: ubuntu-latest + python-version: '3.10' + - toxenv: test-numpy121 + os: ubuntu-latest + python-version: '3.10' + - toxenv: test-numpy122 + os: ubuntu-latest + python-version: '3.9' + - toxenv: test-numpy121 + os: ubuntu-latest + python-version: '3.9' + - toxenv: test-numpy120 + os: ubuntu-latest + python-version: '3.9' + - toxenv: test-numpy122 + os: ubuntu-latest + python-version: '3.8' + - toxenv: test-numpy121 + os: ubuntu-latest + python-version: '3.8' + - toxenv: test-numpy120 + os: ubuntu-latest + python-version: '3.8' + - toxenv: test-jwst-xdist + os: ubuntu-latest + python-version: '3.x' + - toxenv: test-romancal-xdist + os: ubuntu-latest + python-version: '3.x' steps: - - uses: actions/checkout@v2 + - run: echo "HOME=$HOME" >> $GITHUB_ENV + - uses: actions/checkout@v3 with: fetch-depth: 0 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - - name: Install tox - run: | - python -m pip install --upgrade pip - pip install tox - - - name: Run tox - run: tox -e ${{ matrix.toxenv }} - - - name: Upload coverage to codecov - if: ${{ contains(matrix.toxenv,'-cov') }} - uses: codecov/codecov-action@v2 + cache: 'pip' + cache-dependency-path: setup.cfg + - run: pip install "tox>=4.0" + - run: tox -e ${{ matrix.toxenv }} + - if: ${{ contains(matrix.toxenv,'-cov') }} + uses: codecov/codecov-action@v3 with: file: ./coverage.xml + flags: unit + fail_ci_if_error: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/.readthedocs.yaml new/gwcs-0.18.3/.readthedocs.yaml --- old/gwcs-0.18.2/.readthedocs.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/gwcs-0.18.3/.readthedocs.yaml 2022-12-23 20:01:15.000000000 +0100 @@ -0,0 +1,21 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details +version: 2 + +sphinx: + builder: html + configuration: docs/conf.py + fail_on_warning: true + +# Set the version of Python and requirements required to build your docs +python: + version: 3.8 + system_packages: true + install: + - method: pip + path: . + extra_requirements: + - docs + +# Don't build any extra formats +formats: [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/CHANGES.rst new/gwcs-0.18.3/CHANGES.rst --- old/gwcs-0.18.2/CHANGES.rst 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/CHANGES.rst 2022-12-23 20:01:15.000000000 +0100 @@ -1,3 +1,15 @@ +0.18.3 (2022-12-23) +------------------- +Bug Fixes +^^^^^^^^^ + +- Fixed a bug in the estimate of pixel scale in the iterative inverse + code. [#423] + +- Fixed constant term in the polynomial used for SIP fitting. + Improved stability and accuracy of the SIP fitting code. [#427] + + 0.18.2 (2022-09-07) ------------------- Bug Fixes @@ -222,6 +234,7 @@ - Fixed WCS API issues when ``output_frame`` is 1D, e.g. ``Spectral`` only. [#232] + 0.10.0 (12/20/2018) ------------------- @@ -239,6 +252,7 @@ - Added a ``wcs_from_[points`` function which creates a WCS object two matching sets of points ``(x,y)`` and ``(ra, dec)``. [#42] + 0.9.0 (2018-05-23) ------------------ @@ -280,6 +294,7 @@ - Fixed a bug in ``grid_from_bounding_box`` which caused the grid to be larger than the image in cases when the bounding box is on the edges of an image. [#121] + 0.8.0 (2017-11-02) ------------------ @@ -292,7 +307,7 @@ - Replace ``domain`` with ``bounding_box``. [#74] - Added a ``LabelMapper`` model where ``mapper`` is an instance of - `~astropy.modeling.core.Model`. [#78] + `~astropy.modeling.Model`. [#78] - Evaluating a WCS with bounding box was moved to ``astropy.modeling``. [#86] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/PKG-INFO new/gwcs-0.18.3/PKG-INFO --- old/gwcs-0.18.2/PKG-INFO 2022-09-07 19:01:34.642974100 +0200 +++ new/gwcs-0.18.3/PKG-INFO 2022-12-23 20:01:27.781919200 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: gwcs -Version: 0.18.2 +Version: 0.18.3 Summary: Generalized World Coordinate System Home-page: https://github.com/spacetelescope/gwcs Author: gwcs developers @@ -10,6 +10,7 @@ Project-URL: Documentation, https://gwcs.readthedocs.io/en/stable/ Project-URL: Source Code, https://github.com/spacetelescope/jwst Requires-Python: >=3.8 +Description-Content-Type: text/x-rst Provides-Extra: docs Provides-Extra: test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/docs/conf.py new/gwcs-0.18.3/docs/conf.py --- old/gwcs-0.18.2/docs/conf.py 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/docs/conf.py 2022-12-23 20:01:15.000000000 +0100 @@ -165,3 +165,12 @@ sys.path.insert(0, os.path.join(os.path.dirname('__file__'), 'sphinxext')) extensions += ['sphinx_asdf'] + +# Enable nitpicky mode - which ensures that all references in the docs resolve. +nitpicky = True +nitpick_ignore = [ + ('py:class', 'gwcs.api.GWCSAPIMixin'), + ('py:obj', 'astropy.modeling.projections.projcodes'), + ('py:attr', 'gwcs.WCS.bounding_box'), + ('py:meth', 'gwcs.WCS.footprint') +] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/docs/gwcs/points_to_wcs.rst new/gwcs-0.18.3/docs/gwcs/points_to_wcs.rst --- old/gwcs-0.18.2/docs/gwcs/points_to_wcs.rst 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/docs/gwcs/points_to_wcs.rst 2022-12-23 20:01:15.000000000 +0100 @@ -12,7 +12,7 @@ This example shows how to use the `~gwcs.wcstools.wcs_from_points` tool to fit a WCS to a matched set of pixel and sky positions. Along with arrays of the (x,y) pixel position in the image and the matched sky coordinates, the fiducial point for the projection must be supplied as a `~astropy.coordinates.SkyCoord` object. Additionally, -the projection type must be specified from the available projections in `~astropy.modeling.projections.projcode`. +the projection type must be specified from the available projections in `~astropy.modeling.projections.projcodes`. Geometric distortion can also be fit to the input coordinates - the distortion type (2D polynomial, chebyshev, legendre) and the degree can be supplied to fit this component of the model. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/docs/gwcs/using_wcs.rst new/gwcs-0.18.3/docs/gwcs/using_wcs.rst --- old/gwcs-0.18.2/docs/gwcs/using_wcs.rst 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/docs/gwcs/using_wcs.rst 2022-12-23 20:01:15.000000000 +0100 @@ -145,7 +145,7 @@ In order to catch these kind of errors that can occur during numerical inversion, we need to turn off ``quiet`` mode and be prepared to catch -`gwcs.wcs.WCS.NoConvergence` exceptions. In the next example, let's also add a +`gwcs.wcs.NoConvergence` exceptions. In the next example, let's also add a point far away from the image for which numerical inverse fails. .. doctest-skip:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/docs/index.rst new/gwcs-0.18.3/docs/index.rst --- old/gwcs-0.18.2/docs/index.rst 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/docs/index.rst 2022-12-23 20:01:15.000000000 +0100 @@ -297,8 +297,8 @@ -Using `gwcs` ------------- +Using ``gwcs`` +-------------- .. toctree:: :maxdepth: 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/gwcs/coordinate_frames.py new/gwcs-0.18.3/gwcs/coordinate_frames.py --- old/gwcs-0.18.2/gwcs/coordinate_frames.py 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/gwcs/coordinate_frames.py 2022-12-23 20:01:15.000000000 +0100 @@ -94,7 +94,7 @@ reference_frame : astropy.coordinates.builtin_frames Reference frame (usually used with output_frame to convert to world coordinate objects). reference_position : str - Reference position - one of `STANDARD_REFERENCE_POSITION` + Reference position - one of ``STANDARD_REFERENCE_POSITION`` unit : list of astropy.units.Unit Unit for each axis. axes_names : list @@ -431,7 +431,7 @@ name : str Name for this frame. reference_position : str - Reference position - one of `STANDARD_REFERENCE_POSITION` + Reference position - one of ``STANDARD_REFERENCE_POSITION`` """ @@ -481,7 +481,7 @@ reference_frame : `~astropy.time.Time` A Time object which holds the time scale and format. If data is provided, it is the time zero point. - To not set a zero point for the frame initialize `reference_frame` + To not set a zero point for the frame initialize ``reference_frame`` with an empty list. unit : str or `~astropy.units.Unit` Time unit. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/gwcs/selector.py new/gwcs-0.18.3/gwcs/selector.py --- old/gwcs-0.18.2/gwcs/selector.py 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/gwcs/selector.py 2022-12-23 20:01:15.000000000 +0100 @@ -13,7 +13,7 @@ - A mapping of inputs to labels - "label_mapper" - A mapping of labels to transforms - "transform_selector" -A "label_mapper" is also a transform, a subclass of `astropy.modeling.core.Model`, +A "label_mapper" is also a transform, a subclass of `astropy.modeling.Model`, which returns the labels corresponding to the inputs. An instance of a ``LabelMapper`` class is passed to `RegionsSelector`. @@ -504,7 +504,7 @@ selector : dict Mapping of region labels to transforms. Labels can be of type int or str, transforms are of type - `~astropy.modeling.core.Model`. + `~astropy.modeling.Model`. label_mapper : a subclass of `~gwcs.selector._LabelMapper` A model which maps locations to region labels. undefined_transform_value : float, np.nan (default) @@ -651,7 +651,7 @@ Parameters ---------- - mapper : `~astropy.modeling.core.Model` + mapper : `~astropy.modeling.Model` A function which returns a region. no_label : str or int "" or 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/gwcs/spectroscopy.py new/gwcs-0.18.3/gwcs/spectroscopy.py --- old/gwcs-0.18.2/gwcs/spectroscopy.py 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/gwcs/spectroscopy.py 2022-12-23 20:01:15.000000000 +0100 @@ -189,7 +189,7 @@ Iterable of size 3 containing B coefficients. C_coef : ndarray Iterable of size 3 containing c coefficients in - units of `u.um**2`. + units of ``u.um**2``. Returns ------- @@ -264,9 +264,9 @@ Parameters ---------- temperature : float - Temperature of the material in `u.Kelvin`. + Temperature of the material in ``u.Kelvin``. ref_temperature : float - Reference emperature of the glass in `u.Kelvin`. + Reference emperature of the glass in ``u.Kelvin``. ref_pressure : float Reference pressure in ATM. pressure : float @@ -275,7 +275,7 @@ Iterable of size 3 containing B coefficients. C_coef : ndarray Iterable of size 3 containing C coefficients in - units of `u.um**2`. + units of ``u.um**2``. D_coef : ndarray Iterable of size 3 containing constants to describe the behavior of the material. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/gwcs/tests/test_wcs.py new/gwcs-0.18.3/gwcs/tests/test_wcs.py --- old/gwcs-0.18.2/gwcs/tests/test_wcs.py 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/gwcs/tests/test_wcs.py 2022-12-23 20:01:15.000000000 +0100 @@ -19,6 +19,7 @@ from .. import coordinate_frames as cf from .. import utils from ..utils import CoordinateFrameError +from .utils import _gwcs_from_hst_fits_wcs import asdf @@ -605,8 +606,8 @@ assert_allclose(footprint, fits_footprint) def test_inverse(self): - sky_coord = self.wcs(1, 2, with_units=True) - assert np.allclose(self.wcs.invert(sky_coord), (1, 2)) + sky_coord = self.wcs(10, 20, with_units=True) + assert np.allclose(self.wcs.invert(sky_coord), (10, 20)) def test_back_coordinates(self): sky_coord = self.wcs(1, 2, with_units=True) @@ -636,9 +637,9 @@ af = asdf.open(get_pkg_data_filename('data/miriwcs.asdf')) miriwcs = af.tree['wcs'] bounding_box = ((0, 1024), (0, 1024)) - mirisip = miriwcs.to_fits_sip(bounding_box, max_inv_pix_error=0.1) + mirisip = miriwcs.to_fits_sip(bounding_box, max_inv_pix_error=0.1, verbose=True) fitssip = astwcs.WCS(mirisip) - fitsvalx, fitsvaly = fitssip.all_pix2world(xflat+1, yflat+1, 1) + fitsvalx, fitsvaly = fitssip.all_pix2world(xflat + 1, yflat + 1, 1) gwcsvalx, gwcsvaly = miriwcs(xflat, yflat) assert_allclose(gwcsvalx, fitsvalx, atol=1e-10, rtol=0) assert_allclose(gwcsvaly, fitsvaly, atol=1e-10, rtol=0) @@ -648,7 +649,7 @@ mirisip = miriwcs.to_fits_sip(bounding_box=None, max_inv_pix_error=0.1) fitssip = astwcs.WCS(mirisip) - fitsvalx, fitsvaly = fitssip.all_pix2world(xflat+1, yflat+1, 1) + fitsvalx, fitsvaly = fitssip.all_pix2world(xflat + 1, yflat + 1, 1) assert_allclose(gwcsvalx, fitsvalx, atol=4e-11, rtol=0) assert_allclose(gwcsvaly, fitsvaly, atol=4e-11, rtol=0) @@ -699,7 +700,7 @@ crpix=None, projection='TAN', matrix_type=matrix_type, - verbose=False + verbose=True ) fitssip = astwcs.WCS(fits_wcs) @@ -1224,3 +1225,38 @@ with warnings.catch_warnings(): warnings.simplefilter("error") wcs.WCS(pipeline) + + +def test_sip_roundtrip(): + hdr = fits.Header.fromtextfile(get_pkg_data_filename("data/acs.hdr"), + endcard=False) + nx = ny = 1024 + hdr['naxis'] = 2 + hdr['naxis1'] = nx + hdr['naxis2'] = ny + gw = _gwcs_from_hst_fits_wcs(hdr) + hdr_back = gw.to_fits_sip( + max_pix_error=1e-6, + max_inv_pix_error=None, + npoints=64, + crpix=(hdr['crpix1'], hdr['crpix2']) + ) + + for k in ['naxis', 'naxis1', 'naxis2', 'ctype1', 'ctype2', 'a_order', 'b_order']: + assert hdr[k] == hdr_back[k] + + for k in ['cd1_1', 'cd1_2', 'cd2_1', 'cd2_2']: + assert np.allclose(hdr[k], hdr_back[k], atol=0, rtol=1e-9) + + for t in ('a', 'b'): + order = hdr[f'{t}_order'] + for i in range(order + 1): + for j in range(order + 1): + if 1 < i + j <= order: + k = f'{t}_{i}_{j}' + assert np.allclose( + hdr[k], + hdr_back[k], + atol=0.0, + rtol=1.0e-8 * 10**(i + j) + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/gwcs/tests/utils.py new/gwcs-0.18.3/gwcs/tests/utils.py --- old/gwcs-0.18.2/gwcs/tests/utils.py 1970-01-01 01:00:00.000000000 +0100 +++ new/gwcs-0.18.3/gwcs/tests/utils.py 2022-12-23 20:01:15.000000000 +0100 @@ -0,0 +1,59 @@ +import numpy as np + +from astropy.modeling.models import ( + Shift, Polynomial2D, Pix2Sky_TAN, RotateNative2Celestial, Mapping +) + +from astropy import coordinates as coord +from astropy import units +from astropy import wcs as fits_wcs + +from .. wcs import WCS +from .. import coordinate_frames as cf + + +def _gwcs_from_hst_fits_wcs(header, hdu=None): + # NOTE: this function ignores table distortions + def coeffs_to_poly(mat, degree): + pol = Polynomial2D(degree=degree) + for i in range(mat.shape[0]): + for j in range(mat.shape[1]): + if 0 < i + j <= degree: + setattr(pol, f'c{i}_{j}', mat[i, j]) + return pol + + w = fits_wcs.WCS(header, hdu) + ny, nx = w.pixel_shape + x0, y0 = w.wcs.crpix - 1 + + cd = w.wcs.piximg_matrix + + cfx, cfy = np.dot(cd, [w.sip.a.ravel(), w.sip.b.ravel()]) + a = np.reshape(cfx, w.sip.a.shape) + b = np.reshape(cfy, w.sip.b.shape) + a[1, 0] = cd[0, 0] + a[0, 1] = cd[0, 1] + b[1, 0] = cd[1, 0] + b[0, 1] = cd[1, 1] + + polx = coeffs_to_poly(a, w.sip.a_order) + poly = coeffs_to_poly(b, w.sip.b_order) + + # construct GWCS: + det2sky = ( + (Shift(-x0) & Shift(-y0)) | Mapping((0, 1, 0, 1)) | (polx & poly) | + Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180) + ) + + detector_frame = cf.Frame2D(name="detector", axes_names=("x", "y"), + unit=(units.pix, units.pix)) + sky_frame = cf.CelestialFrame( + reference_frame=getattr(coord, w.wcs.radesys).__call__(), + name=w.wcs.radesys, + unit=(units.deg, units.deg) + ) + pipeline = [(detector_frame, det2sky), (sky_frame, None)] + gw = WCS(pipeline) + gw.bounding_box = ((-0.5, nx - 0.5), (-0.5, ny - 0.5)) + + return gw diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/gwcs/wcs.py new/gwcs-0.18.3/gwcs/wcs.py --- old/gwcs-0.18.2/gwcs/wcs.py 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/gwcs/wcs.py 2022-12-23 20:01:15.000000000 +0100 @@ -4,7 +4,7 @@ import warnings import numpy as np import numpy.linalg as npla -from scipy import optimize +from scipy import optimize, linalg from astropy import units as u from astropy.modeling.core import Model from astropy.modeling.models import ( @@ -12,7 +12,6 @@ Sky2Pix_TAN, RotateCelestial2Native ) from astropy.modeling import projections, fix_inputs -from astropy.modeling.fitting import LinearLSQFitter import astropy.io.fits as fits from astropy.wcs.utils import celestial_frame_to_wcs, proj_plane_pixel_scales @@ -199,9 +198,9 @@ Parameters ---------- - from_frame : str or `~gwcs.coordinate_frame.CoordinateFrame` + from_frame : str or `~gwcs.coordinate_frames.CoordinateFrame` Initial coordinate frame name of object. - to_frame : str, or instance of `~gwcs.cordinate_frames.CoordinateFrame` + to_frame : str, or instance of `~gwcs.coordinate_frames.CoordinateFrame` End coordinate frame name or object. Returns @@ -237,9 +236,9 @@ Parameters ---------- - from_frame : str or `~gwcs.coordinate_frame.CoordinateFrame` + from_frame : str or `~gwcs.coordinate_frames.CoordinateFrame` Initial coordinate frame. - to_frame : str, or instance of `~gwcs.cordinate_frames.CoordinateFrame` + to_frame : str, or instance of `~gwcs.coordinate_frames.CoordinateFrame` End coordinate frame. transform : `~astropy.modeling.Model` Transform between ``from_frame`` and ``to_frame``. @@ -871,10 +870,8 @@ l2, phi2 = np.deg2rad(self.__call__(*(crpix + [-0.5, 0.5]))) l3, phi3 = np.deg2rad(self.__call__(*(crpix + 0.5))) l4, phi4 = np.deg2rad(self.__call__(*(crpix + [0.5, -0.5]))) - area = np.abs(0.5 * ((l4 - l2) * np.sin(phi1) + - (l1 - l3) * np.sin(phi2) + - (l2 - l4) * np.sin(phi3) + - (l3 - l2) * np.sin(phi4))) + area = np.abs(0.5 * ((l4 - l2) * (np.sin(phi1) - np.sin(phi3)) + + (l1 - l3) * (np.sin(phi2) - np.sin(phi4)))) inv_pscale = 1 / np.rad2deg(np.sqrt(area)) # form equation: @@ -1117,7 +1114,7 @@ ---------- from_frame : str or `~gwcs.coordinate_frames.CoordinateFrame` Initial coordinate frame. - to_frame : str, or instance of `~gwcs.cordinate_frames.CoordinateFrame` + to_frame : str, or instance of `~gwcs.coordinate_frames.CoordinateFrame` Coordinate frame into which to transform. args : float or array-like Inputs in ``from_frame``, separate inputs for each dimension. @@ -1182,7 +1179,7 @@ Parameters ---------- - frame : str or `~gwcs.coordinate_frame.CoordinateFrame` + frame : str or `~gwcs.coordinate_frames.CoordinateFrame` Coordinate frame which sets the point of insertion. transform : `~astropy.modeling.Model` New transform to be inserted in the pipeline @@ -1204,17 +1201,17 @@ Insert a new frame into an existing pipeline. This frame must be anchored to a frame already in the pipeline by a transform. This existing frame is identified solely by its name, although an entire - `~gwcs.coordinate_frame.CoordinateFrame` can be passed (e.g., the + `~gwcs.coordinate_frames.CoordinateFrame` can be passed (e.g., the `input_frame` or `output_frame` attribute). This frame is never modified. Parameters ---------- - input_frame : str or `~gwcs.coordinate_frame.CoordinateFrame` + input_frame : str or `~gwcs.coordinate_frames.CoordinateFrame` Coordinate frame at start of new transform transform : `~astropy.modeling.Model` New transform to be inserted in the pipeline - output_frame: str or `~gwcs.coordinate_frame.CoordinateFrame` + output_frame: str or `~gwcs.coordinate_frames.CoordinateFrame` Coordinate frame at end of new transform """ input_name, input_frame_obj = self._get_frame_name(input_frame) @@ -1420,12 +1417,12 @@ Parameters ---------- bounding_box : tuple of floats: (start, stop) - `prop: bounding_box` + ``prop: bounding_box`` center : bool If `True` use the center of the pixel, otherwise use the corner. axis_type : str - A supported ``output_frame.axes_type`` or "all" (default). - One of ['spatial', 'spectral', 'temporal'] or a custom type. + A supported ``output_frame.axes_type`` or ``"all"`` (default). + One of [``'spatial'``, ``'spectral'``, ``'temporal'``] or a custom type. Returns ------- @@ -1483,12 +1480,12 @@ Parameters ---------- fixed : dict - Keyword arguments with fixed values corresponding to `self.selector`. + Keyword arguments with fixed values corresponding to ``self.selector``. Returns ------- new_wcs : `WCS` - A new unique WCS corresponding to the values in `fixed`. + A new unique WCS corresponding to the values in ``fixed``. Examples -------- @@ -1528,7 +1525,8 @@ Maximum allowed error over the domain of the pixel array. This error is the equivalent pixel error that corresponds to the maximum error in the output coordinate resulting from the fit based on - a nominal plate scale. + a nominal plate scale. Ignored when ``degree`` is an integer or + a list with a single degree. degree : int, iterable, None, optional Degree of the SIP polynomial. Default value `None` indicates that @@ -1544,7 +1542,8 @@ max_inv_pix_error : float, optional Maximum allowed inverse error over the domain of the pixel array - in pixel units. If None, no inverse is generated. + in pixel units. If None, no inverse is generated. Ignored when + ``degree`` is an integer or a list with a single degree. inv_degree : int, iterable, None, optional Degree of the SIP polynomial. Default value `None` indicates that @@ -1829,8 +1828,9 @@ plate_scale = np.sqrt(pixarea) # The fitting section. + if verbose: + print("\nFitting forward SIP ...") fit_poly_x, fit_poly_y, max_resid = _fit_2D_poly( - ntransform, npoints, degree, max_pix_error, plate_scale, u, v, undist_x, undist_y, ud, vd, undist_xd, undist_yd, @@ -1849,12 +1849,15 @@ Vd = (-cdmat[1][0] * undist_xd + cdmat[0][0] * undist_yd) / detd if max_inv_pix_error: - fit_inv_poly_u, fit_inv_poly_v, max_inv_resid = _fit_2D_poly(ntransform, - npoints, inv_degree, - max_inv_pix_error, 1, - U, V, u-U, v-V, - Ud, Vd, ud-Ud, vd-Vd, - verbose=verbose) + if verbose: + print("\nFitting inverse SIP ...") + fit_inv_poly_u, fit_inv_poly_v, max_inv_resid = _fit_2D_poly( + inv_degree, + max_inv_pix_error, 1, + U, V, u-U, v-V, + Ud, Vd, ud-Ud, vd-Vd, + verbose=verbose + ) # create header with WCS info: w = celestial_frame_to_wcs(frame.reference_frame, projection=projection) @@ -2178,8 +2181,8 @@ does not match the number of image axes. RuntimeError - If the number of image axes (`~gwcs.WCS.pixel_n_dim`) is larger - than the number of world axes (`~gwcs.WCS.world_n_dim`). + If the number of image axes (``~gwcs.WCS.pixel_n_dim``) is larger + than the number of world axes (``~gwcs.WCS.world_n_dim``). """ if bounding_box is None: @@ -2359,8 +2362,8 @@ does not match the number of image axes. RuntimeError - If the number of image axes (`~gwcs.WCS.pixel_n_dim`) is larger - than the number of world axes (`~gwcs.WCS.world_n_dim`). + If the number of image axes (``~gwcs.WCS.pixel_n_dim``) is larger + than the number of world axes (``~gwcs.WCS.world_n_dim``). """ if bounding_box is None: @@ -2541,8 +2544,8 @@ TypeError RuntimeError - If the number of image axes (`~gwcs.WCS.pixel_n_dim`) is larger - than the number of world axes (`~gwcs.WCS.world_n_dim`). + If the number of image axes (``~gwcs.WCS.pixel_n_dim``) is larger + than the number of world axes (``~gwcs.WCS.world_n_dim``). """ if isinstance(bin_ext, str): @@ -2740,8 +2743,7 @@ undist_xd, undist_yd = ntransform(ud, vd) fit_inv_poly_u, fit_inv_poly_v, max_inv_resid = _fit_2D_poly( - ntransform, - npoints, None, + None, max_inv_pix_error, 1, undist_x, undist_y, u, v, undist_xd, undist_yd, ud, vd, @@ -2754,7 +2756,106 @@ (Shift(crpix[0]) & Shift(crpix[1]))) -def _fit_2D_poly(ntransform, npoints, degree, max_error, plate_scale, +def _poly_fit_lu(xin, yin, xout, yout, degree, coord_pow=None): + # This function fits 2D polynomials to data by writing the normal system + # of equations and solving it using LU-decomposition. In theory this + # should be less stable than the SVD method used by numpy's lstsq or + # astropy's LinearLSQFitter because the condition of the normal matrix + # is squared compared to the direct matrix. However, in practice, + # in our (Mihai Cara) tests of fitting WCS distortions, solving the + # normal system proved to be significantly more accurate, efficient, + # and stable than SVD. + # + # coord_pow - a dictionary used to store powers of coordinate arrays + # of the form x**p * y**q used to build the pseudo-Vandermonde matrix. + # This improves efficiency especially when fitting multiple degrees + # on the same coordinate grid in _fit_2D_poly by reusing computed + # powers. + powers = [ + (i, j) + for i in range(degree + 1) for j in range(degree + 1 - i) if i + j > 0 + ] + if coord_pow is None: + coord_pow = {} + + nterms = len(powers) + + flt_type = np.longdouble + + # allocate array for the coefficients of the system of equations (a*x=b): + a = np.empty((nterms, nterms), dtype=flt_type) + bx = np.empty(nterms, dtype=flt_type) + by = np.empty(nterms, dtype=flt_type) + + xout = xout.ravel() + yout = yout.ravel() + + x = np.asarray(xin.ravel(), dtype=flt_type) + y = np.asarray(yin.ravel(), dtype=flt_type) + + # pseudo_vander - a reduced Vandermonde matrix for 2D polynomials + # that has only terms x^i * y^j with powers i, j that satisfy: + # 0 < i + j <= degree. + pseudo_vander = np.empty((x.size, nterms), dtype=float) + + def pow2(p, q): + # computes product of powers of coordinate arrays (x**p) * (y**q) + # in an efficient way avoiding unnecessary array copying + # and/or raising to power + if (p, q) in coord_pow: + return coord_pow[(p, q)] + if p == 0: + arr = y**q if q > 1 else y + elif q == 0: + arr = x**p if p > 1 else x + else: + xp = x if p == 1 else x**p + yq = y if q == 1 else y**q + arr = xp * yq + coord_pow[(p, q)] = arr + return arr + + for i in range(nterms): + pi, qi = powers[i] + coord_pq = pow2(pi, qi) + pseudo_vander[:, i] = coord_pq + bx[i] = np.sum(xout * coord_pq, dtype=flt_type) + by[i] = np.sum(yout * coord_pq, dtype=flt_type) + + for j in range(i, nterms): + pj, qj = powers[j] + coord_pq = pow2(pi + pj, qi + qj) + a[i, j] = np.sum(coord_pq, dtype=flt_type) + a[j, i] = a[i, j] + + with warnings.catch_warnings(record=True): + warnings.simplefilter('error', category=linalg.LinAlgWarning) + try: + lu_piv = linalg.lu_factor(a) + poly_coeff_x = linalg.lu_solve(lu_piv, bx).astype(float) + poly_coeff_y = linalg.lu_solve(lu_piv, by).astype(float) + except (ValueError, linalg.LinAlgWarning, np.linalg.LinAlgError) as e: + raise np.linalg.LinAlgError( + f"Failed to fit SIP. Reported error:\n{e.args[0]}" + ) + + if not np.all(np.isfinite([poly_coeff_x, poly_coeff_y])): + raise np.linalg.LinAlgError( + "Failed to fit SIP. Computed coefficients are not finite." + ) + + cond = np.linalg.cond(a.astype(float)) + + fitx = np.dot(pseudo_vander, poly_coeff_x) + fity = np.dot(pseudo_vander, poly_coeff_y) + + dist = np.sqrt((xout - fitx)**2 + (yout - fity)**2) + max_resid = dist.max() + + return poly_coeff_x, poly_coeff_y, max_resid, powers, cond + + +def _fit_2D_poly(degree, max_error, plate_scale, xin, yin, xout, yout, xind, yind, xoutd, youtd, verbose=False): @@ -2762,14 +2863,12 @@ Fit a pair of ordinary 2D polynomials to the supplied transform. """ - llsqfitter = LinearLSQFitter() - # The case of one pass with the specified polynomial degree if degree is None: - deglist = range(1, 10) + deglist = list(range(1, 10)) elif hasattr(degree, '__iter__'): deglist = sorted(map(int, degree)) - if set(deglist).difference(range(1, 10)): + if deglist[0] < 1 or deglist[-1] > 9: raise ValueError("Allowed values for SIP degree are [1...9]") else: degree = int(degree) @@ -2777,44 +2876,116 @@ raise ValueError("Allowed values for SIP degree are [1...9]") deglist = [degree] - prev_max_error = float(np.inf) - if verbose: - print(f'Maximum_specified_error: {max_error}') + single_degree = len(deglist) == 1 + + fit_error = np.inf + if verbose and not single_degree: + print(f'Maximum specified SIP approximation error: {max_error}') max_error *= plate_scale + fit_warning_msg = "Failed to achieve requested SIP approximation accuracy." + + # Fit lowest degree SIP first. + coord_pow = {} # hold coordinate arrays powers for optimization purpose for deg in deglist: - poly_x = Polynomial2D(degree=deg) - poly_y = Polynomial2D(degree=deg) - fit_poly_x = llsqfitter(poly_x, xin, yin, xout) - fit_poly_y = llsqfitter(poly_y, xin, yin, yout) - max_resid = _compute_distance_residual(xout, yout, - fit_poly_x(xin, yin), - fit_poly_y(xin, yin)) - if max_resid > prev_max_error: - raise RuntimeError('Failed to achieve required error tolerance') - prev_max_error = max_resid + try: + cfx_i, cfy_i, fit_error_i, powers_i, cond = _poly_fit_lu( + xin, yin, xout, yout, degree=deg, coord_pow=coord_pow + ) + if verbose and not single_degree: + print( + f" - SIP degree: {deg}. " + f"Maximum residual: {fit_error_i / plate_scale:.5g}" + ) - if verbose: - print(f'Degree = {deg}, max_resid = {max_resid / plate_scale}') - if max_resid < max_error: - # Check to see if double sampling meets error requirement. - max_resid = _compute_distance_residual(xoutd, youtd, - fit_poly_x(xind, yind), - fit_poly_y(xind, yind)) - if verbose: - print(f'Double sampling check: maximum residual={max_resid / plate_scale}') - if max_resid < max_error: - if verbose: - print('Terminating condition met') + except np.linalg.LinAlgError as e: + if single_degree: + # Nothing to do if failure is for the lowest degree + raise e + else: + # Keep results from the previous iteration. Discard current fit break - return fit_poly_x, fit_poly_y, max_resid / plate_scale + if not np.isfinite(cond): + # Ill-conditioned system + if single_degree: + warnings.warn("The fit may be poorly conditioned.") + cfx = cfx_i + cfy = cfy_i + fit_error = fit_error_i + powers = powers_i + break + + if fit_error_i >= fit_error: + # Accuracy does not improve. Likely ill-conditioned system + break + + cfx = cfx_i + cfy = cfy_i + powers = powers_i + + fit_error = fit_error_i + + if fit_error <= max_error: + # Requested accuracy has been achieved + fit_warning_msg = None + break + + # Continue to the next degree + + fit_poly_x = Polynomial2D(degree=deg, c0_0=0.0) + fit_poly_y = Polynomial2D(degree=deg, c0_0=0.0) + for cx, cy, (p, q) in zip(cfx, cfy, powers): + setattr(fit_poly_x, f'c{p:1d}_{q:1d}', cx) + setattr(fit_poly_y, f'c{p:1d}_{q:1d}', cy) + + if fit_warning_msg: + warnings.warn(fit_warning_msg, linalg.LinAlgWarning) + + if fit_error <= max_error or single_degree: + # Check to see if double sampling meets error requirement. + max_resid = _compute_distance_residual( + xoutd, + youtd, + fit_poly_x(xind, yind), + fit_poly_y(xind, yind) + ) + if verbose: + print( + "* Maximum residual, double sampled grid: " + f"{max_resid / plate_scale:.5g}" + ) + + if max_resid > min(5.0 * fit_error, max_error): + warnings.warn( + "Double sampling check FAILED: Sampling may be too coarse for " + "the distortion model being fitted." + ) + + # Residuals on the double-dense grid may be better estimates + # of the accuracy of the fit. So we report the largest of + # the residuals (on single- and double-sampled grid) as the fit error: + fit_error = max(max_resid, fit_error) + + if verbose: + if single_degree: + print( + f"Maximum residual: {fit_error / plate_scale:.5g}" + ) + else: + print( + f"* Final SIP degree: {deg}. " + f"Maximum residual: {fit_error / plate_scale:.5g}" + ) + + return fit_poly_x, fit_poly_y, fit_error / plate_scale def _make_sampling_grid(npoints, bounding_box, crpix): step = np.subtract.reduce(bounding_box, axis=1) / (1.0 - npoints) crpix = np.asanyarray(crpix)[:, None, None] - return grid_from_bounding_box(bounding_box, step=step, center=False) - crpix + x, y = grid_from_bounding_box(bounding_box, step=step, center=False) - crpix + return x.flatten(), y.flatten() def _compute_distance_residual(undist_x, undist_y, fit_poly_x, fit_poly_y): @@ -2913,9 +3084,9 @@ ---------- frame : `~gwcs.coordinate_frames.CoordinateFrame` A gwcs coordinate frame object. - transform : `~astropy.modeling.core.Model` or None + transform : `~astropy.modeling.Model` or None A transform from this step's frame to next step's frame. - The transform of the last step should be ``None``. + The transform of the last step should be `None`. """ def __init__(self, frame, transform=None): self.frame = frame diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/gwcs/wcstools.py new/gwcs-0.18.3/gwcs/wcstools.py --- old/gwcs-0.18.2/gwcs/wcstools.py 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/gwcs/wcstools.py 2022-12-23 20:01:15.000000000 +0100 @@ -46,7 +46,7 @@ bounding_box : tuple The bounding box over which the WCS is valid. It is a tuple of tuples of size 2 where each tuple - represents a range of (low, high) values. The `bounding_box` is in the order of + represents a range of (low, high) values. The ``bounding_box`` is in the order of the axes, `~gwcs.coordinate_frames.CoordinateFrame.axes_order`. For two inputs and axes_order(0, 1) the bounding box is ((xlow, xhigh), (ylow, yhigh)). input_frame : ~gwcs.coordinate_frames.CoordinateFrame` @@ -247,8 +247,8 @@ be passed in. projection : `~astropy.modeling.projections.Projection` A projection type. One of the projections in - `~astropy.modeling.projections.projcode`. Defaults to TAN projection - (`projections.Sky2Pix_TAN()`). + `~astropy.modeling.projections.projcodes`. Defaults to TAN projection + (`astropy.modeling.projections.Sky2Pix_TAN`). poly_degree : int Degree of polynomial model to be fit to data. Defaults to 4. polynomial_type : str diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/gwcs.egg-info/PKG-INFO new/gwcs-0.18.3/gwcs.egg-info/PKG-INFO --- old/gwcs-0.18.2/gwcs.egg-info/PKG-INFO 2022-09-07 19:01:34.000000000 +0200 +++ new/gwcs-0.18.3/gwcs.egg-info/PKG-INFO 2022-12-23 20:01:27.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: gwcs -Version: 0.18.2 +Version: 0.18.3 Summary: Generalized World Coordinate System Home-page: https://github.com/spacetelescope/gwcs Author: gwcs developers @@ -10,6 +10,7 @@ Project-URL: Documentation, https://gwcs.readthedocs.io/en/stable/ Project-URL: Source Code, https://github.com/spacetelescope/jwst Requires-Python: >=3.8 +Description-Content-Type: text/x-rst Provides-Extra: docs Provides-Extra: test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/gwcs.egg-info/SOURCES.txt new/gwcs-0.18.3/gwcs.egg-info/SOURCES.txt --- old/gwcs-0.18.2/gwcs.egg-info/SOURCES.txt 2022-09-07 19:01:34.000000000 +0200 +++ new/gwcs-0.18.3/gwcs.egg-info/SOURCES.txt 2022-12-23 20:01:27.000000000 +0100 @@ -1,5 +1,6 @@ .bandit.yaml .gitignore +.readthedocs.yaml CHANGES.rst CODE_OF_CONDUCT.md CONTRIBUTING.md @@ -8,7 +9,6 @@ conftest.py convert_schemas.py pyproject.toml -readthedocs.yml requirements-dev.txt setup.cfg setup.py @@ -69,6 +69,7 @@ gwcs/tests/test_spectroscopy_models.py gwcs/tests/test_utils.py gwcs/tests/test_wcs.py +gwcs/tests/utils.py gwcs/tests/data/acs.hdr gwcs/tests/data/acs_wfc.hdr gwcs/tests/data/miri_lrs_wcs.asdf diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/readthedocs.yml new/gwcs-0.18.3/readthedocs.yml --- old/gwcs-0.18.2/readthedocs.yml 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/readthedocs.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,21 +0,0 @@ -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details -version: 2 - -sphinx: - builder: html - configuration: docs/conf.py - fail_on_warning: true - -# Set the version of Python and requirements required to build your docs -python: - version: 3.8 - system_packages: true - install: - - method: pip - path: . - extra_requirements: - - docs - -# Don't build any extra formats -formats: [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/setup.cfg new/gwcs-0.18.3/setup.cfg --- old/gwcs-0.18.2/setup.cfg 2022-09-07 19:01:34.642974100 +0200 +++ new/gwcs-0.18.3/setup.cfg 2022-12-23 20:01:27.781919200 +0100 @@ -2,6 +2,7 @@ name = gwcs description = Generalized World Coordinate System long_description = Tools for managing the WCS of astronomical observations in a general (non-FITS) way +long_description_content_type = text/x-rst author = gwcs developers author_email = h...@stsci.edu license = BSD diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gwcs-0.18.2/tox.ini new/gwcs-0.18.3/tox.ini --- old/gwcs-0.18.2/tox.ini 2022-09-07 19:01:21.000000000 +0200 +++ new/gwcs-0.18.3/tox.ini 2022-12-23 20:01:15.000000000 +0100 @@ -1,55 +1,104 @@ [tox] -envlist= {py38,py39, py310}-test - py38-dev - py38-test-numpy{119,120} - style - bandit - py38-cov - -requires = - setuptools >= 30.3.0 - pip >= 19.3.1 -isolated_build = true +envlist = + check-{style,security,build} + test{,-dev}{,-pyargs,-cov} + test-numpy{120,121,122} + build-{docs,dist} + +# tox environments are constructed 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 +# + +[testenv:check-style] +description = check code style, e.g. with flake8 +skip_install = true +deps = + flake8 +commands = + flake8 . {posargs} -[testenv] -passenv= HOME GITHUB_* TOXENV CI CODECOV_* DISPLAY +[testenv:check-security] +description = run bandit to check security compliance +skip_install = true +deps = + bandit>=1.7 +commands = + bandit -r -ll -c .bandit.yaml gwcs + +[testenv:check-build] +description = check build sdist/wheel and a strict twine check for metadata +skip_install = true +deps = + twine>=3.3 + build +commands = + python -m build . + twine check --strict dist/* +[testenv] description = run tests + jwst: of JWST pipeline + romancal: of Romancal pipeline dev: with the latest developer version of key dependencies - cov: and test coverage - numpy120: with numpy 1.20.* - numpy119: with numpy 1.19.* - -deps= - dev: -rrequirements-dev.txt - - cov: coverage - - numpy120: numpy==1.20.* - numpy119: numpy==1.19.* - + pyargs: with --pyargs on installed package + warnings: treating warnings as errors + cov: with coverage + xdist: using parallel processing +passenv = + HOME + GITHUB_* + TOXENV + CI + CODECOV_* + DISPLAY +args_are_paths = false +change_dir = pyargs: {homedir} extras = test - -commands = + alldeps: all +deps = + xdist: pytest-xdist + dev: -rrequirements-dev.txt + cov: pytest-cov + jwst: jwst[test] @ git+https://github.com/spacetelescope/jwst.git + romancal: romancal[test] @ git+https://github.com/spacetelescope/romancal.git + numpy120: numpy==1.20.* + numpy121: numpy==1.21.* + numpy122: numpy==1.22.* +set_env = + jwst: CRDS_SERVER_URL=https://jwst-crds.stsci.edu + romancal: CRDS_SERVER_URL=https://roman-crds.stsci.edu + jwst,romancal: CRDS_PATH=$HOME/crds_cache + jwst,romancal: CRDS_CLIENT_RETRY_COUNT=3 + jwst,romancal: CRDS_CLIENT_RETRY_DELAY_SECONDS=20 +commands_pre = pip freeze - !cov: pytest {posargs} - cov: pytest --cov-report xml --cov {posargs} - -[testenv:pyargs] -changedir = {homedir} commands = - pyargs: pytest {toxinidir}/docs --pyargs {posargs} - -[testenv:style] -deps= - flake8 -commands= - flake8 {posargs} + pytest \ + warnings: -W error \ + xdist: -n auto \ + pyargs: {toxinidir}/docs --pyargs gwcs \ + jwst: --pyargs jwst --ignore-glob=timeconversion --ignore-glob=associations \ + romancal: --pyargs romancal \ + cov: --cov=. --cov-config=setup.cfg --cov-report=term-missing --cov-report=xml \ + {posargs} + +[testenv:build-docs] +description = invoke sphinx-build to build the HTML docs +extras = docs +commands = + sphinx-build -W docs docs/_build -[testenv:bandit] -deps= - bandit -commands= - bandit -r -ll -c .bandit.yaml gwcs +[testenv:build-dist] +description = build wheel and sdist +skip_install = true +deps = + build +commands = + python -m build .