From ceeeea73859f184017227148cc4cbf3b8fa16e4e Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 28 Oct 2024 09:21:00 -0400 Subject: [PATCH 001/145] Backport PR #17249: DOC: amend gh-17246 changelog to better emphasize user visible changes --- docs/changes/units/17246.api.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changes/units/17246.api.rst b/docs/changes/units/17246.api.rst index 40288788a808..ef69bb2da20e 100644 --- a/docs/changes/units/17246.api.rst +++ b/docs/changes/units/17246.api.rst @@ -1,4 +1,4 @@ The ``eV`` and ``rydberg`` units were moved to ``astropy.units.misc`` (from -``astropy.units.si`` and ``astropy.units.astrophys``, respectively). They remain -available under the root namespace ``astropy.units`` as ``astropy.units.eV`` -and ``astropy.units.rydberg``. +``astropy.units.si`` and ``astropy.units.astrophys``, respectively). +Practically, this means that ``Unit.to_system(u.si)`` no longer includes +``eV`` as a SI-compatible unit. From 77b159661cb82b8154a7abb27032ea5e81796bae Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:22:44 -0400 Subject: [PATCH 002/145] Backport PR #16615: Add an announcement banner --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 71d248a3e0bd..282092e91d60 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -246,6 +246,7 @@ }, # https://github.com/pydata/pydata-sphinx-theme/issues/1492 "navigation_with_keys": False, + "announcement": "https://www.astropy.org/annoucement_banner.html", } ) From 2c880ef4f210ae284622ae838ba7eb6cd340f591 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:26:40 -0400 Subject: [PATCH 003/145] Backport PR #17268: Clearer error when show_in_notebook is missing optional dependencies --- astropy/table/table.py | 10 +++++++++- pyproject.toml | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/astropy/table/table.py b/astropy/table/table.py index 0a29c28400af..adf76f6f9ba3 100644 --- a/astropy/table/table.py +++ b/astropy/table/table.py @@ -1795,7 +1795,15 @@ def show_in_notebook(self, *, backend="ipydatagrid", **kwargs): """ if backend == "ipydatagrid": - from astropy.table.notebook_backends import ipydatagrid + try: + import pandas # noqa: F401 + + from astropy.table.notebook_backends import ipydatagrid + except ImportError: + raise ImportError( + "The default option for show_in_notebook now requires pandas " + "and ipydatagrid to also be installed, or please consider using the astropy[jupyter] extras" + ) from None func = ipydatagrid diff --git a/pyproject.toml b/pyproject.toml index 27d9dd85cd9a..15b285386221 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ jupyter = [ # these are optional dependencies for utils.console and table "ipywidgets", "ipykernel", "ipydatagrid", + "pandas", ] # This is ALL the run-time optional dependencies. all = [ From 5fac0ad98808178de2036b4d860ef0e700326ab5 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 29 Oct 2024 10:55:59 +0000 Subject: [PATCH 004/145] Added what's new entry for modeling speedups --- docs/whatsnew/7.0.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/whatsnew/7.0.rst b/docs/whatsnew/7.0.rst index d13d0e33efd4..816b8fc3b194 100644 --- a/docs/whatsnew/7.0.rst +++ b/docs/whatsnew/7.0.rst @@ -23,6 +23,7 @@ In particular, this release includes: * :ref:`whatsnew_7_0_contributor_doc_improvement` * :ref:`whatsnew_7_0_typing_stats` * :ref:`whatsnew_7_0_unit_conversion_array_like` +* :ref:`whatsnew_7_0_modeling_speedup` * :ref:`whatsnew_7_0_parallel_fitting` * :ref:`whatsnew_7_0_rgb_image_visualization_enhancement` * :ref:`whatsnew_7_0_lorentz2d_model` @@ -273,6 +274,15 @@ value in ``Unit.to`` and have those arrays not be converted to Numpy arrays: Note that it is not yet possible to use ``Quantity`` with dask arrays directly. +.. _whatsnew_7_0_modeling_speedup: + +Performance improvements in astropy.modeling +============================================ + +There have been significant improvements to the performance of fitting in +:mod:`astropy.modeling`, with typical speedups of 2x for simple models and 4x or +more for compound models. + .. _whatsnew_7_0_parallel_fitting: Fitting models in parallel with N-dimensional data From ea947c5159a9f3a552ecb73c072760140486ff68 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 30 Oct 2024 12:27:19 +0000 Subject: [PATCH 005/145] Clarify wording --- docs/whatsnew/7.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/whatsnew/7.0.rst b/docs/whatsnew/7.0.rst index 816b8fc3b194..7a857844bcf3 100644 --- a/docs/whatsnew/7.0.rst +++ b/docs/whatsnew/7.0.rst @@ -279,7 +279,7 @@ Note that it is not yet possible to use ``Quantity`` with dask arrays directly. Performance improvements in astropy.modeling ============================================ -There have been significant improvements to the performance of fitting in +There have been significant improvements to the performance of non-linear fitters in :mod:`astropy.modeling`, with typical speedups of 2x for simple models and 4x or more for compound models. From 5f44973755e7f1c24599cd56d96ded999461e938 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 31 Oct 2024 10:59:34 +0000 Subject: [PATCH 006/145] Backport PR #17281: Add support for kwarg only and positional only arguments in support_nddata --- astropy/nddata/decorators.py | 73 +++++----- astropy/nddata/tests/test_decorators.py | 168 +++++++++++++++++++----- docs/changes/nddata/17281.bugfix.rst | 1 + 3 files changed, 169 insertions(+), 73 deletions(-) create mode 100644 docs/changes/nddata/17281.bugfix.rst diff --git a/astropy/nddata/decorators.py b/astropy/nddata/decorators.py index a8a20f13e6a6..91d84d220877 100644 --- a/astropy/nddata/decorators.py +++ b/astropy/nddata/decorators.py @@ -145,41 +145,22 @@ def downsample(data, wcs=None): all_returns = returns + keeps def support_nddata_decorator(func): - # Find out args and kwargs - func_args, func_kwargs = [], [] - sig = signature(func).parameters - for param_name, param in sig.items(): + func_sig = signature(func) + func_parameters = func_sig.parameters + for param in func_parameters.values(): if param.kind in (param.VAR_POSITIONAL, param.VAR_KEYWORD): raise ValueError("func may not have *args or **kwargs.") - try: - if param.default == param.empty: - func_args.append(param_name) - else: - func_kwargs.append(param_name) - # The comparison to param.empty may fail if the default is a - # numpy array or something similar. So if the comparison fails then - # it's quite obvious that there was a default and it should be - # appended to the "func_kwargs". - except ValueError as exc: - if ( - "The truth value of an array with more than one element " - "is ambiguous." in str(exc) - ): - func_kwargs.append(param_name) - else: - raise - - # First argument should be data - if not func_args or func_args[0] != attr_arg_map.get("data", "data"): + + data_arg_name = attr_arg_map.get("data", "data") + if data_arg_name not in func_parameters.keys(): raise ValueError( - "Can only wrap functions whose first positional " - "argument is `{}`" - "".format(attr_arg_map.get("data", "data")) + f"Can only wrap a function with a {data_arg_name} argument." ) @wraps(func) - def wrapper(data, *args, **kwargs): - bound_args = signature(func).bind(data, *args, **kwargs) + def wrapper(*args, **kwargs): + bound_args = func_sig.bind_partial(*args, **kwargs) + data = bound_args.arguments[data_arg_name] unpack = isinstance(data, accepts) input_data = data ignored = [] @@ -187,7 +168,6 @@ def wrapper(data, *args, **kwargs): raise TypeError( f"Only NDData sub-classes that inherit from {accepts.__name__}" " can be used by this function" - "" ) # If data is an NDData instance, we can try and find properties @@ -208,7 +188,7 @@ def wrapper(data, *args, **kwargs): continue # Warn if the property is set but not used by the function. propmatch = attr_arg_map.get(prop, prop) - if propmatch not in func_kwargs: + if propmatch not in func_parameters: ignored.append(prop) continue @@ -228,13 +208,23 @@ def wrapper(data, *args, **kwargs): # indistinguishable from an explicitly passed kwarg # and it won't notice that and use the attribute of the # NDData. - if propmatch in func_args or ( - propmatch in func_kwargs - and ( + warn_conflict = False + try: + if ( bound_args.arguments[propmatch] - is not sig[propmatch].default - ) - ): + is not func_parameters[propmatch].default + ): + warn_conflict = True + except ValueError as exc: + if ( + "The truth value of an array with more than one element " + "is ambiguous." in str(exc) + ): + warn_conflict = False + else: + raise + + if warn_conflict: warnings.warn( "Property {} has been passed explicitly and " "as an NDData property{}, using explicitly " @@ -246,9 +236,9 @@ def wrapper(data, *args, **kwargs): ) continue # Otherwise use the property as input for the function. - kwargs[propmatch] = value + bound_args.arguments[propmatch] = value # Finally, replace data by the data attribute - data = data.data + bound_args.arguments[data_arg_name] = data.data if ignored: warnings.warn( @@ -258,7 +248,10 @@ def wrapper(data, *args, **kwargs): AstropyUserWarning, ) - result = func(data, *args, **kwargs) + # Need to apply defaults so that any positional only arguments are + # passed as positional properly + bound_args.apply_defaults() + result = func(*bound_args.args, **bound_args.kwargs) if unpack and repack: # If there are multiple required returned arguments make sure diff --git a/astropy/nddata/tests/test_decorators.py b/astropy/nddata/tests/test_decorators.py index 6ff52844833a..d104930b41b2 100644 --- a/astropy/nddata/tests/test_decorators.py +++ b/astropy/nddata/tests/test_decorators.py @@ -58,6 +58,129 @@ def test_pass_nddata(): assert unit_out is unit_in +@pytest.mark.parametrize( + "func", + ( + lambda *, data, wcs, unit=None: (data, wcs, unit), + lambda *, wcs=None, data, unit=None: (data, wcs, unit), + ), +) +def test_pass_nddata_kwarg_only(func): + wrapped_function = support_nddata(func) + + data_in = np.array([1, 2, 3]) + wcs_in = WCS(naxis=1) + unit_in = u.Jy + + nddata_in = NDData(data_in, wcs=wcs_in, unit=unit_in) + + data_out, wcs_out, unit_out = wrapped_function(data=nddata_in) + + assert data_out is data_in + assert wcs_out is wcs_in + assert unit_out is unit_in + + +@pytest.mark.parametrize( + "func, call_type", + ( + pytest.param( + lambda data, wcs=None, mask=None, /, *, unit=None: (data, wcs, unit, mask), + "data_as_pos", + id="data_wcs_mask_pos-only", + ), + pytest.param( + lambda data, wcs=None, /, mask=None, *, unit: (data, wcs, unit, mask), + "data_as_pos", + id="data_wcs_pos-only", + ), + pytest.param( + lambda wcs=None, /, data=None, mask=None, *, unit: (data, wcs, unit, mask), + "data_as_kw", + id="data_pos-or-kw", + ), + pytest.param( + lambda wcs=None, /, mask=None, *, data, unit: (data, wcs, unit, mask), + "data_as_kw", + id="data_kw-only", + ), + ), +) +def test_pass_nddata_constrained_signature(func, call_type): + wrapped_function = support_nddata(func) + + data_in = np.array([1, 2, 3]) + wcs_in = WCS(naxis=1) + unit_in = u.Jy + mask_in = np.array([True, False, False]) + + nddata_in = NDData(data_in, wcs=wcs_in, unit=unit_in, mask=mask_in) + + if call_type == "data_as_pos": + args = (nddata_in,) + kwargs = {} + elif call_type == "data_as_kw": + args = () + kwargs = {"data": nddata_in} + + data_out, wcs_out, unit_out, mask_out = wrapped_function(*args, **kwargs) + + assert data_out is data_in + assert wcs_out is wcs_in + assert unit_out is unit_in + assert mask_out is mask_in + + nddata2 = NDData(data_in, unit=unit_in, mask=mask_in) + + if call_type == "data_as_pos": + args = (nddata2,) + kwargs = {} + elif call_type == "data_as_kw": + args = () + kwargs = {"data": nddata2} + + data_out, wcs_out, unit_out, mask_out = wrapped_function(*args, **kwargs) + + assert data_out is data_in + assert wcs_out is None + assert unit_out is unit_in + assert mask_out is mask_in + + if call_type == "data_as_pos": + args = (nddata2, wcs_in) + kwargs = {} + elif call_type == "data_as_kw": + args = (wcs_in,) + kwargs = {"data": nddata2} + + data_out, wcs_out, unit_out, mask_out = wrapped_function(*args, **kwargs) + + assert data_out is data_in + assert wcs_out is wcs_in + assert unit_out is unit_in + assert mask_out is mask_in + + if call_type == "data_as_pos": + args = (nddata_in, wcs_in) + kwargs = {} + elif call_type == "data_as_kw": + args = (wcs_in,) + kwargs = {"data": nddata_in} + + with pytest.warns( + AstropyUserWarning, + match=( + "Property wcs has been passed explicitly and as " + "an NDData property, using explicitly specified value" + ), + ): + data_out, wcs_out, unit_out, mask_out = wrapped_function(*args, **kwargs) + assert data_out is data_in + assert wcs_out is wcs_in + assert unit_out is unit_in + assert mask_out is mask_in + + def test_pass_nddata_and_explicit(): data_in = np.array([1, 2, 3]) wcs_in = WCS(naxis=1) @@ -103,39 +226,18 @@ def test_pass_nddata_ignored(): assert unit_out is unit_in -def test_incorrect_first_argument(): - with pytest.raises(ValueError) as exc: - - @support_nddata - def wrapped_function_2(something, wcs=None, unit=None): - pass - - assert ( - exc.value.args[0] - == "Can only wrap functions whose first positional argument is `data`" - ) - - with pytest.raises(ValueError) as exc: - - @support_nddata - def wrapped_function_3(something, data, wcs=None, unit=None): - pass - - assert ( - exc.value.args[0] - == "Can only wrap functions whose first positional argument is `data`" - ) - - with pytest.raises(ValueError) as exc: - - @support_nddata - def wrapped_function_4(wcs=None, unit=None): - pass - - assert ( - exc.value.args[0] - == "Can only wrap functions whose first positional argument is `data`" - ) +@pytest.mark.parametrize( + "func", + ( + lambda something, wcs=None, unit=None: None, + lambda wcs=None, unit=None: None, + ), +) +def test_incorrect_first_argument(func): + with pytest.raises( + ValueError, match="Can only wrap a function with a data argument" + ): + support_nddata(func) def test_wrap_function_no_kwargs(): diff --git a/docs/changes/nddata/17281.bugfix.rst b/docs/changes/nddata/17281.bugfix.rst new file mode 100644 index 000000000000..a226c98a861d --- /dev/null +++ b/docs/changes/nddata/17281.bugfix.rst @@ -0,0 +1 @@ +Add support for positional only and keyword only arguments when using the ``support_nddata`` decorator. From b9d0dc097ad24d7261781c0b4416918581a3de92 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:55:46 -0400 Subject: [PATCH 007/145] Backport PR #17293: Fix new model example --- docs/modeling/new-model.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/modeling/new-model.rst b/docs/modeling/new-model.rst index 232c5c84dd2e..a262f277477e 100644 --- a/docs/modeling/new-model.rst +++ b/docs/modeling/new-model.rst @@ -31,17 +31,17 @@ of two Gaussians: # Define model @custom_model - def sum_of_gaussians(x, amplitude1=1., mean1=-1., sigma1=1., - amplitude2=1., mean2=1., sigma2=1.): + def sum_of_gaussians(x, amplitude1=1.0, mean1=-1.0, sigma1=1.0, + amplitude2=1.0, mean2=1.5, sigma2=1.0): return (amplitude1 * np.exp(-0.5 * ((x - mean1) / sigma1)**2) + amplitude2 * np.exp(-0.5 * ((x - mean2) / sigma2)**2)) - # Generate fake data + # Generate fake data with some noise rng = np.random.default_rng(0) x = np.linspace(-5., 5., 200) m_ref = sum_of_gaussians(amplitude1=2., mean1=-0.5, sigma1=0.4, amplitude2=0.5, mean2=2., sigma2=1.0) - y = m_ref(x) + rng.normal(0., 0.1, x.shape) + y = m_ref(x) + rng.normal(0., 0.05, x.shape) # Fit model to data m_init = sum_of_gaussians() From fab9da394e19d3a1969b9a0bfe6d8867f650d9cb Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 31 Oct 2024 21:45:50 +0000 Subject: [PATCH 008/145] Backport PR #17273: Add `support_nddata` to `parallel_fit_dask` --- astropy/modeling/_fitting_parallel.py | 20 ++++- .../modeling/tests/test_fitting_parallel.py | 73 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/astropy/modeling/_fitting_parallel.py b/astropy/modeling/_fitting_parallel.py index 0f38fc25cfb0..be68e3af9fac 100644 --- a/astropy/modeling/_fitting_parallel.py +++ b/astropy/modeling/_fitting_parallel.py @@ -8,6 +8,7 @@ import astropy.units as u from astropy.modeling.utils import _combine_equivalency_dict +from astropy.nddata import NDUncertainty, StdDevUncertainty, support_nddata from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS from astropy.wcs.wcsapi.wrappers import SlicedLowLevelWCS @@ -252,6 +253,7 @@ def __getitem__(self, item): return values[item] +@support_nddata(wcs="world", uncertainty="weights", unit="data_unit") def parallel_fit_dask( *, model, @@ -259,6 +261,7 @@ def parallel_fit_dask( data, data_unit=None, weights=None, + mask=None, fitting_axes=None, world=None, chunk_n_max=None, @@ -292,9 +295,13 @@ def parallel_fit_dask( data_units : `astropy.units.Unit` Units for the data array, for when the data array is not a ``Quantity`` instance. - weights : `numpy.ndarray` or `dask.array.core.Array` + weights : `numpy.ndarray`, `dask.array.core.Array` or `astropy.nddata.NDUncertainty` The weights to use in the fitting. See the documentation for specific fitters for more information about the meaning of weights. + If passed as a `.NDUncertainty` object it will be converted to a + `.StdDevUncertainty` and then passed to the fitter as 1 over that. + mask : `numpy.ndarray` + A boolean mask to be applied to the data. fitting_axes : int or tuple The axes to keep for the fitting (other axes will be sliced/iterated over) world : `None` or tuple or APE-14-WCS @@ -381,6 +388,17 @@ def parallel_fit_dask( "Can only set preserve_native_chunks=True if input weights is a dask array (if specified)" ) + if isinstance(weights, NDUncertainty): + weights = weights.represent_as(StdDevUncertainty) + weights = 1 / weights.array + + if mask is not None: + imask = np.logical_not(mask).astype(float) + if weights is None: + weights = imask + else: + weights *= imask + # Sanitize fitting_axes and determine iterating_axes ndim = data.ndim fitting_axes = tuple([(fi if fi >= 0 else ndim - fi) for fi in fitting_axes]) diff --git a/astropy/modeling/tests/test_fitting_parallel.py b/astropy/modeling/tests/test_fitting_parallel.py index 0440ac6fb7c3..3835a76dd862 100644 --- a/astropy/modeling/tests/test_fitting_parallel.py +++ b/astropy/modeling/tests/test_fitting_parallel.py @@ -22,6 +22,7 @@ Linear1D, Planar2D, ) +from astropy.nddata import NDData, StdDevUncertainty # noqa: E402 from astropy.tests.helper import assert_quantity_allclose # noqa: E402 from astropy.utils.compat.optional_deps import HAS_PLT # noqa: E402 from astropy.wcs import WCS # noqa: E402 @@ -1036,3 +1037,75 @@ def test_world_wcs_axis_correlation(): model_fit = parallel_fit_dask(fitting_axes=1, world=wcs1, **common_kwargs) assert_allclose(model_fit.mean, [6, 4]) + + +def test_support_nddata(): + data = gaussian(np.arange(20), 2, 10, 1).reshape((20, 1)) * u.Jy + + # Introduce outliers + data[10, 0] = 1000.0 * u.Jy + # Mask the outliers (invalid is True) + mask = data > 100.0 * u.Jy + + model = Gaussian1D(amplitude=1.5 * u.Jy, mean=7 * u.um, stddev=0.002 * u.mm) + fitter = LevMarLSQFitter() + + wcs = WCS(naxis=2) + wcs.wcs.ctype = "OFFSET", "WAVE" + wcs.wcs.crval = 10, 0.1 + wcs.wcs.crpix = 1, 1 + wcs.wcs.cdelt = 10, 0.1 + wcs.wcs.cunit = "deg", "um" + + nd_data = NDData( + data=data, + wcs=wcs, + mask=mask, + ) + + model_fit = parallel_fit_dask( + data=nd_data, + model=model, + fitter=fitter, + fitting_axes=0, + scheduler="synchronous", + ) + + assert_allclose(model_fit.amplitude.quantity, 2 * u.Jy) + assert_allclose(model_fit.mean.quantity, 1.1 * u.um) + assert_allclose(model_fit.stddev.quantity, 0.1 * u.um) + + +def test_support_nddata_uncert(): + data = np.repeat(np.array([1, 2, 4]), 2).reshape((2, -1), order="F").T + uncert = ( + np.repeat(np.array([1 / 7**0.5, 1 / 2**0.5, 1]), 2) + .reshape((2, -1), order="F") + .T + ) + + model = Const1D(0) + fitter = LevMarLSQFitter() + + wcs = WCS(naxis=2) + wcs.wcs.ctype = "OFFSET", "WAVE" + wcs.wcs.crval = 10, 1 + wcs.wcs.crpix = 1, 1 + wcs.wcs.cdelt = 10, 1 + wcs.wcs.cunit = "deg", "m" + + nd_data = NDData( + data=data, + wcs=wcs, + uncertainty=StdDevUncertainty(uncert), + ) + + model_fit = parallel_fit_dask( + data=nd_data, + model=model, + fitter=fitter, + fitting_axes=0, + scheduler="synchronous", + ) + + assert_allclose(model_fit.amplitude, 1.5) From 11019f7b2ba3fa630fc8c89713c2499c5ae1573a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Sat, 2 Nov 2024 07:02:37 +0100 Subject: [PATCH 009/145] Backport PR #17305: TST: Pin hypothesis to avoid double tests job failure --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 15b285386221..b1d849462d0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,6 +96,7 @@ test = [ "coverage>=6.4.4", "pre-commit>=2.9.3", # lower bound is not tested beyond installation "pytest>=7.3.0", + "hypothesis!=6.116.0", # https://github.com/astropy/astropy/issues/17299 "pytest-doctestplus>=0.12", "pytest-astropy-header>=0.2.1", "pytest-astropy>=0.10.0", From 2faed350b75343c56fed12d8826641c8ca47758f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Sat, 2 Nov 2024 08:20:10 +0100 Subject: [PATCH 010/145] Backport PR #17307: Update minimum required version of astropy-iers-data --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 15b285386221..fecb7747d9a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ keywords = [ dependencies = [ "numpy>=1.23.2", "pyerfa>=2.0.1.1", - "astropy-iers-data>=0.2024.9.30.0.32.59", + "astropy-iers-data>=0.2024.10.28.0.34.7", "PyYAML>=6.0.0", "packaging>=22.0.0", ] From b3c971b6896b6c9e54c0728f95301e32a989a7e9 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Mon, 4 Nov 2024 18:08:26 +0100 Subject: [PATCH 011/145] Added contributor statistics and names --- docs/whatsnew/7.0.rst | 66 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/docs/whatsnew/7.0.rst b/docs/whatsnew/7.0.rst index 7a857844bcf3..6ffccc896782 100644 --- a/docs/whatsnew/7.0.rst +++ b/docs/whatsnew/7.0.rst @@ -38,9 +38,11 @@ In addition to these major changes, Astropy v7.0 includes a large number of smaller improvements and bug fixes, which are described in the :ref:`changelog`. By the numbers: -* X issues have been closed since v6.1 -* X pull requests have been merged since v6.1 -* X distinct people have contributed code +* 1440 commits have been added since 6.1 +* 213 issues have been closed since 6.1 +* 466 pull requests have been merged since 6.1 +* 52 people have contributed since 6.1 +* 22 of which are new contributors .. _whatsnew-7.0-table-masked-quantity: @@ -559,3 +561,61 @@ Full change log To see a detailed list of all changes in version v7.0, including changes in API, please see the :ref:`changelog`. + +Contributors to the 7.0 release +=============================== + +The people who have contributed to the code for this release are: + +.. hlist:: + :columns: 4 + + - Albert Y. Shih + - Albert Zhang * + - Alfio Puglisi * + - Andreas Faisst + - Brandie-M * + - Brett Morris + - Brigitta Sipőcz + - Clément Robert + - Deen-Dot * + - Derek Homeier + - Eero Vaher + - Hans Moritz Günther + - Henrike F * + - Hugo Buddelmeijer + - Jeff Jennings * + - Jost Migenda * + - Larry Bradley + - Leo Singer + - Marten van Kerkwijk + - Maximilian Linhoff + - Maximillian Weber * + - Melissa Weber Mendonça * + - Michael Kelley * + - Mihai Cara + - Mridul Seth + - Nabil Freij + - Nathaniel Starkman + - P. L. Lim + - Parkerwise * + - Peter Teuben + - Sam Holt + - Sam Van Kooten + - Sedona Price * + - Shane Maloney * + - Simon Alinder * + - Simon Conseil + - SteinMatthiasPTB * + - Stuart Mumford + - Tanvi Pooranmal Meena + - Thomas Robitaille + - Thomas Vandal * + - Tom Aldcroft + - Tom Donaldson + - Zach Burnett * + - Zhen-Kai Gao * + - rachel guo * + - sharath * + +Where a * indicates that this release contains their first contribution to astropy. From c7c05f8c8c66ba036e1d36f3cd5cfc0c4f03b201 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:32:28 -0500 Subject: [PATCH 012/145] Backport PR #17321: Updated list of contributors and .mailmap file --- .mailmap | 18 ++++++++++++++++-- docs/credits.rst | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index d0bc09cea7db..f925832935d8 100644 --- a/.mailmap +++ b/.mailmap @@ -93,6 +93,8 @@ David Grant <33813984+DavoGrant@ David Kirkby David Pérez-Suárez David Shupe +Deen-Dot Deen-dot +Deen-Dot Deen-dot <80238871+Deen-dot@users.noreply.github.com> Demitri Muna Demitri Muna Demitri Muna @@ -148,6 +150,7 @@ Jaime Andrés Jake VanderPlas Jake VanderPlas Jake VanderPlas +James Davies James McCormac James Tocknell James Tocknell @@ -164,9 +167,12 @@ Javier Duran Javier Duran Javier Pascual Granado +Jeff Jennings Jeff Taylor Jennifer Karr Jero Bado +Jero Bado <10357742+jerobado@users.noreply.github.com> +Jo Bovy Joe Hunkeler Johannes Zeman John Parejko @@ -222,6 +228,7 @@ Magnus Persson Maneesh Yadav Mangala Gowri Krishnamoorthy Manon Marchand +Marcello Nascif <118627858+marcellonascif@users.noreply.github.com> Marten van Kerkwijk Marten van Kerkwijk Marten H. van Kerkwijk Marten van Kerkwijk Marten Henric van Kerkwijk @@ -245,6 +252,7 @@ Mihai Cara Mikhail Minin Moataz Hisham +Mridul Seth Mubin Manasia Mubin17 <48038715+Mubin17@users.noreply.github.com> Nabil Freij Nadia Dencheva @@ -267,6 +275,7 @@ Pauline Barmby Perry Greenfield P. L. Lim <2090236+pllim@users.noreply.github.com> P. L. Lim <2090236+pllim@users.noreply.github.com> +Porter Averett <46609497+paverett@users.noreply.github.com> Prajwel Joseph Pratik Patel Pritish Chakraborty @@ -285,19 +294,23 @@ Rohit Patil Rohit Patil Roman Tolesnikov Ryan Cooke -Sam Lee +Sam Lee +Sam Van Kooten Sam Verstocken Sanjeev Dubey Sara Ogaz Sarah Graves +Sashank Mishra sashmish Sebastian Meßlinger <39328484+krachyon@users.noreply.github.com> Sebastian Meßlinger Sebastian Sergio Pascual +Shane Maloney +Shane Maloney Shantanu Srivastava Shilpi Jain Shivansh Mishra Shivansh Mishra -Simon Alinder +Simon Alinder <92031780+AlinderS@users.noreply.github.com> Simon Conseil Simon Conseil Simon Conseil @@ -315,6 +328,7 @@ Stuart Mumford Sudheesh Singanamalla Sudheesh Singanamalla Sushobhana Patra +Tanvi Pooranmal Meena <96572616+mtanvi19@users.noreply.github.com> <96572616+TanviPooranmal@users.noreply.github.com> Thomas Erben Thompson Le Blanc Thompson Le Blanc diff --git a/docs/credits.rst b/docs/credits.rst index 62f9ee22d640..99c9b93aa5b3 100644 --- a/docs/credits.rst +++ b/docs/credits.rst @@ -21,6 +21,7 @@ Core Package Contributors * Akshat1Nar * Al Niessner * Albert Y. Shih +* Albert Zhang * Aleh Khvalko * Alex Conley * Alex de la Vega @@ -30,6 +31,7 @@ Core Package Contributors * Alexander Bakanov * Alexandre Beelen * Alexandre R. Bomfim Junior +* Alfio Puglisi * Alpha-Ursae-Minoris * Amit Kumar * AMHermansen @@ -72,6 +74,7 @@ Core Package Contributors * Bill Cleveland * Bogdan Nicula * Bojan Nikolic +* Brandie-M * Brett Graham * Brett Morris * Brett Woltz @@ -122,6 +125,7 @@ Core Package Contributors * David Shiga * David Shupe * David Stansby +* Deen-Dot * Demitri Muna * Derek Homeier * Devin Crichton @@ -189,6 +193,7 @@ Core Package Contributors * Hans Moritz Günther * Harry Ferguson * Heinz-Alexander Fuetterer +* Henrike F * Henry Schreiner * Helen Sherwood-Taylor * Hélvio Peixoto @@ -220,6 +225,7 @@ Core Package Contributors * Javier Pascual Granado * JC Hsu * Jean Connelly +* Jeff Jennings * Jeff Taylor * Jeffrey McBeth * Jero Bado @@ -245,6 +251,7 @@ Core Package Contributors * Joseph Ryan * Joseph Schlitz * José Sabater Montes +* Jost Migenda * JP Maia * Juan Luis Cano Rodríguez * Juanjo Bazán @@ -330,12 +337,15 @@ Core Package Contributors * Max Silbiger * Max Voronkov * Maximilian Linhoff +* Maximillian Weber * Médéric Boquien * Megan Sosey +* Melissa Weber Mendonça * Michael Brewer * Michael Droettboom * Michael Hirsch * Michael Hoenig +* Michael Kelley * Michael Lindner-D'Addario * Michael Mueller * Michael Seifert @@ -450,11 +460,13 @@ Core Package Contributors * Scott Thomas * Sébastien Maret * Sebastian Meßlinger +* Sedona Price * Semyeong Oh * Serge Montagnac * Sergio Pascual * Shaheer Ahmad * Shailesh Ahuja +* Shane Maloney * Shankar Kulumani * Shantanu Srivastava * Shilpi Jain @@ -493,6 +505,7 @@ Core Package Contributors * Thomas J. Fan * Thomas Erben * Thomas Robitaille +* Thomas Vandal * Thompson Le Blanc * Tiago Gomes * Tiago Ribeiro @@ -526,9 +539,11 @@ Core Package Contributors * Yash Sharma * Yingqi Ying * Zac Hatfield-Dodds +* Zach Burnett * Zach Edwards * Zachary Kurtz * Zeljko Ivezic +* Zhen-Kai Gao * Zhiyuan Ma * Zlatan Vasović * Zé Vinicius From d88618950023223757b2ef66009a8d573cdd58f7 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 4 Nov 2024 15:53:42 -0500 Subject: [PATCH 013/145] DOC: Remove iraf.net from API doc that is a partial backport of #17278 onto v7.0.x branch --- astropy/visualization/interval.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/astropy/visualization/interval.py b/astropy/visualization/interval.py index e947a9df9a4d..18b165b95c2c 100644 --- a/astropy/visualization/interval.py +++ b/astropy/visualization/interval.py @@ -210,8 +210,6 @@ class ZScaleInterval(BaseInterval): """ Interval based on IRAF's zscale. - https://iraf.net/forum/viewtopic.php?showtopic=134139 - Original implementation: https://github.com/spacetelescope/stsci.numdisplay/blob/master/lib/stsci/numdisplay/zscale.py From 3a379d7d29c9384f44e8c1bd62b39867aa65eada Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Mon, 4 Nov 2024 21:56:34 +0100 Subject: [PATCH 014/145] Backport PR #17322: Remove duplicate Ed Slavich from credits --- .mailmap | 3 +-- docs/credits.rst | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index f925832935d8..1a9ba0b9c526 100644 --- a/.mailmap +++ b/.mailmap @@ -105,8 +105,7 @@ Diego Asterio de Zaballa Douglas Burke Dylan Gregersen Edward Gomez -Ed Slavich -Ed Slavich Edward Slavich +Edward Slavich Ed Slavich Eduardo Olinto <90293761+olintoeduardo@users.noreply.github.com> Eero Vaher Elijah Bernstein-Cooper diff --git a/docs/credits.rst b/docs/credits.rst index 99c9b93aa5b3..1d1bf3284f59 100644 --- a/docs/credits.rst +++ b/docs/credits.rst @@ -142,7 +142,6 @@ Core Package Contributors * E\. Madison Bray * E\. Rykoff * E.C. Herenz -* Ed Slavich * Eduardo Olinto * Edward Betts * Edward Slavich From dd0f99b872436cfaa01e753067545c24a76c5008 Mon Sep 17 00:00:00 2001 From: Eero Vaher Date: Tue, 5 Nov 2024 15:35:34 +0100 Subject: [PATCH 015/145] Backport PR #17263: BUG: fix instanciating Angle from a pyarrow array --- astropy/coordinates/angles/core.py | 11 ++++++++--- astropy/coordinates/tests/test_arrays.py | 12 ++++++++++++ docs/changes/coordinates/17263.bugfix.rst | 2 ++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 docs/changes/coordinates/17263.bugfix.rst diff --git a/astropy/coordinates/angles/core.py b/astropy/coordinates/angles/core.py index 6d2ff95859e5..10e40e76af58 100644 --- a/astropy/coordinates/angles/core.py +++ b/astropy/coordinates/angles/core.py @@ -180,9 +180,14 @@ def __new__(cls, angle, unit=None, dtype=np.inexact, copy=True, **kwargs): # Possible conversion to `unit` will be done below. angle = u.Quantity(angle, angle_unit, copy=COPY_IF_NEEDED) - elif isiterable(angle) and not ( - isinstance(angle, np.ndarray) and angle.dtype.kind not in "SUVO" - ): + elif isinstance(angle, np.ndarray): + if angle.dtype.kind in "SUVO": + angle = [cls(x, unit, copy=COPY_IF_NEEDED) for x in angle] + + elif hasattr(angle, "__array__"): + angle = np.asarray(angle) + + elif isiterable(angle): angle = [cls(x, unit, copy=COPY_IF_NEEDED) for x in angle] return super().__new__(cls, angle, unit, dtype=dtype, copy=copy, **kwargs) diff --git a/astropy/coordinates/tests/test_arrays.py b/astropy/coordinates/tests/test_arrays.py index c02cc9406df6..102b0a4f4129 100644 --- a/astropy/coordinates/tests/test_arrays.py +++ b/astropy/coordinates/tests/test_arrays.py @@ -55,6 +55,18 @@ def test_angle_arrays(): Angle(["04:02:02", "03:02:01", "06:02:01"]) +def test_angle_from_pyarrow(): + # Creating Angle instances from some array classes (e.g. pyarrow.array) failed + # even though creating Quantity instances succeeded. + # see https://github.com/astropy/astropy/issues/17255 + pa = pytest.importorskip("pyarrow") + + input_data = [1.1, 2.2] + arr = pa.array(input_data) + angle = Angle(arr, "deg") + npt.assert_array_equal(angle.value, input_data) + + def test_dms(): a1 = Angle([0, 45.5, -45.5], unit=u.degree) d, m, s = a1.dms diff --git a/docs/changes/coordinates/17263.bugfix.rst b/docs/changes/coordinates/17263.bugfix.rst new file mode 100644 index 000000000000..fb618bdb70e9 --- /dev/null +++ b/docs/changes/coordinates/17263.bugfix.rst @@ -0,0 +1,2 @@ +Fix a crash when instantiating ``Angle`` (or ``Latitude``, or ``Longitude``) +from a non-numpy array (for instance pyarrow arrays). From 0982df3f1e1094a6ec76ae6c3f999dce5238cd75 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Tue, 5 Nov 2024 16:31:21 +0100 Subject: [PATCH 016/145] fix RST syntax in README --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index fd54e9ae752f..90d3899b4a23 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,9 @@ ---- |Actions Status| |CircleCI Status| |Coverage Status| |PyPI Status| |Documentation Status| |Pre-Commit| |Ruff| |Zenodo| + ---- + The Astropy Project is a community effort to develop a single core package for astronomy in Python and foster interoperability between packages used in the field. This repository contains the core library. From 7ae9034e45ed87d3a7e852ea9b356b266dde38ee Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:32:01 -0500 Subject: [PATCH 017/145] Backport PR #17327: TST: filter hypothesis healchecks on double tests --- conftest.py | 8 +++++++- pyproject.toml | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/conftest.py b/conftest.py index 6f4d1d8468ae..4284ff09e352 100644 --- a/conftest.py +++ b/conftest.py @@ -45,7 +45,13 @@ def pytest_report_header(config): # `pytest --hypothesis-profile=fuzz ...` argument. hypothesis.settings.register_profile( - "ci", deadline=None, print_blob=True, derandomize=True + "ci", + deadline=None, + print_blob=True, + derandomize=True, + # disabling HealthCheck.differing_executors to allow double test + # see https://github.com/astropy/astropy/issues/17299 + suppress_health_check=[hypothesis.HealthCheck.differing_executors], ) hypothesis.settings.register_profile( "fuzzing", deadline=None, print_blob=True, max_examples=1000 diff --git a/pyproject.toml b/pyproject.toml index 3806458667e9..fecb7747d9a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,7 +96,6 @@ test = [ "coverage>=6.4.4", "pre-commit>=2.9.3", # lower bound is not tested beyond installation "pytest>=7.3.0", - "hypothesis!=6.116.0", # https://github.com/astropy/astropy/issues/17299 "pytest-doctestplus>=0.12", "pytest-astropy-header>=0.2.1", "pytest-astropy>=0.10.0", From 5a031a384dd3307396200f98f21509ed96802d74 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:37:28 -0500 Subject: [PATCH 018/145] Backport PR #17338: README: temporary force user stats image for pypi --- README.rst | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 90d3899b4a23..2a9241a09810 100644 --- a/README.rst +++ b/README.rst @@ -32,13 +32,7 @@ For more detailed instructions, see the `install guide Contributing ============ -.. raw:: html - - - - - Astropy user statistics - +|User Stats| The Astropy Project is made both by and for its users, so we welcome and encourage contributions of many kinds. Our goal is to keep this a positive, @@ -91,6 +85,10 @@ Astropy is licensed under a 3-clause BSD style license - see the :target: https://www.astropy.org/ :alt: Astropy +.. |User Stats| image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_user_stats_light.png + :target: https://docs.astropy.org/en/latest/impact_health.html + :alt: Astropy User Statistics + .. |Actions Status| image:: https://github.com/astropy/astropy/actions/workflows/ci_workflows.yml/badge.svg :target: https://github.com/astropy/astropy/actions :alt: Astropy's GitHub Actions CI Status From ecbb641b4479b050a1e88eb6f1fdead84035b4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brigitta=20Sip=C5=91cz?= Date: Wed, 6 Nov 2024 12:44:50 -0800 Subject: [PATCH 019/145] Backport PR #17344: Add groups to VOTables.Resource.to_xml() --- astropy/io/votable/tests/test_resource.py | 27 +++++++++++++++++++++++ astropy/io/votable/tree.py | 7 ++++-- docs/changes/io.votable/17344.bugfix.rst | 1 + 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 docs/changes/io.votable/17344.bugfix.rst diff --git a/astropy/io/votable/tests/test_resource.py b/astropy/io/votable/tests/test_resource.py index 92f4cf0ff506..ded738e4c853 100644 --- a/astropy/io/votable/tests/test_resource.py +++ b/astropy/io/votable/tests/test_resource.py @@ -1,5 +1,7 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst # LOCAL +import io + from astropy.io.votable import parse from astropy.utils.data import get_pkg_data_filename @@ -20,3 +22,28 @@ def test_resource_groups(): assert len(params) == 2 assert params[0].name == "standardID" assert params[1].name == "accessURL" + + +def test_roundtrip(): + # Issue #16511 VOTable writer does not write out GROUPs within RESOURCEs + + # Read the VOTABLE + votable = parse(get_pkg_data_filename("data/resource_groups.xml")) + + bio = io.BytesIO() + votable.to_xml(bio) + bio.seek(0) + votable = parse(bio) + + resource = votable.resources[0] + groups = resource.groups + params = resource.params + + # Test that params inside groups are not outside + + assert len(groups[0].entries) == 1 + assert groups[0].entries[0].name == "ID" + + assert len(params) == 2 + assert params[0].name == "standardID" + assert params[1].name == "accessURL" diff --git a/astropy/io/votable/tree.py b/astropy/io/votable/tree.py index f4678798f926..953cde027f74 100644 --- a/astropy/io/votable/tree.py +++ b/astropy/io/votable/tree.py @@ -3969,13 +3969,16 @@ def to_xml(self, w, **kwargs): w.element("DESCRIPTION", self.description, wrap=True) if self.mivot_block is not None and self.type == "meta": self.mivot_block.to_xml(w) - for element_set in ( + element_sets = [ self.coordinate_systems, self.time_systems, self.params, self.infos, self.links, - ): + ] + if kwargs["version_1_2_or_later"]: + element_sets.append(self.groups) + for element_set in element_sets: for element in element_set: element.to_xml(w, **kwargs) diff --git a/docs/changes/io.votable/17344.bugfix.rst b/docs/changes/io.votable/17344.bugfix.rst new file mode 100644 index 000000000000..73f65d8b7592 --- /dev/null +++ b/docs/changes/io.votable/17344.bugfix.rst @@ -0,0 +1 @@ +Updated xml writer for VOTable Resource elements to include groups. From ce3056ba5958dc5cfc47f0ae8f364e43b7f12bed Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Sat, 9 Nov 2024 11:08:21 -0500 Subject: [PATCH 020/145] Backport PR #17358: BUG: fix instanciating Angle from a pandas Series --- astropy/coordinates/angles/core.py | 4 +++- astropy/coordinates/tests/test_arrays.py | 11 +++++++++++ docs/changes/coordinates/17358.bugfix.rst | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 docs/changes/coordinates/17358.bugfix.rst diff --git a/astropy/coordinates/angles/core.py b/astropy/coordinates/angles/core.py index 10e40e76af58..b91543a405db 100644 --- a/astropy/coordinates/angles/core.py +++ b/astropy/coordinates/angles/core.py @@ -184,7 +184,9 @@ def __new__(cls, angle, unit=None, dtype=np.inexact, copy=True, **kwargs): if angle.dtype.kind in "SUVO": angle = [cls(x, unit, copy=COPY_IF_NEEDED) for x in angle] - elif hasattr(angle, "__array__"): + elif hasattr(angle, "__array__") and ( + not hasattr(angle, "dtype") or angle.dtype.kind not in "SUVO" + ): angle = np.asarray(angle) elif isiterable(angle): diff --git a/astropy/coordinates/tests/test_arrays.py b/astropy/coordinates/tests/test_arrays.py index 102b0a4f4129..bbb0570c54fa 100644 --- a/astropy/coordinates/tests/test_arrays.py +++ b/astropy/coordinates/tests/test_arrays.py @@ -67,6 +67,17 @@ def test_angle_from_pyarrow(): npt.assert_array_equal(angle.value, input_data) +def test_angle_from_pandas(): + # see https://github.com/astropy/astropy/issues/17357 + pd = pytest.importorskip("pandas") + + input_data = ["10 0 0", "12 0 0"] + df = pd.DataFrame({"angle": input_data}) + angle = Angle(df["angle"], unit=u.hourangle) + expected = Angle(input_data, u.hourangle) + npt.assert_array_equal(angle.value, expected.value) + + def test_dms(): a1 = Angle([0, 45.5, -45.5], unit=u.degree) d, m, s = a1.dms diff --git a/docs/changes/coordinates/17358.bugfix.rst b/docs/changes/coordinates/17358.bugfix.rst new file mode 100644 index 000000000000..6d6076372b0b --- /dev/null +++ b/docs/changes/coordinates/17358.bugfix.rst @@ -0,0 +1 @@ +Fixed instantiating ``Angle`` from a ``pandas`` ``Series`` object. From 64fa9a95a133ec6df8912bef193393931cc0cb0e Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Sun, 10 Nov 2024 13:59:38 -0500 Subject: [PATCH 021/145] Backport PR #17354: BUG: fix calling `np.nanvar` and `np.nanstd` with `Quantity` inputs + an `out` argument --- .../units/quantity_helper/function_helpers.py | 54 ++++++++++++------- .../units/tests/test_quantity_non_ufuncs.py | 42 +++++++++++++++ docs/changes/units/17354.bugfix.rst | 1 + 3 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 docs/changes/units/17354.bugfix.rst diff --git a/astropy/units/quantity_helper/function_helpers.py b/astropy/units/quantity_helper/function_helpers.py index be464c573b07..dafbd6009fa5 100644 --- a/astropy/units/quantity_helper/function_helpers.py +++ b/astropy/units/quantity_helper/function_helpers.py @@ -218,7 +218,6 @@ def __call__(self, f=None, helps=None, module=np): np.fft.fft2, np.fft.ifft2, np.fft.rfft2, np.fft.irfft2, np.fft.fftn, np.fft.ifftn, np.fft.rfftn, np.fft.irfftn, np.fft.hfft, np.fft.ihfft, - np.nanstd, # See comment on nanvar helper. np.linalg.eigvals, np.linalg.eigvalsh, } | ({np.asfarray} if NUMPY_LT_2_0 else set()) # noqa: NPY201 ) # fmt: skip @@ -252,15 +251,45 @@ def like_helper(a, *args, **kwargs): return (a.view(np.ndarray),) + args, kwargs, unit, None +def _quantity_out_as_array(out): + from astropy.units import Quantity + + if isinstance(out, Quantity): + return out.view(np.ndarray) + else: + # TODO: for an ndarray output, one could in principle + # try converting the input to dimensionless. + raise NotImplementedError + + # nanvar is safe for Quantity and was previously in SUBCLASS_FUNCTIONS, but it # is not safe for Angle, since the resulting unit is inconsistent with being # an Angle. By using FUNCTION_HELPERS, the unit gets passed through # _result_as_quantity, which will correctly drop to Quantity. # A side effect would be that np.nanstd then also produces Quantity; this -# is avoided by it being helped by invariant_a_helpers above. +# is avoided by it being helped below. @function_helper -def nanvar(a, *args, **kwargs): - return (a.view(np.ndarray),) + args, kwargs, a.unit**2, None +def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, **kwargs): + a = _as_quantity(a) + out_array = None if out is None else _quantity_out_as_array(out) + return ( + (a.view(np.ndarray), axis, dtype, out_array, ddof, keepdims), + kwargs, + a.unit**2, + out, + ) + + +@function_helper +def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, **kwargs): + a = _as_quantity(a) + out_array = None if out is None else _quantity_out_as_array(out) + return ( + (a.view(np.ndarray), axis, dtype, out_array, ddof, keepdims), + kwargs, + a.unit, + out, + ) @function_helper @@ -424,15 +453,8 @@ def _quantities2arrays(*args, unit_from_first=False): def _iterable_helper(*args, out=None, **kwargs): """Convert arguments to Quantity, and treat possible 'out'.""" - from astropy.units import Quantity - if out is not None: - if isinstance(out, Quantity): - kwargs["out"] = out.view(np.ndarray) - else: - # TODO: for an ndarray output, we could in principle - # try converting all Quantity to dimensionless. - raise NotImplementedError + kwargs["out"] = _quantity_out_as_array(out) # raises if not Quantity. arrays, unit = _quantities2arrays(*args) return arrays, kwargs, unit, out @@ -867,19 +889,13 @@ def cross_like_a_v(a, v, *args, **kwargs): @function_helper def einsum(*operands, out=None, **kwargs): - from astropy.units import Quantity - subscripts, *operands = operands if not isinstance(subscripts, str): raise ValueError('only "subscripts" string mode supported for einsum.') if out is not None: - if not isinstance(out, Quantity): - raise NotImplementedError - - else: - kwargs["out"] = out.view(np.ndarray) + kwargs["out"] = _quantity_out_as_array(out) qs = _as_quantities(*operands) unit = functools.reduce(operator.mul, (q.unit for q in qs), dimensionless_unscaled) diff --git a/astropy/units/tests/test_quantity_non_ufuncs.py b/astropy/units/tests/test_quantity_non_ufuncs.py index 21ae981d175b..d52751bfbb52 100644 --- a/astropy/units/tests/test_quantity_non_ufuncs.py +++ b/astropy/units/tests/test_quantity_non_ufuncs.py @@ -1280,11 +1280,53 @@ def test_nancumsum(self): def test_nanstd(self): self.check(np.nanstd) + @pytest.mark.parametrize( + "out_init", + [ + pytest.param(u.Quantity(-1, "m"), id="out with coorect unit"), + # this should work too: out.unit will be overridden + pytest.param(u.Quantity(-1), id="out with a different unit"), + ], + ) + def test_nanstd_out(self, out_init): + out = out_init.copy() + o = np.nanstd(self.q, out=out) + assert o is out + assert o == np.nanstd(self.q) + + # Also check array input, Quantity output. + out = out_init.copy() + o2 = np.nanstd(self.q.value, out=out) + assert o2 is out + assert o2.unit == u.dimensionless_unscaled + assert o2 == np.nanstd(self.q.value) + def test_nanvar(self): out = np.nanvar(self.q) expected = np.nanvar(self.q.value) * self.q.unit**2 assert np.all(out == expected) + @pytest.mark.parametrize( + "out_init", + [ + pytest.param(u.Quantity(-1, "m"), id="out with coorect unit"), + # this should work too: out.unit will be overridden + pytest.param(u.Quantity(-1), id="out with a different unit"), + ], + ) + def test_nanvar_out(self, out_init): + out = out_init.copy() + o = np.nanvar(self.q, out=out) + assert o is out + assert o == np.nanvar(self.q) + + # Also check array input, Quantity output. + out = out_init.copy() + o2 = np.nanvar(self.q.value, out=out) + assert o2 is out + assert o2.unit == u.dimensionless_unscaled + assert o2 == np.nanvar(self.q.value) + def test_nanprod(self): with pytest.raises(u.UnitsError): np.nanprod(self.q) diff --git a/docs/changes/units/17354.bugfix.rst b/docs/changes/units/17354.bugfix.rst new file mode 100644 index 000000000000..a22e1743ef9a --- /dev/null +++ b/docs/changes/units/17354.bugfix.rst @@ -0,0 +1 @@ +Fixed calling ``np.nanvar`` and ``np.nanstd`` with ``Quantity`` ``out`` argument. From 32f4e5a7aca8d2d5bd30b04d66bfc6c7e13f025c Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Sun, 10 Nov 2024 14:01:32 -0500 Subject: [PATCH 022/145] Backport PR #17364: TST: fix running array repr tests with an ellipsis on numpy 2.2 (dev) --- astropy/utils/compat/numpycompat.py | 2 ++ astropy/utils/masked/tests/test_masked.py | 9 +++++++-- pyproject.toml | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/astropy/utils/compat/numpycompat.py b/astropy/utils/compat/numpycompat.py index 0a383fc83790..3a9d82d56f01 100644 --- a/astropy/utils/compat/numpycompat.py +++ b/astropy/utils/compat/numpycompat.py @@ -14,6 +14,7 @@ "NUMPY_LT_1_26", "NUMPY_LT_2_0", "NUMPY_LT_2_1", + "NUMPY_LT_2_2", "COPY_IF_NEEDED", ] @@ -25,6 +26,7 @@ NUMPY_LT_1_26 = not minversion(np, "1.26") NUMPY_LT_2_0 = not minversion(np, "2.0") NUMPY_LT_2_1 = not minversion(np, "2.1.0.dev") +NUMPY_LT_2_2 = not minversion(np, "2.2.0.dev0") COPY_IF_NEEDED = False if NUMPY_LT_2_0 else None diff --git a/astropy/utils/masked/tests/test_masked.py b/astropy/utils/masked/tests/test_masked.py index 86f8bd4613fe..a4fdf1b1687c 100644 --- a/astropy/utils/masked/tests/test_masked.py +++ b/astropy/utils/masked/tests/test_masked.py @@ -14,7 +14,7 @@ from astropy import units as u from astropy.coordinates import Longitude from astropy.units import Quantity -from astropy.utils.compat import NUMPY_LT_2_0 +from astropy.utils.compat import NUMPY_LT_2_0, NUMPY_LT_2_2 from astropy.utils.compat.optional_deps import HAS_PLT from astropy.utils.masked import Masked, MaskedNDArray @@ -1449,8 +1449,13 @@ def test_masked_repr_explicit_structured(): def test_masked_repr_summary(): ma = Masked(np.arange(15.0), mask=[True] + [False] * 14) + if NUMPY_LT_2_2: + expected = "MaskedNDArray([———, 1., 2., ..., 12., 13., 14.])" + else: + expected = "MaskedNDArray([———, 1., 2., ..., 12., 13., 14.], shape=(15,))" + with np.printoptions(threshold=2): - assert repr(ma) == "MaskedNDArray([———, 1., 2., ..., 12., 13., 14.])" + assert repr(ma) == expected def test_masked_repr_nodata(): diff --git a/pyproject.toml b/pyproject.toml index fecb7747d9a7..67cabfa0a7e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -234,6 +234,7 @@ doctest_subpackage_requires = [ "astropy/table/table.py = python<3.12", # PYTHON_LT_3_12 (PR 14784) "astropy/table/mixins/dask.py = dask", "docs/* = numpy>=2", # not NUMPY_LT_2_0 (PR 15065) + "docs/timeseries/index.rst = numpy<2.2.0.dev0", # NUMPY_LT_2_2 (PR 17364) "astropy/stats/info_theory.py = numpy>=2", # not NUMPY_LT_2_0 (PR 15065) "astropy/stats/jackknife.py = numpy>=2", # not NUMPY_LT_2_0 (PR 15065) "astropy/table/row.py = numpy>=2", # not NUMPY_LT_2_0 (PR 15065) From 8d066ccd063cc18e0c7b2bd2c8be0aa9ceeae02a Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Nov 2024 14:26:24 +0000 Subject: [PATCH 023/145] Backport PR #17349: Finalizing changelog for v6.1.5 --- CHANGES.rst | 94 +++++++++++++++++++++ docs/changes/coordinates/17013.bugfix.rst | 2 - docs/changes/coordinates/17132.bugfix.rst | 3 - docs/changes/coordinates/17239.bugfix.rst | 2 - docs/changes/coordinates/17263.bugfix.rst | 2 - docs/changes/io.fits/16996.bugfix.rst | 1 - docs/changes/io.votable/17344.bugfix.rst | 1 - docs/changes/nddata/17281.bugfix.rst | 1 - docs/changes/stats/17086.bugfix.rst | 2 - docs/changes/table/15749.bugfix.rst | 1 - docs/changes/table/17105.bugfix.rst | 2 - docs/changes/units/17011.bugfix.rst | 7 -- docs/changes/units/17200.bugfix.rst | 3 - docs/changes/units/17216.bugfix.rst | 2 - docs/changes/units/17241.bugfix.rst | 2 - docs/changes/utils/17013.bugfix.rst | 2 - docs/changes/utils/17196.bugfix.rst | 1 - docs/changes/visualization/17175.bugfix.rst | 3 - docs/changes/wcs/17147.bugfix.rst | 3 - 19 files changed, 94 insertions(+), 40 deletions(-) delete mode 100644 docs/changes/coordinates/17013.bugfix.rst delete mode 100644 docs/changes/coordinates/17132.bugfix.rst delete mode 100644 docs/changes/coordinates/17239.bugfix.rst delete mode 100644 docs/changes/coordinates/17263.bugfix.rst delete mode 100644 docs/changes/io.fits/16996.bugfix.rst delete mode 100644 docs/changes/io.votable/17344.bugfix.rst delete mode 100644 docs/changes/nddata/17281.bugfix.rst delete mode 100644 docs/changes/stats/17086.bugfix.rst delete mode 100644 docs/changes/table/15749.bugfix.rst delete mode 100644 docs/changes/table/17105.bugfix.rst delete mode 100644 docs/changes/units/17011.bugfix.rst delete mode 100644 docs/changes/units/17200.bugfix.rst delete mode 100644 docs/changes/units/17216.bugfix.rst delete mode 100644 docs/changes/units/17241.bugfix.rst delete mode 100644 docs/changes/utils/17013.bugfix.rst delete mode 100644 docs/changes/utils/17196.bugfix.rst delete mode 100644 docs/changes/visualization/17175.bugfix.rst delete mode 100644 docs/changes/wcs/17147.bugfix.rst diff --git a/CHANGES.rst b/CHANGES.rst index 089579b245d7..46c292bc3007 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,97 @@ +Version 6.1.5 (2024-11-07) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Ensure that coordinates can be transformed to other coordinate frames + also if they have size zero (i.e., hold empty data arrays). [#17013] + +- ``Longitude`` and ``Latitude`` can no longer be initialized with strings + ending in "N" or "S", and "E" or "W", respectively, since those suggest + the other type. [#17132] + +- ``np.nanvar(angle)`` now produces a ``Quantity`` with the correct + unit, rather than raising an exception. [#17239] + +- Fix a crash when instantiating ``Angle`` (or ``Latitude``, or ``Longitude``) + from a non-numpy array (for instance pyarrow arrays). [#17263] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix access to VLA columns after slicing ``.data``. [#16996] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Updated xml writer for VOTable Resource elements to include groups. [#17344] + +astropy.nddata +^^^^^^^^^^^^^^ + +- Add support for positional only and keyword only arguments when using the ``support_nddata`` decorator. [#17281] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed a bug where float32 inputs to sigma_clip and SigmaClip were + changed to float. [#17086] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a crash when calling ``Column.pprint`` on a scalar column. [#15749] + +- Ensure that setting an existing column to a scalar always properly fills it + (rather than breaking the table if there was only one column in it). [#17105] + +astropy.units +^^^^^^^^^^^^^ + +- The unit parsers are now better at recognizing unusual composite + units: + + - units involving special unicode symbols, like "L☉/pc²"; + - units that include CDS units ending in a 0, like "eps0/s"; + - units including the degree symbol, "°". For example, "°C/s" is no + longer incorrectly interpreted as "°C/s^2". [#17011] + +- Converting the ohm to a string with the OGIP unit formatter (e.g. + ``f"{u.ohm:ogip}"``) previously produced the string ``'V / A'``, but now + produces ``'ohm'`` as expected. [#17200] + +- The ``OGIP`` unit formatter now handles the unit ``day`` and the corresponding + string ``"d"`` in full compliance with the standard. [#17216] + +- The ``"ogip"`` unit format now represents the unit angstrom as ``"angstrom"`` + instead of ``"0.1 nm"``. [#17241] + +astropy.utils +^^^^^^^^^^^^^ + +- Ensure that queries of ``.ut1_utc()`` and ``.pm_xy()`` return the correct + results also when passing in an empty array of times. [#17013] + +- Fixed a bug where astropy's logger wouldn't perform lazy string interpolation. [#17196] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Fixed a bug that caused ``CoordinateHelper.get_axislabel()`` to return an + empty string instead of the default label if no label has been explicitly + provided. [#17175] + +astropy.wcs +^^^^^^^^^^^ + +- Fixed a bug that caused ``WCS.slice`` to ignore ``numpy_order`` and always + interpret the slices as if ``numpy_order`` was ``True``, in the specific case + where the slices were such that dimensions in the WCS would be dropped. [#17147] + Version 6.1.4 (2024-09-26) ========================== diff --git a/docs/changes/coordinates/17013.bugfix.rst b/docs/changes/coordinates/17013.bugfix.rst deleted file mode 100644 index 21b7292f3665..000000000000 --- a/docs/changes/coordinates/17013.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure that coordinates can be transformed to other coordinate frames -also if they have size zero (i.e., hold empty data arrays). diff --git a/docs/changes/coordinates/17132.bugfix.rst b/docs/changes/coordinates/17132.bugfix.rst deleted file mode 100644 index c768a96de943..000000000000 --- a/docs/changes/coordinates/17132.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -``Longitude`` and ``Latitude`` can no longer be initialized with strings -ending in "N" or "S", and "E" or "W", respectively, since those suggest -the other type. diff --git a/docs/changes/coordinates/17239.bugfix.rst b/docs/changes/coordinates/17239.bugfix.rst deleted file mode 100644 index 34f071f1f925..000000000000 --- a/docs/changes/coordinates/17239.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -``np.nanvar(angle)`` now produces a ``Quantity`` with the correct -unit, rather than raising an exception. diff --git a/docs/changes/coordinates/17263.bugfix.rst b/docs/changes/coordinates/17263.bugfix.rst deleted file mode 100644 index fb618bdb70e9..000000000000 --- a/docs/changes/coordinates/17263.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a crash when instantiating ``Angle`` (or ``Latitude``, or ``Longitude``) -from a non-numpy array (for instance pyarrow arrays). diff --git a/docs/changes/io.fits/16996.bugfix.rst b/docs/changes/io.fits/16996.bugfix.rst deleted file mode 100644 index f1e27100fb19..000000000000 --- a/docs/changes/io.fits/16996.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix access to VLA columns after slicing ``.data``. diff --git a/docs/changes/io.votable/17344.bugfix.rst b/docs/changes/io.votable/17344.bugfix.rst deleted file mode 100644 index 73f65d8b7592..000000000000 --- a/docs/changes/io.votable/17344.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Updated xml writer for VOTable Resource elements to include groups. diff --git a/docs/changes/nddata/17281.bugfix.rst b/docs/changes/nddata/17281.bugfix.rst deleted file mode 100644 index a226c98a861d..000000000000 --- a/docs/changes/nddata/17281.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Add support for positional only and keyword only arguments when using the ``support_nddata`` decorator. diff --git a/docs/changes/stats/17086.bugfix.rst b/docs/changes/stats/17086.bugfix.rst deleted file mode 100644 index 32c5aa3aee06..000000000000 --- a/docs/changes/stats/17086.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed a bug where float32 inputs to sigma_clip and SigmaClip were -changed to float. diff --git a/docs/changes/table/15749.bugfix.rst b/docs/changes/table/15749.bugfix.rst deleted file mode 100644 index 6f2728560973..000000000000 --- a/docs/changes/table/15749.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a crash when calling ``Column.pprint`` on a scalar column. diff --git a/docs/changes/table/17105.bugfix.rst b/docs/changes/table/17105.bugfix.rst deleted file mode 100644 index 419e388dfc4b..000000000000 --- a/docs/changes/table/17105.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure that setting an existing column to a scalar always properly fills it -(rather than breaking the table if there was only one column in it). diff --git a/docs/changes/units/17011.bugfix.rst b/docs/changes/units/17011.bugfix.rst deleted file mode 100644 index 94626e4f8eef..000000000000 --- a/docs/changes/units/17011.bugfix.rst +++ /dev/null @@ -1,7 +0,0 @@ -The unit parsers are now better at recognizing unusual composite -units: - -- units involving special unicode symbols, like "L☉/pc²"; -- units that include CDS units ending in a 0, like "eps0/s"; -- units including the degree symbol, "°". For example, "°C/s" is no - longer incorrectly interpreted as "°C/s^2". diff --git a/docs/changes/units/17200.bugfix.rst b/docs/changes/units/17200.bugfix.rst deleted file mode 100644 index a471295f80bb..000000000000 --- a/docs/changes/units/17200.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Converting the ohm to a string with the OGIP unit formatter (e.g. -``f"{u.ohm:ogip}"``) previously produced the string ``'V / A'``, but now -produces ``'ohm'`` as expected. diff --git a/docs/changes/units/17216.bugfix.rst b/docs/changes/units/17216.bugfix.rst deleted file mode 100644 index 0060f6b4fc53..000000000000 --- a/docs/changes/units/17216.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -The ``OGIP`` unit formatter now handles the unit ``day`` and the corresponding -string ``"d"`` in full compliance with the standard. diff --git a/docs/changes/units/17241.bugfix.rst b/docs/changes/units/17241.bugfix.rst deleted file mode 100644 index 82b8fc3f1bd7..000000000000 --- a/docs/changes/units/17241.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -The ``"ogip"`` unit format now represents the unit angstrom as ``"angstrom"`` -instead of ``"0.1 nm"``. diff --git a/docs/changes/utils/17013.bugfix.rst b/docs/changes/utils/17013.bugfix.rst deleted file mode 100644 index 433013d88c63..000000000000 --- a/docs/changes/utils/17013.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensure that queries of ``.ut1_utc()`` and ``.pm_xy()`` return the correct -results also when passing in an empty array of times. diff --git a/docs/changes/utils/17196.bugfix.rst b/docs/changes/utils/17196.bugfix.rst deleted file mode 100644 index f7332493d30b..000000000000 --- a/docs/changes/utils/17196.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a bug where astropy's logger wouldn't perform lazy string interpolation. diff --git a/docs/changes/visualization/17175.bugfix.rst b/docs/changes/visualization/17175.bugfix.rst deleted file mode 100644 index 945d7a00bc67..000000000000 --- a/docs/changes/visualization/17175.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed a bug that caused ``CoordinateHelper.get_axislabel()`` to return an -empty string instead of the default label if no label has been explicitly -provided. diff --git a/docs/changes/wcs/17147.bugfix.rst b/docs/changes/wcs/17147.bugfix.rst deleted file mode 100644 index ef9620a9c9f1..000000000000 --- a/docs/changes/wcs/17147.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed a bug that caused ``WCS.slice`` to ignore ``numpy_order`` and always -interpret the slices as if ``numpy_order`` was ``True``, in the specific case -where the slices were such that dimensions in the WCS would be dropped. From 74f27bbe56da3499d1bb2d34ea50bb6cb1f49cf2 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 11 Nov 2024 09:29:11 -0500 Subject: [PATCH 024/145] Backport PR #17359: [7.0.0rc1] Mark some "future" test in coordinates as remote_data --- astropy/coordinates/tests/test_iau_fullstack.py | 1 + astropy/coordinates/tests/test_regression.py | 1 + astropy/coordinates/tests/test_utils.py | 1 + 3 files changed, 3 insertions(+) diff --git a/astropy/coordinates/tests/test_iau_fullstack.py b/astropy/coordinates/tests/test_iau_fullstack.py index bb162e5ac3a5..b39381098749 100644 --- a/astropy/coordinates/tests/test_iau_fullstack.py +++ b/astropy/coordinates/tests/test_iau_fullstack.py @@ -180,6 +180,7 @@ def test_fiducial_roudtrip(fullstack_icrs, fullstack_fiducial_altaz): npt.assert_allclose(fullstack_icrs.dec.deg, icrs2.dec.deg) +@pytest.mark.remote_data def test_future_altaz(): """ While this does test the full stack, it is mostly meant to check that a diff --git a/astropy/coordinates/tests/test_regression.py b/astropy/coordinates/tests/test_regression.py index e4ef4cdd1098..1c5b3277dc91 100644 --- a/astropy/coordinates/tests/test_regression.py +++ b/astropy/coordinates/tests/test_regression.py @@ -222,6 +222,7 @@ def test_regression_4210(): eclobj.distance +@pytest.mark.remote_data def test_regression_futuretimes_4302(): """ Checks that an error is not raised for future times not covered by IERS diff --git a/astropy/coordinates/tests/test_utils.py b/astropy/coordinates/tests/test_utils.py index 7ffd03ae94f3..048522e136b6 100644 --- a/astropy/coordinates/tests/test_utils.py +++ b/astropy/coordinates/tests/test_utils.py @@ -13,6 +13,7 @@ from astropy.utils.exceptions import AstropyWarning +@pytest.mark.remote_data def test_polar_motion_unsupported_dates(): msg = r"Tried to get polar motions for times {} IERS.*" From 4721139e0180117d95f58fcbd6d5a77f58950e6b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 11 Nov 2024 15:02:07 +0000 Subject: [PATCH 025/145] Backport PR #17370: Finalizing changelog for v6.1.6 --- CHANGES.rst | 17 +++++++++++++++++ docs/changes/coordinates/17358.bugfix.rst | 1 - docs/changes/units/17354.bugfix.rst | 1 - 3 files changed, 17 insertions(+), 2 deletions(-) delete mode 100644 docs/changes/coordinates/17358.bugfix.rst delete mode 100644 docs/changes/units/17354.bugfix.rst diff --git a/CHANGES.rst b/CHANGES.rst index 46c292bc3007..87be776a98fc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,20 @@ +Version 6.1.6 (2024-11-11) +========================== + +Bug Fixes +--------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- Fixed instantiating ``Angle`` from a ``pandas`` ``Series`` object. [#17358] + +astropy.units +^^^^^^^^^^^^^ + +- Fixed calling ``np.nanvar`` and ``np.nanstd`` with ``Quantity`` ``out`` argument. [#17354] + + Version 6.1.5 (2024-11-07) ========================== diff --git a/docs/changes/coordinates/17358.bugfix.rst b/docs/changes/coordinates/17358.bugfix.rst deleted file mode 100644 index 6d6076372b0b..000000000000 --- a/docs/changes/coordinates/17358.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed instantiating ``Angle`` from a ``pandas`` ``Series`` object. diff --git a/docs/changes/units/17354.bugfix.rst b/docs/changes/units/17354.bugfix.rst deleted file mode 100644 index a22e1743ef9a..000000000000 --- a/docs/changes/units/17354.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed calling ``np.nanvar`` and ``np.nanstd`` with ``Quantity`` ``out`` argument. From d283a9db232e9087653d46c75baac346a590af4b Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:49:02 -0500 Subject: [PATCH 026/145] Backport PR #17377: Allow more tests to run without latest IERS data --- astropy/coordinates/tests/test_iau_fullstack.py | 2 +- astropy/coordinates/tests/test_regression.py | 4 ++-- astropy/coordinates/tests/test_utils.py | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/astropy/coordinates/tests/test_iau_fullstack.py b/astropy/coordinates/tests/test_iau_fullstack.py index b39381098749..90ab5fe075f3 100644 --- a/astropy/coordinates/tests/test_iau_fullstack.py +++ b/astropy/coordinates/tests/test_iau_fullstack.py @@ -180,7 +180,6 @@ def test_fiducial_roudtrip(fullstack_icrs, fullstack_fiducial_altaz): npt.assert_allclose(fullstack_icrs.dec.deg, icrs2.dec.deg) -@pytest.mark.remote_data def test_future_altaz(): """ While this does test the full stack, it is mostly meant to check that a @@ -209,5 +208,6 @@ def test_future_altaz(): AstropyWarning, match="Tried to get polar motions for times after IERS data is valid.*", ), + iers.conf.set_temp("auto_max_age", None), ): SkyCoord(1 * u.deg, 2 * u.deg).transform_to(AltAz(location=location, obstime=t)) diff --git a/astropy/coordinates/tests/test_regression.py b/astropy/coordinates/tests/test_regression.py index 1c5b3277dc91..828b8f1ebe94 100644 --- a/astropy/coordinates/tests/test_regression.py +++ b/astropy/coordinates/tests/test_regression.py @@ -222,7 +222,6 @@ def test_regression_4210(): eclobj.distance -@pytest.mark.remote_data def test_regression_futuretimes_4302(): """ Checks that an error is not raised for future times not covered by IERS @@ -263,7 +262,8 @@ def test_regression_futuretimes_4302(): with ctx1, ctx2, ctx3: future_time = Time("2511-5-1") c = CIRS(1 * u.deg, 2 * u.deg, obstime=future_time) - c.transform_to(ITRS(obstime=future_time)) + with iers.conf.set_temp("auto_max_age", None): + c.transform_to(ITRS(obstime=future_time)) def test_regression_4996(): diff --git a/astropy/coordinates/tests/test_utils.py b/astropy/coordinates/tests/test_utils.py index 048522e136b6..513abf69f582 100644 --- a/astropy/coordinates/tests/test_utils.py +++ b/astropy/coordinates/tests/test_utils.py @@ -10,10 +10,10 @@ from astropy.coordinates.solar_system import get_body_barycentric_posvel from astropy.tests.helper import PYTEST_LT_8_0, assert_quantity_allclose from astropy.time import Time +from astropy.utils import iers from astropy.utils.exceptions import AstropyWarning -@pytest.mark.remote_data def test_polar_motion_unsupported_dates(): msg = r"Tried to get polar motions for times {} IERS.*" @@ -25,7 +25,11 @@ def test_polar_motion_unsupported_dates(): with pytest.warns(AstropyWarning, match=msg.format("before")), ctx: get_polar_motion(Time("1900-01-01")) - with pytest.warns(AstropyWarning, match=msg.format("after")), ctx: + with ( + pytest.warns(AstropyWarning, match=msg.format("after")), + ctx, + iers.conf.set_temp("auto_max_age", None), + ): get_polar_motion(Time("2100-01-01")) From 70590333a5eed6144db299c3469cdaf6c7710410 Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Wed, 13 Nov 2024 09:14:19 -0500 Subject: [PATCH 027/145] Backport PR #17385: Fix aggregation for empty tables (alternative) --- astropy/table/groups.py | 4 ++-- astropy/table/tests/test_groups.py | 13 +++++++++++++ docs/changes/table/17385.bugfix.rst | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 docs/changes/table/17385.bugfix.rst diff --git a/astropy/table/groups.py b/astropy/table/groups.py index a60ebda827d4..8b8bdf5154c7 100644 --- a/astropy/table/groups.py +++ b/astropy/table/groups.py @@ -254,7 +254,7 @@ def __init__(self, parent_column, indices=None, keys=None): @property def indices(self): # If the parent column is in a table then use group indices from table - if self.parent_table: + if self.parent_table is not None: return self.parent_table.groups.indices else: if self._indices is None: @@ -265,7 +265,7 @@ def indices(self): @property def keys(self): # If the parent column is in a table then use group indices from table - if self.parent_table: + if self.parent_table is not None: return self.parent_table.groups.keys else: return self._keys diff --git a/astropy/table/tests/test_groups.py b/astropy/table/tests/test_groups.py index 7f3065fd37e6..0ab8676b4423 100644 --- a/astropy/table/tests/test_groups.py +++ b/astropy/table/tests/test_groups.py @@ -576,6 +576,19 @@ def np_add(x): assert tga.pformat() == [" a ", "---", " 0", " 1", " 2"] +def test_table_aggregate_reduceat_empty(): + for masked in (False, True): + tg = Table( + { + "action": np.asarray([], dtype=str), + "duration": np.asarray([], dtype=float), + }, + masked=masked, + ) + tga = tg.group_by("action").groups.aggregate(np.sum) + assert tga.pformat() == ["action duration", "------ --------"] + + def test_column_aggregate(T1): """ Aggregate a single table column diff --git a/docs/changes/table/17385.bugfix.rst b/docs/changes/table/17385.bugfix.rst new file mode 100644 index 000000000000..c31e40a9b2d5 --- /dev/null +++ b/docs/changes/table/17385.bugfix.rst @@ -0,0 +1 @@ +Fixed table aggregate with empty columns when float is present. From 6ece75a39f718f83de938deeac22e47929e31412 Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Wed, 13 Nov 2024 09:26:01 -0500 Subject: [PATCH 028/145] Backport PR #17387: Even if IERS_Auto predictive values are stale, do not download IERS-A if downloading is disabled --- astropy/utils/iers/iers.py | 5 +++++ astropy/utils/iers/tests/test_iers.py | 16 +++++++++++++--- docs/changes/utils/17387.bugfix.rst | 2 ++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 docs/changes/utils/17387.bugfix.rst diff --git a/astropy/utils/iers/iers.py b/astropy/utils/iers/iers.py index 4ba0fc646667..5388c93b9570 100644 --- a/astropy/utils/iers/iers.py +++ b/astropy/utils/iers/iers.py @@ -883,6 +883,11 @@ def _refresh_table_as_needed(self, mjd): In other words the IERS-A table was created by IERS long enough ago that it can be considered stale for predictions. """ + # If downloading is disabled, bail out silently. + # _check_interpolate_indices() will error later if appropriate. + if not conf.auto_download: + return + # Pass in initial to np.max to allow things to work for empty mjd. max_input_mjd = np.max(mjd, initial=50000) now_mjd = self.time_now.mjd diff --git a/astropy/utils/iers/tests/test_iers.py b/astropy/utils/iers/tests/test_iers.py index 7bbe06f2532b..b1cd263cdf64 100644 --- a/astropy/utils/iers/tests/test_iers.py +++ b/astropy/utils/iers/tests/test_iers.py @@ -341,12 +341,22 @@ def test_simple(self): dat.ut1_utc(Time(60000, format="mjd").jd) assert len(warns) == 1 - # Warning only if we are getting return status + # Confirm that disabling the download means no warning because there is no + # refresh to even fail, but there will still be the interpolation error + with ( + iers.conf.set_temp("auto_download", False), + pytest.raises( + ValueError, + match="interpolating from IERS_Auto using predictive values that are more", + ), + ): + dat.ut1_utc(Time(60000, format="mjd").jd) + + # Warning only (i.e., no exception) if we are getting return status with pytest.warns( iers.IERSStaleWarning, match="IERS_Auto predictive values are older" - ) as warns: + ): dat.ut1_utc(Time(60000, format="mjd").jd, return_status=True) - assert len(warns) == 1 # Now set auto_max_age = None which says that we don't care how old the # available IERS-A file is. There should be no warnings or exceptions. diff --git a/docs/changes/utils/17387.bugfix.rst b/docs/changes/utils/17387.bugfix.rst new file mode 100644 index 000000000000..71a82d59183c --- /dev/null +++ b/docs/changes/utils/17387.bugfix.rst @@ -0,0 +1,2 @@ +Fixed a bug where an old IERS-A table with stale predictive values could trigger +the download of a new IERS-A table even if automatic downloading was disabled. From 4855ea6141943625faf802ac67d67e508c320781 Mon Sep 17 00:00:00 2001 From: Eero Vaher Date: Thu, 14 Nov 2024 10:18:05 +0100 Subject: [PATCH 029/145] Backport PR #17355: Allow `Unit` to create dimensionless units with complex or rational scale factors --- astropy/units/core.py | 4 +++- astropy/units/tests/test_units.py | 20 +++++++++++++++++++- docs/changes/units/17355.bugfix.rst | 3 +++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 docs/changes/units/17355.bugfix.rst diff --git a/astropy/units/core.py b/astropy/units/core.py index 1031c9c6028a..e5cf130e9914 100644 --- a/astropy/units/core.py +++ b/astropy/units/core.py @@ -10,6 +10,7 @@ import operator import textwrap import warnings +from fractions import Fraction from functools import cached_property from threading import RLock from typing import TYPE_CHECKING @@ -2144,7 +2145,8 @@ def __call__( warnings.warn(msg, UnitsWarning) return UnrecognizedUnit(s) - elif isinstance(s, (int, float, np.floating, np.integer)): + # can't use units.typing.Complex because of an import loop + elif isinstance(s, (int, float, complex, Fraction, np.number)): return CompositeUnit(s, [], []) elif isinstance(s, tuple): diff --git a/astropy/units/tests/test_units.py b/astropy/units/tests/test_units.py index d61afcbf9862..4d2d7f586d6e 100644 --- a/astropy/units/tests/test_units.py +++ b/astropy/units/tests/test_units.py @@ -36,7 +36,6 @@ def test_initialisation(): assert u.Unit("") == u.dimensionless_unscaled assert u.one == u.dimensionless_unscaled assert u.Unit("10 m") == ten_meter - assert u.Unit(10.0) == u.CompositeUnit(10.0, [], []) assert u.Unit() == u.dimensionless_unscaled @@ -1120,3 +1119,22 @@ def test_error_on_conversion_of_zero_to_unit(): ) def test_scale_sanitization(unsanitized, sanitized): assert u.CompositeUnit(unsanitized, [u.m], [1]).scale == sanitized + + +@pytest.mark.parametrize( + "scale", + [ + 5, + 10.0, + 7 + 3j, + Fraction(1, 3), + np.int32(100), + np.float32(0.01), + np.complex128(1 - 4j), + ], + ids=type, +) +def test_dimensionless_scale_factor_types(scale): + # Regression test for #17355 - Unit did not accept all scale factor + # types that CompositeUnit accepted + assert u.Unit(scale) == u.CompositeUnit(scale, [], []) diff --git a/docs/changes/units/17355.bugfix.rst b/docs/changes/units/17355.bugfix.rst new file mode 100644 index 000000000000..16952271bf53 --- /dev/null +++ b/docs/changes/units/17355.bugfix.rst @@ -0,0 +1,3 @@ +It is now possible to use ``Unit`` to create dimensionless units with a scale +factor that is a complex number or a ``fractions.Fraction`` instance. +It was already possible to create such units directly with ``CompositeUnit``. From e064e292be6a6ee467ed076256b51e6e3a2b4f7c Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:37:51 -0500 Subject: [PATCH 030/145] Backport PR #17380: BUG: ensure zero length tables with coordinates or representations can be stacked. --- astropy/coordinates/baseframe.py | 16 ++-- astropy/coordinates/representation/base.py | 14 ++-- astropy/table/tests/test_mixin.py | 26 +++--- astropy/table/tests/test_operations.py | 92 ++++++++++++++-------- docs/changes/table/17380.bugfix.rst | 3 + 5 files changed, 92 insertions(+), 59 deletions(-) create mode 100644 docs/changes/table/17380.bugfix.rst diff --git a/astropy/coordinates/baseframe.py b/astropy/coordinates/baseframe.py index 3a4348962ede..c69010f177cf 100644 --- a/astropy/coordinates/baseframe.py +++ b/astropy/coordinates/baseframe.py @@ -270,19 +270,17 @@ def new_like(self, coords, length, metadata_conflicts="warn", name=None): attrs = self.merge_cols_attributes( coords, metadata_conflicts, name, ("meta", "description") ) - coord0 = coords[0] - # Make a new coord object with the desired length and attributes - # by using the _apply / __getitem__ machinery to effectively return - # coord0[[0, 0, ..., 0, 0]]. This will have the all the right frame - # attributes with the right shape. - indexes = np.zeros(length, dtype=np.int64) - out = coord0[indexes] + # Make a new coordinate with the desired length. + coord0 = coords[0] + out = coord0._apply(np.zeros_like, shape=(length,) + coord0.shape[1:]) - # Use __setitem__ machinery to check for consistency of all coords + # Use __setitem__ machinery to check for consistency of all coords. + # We use :0 to ensure we do not break on empty coordinates (with the + # side benefit that we do not actually set anything). for coord in coords[1:]: try: - out[0] = coord[0] + out[:0] = coord[:0] except Exception as err: raise ValueError("Input coords are inconsistent.") from err diff --git a/astropy/coordinates/representation/base.py b/astropy/coordinates/representation/base.py index 0ab48b765ce7..e44f32767d87 100644 --- a/astropy/coordinates/representation/base.py +++ b/astropy/coordinates/representation/base.py @@ -104,18 +104,18 @@ def new_like(self, reps, length, metadata_conflicts="warn", name=None): attrs = self.merge_cols_attributes( reps, metadata_conflicts, name, ("meta", "description") ) - # Make a new representation or differential with the desired length - # using the _apply / __getitem__ machinery to effectively return - # rep0[[0, 0, ..., 0, 0]]. This will have the right shape, and - # include possible differentials. - indexes = np.zeros(length, dtype=np.int64) - out = reps[0][indexes] + + # Make a new representation or differential with the desired length. + rep0 = reps[0] + out = rep0._apply(np.zeros_like, shape=(length,) + rep0.shape[1:]) # Use __setitem__ machinery to check whether all representations # can represent themselves as this one without loss of information. + # We use :0 to ensure we do not break on empty coordinates (with the + # side benefit that we do not actually set anything). for rep in reps[1:]: try: - out[0] = rep[0] + out[:0] = rep[:0] except Exception as err: raise ValueError("input representations are inconsistent.") from err diff --git a/astropy/table/tests/test_mixin.py b/astropy/table/tests/test_mixin.py index e23576872200..6882b3566bac 100644 --- a/astropy/table/tests/test_mixin.py +++ b/astropy/table/tests/test_mixin.py @@ -632,30 +632,34 @@ def test_vstack(): vstack([t1, t2]) -def test_insert_row(mixin_cols): +@pytest.mark.parametrize("empty_table", [False, True]) +def test_insert_row(mixin_cols, empty_table): """ Test inserting a row, which works for Column, Quantity, Time and SkyCoord. """ - t = QTable(mixin_cols) - t0 = t.copy() + t0 = QTable(mixin_cols) + if empty_table: + t = t0[:0].copy() + insert_index = 0 + result_indices = [-1] + else: + t = t0.copy() + insert_index = 1 + result_indices = [0, -1, 1, 2, 3] t["m"].info.description = "d" - idxs = [0, -1, 1, 2, 3] if isinstance( t["m"], (u.Quantity, Column, time.Time, time.TimeDelta, coordinates.SkyCoord) ): - t.insert_row(1, t[-1]) + t.insert_row(insert_index, t0[-1]) for name in t.colnames: - col = t[name] - if isinstance(col, coordinates.SkyCoord): - assert skycoord_equal(col, t0[name][idxs]) - else: - assert np.all(col == t0[name][idxs]) + assert np.all(t[name] == t0[name][result_indices]) + assert np.all(t == t0[result_indices]) assert t["m"].info.description == "d" else: with pytest.raises(ValueError) as exc: - t.insert_row(1, t[-1]) + t.insert_row(insert_index, t0[-1]) assert "Unable to insert row" in str(exc.value) diff --git a/astropy/table/tests/test_operations.py b/astropy/table/tests/test_operations.py index d2cbce6942f2..93c1197f33bd 100644 --- a/astropy/table/tests/test_operations.py +++ b/astropy/table/tests/test_operations.py @@ -28,6 +28,7 @@ from astropy.utils import metadata from astropy.utils.compat import NUMPY_LT_2_0 from astropy.utils.compat.optional_deps import HAS_SCIPY +from astropy.utils.masked import Masked from astropy.utils.metadata import MergeConflictError @@ -35,6 +36,17 @@ def sort_eq(list1, list2): return sorted(list1) == sorted(list2) +def check_cols_equal(col1, col2): + """Check that col1 == col2, taking care of zero-length masked columns.""" + assert ( + type(col1) is type(col2) + or (isinstance(col1, Masked) and type(col1) is Masked(type(col2))) + or (isinstance(col2, Masked) and type(col2) is Masked(type(col1))) + ) + eq = np.all(col1 == col2) + return eq or isinstance(eq, Masked) and not eq.shape and eq.unmasked + + def check_mask(col, exp_mask): """Check that col.mask == exp_mask""" if hasattr(col, "mask"): @@ -1438,15 +1450,22 @@ def test_vstack_one_table(self, operation_table_type): assert (self.t1 == table.vstack(self.t1)).all() assert (self.t1 == table.vstack([self.t1])).all() - def test_mixin_functionality(self, mixin_cols): - col = mixin_cols["m"] - len_col = len(col) - t = table.QTable([col], names=["a"]) - cls_name = type(col).__name__ + @pytest.mark.parametrize("empty_table1", [False, True]) + @pytest.mark.parametrize("empty_table2", [False, True]) + def test_mixin_functionality(self, mixin_cols, empty_table1, empty_table2): + col1 = col2 = mixin_cols["m"] + if empty_table1: + col1 = col1[:0] + if empty_table2: + col2 = col2[:0] + len_col1 = len(col1) + t1 = table.QTable([col1], names=["a"]) + len_col2 = len(col2) + t2 = table.QTable([col2], names=["a"]) # Vstack works for these classes: if isinstance( - col, + col1, ( u.Quantity, Time, @@ -1457,40 +1476,32 @@ def test_mixin_functionality(self, mixin_cols): StokesCoord, ), ): - out = table.vstack([t, t]) - assert len(out) == len_col * 2 - if cls_name == "SkyCoord": - # Argh, SkyCoord needs __eq__!! - assert skycoord_equal(out["a"][len_col:], col) - assert skycoord_equal(out["a"][:len_col], col) - elif "Repr" in cls_name or "Diff" in cls_name: - assert np.all(representation_equal(out["a"][:len_col], col)) - assert np.all(representation_equal(out["a"][len_col:], col)) - else: - assert np.all(out["a"][:len_col] == col) - assert np.all(out["a"][len_col:] == col) + out = table.vstack([t1, t2]) + assert len(out) == len_col1 + len_col2 + assert check_cols_equal(out["a"][:len_col1], col1) + assert check_cols_equal(out["a"][len_col1:], col2) else: - msg = f"vstack unavailable for mixin column type(s): {cls_name}" + msg = f"vstack unavailable for mixin column type(s): {type(col1).__name__}" with pytest.raises(NotImplementedError, match=re.escape(msg)): - table.vstack([t, t]) + table.vstack([t1, t2]) # Check for outer stack which requires masking. Works for # the listed mixins classes. - t2 = table.QTable([col], names=["b"]) # different from col name for t - if isinstance(col, (Time, TimeDelta, Quantity, SkyCoord)): - out = table.vstack([t, t2], join_type="outer") - assert len(out) == len_col * 2 - assert np.all(out["a"][:len_col] == col) - assert np.all(out["b"][len_col:] == col) - assert check_mask(out["a"], [False] * len_col + [True] * len_col) - assert check_mask(out["b"], [True] * len_col + [False] * len_col) + t2 = table.QTable([col2], names=["b"]) # different from col name for t + if isinstance(col1, (Time, TimeDelta, Quantity, SkyCoord)): + out = table.vstack([t1, t2], join_type="outer") + assert len(out) == len_col1 + len_col2 + assert check_cols_equal(out["a"][:len_col1], col1) + assert check_cols_equal(out["b"][len_col1:], col2) + assert check_mask(out["a"], [False] * len_col1 + [True] * len_col2) + assert check_mask(out["b"], [True] * len_col1 + [False] * len_col2) # check directly stacking mixin columns: - out2 = table.vstack([t, t2["b"]]) - assert np.all(out["a"] == out2["a"]) - assert np.all(out["b"] == out2["b"]) + out2 = table.vstack([t1, t2["b"]]) + assert check_cols_equal(out["a"], out2["a"]) + assert check_cols_equal(out["b"], out2["b"]) else: with pytest.raises(NotImplementedError) as err: - table.vstack([t, t2], join_type="outer") + table.vstack([t1, t2], join_type="outer") assert "vstack requires masking" in str( err.value ) or "vstack unavailable" in str(err.value) @@ -1512,6 +1523,15 @@ def test_vstack_different_representation(self): with pytest.raises(ValueError, match="representations are inconsistent"): table.vstack([t1, t3]) + def test_vstack_different_sky_coordinates(self): + """Test that SkyCoord can generally not be mixed together.""" + sc1 = SkyCoord([1, 2] * u.deg, [3, 4] * u.deg) + sc2 = SkyCoord([5, 6] * u.deg, [7, 8] * u.deg, frame="fk5") + t1 = Table([sc1]) + t2 = Table([sc2]) + with pytest.raises(ValueError, match="coords are inconsistent"): + table.vstack([t1, t2]) + def test_vstack_structured_column(self): """Regression tests for gh-13271.""" # Two tables with matching names, including a structured column. @@ -2597,3 +2617,11 @@ def test_table_comp(t1, t2): assert not any(t2 == t1) assert all(t1 != t2) assert all(t2 != t1) + + +def test_empty_skycoord_vstack(): + # Explicit regression test for gh-17378 + table1 = Table({"foo": SkyCoord([], [], unit="deg")}) + table2 = table.vstack([table1, table1]) # Used to fail. + assert len(table2) == 0 + assert isinstance(table2["foo"], SkyCoord) diff --git a/docs/changes/table/17380.bugfix.rst b/docs/changes/table/17380.bugfix.rst new file mode 100644 index 000000000000..3a9cfba8defc --- /dev/null +++ b/docs/changes/table/17380.bugfix.rst @@ -0,0 +1,3 @@ +Ensure that tables holding coordinates or representations can also be stacked +if they have zero length. This fix also ensures that the ``insert`` method +works correctly with a zero-length table holding a coordinate object. From 6c22fbe1abe052b9758f56a2bbd880db901eb261 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:13:07 -0500 Subject: [PATCH 031/145] Backport PR #17394: Fix typos in quantity tests --- astropy/units/tests/test_quantity_non_ufuncs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/astropy/units/tests/test_quantity_non_ufuncs.py b/astropy/units/tests/test_quantity_non_ufuncs.py index d52751bfbb52..bf2b08571724 100644 --- a/astropy/units/tests/test_quantity_non_ufuncs.py +++ b/astropy/units/tests/test_quantity_non_ufuncs.py @@ -1283,7 +1283,7 @@ def test_nanstd(self): @pytest.mark.parametrize( "out_init", [ - pytest.param(u.Quantity(-1, "m"), id="out with coorect unit"), + pytest.param(u.Quantity(-1, "m"), id="out with correct unit"), # this should work too: out.unit will be overridden pytest.param(u.Quantity(-1), id="out with a different unit"), ], @@ -1309,7 +1309,7 @@ def test_nanvar(self): @pytest.mark.parametrize( "out_init", [ - pytest.param(u.Quantity(-1, "m"), id="out with coorect unit"), + pytest.param(u.Quantity(-1, "m"), id="out with correct unit"), # this should work too: out.unit will be overridden pytest.param(u.Quantity(-1), id="out with a different unit"), ], From 96e331b8f3881c0f099ea1b75d40ed797991ae71 Mon Sep 17 00:00:00 2001 From: Chris Simpson Date: Mon, 18 Nov 2024 08:39:02 -1000 Subject: [PATCH 032/145] Backport PR #17402: Fix loss of mask sigma clipping (issue #17401) --- astropy/stats/sigma_clipping.py | 2 +- astropy/stats/tests/test_sigma_clipping.py | 9 +++++++++ docs/changes/stats/17402.bugfix.rst | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 docs/changes/stats/17402.bugfix.rst diff --git a/astropy/stats/sigma_clipping.py b/astropy/stats/sigma_clipping.py index 334c16a8f781..1acfac559477 100644 --- a/astropy/stats/sigma_clipping.py +++ b/astropy/stats/sigma_clipping.py @@ -462,7 +462,7 @@ def _sigmaclip_withaxis( # float array type is needed to insert nans into the array filtered_data = data.astype(np.float32) # also makes a copy else: - filtered_data = np.copy(data) + filtered_data = data.copy() # remove invalid values bad_mask = ~np.isfinite(filtered_data) diff --git a/astropy/stats/tests/test_sigma_clipping.py b/astropy/stats/tests/test_sigma_clipping.py index 886e0aa140e3..a77fa96c115a 100644 --- a/astropy/stats/tests/test_sigma_clipping.py +++ b/astropy/stats/tests/test_sigma_clipping.py @@ -696,3 +696,12 @@ def nan_mad_std(data, axis=None): ) assert_allclose(result1, result2) + + +@pytest.mark.skipif(not HAS_SCIPY, reason="test requires scipy") +def test_propagation_of_mask(): + # Quick test to check that the mask is propagated correctly + x = np.array([1, 1, 1, 1, 1, 1, 1, 1, 5, 5]).astype(float) + y = np.ma.masked_where(x > 1, x) + + assert_allclose(sigma_clipped_stats(y, grow=1), (1, 1, 0)) diff --git a/docs/changes/stats/17402.bugfix.rst b/docs/changes/stats/17402.bugfix.rst new file mode 100644 index 000000000000..88cd3c073493 --- /dev/null +++ b/docs/changes/stats/17402.bugfix.rst @@ -0,0 +1,2 @@ +Fix an issue in sigma-clipping where the use of ``np.copy()`` was causing +the input data mask to be discarded in cases where ``grow`` was set. From a7b9f6ce48b2c8c269d77b237d9708b616639d1f Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Tue, 19 Nov 2024 12:36:00 +0100 Subject: [PATCH 033/145] Backport PR #17410: Add sigma_clip tests for MaskedArray masks --- astropy/stats/tests/test_sigma_clipping.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/astropy/stats/tests/test_sigma_clipping.py b/astropy/stats/tests/test_sigma_clipping.py index a77fa96c115a..5887abdadd16 100644 --- a/astropy/stats/tests/test_sigma_clipping.py +++ b/astropy/stats/tests/test_sigma_clipping.py @@ -98,6 +98,24 @@ def test_sigma_clip_scalar_mask(): assert result.mask.shape != () +def test_sigma_clip_masked_array(): + """ + Regression test to check that MaskedArray masks are propagated + correctly if cenfunc/stdfunc is not the default, axis is not None, + and masked=False. + """ + arr = np.ones(10).astype(float) + arr[-2:] = 5 + arr = np.ma.masked_where(arr > 1, arr) + out = sigma_clip(arr, cenfunc=np.mean, axis=0, masked=False) + assert np.all(np.isnan(out[-2:])) + assert_allclose(np.nanmean(out), 1.0) + + out = sigma_clip(arr, stdfunc=np.std, axis=0, masked=False) + assert np.all(np.isnan(out[-2:])) + assert_allclose(np.nanmean(out), 1.0) + + def test_sigma_clip_class(): with NumpyRNGContext(12345): data = np.random.randn(100) From e3757049442505aacc26e3b2745c093bba393368 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Tue, 19 Nov 2024 12:38:39 +0100 Subject: [PATCH 034/145] Backport PR #17336: Update docs to reflect default `Table.meta` is now `dict` --- docs/table/access_table.rst | 2 +- docs/table/construct_table.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/table/access_table.rst b/docs/table/access_table.rst index 299c373c1c63..45f753ed45e5 100644 --- a/docs/table/access_table.rst +++ b/docs/table/access_table.rst @@ -178,7 +178,7 @@ Accessing Properties The code below shows accessing the table columns as a |TableColumns| object, getting the column names, table metadata, and number of table rows. The table -metadata is an `~collections.OrderedDict` by default. +metadata is a :class:`dict` by default. :: >>> t.columns diff --git a/docs/table/construct_table.rst b/docs/table/construct_table.rst index 04baa5bae78c..b77746877f02 100644 --- a/docs/table/construct_table.rst +++ b/docs/table/construct_table.rst @@ -720,10 +720,10 @@ meta ---- The ``meta`` argument is an object that contains metadata associated with the -table. It is recommended that this object be a :class:`dict` or -:class:`~collections.OrderedDict`, but the only firm requirement is that it can +table. It is recommended that this object be a :class:`dict`, but the +only firm requirement is that it *must be a dict-like mapping* and can be copied with the standard library :func:`copy.deepcopy` routine. By -default, ``meta`` is an empty :class:`~collections.OrderedDict`. +default, ``meta`` is an empty :class:`dict`. copy ---- From 5c34b6dc94dd82bd7c2cb2bc2cfd811ab7457b92 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:44:29 -0500 Subject: [PATCH 035/145] Backport PR #17417: Fix grammatical error in fitting docstrings --- astropy/modeling/fitting.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/astropy/modeling/fitting.py b/astropy/modeling/fitting.py index 9983ebbed6bb..9ca26ae2b71a 100644 --- a/astropy/modeling/fitting.py +++ b/astropy/modeling/fitting.py @@ -1134,7 +1134,7 @@ class _NonLinearLSQFitter(metaclass=_FitterMeta): If the covariance matrix should be computed and set in the fit_info. Default: False use_min_max_bounds : bool - If the set parameter bounds for a model will be enforced each given + If set, the parameter bounds for a model will be enforced for each given parameter while fitting via a simple min/max condition. Default: True """ @@ -1555,7 +1555,7 @@ class _NLLSQFitter(_NonLinearLSQFitter): If the covariance matrix should be computed and set in the fit_info. Default: False use_min_max_bounds: bool - If the set parameter bounds for a model will be enforced each given + If set, the parameter bounds for a model will be enforced for each given parameter while fitting via a simple min/max condition. A True setting will replicate how LevMarLSQFitter enforces bounds. Default: False @@ -2161,7 +2161,7 @@ def fitter_to_model_params_array( fps : The fit parameter values to be assigned use_min_max_bounds: bool - If the set parameter bounds for model will be enforced on each + If set, the parameter bounds for the model will be enforced on each parameter with bounds. Default: True """ @@ -2235,7 +2235,7 @@ def fitter_to_model_params(model, fps, use_min_max_bounds=True): fps : The fit parameter values to be assigned use_min_max_bounds: bool - If the set parameter bounds for model will be enforced on each + If set, the parameter bounds for the model will be enforced on each parameter with bounds. Default: True """ From e0fdbad545c737a3cb634ac0f318c84671afd53c Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Thu, 21 Nov 2024 19:49:48 +0100 Subject: [PATCH 036/145] Finalizing changelog for v7.0.0 --- CHANGES.rst | 718 +++++++++++++++++++ docs/changes/16308.other.rst | 2 - docs/changes/16557.other.rst | 1 - docs/changes/16561.other.rst | 4 - docs/changes/16903.other.rst | 6 - docs/changes/config/17118.feature.rst | 3 - docs/changes/coordinates/16831.feature.rst | 5 - docs/changes/coordinates/16833.api.rst | 4 - docs/changes/coordinates/17009.feature.rst | 2 - docs/changes/coordinates/17016.feature.rst | 5 - docs/changes/coordinates/17046.api.rst | 2 - docs/changes/coordinates/17252.api.rst | 2 - docs/changes/cosmology/16597.api.rst | 1 - docs/changes/cosmology/16730.api.rst | 2 - docs/changes/cosmology/16847.api.rst | 5 - docs/changes/io.ascii/15758.api.rst | 17 - docs/changes/io.ascii/15774.bugfix.rst | 2 - docs/changes/io.ascii/16840.perf.rst | 5 - docs/changes/io.ascii/16930.feature.rst | 2 - docs/changes/io.fits/15474.api.rst | 6 - docs/changes/io.fits/15474.bugfix.rst | 11 - docs/changes/io.fits/15781.api.rst | 8 - docs/changes/io.fits/17097.feature.rst | 2 - docs/changes/io.fits/17099.bugfix.rst | 1 - docs/changes/io.fits/17100.api.rst | 4 - docs/changes/io.fits/17155.api.rst | 4 - docs/changes/io.fits/17209.perf.rst | 1 - docs/changes/io.fits/17237.bugfix.rst | 2 - docs/changes/io.misc/16375.api.rst | 2 - docs/changes/io.misc/16955.feature.rst | 2 - docs/changes/io.votable/15959.api.rst | 5 - docs/changes/io.votable/16488.bugfix.rst | 3 - docs/changes/io.votable/16856.feature.rst | 2 - docs/changes/io.votable/17156.bugfix.rst | 3 - docs/changes/modeling/16670.perf.rst | 4 - docs/changes/modeling/16673.api.rst | 3 - docs/changes/modeling/16673.perf.rst | 1 - docs/changes/modeling/16677.feature.rst | 3 - docs/changes/modeling/16696.feature.rst | 3 - docs/changes/modeling/16770.api.rst | 4 - docs/changes/modeling/16800.feature.rst | 1 - docs/changes/modeling/16829.bugfix.rst | 2 - docs/changes/modeling/16994.api.rst | 3 - docs/changes/modeling/16995.api.rst | 4 - docs/changes/modeling/17033.feature.rst | 4 - docs/changes/modeling/17033.perf.rst | 2 - docs/changes/modeling/17034.perf.rst | 1 - docs/changes/modeling/17248.api.rst | 2 - docs/changes/stats/16967.perf.rst | 4 - docs/changes/stats/17116.api.rst | 2 - docs/changes/stats/17204.bugfix.rst | 4 - docs/changes/stats/17221.feature.rst | 2 - docs/changes/stats/17402.bugfix.rst | 2 - docs/changes/table/15735.bugfix.rst | 1 - docs/changes/table/16250.api.rst | 9 - docs/changes/table/16250.feature.rst | 4 - docs/changes/table/16360.api.rst | 2 - docs/changes/table/16361.feature.rst | 4 - docs/changes/table/16498.api.rst | 2 - docs/changes/table/16498.bugfix.rst | 2 - docs/changes/table/16500.api.rst | 7 - docs/changes/table/17102.api.rst | 3 - docs/changes/table/17165.api.rst | 5 - docs/changes/table/17184.api.rst | 6 - docs/changes/table/17380.bugfix.rst | 3 - docs/changes/table/17385.bugfix.rst | 1 - docs/changes/time/17212.api.rst | 5 - docs/changes/units/15642.api.rst | 2 - docs/changes/units/16087.feature.rst | 3 - docs/changes/units/16343.api.rst | 4 - docs/changes/units/16441.feature.rst | 2 - docs/changes/units/16455.api.rst | 4 - docs/changes/units/16516.feature.rst | 2 - docs/changes/units/16529.bugfix.rst | 1 - docs/changes/units/16613.api.rst | 3 - docs/changes/units/16676.api.rst | 16 - docs/changes/units/16707.api.rst | 2 - docs/changes/units/16729.feature.rst | 1 - docs/changes/units/16742.perf.rst | 1 - docs/changes/units/16749.bugfix.rst | 4 - docs/changes/units/16761.perf.rst | 1 - docs/changes/units/16779.bugfix.rst | 3 - docs/changes/units/16784.api.rst | 2 - docs/changes/units/16788.bugfix.rst | 7 - docs/changes/units/16813.perf.rst | 1 - docs/changes/units/16861.bugfix.rst | 1 - docs/changes/units/16875.api.rst | 2 - docs/changes/units/16892.feature.rst | 5 - docs/changes/units/16958.api.rst | 3 - docs/changes/units/17004.perf.rst | 2 - docs/changes/units/17043.perf.rst | 2 - docs/changes/units/17059.feature.rst | 2 - docs/changes/units/17120.feature.rst | 2 - docs/changes/units/17121.api.rst | 2 - docs/changes/units/17122.api.rst | 7 - docs/changes/units/17125.feature.rst | 3 - docs/changes/units/17128.feature.rst | 3 - docs/changes/units/17130.feature.rst | 3 - docs/changes/units/17178.api.rst | 2 - docs/changes/units/17232.bugfix.rst | 6 - docs/changes/units/17246.api.rst | 4 - docs/changes/units/17355.bugfix.rst | 3 - docs/changes/utils/16187.api.rst | 5 - docs/changes/utils/16187.bugfix.rst | 1 - docs/changes/utils/16335.feature.rst | 2 - docs/changes/utils/16346.api.rst | 2 - docs/changes/utils/16463.api.rst | 1 - docs/changes/utils/16479.api.rst | 2 - docs/changes/utils/16503.bugfix.rst | 3 - docs/changes/utils/16807.api.rst | 2 - docs/changes/utils/16844.api.rst | 4 - docs/changes/utils/16931.feature.rst | 2 - docs/changes/utils/17014.bugfix.rst | 4 - docs/changes/utils/17156.bugfix.rst | 3 - docs/changes/utils/17190.api.rst | 1 - docs/changes/utils/17387.bugfix.rst | 2 - docs/changes/visualization/15081.feature.rst | 5 - docs/changes/visualization/15751.api.rst | 5 - docs/changes/visualization/15781.api.rst | 1 - docs/changes/visualization/16347.feature.rst | 2 - docs/changes/visualization/16366.perf.rst | 4 - docs/changes/visualization/16685.api.rst | 26 - docs/changes/visualization/16686.feature.rst | 2 - docs/changes/visualization/16938.feature.rst | 2 - docs/changes/visualization/16985.feature.rst | 3 - docs/changes/visualization/17006.feature.rst | 2 - docs/changes/visualization/17020.feature.rst | 1 - docs/changes/visualization/17186.api.rst | 1 - docs/changes/visualization/17217.feature.rst | 1 - docs/changes/visualization/17243.feature.rst | 1 - docs/changes/wcs/16287.api.rst | 3 - docs/changes/wcs/16328.api.rst | 3 - docs/changes/wcs/17236.bugfix.rst | 2 - 133 files changed, 718 insertions(+), 440 deletions(-) delete mode 100644 docs/changes/16308.other.rst delete mode 100644 docs/changes/16557.other.rst delete mode 100644 docs/changes/16561.other.rst delete mode 100644 docs/changes/16903.other.rst delete mode 100644 docs/changes/config/17118.feature.rst delete mode 100644 docs/changes/coordinates/16831.feature.rst delete mode 100644 docs/changes/coordinates/16833.api.rst delete mode 100644 docs/changes/coordinates/17009.feature.rst delete mode 100644 docs/changes/coordinates/17016.feature.rst delete mode 100644 docs/changes/coordinates/17046.api.rst delete mode 100644 docs/changes/coordinates/17252.api.rst delete mode 100644 docs/changes/cosmology/16597.api.rst delete mode 100644 docs/changes/cosmology/16730.api.rst delete mode 100644 docs/changes/cosmology/16847.api.rst delete mode 100644 docs/changes/io.ascii/15758.api.rst delete mode 100644 docs/changes/io.ascii/15774.bugfix.rst delete mode 100644 docs/changes/io.ascii/16840.perf.rst delete mode 100644 docs/changes/io.ascii/16930.feature.rst delete mode 100644 docs/changes/io.fits/15474.api.rst delete mode 100644 docs/changes/io.fits/15474.bugfix.rst delete mode 100644 docs/changes/io.fits/15781.api.rst delete mode 100644 docs/changes/io.fits/17097.feature.rst delete mode 100644 docs/changes/io.fits/17099.bugfix.rst delete mode 100644 docs/changes/io.fits/17100.api.rst delete mode 100644 docs/changes/io.fits/17155.api.rst delete mode 100644 docs/changes/io.fits/17209.perf.rst delete mode 100644 docs/changes/io.fits/17237.bugfix.rst delete mode 100644 docs/changes/io.misc/16375.api.rst delete mode 100644 docs/changes/io.misc/16955.feature.rst delete mode 100644 docs/changes/io.votable/15959.api.rst delete mode 100644 docs/changes/io.votable/16488.bugfix.rst delete mode 100644 docs/changes/io.votable/16856.feature.rst delete mode 100644 docs/changes/io.votable/17156.bugfix.rst delete mode 100644 docs/changes/modeling/16670.perf.rst delete mode 100644 docs/changes/modeling/16673.api.rst delete mode 100644 docs/changes/modeling/16673.perf.rst delete mode 100644 docs/changes/modeling/16677.feature.rst delete mode 100644 docs/changes/modeling/16696.feature.rst delete mode 100644 docs/changes/modeling/16770.api.rst delete mode 100644 docs/changes/modeling/16800.feature.rst delete mode 100644 docs/changes/modeling/16829.bugfix.rst delete mode 100644 docs/changes/modeling/16994.api.rst delete mode 100644 docs/changes/modeling/16995.api.rst delete mode 100644 docs/changes/modeling/17033.feature.rst delete mode 100644 docs/changes/modeling/17033.perf.rst delete mode 100644 docs/changes/modeling/17034.perf.rst delete mode 100644 docs/changes/modeling/17248.api.rst delete mode 100644 docs/changes/stats/16967.perf.rst delete mode 100644 docs/changes/stats/17116.api.rst delete mode 100644 docs/changes/stats/17204.bugfix.rst delete mode 100644 docs/changes/stats/17221.feature.rst delete mode 100644 docs/changes/stats/17402.bugfix.rst delete mode 100644 docs/changes/table/15735.bugfix.rst delete mode 100644 docs/changes/table/16250.api.rst delete mode 100644 docs/changes/table/16250.feature.rst delete mode 100644 docs/changes/table/16360.api.rst delete mode 100644 docs/changes/table/16361.feature.rst delete mode 100644 docs/changes/table/16498.api.rst delete mode 100644 docs/changes/table/16498.bugfix.rst delete mode 100644 docs/changes/table/16500.api.rst delete mode 100644 docs/changes/table/17102.api.rst delete mode 100644 docs/changes/table/17165.api.rst delete mode 100644 docs/changes/table/17184.api.rst delete mode 100644 docs/changes/table/17380.bugfix.rst delete mode 100644 docs/changes/table/17385.bugfix.rst delete mode 100644 docs/changes/time/17212.api.rst delete mode 100644 docs/changes/units/15642.api.rst delete mode 100644 docs/changes/units/16087.feature.rst delete mode 100644 docs/changes/units/16343.api.rst delete mode 100644 docs/changes/units/16441.feature.rst delete mode 100644 docs/changes/units/16455.api.rst delete mode 100644 docs/changes/units/16516.feature.rst delete mode 100644 docs/changes/units/16529.bugfix.rst delete mode 100644 docs/changes/units/16613.api.rst delete mode 100644 docs/changes/units/16676.api.rst delete mode 100644 docs/changes/units/16707.api.rst delete mode 100644 docs/changes/units/16729.feature.rst delete mode 100644 docs/changes/units/16742.perf.rst delete mode 100644 docs/changes/units/16749.bugfix.rst delete mode 100644 docs/changes/units/16761.perf.rst delete mode 100644 docs/changes/units/16779.bugfix.rst delete mode 100644 docs/changes/units/16784.api.rst delete mode 100644 docs/changes/units/16788.bugfix.rst delete mode 100644 docs/changes/units/16813.perf.rst delete mode 100644 docs/changes/units/16861.bugfix.rst delete mode 100644 docs/changes/units/16875.api.rst delete mode 100644 docs/changes/units/16892.feature.rst delete mode 100644 docs/changes/units/16958.api.rst delete mode 100644 docs/changes/units/17004.perf.rst delete mode 100644 docs/changes/units/17043.perf.rst delete mode 100644 docs/changes/units/17059.feature.rst delete mode 100644 docs/changes/units/17120.feature.rst delete mode 100644 docs/changes/units/17121.api.rst delete mode 100644 docs/changes/units/17122.api.rst delete mode 100644 docs/changes/units/17125.feature.rst delete mode 100644 docs/changes/units/17128.feature.rst delete mode 100644 docs/changes/units/17130.feature.rst delete mode 100644 docs/changes/units/17178.api.rst delete mode 100644 docs/changes/units/17232.bugfix.rst delete mode 100644 docs/changes/units/17246.api.rst delete mode 100644 docs/changes/units/17355.bugfix.rst delete mode 100644 docs/changes/utils/16187.api.rst delete mode 100644 docs/changes/utils/16187.bugfix.rst delete mode 100644 docs/changes/utils/16335.feature.rst delete mode 100644 docs/changes/utils/16346.api.rst delete mode 100644 docs/changes/utils/16463.api.rst delete mode 100644 docs/changes/utils/16479.api.rst delete mode 100644 docs/changes/utils/16503.bugfix.rst delete mode 100644 docs/changes/utils/16807.api.rst delete mode 100644 docs/changes/utils/16844.api.rst delete mode 100644 docs/changes/utils/16931.feature.rst delete mode 100644 docs/changes/utils/17014.bugfix.rst delete mode 100644 docs/changes/utils/17156.bugfix.rst delete mode 100644 docs/changes/utils/17190.api.rst delete mode 100644 docs/changes/utils/17387.bugfix.rst delete mode 100644 docs/changes/visualization/15081.feature.rst delete mode 100644 docs/changes/visualization/15751.api.rst delete mode 100644 docs/changes/visualization/15781.api.rst delete mode 100644 docs/changes/visualization/16347.feature.rst delete mode 100644 docs/changes/visualization/16366.perf.rst delete mode 100644 docs/changes/visualization/16685.api.rst delete mode 100644 docs/changes/visualization/16686.feature.rst delete mode 100644 docs/changes/visualization/16938.feature.rst delete mode 100644 docs/changes/visualization/16985.feature.rst delete mode 100644 docs/changes/visualization/17006.feature.rst delete mode 100644 docs/changes/visualization/17020.feature.rst delete mode 100644 docs/changes/visualization/17186.api.rst delete mode 100644 docs/changes/visualization/17217.feature.rst delete mode 100644 docs/changes/visualization/17243.feature.rst delete mode 100644 docs/changes/wcs/16287.api.rst delete mode 100644 docs/changes/wcs/16328.api.rst delete mode 100644 docs/changes/wcs/17236.bugfix.rst diff --git a/CHANGES.rst b/CHANGES.rst index 87be776a98fc..547ef2b63d0e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,721 @@ +Version 7.0.0 (2024-11-21) +========================== + + +New Features +------------ + +astropy.config +^^^^^^^^^^^^^^ + +- Added ``get_config_dir_path`` (and ``get_cache_dir_path``) which is equivalent + to ``get_config_dir`` (respectively ``get_cache_dir``) except that it returns a + ``pathlib.Path`` object instead of ``str``. [#17118] + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- ``BaseCoordinateFrame`` instances such as ``ICRS``, ``SkyOffsetFrame``, etc., + can now be stored directly in tables (previously, they were stored as + ``object`` type columns). Furthermore, storage in tables is now also possible + for frames that have no data (but which have attributes with the correct shape + to fit in the table). [#16831] + +- ``BaseCoordinateFrame`` now has a ``to_table()`` method, which converts the + frame to a ``QTable``, analogously to the ``SkyCoord.to_table()`` method. [#17009] + +- ``SkyCoord``, coordinate frames, and representations have all have gained the + ability to deal with ``Masked`` data. In general, the procedure is similar to + that of ``Time``, except that different representation components do not share + the mask, to enable holding, e.g., a catalogue of objects in which only some + have associated distances. [#17016] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Add support for ``pathlib.Path`` objects in + ``astropy.io.ascii.core.BaseInputter.get_lines``. [#16930] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Expanded ``FITSDiff`` output for ``PrimaryHDU`` and ``ImageHDU`` to include the + maximum relative and absolute differences in the data. [#17097] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- The HDF5 writer, ``write_table_hdf5()``, now accepts ``os.PathLike`` objects + as ``output``. [#16955] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Support reading and writing of VOTable version 1.5, including the new + ``refposition`` attribute of ``COOSYS``. [#16856] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Added ``Model.has_tied``, ``Model.has_fixed``, and ``Model.has_bounds`` attributes to make + it easy to check whether models have various kinds of constraints set without having to + inspect ``Model.tied``, ``Model.fixed``, and ``Model.bounds`` in detail. [#16677] + +- Added a new ``parallel_fit_dask`` function that can be used to fit models to + many sections (e.g. spectra, image slices) on an N-dimensional array in + parallel. [#16696] + +- Added a ``Lorentz2D`` model. [#16800] + +- Added ``inplace=False/True`` keyword argument to the ``__call__`` method of most fitters, + to optionally allow the original model passed to the fitter to be modified with the fitted + values of the parameters, rather than return a copy. This can improve performance if users + don't need to keep hold of the initial parameter values. [#17033] + +astropy.stats +^^^^^^^^^^^^^ + +- Added a ``SigmaClippedStats`` convenience class for computing sigma-clipped + statistics. [#17221] + +astropy.table +^^^^^^^^^^^^^ + +- Changed a number of dict-like containers in ``io.ascii`` from ``OrderedDict`` to + ``dict``. The ``dict`` class maintains key order since Python 3.8 so ``OrderedDict`` is + no longer needed. The changes are largely internal and should not affect users in any + way. See also the API change log entry for this PR. [#16250] + +- Add a ``keep_order`` argument to the ``astropy.table.join`` function which specifies to + maintain the original order of the key table in the joined output. This applies for + inner, left, and right joins. The default is ``False`` in which case the output is + ordered by the join keys, consistent with prior behavior. [#16361] + +astropy.units +^^^^^^^^^^^^^ + +- Add a ``formatter`` argument to the ``to_string`` method of the ``Quantity`` + class. Enables custom number formatting with a callable formatter or + format_spec, especially useful for consistent notation. [#16087] + +- Add the unit foe (or Bethe, equivalent to 1e51 erg), which is often used to + express the energy emitted by a supernova explosion. [#16441] + +- Add ``magnetic_flux_field`` equivalency to convert magnetic field between + magnetic field strength (H) and magnetic flux density (B). [#16516] + +- Added SI-units ``sievert``, ``gray``, ``katal``, and ``hectare`` in ``astropy.units.si``. [#16729] + +- When parsing invalid unit strings with ``u.Unit(..., parse_strict="warn")`` or + ``u.Unit(..., parse_strict="silent")``, a normal unit may be returned if the + problem is not too serious. + If parsing the string fails completely then an ``UnrecognizedUnit`` instance is + returned, just as before. [#16892] + +- Added a ``np.arange`` dispatch for ``Quantity`` (requires one to use + ``like=``). [#17059] + +- Added support for calling numpy array constructors (``np.empty``, ``np.ones``, + ``np.zeros`` and ``np.full``) with ``like=Quantity(...)`` . [#17120] + +- Added support for calling numpy array constructors (``np.array``, + ``np.asarray``, ``np.asanyarray``, ``np.ascontiguousarray`` and + ``np.asfortranarray``) with ``like=Quantity(...)`` . [#17125] + +- Added support for calling numpy array constructors (``np.frombuffer``, + ``np.fromfile``, ``np.fromiter``, ``np.fromstring`` and ``np.fromfunction``) + with ``like=Quantity(...))`` . [#17128] + +- Added support for calling numpy array constructors (``np.require``, + ``np.identity``, ``np.eye``, ``np.tri``, ``np.genfromtxt`` and ``np.loadtxt``) + with ``like=Quantity(...))`` . [#17130] + +astropy.utils +^^^^^^^^^^^^^ + +- Added the ``astropy.system_info`` function to help document runtime systems in + bug reports. [#16335] + +- Add support for specifying files as ``pathlib.Path`` objects in ``IERS_A.read`` + and ``IERS_B.read``. [#16931] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Add ``make_rgb()``, a convenience + function for creating RGB images with independent scaling on each filter. + Refactors ``make_lupton_rgb()`` to work with instances of subclasses of + ``BaseStretch``, including the new Lupton-specific classes + ``LuptonAsinhStretch`` and ``LuptonAsinhZscaleStretch``. [#15081] + +- Add support for custom coordinate frames for ``WCSAxes`` through a context + manager ``astropy.visualization.wcsaxes.custom_ucd_coord_meta_mapping``. [#16347] + +- Added ``get_ticks_position``, ``get_ticklabel_position``, and + ``get_axislabel_position`` methods on ``CoordinateHelper`` in WCSAxes. [#16686] + +- Added the ability to disable the automatic simplification of WCSAxes tick labels + by specifying ``simplify=False`` to ``set_ticklabel()`` for a coordinate axis. [#16938] + +- Added the ability to specify that WCSAxes tick labels always include the sign + (namely for positive values) by starting the format string with a ``+`` + character. [#16985] + +- Allow ``astropy.visualization.units.quantity_support`` to be used as a + decorator in addition to the already supported use as a context manager. [#17006] + +- Added the ability to specify a callable function in ``CoordinateHelper.set_major_formatter`` [#17020] + +- Added a ``SimpleNorm`` class to create a matplotlib normalization object. [#17217] + +- WCSAxes will now select which axis to draw which tick labels and axis labels on based on the number of drawn tick labels, rather than picking them in the order they are listed in the WCS. This means that axes may be swapped in comparison with previous versions of Astropy by default. [#17243] + + +API Changes +----------- + +astropy.coordinates +^^^^^^^^^^^^^^^^^^^ + +- For non-scalar frames without data, ``len(frame)`` will now return the first + element of its ``shape``, just like for frames with data (or arrays more + generally). For scalar frames, a ``TypeError`` will be raised. Both these + instead of raising a ``ValueError`` stating the frame has no data. [#16833] + +- The deprecated ``coordinates.get_moon()`` function has been removed. Use + ``coordinates.get_body("moon")`` instead. [#17046] + +- The deprecated ``BaseCoordinateFrame.get_frame_attr_names()`` is removed. + Use ``get_frame_attr_defaults()`` instead. [#17252] + +astropy.cosmology +^^^^^^^^^^^^^^^^^ + +- Passing redshift arguments as keywords is deprecated in many methods. [#16597] + +- Deprecated ``cosmology.utils`` module has been removed. Any public API may + be imported directly from the ``cosmology`` module instead. [#16730] + +- Setting ``Ob0 = None`` in FLRW cosmologies has been deprecated in favor of ``Ob0 = + 0.0``. Conceptually this is a change in that baryons are now always a component of the + cosmology. Practically, the only change (besides that ``Ob0`` is never ``None``) is that + methods relying on ``Ob0`` always work, rather than sometimes raising an exception, + instead by default taking the contribution of the baryons to be negligible. [#16847] + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Remove all deprecated arguments from functions within ``astropy.io.ascii``. + + ``read()``: + - ``Reader`` is removed. Instead supply the equivalent ``format`` argument. + - Use ``inputter_cls`` instead of ``Inputter``. + - Use ``outputter_cls`` instead of ``Outputter``. + + ``get_reader()``: + - Use ``reader_cls`` instead of ``Reader``. + - Use ``inputter_cls`` instead of ``Inputter``. + - Use ``outputter_cls`` instead of ``Outputter``. + + ``write()``: + - ``Writer`` is removed. Instead supply the equivalent ``format`` argument. + + ``get_writer()``: + - Use ``writer_cls`` instead of ``Writer``. [#15758] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- The ``CompImageHDU`` class has been refactored to inherit from ``ImageHDU`` + instead of ``BinTableHDU``. This change should be for the most part preserve the + API, but any calls to ``isinstance(hdu, BinTableHDU)`` will now return ``False`` + if ``hdu`` is a ``CompImageHDU`` whereas before it would have returned ``True``. + In addition, the ``uint`` keyword argument to ``CompImageHDU`` now defaults to + ``True`` for consistency with ``ImageHDU``. [#15474] + +- Remove many unintended exports from ``astropy.io.fits.hdu.compressed``. + The low-level functions ``compress_image_data`` and ``decompress_image_data_section`` + are now only available at the qualified names + ``astropy.io.fits.hdu.compressed._tiled_compression.compress_image_data`` + and ``astropy.io.fits.hdu.compressed._tiled_compression.decompress_image_data_section``. + The rest of the removed exports are external modules or properly exported + elsewhere in astropy. May break imports in rare cases that relied + on these exports. [#15781] + +- The ``CompImageHeader`` class is now deprecated, and headers on ``CompImageHDU`` + instances are now plain ``Header`` instances. If a reserved keyword is set on + ``CompImageHDU.header``, a warning will now be emitted at the point where the + file is written rather than at the point where the keyword is set. [#17100] + +- - Remove code that was deprecated in previous versions: ``_ExtensionHDU`` and + ``_NonstandardExtHDU``, ``(Bin)Table.update``, ``tile_size`` argument for + ``CompImageHDU``. Also specifying an invalid ``tile_shape`` now raises an + error. [#17155] + +astropy.io.misc +^^^^^^^^^^^^^^^ + +- New format ``"parquet.votable"`` is added to read and write a parquet file + with a votable metadata included. [#16375] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- ``Table.read(..., format='votable')``, ``votable.parse`` and + ``votable.parse_single_table`` now respect the ``columns`` argument and will only output + selected columns. Previously, unselected columns would just be masked (and unallocated). + ``astropy.io.votable.tree.TableElement.create_arrays`` also gained a ``colnumbers`` + keyword argument to allow column selection. [#15959] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Subclasses of ``_NonLinearLSQFitter``, so any subclasses of the public ``LevMarLSQFitter``, ``TRFLSQFitter``, ``LMLSQFitter`` or ``DogBoxLSQFitter``, should now accept an additional ``fit_param_indices`` kwarg in the function signature of their ``objective_function`` methods. + Nothing is needed to be done with this kwarg, and it might not be set, but it can optionally be passed through to ``fitter_to_model_params_array`` for a performance improvement. + We also recommended accepting all kwargs (with ``**kwargs``) in this method so that future additional kwargs do not cause breakage. [#16673] + +- Exception message for when broadcast shapes mismatch has changed. + Previously, it used complicated regex to maintain backward compatibility. + To ease maintenance, this regex has been removed and now directly + passes exception from ``numpy.broadcast_shapes`` function. [#16770] + +- Using the ``LMLSQFitter`` fitter with models that have bounds is now deprecated, + as support for bounds was very basic. Instead, non-linear fitters with more + sophisticated support for bounds should be used instead. [#16994] + +- The optional ``use_min_max_bounds`` keyword argument in ``TRFLSQFitter`` and + ``DogBoxLSQFitter`` has now been deprecated and should not be used. These + fitters handle bounds correctly by default and this keyword argument was only + provided to opt-in to a more basic form of bounds handling. [#16995] + +- The deprecated ``comb()`` function has been removed. + Use ``math.comb()`` from the Python standard library instead. [#17248] + +astropy.stats +^^^^^^^^^^^^^ + +- Integer inputs to ``sigma_clip`` and ``SigmaClip`` are not converted to + ``np.float32`` instead of ``float`` if necessary. [#17116] + +astropy.table +^^^^^^^^^^^^^ + +- Change the default type for the ``meta`` attribute in ``Table`` and ``Column`` (and + subclasses) from ``OrderedDict`` to ``dict``. Since Python 3.8 the ``dict`` class is + ordered by default, so there is no need to use ``OrderedDict``. + + In addition the ECSV table writer in ``astropy.io.ascii`` was updated to consistently + write the ``meta`` attribute as an ordered map using the ``!!omap`` tag. This + convention conforms to the ECSV specification and is supported by existing ECSV readers. + Previously the ``meta`` attribute could be written as an ordinary YAML map, which is not + guaranteed to preserve the order of the keys. [#16250] + +- An exception is now raised when trying to add a multi-dimensional column as an + index via ``Table.add_index``. [#16360] + +- Aggregating table groups for ``MaskedColumn`` no longer converts + fully masked groups to ``NaN``, but instead returns a masked element. [#16498] + +- Always use ``MaskedQuantity`` in ``QTable`` to represent masked ``Quantity`` + data or when the ``QTable`` is created with ``masked=True``. Previously the + default was to use a normal ``Quantity`` with a ``mask`` attribute of type + ``FalseArray`` as a stub to allow a minimal level of compatibility for certain + operations. This update brings more consistent behavior and fixes functions + like reading of table data from a list of dict that includes quantities with + missing entries, and aggregation of ``MaskedQuantity`` in table groups. [#16500] + +- Setting an empty table to a scalar no longer raises an exception, but + creates an empty column. This is to support cases where the number of + elements in a table is not known in advance, and could be zero. [#17102] + +- ``show_in_notebook`` method for Astropy tables has been un-deprecated and the API has + been updated to accept a ``backend`` keyword and require only keyword arguments. The new + default ``backend="ipydatagrid"`` relies on an optional dependency, ``ipydatagrid``. The + previous default table viewer (prior to v7.0) is still available as + ``backend="classic"``, but it has been deprecated since v6.1 and will be removed in a future release. [#17165] + +- The default behavior of ``Table.pformat`` was changed to include all rows and columns + instead of truncating the outputs to fit the current terminal. The new default + keyword arguments ``max_width=-1`` and ``max_lines=-1`` now match those in + ``Table.pformat_all``. Since the ``Table.pformat_all`` method is now redundant, it is + pending deprecation. Similarly, the default behavior of ``Column.pformat`` was changed + to include all rows instead of truncating the outputs to fit the current terminal. [#17184] + +astropy.time +^^^^^^^^^^^^ + +- ``Time.ptp`` now properly emits a deprecation warning independently of NumPy's + version. This method was previously deprecated in astropy 6.1, but the warning + was not visible for users that had NumPy 1.x installed. Because of this, the + warning message was updated to state that ``Time.ptp`` is deprecated since + version 7.0 instead. [#17212] + +astropy.units +^^^^^^^^^^^^^ + +- The deprecated ``Quantity.nansum()`` method has been removed. Use + ``np.nansum`` instead. [#15642] + +- The ``factor`` parameter of the ``spectral_density`` equivalency, the use of + which has been discouraged in the documentation since version 0.3, is now + deprecated. + Use the ``wav`` parameter as a ``Quantity``, not as a bare unit. [#16343] + +- The ``format.Fits`` formatter class has been renamed to ``format.FITS`` and the + old name is deprecated. + Specifying the FITS format for converting ``Quantity`` and ``UnitBase`` + instances to and from strings is not affected by this change. [#16455] + +- Conversion from one unit to another using ``old_unit.to(new_unit, value)`` no longer + converts ``value`` automatically to a numpy array, but passes through array duck types + such as ``dask`` arrays, with equivalencies properly accounted for. [#16613] + +- The ``format_exponential_notation()`` method of the ``Base`` unit formatter has + changed. + Any unit formatters that inherit directly from ``Base`` but have not + implemented their own ``format_exponential_notation()`` and wish to retain + previous behavior should implement it as: + + .. code-block:: python + + def format_exponential_notation(cls, val, format_spec): + return format(val, format_spec) + + Any formatters that inherit directly from ``Base`` and call + ``super().format_exponential_notation(val, format_spec)`` should instead call + ``format(val, format_spec)`` + The specific unit formatters in ``astropy.units`` and custom formatters that + inherit from any of them are not affected. [#16676] + +- The deprecated ``units.format.Unscaled`` has been removed. Use ``units.format.Generic`` + instead. [#16707] + +- Added a __round__() dunder method to ``Quantity`` + in order to support the built-in round() function. [#16784] + +- For ``Masked`` initialization in which a mask is passed in, ensure that that + mask is combined with any mask present on the input. [#16875] + +- The ``get_format_name()`` method of ``NamedUnit`` and its subclasses is + deprecated. + The ``to_string()`` method can be used instead. [#16958] + +- The ``UnitBase.in_units()`` method is deprecated. + The ``to()`` method can be used as a drop-in replacement. [#17121] + +- Unit conversions to a given system with ``unit.to_system()``, + ``unit.si``, and ``unit.cgs``, will now prefer the simplest unit if it + is in the given system, rather than prioritizing more complicated + units if those had a base unit component. E.g., ``u.Pa.si`` will now + simply return ``Unit("Pa")`` rather than ``Unit("N / m2")``. However, + the case where a unit can be simply described in base units remains + unchanged: ``u.Gal.cgs`` will still give ``Unit("cm / s2")``. [#17122] + +- The ``CDS``, ``OGIP`` and ``VOUnit`` unit formatters are now subclasses of the + ``FITS`` unit formatter. [#17178] + +- The ``eV`` and ``rydberg`` units were moved to ``astropy.units.misc`` (from + ``astropy.units.si`` and ``astropy.units.astrophys``, respectively). + Practically, this means that ``Unit.to_system(u.si)`` no longer includes + ``eV`` as a SI-compatible unit. [#17246] + +astropy.utils +^^^^^^^^^^^^^ + +- ``IERS_Auto.open()`` now always returns a table of type ``IERS_Auto`` that + contains the combination of IERS-A and IERS-B data, even if automatic + updating of the IERS-A file is disabled or if downloading the new file fails. + Previously, under those conditions, it would return a table of a different type + (``IERS_B``) with only IERS-B data. [#16187] + +- ``astropy.utils.check_broadcast`` is now deprecated in favor of + ``numpy.broadcast_shapes`` [#16346] + +- Added a new keyword ``pending_warning_type`` to ``deprecated`` decorator so downstream developers could customize the type of warning for pending deprecation state. [#16463] + +- The ``introspection.resolve_name()`` function is deprecated. + It is better to use the standard library ``importlib`` instead. [#16479] + +- ``format_exception()`` is deprecated because it provides little benefit, if + any, over normal Python tracebacks. [#16807] + +- The ``utils.masked`` module has gained a mixin class, ``MaskableShapedLikeNDArray``, + as well as two utility functions, ``get_data_and_mask`` and ``combine_masks``, + that can help make a container classes carry masked data. Within astropy, these + are now used in the implementation of masks for ``Time``. [#16844] + +- The deprecated ``compat.override__dir__()`` utility has been removed. [#17190] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Removed deprecated ``exp`` attribute in the ``LogStretch``, + ``InvertedLogStretch``, ``PowerDistStretch``, and + ``InvertedPowerDistStretch`` stretch classes, and the ``power`` + attribute in the ``PowerStretch``. Instead, use the ``a`` attribute, + which matches the input keyword. [#15751] + +- Removes the unintended NumPy export previously at ``astropy.visualization.np``. [#15781] + +- Accessing or setting the following attributes on ``CoordinateHelper`` has been deprecated: + + * ``ticks`` + * ``ticklabels`` + * ``axislabels`` + + Setting the following attributes on ``CoordinateHelper`` directly has been deprecated: + + * ``parent_axes`` + * ``parent_map`` + * ``transform`` + * ``coord_index`` + * ``coord_unit`` + * ``coord_type`` (use ``set_coord_type`` instead) + * ``coord_wrap`` (use ``set_coord_type`` instead) + * ``frame`` + * ``default_label`` + + Accessing or setting the following attributes on ``CoordinateHelper`` has been + removed (without deprecation, as these were clearly internal variables): + + * ``grid_lines_kwargs`` + * ``grid_lines`` + * ``lblinfo`` + * ``lbl_world`` + * ``minor_frequency`` (there were already public methods to set/get this) [#16685] + +- The deprecated ``nsamples`` parameter of ``ZScaleInterval`` is removed. [#17186] + +astropy.wcs +^^^^^^^^^^^ + +- Errors may now occur if a ``BaseLowLevelWCS`` class defines + ``world_axis_object_components`` which returns values that are not scalars or + plain Numpy arrays as per APE 14. [#16287] + +- ``WCS.pixel_to_world_values``, ``WCS.world_to_pixel_values``, + ``WCS.pixel_to_world`` and ``WCS.world_to_pixel`` now correctly return NaN values for + pixel positions that are outside of ``pixel_bounds``. [#16328] + + +Bug Fixes +--------- + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- Fix the broken behavior of reading an ASCII table and filling values using column names. + This PR addresses the issue and improves the functionality. [#15774] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Fix a number of bugs in ``CompImageHDU``: + + * Fix the ability to pickle ``CompImageHDU`` objects + * Ensure that compression settings are not lost if initializing ``CompImageHDU`` + without data but with compression settings and setting the data later + * Make sure that keywords are properly updated when setting the header of a + ``CompImageHDU`` to an existing image header. + * Fix the ability to use ``CompImageHDU.section`` on instances that have not yet + been written to disk + * Fix the image checksum/datasum in ``CompImageHDU.header`` to be those for the + image HDU instead of for the underlying binary table. [#15474] + +- Fix a spurious exception when reading integer compressed images with blanks. [#17099] + +- Fix creating ``CompImageHDU`` from header with BSCALE/BZERO: keywords are now + ignored, as done in ``ImageHDU``. [#17237] + +astropy.io.votable +^^^^^^^^^^^^^^^^^^ + +- Making the "votable.parquet" format available as a reader format to ensure + consistency with the writer formats, even though the format it recognised + automatically by "votable". [#16488] + +- Explicitly set ``usedforsecurity=False`` when using ``hashlib.md5``. Without this, ``hashlib.md5`` will be blocked in FIPS mode. + FIPS (Federal Information Processing Standards) is a set of standards created by NIST (National Institute of Standards and Technology) for US government agencies regarding computer security and interoperability. + This affects validation results ingestion. [#17156] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Fixed the output representation of models with parameters that have + units of ``dimensionless_unscaled``. [#16829] + +astropy.stats +^^^^^^^^^^^^^ + +- Fixed accuracy of sigma clipping for large ``float32`` arrays when + ``bottleneck`` is installed. Performance may be impacted for computations + involving arrays with dtype other than ``float64``. This change has no impact + for environments that do not have ``bottleneck`` installed. [#17204] + +- Fix an issue in sigma-clipping where the use of ``np.copy()`` was causing + the input data mask to be discarded in cases where ``grow`` was set. [#17402] + +astropy.table +^^^^^^^^^^^^^ + +- Fix a bug where column names would be lost when instantiating ``Table`` from a list of ``Row`` objects. [#15735] + +- Aggregating table groups for ``MaskedColumn`` now ensures that fully-masked + groups result in masked elements rather than ``NaN``. [#16498] + +- Ensure that tables holding coordinates or representations can also be stacked + if they have zero length. This fix also ensures that the ``insert`` method + works correctly with a zero-length table holding a coordinate object. [#17380] + +- Fixed table aggregate with empty columns when float is present. [#17385] + +astropy.units +^^^^^^^^^^^^^ + +- Allow SI-prefixes for radioactivity units ``becquerel`` and ``curie`` in ``astropy.units.si``, conforming to BIPM's guidelines for SI units. [#16529] + +- The OGIP unit parser no longer accepts strings where a component unit is + followed by a parenthesized unit without a separator in between, such as + ``'m(s)'`` or ``'m(s)**2'``. + Such strings are not allowed by the OGIP standard. [#16749] + +- A few edge cases that could result in a power of a unit to be a numerical value + from ``numpy``, instead of the intended Python ``int``, ``float`` or + ``fractions.Fraction`` instance, have been fixed. [#16779] + +- The OGIP unit parser now detects negative powers that are not enclosed in + parenthesis. + For example, ``u.Unit("s**-1", format="ogip")`` now raises an error because the + OGIP standard expects the string to be written as ``"s**(-1)"`` instead, but it + is still possible to parse the unit with + ``u.Unit("s**-1", format="ogip", parse_strict="warn")`` or + ``parse_strict="silent"``. [#16788] + +- ``UnitScaleError`` can now be imported from the ``astropy.units`` namespace. [#16861] + +- Parsing custom units with ``u.Unit()`` using the ``"vounit"`` format now obeys + the ``parse_strict`` parameter, unless the custom units are made explicit with + quotation marks. + For example, ``u.Unit("custom_unit", format="vounit")`` now raises an error, + but ``u.Unit("custom_unit", format="vounit", parse_strict="silent")`` or + ``u.Unit("'custom_unit'", format="vounit")`` do not. [#17232] + +- It is now possible to use ``Unit`` to create dimensionless units with a scale + factor that is a complex number or a ``fractions.Fraction`` instance. + It was already possible to create such units directly with ``CompositeUnit``. [#17355] + +astropy.utils +^^^^^^^^^^^^^ + +- Fixed the unintended behavior where the IERS-A file bundled in ``astropy-iers-data`` would be ignored if automatic updating of the IERS-A file were disabled or if downloading the new file failed. [#16187] + +- Ensure ``MaskedQuantity`` can be initialized with a list of masked + quantities (as long as their shapes match), just like regular + ``Quantity`` and ``ndarray``. [#16503] + +- For ``Masked`` instances, ``np.put``, ``np.putmask``, ``np.place`` and + ``np.copyto`` can now handle putting/copying not just ``np.ma.masked`` but + also ``np.ma.nomask``; for both cases, only the mask of the relevant entries + will be set. [#17014] + +- Explicitly set ``usedforsecurity=False`` when using ``hashlib.md5``. Without this, ``hashlib.md5`` will be blocked in FIPS mode. + FIPS (Federal Information Processing Standards) is a set of standards created by NIST (National Institute of Standards and Technology) for US government agencies regarding computer security and interoperability. + This affects download caching. [#17156] + +- Fixed a bug where an old IERS-A table with stale predictive values could trigger + the download of a new IERS-A table even if automatic downloading was disabled. [#17387] + +astropy.wcs +^^^^^^^^^^^ + +- Avoid a ``RuntimeWarning`` in ``WCS.world_to_array_index`` by converting + NaN inputs to int. [#17236] + + +Performance Improvements +------------------------ + +astropy.io.ascii +^^^^^^^^^^^^^^^^ + +- The performance of guessing the table format when reading large files with + ``astropy.io.ascii`` has been improved. Now the process uses at most + 10000 lines of the file to check if it matches the format. This behavior can + be configured using the ``astropy.io.ascii.conf.guess_limit_lines`` + configuration item, including disabling the limit entirely. [#16840] + +astropy.io.fits +^^^^^^^^^^^^^^^ + +- Optimize checksum computation. [#17209] + +astropy.modeling +^^^^^^^^^^^^^^^^ + +- Improved the performance of 1D models, models with scalar parameters, and models + without units, when evaluating them with scalar or small arrays of inputs. For + models that satisfy all of the conditions above, the improvement can be on the + order of 30-40% in execution time. [#16670] + +- Performance of most non-linear fitters has been significantly improved by reducing the overhead in evaluating models inside the objective function. [#16673] + +- Improved the performance of ``parallel_fit_dask`` by avoiding unnecessary copies of the + model inside the fitter. [#17033] + +- ``CompoundModel`` now implements numerical derivatives of parameters when using the +, -, * or / operators. This improves the speed of fitting these models because numerical derivatives of the parameters are not calculated. [#17034] + +astropy.stats +^^^^^^^^^^^^^ + +- The performance of biweight_location, biweight_scale, + biweight_midvariance, and median_absolute_deviation has been improved by + using the bottleneck nan* functions when available. This requires the + bottleneck optional dependency to be installed. [#16967] + +astropy.units +^^^^^^^^^^^^^ + +- The ``units.quantity_input`` decorator has been optimized, especially in the case that no equivalencies are provided to the decorator, and the speed-up is very noticeable when wrapping short functions. [#16742] + +- Parsing composite units with the OGIP formatter is now up to 25% faster. [#16761] + +- Parsing units with scale factors is now up to 50% faster. [#16813] + +- Parsing strings representing non-composite units with ``Unit`` is now up to 25% + faster. [#17004] + +- Converting composite units to strings with the ``"cds"``, ``"fits"``, + ``"ogip"`` and ``"vounit"`` formatters is now at least twice as fast. [#17043] + +astropy.visualization +^^^^^^^^^^^^^^^^^^^^^ + +- Removed redundant transformations when WCSAxes determines the coordinate ranges + for ticks/gridlines, which speeds up typical plot generation by ~10%, and by + much more if ``astropy.visualization.wcsaxes.conf.coordinate_range_samples`` is + set to a large value [#16366] + + +Other Changes and Additions +--------------------------- + +- Updated minimum supported Python version to 3.11. As a result, minimum + requirements were updated to compatible versions. + Astropy now requires + - ``numpy>=1.23.2`` + - ``PyYAML>=6.0.0`` + - ``packaging>=22.0.0`` [#16903] + +- The minimum supported version of Pandas is now v2.0. + This is in line with https://scientific-python.org/specs/spec-0000/. [#16308] + +- Update minimal recommendation for matplotlib from version 3.3.4 to 3.6.0 [#16557] + +- The Contributor documentation has been significantly improved. It now includes a + Quickstart Guide with concise instructions on setting up a development environment and + making a pull request. In addition, the developer documentation was reorganized and + simplified where possible to improve readability and accessibility. [#16561] + Version 6.1.6 (2024-11-11) ========================== diff --git a/docs/changes/16308.other.rst b/docs/changes/16308.other.rst deleted file mode 100644 index a3a06bde1c6a..000000000000 --- a/docs/changes/16308.other.rst +++ /dev/null @@ -1,2 +0,0 @@ -The minimum supported version of Pandas is now v2.0. -This is in line with https://scientific-python.org/specs/spec-0000/. diff --git a/docs/changes/16557.other.rst b/docs/changes/16557.other.rst deleted file mode 100644 index 707b47032ab4..000000000000 --- a/docs/changes/16557.other.rst +++ /dev/null @@ -1 +0,0 @@ -Update minimal recommendation for matplotlib from version 3.3.4 to 3.6.0 diff --git a/docs/changes/16561.other.rst b/docs/changes/16561.other.rst deleted file mode 100644 index 1aed96eec147..000000000000 --- a/docs/changes/16561.other.rst +++ /dev/null @@ -1,4 +0,0 @@ -The Contributor documentation has been significantly improved. It now includes a -Quickstart Guide with concise instructions on setting up a development environment and -making a pull request. In addition, the developer documentation was reorganized and -simplified where possible to improve readability and accessibility. diff --git a/docs/changes/16903.other.rst b/docs/changes/16903.other.rst deleted file mode 100644 index 5f9073e391e2..000000000000 --- a/docs/changes/16903.other.rst +++ /dev/null @@ -1,6 +0,0 @@ -Updated minimum supported Python version to 3.11. As a result, minimum -requirements were updated to compatible versions. -Astropy now requires -- ``numpy>=1.23.2`` -- ``PyYAML>=6.0.0`` -- ``packaging>=22.0.0`` diff --git a/docs/changes/config/17118.feature.rst b/docs/changes/config/17118.feature.rst deleted file mode 100644 index 648f4874444b..000000000000 --- a/docs/changes/config/17118.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added ``get_config_dir_path`` (and ``get_cache_dir_path``) which is equivalent -to ``get_config_dir`` (respectively ``get_cache_dir``) except that it returns a -``pathlib.Path`` object instead of ``str``. diff --git a/docs/changes/coordinates/16831.feature.rst b/docs/changes/coordinates/16831.feature.rst deleted file mode 100644 index 53b2e5739239..000000000000 --- a/docs/changes/coordinates/16831.feature.rst +++ /dev/null @@ -1,5 +0,0 @@ -``BaseCoordinateFrame`` instances such as ``ICRS``, ``SkyOffsetFrame``, etc., -can now be stored directly in tables (previously, they were stored as -``object`` type columns). Furthermore, storage in tables is now also possible -for frames that have no data (but which have attributes with the correct shape -to fit in the table). diff --git a/docs/changes/coordinates/16833.api.rst b/docs/changes/coordinates/16833.api.rst deleted file mode 100644 index 5f3e4859e178..000000000000 --- a/docs/changes/coordinates/16833.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -For non-scalar frames without data, ``len(frame)`` will now return the first -element of its ``shape``, just like for frames with data (or arrays more -generally). For scalar frames, a ``TypeError`` will be raised. Both these -instead of raising a ``ValueError`` stating the frame has no data. diff --git a/docs/changes/coordinates/17009.feature.rst b/docs/changes/coordinates/17009.feature.rst deleted file mode 100644 index d24f8d0c5a3c..000000000000 --- a/docs/changes/coordinates/17009.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -``BaseCoordinateFrame`` now has a ``to_table()`` method, which converts the -frame to a ``QTable``, analogously to the ``SkyCoord.to_table()`` method. diff --git a/docs/changes/coordinates/17016.feature.rst b/docs/changes/coordinates/17016.feature.rst deleted file mode 100644 index 3ba2f0bc8dfe..000000000000 --- a/docs/changes/coordinates/17016.feature.rst +++ /dev/null @@ -1,5 +0,0 @@ -``SkyCoord``, coordinate frames, and representations have all have gained the -ability to deal with ``Masked`` data. In general, the procedure is similar to -that of ``Time``, except that different representation components do not share -the mask, to enable holding, e.g., a catalogue of objects in which only some -have associated distances. diff --git a/docs/changes/coordinates/17046.api.rst b/docs/changes/coordinates/17046.api.rst deleted file mode 100644 index 13b1c2528143..000000000000 --- a/docs/changes/coordinates/17046.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -The deprecated ``coordinates.get_moon()`` function has been removed. Use -``coordinates.get_body("moon")`` instead. diff --git a/docs/changes/coordinates/17252.api.rst b/docs/changes/coordinates/17252.api.rst deleted file mode 100644 index 695ec79992bb..000000000000 --- a/docs/changes/coordinates/17252.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -The deprecated ``BaseCoordinateFrame.get_frame_attr_names()`` is removed. -Use ``get_frame_attr_defaults()`` instead. diff --git a/docs/changes/cosmology/16597.api.rst b/docs/changes/cosmology/16597.api.rst deleted file mode 100644 index b2134b8092a3..000000000000 --- a/docs/changes/cosmology/16597.api.rst +++ /dev/null @@ -1 +0,0 @@ -Passing redshift arguments as keywords is deprecated in many methods. diff --git a/docs/changes/cosmology/16730.api.rst b/docs/changes/cosmology/16730.api.rst deleted file mode 100644 index 56aac48358c1..000000000000 --- a/docs/changes/cosmology/16730.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -Deprecated ``cosmology.utils`` module has been removed. Any public API may -be imported directly from the ``cosmology`` module instead. diff --git a/docs/changes/cosmology/16847.api.rst b/docs/changes/cosmology/16847.api.rst deleted file mode 100644 index 34255cc77b5d..000000000000 --- a/docs/changes/cosmology/16847.api.rst +++ /dev/null @@ -1,5 +0,0 @@ -Setting ``Ob0 = None`` in FLRW cosmologies has been deprecated in favor of ``Ob0 = -0.0``. Conceptually this is a change in that baryons are now always a component of the -cosmology. Practically, the only change (besides that ``Ob0`` is never ``None``) is that -methods relying on ``Ob0`` always work, rather than sometimes raising an exception, -instead by default taking the contribution of the baryons to be negligible. diff --git a/docs/changes/io.ascii/15758.api.rst b/docs/changes/io.ascii/15758.api.rst deleted file mode 100644 index 788f4b96d3cc..000000000000 --- a/docs/changes/io.ascii/15758.api.rst +++ /dev/null @@ -1,17 +0,0 @@ -Remove all deprecated arguments from functions within ``astropy.io.ascii``. - -``read()``: -- ``Reader`` is removed. Instead supply the equivalent ``format`` argument. -- Use ``inputter_cls`` instead of ``Inputter``. -- Use ``outputter_cls`` instead of ``Outputter``. - -``get_reader()``: -- Use ``reader_cls`` instead of ``Reader``. -- Use ``inputter_cls`` instead of ``Inputter``. -- Use ``outputter_cls`` instead of ``Outputter``. - -``write()``: -- ``Writer`` is removed. Instead supply the equivalent ``format`` argument. - -``get_writer()``: -- Use ``writer_cls`` instead of ``Writer``. diff --git a/docs/changes/io.ascii/15774.bugfix.rst b/docs/changes/io.ascii/15774.bugfix.rst deleted file mode 100644 index ddea55f804a6..000000000000 --- a/docs/changes/io.ascii/15774.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix the broken behavior of reading an ASCII table and filling values using column names. -This PR addresses the issue and improves the functionality. diff --git a/docs/changes/io.ascii/16840.perf.rst b/docs/changes/io.ascii/16840.perf.rst deleted file mode 100644 index d5b25bea3e3d..000000000000 --- a/docs/changes/io.ascii/16840.perf.rst +++ /dev/null @@ -1,5 +0,0 @@ -The performance of guessing the table format when reading large files with -``astropy.io.ascii`` has been improved. Now the process uses at most -10000 lines of the file to check if it matches the format. This behavior can -be configured using the ``astropy.io.ascii.conf.guess_limit_lines`` -configuration item, including disabling the limit entirely. diff --git a/docs/changes/io.ascii/16930.feature.rst b/docs/changes/io.ascii/16930.feature.rst deleted file mode 100644 index 3fad269c4e44..000000000000 --- a/docs/changes/io.ascii/16930.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support for `pathlib.Path` objects in -`astropy.io.ascii.core.BaseInputter.get_lines`. diff --git a/docs/changes/io.fits/15474.api.rst b/docs/changes/io.fits/15474.api.rst deleted file mode 100644 index 307dfec2ca46..000000000000 --- a/docs/changes/io.fits/15474.api.rst +++ /dev/null @@ -1,6 +0,0 @@ -The ``CompImageHDU`` class has been refactored to inherit from ``ImageHDU`` -instead of ``BinTableHDU``. This change should be for the most part preserve the -API, but any calls to ``isinstance(hdu, BinTableHDU)`` will now return ``False`` -if ``hdu`` is a ``CompImageHDU`` whereas before it would have returned ``True``. -In addition, the ``uint`` keyword argument to ``CompImageHDU`` now defaults to -``True`` for consistency with ``ImageHDU``. diff --git a/docs/changes/io.fits/15474.bugfix.rst b/docs/changes/io.fits/15474.bugfix.rst deleted file mode 100644 index 5650714919ce..000000000000 --- a/docs/changes/io.fits/15474.bugfix.rst +++ /dev/null @@ -1,11 +0,0 @@ -Fix a number of bugs in ``CompImageHDU``: - -* Fix the ability to pickle ``CompImageHDU`` objects -* Ensure that compression settings are not lost if initializing ``CompImageHDU`` - without data but with compression settings and setting the data later -* Make sure that keywords are properly updated when setting the header of a - ``CompImageHDU`` to an existing image header. -* Fix the ability to use ``CompImageHDU.section`` on instances that have not yet - been written to disk -* Fix the image checksum/datasum in ``CompImageHDU.header`` to be those for the - image HDU instead of for the underlying binary table. diff --git a/docs/changes/io.fits/15781.api.rst b/docs/changes/io.fits/15781.api.rst deleted file mode 100644 index 7531f2934b35..000000000000 --- a/docs/changes/io.fits/15781.api.rst +++ /dev/null @@ -1,8 +0,0 @@ -Remove many unintended exports from ``astropy.io.fits.hdu.compressed``. -The low-level functions ``compress_image_data`` and ``decompress_image_data_section`` -are now only available at the qualified names -``astropy.io.fits.hdu.compressed._tiled_compression.compress_image_data`` -and ``astropy.io.fits.hdu.compressed._tiled_compression.decompress_image_data_section``. -The rest of the removed exports are external modules or properly exported -elsewhere in astropy. May break imports in rare cases that relied -on these exports. diff --git a/docs/changes/io.fits/17097.feature.rst b/docs/changes/io.fits/17097.feature.rst deleted file mode 100644 index 55da93486329..000000000000 --- a/docs/changes/io.fits/17097.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Expanded ``FITSDiff`` output for ``PrimaryHDU`` and ``ImageHDU`` to include the -maximum relative and absolute differences in the data. diff --git a/docs/changes/io.fits/17099.bugfix.rst b/docs/changes/io.fits/17099.bugfix.rst deleted file mode 100644 index cff2187839a4..000000000000 --- a/docs/changes/io.fits/17099.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a spurious exception when reading integer compressed images with blanks. diff --git a/docs/changes/io.fits/17100.api.rst b/docs/changes/io.fits/17100.api.rst deleted file mode 100644 index e009a17eeb7a..000000000000 --- a/docs/changes/io.fits/17100.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -The ``CompImageHeader`` class is now deprecated, and headers on ``CompImageHDU`` -instances are now plain ``Header`` instances. If a reserved keyword is set on -``CompImageHDU.header``, a warning will now be emitted at the point where the -file is written rather than at the point where the keyword is set. diff --git a/docs/changes/io.fits/17155.api.rst b/docs/changes/io.fits/17155.api.rst deleted file mode 100644 index 8d419a7fc183..000000000000 --- a/docs/changes/io.fits/17155.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -- Remove code that was deprecated in previous versions: ``_ExtensionHDU`` and - ``_NonstandardExtHDU``, ``(Bin)Table.update``, ``tile_size`` argument for - ``CompImageHDU``. Also specifying an invalid ``tile_shape`` now raises an - error. diff --git a/docs/changes/io.fits/17209.perf.rst b/docs/changes/io.fits/17209.perf.rst deleted file mode 100644 index dc6e91181562..000000000000 --- a/docs/changes/io.fits/17209.perf.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize checksum computation. diff --git a/docs/changes/io.fits/17237.bugfix.rst b/docs/changes/io.fits/17237.bugfix.rst deleted file mode 100644 index 2bc43c02c942..000000000000 --- a/docs/changes/io.fits/17237.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix creating CompImageHDU from header with BSCALE/BZERO: keywords are now -ignored, as done in ImageHDU. diff --git a/docs/changes/io.misc/16375.api.rst b/docs/changes/io.misc/16375.api.rst deleted file mode 100644 index a29d4340c87b..000000000000 --- a/docs/changes/io.misc/16375.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -New format ``"parquet.votable"`` is added to read and write a parquet file -with a votable metadata included. diff --git a/docs/changes/io.misc/16955.feature.rst b/docs/changes/io.misc/16955.feature.rst deleted file mode 100644 index 091e3fb7c09d..000000000000 --- a/docs/changes/io.misc/16955.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -The HDF5 writer, ``write_table_hdf5()``, now accepts ``os.PathLike`` objects -as ``output``. diff --git a/docs/changes/io.votable/15959.api.rst b/docs/changes/io.votable/15959.api.rst deleted file mode 100644 index 488ee77fd668..000000000000 --- a/docs/changes/io.votable/15959.api.rst +++ /dev/null @@ -1,5 +0,0 @@ -``Table.read(..., format='votable')``, ``votable.parse`` and -``votable.parse_single_table`` now respect the ``columns`` argument and will only output -selected columns. Previously, unselected columns would just be masked (and unallocated). -``astropy.io.votable.tree.TableElement.create_arrays`` also gained a ``colnumbers`` -keyword argument to allow column selection. diff --git a/docs/changes/io.votable/16488.bugfix.rst b/docs/changes/io.votable/16488.bugfix.rst deleted file mode 100644 index f2a9c583f79d..000000000000 --- a/docs/changes/io.votable/16488.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Making the "votable.parquet" format available as a reader format to ensure -consistency with the writer formats, even though the format it recognised -automatically by "votable". diff --git a/docs/changes/io.votable/16856.feature.rst b/docs/changes/io.votable/16856.feature.rst deleted file mode 100644 index 0ec2eea4936a..000000000000 --- a/docs/changes/io.votable/16856.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Support reading and writing of VOTable version 1.5, including the new -``refposition`` attribute of ``COOSYS``. diff --git a/docs/changes/io.votable/17156.bugfix.rst b/docs/changes/io.votable/17156.bugfix.rst deleted file mode 100644 index 6e58ea3fb10e..000000000000 --- a/docs/changes/io.votable/17156.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Explicitly set ``usedforsecurity=False`` when using ``hashlib.md5``. Without this, ``hashlib.md5`` will be blocked in FIPS mode. -FIPS (Federal Information Processing Standards) is a set of standards created by NIST (National Institute of Standards and Technology) for US government agencies regarding computer security and interoperability. -This affects validation results ingestion. diff --git a/docs/changes/modeling/16670.perf.rst b/docs/changes/modeling/16670.perf.rst deleted file mode 100644 index 8335337df393..000000000000 --- a/docs/changes/modeling/16670.perf.rst +++ /dev/null @@ -1,4 +0,0 @@ -Improved the performance of 1D models, models with scalar parameters, and models -without units, when evaluating them with scalar or small arrays of inputs. For -models that satisfy all of the conditions above, the improvement can be on the -order of 30-40% in execution time. diff --git a/docs/changes/modeling/16673.api.rst b/docs/changes/modeling/16673.api.rst deleted file mode 100644 index 8204b6d46214..000000000000 --- a/docs/changes/modeling/16673.api.rst +++ /dev/null @@ -1,3 +0,0 @@ -Subclasses of ``_NonLinearLSQFitter``, so any subclasses of the public ``LevMarLSQFitter``, ``TRFLSQFitter``, ``LMLSQFitter`` or ``DogBoxLSQFitter``, should now accept an additional ``fit_param_indices`` kwarg in the function signature of their ``objective_function`` methods. -Nothing is needed to be done with this kwarg, and it might not be set, but it can optionally be passed through to ``fitter_to_model_params_array`` for a performance improvement. -We also recommended accepting all kwargs (with ``**kwargs``) in this method so that future additional kwargs do not cause breakage. diff --git a/docs/changes/modeling/16673.perf.rst b/docs/changes/modeling/16673.perf.rst deleted file mode 100644 index 2633e61faded..000000000000 --- a/docs/changes/modeling/16673.perf.rst +++ /dev/null @@ -1 +0,0 @@ -Performance of most non-linear fitters has been significantly improved by reducing the overhead in evaluating models inside the objective function. diff --git a/docs/changes/modeling/16677.feature.rst b/docs/changes/modeling/16677.feature.rst deleted file mode 100644 index 8c7d14e8555d..000000000000 --- a/docs/changes/modeling/16677.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added ``Model.has_tied``, ``Model.has_fixed``, and ``Model.has_bounds`` attributes to make -it easy to check whether models have various kinds of constraints set without having to -inspect ``Model.tied``, ``Model.fixed``, and ``Model.bounds`` in detail. diff --git a/docs/changes/modeling/16696.feature.rst b/docs/changes/modeling/16696.feature.rst deleted file mode 100644 index 4749d50eb63c..000000000000 --- a/docs/changes/modeling/16696.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added a new ``parallel_fit_dask`` function that can be used to fit models to -many sections (e.g. spectra, image slices) on an N-dimensional array in -parallel. diff --git a/docs/changes/modeling/16770.api.rst b/docs/changes/modeling/16770.api.rst deleted file mode 100644 index bbb4e93a158d..000000000000 --- a/docs/changes/modeling/16770.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -Exception message for when broadcast shapes mismatch has changed. -Previously, it used complicated regex to maintain backward compatibility. -To ease maintenance, this regex has been removed and now directly -passes exception from ``numpy.broadcast_shapes`` function. diff --git a/docs/changes/modeling/16800.feature.rst b/docs/changes/modeling/16800.feature.rst deleted file mode 100644 index e1dd8caf33fa..000000000000 --- a/docs/changes/modeling/16800.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Added a ``Lorentz2D`` model. diff --git a/docs/changes/modeling/16829.bugfix.rst b/docs/changes/modeling/16829.bugfix.rst deleted file mode 100644 index a8428a9661f8..000000000000 --- a/docs/changes/modeling/16829.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed the output representation of models with parameters that have -units of ``dimensionless_unscaled``. diff --git a/docs/changes/modeling/16994.api.rst b/docs/changes/modeling/16994.api.rst deleted file mode 100644 index 8328e5cd8363..000000000000 --- a/docs/changes/modeling/16994.api.rst +++ /dev/null @@ -1,3 +0,0 @@ -Using the ``LMLSQFitter`` fitter with models that have bounds is now deprecated, -as support for bounds was very basic. Instead, non-linear fitters with more -sophisticated support for bounds should be used instead. diff --git a/docs/changes/modeling/16995.api.rst b/docs/changes/modeling/16995.api.rst deleted file mode 100644 index 87a1a578648a..000000000000 --- a/docs/changes/modeling/16995.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -The optional ``use_min_max_bounds`` keyword argument in ``TRFLSQFitter`` and -``DogBoxLSQFitter`` has now been deprecated and should not be used. These -fitters handle bounds correctly by default and this keyword argument was only -provided to opt-in to a more basic form of bounds handling. diff --git a/docs/changes/modeling/17033.feature.rst b/docs/changes/modeling/17033.feature.rst deleted file mode 100644 index 953fb57db628..000000000000 --- a/docs/changes/modeling/17033.feature.rst +++ /dev/null @@ -1,4 +0,0 @@ -Added ``inplace=False/True`` keyword argument to the ``__call__`` method of most fitters, -to optionally allow the original model passed to the fitter to be modified with the fitted -values of the parameters, rather than return a copy. This can improve performance if users -don't need to keep hold of the initial parameter values. diff --git a/docs/changes/modeling/17033.perf.rst b/docs/changes/modeling/17033.perf.rst deleted file mode 100644 index 702a43b42e91..000000000000 --- a/docs/changes/modeling/17033.perf.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improved the performance of ``parallel_fit_dask`` by avoiding unnecessary copies of the -model inside the fitter. diff --git a/docs/changes/modeling/17034.perf.rst b/docs/changes/modeling/17034.perf.rst deleted file mode 100644 index 090e3b633c6d..000000000000 --- a/docs/changes/modeling/17034.perf.rst +++ /dev/null @@ -1 +0,0 @@ -``CompoundModel`` now implements numerical derivatives of parameters when using the +, -, * or / operators. This improves the speed of fitting these models because numerical derivatives of the parameters are not calculated. diff --git a/docs/changes/modeling/17248.api.rst b/docs/changes/modeling/17248.api.rst deleted file mode 100644 index 7c92935bb022..000000000000 --- a/docs/changes/modeling/17248.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -The deprecated ``comb()`` function has been removed. -Use ``math.comb()`` from the Python standard library instead. diff --git a/docs/changes/stats/16967.perf.rst b/docs/changes/stats/16967.perf.rst deleted file mode 100644 index c447bb96a77e..000000000000 --- a/docs/changes/stats/16967.perf.rst +++ /dev/null @@ -1,4 +0,0 @@ -The performance of biweight_location, biweight_scale, -biweight_midvariance, and median_absolute_deviation has been improved by -using the bottleneck nan* functions when available. This requires the -bottleneck optional dependency to be installed. diff --git a/docs/changes/stats/17116.api.rst b/docs/changes/stats/17116.api.rst deleted file mode 100644 index 0dcb3e59b7ed..000000000000 --- a/docs/changes/stats/17116.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -Integer inputs to ``sigma_clip`` and ``SigmaClip`` are not converted to -``np.float32`` instead of ``float`` if necessary. diff --git a/docs/changes/stats/17204.bugfix.rst b/docs/changes/stats/17204.bugfix.rst deleted file mode 100644 index c6ef08b1d396..000000000000 --- a/docs/changes/stats/17204.bugfix.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fixed accuracy of sigma clipping for large ``float32`` arrays when -``bottleneck`` is installed. Performance may be impacted for computations -involving arrays with dtype other than ``float64``. This change has no impact -for environments that do not have ``bottleneck`` installed. diff --git a/docs/changes/stats/17221.feature.rst b/docs/changes/stats/17221.feature.rst deleted file mode 100644 index 665fd7fd2030..000000000000 --- a/docs/changes/stats/17221.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added a SigmaClippedStats convenience class for computing sigma-clipped -statistics. diff --git a/docs/changes/stats/17402.bugfix.rst b/docs/changes/stats/17402.bugfix.rst deleted file mode 100644 index 88cd3c073493..000000000000 --- a/docs/changes/stats/17402.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix an issue in sigma-clipping where the use of ``np.copy()`` was causing -the input data mask to be discarded in cases where ``grow`` was set. diff --git a/docs/changes/table/15735.bugfix.rst b/docs/changes/table/15735.bugfix.rst deleted file mode 100644 index 5c83d2f27397..000000000000 --- a/docs/changes/table/15735.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a bug where column names would be lost when instantiating ``Table`` from a list of ``Row`` objects. diff --git a/docs/changes/table/16250.api.rst b/docs/changes/table/16250.api.rst deleted file mode 100644 index 3ee9a61547c4..000000000000 --- a/docs/changes/table/16250.api.rst +++ /dev/null @@ -1,9 +0,0 @@ -Change the default type for the ``meta`` attribute in ``Table`` and ``Column`` (and -subclasses) from ``OrderedDict`` to ``dict``. Since Python 3.8 the ``dict`` class is -ordered by default, so there is no need to use ``OrderedDict``. - -In addition the ECSV table writer in ``astropy.io.ascii`` was updated to consistently -write the ``meta`` attribute as an ordered map using the ``!!omap`` tag. This -convention conforms to the ECSV specification and is supported by existing ECSV readers. -Previously the ``meta`` attribute could be written as an ordinary YAML map, which is not -guaranteed to preserve the order of the keys. diff --git a/docs/changes/table/16250.feature.rst b/docs/changes/table/16250.feature.rst deleted file mode 100644 index 741e0fe754e7..000000000000 --- a/docs/changes/table/16250.feature.rst +++ /dev/null @@ -1,4 +0,0 @@ -Changed a number of dict-like containers in ``io.ascii`` from ``OrderedDict`` to -``dict``. The ``dict`` class maintains key order since Python 3.8 so ``OrderedDict`` is -no longer needed. The changes are largely internal and should not affect users in any -way. See also the API change log entry for this PR. diff --git a/docs/changes/table/16360.api.rst b/docs/changes/table/16360.api.rst deleted file mode 100644 index 047dba475610..000000000000 --- a/docs/changes/table/16360.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -An exception is now raised when trying to add a multi-dimensional column as an -index via ``Table.add_index``. diff --git a/docs/changes/table/16361.feature.rst b/docs/changes/table/16361.feature.rst deleted file mode 100644 index f1cc02fc50d0..000000000000 --- a/docs/changes/table/16361.feature.rst +++ /dev/null @@ -1,4 +0,0 @@ -Add a ``keep_order`` argument to the ``astropy.table.join`` function which specifies to -maintain the original order of the key table in the joined output. This applies for -inner, left, and right joins. The default is ``False`` in which case the output is -ordered by the join keys, consistent with prior behavior. diff --git a/docs/changes/table/16498.api.rst b/docs/changes/table/16498.api.rst deleted file mode 100644 index 89e601dc807f..000000000000 --- a/docs/changes/table/16498.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -Aggregating table groups for ``MaskedColumn`` no longer converts -fully masked groups to ``NaN``, but instead returns a masked element. diff --git a/docs/changes/table/16498.bugfix.rst b/docs/changes/table/16498.bugfix.rst deleted file mode 100644 index 172e9e39fb56..000000000000 --- a/docs/changes/table/16498.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Aggregating table groups for ``MaskedColumn`` now ensures that fully-masked -groups result in masked elements rather than ``NaN``. diff --git a/docs/changes/table/16500.api.rst b/docs/changes/table/16500.api.rst deleted file mode 100644 index 0e3500730a4b..000000000000 --- a/docs/changes/table/16500.api.rst +++ /dev/null @@ -1,7 +0,0 @@ -Always use ``MaskedQuantity`` in ``QTable`` to represent masked ``Quantity`` -data or when the ``QTable`` is created with ``masked=True``. Previously the -default was to use a normal ``Quantity`` with a ``mask`` attribute of type -``FalseArray`` as a stub to allow a minimal level of compatibility for certain -operations. This update brings more consistent behavior and fixes functions -like reading of table data from a list of dict that includes quantities with -missing entries, and aggregation of ``MaskedQuantity`` in table groups. diff --git a/docs/changes/table/17102.api.rst b/docs/changes/table/17102.api.rst deleted file mode 100644 index 08a002ca036c..000000000000 --- a/docs/changes/table/17102.api.rst +++ /dev/null @@ -1,3 +0,0 @@ -Setting an empty table to a scalar no longer raises an exception, but -creates an empty column. This is to support cases where the number of -elements in a table is not known in advance, and could be zero. diff --git a/docs/changes/table/17165.api.rst b/docs/changes/table/17165.api.rst deleted file mode 100644 index eeb545b4744c..000000000000 --- a/docs/changes/table/17165.api.rst +++ /dev/null @@ -1,5 +0,0 @@ -``show_in_notebook`` method for Astropy tables has been un-deprecated and the API has -been updated to accept a ``backend`` keyword and require only keyword arguments. The new -default ``backend="ipydatagrid"`` relies on an optional dependency, ``ipydatagrid``. The -previous default table viewer (prior to v7.0) is still available as -``backend="classic"``, but it has been deprecated since v6.1 and will be removed in a future release. diff --git a/docs/changes/table/17184.api.rst b/docs/changes/table/17184.api.rst deleted file mode 100644 index 4c85e1a8625b..000000000000 --- a/docs/changes/table/17184.api.rst +++ /dev/null @@ -1,6 +0,0 @@ -The default behavior of ``Table.pformat`` was changed to include all rows and columns -instead of truncating the outputs to fit the current terminal. The new default -keyword arguments ``max_width=-1`` and ``max_lines=-1`` now match those in -``Table.pformat_all``. Since the ``Table.pformat_all`` method is now redundant, it is -pending deprecation. Similarly, the default behavior of ``Column.pformat`` was changed -to include all rows instead of truncating the outputs to fit the current terminal. diff --git a/docs/changes/table/17380.bugfix.rst b/docs/changes/table/17380.bugfix.rst deleted file mode 100644 index 3a9cfba8defc..000000000000 --- a/docs/changes/table/17380.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Ensure that tables holding coordinates or representations can also be stacked -if they have zero length. This fix also ensures that the ``insert`` method -works correctly with a zero-length table holding a coordinate object. diff --git a/docs/changes/table/17385.bugfix.rst b/docs/changes/table/17385.bugfix.rst deleted file mode 100644 index c31e40a9b2d5..000000000000 --- a/docs/changes/table/17385.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed table aggregate with empty columns when float is present. diff --git a/docs/changes/time/17212.api.rst b/docs/changes/time/17212.api.rst deleted file mode 100644 index 238495be31fa..000000000000 --- a/docs/changes/time/17212.api.rst +++ /dev/null @@ -1,5 +0,0 @@ -``Time.ptp`` now properly emits a deprecation warning independently of NumPy's -version. This method was previously deprecated in astropy 6.1, but the warning -was not visible for users that had NumPy 1.x installed. Because of this, the -warning message was updated to state that ``Time.ptp`` is deprecated since -version 7.0 instead. diff --git a/docs/changes/units/15642.api.rst b/docs/changes/units/15642.api.rst deleted file mode 100644 index fedc4b8ec388..000000000000 --- a/docs/changes/units/15642.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -The deprecated ``Quantity.nansum()`` method has been removed. Use -``np.nansum`` instead. diff --git a/docs/changes/units/16087.feature.rst b/docs/changes/units/16087.feature.rst deleted file mode 100644 index 48c900d6dc69..000000000000 --- a/docs/changes/units/16087.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add a ``formatter`` argument to the ``to_string`` method of the ``Quantity`` -class. Enables custom number formatting with a callable formatter or -format_spec, especially useful for consistent notation. diff --git a/docs/changes/units/16343.api.rst b/docs/changes/units/16343.api.rst deleted file mode 100644 index fbedfd65df09..000000000000 --- a/docs/changes/units/16343.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -The ``factor`` parameter of the ``spectral_density`` equivalency, the use of -which has been discouraged in the documentation since version 0.3, is now -deprecated. -Use the ``wav`` parameter as a ``Quantity``, not as a bare unit. diff --git a/docs/changes/units/16441.feature.rst b/docs/changes/units/16441.feature.rst deleted file mode 100644 index 0add905f959c..000000000000 --- a/docs/changes/units/16441.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add the unit foe (or Bethe, equivalent to 1e51 erg), which is often used to -express the energy emitted by a supernova explosion. diff --git a/docs/changes/units/16455.api.rst b/docs/changes/units/16455.api.rst deleted file mode 100644 index 93ef1af92e68..000000000000 --- a/docs/changes/units/16455.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -The ``format.Fits`` formatter class has been renamed to ``format.FITS`` and the -old name is deprecated. -Specifying the FITS format for converting ``Quantity`` and ``UnitBase`` -instances to and from strings is not affected by this change. diff --git a/docs/changes/units/16516.feature.rst b/docs/changes/units/16516.feature.rst deleted file mode 100644 index 4fa419d8579b..000000000000 --- a/docs/changes/units/16516.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add ``magnetic_flux_field`` equivalency to convert magnetic field between -magnetic field strength (H) and magnetic flux density (B). diff --git a/docs/changes/units/16529.bugfix.rst b/docs/changes/units/16529.bugfix.rst deleted file mode 100644 index 10ea5a30276b..000000000000 --- a/docs/changes/units/16529.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Allow SI-prefixes for radioactivity units ``becquerel`` and ``curie`` in ``astropy.units.si``, conforming to BIPM's guidelines for SI units. diff --git a/docs/changes/units/16613.api.rst b/docs/changes/units/16613.api.rst deleted file mode 100644 index bd2fa9da4390..000000000000 --- a/docs/changes/units/16613.api.rst +++ /dev/null @@ -1,3 +0,0 @@ -Conversion from one unit to another using ``old_unit.to(new_unit, value)`` no longer -converts ``value`` automatically to a numpy array, but passes through array duck types -such as ``dask`` arrays, with equivalencies properly accounted for. diff --git a/docs/changes/units/16676.api.rst b/docs/changes/units/16676.api.rst deleted file mode 100644 index 5927575843e3..000000000000 --- a/docs/changes/units/16676.api.rst +++ /dev/null @@ -1,16 +0,0 @@ -The ``format_exponential_notation()`` method of the ``Base`` unit formatter has -changed. -Any unit formatters that inherit directly from ``Base`` but have not -implemented their own ``format_exponential_notation()`` and wish to retain -previous behavior should implement it as: - -.. code-block:: python - - def format_exponential_notation(cls, val, format_spec): - return format(val, format_spec) - -Any formatters that inherit directly from ``Base`` and call -``super().format_exponential_notation(val, format_spec)`` should instead call -``format(val, format_spec)`` -The specific unit formatters in ``astropy.units`` and custom formatters that -inherit from any of them are not affected. diff --git a/docs/changes/units/16707.api.rst b/docs/changes/units/16707.api.rst deleted file mode 100644 index 2a2fe43abf9f..000000000000 --- a/docs/changes/units/16707.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -The deprecated ``units.format.Unscaled`` has been removed. Use ``units.format.Generic`` -instead. diff --git a/docs/changes/units/16729.feature.rst b/docs/changes/units/16729.feature.rst deleted file mode 100644 index 30b958a2ef3d..000000000000 --- a/docs/changes/units/16729.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Added SI-units ``sievert``, ``gray``, ``katal``, and ``hectare`` in ``astropy.units.si``. diff --git a/docs/changes/units/16742.perf.rst b/docs/changes/units/16742.perf.rst deleted file mode 100644 index 37ac5e9dede2..000000000000 --- a/docs/changes/units/16742.perf.rst +++ /dev/null @@ -1 +0,0 @@ -The ``units.quantity_input`` decorator has been optimized, especially in the case that no equivalencies are provided to the decorator, and the speed-up is very noticeable when wrapping short functions. diff --git a/docs/changes/units/16749.bugfix.rst b/docs/changes/units/16749.bugfix.rst deleted file mode 100644 index 76abe28c86ea..000000000000 --- a/docs/changes/units/16749.bugfix.rst +++ /dev/null @@ -1,4 +0,0 @@ -The OGIP unit parser no longer accepts strings where a component unit is -followed by a parenthesized unit without a separator in between, such as -``'m(s)'`` or ``'m(s)**2'``. -Such strings are not allowed by the OGIP standard. diff --git a/docs/changes/units/16761.perf.rst b/docs/changes/units/16761.perf.rst deleted file mode 100644 index 27d6d3453110..000000000000 --- a/docs/changes/units/16761.perf.rst +++ /dev/null @@ -1 +0,0 @@ -Parsing composite units with the OGIP formatter is now up to 25% faster. diff --git a/docs/changes/units/16779.bugfix.rst b/docs/changes/units/16779.bugfix.rst deleted file mode 100644 index d033f4bbe696..000000000000 --- a/docs/changes/units/16779.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -A few edge cases that could result in a power of a unit to be a numerical value -from ``numpy``, instead of the intended Python ``int``, ``float`` or -``fractions.Fraction`` instance, have been fixed. diff --git a/docs/changes/units/16784.api.rst b/docs/changes/units/16784.api.rst deleted file mode 100644 index 6579cf2a654c..000000000000 --- a/docs/changes/units/16784.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added a __round__() dunder method to ``Quantity`` -in order to support the built-in round() function. diff --git a/docs/changes/units/16788.bugfix.rst b/docs/changes/units/16788.bugfix.rst deleted file mode 100644 index 0254ba3ddfe2..000000000000 --- a/docs/changes/units/16788.bugfix.rst +++ /dev/null @@ -1,7 +0,0 @@ -The OGIP unit parser now detects negative powers that are not enclosed in -parenthesis. -For example, ``u.Unit("s**-1", format="ogip")`` now raises an error because the -OGIP standard expects the string to be written as ``"s**(-1)"`` instead, but it -is still possible to parse the unit with -``u.Unit("s**-1", format="ogip", parse_strict="warn")`` or -``parse_strict="silent"``. diff --git a/docs/changes/units/16813.perf.rst b/docs/changes/units/16813.perf.rst deleted file mode 100644 index 048552997f6c..000000000000 --- a/docs/changes/units/16813.perf.rst +++ /dev/null @@ -1 +0,0 @@ -Parsing units with scale factors is now up to 50% faster. diff --git a/docs/changes/units/16861.bugfix.rst b/docs/changes/units/16861.bugfix.rst deleted file mode 100644 index 835392b10c7a..000000000000 --- a/docs/changes/units/16861.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -``UnitScaleError`` can now be imported from the ``astropy.units`` namespace. diff --git a/docs/changes/units/16875.api.rst b/docs/changes/units/16875.api.rst deleted file mode 100644 index 4a730e876017..000000000000 --- a/docs/changes/units/16875.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -For ``Masked`` initialization in which a mask is passed in, ensure that that -mask is combined with any mask present on the input. diff --git a/docs/changes/units/16892.feature.rst b/docs/changes/units/16892.feature.rst deleted file mode 100644 index 44492dba351e..000000000000 --- a/docs/changes/units/16892.feature.rst +++ /dev/null @@ -1,5 +0,0 @@ -When parsing invalid unit strings with ``u.Unit(..., parse_strict="warn")`` or -``u.Unit(..., parse_strict="silent")``, a normal unit may be returned if the -problem is not too serious. -If parsing the string fails completely then an ``UnrecognizedUnit`` instance is -returned, just as before. diff --git a/docs/changes/units/16958.api.rst b/docs/changes/units/16958.api.rst deleted file mode 100644 index e42f4815fe95..000000000000 --- a/docs/changes/units/16958.api.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``get_format_name()`` method of ``NamedUnit`` and its subclasses is -deprecated. -The ``to_string()`` method can be used instead. diff --git a/docs/changes/units/17004.perf.rst b/docs/changes/units/17004.perf.rst deleted file mode 100644 index 313adda81479..000000000000 --- a/docs/changes/units/17004.perf.rst +++ /dev/null @@ -1,2 +0,0 @@ -Parsing strings representing non-composite units with ``Unit`` is now up to 25% -faster. diff --git a/docs/changes/units/17043.perf.rst b/docs/changes/units/17043.perf.rst deleted file mode 100644 index 1bc1c5a12d84..000000000000 --- a/docs/changes/units/17043.perf.rst +++ /dev/null @@ -1,2 +0,0 @@ -Converting composite units to strings with the ``"cds"``, ``"fits"``, -``"ogip"`` and ``"vounit"`` formatters is now at least twice as fast. diff --git a/docs/changes/units/17059.feature.rst b/docs/changes/units/17059.feature.rst deleted file mode 100644 index e8a51be411f2..000000000000 --- a/docs/changes/units/17059.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added a ``np.arange`` dispatch for ``Quantity`` (requires one to use -``like=``). diff --git a/docs/changes/units/17120.feature.rst b/docs/changes/units/17120.feature.rst deleted file mode 100644 index 75e9d4ad9870..000000000000 --- a/docs/changes/units/17120.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added support for calling numpy array constructors (``np.empty``, ``np.ones``, -``np.zeros`` and ``np.full``) with ``like=Quantity(...)`` . diff --git a/docs/changes/units/17121.api.rst b/docs/changes/units/17121.api.rst deleted file mode 100644 index 7ac2ff86db21..000000000000 --- a/docs/changes/units/17121.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -The ``UnitBase.in_units()`` method is deprecated. -The ``to()`` method can be used as a drop-in replacement. diff --git a/docs/changes/units/17122.api.rst b/docs/changes/units/17122.api.rst deleted file mode 100644 index 49156a5b47a1..000000000000 --- a/docs/changes/units/17122.api.rst +++ /dev/null @@ -1,7 +0,0 @@ -Unit conversions to a given system with ``unit.to_system()``, -``unit.si``, and ``unit.cgs``, will now prefer the simplest unit if it -is in the given system, rather than prioritizing more complicated -units if those had a base unit component. E.g., ``u.Pa.si`` will now -simply return ``Unit("Pa")`` rather than ``Unit("N / m2")``. However, -the case where a unit can be simply described in base units remains -unchanged: ``u.Gal.cgs`` will still give ``Unit("cm / s2")``. diff --git a/docs/changes/units/17125.feature.rst b/docs/changes/units/17125.feature.rst deleted file mode 100644 index 18a4cdd71404..000000000000 --- a/docs/changes/units/17125.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added support for calling numpy array constructors (``np.array``, -``np.asarray``, ``np.asanyarray``, ``np.ascontiguousarray`` and -``np.asfortranarray``) with ``like=Quantity(...)`` . diff --git a/docs/changes/units/17128.feature.rst b/docs/changes/units/17128.feature.rst deleted file mode 100644 index ff1aa068199b..000000000000 --- a/docs/changes/units/17128.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added support for calling numpy array constructors (``np.frombuffer``, -``np.fromfile``, ``np.fromiter``, ``np.fromstring`` and ``np.fromfunction``) -with ``like=Quantity(...))`` . diff --git a/docs/changes/units/17130.feature.rst b/docs/changes/units/17130.feature.rst deleted file mode 100644 index d49966d6f7cb..000000000000 --- a/docs/changes/units/17130.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added support for calling numpy array constructors (``np.require``, -``np.identity``, ``np.eye``, ``np.tri``, ``np.genfromtxt`` and ``np.loadtxt``) -with ``like=Quantity(...))`` . diff --git a/docs/changes/units/17178.api.rst b/docs/changes/units/17178.api.rst deleted file mode 100644 index c7f4fc731832..000000000000 --- a/docs/changes/units/17178.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -The ``CDS``, ``OGIP`` and ``VOUnit`` unit formatters are now subclasses of the -``FITS`` unit formatter. diff --git a/docs/changes/units/17232.bugfix.rst b/docs/changes/units/17232.bugfix.rst deleted file mode 100644 index 003f270b7b62..000000000000 --- a/docs/changes/units/17232.bugfix.rst +++ /dev/null @@ -1,6 +0,0 @@ -Parsing custom units with ``u.Unit()`` using the ``"vounit"`` format now obeys -the ``parse_strict`` parameter, unless the custom units are made explicit with -quotation marks. -For example, ``u.Unit("custom_unit", format="vounit")`` now raises an error, -but ``u.Unit("custom_unit", format="vounit", parse_strict="silent")`` or -``u.Unit("'custom_unit'", format="vounit")`` do not. diff --git a/docs/changes/units/17246.api.rst b/docs/changes/units/17246.api.rst deleted file mode 100644 index ef69bb2da20e..000000000000 --- a/docs/changes/units/17246.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -The ``eV`` and ``rydberg`` units were moved to ``astropy.units.misc`` (from -``astropy.units.si`` and ``astropy.units.astrophys``, respectively). -Practically, this means that ``Unit.to_system(u.si)`` no longer includes -``eV`` as a SI-compatible unit. diff --git a/docs/changes/units/17355.bugfix.rst b/docs/changes/units/17355.bugfix.rst deleted file mode 100644 index 16952271bf53..000000000000 --- a/docs/changes/units/17355.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -It is now possible to use ``Unit`` to create dimensionless units with a scale -factor that is a complex number or a ``fractions.Fraction`` instance. -It was already possible to create such units directly with ``CompositeUnit``. diff --git a/docs/changes/utils/16187.api.rst b/docs/changes/utils/16187.api.rst deleted file mode 100644 index f5eccb2bf79b..000000000000 --- a/docs/changes/utils/16187.api.rst +++ /dev/null @@ -1,5 +0,0 @@ -``IERS_Auto.open()`` now always returns a table of type ``IERS_Auto`` that -contains the combination of IERS-A and IERS-B data, even if automatic -updating of the IERS-A file is disabled or if downloading the new file fails. -Previously, under those conditions, it would return a table of a different type -(``IERS_B``) with only IERS-B data. diff --git a/docs/changes/utils/16187.bugfix.rst b/docs/changes/utils/16187.bugfix.rst deleted file mode 100644 index 50d7d578f2ab..000000000000 --- a/docs/changes/utils/16187.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed the unintended behavior where the IERS-A file bundled in ``astropy-iers-data`` would be ignored if automatic updating of the IERS-A file were disabled or if downloading the new file failed. diff --git a/docs/changes/utils/16335.feature.rst b/docs/changes/utils/16335.feature.rst deleted file mode 100644 index 1c8495837458..000000000000 --- a/docs/changes/utils/16335.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added the ``astropy.system_info`` function to help document runtime systems in -bug reports. diff --git a/docs/changes/utils/16346.api.rst b/docs/changes/utils/16346.api.rst deleted file mode 100644 index 79e99ed68873..000000000000 --- a/docs/changes/utils/16346.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -``astropy.utils.check_broadcast`` is now deprecated in favor of -``numpy.broadcast_shapes`` diff --git a/docs/changes/utils/16463.api.rst b/docs/changes/utils/16463.api.rst deleted file mode 100644 index e8fcd68068c4..000000000000 --- a/docs/changes/utils/16463.api.rst +++ /dev/null @@ -1 +0,0 @@ -Added a new keyword ``pending_warning_type`` to ``deprecated`` decorator so downstream developers could customize the type of warning for pending deprecation state. diff --git a/docs/changes/utils/16479.api.rst b/docs/changes/utils/16479.api.rst deleted file mode 100644 index c89f111dabd5..000000000000 --- a/docs/changes/utils/16479.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -The ``introspection.resolve_name()`` function is deprecated. -It is better to use the standard library ``importlib`` instead. diff --git a/docs/changes/utils/16503.bugfix.rst b/docs/changes/utils/16503.bugfix.rst deleted file mode 100644 index c52cbb6252d5..000000000000 --- a/docs/changes/utils/16503.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Ensure ``MaskedQuantity`` can be initialized with a list of masked -quantities (as long as their shapes match), just like regular -``Quantity`` and ``ndarray``. diff --git a/docs/changes/utils/16807.api.rst b/docs/changes/utils/16807.api.rst deleted file mode 100644 index 307c8f916096..000000000000 --- a/docs/changes/utils/16807.api.rst +++ /dev/null @@ -1,2 +0,0 @@ -``format_exception()`` is deprecated because it provides little benefit, if -any, over normal Python tracebacks. diff --git a/docs/changes/utils/16844.api.rst b/docs/changes/utils/16844.api.rst deleted file mode 100644 index 6139448bc520..000000000000 --- a/docs/changes/utils/16844.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -The ``utils.masked`` module has gained a mixin class, ``MaskableShapedLikeNDArray``, -as well as two utility functions, ``get_data_and_mask`` and ``combine_masks``, -that can help make a container classes carry masked data. Within astropy, these -are now used in the implementation of masks for ``Time``. diff --git a/docs/changes/utils/16931.feature.rst b/docs/changes/utils/16931.feature.rst deleted file mode 100644 index c9c7174e1e1f..000000000000 --- a/docs/changes/utils/16931.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support for specifying files as ``pathlib.Path`` objects in ``IERS_A.read`` -and ``IERS_B.read``. diff --git a/docs/changes/utils/17014.bugfix.rst b/docs/changes/utils/17014.bugfix.rst deleted file mode 100644 index 1c5983739836..000000000000 --- a/docs/changes/utils/17014.bugfix.rst +++ /dev/null @@ -1,4 +0,0 @@ -For ``Masked`` instances, ``np.put``, ``np.putmask``, ``np.place`` and -``np.copyto`` can now handle putting/copying not just ``np.ma.masked`` but -also ``np.ma.nomask``; for both cases, only the mask of the relevant entries -will be set. diff --git a/docs/changes/utils/17156.bugfix.rst b/docs/changes/utils/17156.bugfix.rst deleted file mode 100644 index 246e566ea52b..000000000000 --- a/docs/changes/utils/17156.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Explicitly set ``usedforsecurity=False`` when using ``hashlib.md5``. Without this, ``hashlib.md5`` will be blocked in FIPS mode. -FIPS (Federal Information Processing Standards) is a set of standards created by NIST (National Institute of Standards and Technology) for US government agencies regarding computer security and interoperability. -This affects download caching. diff --git a/docs/changes/utils/17190.api.rst b/docs/changes/utils/17190.api.rst deleted file mode 100644 index b701c89d3efe..000000000000 --- a/docs/changes/utils/17190.api.rst +++ /dev/null @@ -1 +0,0 @@ -The deprecated ``compat.override__dir__()`` utility has been removed. diff --git a/docs/changes/utils/17387.bugfix.rst b/docs/changes/utils/17387.bugfix.rst deleted file mode 100644 index 71a82d59183c..000000000000 --- a/docs/changes/utils/17387.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed a bug where an old IERS-A table with stale predictive values could trigger -the download of a new IERS-A table even if automatic downloading was disabled. diff --git a/docs/changes/visualization/15081.feature.rst b/docs/changes/visualization/15081.feature.rst deleted file mode 100644 index c75aacb0f664..000000000000 --- a/docs/changes/visualization/15081.feature.rst +++ /dev/null @@ -1,5 +0,0 @@ -Add ``make_rgb()``, a convenience -function for creating RGB images with independent scaling on each filter. -Refactors ``make_lupton_rgb()`` to work with instances of subclasses of -``BaseStretch``, including the new Lupton-specific classes -``LuptonAsinhStretch`` and ``LuptonAsinhZscaleStretch``. diff --git a/docs/changes/visualization/15751.api.rst b/docs/changes/visualization/15751.api.rst deleted file mode 100644 index 1cd338316919..000000000000 --- a/docs/changes/visualization/15751.api.rst +++ /dev/null @@ -1,5 +0,0 @@ -Removed deprecated ``exp`` attribute in the ``LogStretch``, -``InvertedLogStretch``, ``PowerDistStretch``, and -``InvertedPowerDistStretch`` stretch classes, and the ``power`` -attribute in the ``PowerStretch``. Instead, use the ``a`` attribute, -which matches the input keyword. diff --git a/docs/changes/visualization/15781.api.rst b/docs/changes/visualization/15781.api.rst deleted file mode 100644 index 4968543cf531..000000000000 --- a/docs/changes/visualization/15781.api.rst +++ /dev/null @@ -1 +0,0 @@ -Removes the unintended NumPy export previously at ``astropy.visualization.np``. diff --git a/docs/changes/visualization/16347.feature.rst b/docs/changes/visualization/16347.feature.rst deleted file mode 100644 index a65c158e34d5..000000000000 --- a/docs/changes/visualization/16347.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support for custom coordinate frames for ``WCSAxes`` through a context -manager ``astropy.visualization.wcsaxes.custom_ucd_coord_meta_mapping``. diff --git a/docs/changes/visualization/16366.perf.rst b/docs/changes/visualization/16366.perf.rst deleted file mode 100644 index edd79397ee9c..000000000000 --- a/docs/changes/visualization/16366.perf.rst +++ /dev/null @@ -1,4 +0,0 @@ -Removed redundant transformations when WCSAxes determines the coordinate ranges -for ticks/gridlines, which speeds up typical plot generation by ~10%, and by -much more if ``astropy.visualization.wcsaxes.conf.coordinate_range_samples`` is -set to a large value diff --git a/docs/changes/visualization/16685.api.rst b/docs/changes/visualization/16685.api.rst deleted file mode 100644 index 4f10bcd5ec5c..000000000000 --- a/docs/changes/visualization/16685.api.rst +++ /dev/null @@ -1,26 +0,0 @@ -Accessing or setting the following attributes on ``CoordinateHelper`` has been deprecated: - -* ``ticks`` -* ``ticklabels`` -* ``axislabels`` - -Setting the following attributes on ``CoordinateHelper`` directly has been deprecated: - -* ``parent_axes`` -* ``parent_map`` -* ``transform`` -* ``coord_index`` -* ``coord_unit`` -* ``coord_type`` (use ``set_coord_type`` instead) -* ``coord_wrap`` (use ``set_coord_type`` instead) -* ``frame`` -* ``default_label`` - -Accessing or setting the following attributes on ``CoordinateHelper`` has been -removed (without deprecation, as these were clearly internal variables): - -* ``grid_lines_kwargs`` -* ``grid_lines`` -* ``lblinfo`` -* ``lbl_world`` -* ``minor_frequency`` (there were already public methods to set/get this) diff --git a/docs/changes/visualization/16686.feature.rst b/docs/changes/visualization/16686.feature.rst deleted file mode 100644 index 9494457bb6bd..000000000000 --- a/docs/changes/visualization/16686.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added ``get_ticks_position``, ``get_ticklabel_position``, and -``get_axislabel_position`` methods on ``CoordinateHelper`` in WCSAxes. diff --git a/docs/changes/visualization/16938.feature.rst b/docs/changes/visualization/16938.feature.rst deleted file mode 100644 index b81d6d094746..000000000000 --- a/docs/changes/visualization/16938.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added the ability to disable the automatic simplification of WCSAxes tick labels -by specifying ``simplify=False`` to ``set_ticklabel()`` for a coordinate axis. diff --git a/docs/changes/visualization/16985.feature.rst b/docs/changes/visualization/16985.feature.rst deleted file mode 100644 index c6db13ac4653..000000000000 --- a/docs/changes/visualization/16985.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Added the ability to specify that WCSAxes tick labels always include the sign -(namely for positive values) by starting the format string with a ``+`` -character. diff --git a/docs/changes/visualization/17006.feature.rst b/docs/changes/visualization/17006.feature.rst deleted file mode 100644 index e196e06d75f1..000000000000 --- a/docs/changes/visualization/17006.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allow ``astropy.visualization.units.quantity_support`` to be used as a -decorator in addition to the already supported use as a context manager. diff --git a/docs/changes/visualization/17020.feature.rst b/docs/changes/visualization/17020.feature.rst deleted file mode 100644 index b1645183d5e0..000000000000 --- a/docs/changes/visualization/17020.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Added the ability to specify a callable function in ``CoordinateHelper.set_major_formatter`` diff --git a/docs/changes/visualization/17186.api.rst b/docs/changes/visualization/17186.api.rst deleted file mode 100644 index 9d5e2912e7a5..000000000000 --- a/docs/changes/visualization/17186.api.rst +++ /dev/null @@ -1 +0,0 @@ -The deprecated ``nsamples`` parameter of ``ZScaleInterval`` is removed. diff --git a/docs/changes/visualization/17217.feature.rst b/docs/changes/visualization/17217.feature.rst deleted file mode 100644 index 8d58074a9c1f..000000000000 --- a/docs/changes/visualization/17217.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Added a SimpleNorm class to create a matplotlib normalization object. diff --git a/docs/changes/visualization/17243.feature.rst b/docs/changes/visualization/17243.feature.rst deleted file mode 100644 index e04479616847..000000000000 --- a/docs/changes/visualization/17243.feature.rst +++ /dev/null @@ -1 +0,0 @@ -WCSAxes will now select which axis to draw which tick labels and axis labels on based on the number of drawn tick labels, rather than picking them in the order they are listed in the WCS. This means that axes may be swapped in comparison with previous versions of Astropy by default. diff --git a/docs/changes/wcs/16287.api.rst b/docs/changes/wcs/16287.api.rst deleted file mode 100644 index 8a791e94e62e..000000000000 --- a/docs/changes/wcs/16287.api.rst +++ /dev/null @@ -1,3 +0,0 @@ -Errors may now occur if a ``BaseLowLevelWCS`` class defines -``world_axis_object_components`` which returns values that are not scalars or -plain Numpy arrays as per APE 14. diff --git a/docs/changes/wcs/16328.api.rst b/docs/changes/wcs/16328.api.rst deleted file mode 100644 index 674f921a125f..000000000000 --- a/docs/changes/wcs/16328.api.rst +++ /dev/null @@ -1,3 +0,0 @@ -``WCS.pixel_to_world_values``, ``WCS.world_to_pixel_values``, -``WCS.pixel_to_world`` and ``WCS.world_to_pixel`` now correctly return NaN values for -pixel positions that are outside of ``pixel_bounds``. diff --git a/docs/changes/wcs/17236.bugfix.rst b/docs/changes/wcs/17236.bugfix.rst deleted file mode 100644 index 8f391288c54a..000000000000 --- a/docs/changes/wcs/17236.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Avoid a ``RuntimeWarning`` in ``WCS.world_to_array_index`` by converting -NaN inputs to int. From 874fc6e5c5b53c55876bc56b820542c8efb6e82a Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 22 Nov 2024 13:42:40 +0000 Subject: [PATCH 037/145] Finalizing changelog for v6.1.7 --- CHANGES.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 547ef2b63d0e..ce16bf934690 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -716,6 +716,18 @@ Other Changes and Additions making a pull request. In addition, the developer documentation was reorganized and simplified where possible to improve readability and accessibility. [#16561] +Version 6.1.7 (2024-11-22) +========================== + +Bug Fixes +--------- + +astropy.stats +^^^^^^^^^^^^^ + +- Fix an issue in sigma-clipping where the use of ``np.copy()`` was causing + the input data mask to be discarded in cases where ``grow`` was set. [#17402] + Version 6.1.6 (2024-11-11) ========================== From aceff1bbbfa59dfa7dc796717659abef0c852412 Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Sun, 24 Nov 2024 13:43:32 -0500 Subject: [PATCH 038/145] Backport PR #17445: TST: adapt test completness metatest for numpy 2.2 --- astropy/utils/masked/function_helpers.py | 13 ++++++++++++- astropy/utils/masked/tests/test_function_helpers.py | 12 ++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/astropy/utils/masked/function_helpers.py b/astropy/utils/masked/function_helpers.py index 64c7ef66fb15..e501b3ccd7e7 100644 --- a/astropy/utils/masked/function_helpers.py +++ b/astropy/utils/masked/function_helpers.py @@ -15,7 +15,7 @@ import numpy as np from astropy.units.quantity_helper.function_helpers import FunctionAssigner -from astropy.utils.compat import NUMPY_LT_1_24, NUMPY_LT_2_0, NUMPY_LT_2_1 +from astropy.utils.compat import NUMPY_LT_1_24, NUMPY_LT_2_0, NUMPY_LT_2_1, NUMPY_LT_2_2 if NUMPY_LT_2_0: import numpy.core as np_core @@ -99,6 +99,17 @@ """Set of supported numpy functions with a 'like' keyword argument that dispatch on it (NEP 35). """ +if not NUMPY_LT_2_2: + # in numpy 2.2 these are auto detected by numpy itself + # xref https://github.com/numpy/numpy/issues/27451 + SUPPORTED_NEP35_FUNCTIONS |= set() + UNSUPPORTED_FUNCTIONS |= { + np.arange, + np.empty, np.ones, np.zeros, np.full, + np.array, np.asarray, np.asanyarray, np.ascontiguousarray, np.asfortranarray, + np.frombuffer, np.fromfile, np.fromfunction, np.fromiter, np.fromstring, + np.require, np.identity, np.eye, np.tri, np.genfromtxt, np.loadtxt, + } # fmt: skip # Almost all from np.core.fromnumeric defer to methods so are OK. MASKED_SAFE_FUNCTIONS |= { diff --git a/astropy/utils/masked/tests/test_function_helpers.py b/astropy/utils/masked/tests/test_function_helpers.py index da065c45db6a..a360df66a3f5 100644 --- a/astropy/utils/masked/tests/test_function_helpers.py +++ b/astropy/utils/masked/tests/test_function_helpers.py @@ -28,6 +28,7 @@ NUMPY_LT_1_25, NUMPY_LT_2_0, NUMPY_LT_2_1, + NUMPY_LT_2_2, ) from astropy.utils.masked import Masked, MaskedNDArray from astropy.utils.masked.function_helpers import ( @@ -1754,12 +1755,11 @@ def test_setdiff1d(self): def test_basic_testing_completeness(): - assert all_wrapped_functions == ( - tested_functions - | IGNORED_FUNCTIONS - | UNSUPPORTED_FUNCTIONS - | SUPPORTED_NEP35_FUNCTIONS - ) + declared_functions = tested_functions | IGNORED_FUNCTIONS | UNSUPPORTED_FUNCTIONS + if NUMPY_LT_2_2: + declared_functions |= SUPPORTED_NEP35_FUNCTIONS + + assert declared_functions == all_wrapped_functions @pytest.mark.xfail(reason="coverage not completely set up yet") From cff6a816107850886a74c48073063fd607467b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Tue, 26 Nov 2024 12:23:51 +0100 Subject: [PATCH 039/145] Backport PR #17381: BUG: ensure representation masks are used in table joins --- astropy/coordinates/representation/base.py | 1 + astropy/table/tests/test_operations.py | 28 ++++++++++------------ docs/changes/table/17381.bugfix.rst | 2 ++ 3 files changed, 16 insertions(+), 15 deletions(-) create mode 100644 docs/changes/table/17381.bugfix.rst diff --git a/astropy/coordinates/representation/base.py b/astropy/coordinates/representation/base.py index e44f32767d87..0c58d3912d8b 100644 --- a/astropy/coordinates/representation/base.py +++ b/astropy/coordinates/representation/base.py @@ -50,6 +50,7 @@ class BaseRepresentationOrDifferentialInfo(MixinInfo): attrs_from_parent = {"unit"} # Indicates unit is read-only _supports_indexing = False + mask_val = np.ma.masked @staticmethod def default_format(val): diff --git a/astropy/table/tests/test_operations.py b/astropy/table/tests/test_operations.py index 93c1197f33bd..4f1ff294cd54 100644 --- a/astropy/table/tests/test_operations.py +++ b/astropy/table/tests/test_operations.py @@ -31,6 +31,15 @@ from astropy.utils.masked import Masked from astropy.utils.metadata import MergeConflictError +MIXINS_WITH_FULL_MASK_SUPPORT = ( + Quantity, + Time, + TimeDelta, + BaseRepresentationOrDifferential, + SkyCoord, + EarthLocation, # Currently, a Quantity subclass, but that may change. +) + def sort_eq(list1, list2): return sorted(list1) == sorted(list2) @@ -673,7 +682,7 @@ def test_mixin_functionality(self, mixin_cols): # Check for left, right, outer join which requires masking. Works for # the listed mixins classes. - if isinstance(col, (Quantity, Time, TimeDelta, SkyCoord)): + if isinstance(col, MIXINS_WITH_FULL_MASK_SUPPORT): out = table.join(t1, t2, join_type="left") assert len(out) == 3 assert np.all(out["idx"] == [0, 1, 3]) @@ -1464,18 +1473,7 @@ def test_mixin_functionality(self, mixin_cols, empty_table1, empty_table2): t2 = table.QTable([col2], names=["a"]) # Vstack works for these classes: - if isinstance( - col1, - ( - u.Quantity, - Time, - TimeDelta, - SkyCoord, - EarthLocation, - BaseRepresentationOrDifferential, - StokesCoord, - ), - ): + if isinstance(col1, MIXINS_WITH_FULL_MASK_SUPPORT + (StokesCoord,)): out = table.vstack([t1, t2]) assert len(out) == len_col1 + len_col2 assert check_cols_equal(out["a"][:len_col1], col1) @@ -1488,7 +1486,7 @@ def test_mixin_functionality(self, mixin_cols, empty_table1, empty_table2): # Check for outer stack which requires masking. Works for # the listed mixins classes. t2 = table.QTable([col2], names=["b"]) # different from col name for t - if isinstance(col1, (Time, TimeDelta, Quantity, SkyCoord)): + if isinstance(col1, MIXINS_WITH_FULL_MASK_SUPPORT): out = table.vstack([t1, t2], join_type="outer") assert len(out) == len_col1 + len_col2 assert check_cols_equal(out["a"][:len_col1], col1) @@ -2096,7 +2094,7 @@ def test_mixin_functionality(self, mixin_cols): # Check mixin classes that support masking (and that we raise for # those that do not). - if isinstance(col1, (Time, TimeDelta, Quantity, SkyCoord)): + if isinstance(col1, MIXINS_WITH_FULL_MASK_SUPPORT): out = table.hstack([t1, t2], join_type="outer") assert len(out) == len(t1) assert np.all(out["col0_1"] == col1) diff --git a/docs/changes/table/17381.bugfix.rst b/docs/changes/table/17381.bugfix.rst new file mode 100644 index 000000000000..d012ba8bec6c --- /dev/null +++ b/docs/changes/table/17381.bugfix.rst @@ -0,0 +1,2 @@ +Ensure that representations and differentials, like SkyCoord, can be used in +table join operations, by making use of the fact that they can now be masked. From 1ccff2ed51321079848e0f7e983074f0c59b6390 Mon Sep 17 00:00:00 2001 From: Chiara Marmo Date: Tue, 26 Nov 2024 19:01:14 +0100 Subject: [PATCH 040/145] Backport PR #16143: Fix BinTableHDU.copy() memory leak --- astropy/io/fits/fitsrec.py | 1 + astropy/io/fits/tests/test_table.py | 24 ++++++++++++++++++++++++ docs/changes/io.fits/16143.bugfix.rst | 1 + 3 files changed, 26 insertions(+) create mode 100644 docs/changes/io.fits/16143.bugfix.rst diff --git a/astropy/io/fits/fitsrec.py b/astropy/io/fits/fitsrec.py index 01d416143387..624fbafee6c8 100644 --- a/astropy/io/fits/fitsrec.py +++ b/astropy/io/fits/fitsrec.py @@ -596,6 +596,7 @@ def copy(self, order="C"): new = super().copy(order=order) new.__dict__ = copy.deepcopy(self.__dict__) + new._col_weakrefs = weakref.WeakSet() return new @property diff --git a/astropy/io/fits/tests/test_table.py b/astropy/io/fits/tests/test_table.py index a37712384370..4457b1cc6550 100644 --- a/astropy/io/fits/tests/test_table.py +++ b/astropy/io/fits/tests/test_table.py @@ -2858,6 +2858,27 @@ def test_reference_leak2(self, tmp_path): t3.teardown_class() del t3 + @pytest.mark.skipif(not HAVE_OBJGRAPH, reason="requires objgraph") + def test_reference_leak_copyhdu(self): + """Regression test for https://github.com/astropy/astropy/issues/15649""" + + def readfile(filename): + with fits.open(filename) as hdul: + hdu = hdul[1].copy() + + with _refcounting("FITS_rec"): + readfile(self.data("memtest.fits")) + + def test_converted_copy(self): + """ + Test that the bintable converted arrays are copied when the fitsrec is. + Related to https://github.com/astropy/astropy/issues/15649 + """ + + with fits.open(self.data("memtest.fits")) as hdul: + data = hdul[1].data.copy() + assert data._converted is not hdul[1].data._converted + def test_dump_overwrite(self): with fits.open(self.data("table.fits")) as hdul: tbhdu = hdul[1] @@ -3001,6 +3022,9 @@ def test(format_code): q = toto[1].data.field("QUAL_SPE") assert (q[0][4:8] == np.array([0, 0, 0, 0], dtype=np.uint8)).all() assert toto[1].columns[0].format.endswith("J(1571)") + # Test BINTableHDU copy + copytoto = toto[1].copy() + assert (toto[1].data.field("QUAL_SPE")[0][4:8] == q[0][4:8]).all() for code in ("PJ()", "QJ()"): test(code) diff --git a/docs/changes/io.fits/16143.bugfix.rst b/docs/changes/io.fits/16143.bugfix.rst new file mode 100644 index 000000000000..8068886d0f46 --- /dev/null +++ b/docs/changes/io.fits/16143.bugfix.rst @@ -0,0 +1 @@ +Fix memory leak in ```BinTableHDU.copy()``` From 415a621b16b2299bddc2da5d5b5e652a27c2e93f Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:29:46 -0500 Subject: [PATCH 041/145] Backport PR #17456: Move Header.insert example to the correct method --- astropy/io/fits/header.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/astropy/io/fits/header.py b/astropy/io/fits/header.py index e8391b086776..07e94c11b8ca 100644 --- a/astropy/io/fits/header.py +++ b/astropy/io/fits/header.py @@ -47,7 +47,6 @@ "Header.comments", "Header.fromtextfile", "Header.totextfile", - "Header.set", "Header.update", ] @@ -905,21 +904,6 @@ def set(self, keyword, value=None, comment=None, before=None, after=None): ``header[keyword] = value`` and ``header[keyword] = (value, comment)`` respectively. - New keywords can also be inserted relative to existing keywords - using, for example:: - - >>> header.insert('NAXIS1', ('NAXIS', 2, 'Number of axes')) - - to insert before an existing keyword, or:: - - >>> header.insert('NAXIS', ('NAXIS1', 4096), after=True) - - to insert after an existing keyword. - - The only advantage of using :meth:`Header.set` is that it - easily replaces the old usage of :meth:`Header.update` both - conceptually and in terms of function signature. - Parameters ---------- keyword : str @@ -1362,6 +1346,18 @@ def insert(self, key, card, useblanks=True, after=False): Inserts a new keyword+value card into the Header at a given location, similar to `list.insert`. + New keywords can also be inserted relative to existing keywords + using, for example:: + + >>> header = Header({"NAXIS1": 10}) + >>> header.insert('NAXIS1', ('NAXIS', 2, 'Number of axes')) + + to insert before an existing keyword, or:: + + >>> header.insert('NAXIS1', ('NAXIS2', 4096), after=True) + + to insert after an existing keyword. + Parameters ---------- key : int, str, or tuple From 9d38935aea1e2ddb4a6adf3b0356022a7df898f1 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:41:32 -0500 Subject: [PATCH 042/145] Backport PR #17458: Fix ImageHDU.scale with float --- astropy/io/fits/hdu/image.py | 5 ++++- astropy/io/fits/tests/test_image.py | 7 +++++++ docs/changes/io.fits/17458.bugfix.rst | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 docs/changes/io.fits/17458.bugfix.rst diff --git a/astropy/io/fits/hdu/image.py b/astropy/io/fits/hdu/image.py index de8559444942..6559e30c3e91 100644 --- a/astropy/io/fits/hdu/image.py +++ b/astropy/io/fits/hdu/image.py @@ -577,7 +577,10 @@ def _scale_internal( self._header["BLANK"] = blank if self.data.dtype.type != _type: - self.data = np.array(np.around(self.data), dtype=_type) + if issubclass(_type, np.floating): + self.data = np.array(self.data, dtype=_type) + else: + self.data = np.array(np.around(self.data), dtype=_type) # Update the BITPIX Card to match the data self._bitpix = DTYPE2BITPIX[self.data.dtype.name] diff --git a/astropy/io/fits/tests/test_image.py b/astropy/io/fits/tests/test_image.py index 9095c3cd5cb4..c02d15726758 100644 --- a/astropy/io/fits/tests/test_image.py +++ b/astropy/io/fits/tests/test_image.py @@ -1127,6 +1127,13 @@ def test_scale_implicit_casting(): hdu.scale(bzero=1.3) +def test_scale_floats(): + data = np.arange(10) / 10 + hdu = fits.ImageHDU(data) + hdu.scale("float32") + np.testing.assert_array_equal(hdu.data, data.astype("float32")) + + def test_bzero_implicit_casting_compressed(): # Regression test for an issue that occurred because Numpy now does not # allow implicit type casting during inplace operations. Astropy is diff --git a/docs/changes/io.fits/17458.bugfix.rst b/docs/changes/io.fits/17458.bugfix.rst new file mode 100644 index 000000000000..e51bb520f4f1 --- /dev/null +++ b/docs/changes/io.fits/17458.bugfix.rst @@ -0,0 +1 @@ +Fix ``ImageHDU.scale`` with float. From 310e54e1f43b149722123c2209ccd3bd39659f9d Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Wed, 27 Nov 2024 14:17:24 -0500 Subject: [PATCH 043/145] Backport PR #17464: BUG: add missing ufunc helpers for `np.matvec` and `np.vecmat` --- astropy/units/quantity_helper/helpers.py | 7 +++-- astropy/units/tests/test_quantity_ufuncs.py | 25 +++++++++++++++++- astropy/utils/compat/numpycompat.py | 2 ++ astropy/utils/masked/tests/test_masked.py | 29 ++++++++++++++++++--- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/astropy/units/quantity_helper/helpers.py b/astropy/units/quantity_helper/helpers.py index ce199b3057b4..fff84aae83cf 100644 --- a/astropy/units/quantity_helper/helpers.py +++ b/astropy/units/quantity_helper/helpers.py @@ -13,7 +13,7 @@ from astropy.units.core import dimensionless_unscaled, unit_scale_converter from astropy.units.errors import UnitConversionError, UnitsError, UnitTypeError -from astropy.utils.compat.numpycompat import NUMPY_LT_2_0, NUMPY_LT_2_1 +from astropy.utils.compat.numpycompat import NUMPY_LT_2_0, NUMPY_LT_2_1, NUMPY_LT_2_3 if NUMPY_LT_2_0: from numpy.core import umath as np_umath @@ -544,8 +544,11 @@ def helper_clip(f, unit1, unit2, unit3): # ufuncs handled as special cases UFUNC_HELPERS[np.multiply] = helper_multiplication UFUNC_HELPERS[np.matmul] = helper_multiplication -if isinstance(getattr(np, "vecdot", None), np.ufunc): +if not NUMPY_LT_2_0: UFUNC_HELPERS[np.vecdot] = helper_multiplication +if not NUMPY_LT_2_3: + UFUNC_HELPERS[np.vecmat] = helper_multiplication + UFUNC_HELPERS[np.matvec] = helper_multiplication UFUNC_HELPERS[np.divide] = helper_division UFUNC_HELPERS[np.true_divide] = helper_division UFUNC_HELPERS[np.power] = helper_power diff --git a/astropy/units/tests/test_quantity_ufuncs.py b/astropy/units/tests/test_quantity_ufuncs.py index e1d34f71cff2..8e99330b4cca 100644 --- a/astropy/units/tests/test_quantity_ufuncs.py +++ b/astropy/units/tests/test_quantity_ufuncs.py @@ -18,7 +18,7 @@ from astropy.units import quantity_helper as qh from astropy.units.quantity_helper.converters import UfuncHelpers from astropy.units.quantity_helper.helpers import helper_sqrt -from astropy.utils.compat.numpycompat import NUMPY_LT_1_25, NUMPY_LT_2_0 +from astropy.utils.compat.numpycompat import NUMPY_LT_1_25, NUMPY_LT_2_0, NUMPY_LT_2_3 from astropy.utils.compat.optional_deps import HAS_SCIPY if TYPE_CHECKING: @@ -387,6 +387,29 @@ def test_vecdot(self): o = np.vecdot(q1, q2) assert o == (32.0 + 0j) * u.m / u.s + @pytest.mark.skipif( + NUMPY_LT_2_3, reason="np.matvec and np.vecmat are new in NumPy 2.3" + ) + def test_matvec(): + vec = np.arange(3) << u.s + mat = ( + np.array( + [ + [1.0, -1.0, 2.0], + [0.0, 3.0, -1.0], + [-1.0, -1.0, 1.0], + ] + ) + << u.m + ) + ref_matvec = (vec * mat).sum(-1) + res_matvec = np.matvec(mat, vec) + assert_array_equal(res_matvec, ref_matvec) + + ref_vecmat = (vec * mat.T).sum(-1) + res_vecmat = np.vecmat(vec, mat) + assert_array_equal(res_vecmat, ref_vecmat) + @pytest.mark.parametrize("function", (np.divide, np.true_divide)) def test_divide_scalar(self, function): assert function(4.0 * u.m, 2.0 * u.s) == function(4.0, 2.0) * u.m / u.s diff --git a/astropy/utils/compat/numpycompat.py b/astropy/utils/compat/numpycompat.py index 3a9d82d56f01..4e18b2b45fe3 100644 --- a/astropy/utils/compat/numpycompat.py +++ b/astropy/utils/compat/numpycompat.py @@ -15,6 +15,7 @@ "NUMPY_LT_2_0", "NUMPY_LT_2_1", "NUMPY_LT_2_2", + "NUMPY_LT_2_3", "COPY_IF_NEEDED", ] @@ -27,6 +28,7 @@ NUMPY_LT_2_0 = not minversion(np, "2.0") NUMPY_LT_2_1 = not minversion(np, "2.1.0.dev") NUMPY_LT_2_2 = not minversion(np, "2.2.0.dev0") +NUMPY_LT_2_3 = not minversion(np, "2.3.0.dev0") COPY_IF_NEEDED = False if NUMPY_LT_2_0 else None diff --git a/astropy/utils/masked/tests/test_masked.py b/astropy/utils/masked/tests/test_masked.py index a4fdf1b1687c..39213539f1a4 100644 --- a/astropy/utils/masked/tests/test_masked.py +++ b/astropy/utils/masked/tests/test_masked.py @@ -14,7 +14,7 @@ from astropy import units as u from astropy.coordinates import Longitude from astropy.units import Quantity -from astropy.utils.compat import NUMPY_LT_2_0, NUMPY_LT_2_2 +from astropy.utils.compat import NUMPY_LT_2_0, NUMPY_LT_2_2, NUMPY_LT_2_3 from astropy.utils.compat.optional_deps import HAS_PLT from astropy.utils.masked import Masked, MaskedNDArray @@ -821,7 +821,7 @@ def test_matmul_axes(self): assert_array_equal(mxm2.unmasked, exp2) assert_array_equal(mxm2.mask, mask2) - def test_matvec(self): + def test_matmul_matvec(self): result = self.ma @ self.mb assert np.all(result.mask) assert_array_equal(result.unmasked, self.a @ self.b) @@ -835,7 +835,7 @@ def test_matvec(self): assert_array_equal(result3.unmasked, self.a @ self.b) assert_array_equal(result3.mask, new_ma.mask.any(-1)) - def test_vecmat(self): + def test_matmul_vecmat(self): result = self.mb @ self.ma.T assert np.all(result.mask) assert_array_equal(result.unmasked, self.b @ self.a.T) @@ -848,7 +848,7 @@ def test_vecmat(self): assert_array_equal(result3.unmasked, self.b @ self.a.T) assert_array_equal(result3.mask, new_ma.mask.any(0)) - def test_vecvec(self): + def test_matmul_vecvec(self): result = self.mb @ self.mb assert result.shape == () assert result.mask @@ -857,6 +857,27 @@ def test_vecvec(self): result2 = mb_no_mask @ mb_no_mask assert not result2.mask + @pytest.mark.skipif( + NUMPY_LT_2_3, + reason="np.matvec and np.vecmat are new in NumPy 2.3", + ) + def test_matvec_vecmat(self): + vec = Masked(np.arange(3, like=self.a), [True, False, False]) + mat_mask = np.zeros((3, 3), bool) + mat_mask[0, 0] = True + mat = Masked( + np.array([[1.0, -1.0, 2.0], [0.0, 3.0, -1.0], [-1.0, -1.0, 1.0]]), + mat_mask, + ) + + ref_matvec = (vec * mat).sum(-1) + res_matvec = np.matvec(mat, vec) + assert_masked_equal(res_matvec, ref_matvec) + + ref_vecmat = (vec * mat.T).sum(-1) + res_vecmat = np.vecmat(vec, mat) + assert_masked_equal(res_vecmat, ref_vecmat) + class TestMaskedArrayOperators(MaskedOperatorTests): # Some further tests that use strings, which are not useful for Quantity. From 7d3b96496c210ba7c7c579879681d5033bf68498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Thu, 28 Nov 2024 09:47:42 +0100 Subject: [PATCH 044/145] Backport PR #17467: TST: fixup test from pr-17464 --- astropy/units/tests/test_quantity_ufuncs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astropy/units/tests/test_quantity_ufuncs.py b/astropy/units/tests/test_quantity_ufuncs.py index 8e99330b4cca..c7281865abb9 100644 --- a/astropy/units/tests/test_quantity_ufuncs.py +++ b/astropy/units/tests/test_quantity_ufuncs.py @@ -390,7 +390,7 @@ def test_vecdot(self): @pytest.mark.skipif( NUMPY_LT_2_3, reason="np.matvec and np.vecmat are new in NumPy 2.3" ) - def test_matvec(): + def test_matvec(self): vec = np.arange(3) << u.s mat = ( np.array( From 28aa1ce65492cc4764e87db30098dcfec528dbe2 Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Fri, 29 Nov 2024 15:09:07 -0500 Subject: [PATCH 045/145] Backport PR #17469: BUG: ensure array duck types are passed through in unit conversion --- astropy/units/core.py | 30 ++++++++++++++++------------- astropy/units/tests/test_units.py | 18 ++++++++++++++++- docs/changes/units/17469.bugfix.rst | 4 ++++ 3 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 docs/changes/units/17469.bugfix.rst diff --git a/astropy/units/core.py b/astropy/units/core.py index e5cf130e9914..9995bedcb3fe 100644 --- a/astropy/units/core.py +++ b/astropy/units/core.py @@ -2649,35 +2649,39 @@ def def_unit( return result +KNOWN_GOOD = np.ndarray | float | int | complex + + def _condition_arg(value): - """ - Validate value is acceptable for conversion purposes. + """Validate value is acceptable for conversion purposes. - Will convert into an array if not a scalar, and can be converted - into an array + Will convert into an array if not a scalar or array-like, where scalars + and arrays can be python and numpy types, anything that defines + ``__array_namespace__`` or anything that has a ``.dtype`` attribute. Parameters ---------- - value : int or float value, or sequence of such values + value : scalar or array-like Returns ------- - Scalar value or numpy array + Scalar value or array Raises ------ ValueError If value is not as expected + """ - if isinstance(value, (np.ndarray, float, int, complex, np.void)): + if ( + isinstance(value, KNOWN_GOOD) + or hasattr(value, "dtype") + or hasattr(value, "__array_namespace__") + ): return value - dtype = getattr(value, "dtype", None) - if dtype is None: - value = np.array(value) - dtype = value.dtype - - if dtype.kind not in "ifc": + value = np.array(value) + if value.dtype.kind not in "ifc": raise ValueError( "Value not scalar compatible or convertible to " "an int, float, or complex array" diff --git a/astropy/units/tests/test_units.py b/astropy/units/tests/test_units.py index 4d2d7f586d6e..b82adae66f88 100644 --- a/astropy/units/tests/test_units.py +++ b/astropy/units/tests/test_units.py @@ -14,7 +14,7 @@ from astropy import units as u from astropy.tests.helper import PYTEST_LT_8_0 from astropy.units import utils -from astropy.utils.compat.optional_deps import HAS_DASK +from astropy.utils.compat.optional_deps import HAS_ARRAY_API_STRICT, HAS_DASK from astropy.utils.exceptions import AstropyDeprecationWarning FLOAT_EPS = np.finfo(float).eps @@ -1064,6 +1064,22 @@ def test_hash_represents_unit(unit, power): assert hash(tu2) == hash(unit) +@pytest.mark.skipif(not HAS_ARRAY_API_STRICT, reason="tests array_api_strict") +def test_array_api_strict_arrays(): + # Ensure strict array api arrays can be passed in/out of Unit.to() + # Note that those have non-standard dtype. + import array_api_strict as xp + + data1 = xp.asarray([1.0, 2.0, 3.0]) + data2 = u.m.to(u.km, value=data1) + assert isinstance(data2, type(data1)) + assert_allclose(data2, [0.001, 0.002, 0.003]) + + data3 = u.K.to(u.deg_C, value=data1, equivalencies=u.temperature()) + assert isinstance(data3, type(data1)) + assert_allclose(data3, [-272.15, -271.15, -270.15]) + + @pytest.mark.skipif(not HAS_DASK, reason="tests dask.array") def test_dask_arrays(): # Make sure that dask arrays can be passed in/out of Unit.to() diff --git a/docs/changes/units/17469.bugfix.rst b/docs/changes/units/17469.bugfix.rst new file mode 100644 index 000000000000..90a2cc1a8cc3 --- /dev/null +++ b/docs/changes/units/17469.bugfix.rst @@ -0,0 +1,4 @@ +Ensure that ``Unit.to`` allows as ``value`` argument all array types that +follow the array API standard and define ``__array_namespace__``. Furthermore, +for backwards compatibility, generally pass through arguments that define a +``.dtype``, independent of whether that is a numpy data type. From a22faacfa4fa93d591296b4c005cb84690e4fd86 Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Fri, 29 Nov 2024 16:18:32 -0500 Subject: [PATCH 046/145] Backport PR #17328: fix overflow with VLA columns and numpy 2 --- astropy/io/fits/column.py | 10 ++++++++++ astropy/io/fits/fitsrec.py | 6 +----- astropy/io/fits/tests/test_table.py | 5 +---- docs/changes/io.fits/17328.bugfix.rst | 1 + 4 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 docs/changes/io.fits/17328.bugfix.rst diff --git a/astropy/io/fits/column.py b/astropy/io/fits/column.py index 8f81a50b9e38..3bce278015f8 100644 --- a/astropy/io/fits/column.py +++ b/astropy/io/fits/column.py @@ -2271,6 +2271,16 @@ def _makep(array, descr_output, format, nrows=None): nelem = data_output[idx].shape descr_output[idx, 0] = np.prod(nelem) descr_output[idx, 1] = _offset + + # detect overflow when using P format + if ( + descr_output.dtype == np.int32 + and int(descr_output[idx, 0]) * _nbytes >= 2**31 + ): + raise ValueError( + "The heapsize limit for 'P' format has been reached. " + "Please consider using the 'Q' format for your file." + ) _offset += descr_output[idx, 0] * _nbytes return data_output diff --git a/astropy/io/fits/fitsrec.py b/astropy/io/fits/fitsrec.py index 624fbafee6c8..7b1550164088 100644 --- a/astropy/io/fits/fitsrec.py +++ b/astropy/io/fits/fitsrec.py @@ -1125,11 +1125,7 @@ def _scale_back(self, update_heap_pointers=True): # Even if this VLA has not been read or updated, we need to # include the size of its constituent arrays in the heap size # total - if type(recformat) == _FormatP and heapsize >= 2**31: - raise ValueError( - "The heapsize limit for 'P' format has been reached. " - "Please consider using the 'Q' format for your file." - ) + if isinstance(recformat, _FormatX) and name in self._converted: _wrapx(self._converted[name], raw_field, recformat.repeat) continue diff --git a/astropy/io/fits/tests/test_table.py b/astropy/io/fits/tests/test_table.py index 4457b1cc6550..e0f58c5845ff 100644 --- a/astropy/io/fits/tests/test_table.py +++ b/astropy/io/fits/tests/test_table.py @@ -3234,13 +3234,10 @@ def test_heapsize_P_limit(self): col = fits.Column(name="MATRIX", format=f"PD({nelem})", unit="", array=matrix) - t = fits.BinTableHDU.from_columns([col]) - t.name = "MATRIX" - with pytest.raises( ValueError, match="Please consider using the 'Q' format for your file." ): - t.writeto(self.temp("matrix.fits")) + fits.BinTableHDU.from_columns([col]) @pytest.mark.skipif(sys.maxsize < 2**32, reason="requires 64-bit system") @pytest.mark.skipif(sys.platform == "win32", reason="Cannot test on Windows") diff --git a/docs/changes/io.fits/17328.bugfix.rst b/docs/changes/io.fits/17328.bugfix.rst new file mode 100644 index 000000000000..85d221b959ad --- /dev/null +++ b/docs/changes/io.fits/17328.bugfix.rst @@ -0,0 +1 @@ +Fix overflow error with Numpy 2 and VLA columns using P format. From d923675771e08115964b9821ac718b47e2f20176 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 08:11:14 +0000 Subject: [PATCH 047/145] Backport PR #17486: Update minimum required version of astropy-iers-data --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 67cabfa0a7e4..5f6ec0f41e3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ keywords = [ dependencies = [ "numpy>=1.23.2", "pyerfa>=2.0.1.1", - "astropy-iers-data>=0.2024.10.28.0.34.7", + "astropy-iers-data>=0.2024.11.25.0.34.48", "PyYAML>=6.0.0", "packaging>=22.0.0", ] From df72dc1e5b2284dea7c327f50d535d8fc7875390 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 3 Dec 2024 17:00:13 -0500 Subject: [PATCH 048/145] Backport PR #17356: Fix writing COOSYS elements from votable to xml --- astropy/io/votable/tests/data/coosys.xml | 7 +++++-- .../tests/data/regression.bin.tabledata.truth.1.3.xml | 1 + astropy/io/votable/tests/test_coosys.py | 7 ++++++- astropy/io/votable/tree.py | 4 +++- docs/changes/io.votable/17356.bugfix.rst | 1 + 5 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 docs/changes/io.votable/17356.bugfix.rst diff --git a/astropy/io/votable/tests/data/coosys.xml b/astropy/io/votable/tests/data/coosys.xml index eaab39880c89..19d9e21f528a 100644 --- a/astropy/io/votable/tests/data/coosys.xml +++ b/astropy/io/votable/tests/data/coosys.xml @@ -1,18 +1,21 @@ + - - + + + + diff --git a/astropy/io/votable/tests/data/regression.bin.tabledata.truth.1.3.xml b/astropy/io/votable/tests/data/regression.bin.tabledata.truth.1.3.xml index db4e1223a840..3c2b36d84952 100644 --- a/astropy/io/votable/tests/data/regression.bin.tabledata.truth.1.3.xml +++ b/astropy/io/votable/tests/data/regression.bin.tabledata.truth.1.3.xml @@ -12,6 +12,7 @@ itself modeled on the FITS Table format [2]; VOTable was designed to be closer to the FITS Binary Table format. + diff --git a/astropy/io/votable/tests/test_coosys.py b/astropy/io/votable/tests/test_coosys.py index 42ec90e88638..b9d4c7b30f69 100644 --- a/astropy/io/votable/tests/test_coosys.py +++ b/astropy/io/votable/tests/test_coosys.py @@ -77,7 +77,12 @@ def test_coosys_system(): def _coosys_tests(votable): - assert len(list(votable.iter_coosys())) == 2 + assert len(list(votable.iter_coosys())) == 3 + coosys = votable.get_coosys_by_id("coosys0") + assert coosys.system == "ICRS" + assert coosys.equinox == "J2000" + assert coosys.epoch == "J2014.5" + assert coosys.refposition == "BARYCENTER" coosys = votable.get_coosys_by_id("coosys1") assert coosys.system == "ICRS" diff --git a/astropy/io/votable/tree.py b/astropy/io/votable/tree.py index 953cde027f74..1cda9e9054cc 100644 --- a/astropy/io/votable/tree.py +++ b/astropy/io/votable/tree.py @@ -3978,6 +3978,7 @@ def to_xml(self, w, **kwargs): ] if kwargs["version_1_2_or_later"]: element_sets.append(self.groups) + for element_set in element_sets: for element in element_set: element.to_xml(w, **kwargs) @@ -4378,7 +4379,8 @@ def to_xml( self.resources, ] if kwargs["version_1_2_or_later"]: - element_sets[0] = self.groups + element_sets.append(self.groups) + for element_set in element_sets: for element in element_set: element.to_xml(w, **kwargs) diff --git a/docs/changes/io.votable/17356.bugfix.rst b/docs/changes/io.votable/17356.bugfix.rst new file mode 100644 index 000000000000..3fbcb6464f23 --- /dev/null +++ b/docs/changes/io.votable/17356.bugfix.rst @@ -0,0 +1 @@ +Updated XML writer for ``VOTableFile`` element to include or drop ``coordinate_systems`` regardless of version. From 66556924ba6cbcacdd4bbe956d9b736479541633 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 4 Dec 2024 14:18:51 +0000 Subject: [PATCH 049/145] Backport PR #17444: BUG: Fix ticklabels getter in `CoordinateHelper` --- .../visualization/wcsaxes/coordinate_helpers.py | 2 +- .../wcsaxes/tests/test_coordinate_helpers.py | 17 +++++++++++++++++ docs/changes/visualization/17444.bugfix.rst | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 docs/changes/visualization/17444.bugfix.rst diff --git a/astropy/visualization/wcsaxes/coordinate_helpers.py b/astropy/visualization/wcsaxes/coordinate_helpers.py index d46af7217809..a893711590e0 100644 --- a/astropy/visualization/wcsaxes/coordinate_helpers.py +++ b/astropy/visualization/wcsaxes/coordinate_helpers.py @@ -311,7 +311,7 @@ def ticklabels(self): "CoordinateHelper.ticklabels should not be accessed directly and is deprecated", AstropyDeprecationWarning, ) - return self._ticks + return self._ticklabels @ticklabels.setter def ticklabels(self, value): diff --git a/astropy/visualization/wcsaxes/tests/test_coordinate_helpers.py b/astropy/visualization/wcsaxes/tests/test_coordinate_helpers.py index ba49426e4219..3360dc8ca5a5 100644 --- a/astropy/visualization/wcsaxes/tests/test_coordinate_helpers.py +++ b/astropy/visualization/wcsaxes/tests/test_coordinate_helpers.py @@ -9,6 +9,7 @@ from astropy import units as u from astropy.io import fits from astropy.utils.data import get_pkg_data_filename +from astropy.utils.exceptions import AstropyDeprecationWarning from astropy.visualization.wcsaxes.coordinate_helpers import CoordinateHelper from astropy.visualization.wcsaxes.core import WCSAxes from astropy.wcs import WCS @@ -163,3 +164,19 @@ def test_get_position(): assert ax.coords[1].get_ticklabel_position() == ["r", "l"] assert ax.coords[0].get_axislabel_position() == ["t"] assert ax.coords[1].get_axislabel_position() == ["r"] + + +def test_deprecated_getters(): + fig, _ = plt.subplots() + ax = WCSAxes(fig, [0.1, 0.1, 0.8, 0.8], aspect="equal") + helper = CoordinateHelper(parent_axes=ax) + + with pytest.warns(AstropyDeprecationWarning): + ticks = helper.ticks + assert not ticks.get_display_minor_ticks() + with pytest.warns(AstropyDeprecationWarning): + ticklabels = helper.ticklabels + assert ticklabels.text == {} + with pytest.warns(AstropyDeprecationWarning): + axislabels = helper.axislabels + assert axislabels.get_visibility_rule() == "labels" diff --git a/docs/changes/visualization/17444.bugfix.rst b/docs/changes/visualization/17444.bugfix.rst new file mode 100644 index 000000000000..27dce17de488 --- /dev/null +++ b/docs/changes/visualization/17444.bugfix.rst @@ -0,0 +1,2 @@ +Fix ``CoordinateHelper.ticklabels``. The getter was incorrectly returning +the helper's ticks rather than the labels. From 92386df9c170981e2f0ba5828fabafba7aff4991 Mon Sep 17 00:00:00 2001 From: Marten van Kerkwijk Date: Thu, 5 Dec 2024 10:36:38 -0500 Subject: [PATCH 050/145] Backport PR #17501: fix: Ob0 is never None --- astropy/cosmology/flrw/base.py | 20 +++---------------- astropy/cosmology/flrw/tests/test_base.py | 16 +++------------ astropy/cosmology/flrw/tests/test_w0cdm.py | 5 +---- astropy/cosmology/flrw/tests/test_w0wacdm.py | 5 +---- astropy/cosmology/flrw/tests/test_w0wzcdm.py | 7 +------ .../cosmology/flrw/tests/test_wpwazpcdm.py | 5 +---- 6 files changed, 10 insertions(+), 48 deletions(-) diff --git a/astropy/cosmology/flrw/base.py b/astropy/cosmology/flrw/base.py index a640823bbaa6..e3c79ed97a81 100644 --- a/astropy/cosmology/flrw/base.py +++ b/astropy/cosmology/flrw/base.py @@ -176,7 +176,7 @@ class FLRW(Cosmology, _ScaleFactor): provide three neutrino masses unless you are considering something like a sterile neutrino. - Ob0 : float or None, optional + Ob0 : float, optional Omega baryons: density of baryonic matter in units of the critical density at z=0. @@ -346,9 +346,9 @@ def Otot0(self) -> float: return self.Om0 + self.Ogamma0 + self.Onu0 + self.Ode0 + self.Ok0 @cached_property - def Odm0(self) -> float | None: + def Odm0(self) -> float: """Omega dark matter; dark matter density/critical density at z=0.""" - return None if self.Ob0 is None else (self.Om0 - self.Ob0) + return self.Om0 - self.Ob0 @cached_property def Ok0(self) -> float: @@ -506,10 +506,6 @@ def Ob(self, z): each redshift. Returns `float` if the input is scalar. - Raises - ------ - ValueError - If ``Ob0`` is `None`. """ z = aszarr(z) return self.Ob0 * (z + 1.0) ** 3 * self.inv_efunc(z) ** 2 @@ -533,21 +529,11 @@ def Odm(self, z): critical density at each redshift. Returns `float` if the input is scalar. - Raises - ------ - ValueError - If ``Ob0`` is `None`. - Notes ----- This does not include neutrinos, even if non-relativistic at the redshift of interest. """ - if self.Odm0 is None: - raise ValueError( - "Baryonic density not set for this cosmology, " - "unclear meaning of dark matter density" - ) z = aszarr(z) return self.Odm0 * (z + 1.0) ** 3 * self.inv_efunc(z) ** 2 diff --git a/astropy/cosmology/flrw/tests/test_base.py b/astropy/cosmology/flrw/tests/test_base.py index c5b4bbaa4cf6..39ea72f17762 100644 --- a/astropy/cosmology/flrw/tests/test_base.py +++ b/astropy/cosmology/flrw/tests/test_base.py @@ -125,11 +125,7 @@ def test_Odm0(self, cosmo_cls, cosmo): assert isinstance(cosmo_cls.Odm0, cached_property) # on the instance - assert ( - cosmo.Odm0 is None - if cosmo.Ob0 is None - else np.allclose(cosmo.Odm0, cosmo.Om0 - cosmo.Ob0) - ) + assert np.allclose(cosmo.Odm0, cosmo.Om0 - cosmo.Ob0) def test_Ok0(self, cosmo_cls, cosmo): """Test ``cached_property`` ``Ok0``.""" @@ -354,10 +350,7 @@ def test_clone_change_param(self, cosmo): assert c.H0.value == 100 for n, v in filter_keys_from_items(c.parameters, ("H0",)): v_expect = getattr(cosmo, n) - if v is None: - assert v is v_expect - else: - assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) assert not u.allclose(c.Ogamma0, cosmo.Ogamma0) assert not u.allclose(c.Onu0, cosmo.Onu0) @@ -370,10 +363,7 @@ def test_clone_change_param(self, cosmo): assert c.meta == {**cosmo.meta, **dict(zz="tops")} for n, v in filter_keys_from_items(c.parameters, ("H0", "Tcmb0")): v_expect = getattr(cosmo, n) - if v is None: - assert v is v_expect - else: - assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) assert not u.allclose(c.Ogamma0, cosmo.Ogamma0) assert not u.allclose(c.Onu0, cosmo.Onu0) assert not u.allclose(c.Tcmb0.value, cosmo.Tcmb0.value) diff --git a/astropy/cosmology/flrw/tests/test_w0cdm.py b/astropy/cosmology/flrw/tests/test_w0cdm.py index c9dae0626a8a..2d5bff18308c 100644 --- a/astropy/cosmology/flrw/tests/test_w0cdm.py +++ b/astropy/cosmology/flrw/tests/test_w0cdm.py @@ -81,10 +81,7 @@ def test_clone_change_param(self, cosmo): assert c.w0 == 0.1 for n, v in filter_keys_from_items(c.parameters, ("w0",)): v_expect = getattr(cosmo, n) - if v is None: - assert v is v_expect - else: - assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) @pytest.mark.parametrize("z", valid_zs) def test_w(self, cosmo, z): diff --git a/astropy/cosmology/flrw/tests/test_w0wacdm.py b/astropy/cosmology/flrw/tests/test_w0wacdm.py index fb44ca26ba6e..3ef0b8f68c15 100644 --- a/astropy/cosmology/flrw/tests/test_w0wacdm.py +++ b/astropy/cosmology/flrw/tests/test_w0wacdm.py @@ -82,10 +82,7 @@ def test_clone_change_param(self, cosmo): assert c.wa == 0.2 for n, v in filter_keys_from_items(c.parameters, ("w0", "wa")): v_expect = getattr(cosmo, n) - if v is None: - assert v is v_expect - else: - assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) # @pytest.mark.parametrize("z", valid_zs) # TODO! recompute comparisons below def test_w(self, cosmo): diff --git a/astropy/cosmology/flrw/tests/test_w0wzcdm.py b/astropy/cosmology/flrw/tests/test_w0wzcdm.py index 811094a30294..287dc8fb924b 100644 --- a/astropy/cosmology/flrw/tests/test_w0wzcdm.py +++ b/astropy/cosmology/flrw/tests/test_w0wzcdm.py @@ -88,12 +88,7 @@ def test_clone_change_param(self, cosmo): assert c.w0 == 0.1 assert c.wz == 0.2 for n, v in filter_keys_from_items(c.parameters, ("w0", "wz")): - if v is None: - assert v is getattr(cosmo, n) - else: - assert u.allclose( - v, getattr(cosmo, n), atol=1e-4 * getattr(v, "unit", 1) - ) + assert u.allclose(v, getattr(cosmo, n), atol=1e-4 * getattr(v, "unit", 1)) # @pytest.mark.parametrize("z", valid_zs) # TODO! recompute comparisons below def test_w(self, cosmo): diff --git a/astropy/cosmology/flrw/tests/test_wpwazpcdm.py b/astropy/cosmology/flrw/tests/test_wpwazpcdm.py index 3c9c73ee178c..88e56c4765a9 100644 --- a/astropy/cosmology/flrw/tests/test_wpwazpcdm.py +++ b/astropy/cosmology/flrw/tests/test_wpwazpcdm.py @@ -131,10 +131,7 @@ def test_clone_change_param(self, cosmo): assert c.zp == 14 for n, v in filter_keys_from_items(c.parameters, ("wp", "wa", "zp")): v_expect = getattr(cosmo, n) - if v is None: - assert v is v_expect - else: - assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) + assert_quantity_allclose(v, v_expect, atol=1e-4 * getattr(v, "unit", 1)) # @pytest.mark.parametrize("z", valid_zs) # TODO! recompute comparisons below def test_w(self, cosmo): From 8cb276bebe84ab47a59ba14624304ad63ecfe506 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:58:33 +0000 Subject: [PATCH 051/145] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- astropy/cosmology/flrw/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/astropy/cosmology/flrw/base.py b/astropy/cosmology/flrw/base.py index e3c79ed97a81..ff00a0e77e6e 100644 --- a/astropy/cosmology/flrw/base.py +++ b/astropy/cosmology/flrw/base.py @@ -505,7 +505,6 @@ def Ob(self, z): The density of baryonic matter relative to the critical density at each redshift. Returns `float` if the input is scalar. - """ z = aszarr(z) return self.Ob0 * (z + 1.0) ** 3 * self.inv_efunc(z) ** 2 From 011bf19bc770d516ea10b744476218e2fdf03da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Fri, 6 Dec 2024 12:40:54 +0100 Subject: [PATCH 052/145] Backport PR #17510: fix table extensions compilation --- astropy/table/setup_package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astropy/table/setup_package.py b/astropy/table/setup_package.py index 92f4051eeb64..53983824fa2b 100644 --- a/astropy/table/setup_package.py +++ b/astropy/table/setup_package.py @@ -17,7 +17,7 @@ def get_extensions(): Extension( name=f"astropy.table.{source.stem}", define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")], - sources=[str(s) for s in sources], + sources=[str(source)], include_dirs=include_dirs, ) for source in sources From dda4e42fe23cae43a8b002a84e14cc49df7d7127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Moritz=20G=C3=BCnther?= Date: Fri, 6 Dec 2024 15:40:21 -0500 Subject: [PATCH 053/145] Backport PR #17513: BUG: fix a crash in `Table.show_in_browser` due to an internal type inconsistency --- astropy/table/table.py | 2 +- docs/changes/table/17513.bugfix.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 docs/changes/table/17513.bugfix.rst diff --git a/astropy/table/table.py b/astropy/table/table.py index adf76f6f9ba3..7f1e271a2f90 100644 --- a/astropy/table/table.py +++ b/astropy/table/table.py @@ -1922,7 +1922,7 @@ def show_in_browser( except webbrowser.Error: log.error(f"Browser '{browser}' not found.") else: - br.open(urljoin("file:", pathname2url(path))) + br.open(urljoin("file:", pathname2url(str(path)))) @format_doc(_pformat_docs, id="{id}") def pformat( diff --git a/docs/changes/table/17513.bugfix.rst b/docs/changes/table/17513.bugfix.rst new file mode 100644 index 000000000000..ab9933e4b0f9 --- /dev/null +++ b/docs/changes/table/17513.bugfix.rst @@ -0,0 +1 @@ +Fix a crash in ``Table.show_in_browser`` due to an internal type inconsistency. From 9a578ed55722a36962d4520925b28299e441e631 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 9 Dec 2024 09:55:53 -1000 Subject: [PATCH 054/145] Backport PR #17523: TST: fix incorrect branching on numpy version --- astropy/units/quantity_helper/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/astropy/units/quantity_helper/helpers.py b/astropy/units/quantity_helper/helpers.py index fff84aae83cf..512a24106e00 100644 --- a/astropy/units/quantity_helper/helpers.py +++ b/astropy/units/quantity_helper/helpers.py @@ -13,7 +13,7 @@ from astropy.units.core import dimensionless_unscaled, unit_scale_converter from astropy.units.errors import UnitConversionError, UnitsError, UnitTypeError -from astropy.utils.compat.numpycompat import NUMPY_LT_2_0, NUMPY_LT_2_1, NUMPY_LT_2_3 +from astropy.utils.compat.numpycompat import NUMPY_LT_2_0, NUMPY_LT_2_1, NUMPY_LT_2_2 if NUMPY_LT_2_0: from numpy.core import umath as np_umath @@ -546,7 +546,7 @@ def helper_clip(f, unit1, unit2, unit3): UFUNC_HELPERS[np.matmul] = helper_multiplication if not NUMPY_LT_2_0: UFUNC_HELPERS[np.vecdot] = helper_multiplication -if not NUMPY_LT_2_3: +if not NUMPY_LT_2_2: UFUNC_HELPERS[np.vecmat] = helper_multiplication UFUNC_HELPERS[np.matvec] = helper_multiplication UFUNC_HELPERS[np.divide] = helper_division From 099c859d185027298cee328ab54fabd452145a7b Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:24:42 -0500 Subject: [PATCH 055/145] Backport PR #17315: SEC: fix security breaches in GHA workflows detected with zizmor SEC: fix security breaches in GHA workflows detected with zizmor (cherry picked from commit 803c7bd20dae347d99aab8980119c364c5b48610) --- .github/workflows/ci_benchmark.yml | 9 ++++++--- .github/workflows/ci_cron_daily.yml | 1 + .github/workflows/ci_cron_weekly.yml | 3 +++ .github/workflows/codeql-analysis.yml | 1 + .github/workflows/open_actions.yml | 2 +- .github/workflows/update_astropy_iers_data_pin.yml | 2 ++ 6 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci_benchmark.yml b/.github/workflows/ci_benchmark.yml index ea8f3fbf2a0b..9e340e236465 100644 --- a/.github/workflows/ci_benchmark.yml +++ b/.github/workflows/ci_benchmark.yml @@ -28,6 +28,7 @@ jobs: steps: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: + persist-credentials: false fetch-depth: 0 - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 @@ -69,6 +70,8 @@ jobs: OMP_NUM_THREADS: 1 ASV_FACTOR: 1.3 ASV_SKIP_SLOW: 1 + BASE_SHA: ${{ github.event.pull_request.base.sha }} + BASE_LABEL: ${{ github.event.pull_request.base.label }} run: | set -x python -m pip install asv virtualenv packaging @@ -78,12 +81,12 @@ jobs: # ID this runner python -m asv machine --yes --conf asv.ci.conf.json - echo "Baseline: ${{ github.event.pull_request.base.sha }} (${{ github.event.pull_request.base.label }})" - echo "Contender: ${GITHUB_SHA} (${{ github.event.pull_request.head.label }})" + echo "Baseline: ${BASE_SHA} (${BASE_LABEL})" + echo "Contender: ${GITHUB_SHA} (${BASE_LABEL})" # Run benchmarks for current commit against base ASV_OPTIONS="--split --show-stderr --factor $ASV_FACTOR --conf asv.ci.conf.json" - python -m asv continuous $ASV_OPTIONS ${{ github.event.pull_request.base.sha }} ${GITHUB_SHA} + python -m asv continuous $ASV_OPTIONS ${BASE_SHA} ${GITHUB_SHA} - name: "Check ccache performance" shell: bash -l {0} diff --git a/.github/workflows/ci_cron_daily.yml b/.github/workflows/ci_cron_daily.yml index 9d33731e7708..7f9b72dafe72 100644 --- a/.github/workflows/ci_cron_daily.yml +++ b/.github/workflows/ci_cron_daily.yml @@ -56,6 +56,7 @@ jobs: - name: Checkout code uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: + persist-credentials: false fetch-depth: 0 - name: Set up Python diff --git a/.github/workflows/ci_cron_weekly.yml b/.github/workflows/ci_cron_weekly.yml index acc4d70e40ae..2421fc4ba6bc 100644 --- a/.github/workflows/ci_cron_weekly.yml +++ b/.github/workflows/ci_cron_weekly.yml @@ -75,6 +75,7 @@ jobs: - name: Checkout code uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: + persist-credentials: false fetch-depth: 0 - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 @@ -118,6 +119,7 @@ jobs: steps: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: + persist-credentials: false fetch-depth: 0 - uses: uraimo/run-on-arch-action@5397f9e30a9b62422f302092631c99ae1effcd9e # v2.8.1 name: Run tests @@ -182,6 +184,7 @@ jobs: steps: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: + persist-credentials: false fetch-depth: 0 - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b7922add814d..114c227c05ae 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,6 +40,7 @@ jobs: - name: Checkout repository uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: + persist-credentials: false fetch-depth: 0 # Initializes the CodeQL tools for scanning. diff --git a/.github/workflows/open_actions.yml b/.github/workflows/open_actions.yml index fe03a912c1a6..519f6f23cd77 100644 --- a/.github/workflows/open_actions.yml +++ b/.github/workflows/open_actions.yml @@ -4,7 +4,7 @@ on: issues: types: - opened - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] types: - opened diff --git a/.github/workflows/update_astropy_iers_data_pin.yml b/.github/workflows/update_astropy_iers_data_pin.yml index 2721f38cae63..d8ff7e6b4c5c 100644 --- a/.github/workflows/update_astropy_iers_data_pin.yml +++ b/.github/workflows/update_astropy_iers_data_pin.yml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + with: + persist-credentials: false - name: Set up Python uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: From da0d4dbb7557007c244ce8650f1b6d6fa36049fc Mon Sep 17 00:00:00 2001 From: Tom Aldcroft Date: Wed, 11 Dec 2024 05:21:25 -0500 Subject: [PATCH 056/145] Backport PR #17520: DOC: add documentation on why astropy has tables --- docs/table/index.rst | 44 +++++----- docs/table/pandas.rst | 125 ++++++++++++++++++++++++++-- docs/table/table_and_dataframes.rst | 91 ++++++++++++++++++++ 3 files changed, 232 insertions(+), 28 deletions(-) create mode 100644 docs/table/table_and_dataframes.rst diff --git a/docs/table/index.rst b/docs/table/index.rst index 1c19a61ee989..3d2252ceb31c 100644 --- a/docs/table/index.rst +++ b/docs/table/index.rst @@ -7,24 +7,22 @@ Data Tables (`astropy.table`) Introduction ============ -`astropy.table` provides functionality for storing and manipulating -heterogeneous tables of data in a way that is familiar to ``numpy`` users. A few -notable capabilities of this package are: - -* Initialize a table from a wide variety of input data structures and types. -* Modify a table by adding or removing columns, changing column names, - or adding new rows of data. -* Handle tables containing missing values. -* Include table and column metadata as flexible data structures. -* Specify a description, units, and output formatting for columns. -* Interactively scroll through long tables similar to using ``more``. -* Create a new table by selecting rows or columns from a table. +`astropy.table` provides a flexible and easy-to-use set of tools for working with +tabular data using an interface based on `numpy`. In addition to basic table creation, +access, and modification operations, key features include: + +* Support columns of astropy :ref:`time `, :ref:`coordinates `, and :ref:`quantities `. +* Support multidimensional and :ref:`structured array columns `. +* Maintain the units, description, and format of columns. +* Provide flexible metadata structures for the table and individual columns. * Perform :ref:`table_operations` like database joins, concatenation, and binning. * Maintain a table index for fast retrieval of table items or ranges. -* Manipulate multidimensional and :ref:`structured array columns `. -* Handle non-native (mixin) column types within table. -* Methods for :ref:`read_write_tables` to files. -* Hooks for :ref:`subclassing_table` and its component classes. +* Support a general :ref:`mixin protocol ` for flexible data containers in tables. +* :ref:`Read and write ` to files via the :ref:`Unified File Read/Write Interface `. +* Convert to and from `pandas.DataFrame`. + +The :ref:`astropy-table-and-dataframes` page provides the rationale for maintaining +and using the dedicated `astropy.table` package instead of relying on `pandas`. Getting Started =============== @@ -341,6 +339,14 @@ Masking masking.rst +Mixin Columns +------------- + +.. toctree:: + :maxdepth: 2 + + mixin_columns.rst + I/O with Tables --------------- @@ -350,13 +356,13 @@ I/O with Tables io.rst pandas.rst -Mixin Columns -------------- +Astropy Table and DataFrames +---------------------------- .. toctree:: :maxdepth: 2 - mixin_columns.rst + table_and_dataframes.rst Implementation -------------- diff --git a/docs/table/pandas.rst b/docs/table/pandas.rst index 678fabfdf966..553eff67a267 100644 --- a/docs/table/pandas.rst +++ b/docs/table/pandas.rst @@ -14,8 +14,8 @@ the :class:`pandas.DataFrame` class (the main data structure in ``pandas``), the |Table| class includes two methods, :meth:`~astropy.table.Table.to_pandas` and :meth:`~astropy.table.Table.from_pandas`. -Example -------- +Basic Example +------------- .. EXAMPLE START: Interfacing Tables with the Pandas Package @@ -53,17 +53,124 @@ It is also possible to create a table from a :class:`~pandas.DataFrame`:: .. EXAMPLE END +Details +------- The conversions to and from ``pandas`` are subject to the following caveats: * The :class:`~pandas.DataFrame` structure does not support multidimensional columns, so |Table| objects with multidimensional columns cannot be converted to :class:`~pandas.DataFrame`. -* Masked tables can be converted, but in columns of ``float`` or string values - the resulting :class:`~pandas.DataFrame` uses `numpy.nan` to indicate missing - values. For ``float`` columns, the conversion therefore does not necessarily - round-trip if converting back to an ``astropy`` table, because the - distinction between `numpy.nan` and masked values is lost. This is not a - problem for integer columns. +* Masked tables can be converted, but pandas uses a sentinel such as `numpy.nan` to + represent missing values. Astropy uses a separate mask array to represent masked + values, where the value "under the mask" is preserved. When converting any astropy + masked column to a pandas DataFrame, the original values are lost. + +* Tables with :ref:`mixin_columns` such as `~astropy.time.Time`, + `~astropy.coordinates.SkyCoord`, and |Quantity| can be converted, but + *with loss of information or fidelity*. For instance, `~astropy.time.Time` columns + will be converted to a `pandas TimeSeries + `_, but this object has + only 64-bit precision and does not support leap seconds or time scales. + +These issues are highlighted below in a more complex example with a table that includes +masked and mixin columns. + +.. EXAMPLE START: Interfacing Tables with the Pandas Package (Complex Example) + +First we create a table with a masked columns and a mixin column:: + + >>> import numpy as np + >>> from astropy.table import MaskedColumn, QTable + >>> from astropy.time import Time + >>> from astropy.coordinates import SkyCoord + >>> import astropy.units as u + >>> t = QTable() + >>> t['a'] = MaskedColumn([1, 2, 3], mask=[False, True, False]) + >>> t['b'] = MaskedColumn([1.0, 2.0, 3.0], mask=[False, False, True]) + >>> t['c'] = MaskedColumn(["a", "b", "c"], mask=[True, False, False]) + >>> t['tm'] = Time(["2021-01-01", "2021-01-02", "2021-01-03"]) + >>> t['sc'] = SkyCoord(ra=[1, 2, 3] * u.deg, dec=[4, 5, 6] * u.deg) + >>> t['q'] = [1, 2, 3] * u.m + + >>> t + + a b c tm sc q + deg,deg m + int64 float64 str1 Time SkyCoord float64 + ----- ------- ---- ----------------------- -------- ------- + 1 1.0 -- 2021-01-01 00:00:00.000 1.0,4.0 1.0 + -- 2.0 b 2021-01-02 00:00:00.000 2.0,5.0 2.0 + 3 -- c 2021-01-03 00:00:00.000 3.0,6.0 3.0 + +Now we convert this table to a :class:`~pandas.DataFrame`:: + + >>> df = t.to_pandas() + >>> df + a b c tm sc.ra sc.dec q + 0 1 1.0 NaN 2021-01-01 1.0 4.0 1.0 + 1 2.0 b 2021-01-02 2.0 5.0 2.0 + 2 3 NaN c 2021-01-03 3.0 6.0 3.0 + + >>> df.info() + + RangeIndex: 3 entries, 0 to 2 + Data columns (total 7 columns): + # Column Non-Null Count Dtype + --- ------ -------------- ----- + 0 a 2 non-null Int64 + 1 b 2 non-null float64 + 2 c 2 non-null object + 3 tm 3 non-null datetime64[ns] + 4 sc.ra 3 non-null float64 + 5 sc.dec 3 non-null float64 + 6 q 3 non-null float64 + dtypes: Int64(1), datetime64[ns](1), float64(4), object(1) + memory usage: 303.0+ bytes + +Notice a few things: + +- The masked values in the original table are replaced with sentinel values + in pandas. The integer column ``a`` is converted to a nullable integer column, and + the string column ``c`` is converted to an ``object`` column. +- The `~astropy.time.Time` object is converted to a pandas TimeSeries using + ``datetime64[ns]``. +- The `~astropy.coordinates.SkyCoord` object is converted to two float columns + ``sc.ra`` and ``sc.dec``, and the unit is lost. +- The `~astropy.units.Quantity` object is converted to a float column and the unit is + lost. + +Now convert back to a table:: + + >>> t_df = QTable.from_pandas(df) + >>> t_df + + a b c tm sc.ra sc.dec q + int64 float64 str1 Time float64 float64 float64 + ----- ------- ---- ----------------------- ------- ------- ------- + 1 1.0 -- 2021-01-01T00:00:00.000 1.0 4.0 1.0 + -- 2.0 b 2021-01-02T00:00:00.000 2.0 5.0 2.0 + 3 -- c 2021-01-03T00:00:00.000 3.0 6.0 3.0 + +The `~astropy.time.Time` column is restored (subject to the limitations discussed +previously), but the `~astropy.coordinates.SkyCoord` and `~astropy.units.Quantity` +columns are not restored as they were in the original table. + +Finally see that the masked values in the original table are replaced with zero or "" in +the round-trip conversion:: + + # Original data values + >>> for nm in 'a', 'b', 'c': + ... print(t[nm].data.data) + [1 2 3] + [1. 2. 3.] + ['a' 'b' 'c'] + + # Data values after round-trip conversion + >>> for nm in 'a', 'b', 'c': + ... print(t_df[nm].data.data) + [1 0 3] + [ 1. 2. nan] + ['' 'b' 'c'] -* Tables with :ref:`mixin_columns` can not be converted. +.. EXAMPLE END diff --git a/docs/table/table_and_dataframes.rst b/docs/table/table_and_dataframes.rst new file mode 100644 index 000000000000..4bd4c6ad8b34 --- /dev/null +++ b/docs/table/table_and_dataframes.rst @@ -0,0 +1,91 @@ +.. _astropy-table-and-dataframes: + +Astropy Table and DataFrames +============================ + +`Pandas `_ is a popular data manipulation library for Python +that provides a `~pandas.DataFrame` object which is similar to `astropy.table`. A common +question is why Astropy does not use `~pandas.DataFrame` as the base table object. The +answer stems from a number of domain-specific requirements related to astronomical data +and analysis. + +Units and Quantities +-------------------- + +Astronomy is a physical science, and the data often have units associated with +them. The `astropy.table` package natively supports |Quantity| columns, which are a +powerful way to attach units to array data and perform unit-aware operations. In +addition, the base `~astropy.table.Column` class holds a ``unit`` attribute as +metadata to allow tracking of the units of the data for applications not using +|Quantity|. + +*Pandas does not provide support for units.* + +Multi-dimensional and Structured Columns +---------------------------------------- + +Astronomers deal with images, spectra, and other multi-dimensional data that are +commonly stored in a table. An example is a source catalog with an image thumbnail and a +spectrum for each source. Structured columns are less common, but are useful for storing +vectorized data like an `~astropy.coordinates.EarthLocation` in a table. + +*Pandas is not able to natively store multi-dimensional or structured columns.* + +Lossless representation of FITS and VOTable data via metadata +------------------------------------------------------------- + +The `astropy.table` package strives to provide lossless representation of FITS and +VOTable data. This means that when you read a FITS or VOTable file into a table and then +write it back out, the data will be effectively identical. This is made possible by +robust support for table and column metadata which allows storing and propagating common +column information such as the unit, description, and format. For VOTable data, more +information like the UCD is maintained. + +*Pandas provides limited support for metadata, but as of late-2024 it is highlighted as +"experimental" in the documentation.* + +Time and Coordinates +-------------------- + +Time and coordinates are fundamental to astronomy, and astropy provides robust support +for them with the `~astropy.time.Time` and `~astropy.coordinates.SkyCoord` classes. +Arrays of times and coordinates can be natively stored in `astropy.table`, meaning that +the full power of these objects is available when working with them as columns within a +table. + +*Pandas supports* `timeseries +`_ *data, but with key +limitations*: + +- Leap seconds are not supported. In many circumstances (for instance planning an + observation) this limitation is not acceptable. +- Pandas times are stored with 64-bit precision, which is not sufficient for some + astronomical applications. Astropy uses 128-bit precision for time to allow + sub-nanosecond precision over the age of the universe. +- Different :ref:`time scales ` common in astronomy (e.g., TAI, UT1) are + not supported. +- :ref:`Time formats ` used in astronomy such as the FITS time format are + not supported. + +*Pandas does not support sky coordinate columns.* + +Responsiveness to Community Needs +--------------------------------- + +The `astropy.table` package is developed by the Astropy community, which is focused on +the needs of astronomers and astrophysicists. This means that the development of the +package can be responsive to the needs of this community and we can develop features +without being constrained by the potential impact to the far broader user base of +Pandas. + +Interoperability +---------------- + +We recognize that Pandas is a popular library and that there are many users who are +familiar with it. For this reason, we have made it easy to convert between +`astropy.table` and `~pandas.DataFrame`, as documented in :ref:`pandas`. This allows +users to take advantage of the features of both packages as needed, +within the limitations stated above. + +We are also committed to supporting interoperability with a more generalized concept of +the DataFrame, with packages like `polars `_ gaining popularity. From b331f3af0e8966735dbc198fb2a1bd4d940dfb5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Wed, 11 Dec 2024 16:19:57 +0100 Subject: [PATCH 057/145] Backport PR #17529: Change setting up temporary directories in `astropy` test runner --- astropy/tests/runner.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/astropy/tests/runner.py b/astropy/tests/runner.py index 1a6b513d14cf..92fabbc6b3b7 100644 --- a/astropy/tests/runner.py +++ b/astropy/tests/runner.py @@ -10,8 +10,8 @@ import warnings from functools import wraps from importlib.util import find_spec +from pathlib import Path -from astropy.config.paths import set_temp_cache, set_temp_config from astropy.utils import find_current_module from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyWarning @@ -235,23 +235,25 @@ def run_tests(self, **kwargs): else: plugins = [] - # Override the config locations to not make a new directory nor use - # existing cache or config. Note that we need to do this here in + # Avoid the existing config. Note that we need to do this here in # addition to in conftest.py - for users running tests interactively # in e.g. IPython, conftest.py would get read in too late, so we need # to do it here - but at the same time the code here doesn't work when # running tests in parallel mode because this uses subprocesses which # don't know about the temporary config/cache. - astropy_config = tempfile.mkdtemp("astropy_config") - astropy_cache = tempfile.mkdtemp("astropy_cache") - - # Have to use nested with statements for cross-Python support - # Note, using these context managers here is superfluous if the - # config_dir or cache_dir options to pytest are in use, but it's - # also harmless to nest the contexts - with set_temp_config(astropy_config, delete=True): - with set_temp_cache(astropy_cache, delete=True): + # Note, this is superfluous if the config_dir option to pytest is in use, + # but it's also harmless + orig_xdg_config = os.environ.get("XDG_CONFIG_HOME") + with tempfile.TemporaryDirectory("astropy_config") as astropy_config: + Path(astropy_config, "astropy").mkdir() + os.environ["XDG_CONFIG_HOME"] = astropy_config + try: return pytest.main(args=args, plugins=plugins) + finally: + if orig_xdg_config is None: + os.environ.pop("XDG_CONFIG_HOME", None) + else: + os.environ["XDG_CONFIG_HOME"] = orig_xdg_config @classmethod def make_test_runner_in(cls, path): From 8bb65748c925baa80b559eb4b54fd5c85acb6824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Wed, 11 Dec 2024 20:39:58 +0100 Subject: [PATCH 058/145] MNT: bump OpenAstronomy's publish action (1.13.0 -> 1.15.0) (v7.0.x) --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index dcb46d44363e..d9f40e5aab38 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,7 +26,7 @@ jobs: # or if triggered manually via the workflow dispatch, or for a tag. permissions: contents: none - uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@924441154cf3053034c6513d5e06c69d262fb9a6 # v1.13.0 + uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@9f1f43251dde69da8613ea8e11144f05cdea41d5 # v1.15.0 if: (github.repository == 'astropy/astropy' && ( startsWith(github.ref, 'refs/tags/v') || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'Build all wheels'))) with: From 09d3964b5896da0bab6a7be2d694d8bf2d6393ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Thu, 12 Dec 2024 08:41:35 +0100 Subject: [PATCH 059/145] Backport PR #17514: Fix configuring `astropy` cache location --- astropy/config/paths.py | 5 ++--- astropy/config/tests/test_configs.py | 15 +++++++++++++++ docs/changes/config/17514.bugfix.rst | 11 +++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 docs/changes/config/17514.bugfix.rst diff --git a/astropy/config/paths.py b/astropy/config/paths.py index 21c3f71c7baf..0361e927808d 100644 --- a/astropy/config/paths.py +++ b/astropy/config/paths.py @@ -27,10 +27,9 @@ def _get_dir_path(rootname: str, cls: type, fallback: str) -> Path: path.mkdir(exist_ok=True) return path.resolve() - # first look for XDG_CONFIG_HOME if ( - (xdg_config_home := os.getenv("XDG_CONFIG_HOME")) is not None - and (xch := Path(xdg_config_home)).exists() + (xdg_dir := os.getenv(f"XDG_{fallback.upper()}_HOME")) is not None + and (xch := Path(xdg_dir)).exists() and not (xchpth := xch / rootname).is_symlink() ): if xchpth.exists(): diff --git a/astropy/config/tests/test_configs.py b/astropy/config/tests/test_configs.py index 99997fb78481..ea238a802dd2 100644 --- a/astropy/config/tests/test_configs.py +++ b/astropy/config/tests/test_configs.py @@ -37,6 +37,21 @@ def test_paths(): assert "testpkg" in paths.get_cache_dir(rootname="testpkg") +@pytest.mark.parametrize( + "environment_variable,func", + [ + # Regression test for #17514 - XDG_CACHE_HOME had no effect + pytest.param("XDG_CACHE_HOME", paths.get_cache_dir_path, id="cache"), + pytest.param("XDG_CONFIG_HOME", paths.get_config_dir_path, id="config"), + ], +) +def test_xdg_variables(monkeypatch, tmp_path, environment_variable, func): + config_dir = tmp_path / "astropy" + config_dir.mkdir() + monkeypatch.setenv(environment_variable, str(tmp_path)) + assert func() == config_dir + + def test_set_temp_config(tmp_path, monkeypatch): # Check that we start in an understood state. assert configuration._cfgobjs == OLD_CONFIG diff --git a/docs/changes/config/17514.bugfix.rst b/docs/changes/config/17514.bugfix.rst new file mode 100644 index 000000000000..a42652499ecf --- /dev/null +++ b/docs/changes/config/17514.bugfix.rst @@ -0,0 +1,11 @@ +With ``astropy`` v7.0.0 the cache directory cannot be customized with the +``XDG_CACHE_HOME`` environment variable. +Instead, ``XDG_CONFIG_HOME`` erroneously controls both configuration and cache +directories. +The correct pre-v7.0.0 behaviour has been restored, but it is possible that +``astropy`` v7.0.0 has written cache files to surprising locations. +Concerned users can use the ``get_cache_dir_path()`` function to check where +the cache files are written. + +The bug in question does not affect systems where the ``XDG_CACHE_HOME`` and +``XDG_CONFIG_HOME`` environment variables are unset. From e6e31a78f8ae49e2ef4d4ac98f66a0270382fdf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Mon, 16 Dec 2024 11:47:50 +0100 Subject: [PATCH 060/145] Backport PR #17544: MAINT: remove duplicated calculation of number of points --- astropy/utils/masked/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/astropy/utils/masked/core.py b/astropy/utils/masked/core.py index 65a332ad32c0..8fd99a203fb5 100644 --- a/astropy/utils/masked/core.py +++ b/astropy/utils/masked/core.py @@ -1302,7 +1302,6 @@ def mean(self, axis=None, dtype=None, out=None, keepdims=False, *, where=True): n = np.add.reduce(where, axis=axis, keepdims=keepdims) # catch the case when an axis is fully masked to prevent div by zero: - n = np.add.reduce(where, axis=axis, keepdims=keepdims) neq0 = n == 0 n += neq0 result /= n From 49c37346c8b3e4aff7e7691a59402268daed8baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20Moritz=20G=C3=BCnther?= Date: Fri, 29 Nov 2024 07:20:03 -0500 Subject: [PATCH 061/145] Update bundeled datatables with current version --- .pre-commit-config.yaml | 2 +- astropy/config/tests/test_configs.py | 2 +- astropy/extern/jquery/data/css/datatables.css | 795 + .../extern/jquery/data/css/datatables.min.css | 15 + .../jquery/data/css/jquery.dataTables.css | 459 - .../{jquery.dataTables.js => datatables.js} | 14403 +++++++--------- .../extern/jquery/data/js/datatables.min.js | 22 + .../jquery/data/js/jquery.dataTables.min.js | 184 - astropy/table/jsviewer.py | 10 +- astropy/table/tests/test_jsviewer.py | 23 +- docs/changes/17480.other.rst | 1 + 11 files changed, 7195 insertions(+), 8721 deletions(-) create mode 100644 astropy/extern/jquery/data/css/datatables.css create mode 100644 astropy/extern/jquery/data/css/datatables.min.css delete mode 100644 astropy/extern/jquery/data/css/jquery.dataTables.css rename astropy/extern/jquery/data/js/{jquery.dataTables.js => datatables.js} (53%) create mode 100644 astropy/extern/jquery/data/js/datatables.min.js delete mode 100644 astropy/extern/jquery/data/js/jquery.dataTables.min.js create mode 100644 docs/changes/17480.other.rst diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b358e7d1dfb4..77f69491632e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: exclude: "^(\ cextern/wcslib/C/flexed/.*|\ CHANGES.rst|\ - astropy/extern/jquery/data/js/jquery.dataTables.js|\ + astropy/extern/jquery/data/js/dataTables.js|\ )$" # Prevent giant files from being committed. - id: check-case-conflict diff --git a/astropy/config/tests/test_configs.py b/astropy/config/tests/test_configs.py index ea238a802dd2..44daffffca8b 100644 --- a/astropy/config/tests/test_configs.py +++ b/astropy/config/tests/test_configs.py @@ -168,7 +168,7 @@ def check_config(conf): assert "[table]" in conf assert "# replace_warnings = ," in conf assert "[table.jsviewer]" in conf - assert "# css_urls = https://cdn.datatables.net/1.10.12/css/jquery.dataTables.css," in conf # fmt: skip + assert "# css_urls = https://cdn.datatables.net/2.1.8/css/dataTables.dataTables.min.css" in conf # fmt: skip assert "[visualization.wcsaxes]" in conf assert "## Whether to log exceptions before raising them." in conf assert "# log_exceptions = False" in conf diff --git a/astropy/extern/jquery/data/css/datatables.css b/astropy/extern/jquery/data/css/datatables.css new file mode 100644 index 000000000000..ca909dfd88ff --- /dev/null +++ b/astropy/extern/jquery/data/css/datatables.css @@ -0,0 +1,795 @@ +/* + * This combined file was created by the DataTables downloader builder: + * https://datatables.net/download + * + * To rebuild or modify this file with the latest versions of the included + * software please visit: + * https://datatables.net/download/#dt/dt-2.1.8 + * + * Included libraries: + * DataTables 2.1.8 + */ + +@charset "UTF-8"; +:root { + --dt-row-selected: 13, 110, 253; + --dt-row-selected-text: 255, 255, 255; + --dt-row-selected-link: 9, 10, 11; + --dt-row-stripe: 0, 0, 0; + --dt-row-hover: 0, 0, 0; + --dt-column-ordering: 0, 0, 0; + --dt-html-background: white; +} +:root.dark { + --dt-html-background: rgb(33, 37, 41); +} + +table.dataTable td.dt-control { + text-align: center; + cursor: pointer; +} +table.dataTable td.dt-control:before { + display: inline-block; + box-sizing: border-box; + content: ""; + border-top: 5px solid transparent; + border-left: 10px solid rgba(0, 0, 0, 0.5); + border-bottom: 5px solid transparent; + border-right: 0px solid transparent; +} +table.dataTable tr.dt-hasChild td.dt-control:before { + border-top: 10px solid rgba(0, 0, 0, 0.5); + border-left: 5px solid transparent; + border-bottom: 0px solid transparent; + border-right: 5px solid transparent; +} + +html.dark table.dataTable td.dt-control:before, +:root[data-bs-theme=dark] table.dataTable td.dt-control:before, +:root[data-theme=dark] table.dataTable td.dt-control:before { + border-left-color: rgba(255, 255, 255, 0.5); +} +html.dark table.dataTable tr.dt-hasChild td.dt-control:before, +:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before, +:root[data-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before { + border-top-color: rgba(255, 255, 255, 0.5); + border-left-color: transparent; +} + +div.dt-scroll { + width: 100%; +} + +div.dt-scroll-body thead tr, +div.dt-scroll-body tfoot tr { + height: 0; +} +div.dt-scroll-body thead tr th, div.dt-scroll-body thead tr td, +div.dt-scroll-body tfoot tr th, +div.dt-scroll-body tfoot tr td { + height: 0 !important; + padding-top: 0px !important; + padding-bottom: 0px !important; + border-top-width: 0px !important; + border-bottom-width: 0px !important; +} +div.dt-scroll-body thead tr th div.dt-scroll-sizing, div.dt-scroll-body thead tr td div.dt-scroll-sizing, +div.dt-scroll-body tfoot tr th div.dt-scroll-sizing, +div.dt-scroll-body tfoot tr td div.dt-scroll-sizing { + height: 0 !important; + overflow: hidden !important; +} + +table.dataTable thead > tr > th:active, +table.dataTable thead > tr > td:active { + outline: none; +} +table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before { + position: absolute; + display: block; + bottom: 50%; + content: "▲"; + content: "▲"/""; +} +table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after { + position: absolute; + display: block; + top: 50%; + content: "▼"; + content: "▼"/""; +} +table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc, table.dataTable thead > tr > th.dt-ordering-asc, table.dataTable thead > tr > th.dt-ordering-desc, +table.dataTable thead > tr > td.dt-orderable-asc, +table.dataTable thead > tr > td.dt-orderable-desc, +table.dataTable thead > tr > td.dt-ordering-asc, +table.dataTable thead > tr > td.dt-ordering-desc { + position: relative; + padding-right: 30px; +} +table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order { + position: absolute; + right: 12px; + top: 0; + bottom: 0; + width: 12px; +} +table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after { + left: 0; + opacity: 0.125; + line-height: 9px; + font-size: 0.8em; +} +table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc, +table.dataTable thead > tr > td.dt-orderable-asc, +table.dataTable thead > tr > td.dt-orderable-desc { + cursor: pointer; +} +table.dataTable thead > tr > th.dt-orderable-asc:hover, table.dataTable thead > tr > th.dt-orderable-desc:hover, +table.dataTable thead > tr > td.dt-orderable-asc:hover, +table.dataTable thead > tr > td.dt-orderable-desc:hover { + outline: 2px solid rgba(0, 0, 0, 0.05); + outline-offset: -2px; +} +table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after { + opacity: 0.6; +} +table.dataTable thead > tr > th.sorting_desc_disabled span.dt-column-order:after, table.dataTable thead > tr > th.sorting_asc_disabled span.dt-column-order:before, +table.dataTable thead > tr > td.sorting_desc_disabled span.dt-column-order:after, +table.dataTable thead > tr > td.sorting_asc_disabled span.dt-column-order:before { + display: none; +} +table.dataTable thead > tr > th:active, +table.dataTable thead > tr > td:active { + outline: none; +} + +div.dt-scroll-body > table.dataTable > thead > tr > th, +div.dt-scroll-body > table.dataTable > thead > tr > td { + overflow: hidden; +} + +:root.dark table.dataTable thead > tr > th.dt-orderable-asc:hover, :root.dark table.dataTable thead > tr > th.dt-orderable-desc:hover, +:root.dark table.dataTable thead > tr > td.dt-orderable-asc:hover, +:root.dark table.dataTable thead > tr > td.dt-orderable-desc:hover, +:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-asc:hover, +:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-desc:hover, +:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-asc:hover, +:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-desc:hover { + outline: 2px solid rgba(255, 255, 255, 0.05); +} + +div.dt-processing { + position: absolute; + top: 50%; + left: 50%; + width: 200px; + margin-left: -100px; + margin-top: -22px; + text-align: center; + padding: 2px; + z-index: 10; +} +div.dt-processing > div:last-child { + position: relative; + width: 80px; + height: 15px; + margin: 1em auto; +} +div.dt-processing > div:last-child > div { + position: absolute; + top: 0; + width: 13px; + height: 13px; + border-radius: 50%; + background: rgb(13, 110, 253); + background: rgb(var(--dt-row-selected)); + animation-timing-function: cubic-bezier(0, 1, 1, 0); +} +div.dt-processing > div:last-child > div:nth-child(1) { + left: 8px; + animation: datatables-loader-1 0.6s infinite; +} +div.dt-processing > div:last-child > div:nth-child(2) { + left: 8px; + animation: datatables-loader-2 0.6s infinite; +} +div.dt-processing > div:last-child > div:nth-child(3) { + left: 32px; + animation: datatables-loader-2 0.6s infinite; +} +div.dt-processing > div:last-child > div:nth-child(4) { + left: 56px; + animation: datatables-loader-3 0.6s infinite; +} + +@keyframes datatables-loader-1 { + 0% { + transform: scale(0); + } + 100% { + transform: scale(1); + } +} +@keyframes datatables-loader-3 { + 0% { + transform: scale(1); + } + 100% { + transform: scale(0); + } +} +@keyframes datatables-loader-2 { + 0% { + transform: translate(0, 0); + } + 100% { + transform: translate(24px, 0); + } +} +table.dataTable.nowrap th, table.dataTable.nowrap td { + white-space: nowrap; +} +table.dataTable th, +table.dataTable td { + box-sizing: border-box; +} +table.dataTable th.dt-left, +table.dataTable td.dt-left { + text-align: left; +} +table.dataTable th.dt-center, +table.dataTable td.dt-center { + text-align: center; +} +table.dataTable th.dt-right, +table.dataTable td.dt-right { + text-align: right; +} +table.dataTable th.dt-justify, +table.dataTable td.dt-justify { + text-align: justify; +} +table.dataTable th.dt-nowrap, +table.dataTable td.dt-nowrap { + white-space: nowrap; +} +table.dataTable th.dt-empty, +table.dataTable td.dt-empty { + text-align: center; + vertical-align: top; +} +table.dataTable th.dt-type-numeric, table.dataTable th.dt-type-date, +table.dataTable td.dt-type-numeric, +table.dataTable td.dt-type-date { + text-align: right; +} +table.dataTable thead th, +table.dataTable thead td, +table.dataTable tfoot th, +table.dataTable tfoot td { + text-align: left; +} +table.dataTable thead th.dt-head-left, +table.dataTable thead td.dt-head-left, +table.dataTable tfoot th.dt-head-left, +table.dataTable tfoot td.dt-head-left { + text-align: left; +} +table.dataTable thead th.dt-head-center, +table.dataTable thead td.dt-head-center, +table.dataTable tfoot th.dt-head-center, +table.dataTable tfoot td.dt-head-center { + text-align: center; +} +table.dataTable thead th.dt-head-right, +table.dataTable thead td.dt-head-right, +table.dataTable tfoot th.dt-head-right, +table.dataTable tfoot td.dt-head-right { + text-align: right; +} +table.dataTable thead th.dt-head-justify, +table.dataTable thead td.dt-head-justify, +table.dataTable tfoot th.dt-head-justify, +table.dataTable tfoot td.dt-head-justify { + text-align: justify; +} +table.dataTable thead th.dt-head-nowrap, +table.dataTable thead td.dt-head-nowrap, +table.dataTable tfoot th.dt-head-nowrap, +table.dataTable tfoot td.dt-head-nowrap { + white-space: nowrap; +} +table.dataTable tbody th.dt-body-left, +table.dataTable tbody td.dt-body-left { + text-align: left; +} +table.dataTable tbody th.dt-body-center, +table.dataTable tbody td.dt-body-center { + text-align: center; +} +table.dataTable tbody th.dt-body-right, +table.dataTable tbody td.dt-body-right { + text-align: right; +} +table.dataTable tbody th.dt-body-justify, +table.dataTable tbody td.dt-body-justify { + text-align: justify; +} +table.dataTable tbody th.dt-body-nowrap, +table.dataTable tbody td.dt-body-nowrap { + white-space: nowrap; +} + +/* + * Table styles + */ +table.dataTable { + width: 100%; + margin: 0 auto; + border-spacing: 0; + /* + * Header and footer styles + */ + /* + * Body styles + */ +} +table.dataTable thead th, +table.dataTable tfoot th { + font-weight: bold; +} +table.dataTable > thead > tr > th, +table.dataTable > thead > tr > td { + padding: 10px; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); +} +table.dataTable > thead > tr > th:active, +table.dataTable > thead > tr > td:active { + outline: none; +} +table.dataTable > tfoot > tr > th, +table.dataTable > tfoot > tr > td { + border-top: 1px solid rgba(0, 0, 0, 0.3); + padding: 10px 10px 6px 10px; +} +table.dataTable > tbody > tr { + background-color: transparent; +} +table.dataTable > tbody > tr:first-child > * { + border-top: none; +} +table.dataTable > tbody > tr:last-child > * { + border-bottom: none; +} +table.dataTable > tbody > tr.selected > * { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.9); + color: rgb(255, 255, 255); + color: rgb(var(--dt-row-selected-text)); +} +table.dataTable > tbody > tr.selected a { + color: rgb(9, 10, 11); + color: rgb(var(--dt-row-selected-link)); +} +table.dataTable > tbody > tr > th, +table.dataTable > tbody > tr > td { + padding: 8px 10px; +} +table.dataTable.row-border > tbody > tr > *, table.dataTable.display > tbody > tr > * { + border-top: 1px solid rgba(0, 0, 0, 0.15); +} +table.dataTable.row-border > tbody > tr:first-child > *, table.dataTable.display > tbody > tr:first-child > * { + border-top: none; +} +table.dataTable.row-border > tbody > tr.selected + tr.selected > td, table.dataTable.display > tbody > tr.selected + tr.selected > td { + border-top-color: rgba(13, 110, 253, 0.65); + border-top-color: rgba(var(--dt-row-selected), 0.65); +} +table.dataTable.cell-border > tbody > tr > * { + border-top: 1px solid rgba(0, 0, 0, 0.15); + border-right: 1px solid rgba(0, 0, 0, 0.15); +} +table.dataTable.cell-border > tbody > tr > *:first-child { + border-left: 1px solid rgba(0, 0, 0, 0.15); +} +table.dataTable.cell-border > tbody > tr:first-child > * { + border-top: 1px solid rgba(0, 0, 0, 0.3); +} +table.dataTable.stripe > tbody > tr:nth-child(odd) > *, table.dataTable.display > tbody > tr:nth-child(odd) > * { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.023); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.023); +} +table.dataTable.stripe > tbody > tr:nth-child(odd).selected > *, table.dataTable.display > tbody > tr:nth-child(odd).selected > * { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.923); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.923); +} +table.dataTable.hover > tbody > tr:hover > *, table.dataTable.display > tbody > tr:hover > * { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.035); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.035); +} +table.dataTable.hover > tbody > tr.selected:hover > *, table.dataTable.display > tbody > tr.selected:hover > * { + box-shadow: inset 0 0 0 9999px #0d6efd !important; + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 1) !important; +} +table.dataTable.order-column > tbody tr > .sorting_1, +table.dataTable.order-column > tbody tr > .sorting_2, +table.dataTable.order-column > tbody tr > .sorting_3, table.dataTable.display > tbody tr > .sorting_1, +table.dataTable.display > tbody tr > .sorting_2, +table.dataTable.display > tbody tr > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.019); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.019); +} +table.dataTable.order-column > tbody tr.selected > .sorting_1, +table.dataTable.order-column > tbody tr.selected > .sorting_2, +table.dataTable.order-column > tbody tr.selected > .sorting_3, table.dataTable.display > tbody tr.selected > .sorting_1, +table.dataTable.display > tbody tr.selected > .sorting_2, +table.dataTable.display > tbody tr.selected > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.919); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919); +} +table.dataTable.display > tbody > tr:nth-child(odd) > .sorting_1, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd) > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.054); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.054); +} +table.dataTable.display > tbody > tr:nth-child(odd) > .sorting_2, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd) > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.047); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.047); +} +table.dataTable.display > tbody > tr:nth-child(odd) > .sorting_3, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd) > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.039); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.039); +} +table.dataTable.display > tbody > tr:nth-child(odd).selected > .sorting_1, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd).selected > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.954); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.954); +} +table.dataTable.display > tbody > tr:nth-child(odd).selected > .sorting_2, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd).selected > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.947); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.947); +} +table.dataTable.display > tbody > tr:nth-child(odd).selected > .sorting_3, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd).selected > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.939); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.939); +} +table.dataTable.display > tbody > tr.even > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.019); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.019); +} +table.dataTable.display > tbody > tr.even > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.011); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.011); +} +table.dataTable.display > tbody > tr.even > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.003); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.003); +} +table.dataTable.display > tbody > tr.even.selected > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.919); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919); +} +table.dataTable.display > tbody > tr.even.selected > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.911); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.911); +} +table.dataTable.display > tbody > tr.even.selected > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.903); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.903); +} +table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.082); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.082); +} +table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.074); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.074); +} +table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.062); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.062); +} +table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.982); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.982); +} +table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.974); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.974); +} +table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.962); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.962); +} +table.dataTable.compact thead th, +table.dataTable.compact thead td, +table.dataTable.compact tfoot th, +table.dataTable.compact tfoot td, +table.dataTable.compact tbody th, +table.dataTable.compact tbody td { + padding: 4px; +} + +div.dt-container div.dt-layout-row { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + margin: 0.75em 0; +} +div.dt-container div.dt-layout-row div.dt-layout-cell { + display: flex; + justify-content: space-between; + align-items: center; +} +div.dt-container div.dt-layout-row div.dt-layout-cell.dt-layout-start { + justify-content: flex-start; + margin-right: auto; +} +div.dt-container div.dt-layout-row div.dt-layout-cell.dt-layout-end { + justify-content: flex-end; + margin-left: auto; +} +div.dt-container div.dt-layout-row div.dt-layout-cell:empty { + display: none; +} + +@media screen and (max-width: 767px) { + div.dt-container div.dt-layout-row:not(.dt-layout-table) { + display: block; + } + div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell { + display: block; + text-align: center; + } + div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell > * { + margin: 0.5em 0; + } + div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell.dt-layout-start { + margin-right: 0; + } + div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell.dt-layout-end { + margin-left: 0; + } +} +div.dt-container div.dt-layout-start > *:not(:last-child) { + margin-right: 1em; +} +div.dt-container div.dt-layout-end > *:not(:first-child) { + margin-left: 1em; +} +div.dt-container div.dt-layout-full { + width: 100%; +} +div.dt-container div.dt-layout-full > *:only-child { + margin-left: auto; + margin-right: auto; +} +div.dt-container div.dt-layout-table > div { + display: block !important; +} + +@media screen and (max-width: 767px) { + div.dt-container div.dt-layout-start > *:not(:last-child) { + margin-right: 0; + } + div.dt-container div.dt-layout-end > *:not(:first-child) { + margin-left: 0; + } +} +/* + * Control feature layout + */ +div.dt-container { + position: relative; + clear: both; +} +div.dt-container .dt-search input { + border: 1px solid #aaa; + border-radius: 3px; + padding: 5px; + background-color: transparent; + color: inherit; + margin-left: 3px; +} +div.dt-container .dt-input { + border: 1px solid #aaa; + border-radius: 3px; + padding: 5px; + background-color: transparent; + color: inherit; +} +div.dt-container select.dt-input { + padding: 4px; +} +div.dt-container .dt-paging .dt-paging-button { + box-sizing: border-box; + display: inline-block; + min-width: 1.5em; + padding: 0.5em 1em; + margin-left: 2px; + text-align: center; + text-decoration: none !important; + cursor: pointer; + color: inherit !important; + border: 1px solid transparent; + border-radius: 2px; + background: transparent; +} +div.dt-container .dt-paging .dt-paging-button.current, div.dt-container .dt-paging .dt-paging-button.current:hover { + color: inherit !important; + border: 1px solid rgba(0, 0, 0, 0.3); + background-color: rgba(0, 0, 0, 0.05); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(230, 230, 230, 0.05)), color-stop(100%, rgba(0, 0, 0, 0.05))); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* Chrome10+,Safari5.1+ */ + background: -moz-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* FF3.6+ */ + background: -ms-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* IE10+ */ + background: -o-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* Opera 11.10+ */ + background: linear-gradient(to bottom, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* W3C */ +} +div.dt-container .dt-paging .dt-paging-button.disabled, div.dt-container .dt-paging .dt-paging-button.disabled:hover, div.dt-container .dt-paging .dt-paging-button.disabled:active { + cursor: default; + color: rgba(0, 0, 0, 0.5) !important; + border: 1px solid transparent; + background: transparent; + box-shadow: none; +} +div.dt-container .dt-paging .dt-paging-button:hover { + color: white !important; + border: 1px solid #111; + background-color: #111; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #585858 0%, #111 100%); /* Chrome10+,Safari5.1+ */ + background: -moz-linear-gradient(top, #585858 0%, #111 100%); /* FF3.6+ */ + background: -ms-linear-gradient(top, #585858 0%, #111 100%); /* IE10+ */ + background: -o-linear-gradient(top, #585858 0%, #111 100%); /* Opera 11.10+ */ + background: linear-gradient(to bottom, #585858 0%, #111 100%); /* W3C */ +} +div.dt-container .dt-paging .dt-paging-button:active { + outline: none; + background-color: #0c0c0c; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* Chrome10+,Safari5.1+ */ + background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* FF3.6+ */ + background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* IE10+ */ + background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* Opera 11.10+ */ + background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%); /* W3C */ + box-shadow: inset 0 0 3px #111; +} +div.dt-container .dt-paging .ellipsis { + padding: 0 1em; +} +div.dt-container .dt-length, +div.dt-container .dt-search, +div.dt-container .dt-info, +div.dt-container .dt-processing, +div.dt-container .dt-paging { + color: inherit; +} +div.dt-container .dataTables_scroll { + clear: both; +} +div.dt-container .dataTables_scroll div.dt-scroll-body { + -webkit-overflow-scrolling: touch; +} +div.dt-container .dataTables_scroll div.dt-scroll-body > table > thead > tr > th, div.dt-container .dataTables_scroll div.dt-scroll-body > table > thead > tr > td, div.dt-container .dataTables_scroll div.dt-scroll-body > table > tbody > tr > th, div.dt-container .dataTables_scroll div.dt-scroll-body > table > tbody > tr > td { + vertical-align: middle; +} +div.dt-container .dataTables_scroll div.dt-scroll-body > table > thead > tr > th > div.dataTables_sizing, +div.dt-container .dataTables_scroll div.dt-scroll-body > table > thead > tr > td > div.dataTables_sizing, div.dt-container .dataTables_scroll div.dt-scroll-body > table > tbody > tr > th > div.dataTables_sizing, +div.dt-container .dataTables_scroll div.dt-scroll-body > table > tbody > tr > td > div.dataTables_sizing { + height: 0; + overflow: hidden; + margin: 0 !important; + padding: 0 !important; +} +div.dt-container.dt-empty-footer tbody > tr:last-child > * { + border-bottom: 1px solid rgba(0, 0, 0, 0.3); +} +div.dt-container.dt-empty-footer .dt-scroll-body { + border-bottom: 1px solid rgba(0, 0, 0, 0.3); +} +div.dt-container.dt-empty-footer .dt-scroll-body tbody > tr:last-child > * { + border-bottom: none; +} + +html.dark { + --dt-row-hover: 255, 255, 255; + --dt-row-stripe: 255, 255, 255; + --dt-column-ordering: 255, 255, 255; +} +html.dark table.dataTable > thead > tr > th, +html.dark table.dataTable > thead > tr > td { + border-bottom: 1px solid rgb(89, 91, 94); +} +html.dark table.dataTable > thead > tr > th:active, +html.dark table.dataTable > thead > tr > td:active { + outline: none; +} +html.dark table.dataTable > tfoot > tr > th, +html.dark table.dataTable > tfoot > tr > td { + border-top: 1px solid rgb(89, 91, 94); +} +html.dark table.dataTable.row-border > tbody > tr > *, html.dark table.dataTable.display > tbody > tr > * { + border-top: 1px solid rgb(64, 67, 70); +} +html.dark table.dataTable.row-border > tbody > tr:first-child > *, html.dark table.dataTable.display > tbody > tr:first-child > * { + border-top: none; +} +html.dark table.dataTable.row-border > tbody > tr.selected + tr.selected > td, html.dark table.dataTable.display > tbody > tr.selected + tr.selected > td { + border-top-color: rgba(13, 110, 253, 0.65); + border-top-color: rgba(var(--dt-row-selected), 0.65); +} +html.dark table.dataTable.cell-border > tbody > tr > th, +html.dark table.dataTable.cell-border > tbody > tr > td { + border-top: 1px solid rgb(64, 67, 70); + border-right: 1px solid rgb(64, 67, 70); +} +html.dark table.dataTable.cell-border > tbody > tr > th:first-child, +html.dark table.dataTable.cell-border > tbody > tr > td:first-child { + border-left: 1px solid rgb(64, 67, 70); +} +html.dark .dt-container.dt-empty-footer table.dataTable { + border-bottom: 1px solid rgb(89, 91, 94); +} +html.dark .dt-container .dt-search input, +html.dark .dt-container .dt-length select { + border: 1px solid rgba(255, 255, 255, 0.2); + background-color: var(--dt-html-background); +} +html.dark .dt-container .dt-paging .dt-paging-button.current, html.dark .dt-container .dt-paging .dt-paging-button.current:hover { + border: 1px solid rgb(89, 91, 94); + background: rgba(255, 255, 255, 0.15); +} +html.dark .dt-container .dt-paging .dt-paging-button.disabled, html.dark .dt-container .dt-paging .dt-paging-button.disabled:hover, html.dark .dt-container .dt-paging .dt-paging-button.disabled:active { + color: #666 !important; +} +html.dark .dt-container .dt-paging .dt-paging-button:hover { + border: 1px solid rgb(53, 53, 53); + background: rgb(53, 53, 53); +} +html.dark .dt-container .dt-paging .dt-paging-button:active { + background: #3a3a3a; +} + +/* + * Overrides for RTL support + */ +*[dir=rtl] table.dataTable thead th, +*[dir=rtl] table.dataTable thead td, +*[dir=rtl] table.dataTable tfoot th, +*[dir=rtl] table.dataTable tfoot td { + text-align: right; +} +*[dir=rtl] table.dataTable th.dt-type-numeric, *[dir=rtl] table.dataTable th.dt-type-date, +*[dir=rtl] table.dataTable td.dt-type-numeric, +*[dir=rtl] table.dataTable td.dt-type-date { + text-align: left; +} +*[dir=rtl] div.dt-container div.dt-layout-cell.dt-start { + text-align: right; +} +*[dir=rtl] div.dt-container div.dt-layout-cell.dt-end { + text-align: left; +} +*[dir=rtl] div.dt-container div.dt-search input { + margin: 0 3px 0 0; +} + + diff --git a/astropy/extern/jquery/data/css/datatables.min.css b/astropy/extern/jquery/data/css/datatables.min.css new file mode 100644 index 000000000000..f3b17f546a92 --- /dev/null +++ b/astropy/extern/jquery/data/css/datatables.min.css @@ -0,0 +1,15 @@ +/* + * This combined file was created by the DataTables downloader builder: + * https://datatables.net/download + * + * To rebuild or modify this file with the latest versions of the included + * software please visit: + * https://datatables.net/download/#dt/dt-2.1.8 + * + * Included libraries: + * DataTables 2.1.8 + */ + +:root{--dt-row-selected: 13, 110, 253;--dt-row-selected-text: 255, 255, 255;--dt-row-selected-link: 9, 10, 11;--dt-row-stripe: 0, 0, 0;--dt-row-hover: 0, 0, 0;--dt-column-ordering: 0, 0, 0;--dt-html-background: white}:root.dark{--dt-html-background: rgb(33, 37, 41)}table.dataTable td.dt-control{text-align:center;cursor:pointer}table.dataTable td.dt-control:before{display:inline-block;box-sizing:border-box;content:"";border-top:5px solid transparent;border-left:10px solid rgba(0, 0, 0, 0.5);border-bottom:5px solid transparent;border-right:0px solid transparent}table.dataTable tr.dt-hasChild td.dt-control:before{border-top:10px solid rgba(0, 0, 0, 0.5);border-left:5px solid transparent;border-bottom:0px solid transparent;border-right:5px solid transparent}html.dark table.dataTable td.dt-control:before,:root[data-bs-theme=dark] table.dataTable td.dt-control:before,:root[data-theme=dark] table.dataTable td.dt-control:before{border-left-color:rgba(255, 255, 255, 0.5)}html.dark table.dataTable tr.dt-hasChild td.dt-control:before,:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before,:root[data-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before{border-top-color:rgba(255, 255, 255, 0.5);border-left-color:transparent}div.dt-scroll{width:100%}div.dt-scroll-body thead tr,div.dt-scroll-body tfoot tr{height:0}div.dt-scroll-body thead tr th,div.dt-scroll-body thead tr td,div.dt-scroll-body tfoot tr th,div.dt-scroll-body tfoot tr td{height:0 !important;padding-top:0px !important;padding-bottom:0px !important;border-top-width:0px !important;border-bottom-width:0px !important}div.dt-scroll-body thead tr th div.dt-scroll-sizing,div.dt-scroll-body thead tr td div.dt-scroll-sizing,div.dt-scroll-body tfoot tr th div.dt-scroll-sizing,div.dt-scroll-body tfoot tr td div.dt-scroll-sizing{height:0 !important;overflow:hidden !important}table.dataTable thead>tr>th:active,table.dataTable thead>tr>td:active{outline:none}table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:before{position:absolute;display:block;bottom:50%;content:"▲";content:"▲"/""}table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:after{position:absolute;display:block;top:50%;content:"▼";content:"▼"/""}table.dataTable thead>tr>th.dt-orderable-asc,table.dataTable thead>tr>th.dt-orderable-desc,table.dataTable thead>tr>th.dt-ordering-asc,table.dataTable thead>tr>th.dt-ordering-desc,table.dataTable thead>tr>td.dt-orderable-asc,table.dataTable thead>tr>td.dt-orderable-desc,table.dataTable thead>tr>td.dt-ordering-asc,table.dataTable thead>tr>td.dt-ordering-desc{position:relative;padding-right:30px}table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order,table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order{position:absolute;right:12px;top:0;bottom:0;width:12px}table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order:after,table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order:before,table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:after,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order:after,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:after{left:0;opacity:.125;line-height:9px;font-size:.8em}table.dataTable thead>tr>th.dt-orderable-asc,table.dataTable thead>tr>th.dt-orderable-desc,table.dataTable thead>tr>td.dt-orderable-asc,table.dataTable thead>tr>td.dt-orderable-desc{cursor:pointer}table.dataTable thead>tr>th.dt-orderable-asc:hover,table.dataTable thead>tr>th.dt-orderable-desc:hover,table.dataTable thead>tr>td.dt-orderable-asc:hover,table.dataTable thead>tr>td.dt-orderable-desc:hover{outline:2px solid rgba(0, 0, 0, 0.05);outline-offset:-2px}table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:after{opacity:.6}table.dataTable thead>tr>th.sorting_desc_disabled span.dt-column-order:after,table.dataTable thead>tr>th.sorting_asc_disabled span.dt-column-order:before,table.dataTable thead>tr>td.sorting_desc_disabled span.dt-column-order:after,table.dataTable thead>tr>td.sorting_asc_disabled span.dt-column-order:before{display:none}table.dataTable thead>tr>th:active,table.dataTable thead>tr>td:active{outline:none}div.dt-scroll-body>table.dataTable>thead>tr>th,div.dt-scroll-body>table.dataTable>thead>tr>td{overflow:hidden}:root.dark table.dataTable thead>tr>th.dt-orderable-asc:hover,:root.dark table.dataTable thead>tr>th.dt-orderable-desc:hover,:root.dark table.dataTable thead>tr>td.dt-orderable-asc:hover,:root.dark table.dataTable thead>tr>td.dt-orderable-desc:hover,:root[data-bs-theme=dark] table.dataTable thead>tr>th.dt-orderable-asc:hover,:root[data-bs-theme=dark] table.dataTable thead>tr>th.dt-orderable-desc:hover,:root[data-bs-theme=dark] table.dataTable thead>tr>td.dt-orderable-asc:hover,:root[data-bs-theme=dark] table.dataTable thead>tr>td.dt-orderable-desc:hover{outline:2px solid rgba(255, 255, 255, 0.05)}div.dt-processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-22px;text-align:center;padding:2px;z-index:10}div.dt-processing>div:last-child{position:relative;width:80px;height:15px;margin:1em auto}div.dt-processing>div:last-child>div{position:absolute;top:0;width:13px;height:13px;border-radius:50%;background:rgb(13, 110, 253);background:rgb(var(--dt-row-selected));animation-timing-function:cubic-bezier(0, 1, 1, 0)}div.dt-processing>div:last-child>div:nth-child(1){left:8px;animation:datatables-loader-1 .6s infinite}div.dt-processing>div:last-child>div:nth-child(2){left:8px;animation:datatables-loader-2 .6s infinite}div.dt-processing>div:last-child>div:nth-child(3){left:32px;animation:datatables-loader-2 .6s infinite}div.dt-processing>div:last-child>div:nth-child(4){left:56px;animation:datatables-loader-3 .6s infinite}@keyframes datatables-loader-1{0%{transform:scale(0)}100%{transform:scale(1)}}@keyframes datatables-loader-3{0%{transform:scale(1)}100%{transform:scale(0)}}@keyframes datatables-loader-2{0%{transform:translate(0, 0)}100%{transform:translate(24px, 0)}}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable th,table.dataTable td{box-sizing:border-box}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable th.dt-empty,table.dataTable td.dt-empty{text-align:center;vertical-align:top}table.dataTable th.dt-type-numeric,table.dataTable th.dt-type-date,table.dataTable td.dt-type-numeric,table.dataTable td.dt-type-date{text-align:right}table.dataTable thead th,table.dataTable thead td,table.dataTable tfoot th,table.dataTable tfoot td{text-align:left}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable{width:100%;margin:0 auto;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable>thead>tr>th,table.dataTable>thead>tr>td{padding:10px;border-bottom:1px solid rgba(0, 0, 0, 0.3)}table.dataTable>thead>tr>th:active,table.dataTable>thead>tr>td:active{outline:none}table.dataTable>tfoot>tr>th,table.dataTable>tfoot>tr>td{border-top:1px solid rgba(0, 0, 0, 0.3);padding:10px 10px 6px 10px}table.dataTable>tbody>tr{background-color:transparent}table.dataTable>tbody>tr:first-child>*{border-top:none}table.dataTable>tbody>tr:last-child>*{border-bottom:none}table.dataTable>tbody>tr.selected>*{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.9);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.9);color:rgb(255, 255, 255);color:rgb(var(--dt-row-selected-text))}table.dataTable>tbody>tr.selected a{color:rgb(9, 10, 11);color:rgb(var(--dt-row-selected-link))}table.dataTable>tbody>tr>th,table.dataTable>tbody>tr>td{padding:8px 10px}table.dataTable.row-border>tbody>tr>*,table.dataTable.display>tbody>tr>*{border-top:1px solid rgba(0, 0, 0, 0.15)}table.dataTable.row-border>tbody>tr:first-child>*,table.dataTable.display>tbody>tr:first-child>*{border-top:none}table.dataTable.row-border>tbody>tr.selected+tr.selected>td,table.dataTable.display>tbody>tr.selected+tr.selected>td{border-top-color:rgba(13, 110, 253, 0.65);border-top-color:rgba(var(--dt-row-selected), 0.65)}table.dataTable.cell-border>tbody>tr>*{border-top:1px solid rgba(0, 0, 0, 0.15);border-right:1px solid rgba(0, 0, 0, 0.15)}table.dataTable.cell-border>tbody>tr>*:first-child{border-left:1px solid rgba(0, 0, 0, 0.15)}table.dataTable.cell-border>tbody>tr:first-child>*{border-top:1px solid rgba(0, 0, 0, 0.3)}table.dataTable.stripe>tbody>tr:nth-child(odd)>*,table.dataTable.display>tbody>tr:nth-child(odd)>*{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.023);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.023)}table.dataTable.stripe>tbody>tr:nth-child(odd).selected>*,table.dataTable.display>tbody>tr:nth-child(odd).selected>*{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.923);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.923)}table.dataTable.hover>tbody>tr:hover>*,table.dataTable.display>tbody>tr:hover>*{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.035);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.035)}table.dataTable.hover>tbody>tr.selected:hover>*,table.dataTable.display>tbody>tr.selected:hover>*{box-shadow:inset 0 0 0 9999px #0d6efd !important;box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 1) !important}table.dataTable.order-column>tbody tr>.sorting_1,table.dataTable.order-column>tbody tr>.sorting_2,table.dataTable.order-column>tbody tr>.sorting_3,table.dataTable.display>tbody tr>.sorting_1,table.dataTable.display>tbody tr>.sorting_2,table.dataTable.display>tbody tr>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.019);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.019)}table.dataTable.order-column>tbody tr.selected>.sorting_1,table.dataTable.order-column>tbody tr.selected>.sorting_2,table.dataTable.order-column>tbody tr.selected>.sorting_3,table.dataTable.display>tbody tr.selected>.sorting_1,table.dataTable.display>tbody tr.selected>.sorting_2,table.dataTable.display>tbody tr.selected>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.919);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919)}table.dataTable.display>tbody>tr:nth-child(odd)>.sorting_1,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd)>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.054);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.054)}table.dataTable.display>tbody>tr:nth-child(odd)>.sorting_2,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd)>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.047);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.047)}table.dataTable.display>tbody>tr:nth-child(odd)>.sorting_3,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd)>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.039);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.039)}table.dataTable.display>tbody>tr:nth-child(odd).selected>.sorting_1,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd).selected>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.954);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.954)}table.dataTable.display>tbody>tr:nth-child(odd).selected>.sorting_2,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd).selected>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.947);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.947)}table.dataTable.display>tbody>tr:nth-child(odd).selected>.sorting_3,table.dataTable.order-column.stripe>tbody>tr:nth-child(odd).selected>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.939);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.939)}table.dataTable.display>tbody>tr.even>.sorting_1,table.dataTable.order-column.stripe>tbody>tr.even>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.019);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.019)}table.dataTable.display>tbody>tr.even>.sorting_2,table.dataTable.order-column.stripe>tbody>tr.even>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.011);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.011)}table.dataTable.display>tbody>tr.even>.sorting_3,table.dataTable.order-column.stripe>tbody>tr.even>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.003);box-shadow:inset 0 0 0 9999px rgba(var(--dt-column-ordering), 0.003)}table.dataTable.display>tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe>tbody>tr.even.selected>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.919);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.919)}table.dataTable.display>tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe>tbody>tr.even.selected>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.911);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.911)}table.dataTable.display>tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe>tbody>tr.even.selected>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.903);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.903)}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.082);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.082)}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.074);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.074)}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(0, 0, 0, 0.062);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.062)}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.982);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.982)}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.974);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.974)}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{box-shadow:inset 0 0 0 9999px rgba(13, 110, 253, 0.962);box-shadow:inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.962)}table.dataTable.compact thead th,table.dataTable.compact thead td,table.dataTable.compact tfoot th,table.dataTable.compact tfoot td,table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}div.dt-container div.dt-layout-row{display:flex;justify-content:space-between;align-items:center;width:100%;margin:.75em 0}div.dt-container div.dt-layout-row div.dt-layout-cell{display:flex;justify-content:space-between;align-items:center}div.dt-container div.dt-layout-row div.dt-layout-cell.dt-layout-start{justify-content:flex-start;margin-right:auto}div.dt-container div.dt-layout-row div.dt-layout-cell.dt-layout-end{justify-content:flex-end;margin-left:auto}div.dt-container div.dt-layout-row div.dt-layout-cell:empty{display:none}@media screen and (max-width: 767px){div.dt-container div.dt-layout-row:not(.dt-layout-table){display:block}div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell{display:block;text-align:center}div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell>*{margin:.5em 0}div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell.dt-layout-start{margin-right:0}div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell.dt-layout-end{margin-left:0}}div.dt-container div.dt-layout-start>*:not(:last-child){margin-right:1em}div.dt-container div.dt-layout-end>*:not(:first-child){margin-left:1em}div.dt-container div.dt-layout-full{width:100%}div.dt-container div.dt-layout-full>*:only-child{margin-left:auto;margin-right:auto}div.dt-container div.dt-layout-table>div{display:block !important}@media screen and (max-width: 767px){div.dt-container div.dt-layout-start>*:not(:last-child){margin-right:0}div.dt-container div.dt-layout-end>*:not(:first-child){margin-left:0}}div.dt-container{position:relative;clear:both}div.dt-container .dt-search input{border:1px solid #aaa;border-radius:3px;padding:5px;background-color:transparent;color:inherit;margin-left:3px}div.dt-container .dt-input{border:1px solid #aaa;border-radius:3px;padding:5px;background-color:transparent;color:inherit}div.dt-container select.dt-input{padding:4px}div.dt-container .dt-paging .dt-paging-button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;color:inherit !important;border:1px solid transparent;border-radius:2px;background:transparent}div.dt-container .dt-paging .dt-paging-button.current,div.dt-container .dt-paging .dt-paging-button.current:hover{color:inherit !important;border:1px solid rgba(0, 0, 0, 0.3);background-color:rgba(0, 0, 0, 0.05);background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(230, 230, 230, 0.05)), color-stop(100%, rgba(0, 0, 0, 0.05)));background:-webkit-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:-moz-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:-ms-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:-o-linear-gradient(top, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:linear-gradient(to bottom, rgba(230, 230, 230, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%)}div.dt-container .dt-paging .dt-paging-button.disabled,div.dt-container .dt-paging .dt-paging-button.disabled:hover,div.dt-container .dt-paging .dt-paging-button.disabled:active{cursor:default;color:rgba(0, 0, 0, 0.5) !important;border:1px solid transparent;background:transparent;box-shadow:none}div.dt-container .dt-paging .dt-paging-button:hover{color:white !important;border:1px solid #111;background-color:#111;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));background:-webkit-linear-gradient(top, #585858 0%, #111 100%);background:-moz-linear-gradient(top, #585858 0%, #111 100%);background:-ms-linear-gradient(top, #585858 0%, #111 100%);background:-o-linear-gradient(top, #585858 0%, #111 100%);background:linear-gradient(to bottom, #585858 0%, #111 100%)}div.dt-container .dt-paging .dt-paging-button:active{outline:none;background-color:#0c0c0c;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));background:-webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);box-shadow:inset 0 0 3px #111}div.dt-container .dt-paging .ellipsis{padding:0 1em}div.dt-container .dt-length,div.dt-container .dt-search,div.dt-container .dt-info,div.dt-container .dt-processing,div.dt-container .dt-paging{color:inherit}div.dt-container .dataTables_scroll{clear:both}div.dt-container .dataTables_scroll div.dt-scroll-body{-webkit-overflow-scrolling:touch}div.dt-container .dataTables_scroll div.dt-scroll-body>table>thead>tr>th,div.dt-container .dataTables_scroll div.dt-scroll-body>table>thead>tr>td,div.dt-container .dataTables_scroll div.dt-scroll-body>table>tbody>tr>th,div.dt-container .dataTables_scroll div.dt-scroll-body>table>tbody>tr>td{vertical-align:middle}div.dt-container .dataTables_scroll div.dt-scroll-body>table>thead>tr>th>div.dataTables_sizing,div.dt-container .dataTables_scroll div.dt-scroll-body>table>thead>tr>td>div.dataTables_sizing,div.dt-container .dataTables_scroll div.dt-scroll-body>table>tbody>tr>th>div.dataTables_sizing,div.dt-container .dataTables_scroll div.dt-scroll-body>table>tbody>tr>td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}div.dt-container.dt-empty-footer tbody>tr:last-child>*{border-bottom:1px solid rgba(0, 0, 0, 0.3)}div.dt-container.dt-empty-footer .dt-scroll-body{border-bottom:1px solid rgba(0, 0, 0, 0.3)}div.dt-container.dt-empty-footer .dt-scroll-body tbody>tr:last-child>*{border-bottom:none}html.dark{--dt-row-hover: 255, 255, 255;--dt-row-stripe: 255, 255, 255;--dt-column-ordering: 255, 255, 255}html.dark table.dataTable>thead>tr>th,html.dark table.dataTable>thead>tr>td{border-bottom:1px solid rgb(89, 91, 94)}html.dark table.dataTable>thead>tr>th:active,html.dark table.dataTable>thead>tr>td:active{outline:none}html.dark table.dataTable>tfoot>tr>th,html.dark table.dataTable>tfoot>tr>td{border-top:1px solid rgb(89, 91, 94)}html.dark table.dataTable.row-border>tbody>tr>*,html.dark table.dataTable.display>tbody>tr>*{border-top:1px solid rgb(64, 67, 70)}html.dark table.dataTable.row-border>tbody>tr:first-child>*,html.dark table.dataTable.display>tbody>tr:first-child>*{border-top:none}html.dark table.dataTable.row-border>tbody>tr.selected+tr.selected>td,html.dark table.dataTable.display>tbody>tr.selected+tr.selected>td{border-top-color:rgba(13, 110, 253, 0.65);border-top-color:rgba(var(--dt-row-selected), 0.65)}html.dark table.dataTable.cell-border>tbody>tr>th,html.dark table.dataTable.cell-border>tbody>tr>td{border-top:1px solid rgb(64, 67, 70);border-right:1px solid rgb(64, 67, 70)}html.dark table.dataTable.cell-border>tbody>tr>th:first-child,html.dark table.dataTable.cell-border>tbody>tr>td:first-child{border-left:1px solid rgb(64, 67, 70)}html.dark .dt-container.dt-empty-footer table.dataTable{border-bottom:1px solid rgb(89, 91, 94)}html.dark .dt-container .dt-search input,html.dark .dt-container .dt-length select{border:1px solid rgba(255, 255, 255, 0.2);background-color:var(--dt-html-background)}html.dark .dt-container .dt-paging .dt-paging-button.current,html.dark .dt-container .dt-paging .dt-paging-button.current:hover{border:1px solid rgb(89, 91, 94);background:rgba(255, 255, 255, 0.15)}html.dark .dt-container .dt-paging .dt-paging-button.disabled,html.dark .dt-container .dt-paging .dt-paging-button.disabled:hover,html.dark .dt-container .dt-paging .dt-paging-button.disabled:active{color:#666 !important}html.dark .dt-container .dt-paging .dt-paging-button:hover{border:1px solid rgb(53, 53, 53);background:rgb(53, 53, 53)}html.dark .dt-container .dt-paging .dt-paging-button:active{background:#3a3a3a}*[dir=rtl] table.dataTable thead th,*[dir=rtl] table.dataTable thead td,*[dir=rtl] table.dataTable tfoot th,*[dir=rtl] table.dataTable tfoot td{text-align:right}*[dir=rtl] table.dataTable th.dt-type-numeric,*[dir=rtl] table.dataTable th.dt-type-date,*[dir=rtl] table.dataTable td.dt-type-numeric,*[dir=rtl] table.dataTable td.dt-type-date{text-align:left}*[dir=rtl] div.dt-container div.dt-layout-cell.dt-start{text-align:right}*[dir=rtl] div.dt-container div.dt-layout-cell.dt-end{text-align:left}*[dir=rtl] div.dt-container div.dt-search input{margin:0 3px 0 0} + + diff --git a/astropy/extern/jquery/data/css/jquery.dataTables.css b/astropy/extern/jquery/data/css/jquery.dataTables.css deleted file mode 100644 index 2d7711f945e1..000000000000 --- a/astropy/extern/jquery/data/css/jquery.dataTables.css +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Table styles - */ - table.dataTable { - width: 100%; - margin: 0 auto; - clear: both; - border-collapse: separate; - border-spacing: 0; - /* - * Header and footer styles - */ - /* - * Body styles - */ -} -table.dataTable thead th, -table.dataTable tfoot th { - font-weight: bold; -} -table.dataTable thead th, -table.dataTable thead td { - padding: 10px 18px; - border-bottom: 1px solid #111; -} -table.dataTable thead th:active, -table.dataTable thead td:active { - outline: none; -} -table.dataTable tfoot th, -table.dataTable tfoot td { - padding: 10px 18px 6px 18px; - border-top: 1px solid #111; -} -table.dataTable thead .sorting, -table.dataTable thead .sorting_asc, -table.dataTable thead .sorting_desc, -table.dataTable thead .sorting_asc_disabled, -table.dataTable thead .sorting_desc_disabled { - cursor: pointer; - *cursor: hand; - background-repeat: no-repeat; - background-position: center right; -} -table.dataTable thead .sorting { - background-image: url("../images/sort_both.png"); -} -table.dataTable thead .sorting_asc { - background-image: url("../images/sort_asc.png") !important; -} -table.dataTable thead .sorting_desc { - background-image: url("../images/sort_desc.png") !important; -} -table.dataTable thead .sorting_asc_disabled { - background-image: url("../images/sort_asc_disabled.png"); -} -table.dataTable thead .sorting_desc_disabled { - background-image: url("../images/sort_desc_disabled.png"); -} -table.dataTable tbody tr { - background-color: #ffffff; -} -table.dataTable tbody tr.selected { - background-color: #B0BED9; -} -table.dataTable tbody th, -table.dataTable tbody td { - padding: 8px 10px; -} -table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td { - border-top: 1px solid #ddd; -} -table.dataTable.row-border tbody tr:first-child th, -table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th, -table.dataTable.display tbody tr:first-child td { - border-top: none; -} -table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td { - border-top: 1px solid #ddd; - border-right: 1px solid #ddd; -} -table.dataTable.cell-border tbody tr th:first-child, -table.dataTable.cell-border tbody tr td:first-child { - border-left: 1px solid #ddd; -} -table.dataTable.cell-border tbody tr:first-child th, -table.dataTable.cell-border tbody tr:first-child td { - border-top: none; -} -table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd { - background-color: #f9f9f9; -} -table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected { - background-color: #acbad4; -} -table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover { - background-color: #f6f6f6; -} -table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected { - background-color: #aab7d1; -} -table.dataTable.order-column tbody tr > .sorting_1, -table.dataTable.order-column tbody tr > .sorting_2, -table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1, -table.dataTable.display tbody tr > .sorting_2, -table.dataTable.display tbody tr > .sorting_3 { - background-color: #fafafa; -} -table.dataTable.order-column tbody tr.selected > .sorting_1, -table.dataTable.order-column tbody tr.selected > .sorting_2, -table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1, -table.dataTable.display tbody tr.selected > .sorting_2, -table.dataTable.display tbody tr.selected > .sorting_3 { - background-color: #acbad5; -} -table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 { - background-color: #f1f1f1; -} -table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 { - background-color: #f3f3f3; -} -table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 { - background-color: whitesmoke; -} -table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 { - background-color: #a6b4cd; -} -table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 { - background-color: #a8b5cf; -} -table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 { - background-color: #a9b7d1; -} -table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 { - background-color: #fafafa; -} -table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 { - background-color: #fcfcfc; -} -table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 { - background-color: #fefefe; -} -table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 { - background-color: #acbad5; -} -table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 { - background-color: #aebcd6; -} -table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 { - background-color: #afbdd8; -} -table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 { - background-color: #eaeaea; -} -table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 { - background-color: #ececec; -} -table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 { - background-color: #efefef; -} -table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 { - background-color: #a2aec7; -} -table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 { - background-color: #a3b0c9; -} -table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 { - background-color: #a5b2cb; -} -table.dataTable.no-footer { - border-bottom: 1px solid #111; -} -table.dataTable.nowrap th, table.dataTable.nowrap td { - white-space: nowrap; -} -table.dataTable.compact thead th, -table.dataTable.compact thead td { - padding: 4px 17px; -} -table.dataTable.compact tfoot th, -table.dataTable.compact tfoot td { - padding: 4px; -} -table.dataTable.compact tbody th, -table.dataTable.compact tbody td { - padding: 4px; -} -table.dataTable th.dt-left, -table.dataTable td.dt-left { - text-align: left; -} -table.dataTable th.dt-center, -table.dataTable td.dt-center, -table.dataTable td.dataTables_empty { - text-align: center; -} -table.dataTable th.dt-right, -table.dataTable td.dt-right { - text-align: right; -} -table.dataTable th.dt-justify, -table.dataTable td.dt-justify { - text-align: justify; -} -table.dataTable th.dt-nowrap, -table.dataTable td.dt-nowrap { - white-space: nowrap; -} -table.dataTable thead th.dt-head-left, -table.dataTable thead td.dt-head-left, -table.dataTable tfoot th.dt-head-left, -table.dataTable tfoot td.dt-head-left { - text-align: left; -} -table.dataTable thead th.dt-head-center, -table.dataTable thead td.dt-head-center, -table.dataTable tfoot th.dt-head-center, -table.dataTable tfoot td.dt-head-center { - text-align: center; -} -table.dataTable thead th.dt-head-right, -table.dataTable thead td.dt-head-right, -table.dataTable tfoot th.dt-head-right, -table.dataTable tfoot td.dt-head-right { - text-align: right; -} -table.dataTable thead th.dt-head-justify, -table.dataTable thead td.dt-head-justify, -table.dataTable tfoot th.dt-head-justify, -table.dataTable tfoot td.dt-head-justify { - text-align: justify; -} -table.dataTable thead th.dt-head-nowrap, -table.dataTable thead td.dt-head-nowrap, -table.dataTable tfoot th.dt-head-nowrap, -table.dataTable tfoot td.dt-head-nowrap { - white-space: nowrap; -} -table.dataTable tbody th.dt-body-left, -table.dataTable tbody td.dt-body-left { - text-align: left; -} -table.dataTable tbody th.dt-body-center, -table.dataTable tbody td.dt-body-center { - text-align: center; -} -table.dataTable tbody th.dt-body-right, -table.dataTable tbody td.dt-body-right { - text-align: right; -} -table.dataTable tbody th.dt-body-justify, -table.dataTable tbody td.dt-body-justify { - text-align: justify; -} -table.dataTable tbody th.dt-body-nowrap, -table.dataTable tbody td.dt-body-nowrap { - white-space: nowrap; -} - -table.dataTable, -table.dataTable th, -table.dataTable td { - box-sizing: content-box; -} - -/* - * Control feature layout - */ -.dataTables_wrapper { - position: relative; - clear: both; - *zoom: 1; - zoom: 1; -} -.dataTables_wrapper .dataTables_length { - float: left; -} -.dataTables_wrapper .dataTables_length select { - border: 1px solid #aaa; - border-radius: 3px; - padding: 5px; - background-color: transparent; - padding: 4px; -} -.dataTables_wrapper .dataTables_filter { - float: right; - text-align: right; -} -.dataTables_wrapper .dataTables_filter input { - border: 1px solid #aaa; - border-radius: 3px; - padding: 5px; - background-color: transparent; - margin-left: 3px; -} -.dataTables_wrapper .dataTables_info { - clear: both; - float: left; - padding-top: 0.755em; -} -.dataTables_wrapper .dataTables_paginate { - float: right; - text-align: right; - padding-top: 0.25em; -} -.dataTables_wrapper .dataTables_paginate .paginate_button { - box-sizing: border-box; - display: inline-block; - min-width: 1.5em; - padding: 0.5em 1em; - margin-left: 2px; - text-align: center; - text-decoration: none !important; - cursor: pointer; - *cursor: hand; - color: #333 !important; - border: 1px solid transparent; - border-radius: 2px; -} -.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover { - color: #333 !important; - border: 1px solid #979797; - background-color: white; - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, #dcdcdc)); - /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, white 0%, #dcdcdc 100%); - /* Chrome10+,Safari5.1+ */ - background: -moz-linear-gradient(top, white 0%, #dcdcdc 100%); - /* FF3.6+ */ - background: -ms-linear-gradient(top, white 0%, #dcdcdc 100%); - /* IE10+ */ - background: -o-linear-gradient(top, white 0%, #dcdcdc 100%); - /* Opera 11.10+ */ - background: linear-gradient(to bottom, white 0%, #dcdcdc 100%); - /* W3C */ -} -.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active { - cursor: default; - color: #666 !important; - border: 1px solid transparent; - background: transparent; - box-shadow: none; -} -.dataTables_wrapper .dataTables_paginate .paginate_button:hover { - color: white !important; - border: 1px solid #111; - background-color: #585858; - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111)); - /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #585858 0%, #111 100%); - /* Chrome10+,Safari5.1+ */ - background: -moz-linear-gradient(top, #585858 0%, #111 100%); - /* FF3.6+ */ - background: -ms-linear-gradient(top, #585858 0%, #111 100%); - /* IE10+ */ - background: -o-linear-gradient(top, #585858 0%, #111 100%); - /* Opera 11.10+ */ - background: linear-gradient(to bottom, #585858 0%, #111 100%); - /* W3C */ -} -.dataTables_wrapper .dataTables_paginate .paginate_button:active { - outline: none; - background-color: #2b2b2b; - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c)); - /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); - /* Chrome10+,Safari5.1+ */ - background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); - /* FF3.6+ */ - background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); - /* IE10+ */ - background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); - /* Opera 11.10+ */ - background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%); - /* W3C */ - box-shadow: inset 0 0 3px #111; -} -.dataTables_wrapper .dataTables_paginate .ellipsis { - padding: 0 1em; -} -.dataTables_wrapper .dataTables_processing { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - height: 40px; - margin-left: -50%; - margin-top: -25px; - padding-top: 20px; - text-align: center; - font-size: 1.2em; - background-color: white; - background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0))); - background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); - background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); - background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); - background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); - background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%); -} -.dataTables_wrapper .dataTables_length, -.dataTables_wrapper .dataTables_filter, -.dataTables_wrapper .dataTables_info, -.dataTables_wrapper .dataTables_processing, -.dataTables_wrapper .dataTables_paginate { - color: #333; -} -.dataTables_wrapper .dataTables_scroll { - clear: both; -} -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody { - *margin-top: -1px; - -webkit-overflow-scrolling: touch; -} -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td { - vertical-align: middle; -} -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing, -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing, -.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing { - height: 0; - overflow: hidden; - margin: 0 !important; - padding: 0 !important; -} -.dataTables_wrapper.no-footer .dataTables_scrollBody { - border-bottom: 1px solid #111; -} -.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable, -.dataTables_wrapper.no-footer div.dataTables_scrollBody > table { - border-bottom: none; -} -.dataTables_wrapper:after { - visibility: hidden; - display: block; - content: ""; - clear: both; - height: 0; -} - -@media screen and (max-width: 767px) { - .dataTables_wrapper .dataTables_info, -.dataTables_wrapper .dataTables_paginate { - float: none; - text-align: center; - } - .dataTables_wrapper .dataTables_paginate { - margin-top: 0.5em; - } -} -@media screen and (max-width: 640px) { - .dataTables_wrapper .dataTables_length, -.dataTables_wrapper .dataTables_filter { - float: none; - text-align: center; - } - .dataTables_wrapper .dataTables_filter { - margin-top: 0.5em; - } -} \ No newline at end of file diff --git a/astropy/extern/jquery/data/js/jquery.dataTables.js b/astropy/extern/jquery/data/js/datatables.js similarity index 53% rename from astropy/extern/jquery/data/js/jquery.dataTables.js rename to astropy/extern/jquery/data/js/datatables.js index a7e3d30fe2c1..624c38eee6d9 100644 --- a/astropy/extern/jquery/data/js/jquery.dataTables.js +++ b/astropy/extern/jquery/data/js/datatables.js @@ -1,29 +1,37 @@ -/*! DataTables 1.10.25 - * ©2008-2021 SpryMedia Ltd - datatables.net/license +/* + * This combined file was created by the DataTables downloader builder: + * https://datatables.net/download + * + * To rebuild or modify this file with the latest versions of the included + * software please visit: + * https://datatables.net/download/#dt/dt-2.1.8 + * + * Included libraries: + * DataTables 2.1.8 + */ + +/*! DataTables 2.1.8 + * © SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables - * @version 1.10.25 - * @file jquery.dataTables.js + * @version 2.1.8 * @author SpryMedia Ltd * @contact www.datatables.net - * @copyright Copyright 2008-2021 SpryMedia Ltd. + * @copyright SpryMedia Ltd. * * This source file is free software, available under the following license: - * MIT license - http://datatables.net/license + * MIT license - https://datatables.net/license * * This source file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. * - * For details please refer to: http://www.datatables.net + * For details please refer to: https://www.datatables.net */ -/*jslint evil: true, undef: true, browser: true */ -/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/ - (function( factory ) { "use strict"; @@ -35,1287 +43,1034 @@ } else if ( typeof exports === 'object' ) { // CommonJS - module.exports = function (root, $) { - if ( ! root ) { - // CommonJS environments without a window global must pass a - // root. This will give an error otherwise - root = window; - } + // jQuery's factory checks for a global window - if it isn't present then it + // returns a factory function that expects the window object + var jq = require('jquery'); - if ( ! $ ) { - $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window - require('jquery') : - require('jquery')( root ); - } + if (typeof window === 'undefined') { + module.exports = function (root, $) { + if ( ! root ) { + // CommonJS environments without a window global must pass a + // root. This will give an error otherwise + root = window; + } - return factory( $, root, root.document ); - }; + if ( ! $ ) { + $ = jq( root ); + } + + return factory( $, root, root.document ); + }; + } + else { + module.exports = factory( jq, window, window.document ); + } } else { // Browser - factory( jQuery, window, document ); + window.DataTable = factory( jQuery, window, document ); } -} -(function( $, window, document, undefined ) { +}(function( $, window, document ) { "use strict"; - /** - * DataTables is a plug-in for the jQuery Javascript library. It is a highly - * flexible tool, based upon the foundations of progressive enhancement, - * which will add advanced interaction controls to any HTML table. For a - * full list of features please refer to - * [DataTables.net](href="http://datatables.net). - * - * Note that the `DataTable` object is not a global variable but is aliased - * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may - * be accessed. - * - * @class - * @param {object} [init={}] Configuration object for DataTables. Options - * are defined by {@link DataTable.defaults} - * @requires jQuery 1.7+ - * - * @example - * // Basic initialisation - * $(document).ready( function { - * $('#example').dataTable(); - * } ); - * - * @example - * // Initialisation with configuration options - in this case, disable - * // pagination and sorting. - * $(document).ready( function { - * $('#example').dataTable( { - * "paginate": false, - * "sort": false - * } ); - * } ); - */ - var DataTable = function ( options ) + + var DataTable = function ( selector, options ) { - /** - * Perform a jQuery selector action on the table's TR elements (from the tbody) and - * return the resulting jQuery object. - * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on - * @param {object} [oOpts] Optional parameters for modifying the rows to be included - * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter - * criterion ("applied") or all TR elements (i.e. no filter). - * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. - * Can be either 'current', whereby the current sorting of the table is used, or - * 'original' whereby the original order the data was read into the table is used. - * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page - * ("current") or not ("all"). If 'current' is given, then order is assumed to be - * 'current' and filter is 'applied', regardless of what they might be given as. - * @returns {object} jQuery object, filtered by the given selector. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Highlight every second row - * oTable.$('tr:odd').css('backgroundColor', 'blue'); - * } ); - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Filter to rows with 'Webkit' in them, add a background colour and then - * // remove the filter, thus highlighting the 'Webkit' rows only. - * oTable.fnFilter('Webkit'); - * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue'); - * oTable.fnFilter(''); - * } ); - */ - this.$ = function ( sSelector, oOpts ) - { - return this.api(true).$( sSelector, oOpts ); - }; - - - /** - * Almost identical to $ in operation, but in this case returns the data for the matched - * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes - * rather than any descendants, so the data can be obtained for the row/cell. If matching - * rows are found, the data returned is the original data array/object that was used to - * create the row (or a generated array if from a DOM source). - * - * This method is often useful in-combination with $ where both functions are given the - * same parameters and the array indexes will match identically. - * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on - * @param {object} [oOpts] Optional parameters for modifying the rows to be included - * @param {string} [oOpts.filter=none] Select elements that meet the current filter - * criterion ("applied") or all elements (i.e. no filter). - * @param {string} [oOpts.order=current] Order of the data in the processed array. - * Can be either 'current', whereby the current sorting of the table is used, or - * 'original' whereby the original order the data was read into the table is used. - * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page - * ("current") or not ("all"). If 'current' is given, then order is assumed to be - * 'current' and filter is 'applied', regardless of what they might be given as. - * @returns {array} Data for the matched elements. If any elements, as a result of the - * selector, were not TR, TD or TH elements in the DataTable, they will have a null - * entry in the array. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Get the data from the first row in the table - * var data = oTable._('tr:first'); - * - * // Do something useful with the data - * alert( "First cell is: "+data[0] ); - * } ); - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Filter to 'Webkit' and get all data for - * oTable.fnFilter('Webkit'); - * var data = oTable._('tr', {"search": "applied"}); - * - * // Do something with the data - * alert( data.length+" rows matched the search" ); - * } ); - */ - this._ = function ( sSelector, oOpts ) - { - return this.api(true).rows( sSelector, oOpts ).data(); - }; - - - /** - * Create a DataTables Api instance, with the currently selected tables for - * the Api's context. - * @param {boolean} [traditional=false] Set the API instance's context to be - * only the table referred to by the `DataTable.ext.iApiIndex` option, as was - * used in the API presented by DataTables 1.9- (i.e. the traditional mode), - * or if all tables captured in the jQuery object should be used. - * @return {DataTables.Api} - */ - this.api = function ( traditional ) - { - return traditional ? - new _Api( - _fnSettingsFromNode( this[ _ext.iApiIndex ] ) - ) : - new _Api( this ); - }; - - - /** - * Add a single new row or multiple rows of data to the table. Please note - * that this is suitable for client-side processing only - if you are using - * server-side processing (i.e. "bServerSide": true), then to add data, you - * must add it to the data source, i.e. the server-side, through an Ajax call. - * @param {array|object} data The data to be added to the table. This can be: - *
    - *
  • 1D array of data - add a single row with the data provided
  • - *
  • 2D array of arrays - add multiple rows in a single call
  • - *
  • object - data object when using mData
  • - *
  • array of objects - multiple data objects when using mData
  • - *
- * @param {bool} [redraw=true] redraw the table or not - * @returns {array} An array of integers, representing the list of indexes in - * aoData ({@link DataTable.models.oSettings}) that have been added to - * the table. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * // Global var for counter - * var giCount = 2; - * - * $(document).ready(function() { - * $('#example').dataTable(); - * } ); - * - * function fnClickAddRow() { - * $('#example').dataTable().fnAddData( [ - * giCount+".1", - * giCount+".2", - * giCount+".3", - * giCount+".4" ] - * ); - * - * giCount++; - * } - */ - this.fnAddData = function( data, redraw ) + // Check if called with a window or jQuery object for DOM less applications + // This is for backwards compatibility + if (DataTable.factory(selector, options)) { + return DataTable; + } + + // When creating with `new`, create a new DataTable, returning the API instance + if (this instanceof DataTable) { + return $(selector).DataTable(options); + } + else { + // Argument switching + options = selector; + } + + var _that = this; + var emptyInit = options === undefined; + var len = this.length; + + if ( emptyInit ) { + options = {}; + } + + // Method to get DT API instance from jQuery object + this.api = function () { - var api = this.api( true ); - - /* Check if we want to add multiple rows or not */ - var rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ? - api.rows.add( data ) : - api.row.add( data ); - - if ( redraw === undefined || redraw ) { - api.draw(); - } - - return rows.flatten().toArray(); + return new _Api( this ); }; - - - /** - * This function will make DataTables recalculate the column sizes, based on the data - * contained in the table and the sizes applied to the columns (in the DOM, CSS or - * through the sWidth parameter). This can be useful when the width of the table's - * parent element changes (for example a window resize). - * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable( { - * "sScrollY": "200px", - * "bPaginate": false - * } ); - * - * $(window).on('resize', function () { - * oTable.fnAdjustColumnSizing(); - * } ); - * } ); - */ - this.fnAdjustColumnSizing = function ( bRedraw ) - { - var api = this.api( true ).columns.adjust(); - var settings = api.settings()[0]; - var scroll = settings.oScroll; - - if ( bRedraw === undefined || bRedraw ) { - api.draw( false ); - } - else if ( scroll.sX !== "" || scroll.sY !== "" ) { - /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ - _fnScrollDraw( settings ); + + this.each(function() { + // For each initialisation we want to give it a clean initialisation + // object that can be bashed around + var o = {}; + var oInit = len > 1 ? // optimisation for single table case + _fnExtend( o, options, true ) : + options; + + + var i=0, iLen; + var sId = this.getAttribute( 'id' ); + var defaults = DataTable.defaults; + var $this = $(this); + + + /* Sanity check */ + if ( this.nodeName.toLowerCase() != 'table' ) + { + _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); + return; } - }; - - - /** - * Quickly and simply clear a table - * @param {bool} [bRedraw=true] redraw the table or not - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * - * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...) - * oTable.fnClearTable(); - * } ); - */ - this.fnClearTable = function( bRedraw ) - { - var api = this.api( true ).clear(); - - if ( bRedraw === undefined || bRedraw ) { - api.draw(); + + $(this).trigger( 'options.dt', oInit ); + + /* Backwards compatibility for the defaults */ + _fnCompatOpts( defaults ); + _fnCompatCols( defaults.column ); + + /* Convert the camel-case defaults to Hungarian */ + _fnCamelToHungarian( defaults, defaults, true ); + _fnCamelToHungarian( defaults.column, defaults.column, true ); + + /* Setting up the initialisation object */ + _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); + + + + /* Check to see if we are re-initialising a table */ + var allSettings = DataTable.settings; + for ( i=0, iLen=allSettings.length ; i').prependTo(this), + fastData: function (row, column, type) { + return _fnGetCellData(oSettings, row, column, type); + } + } ); + oSettings.nTable = this; + oSettings.oInit = oInit; + + allSettings.push( oSettings ); + + // Make a single API instance available for internal handling + oSettings.api = new _Api( oSettings ); + + // Need to add the instance after the instance after the settings object has been added + // to the settings array, so we can self reference the table instance if more than one + oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); + + // Backwards compatibility, before we apply all the defaults + _fnCompatOpts( oInit ); + + // If the length menu is given, but the init display length is not, use the length menu + if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) + { + oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0]) + ? oInit.aLengthMenu[0][0] + : $.isPlainObject( oInit.aLengthMenu[0] ) + ? oInit.aLengthMenu[0].value + : oInit.aLengthMenu[0]; } - - return data; - }; - - - /** - * Restore the table to it's original state in the DOM by removing all of DataTables - * enhancements, alterations to the DOM structure of the table and event listeners. - * @param {boolean} [remove=false] Completely remove the table from the DOM - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * // This example is fairly pointless in reality, but shows how fnDestroy can be used - * var oTable = $('#example').dataTable(); - * oTable.fnDestroy(); - * } ); - */ - this.fnDestroy = function ( remove ) - { - this.api( true ).destroy( remove ); - }; - - + + // Apply the defaults and init options to make a single init object will all + // options defined from defaults and instance options. + oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); + + + // Map the initialisation options onto the settings object + _fnMap( oSettings.oFeatures, oInit, [ + "bPaginate", + "bLengthChange", + "bFilter", + "bSort", + "bSortMulti", + "bInfo", + "bProcessing", + "bAutoWidth", + "bSortClasses", + "bServerSide", + "bDeferRender" + ] ); + _fnMap( oSettings, oInit, [ + "ajax", + "fnFormatNumber", + "sServerMethod", + "aaSorting", + "aaSortingFixed", + "aLengthMenu", + "sPaginationType", + "iStateDuration", + "bSortCellsTop", + "iTabIndex", + "sDom", + "fnStateLoadCallback", + "fnStateSaveCallback", + "renderer", + "searchDelay", + "rowId", + "caption", + "layout", + "orderDescReverse", + "typeDetect", + [ "iCookieDuration", "iStateDuration" ], // backwards compat + [ "oSearch", "oPreviousSearch" ], + [ "aoSearchCols", "aoPreSearchCols" ], + [ "iDisplayLength", "_iDisplayLength" ] + ] ); + _fnMap( oSettings.oScroll, oInit, [ + [ "sScrollX", "sX" ], + [ "sScrollXInner", "sXInner" ], + [ "sScrollY", "sY" ], + [ "bScrollCollapse", "bCollapse" ] + ] ); + _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); + + /* Callback functions which are array driven */ + _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback ); + _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams ); + _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams ); + _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded ); + _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback ); + _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow ); + _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback ); + _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback ); + _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete ); + _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback ); + + oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); + + /* Browser support detection */ + _fnBrowserDetect( oSettings ); + + var oClasses = oSettings.oClasses; + + $.extend( oClasses, DataTable.ext.classes, oInit.oClasses ); + $this.addClass( oClasses.table ); + + if (! oSettings.oFeatures.bPaginate) { + oInit.iDisplayStart = 0; + } + + if ( oSettings.iInitDisplayStart === undefined ) + { + /* Display start point, taking into account the save saving */ + oSettings.iInitDisplayStart = oInit.iDisplayStart; + oSettings._iDisplayStart = oInit.iDisplayStart; + } + + var defer = oInit.iDeferLoading; + if ( defer !== null ) + { + oSettings.deferLoading = true; + + var tmp = Array.isArray(defer); + oSettings._iRecordsDisplay = tmp ? defer[0] : defer; + oSettings._iRecordsTotal = tmp ? defer[1] : defer; + } + + /* + * Columns + * See if we should load columns automatically or use defined ones + */ + var columnsInit = []; + var thead = this.getElementsByTagName('thead'); + var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] ); + + // If we don't have a columns array, then generate one with nulls + if ( oInit.aoColumns ) { + columnsInit = oInit.aoColumns; + } + else if ( initHeaderLayout.length ) { + for ( i=0, iLen=initHeaderLayout[0].length ; i').appendTo( $this ); + } + + caption.html( oSettings.caption ); + } + + // Store the caption side, so we can remove the element from the document + // when creating the element + if (caption.length) { + caption[0]._captionSide = caption.css('caption-side'); + oSettings.captionNode = caption[0]; + } + + if ( thead.length === 0 ) { + thead = $('
').appendTo($this); + } + oSettings.nTHead = thead[0]; + $('tr', thead).addClass(oClasses.thead.row); + + var tbody = $this.children('tbody'); + if ( tbody.length === 0 ) { + tbody = $('').insertAfter(thead); + } + oSettings.nTBody = tbody[0]; + + var tfoot = $this.children('tfoot'); + if ( tfoot.length === 0 ) { + // If we are a scrolling table, and no footer has been given, then we need to create + // a tfoot element for the caption element to be appended to + tfoot = $('').appendTo($this); + } + oSettings.nTFoot = tfoot[0]; + $('tr', tfoot).addClass(oClasses.tfoot.row); + + // Copy the data index array + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + + // Initialisation complete - table can be drawn + oSettings.bInitialised = true; + + // Language definitions + var oLanguage = oSettings.oLanguage; + $.extend( true, oLanguage, oInit.oLanguage ); + + if ( oLanguage.sUrl ) { + // Get the language definitions from a file + $.ajax( { + dataType: 'json', + url: oLanguage.sUrl, + success: function ( json ) { + _fnCamelToHungarian( defaults.oLanguage, json ); + $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); + + _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); + _fnInitialise( oSettings ); + }, + error: function () { + // Error occurred loading language file + _fnLog( oSettings, 0, 'i18n file loading error', 21 ); + + // Continue on as best we can + _fnInitialise( oSettings ); + } + } ); + } + else { + _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); + _fnInitialise( oSettings ); + } + } ); + _that = null; + return this; + }; + + + + /** + * DataTables extensions + * + * This namespace acts as a collection area for plug-ins that can be used to + * extend DataTables capabilities. Indeed many of the build in methods + * use this method to provide their own capabilities (sorting methods for + * example). + * + * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy + * reasons + * + * @namespace + */ + DataTable.ext = _ext = { /** - * Redraw the table - * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); + * Buttons. For use with the Buttons extension for DataTables. This is + * defined here so other extensions can define buttons regardless of load + * order. It is _not_ used by DataTables core. * - * // Re-draw the table - you wouldn't want to do it here, but it's an example :-) - * oTable.fnDraw(); - * } ); + * @type object + * @default {} */ - this.fnDraw = function( complete ) - { - // Note that this isn't an exact match to the old call to _fnDraw - it takes - // into account the new data, but can hold position. - this.api( true ).draw( complete ); - }; - - + buttons: {}, + + /** - * Filter the input based on data - * @param {string} sInput String to filter the table on - * @param {int|null} [iColumn] Column to limit filtering to - * @param {bool} [bRegex=false] Treat as regular expression or not - * @param {bool} [bSmart=true] Perform smart filtering or not - * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es) - * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false) - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); + * Element class names * - * // Sometime later - filter... - * oTable.fnFilter( 'test string' ); - * } ); + * @type object + * @default {} */ - this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive ) - { - var api = this.api( true ); - - if ( iColumn === null || iColumn === undefined ) { - api.search( sInput, bRegex, bSmart, bCaseInsensitive ); - } - else { - api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); - } - - api.draw(); - }; - - + classes: {}, + + /** - * Get the data for the whole table, an individual row or an individual cell based on the - * provided parameters. - * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as - * a TR node then the data source for the whole row will be returned. If given as a - * TD/TH cell node then iCol will be automatically calculated and the data for the - * cell returned. If given as an integer, then this is treated as the aoData internal - * data index for the row (see fnGetPosition) and the data for that row used. - * @param {int} [col] Optional column index that you want the data of. - * @returns {array|object|string} If mRow is undefined, then the data for all rows is - * returned. If mRow is defined, just data for that row, and is iCol is - * defined, only data for the designated cell is returned. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * // Row data - * $(document).ready(function() { - * oTable = $('#example').dataTable(); - * - * oTable.$('tr').click( function () { - * var data = oTable.fnGetData( this ); - * // ... do something with the array / object of data for the row - * } ); - * } ); - * - * @example - * // Individual cell data - * $(document).ready(function() { - * oTable = $('#example').dataTable(); + * DataTables build type (expanded by the download builder) * - * oTable.$('td').click( function () { - * var sData = oTable.fnGetData( this ); - * alert( 'The cell clicked on had the value of '+sData ); - * } ); - * } ); + * @type string */ - this.fnGetData = function( src, col ) - { - var api = this.api( true ); - - if ( src !== undefined ) { - var type = src.nodeName ? src.nodeName.toLowerCase() : ''; - - return col !== undefined || type == 'td' || type == 'th' ? - api.cell( src, col ).data() : - api.row( src ).data() || null; - } - - return api.data().toArray(); - }; - - + builder: "dt/dt-2.1.8", + + /** - * Get an array of the TR nodes that are used in the table's body. Note that you will - * typically want to use the '$' API method in preference to this as it is more - * flexible. - * @param {int} [iRow] Optional row index for the TR element you want - * @returns {array|node} If iRow is undefined, returns an array of all TR elements - * in the table's body, or iRow is defined, just the TR element requested. - * @dtopt API - * @deprecated Since v1.10 - * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); + * Error reporting. + * + * How should DataTables report an error. Can take the value 'alert', + * 'throw', 'none' or a function. * - * // Get the nodes from the table - * var nNodes = oTable.fnGetNodes( ); - * } ); + * @type string|function + * @default alert */ - this.fnGetNodes = function( iRow ) - { - var api = this.api( true ); - - return iRow !== undefined ? - api.row( iRow ).node() : - api.rows().nodes().flatten().toArray(); - }; - - + errMode: "alert", + + /** - * Get the array indexes of a particular cell from it's DOM element - * and column index including hidden columns - * @param {node} node this can either be a TR, TD or TH in the table's body - * @returns {int} If nNode is given as a TR, then a single index is returned, or - * if given as a cell, an array of [row index, column index (visible), - * column index (all)] is given. - * @dtopt API - * @deprecated Since v1.10 + * Legacy so v1 plug-ins don't throw js errors on load + */ + feature: [], + + /** + * Feature plug-ins. + * + * This is an object of callbacks which provide the features for DataTables + * to be initialised via the `layout` option. + */ + features: {}, + + + /** + * Row searching. + * + * This method of searching is complimentary to the default type based + * searching, and a lot more comprehensive as it allows you complete control + * over the searching logic. Each element in this array is a function + * (parameters described below) that is called for every row in the table, + * and your logic decides if it should be included in the searching data set + * or not. * - * @example - * $(document).ready(function() { - * $('#example tbody td').click( function () { - * // Get the position of the current data from the node - * var aPos = oTable.fnGetPosition( this ); + * Searching functions have the following input parameters: + * + * 1. `{object}` DataTables settings object: see + * {@link DataTable.models.oSettings} + * 2. `{array|object}` Data for the row to be processed (same as the + * original format that was passed in as the data source, or an array + * from a DOM data source + * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which + * can be useful to retrieve the `TR` element if you need DOM interaction. * - * // Get the data array for this row - * var aData = oTable.fnGetData( aPos[0] ); + * And the following return is expected: * - * // Update the data array and return the value - * aData[ aPos[1] ] = 'clicked'; - * this.innerHTML = 'clicked'; - * } ); + * * {boolean} Include the row in the searched result set (true) or not + * (false) * - * // Init DataTables - * oTable = $('#example').dataTable(); - * } ); - */ - this.fnGetPosition = function( node ) - { - var api = this.api( true ); - var nodeName = node.nodeName.toUpperCase(); - - if ( nodeName == 'TR' ) { - return api.row( node ).index(); - } - else if ( nodeName == 'TD' || nodeName == 'TH' ) { - var cell = api.cell( node ).index(); - - return [ - cell.row, - cell.columnVisible, - cell.column - ]; - } - return null; - }; - - - /** - * Check to see if a row is 'open' or not. - * @param {node} nTr the table row to check - * @returns {boolean} true if the row is currently open, false otherwise - * @dtopt API - * @deprecated Since v1.10 + * Note that as with the main search ability in DataTables, technically this + * is "filtering", since it is subtractive. However, for consistency in + * naming we call it searching here. + * + * @type array + * @default [] * * @example - * $(document).ready(function() { - * var oTable; + * // The following example shows custom search being applied to the + * // fourth column (i.e. the data[3] index) based on two input values + * // from the end-user, matching the data in a certain range. + * $.fn.dataTable.ext.search.push( + * function( settings, data, dataIndex ) { + * var min = document.getElementById('min').value * 1; + * var max = document.getElementById('max').value * 1; + * var version = data[3] == "-" ? 0 : data[3]*1; * - * // 'open' an information row when a row is clicked on - * $('#example tbody tr').click( function () { - * if ( oTable.fnIsOpen(this) ) { - * oTable.fnClose( this ); - * } else { - * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * if ( min == "" && max == "" ) { + * return true; * } - * } ); - * - * oTable = $('#example').dataTable(); - * } ); + * else if ( min == "" && version < max ) { + * return true; + * } + * else if ( min < version && "" == max ) { + * return true; + * } + * else if ( min < version && version < max ) { + * return true; + * } + * return false; + * } + * ); */ - this.fnIsOpen = function( nTr ) - { - return this.api( true ).row( nTr ).child.isShown(); - }; - - + search: [], + + /** - * This function will place a new row directly after a row which is currently - * on display on the page, with the HTML contents that is passed into the - * function. This can be used, for example, to ask for confirmation that a - * particular record should be deleted. - * @param {node} nTr The table row to 'open' - * @param {string|node|jQuery} mHtml The HTML to put into the row - * @param {string} sClass Class to give the new TD cell - * @returns {node} The row opened. Note that if the table row passed in as the - * first parameter, is not found in the table, this method will silently - * return. - * @dtopt API - * @deprecated Since v1.10 + * Selector extensions * - * @example - * $(document).ready(function() { - * var oTable; + * The `selector` option can be used to extend the options available for the + * selector modifier options (`selector-modifier` object data type) that + * each of the three built in selector types offer (row, column and cell + + * their plural counterparts). For example the Select extension uses this + * mechanism to provide an option to select only rows, columns and cells + * that have been marked as selected by the end user (`{selected: true}`), + * which can be used in conjunction with the existing built in selector + * options. * - * // 'open' an information row when a row is clicked on - * $('#example tbody tr').click( function () { - * if ( oTable.fnIsOpen(this) ) { - * oTable.fnClose( this ); - * } else { - * oTable.fnOpen( this, "Temporary row opened", "info_row" ); - * } - * } ); + * Each property is an array to which functions can be pushed. The functions + * take three attributes: * - * oTable = $('#example').dataTable(); - * } ); + * * Settings object for the host table + * * Options object (`selector-modifier` object type) + * * Array of selected item indexes + * + * The return is an array of the resulting item indexes after the custom + * selector has been applied. + * + * @type object */ - this.fnOpen = function( nTr, mHtml, sClass ) - { - return this.api( true ) - .row( nTr ) - .child( mHtml, sClass ) - .show() - .child()[0]; - }; - - + selector: { + cell: [], + column: [], + row: [] + }, + + /** - * Change the pagination - provides the internal logic for pagination in a simple API - * function. With this function you can have a DataTables table go to the next, - * previous, first or last pages. - * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" - * or page number to jump to (integer), note that page 0 is the first page. - * @param {bool} [bRedraw=true] Redraw the table or not - * @dtopt API - * @deprecated Since v1.10 + * Legacy configuration options. Enable and disable legacy options that + * are available in DataTables. * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * oTable.fnPageChange( 'next' ); - * } ); + * @type object */ - this.fnPageChange = function ( mAction, bRedraw ) - { - var api = this.api( true ).page( mAction ); - - if ( bRedraw === undefined || bRedraw ) { - api.draw(false); - } - }; - - + legacy: { + /** + * Enable / disable DataTables 1.9 compatible server-side processing + * requests + * + * @type boolean + * @default null + */ + ajax: null + }, + + /** - * Show a particular column - * @param {int} iCol The column whose display should be changed - * @param {bool} bShow Show (true) or hide (false) the column - * @param {bool} [bRedraw=true] Redraw the table or not - * @dtopt API - * @deprecated Since v1.10 + * Pagination plug-in methods. + * + * Each entry in this object is a function and defines which buttons should + * be shown by the pagination rendering method that is used for the table: + * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the + * buttons are displayed in the document, while the functions here tell it + * what buttons to display. This is done by returning an array of button + * descriptions (what each button will do). * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); + * Pagination types (the four built in options and any additional plug-in + * options defined here) can be used through the `paginationType` + * initialisation parameter. * - * // Hide the second column after initialisation - * oTable.fnSetColumnVis( 1, false ); - * } ); - */ - this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) - { - var api = this.api( true ).column( iCol ).visible( bShow ); - - if ( bRedraw === undefined || bRedraw ) { - api.columns.adjust().draw(); - } - }; - - - /** - * Get the settings for a particular table for external manipulation - * @returns {object} DataTables settings object. See - * {@link DataTable.models.oSettings} - * @dtopt API - * @deprecated Since v1.10 + * The functions defined take two parameters: * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * var oSettings = oTable.fnSettings(); + * 1. `{int} page` The current page index + * 2. `{int} pages` The number of pages in the table * - * // Show an example parameter from the settings - * alert( oSettings._iDisplayStart ); - * } ); - */ - this.fnSettings = function() - { - return _fnSettingsFromNode( this[_ext.iApiIndex] ); - }; - - - /** - * Sort the table by a particular column - * @param {int} iCol the data index to sort on. Note that this will not match the - * 'display index' if you have hidden data entries - * @dtopt API - * @deprecated Since v1.10 + * Each function is expected to return an array where each element of the + * array can be one of: * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); + * * `first` - Jump to first page when activated + * * `last` - Jump to last page when activated + * * `previous` - Show previous page when activated + * * `next` - Show next page when activated + * * `{int}` - Show page of the index given + * * `{array}` - A nested array containing the above elements to add a + * containing 'DIV' element (might be useful for styling). * - * // Sort immediately with columns 0 and 1 - * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); - * } ); - */ - this.fnSort = function( aaSort ) - { - this.api( true ).order( aaSort ).draw(); - }; - - - /** - * Attach a sort listener to an element for a given column - * @param {node} nNode the element to attach the sort listener to - * @param {int} iColumn the column that a click on this node will sort on - * @param {function} [fnCallback] callback function when sort is run - * @dtopt API - * @deprecated Since v1.10 + * Note that DataTables v1.9- used this object slightly differently whereby + * an object with two functions would be defined for each plug-in. That + * ability is still supported by DataTables 1.10+ to provide backwards + * compatibility, but this option of use is now decremented and no longer + * documented in DataTables 1.10+. * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); + * @type object + * @default {} * - * // Sort on column 1, when 'sorter' is clicked on - * oTable.fnSortListener( document.getElementById('sorter'), 1 ); - * } ); + * @example + * // Show previous, next and current page buttons only + * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { + * return [ 'previous', page, 'next' ]; + * }; */ - this.fnSortListener = function( nNode, iColumn, fnCallback ) - { - this.api( true ).order.listener( nNode, iColumn, fnCallback ); - }; - - + pager: {}, + + + renderer: { + pageButton: {}, + header: {} + }, + + /** - * Update a table cell or row - this method will accept either a single value to - * update the cell with, an array of values with one element for each column or - * an object in the same format as the original data source. The function is - * self-referencing in order to make the multi column updates easier. - * @param {object|array|string} mData Data to update the cell/row with - * @param {node|int} mRow TR element you want to update or the aoData index - * @param {int} [iColumn] The column to update, give as null or undefined to - * update a whole row. - * @param {bool} [bRedraw=true] Redraw the table or not - * @param {bool} [bAction=true] Perform pre-draw actions or not - * @returns {int} 0 on success, 1 on error - * @dtopt API - * @deprecated Since v1.10 + * Ordering plug-ins - custom data source + * + * The extension options for ordering of data available here is complimentary + * to the default type based ordering that DataTables typically uses. It + * allows much greater control over the the data that is being used to + * order a column, but is necessarily therefore more complex. + * + * This type of ordering is useful if you want to do ordering based on data + * live from the DOM (for example the contents of an 'input' element) rather + * than just the static string that DataTables knows of. + * + * The way these plug-ins work is that you create an array of the values you + * wish to be ordering for the column in question and then return that + * array. The data in the array much be in the index order of the rows in + * the table (not the currently ordering order!). Which order data gathering + * function is run here depends on the `dt-init columns.orderDataType` + * parameter that is used for the column (if any). + * + * The functions defined take two parameters: + * + * 1. `{object}` DataTables settings object: see + * {@link DataTable.models.oSettings} + * 2. `{int}` Target column index + * + * Each function is expected to return an array: + * + * * `{array}` Data for the column to be ordering upon + * + * @type array * * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell - * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row - * } ); - */ - this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) - { - var api = this.api( true ); - - if ( iColumn === undefined || iColumn === null ) { - api.row( mRow ).data( mData ); - } - else { - api.cell( mRow, iColumn ).data( mData ); - } - - if ( bAction === undefined || bAction ) { - api.columns.adjust(); - } - - if ( bRedraw === undefined || bRedraw ) { - api.draw(); - } - return 0; - }; - - + * // Ordering using `input` node values + * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) + * { + * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { + * return $('input', td).val(); + * } ); + * } + */ + order: {}, + + /** - * Provide a common method for plug-ins to check the version of DataTables being used, in order - * to ensure compatibility. - * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the - * formats "X" and "X.Y" are also acceptable. - * @returns {boolean} true if this version of DataTables is greater or equal to the required - * version, or false if this version of DataTales is not suitable - * @method - * @dtopt API - * @deprecated Since v1.10 + * Type based plug-ins. * - * @example - * $(document).ready(function() { - * var oTable = $('#example').dataTable(); - * alert( oTable.fnVersionCheck( '1.9.0' ) ); - * } ); + * Each column in DataTables has a type assigned to it, either by automatic + * detection or by direct assignment using the `type` option for the column. + * The type of a column will effect how it is ordering and search (plug-ins + * can also make use of the column type if required). + * + * @namespace */ - this.fnVersionCheck = _ext.fnVersionCheck; - - - var _that = this; - var emptyInit = options === undefined; - var len = this.length; - - if ( emptyInit ) { - options = {}; - } - - this.oApi = this.internal = _ext.internal; - - // Extend with old style plug-in API methods - for ( var fn in DataTable.ext.internal ) { - if ( fn ) { - this[fn] = _fnExternApiFunc(fn); - } - } - - this.each(function() { - // For each initialisation we want to give it a clean initialisation - // object that can be bashed around - var o = {}; - var oInit = len > 1 ? // optimisation for single table case - _fnExtend( o, options, true ) : - options; - - /*global oInit,_that,emptyInit*/ - var i=0, iLen, j, jLen, k, kLen; - var sId = this.getAttribute( 'id' ); - var bInitHandedOff = false; - var defaults = DataTable.defaults; - var $this = $(this); - - - /* Sanity check */ - if ( this.nodeName.toLowerCase() != 'table' ) - { - _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); - return; - } - - /* Backwards compatibility for the defaults */ - _fnCompatOpts( defaults ); - _fnCompatCols( defaults.column ); - - /* Convert the camel-case defaults to Hungarian */ - _fnCamelToHungarian( defaults, defaults, true ); - _fnCamelToHungarian( defaults.column, defaults.column, true ); - - /* Setting up the initialisation object */ - _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); - - - - /* Check to see if we are re-initialising a table */ - var allSettings = DataTable.settings; - for ( i=0, iLen=allSettings.length ; iafnSortData + * for searching data. + * + * The functions defined take a single parameter: + * + * 1. `{*}` Data from the column cell to be prepared for searching + * + * Each function is expected to return: + * + * * `{string|null}` Formatted string that will be used for the searching. + * + * @type object + * @default {} + * + * @example + * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { + * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); + * } */ - var anThs = []; - var aoColumnsInit; - var nThead = this.getElementsByTagName('thead'); - if ( nThead.length !== 0 ) - { - _fnDetectHeader( oSettings.aoHeader, nThead[0] ); - anThs = _fnGetUniqueThs( oSettings ); - } - - /* If not given a column array, generate one with nulls */ - if ( oInit.aoColumns === null ) - { - aoColumnsInit = []; - for ( i=0, iLen=anThs.length ; i0 if the first parameter should be sorted height than the second + * parameter. + * + * @type object + * @default {} + * + * @example + * // Numeric ordering of formatted numbers with a pre-formatter + * $.extend( $.fn.dataTable.ext.type.order, { + * "string-pre": function(x) { + * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); + * return parseFloat( a ); + * } + * } ); + * + * @example + * // Case-sensitive string ordering, with no pre-formatting method + * $.extend( $.fn.dataTable.ext.order, { + * "string-case-asc": function(x,y) { + * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); + * }, + * "string-case-desc": function(x,y) { + * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); + * } + * } ); */ - if ( rowOne.length ) { - var a = function ( cell, name ) { - return cell.getAttribute( 'data-'+name ) !== null ? name : null; - }; - - $( rowOne[0] ).children('th, td').each( function (i, cell) { - var col = oSettings.aoColumns[i]; - - if ( col.mData === i ) { - var sort = a( cell, 'sort' ) || a( cell, 'order' ); - var filter = a( cell, 'filter' ) || a( cell, 'search' ); - - if ( sort !== null || filter !== null ) { - col.mData = { - _: i+'.display', - sort: sort !== null ? i+'.@data-'+sort : undefined, - type: sort !== null ? i+'.@data-'+sort : undefined, - filter: filter !== null ? i+'.@data-'+filter : undefined - }; - - _fnColumnOptions( oSettings, i ); - } - } - } ); - } - - var features = oSettings.oFeatures; - var loadedInit = function () { - /* - * Sorting - * @todo For modularisation (1.11) this needs to do into a sort start up handler - */ - - // If aaSorting is not defined, then we use the first indicator in asSorting - // in case that has been altered, so the default sort reflects that option - if ( oInit.aaSorting === undefined ) { - var sorting = oSettings.aaSorting; - for ( i=0, iLen=sorting.length ; i').appendTo($this); - } - oSettings.nTHead = thead[0]; - - var tbody = $this.children('tbody'); - if ( tbody.length === 0 ) { - tbody = $('').insertAfter(thead); - } - oSettings.nTBody = tbody[0]; - - var tfoot = $this.children('tfoot'); - if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) { - // If we are a scrolling table, and no footer has been given, then we need to create - // a tfoot element for the caption element to be appended to - tfoot = $('').appendTo($this); - } - - if ( tfoot.length === 0 || tfoot.children().length === 0 ) { - $this.addClass( oClasses.sNoFooter ); - } - else if ( tfoot.length > 0 ) { - oSettings.nTFoot = tfoot[0]; - _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); - } - - /* Check if there is data passing into the constructor */ - if ( oInit.aaData ) { - for ( i=0 ; i/g; + var _re_html = /<([^>]*>)/g; + var _max_str_len = Math.pow(2, 28); // This is not strict ISO8601 - Date.parse() is quite lax, although // implementations differ between browsers. - var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/; + var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/; // Escape regular expression special characters var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); - // http://en.wikipedia.org/wiki/Foreign_exchange_market + // https://en.wikipedia.org/wiki/Foreign_exchange_market // - \u20BD - Russian ruble. // - \u20a9 - South Korean Won // - \u20BA - Turkish Lira @@ -1387,13 +1142,18 @@ }; - var _isNumber = function ( d, decimalPoint, formatted ) { - var strType = typeof d === 'string'; + var _isNumber = function ( d, decimalPoint, formatted, allowEmpty ) { + var type = typeof d; + var strType = type === 'string'; + + if ( type === 'number' || type === 'bigint') { + return true; + } // If empty return immediately so there must be a number if it is a // formatted string (this stops the string "k", or "kr", etc being detected // as a formatted number for currency - if ( _empty( d ) ) { + if ( allowEmpty && _empty( d ) ) { return true; } @@ -1414,16 +1174,21 @@ return _empty( d ) || typeof d === 'string'; }; - - var _htmlNumeric = function ( d, decimalPoint, formatted ) { - if ( _empty( d ) ) { + // Is a string a number surrounded by HTML? + var _htmlNumeric = function ( d, decimalPoint, formatted, allowEmpty ) { + if ( allowEmpty && _empty( d ) ) { return true; } + // input and select strings mean that this isn't just a number + if (typeof d === 'string' && d.match(/<(input|select)/i)) { + return null; + } + var html = _isHtml( d ); return ! html ? null : - _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? + _isNumber( _stripHtml( d ), decimalPoint, formatted, allowEmpty ) ? true : null; }; @@ -1465,14 +1230,16 @@ // is essential here if ( prop2 !== undefined ) { for ( ; i _max_str_len) { + throw new Error('Exceeded max str len'); + } + + var previous; + + input = input.replace(_re_html, ''); // Complete tags + + // Safety for incomplete script tag - use do / while to ensure that + // we get all instances + do { + previous = input; + input = input.replace(/
010.68 +41.27+12.34 N 224