Skip to content

Commit

Permalink
Merge pull request #7556 from alasdairwilson/devdeps-erfa
Browse files Browse the repository at this point in the history
fix sunpy and numpy 2.0
  • Loading branch information
nabobalis committed Apr 10, 2024
2 parents bc5224f + bd9fef2 commit c6e2b30
Show file tree
Hide file tree
Showing 17 changed files with 86 additions and 57 deletions.
15 changes: 8 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
needs: [core]
uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main
with:
default_python: '3.10'
default_python: '3.12'
submodules: false
pytest: false
toxdeps: tox-pypi-filter
Expand All @@ -71,6 +71,7 @@ jobs:
needs: [docs]
uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main
with:
# Due to mplcario, we need to use Python 3.10 for the wheels
default_python: '3.10'
submodules: false
coverage: codecov
Expand Down Expand Up @@ -100,14 +101,14 @@ jobs:
)
uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main
with:
default_python: '3.10'
default_python: '3.12'
submodules: false
coverage: codecov
toxdeps: tox-pypi-filter
posargs: -n auto --color=yes
envs: |
- linux: base_deps
- linux: py311-devdeps
- linux: py312-devdeps
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

Expand All @@ -122,7 +123,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.12'
- run: python -m pip install -U --user build
- run: python -m build . --sdist
- run: python -m pip install -U --user twine
Expand All @@ -144,12 +145,12 @@ jobs:
with:
activate-environment: sunpy-test
environment-file: sunpy-dev-env.yml
python-version: "3.11"
python-version: "3.12"
- name: Install sunpy
shell: bash -el {0}
run: |
pip install --no-deps --no-build-isolation .
- name: Run tests
- name: Run test
shell: bash -el {0}
run: |
conda list
Expand Down Expand Up @@ -196,7 +197,7 @@ jobs:
needs: [publish]
uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish_pure_python.yml@main
with:
python-version: "3.10"
python-version: "3.12"
test_extras: 'tests'
test_command: 'pytest -p no:warnings --doctest-rst -m "not mpl_image_compare" --pyargs sunpy'
submodules: false
Expand Down
6 changes: 3 additions & 3 deletions docs/tutorial/units.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ A `~astropy.units.Quantity` can be decomposed into its unit and numerical value

.. code-block:: python
>>> length.value
>>> length.value # doctest: +SKIP
10.0
>>> length.unit
Expand Down Expand Up @@ -126,9 +126,9 @@ As shown above, you can retrieve the just the numerical value of a `~astropy.uni

.. code-block:: python
>>> length.to_value()
>>> length.to_value() # doctest: +SKIP
10.0
>>> length.to_value(u.km)
>>> length.to_value(u.km) # doctest: +SKIP
0.01
Quantities as function arguments
Expand Down
12 changes: 11 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ requires = [
"setuptools_scm[toml]>=6.2",
"wheel",
"extension-helpers",
"numpy>=1.25"
# Comments on numpy build requirement range:
#
# 1. >=2.0.x is the numpy requirement for wheel builds for distribution
# on PyPI - building against 2.x yields wheels that are also
# ABI-compatible with numpy 1.x at runtime.
# 2. Note that building against numpy 1.x works fine too - users and
# redistributors can do this by installing the numpy version they like
# and disabling build isolation.
# 3. The <2.3 upper bound is for matching the numpy deprecation policy,
# it should not be loosened.
"numpy>=2.0.0rc1,<2.3",
]
build-backend = "setuptools.build_meta"

Expand Down
8 changes: 8 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ filterwarnings =
# 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.*
# https://github.com/pytest-dev/pytest-cov/issues/557
# It was fixed and released but it does not seem to be fixed
ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning
Expand Down Expand Up @@ -66,3 +70,7 @@ filterwarnings =
ignore:unclosed \<socket:ResourceWarning
# Pending removal from sunpy 7.0
ignore:The QueryResponse class is deprecated
# This comes from our py310-oldestdeps - skimage/util/dtype.py
ignore:.*np\.bool8.*:DeprecationWarning
# Latest version of parfive raises this warning
ignore:This download has been started in a thread which is not the main thread:UserWarning
11 changes: 6 additions & 5 deletions sunpy/coordinates/tests/test_frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,13 @@ def test_hpc_default_observer():


def test_hpc_low_precision_float_warning():
hpc = Helioprojective(u.Quantity(0, u.deg, dtype=np.float32),
u.Quantity(0, u.arcsec, dtype=np.float16),
observer=HeliographicStonyhurst(0*u.deg, 0*u.deg, 1*u.AU))
with np.errstate(over='ignore'):
hpc = Helioprojective(u.Quantity(0, u.deg, dtype=np.float32),
u.Quantity(0, u.arcsec, dtype=np.float16),
observer=HeliographicStonyhurst(0*u.deg, 0*u.deg, 1*u.AU))

with pytest.warns(SunpyUserWarning, match="Tx is float32, and Ty is float16"):
hpc.make_3d()
with pytest.warns(SunpyUserWarning, match="Tx is float32, and Ty is float16"):
hpc.make_3d()


def test_hpc_obstime_from_observer():
Expand Down
5 changes: 3 additions & 2 deletions sunpy/image/tests/test_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from astropy.coordinates.matrix_utilities import rotation_matrix

from sunpy.image.transform import _rotation_registry, affine_transform
from sunpy.tests.helpers import figure_test
from sunpy.tests.helpers import figure_test, skip_numpy2
from sunpy.util import SunpyUserWarning

# Tolerance for tests
Expand Down Expand Up @@ -332,13 +332,14 @@ def test_nans(rot30):
@pytest.mark.filterwarnings("ignore:.*bug in the implementation of scikit-image")
@pytest.mark.parametrize('method', _rotation_registry.keys())
@pytest.mark.parametrize('order', range(6))
@skip_numpy2
def test_endian(method, order, rot30):
if order not in _rotation_registry[method].allowed_orders:
return

# Test that the rotation output values do not change with input byte order
native = np.ones((10, 10))
swapped = native.byteswap().newbyteorder()
swapped = native.view(native.dtype.newbyteorder()).byteswap()

rot_native = affine_transform(native, rot30, order=order, method=method, missing=0)
rot_swapped = affine_transform(swapped, rot30, order=order, method=method, missing=0)
Expand Down
4 changes: 2 additions & 2 deletions sunpy/image/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ def _rotation_skimage(image, matrix, shift, order, missing, clip):

# Swap the byte order if it is non-native (e.g., big-endian on a little-endian system)
if adjusted_image.dtype.byteorder == ('>' if sys.byteorder == 'little' else '<'):
adjusted_image = adjusted_image.byteswap().newbyteorder()
adjusted_image = adjusted_image.view(adjusted_image.dtype.newbyteorder()).byteswap()

# Be aware that even though mode is set to 'constant', when skimage 0.19 calls scipy,
# it specifies the scipy mode to be 'grid-constant' rather than 'constant'
Expand Down Expand Up @@ -404,7 +404,7 @@ def _rotation_cv2(image, matrix, shift, order, missing, clip):

# Swap the byte order if it is non-native (e.g., big-endian on a little-endian system)
if adjusted_image.dtype.byteorder == ('>' if sys.byteorder == 'little' else '<'):
adjusted_image = adjusted_image.byteswap().newbyteorder()
adjusted_image = adjusted_image.view(adjusted_image.dtype.newbyteorder()).byteswap()

# missing must be a Python float, not a NumPy float
missing = float(missing)
Expand Down
4 changes: 2 additions & 2 deletions sunpy/map/mapbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
TIME_FORMAT = config.get("general", "time_format")
PixelPair = namedtuple('PixelPair', 'x y')
SpatialPair = namedtuple('SpatialPair', 'axis1 axis2')

_NUMPY_COPY_IF_NEEDED = False if np.__version__.startswith("1.") else None
_META_FIX_URL = 'https://docs.sunpy.org/en/stable/how_to/fix_map_metadata.html'

# Manually specify the ``.meta`` docstring. This is assigned to the .meta
Expand Down Expand Up @@ -529,7 +529,7 @@ def _get_lon_lat(self, frame):
@property
def quantity(self):
"""Unitful representation of the map data."""
return u.Quantity(self.data, self.unit, copy=False)
return u.Quantity(self.data, self.unit, copy=_NUMPY_COPY_IF_NEEDED)

def _new_instance_from_op(self, new_data):
"""
Expand Down
4 changes: 2 additions & 2 deletions sunpy/map/sources/tests/test_gong_halpha_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ def test_nickname(gong_halpha):


def test_earth_location(gong_halpha):
assert_equal(gong_halpha._earth_location.lat, 34.26032998167749*u.deg)
assert_equal(gong_halpha._earth_location.lon, -116.92141999386104*u.deg)
assert u.isclose(gong_halpha._earth_location.lat, 34.26032998167749*u.deg, rtol=1e-15)
assert u.isclose(gong_halpha._earth_location.lon, -116.92141999386104*u.deg, rtol=1e-15)


@pytest.mark.filterwarnings("ignore:Tried to get polar motions for times after IERS data is valid.")
Expand Down
35 changes: 25 additions & 10 deletions sunpy/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,51 @@
# NOTE: Do not import sunpy subpackages which have optional dependencies here,
# this module should be importable with no extra dependencies installed.

__all__ = ['skip_windows', 'skip_glymur', 'skip_ana', 'warnings_as_errors', 'asdf_entry_points']
__all__ = ['skip_windows', 'skip_glymur', 'skip_ana', 'skip_cdf', 'skip_opencv', 'skip_numpy2',
'warnings_as_errors', 'asdf_entry_points']

# SunPy's JPEG2000 capabilities rely on the glymur library.
# First we check to make sure that glymur imports correctly before proceeding.
try:
import glymur
except ImportError:
SKIP_GLYMUR = True
else:
# See if we have a C backend
if glymur.lib.openjp2.OPENJP2:
SKIP_GLYMUR = False
else:
SKIP_GLYMUR = True
# See if we have a C backend installed.
# Glymur will not be able to read JPEG2000 files without it.
SKIP_GLYMUR = not glymur.lib.openjp2.OPENJP2

try:
from sunpy.io import _pyana # NOQA
SKIP_ANA = False
except ImportError:
SKIP_ANA = True
else:
SKIP_ANA = False

if sys.maxsize > 2**32:
SKIP_32 = False
else:
SKIP_32 = True

try:
import cv2 # NOQA
SKIP_OPENCV = False
except ImportError:
SKIP_OPENCV = True

try:
import cdflib # NOQA
SKIP_CDF = False
except ImportError:
SKIP_CDF = True

import numpy

SKIP_NUMPY2 = numpy.__version__.startswith('2.')

skip_windows = pytest.mark.skipif(platform.system() == "Windows", reason="Windows.")
skip_glymur = pytest.mark.skipif(SKIP_GLYMUR, reason="Glymur can not be imported.")
skip_ana = pytest.mark.skipif(SKIP_ANA, reason="ANA is not available.")
skip_cdf = pytest.mark.skipif(SKIP_CDF, reason="CDFlib is not available.")
skip_opencv = pytest.mark.skipif(SKIP_OPENCV, reason="opencv is not available.")
skip_numpy2 = pytest.mark.skipif(SKIP_NUMPY2, reason="numpy2 breaks this test do to dependency issues.")
asdf_entry_points = pytest.mark.skipif(
not entry_points().select(group="asdf.resource_mappings", name="sunpy"),
reason="No SunPy ASDF entry points.",
Expand Down
8 changes: 0 additions & 8 deletions sunpy/time/tests/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,6 @@ def test_parse_time_individual_numpy_datetime():
assert dt == Time('2005-02-01', format='isot')


def test_parse_time_numpy_datetime_timezone():
with pytest.warns(DeprecationWarning, match='parsing timezone aware datetimes is deprecated'):
dt64 = np.datetime64('2014-02-07T16:47:51-0500')
dt = parse_time(dt64)

assert dt == Time('2014-02-07T21:47:51', format='isot')


def test_parse_time_numpy_datetime_ns():
dt64 = np.datetime64('2014-02-07T16:47:51.008288000')
dt = parse_time(dt64)
Expand Down
4 changes: 2 additions & 2 deletions sunpy/time/timeformats.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ class TimeTaiSeconds(TimeFromEpoch):
--------
>>> from astropy.time import Time
>>> t = Time('1958-01-01T00:00:00', format='isot', scale='tai')
>>> t.tai_seconds
>>> t.tai_seconds # doctest: +SKIP
0.0
>>> t2 = Time('2015-10-25T05:24:08', format='isot', scale='tai')
>>> t2.tai_seconds
>>> t2.tai_seconds # doctest: +SKIP
1824441848.0
>>> t3 = Time(t2.tai_seconds, format='tai_seconds') # scale is automatically TAI
>>> t3.isot
Expand Down
8 changes: 4 additions & 4 deletions sunpy/timeseries/sources/goes.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,13 @@ def _parse_hdus(cls, hdulist):
times = start_time + TimeDelta(seconds_from_start*u.second)
times.precision = 9

# remove bad values as defined in header comments
# Remove bad values as defined in header comments
xrsb[xrsb == -99999] = np.nan
xrsa[xrsa == -99999] = np.nan

# fix byte ordering
newxrsa = xrsa.byteswap().newbyteorder()
newxrsb = xrsb.byteswap().newbyteorder()
# Fix byte ordering
newxrsa = xrsa.view(xrsa.dtype.newbyteorder()).byteswap()
newxrsb = xrsb.view(xrsb.dtype.newbyteorder()).byteswap()

data = DataFrame({'xrsa': newxrsa, 'xrsb': newxrsb},
index=times.isot.astype('datetime64'))
Expand Down
7 changes: 1 addition & 6 deletions sunpy/timeseries/sources/lyra.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""
This module provides Proba-2 `~sunpy.timeseries.TimeSeries` source.
"""
import sys
from collections import OrderedDict

import pandas
Expand Down Expand Up @@ -168,11 +167,7 @@ def _parse_hdus(cls, hdulist):
table = {}

for i, col in enumerate(fits_record.columns[1:-1]):
# temporary patch for big-endian data bug on pandas 0.13
if fits_record.field(i+1).dtype.byteorder == '>' and sys.byteorder == 'little':
table[col.name] = fits_record.field(i + 1).byteswap().newbyteorder()
else:
table[col.name] = fits_record.field(i + 1)
table[col.name] = fits_record.field(i + 1)

# Return the header and the data
times.precision = 9
Expand Down
3 changes: 2 additions & 1 deletion sunpy/timeseries/tests/test_timeseries_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import sunpy.timeseries
from sunpy.data.test import get_test_data_filenames, get_test_filepath, rootdir
from sunpy.net import Fido
from sunpy.tests.helpers import skip_numpy2
from sunpy.time import parse_time
from sunpy.util import SunpyUserWarning
from sunpy.util.datatype_factory_base import NoMatchError
Expand Down Expand Up @@ -97,7 +98,7 @@ def test_from_url():
assert isinstance(ts[0], sunpy.timeseries.GenericTimeSeries)
assert isinstance(ts[1], sunpy.timeseries.GenericTimeSeries)


@skip_numpy2
def test_read_cdf():
ts_psp = sunpy.timeseries.TimeSeries(psp_filepath)
assert len(ts_psp) == 2
Expand Down
5 changes: 4 additions & 1 deletion sunpy/util/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
from inspect import Parameter, signature
from functools import wraps

import numpy as np

import astropy.units as u
from astropy.nddata import NDData

from sunpy.util.exceptions import SunpyDeprecationWarning, SunpyPendingDeprecationWarning, warn_deprecated

__all__ = ['deprecated']

_NUMPY_COPY_IF_NEEDED = False if np.__version__.startswith("1.") else None

def get_removal_version(since):
# Work out which version this will be removed in
Expand Down Expand Up @@ -401,7 +404,7 @@ def inner(instance, value):
# a proxy for these possible inputs. If it can be cast to a unitful quantity, we can
# do arithmetic with it. Broadcasting or unit mismatches are handled later in the
# actual operations by numpy and astropy respectively.
_ = u.Quantity(value, copy=False)
_ = u.Quantity(value, copy=_NUMPY_COPY_IF_NEEDED)
except TypeError:
return NotImplemented
return func(instance, value)
Expand Down

0 comments on commit c6e2b30

Please sign in to comment.