Repository: astropy/astropy

Issue:
New FITS compression code breaks loading Pan-STARRS images 
### Description

The code for reading compressed FITS files in 5.3 is crashing on loading the images from Pan-STARRS. It was working nicely in 5.2.

The problem manifests like that:
```
$ python -c 'from astropy.io import fits; fits.getdata("http://ps1images.stsci.edu/rings.v3.skycell/1004/016/rings.v3.skycell.1004.016.stk.g.unconv.fits")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/ltzfs/ASTRO101/anaconda3/envs/astro101/lib/python3.11/site-packages/astropy/io/fits/convenience.py", line 232, in getdata
    data = hdu.data
           ^^^^^^^^
  File "/ltzfs/ASTRO101/anaconda3/envs/astro101/lib/python3.11/site-packages/astropy/utils/decorators.py", line 837, in __get__
    val = self.fget(obj)
          ^^^^^^^^^^^^^^
  File "/ltzfs/ASTRO101/anaconda3/envs/astro101/lib/python3.11/site-packages/astropy/io/fits/hdu/compressed.py", line 1558, in data
    data = self.section[...]
           ~~~~~~~~~~~~^^^^^
  File "/ltzfs/ASTRO101/anaconda3/envs/astro101/lib/python3.11/site-packages/astropy/io/fits/hdu/compressed.py", line 2189, in __getitem__
    data = decompress_hdu_section(self.hdu, first_tile_index, last_tile_index)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/ltzfs/ASTRO101/anaconda3/envs/astro101/lib/python3.11/site-packages/astropy/io/fits/_tiled_compression/tiled_compression.py", line 442, in decompress_hdu_section
    tile_data[blank_mask] = np.nan
    ~~~~~~~~~^^^^^^^^^^^^
ValueError: cannot convert float NaN to integer
```

### Expected behavior

The images from Pan-STARRS were loading without issues in pre-5.3

### How to Reproduce

```python
from astropy.io import fits
fits.getdata("http://ps1images.stsci.edu/rings.v3.skycell/1004/016/rings.v3.skycell.1004.016.stk.g.unconv.fits")
```


### Versions

Linux-4.18.0-372.9.1.el8.x86_64-x86_64-with-glibc2.28
Python 3.11.4 (main, Jul  5 2023, 13:45:01) [GCC 11.2.0]
astropy 5.3.2
Numpy 1.25.2
pyerfa 2.0.0.3
Scipy 1.11.2
Matplotlib 3.7.2


Discussion:
github-actions[bot]: Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.

  - [ ] Do the proposed changes actually accomplish desired goals?
  - [ ] Do the proposed changes follow the [Astropy coding guidelines](https://docs.astropy.org/en/latest/development/codeguide.html)?
  - [ ] Are tests added/updated as required? If so, do they follow the [Astropy testing guidelines](https://docs.astropy.org/en/latest/development/testguide.html)?
  - [ ] Are docs added/updated as required? If so, do they follow the [Astropy documentation guidelines](https://docs.astropy.org/en/latest/development/docguide.html)?
  - [ ] Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for [rebase](https://docs.astropy.org/en/latest/development/development_details.html#rebase-if-necessary) and [squash](https://docs.astropy.org/en/latest/development/development_details.html#squash-if-necessary).
  - [ ] Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the [bot](https://docs.astropy.org/en/latest/development/development_details.html#pre-commit).
  - [ ] Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  - [ ] Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  - [ ] At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) *before* merge.
pllim: Maybe we should credit @rlwastro as the co-author here since this is a manual forward-port? (Huh, why did we put the fix in backport branch only if forward port is needed anyway?)

Patch:
diff --git a/README.rst b/README.rst
index fd54e9ae75..532d368d7f 100644
--- a/README.rst
+++ b/README.rst
@@ -1,71 +1,76 @@
-|Astropy Logo|
+=======
+Astropy
+=======
+
+.. container::
 
-----
+    |Actions Status| |CircleCI Status| |Coverage Status| |PyPI Status| |Documentation Status| |Pre-Commit| |Ruff| |Zenodo|
 
-|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.
+The Astropy Project (https://astropy.org/) is a community effort to develop a
+single core package for Astronomy in Python and foster interoperability between
+Python astronomy packages. This repository contains the core package which is
+intended to contain much of the core functionality and some common tools needed
+for performing astronomy and astrophysics with Python.
 
-* `Website <https://astropy.org/>`_
-* `Documentation <https://docs.astropy.org/>`_
-* `Slack <https://astropy.slack.com/>`_
-* `Open Astronomy Discourse <https://community.openastronomy.org/c/astropy/8>`_
-* `Astropy users mailing list <https://mail.python.org/mailman/listinfo/astropy>`_
-* `Astropy developers mailing list <https://groups.google.com/g/astropy-dev>`_
+Table of Contents
+=================
+
+- `Installation <#installation>`_
+- `Contributing <#contributing>`_
+- `Getting Started with GitHub Codespaces <#getting-started-with-github-codespaces>`_
+- `Supporting the Project <#supporting-the-project>`_
+- `License <#license>`_
 
 Installation
 ============
 
+Releases are `registered on PyPI <https://pypi.org/project/astropy>`_,
+and development is occurring at the
+`project's GitHub page <https://github.com/astropy/astropy>`_.
+
+For detailed installation instructions, see the
+`online documentation <https://docs.astropy.org/>`_
+or `docs/install.rst <docs/install.rst>`_ in this source distribution.
+
 To install `astropy` from PyPI, use:
 
 .. code-block:: bash
 
     pip install astropy
 
-For more detailed instructions, see the `install guide
-<https://docs.astropy.org/en/stable/install.html>`_ in the docs.
-
 Contributing
 ============
 
-.. raw:: html
-
-    <picture>
-      <source media="(prefers-color-scheme: dark)" srcset="https://github.com/astropy/repo_stats/blob/cache/cache/astropy_user_stats_dark.png?raw=true">
-      <source media="(prefers-color-scheme: light)" srcset="https://github.com/astropy/repo_stats/blob/cache/cache/astropy_user_stats_light.png?raw=true">
-      <img alt="Astropy user statistics" src="https://github.com/astropy/repo_stats/blob/cache/cache/astropy_user_stats_light.png?raw=true">
-    </picture>
-
 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,
-inclusive, successful, and growing community that abides by the
-`Astropy Community Code of Conduct
-<https://www.astropy.org/about.html#codeofconduct>`_.
+inclusive, successful, and growing community by abiding with the
+`Astropy Community Code of Conduct <https://www.astropy.org/about.html#codeofconduct>`_.
 
-For guidance on contributing to or submitting feedback for the Astropy Project,
-see the `contributions page <https://www.astropy.org/contribute.html>`_.
-For contributing code specifically, the developer docs have a
-`guide <https://docs.astropy.org/en/latest/index_dev.html>`_ with a quickstart.
-There's also a `summary of contribution guidelines <CONTRIBUTING.md>`_.
+More detailed information on contributing to the project or submitting feedback
+can be found on the `contributions page <https://www.astropy.org/contribute.html>`_.
+A `summary of contribution guidelines <CONTRIBUTING.md>`_ can also be
+used as a quick reference when you are ready to start writing or validating
+code for submission.
 
-Developing with Codespaces
-==========================
+Getting Started with GitHub Codespaces
+======================================
 
-GitHub Codespaces is a cloud development environment using Visual Studio Code
-in your browser. This is a convenient way to start developing Astropy, using
-our `dev container <.devcontainer/devcontainer.json>`_ configured
-with the required packages. For help, see the `GitHub Codespaces
-docs <https://docs.github.com/en/codespaces>`_.
+Codespaces is a cloud development environment supported by GitHub.
+None of the Astropy build machinery depends on it, but it is a
+convenient way to quickly get started doing development on Astropy.
+
+To get started, create a codespace for this repository by clicking this:
 
 |Codespaces|
 
-Acknowledging and Citing
-========================
-See the `acknowledgement and citation guide
-<https://www.astropy.org/acknowledging.html>`_ and the `CITATION
-<https://github.com/astropy/astropy/blob/main/astropy/CITATION>`_ file.
+A codespace will open in a web-based version of Visual Studio Code.
+The `dev container <.devcontainer/devcontainer.json>`_ is fully configured
+with software needed for this project. For help, see the `GitHub Codespaces
+Support page <https://docs.github.com/en/codespaces>`_.
+
+**Note**: Dev containers is an open spec which is supported by
+`GitHub Codespaces <https://github.com/codespaces>`_ and
+`other tools <https://containers.dev/supporting>`_.
 
 Supporting the Project
 ======================
@@ -85,10 +90,6 @@ Astropy is licensed under a 3-clause BSD style license - see the
 `LICENSE.rst <LICENSE.rst>`_ file.
 
 
-.. |Astropy Logo| image:: https://github.com/astropy/repo_stats/blob/main/dashboard_template/astropy_banner_gray.svg
-    :target: https://www.astropy.org/
-    :alt: Astropy
-
 .. |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
@@ -106,16 +107,16 @@ Astropy is licensed under a 3-clause BSD style license - see the
     :alt: Astropy's PyPI Status
 
 .. |Zenodo| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.4670728.svg
-    :target: https://doi.org/10.5281/zenodo.4670728
-    :alt: Zenodo DOI
+   :target: https://doi.org/10.5281/zenodo.4670728
+   :alt: Zenodo DOI
 
 .. |Documentation Status| image:: https://img.shields.io/readthedocs/astropy/latest.svg?logo=read%20the%20docs&logoColor=white&label=Docs&version=stable
     :target: https://docs.astropy.org/en/stable/?badge=stable
     :alt: Documentation Status
 
 .. |Pre-Commit| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white
-    :target: https://github.com/pre-commit/pre-commit
-    :alt: pre-commit
+   :target: https://github.com/pre-commit/pre-commit
+   :alt: pre-commit
 
 .. |Ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
     :target: https://github.com/astral-sh/ruff
diff --git a/astropy/io/fits/diff.py b/astropy/io/fits/diff.py
index fef8a5f7dc..a2de2fb181 100644
--- a/astropy/io/fits/diff.py
+++ b/astropy/io/fits/diff.py
@@ -51,12 +51,6 @@ _COL_ATTRS = [
 ]
 
 
-def _get_differences(a, b):
-    relative = abs(b - a) / abs(b)
-    absolute = float(abs(b - a))
-    return relative, absolute
-
-
 class _BaseDiff:
     """
     Base class for all FITS diff objects.
@@ -1115,9 +1109,6 @@ class ImageDataDiff(_BaseDiff):
         if not self.diff_pixels:
             return
 
-        max_relative = 0
-        max_absolute = 0
-
         for index, values in self.diff_pixels:
             # Convert to int to avoid np.int64 in list repr.
             index = [int(x + 1) for x in reversed(index)]
@@ -1130,9 +1121,6 @@ class ImageDataDiff(_BaseDiff):
                 rtol=self.rtol,
                 atol=self.atol,
             )
-            rdiff, adiff = _get_differences(values[0], values[1])
-            max_relative = max(max_relative, rdiff)
-            max_absolute = max(max_absolute, adiff)
 
         if self.diff_total > self.numdiffs:
             self._writeln(" ...")
@@ -1140,8 +1128,6 @@ class ImageDataDiff(_BaseDiff):
             f" {self.diff_total} different pixels found "
             f"({self.diff_ratio:.2%} different)."
         )
-        self._writeln(f" Maximum relative difference: {max_relative}")
-        self._writeln(f" Maximum absolute difference: {max_absolute}")
 
 
 class RawDataDiff(ImageDataDiff):
diff --git a/astropy/io/fits/hdu/compressed/_tiled_compression.py b/astropy/io/fits/hdu/compressed/_tiled_compression.py
index 7cac4655d4..80f07e8249 100644
--- a/astropy/io/fits/hdu/compressed/_tiled_compression.py
+++ b/astropy/io/fits/hdu/compressed/_tiled_compression.py
@@ -284,6 +284,7 @@ def decompress_image_data_section(
     compression_type,
     compressed_header,
     bintable,
+    image_header,
     first_tile_index,
     last_tile_index,
 ):
@@ -359,7 +360,7 @@ def decompress_image_data_section(
     else:
         zzero_column = None
 
-    zblank_header = compressed_header.get("ZBLANK", None)
+    zblank_header = compressed_header.get("ZBLANK", image_header.get("BLANK", None))
 
     gzip_compressed_data_column = None
     gzip_compressed_data_dtype = None
@@ -465,7 +466,7 @@ def decompress_image_data_section(
             if zblank is not None:
                 if not tile_data.flags.writeable:
                     tile_data = tile_data.copy()
-                tile_data[blank_mask] = np.nan
+                tile_data[blank_mask] = zblank_header if zbitpix > 0 else np.nan
 
         image_data[tile_slices] = tile_data
 
diff --git a/astropy/io/fits/hdu/compressed/header.py b/astropy/io/fits/hdu/compressed/header.py
index 3d652d49e9..86accddfd1 100644
--- a/astropy/io/fits/hdu/compressed/header.py
+++ b/astropy/io/fits/hdu/compressed/header.py
@@ -296,6 +296,24 @@ def _bintable_header_to_image_header(bintable_header):
         del image_header["PCOUNT"]
         del image_header["GCOUNT"]
 
+    # Fill in BLANK keyword if necessary
+    if image_header["BITPIX"] > 0 and "BLANK" not in image_header:
+        if "ZBLANK" in bintable_header:
+            image_header["BLANK"] = bintable_header["ZBLANK"]
+        else:
+            # check for column named "ZBLANK"
+            for i in range(1, bintable_header["TFIELDS"] + 1):
+                if bintable_header[f"TTYPE{i}"] == "ZBLANK":
+                    # required BLANK keyword is missing
+                    # use most negative value as default
+                    image_header["BLANK"] = -(1 << (image_header["BITPIX"] - 1))
+                    warnings.warn(
+                        f"Setting default value {image_header['BLANK']} for "
+                        "missing BLANK keyword in compressed extension",
+                        AstropyUserWarning,
+                    )
+                    break
+
     # Look to see if there are any blank cards in the table
     # header.  If there are, there should be the same number
     # of blank cards in the image header.  Add blank cards to
diff --git a/astropy/io/fits/hdu/compressed/section.py b/astropy/io/fits/hdu/compressed/section.py
index 1b9dfe953a..4377e7fbc8 100644
--- a/astropy/io/fits/hdu/compressed/section.py
+++ b/astropy/io/fits/hdu/compressed/section.py
@@ -63,6 +63,7 @@ class CompImageSection:
                 self.hdu.compression_type,
                 self.hdu._bintable.header,
                 self.hdu._bintable,
+                self.hdu.header,
                 first_tile_index,
                 last_tile_index,
             )
@@ -118,6 +119,7 @@ class CompImageSection:
             self.hdu.compression_type,
             self.hdu._bintable.header,
             self.hdu._bintable,
+            self.hdu.header,
             first_tile_index,
             last_tile_index,
         )
diff --git a/astropy/stats/sigma_clipping.py b/astropy/stats/sigma_clipping.py
index cd2075e56c..03fb012f3c 100644
--- a/astropy/stats/sigma_clipping.py
+++ b/astropy/stats/sigma_clipping.py
@@ -349,14 +349,10 @@ class SigmaClip:
         if masked:
             result = np.ma.array(data, mask=mask, copy=copy)
         else:
-            if data.dtype.kind != "f":
-                # float array type is needed to insert nans into the array
-                result = data.astype(np.float32)  # also makes a copy
+            if copy:
+                result = data.astype(float, copy=True)
             else:
-                if copy:
-                    result = data.copy()
-                else:
-                    result = data
+                result = data
             result[mask] = np.nan
 
         if unit is not None:
@@ -609,12 +605,11 @@ class SigmaClip:
             If ``masked=False`` and ``axis`` is specified, then the
             output `~numpy.ndarray` will have the same shape as the
             input ``data`` and contain ``np.nan`` where values were
-            clipped. In this case, integer-type ``data`` arrays will
-            be converted to `~numpy.float32`. If the input ``data``
-            was a masked array, then the output `~numpy.ndarray` will
-            also contain ``np.nan`` where the input mask was `True`. If
-            ``return_bounds=True`` then the returned minimum and maximum
-            clipping thresholds will be be `~numpy.ndarray`\\s.
+            clipped. If the input ``data`` was a masked array, then the
+            output `~numpy.ndarray` will also contain ``np.nan`` where
+            the input mask was `True`. If ``return_bounds=True`` then
+            the returned minimum and maximum clipping thresholds will be
+            be `~numpy.ndarray`\\s.
         """
         data = np.asanyarray(data)
 
@@ -804,15 +799,13 @@ def sigma_clip(
         have been removed. If ``return_bounds=True`` then the returned
         minimum and maximum thresholds are scalars.
 
-        If ``masked=False`` and ``axis`` is specified, then the
-        output `~numpy.ndarray` will have the same shape as the input
-        ``data`` and contain ``np.nan`` where values were clipped. In
-        this case, integer-type ``data`` arrays will be converted to
-        `~numpy.float32`. If the input ``data`` was a masked array,
-        then the output `~numpy.ndarray` will also contain ``np.nan``
-        where the input mask was `True`. If ``return_bounds=True`` then
-        the returned minimum and maximum clipping thresholds will be
-        `~numpy.ndarray`\\s.
+        If ``masked=False`` and ``axis`` is specified, then the output
+        `~numpy.ndarray` will have the same shape as the input ``data``
+        and contain ``np.nan`` where values were clipped. If the input
+        ``data`` was a masked array, then the output `~numpy.ndarray`
+        will also contain ``np.nan`` where the input mask was `True`.
+        If ``return_bounds=True`` then the returned minimum and maximum
+        clipping thresholds will be `~numpy.ndarray`\\s.
 
     See Also
     --------
diff --git a/astropy/table/table.py b/astropy/table/table.py
index 9507412916..0c1382f5ce 100644
--- a/astropy/table/table.py
+++ b/astropy/table/table.py
@@ -2694,9 +2694,6 @@ class Table:
             raise ValueError("cannot replace a table index column")
 
         col = self._convert_data_to_col(col, name=name, copy=copy)
-        if col.shape == ():
-            raise ValueError("cannot replace a column with a scalar.")
-
         self._set_col_parent_table_and_mask(col)
 
         # Ensure that new column is the right length, unless it is the only column
diff --git a/astropy/units/core.py b/astropy/units/core.py
index 14f00cac7e..427aa99c97 100644
--- a/astropy/units/core.py
+++ b/astropy/units/core.py
@@ -31,12 +31,7 @@ from .utils import (
 )
 
 if TYPE_CHECKING:
-    from types import TracebackType
-    from typing import Any, Final, Literal, Self
-
-    from .physical import PhysicalType
-    from .quantity import Quantity
-    from .typing import Real, UnitPower, UnitScale
+    from astropy.units.typing import UnitPower
 
 __all__ = [
     "UnitBase",
@@ -58,7 +53,7 @@ __all__ = [
     "one",
 ]
 
-UNITY: Final[float] = 1.0
+UNITY = 1.0
 
 
 def _flatten_units_collection(items):
@@ -163,28 +158,28 @@ class _UnitRegistry:
             self.add_enabled_equivalencies(equivalencies)
             self.add_enabled_aliases(aliases)
 
-    def _reset_units(self) -> None:
+    def _reset_units(self):
         self._all_units = set()
         self._non_prefix_units = set()
         self._registry = {}
         self._by_physical_type = {}
 
-    def _reset_equivalencies(self) -> None:
+    def _reset_equivalencies(self):
         self._equivalencies = set()
 
-    def _reset_aliases(self) -> None:
+    def _reset_aliases(self):
         self._aliases = {}
 
     @property
-    def registry(self) -> dict[str, UnitBase]:
+    def registry(self):
         return self._registry
 
     @property
-    def all_units(self) -> set[UnitBase]:
+    def all_units(self):
         return self._all_units
 
     @property
-    def non_prefix_units(self) -> set[UnitBase]:
+    def non_prefix_units(self):
         return self._non_prefix_units
 
     def set_enabled_units(self, units):
@@ -244,7 +239,7 @@ class _UnitRegistry:
 
             self._by_physical_type.setdefault(unit._physical_type_id, set()).add(unit)
 
-    def get_units_with_physical_type(self, unit: UnitBase) -> set[UnitBase]:
+    def get_units_with_physical_type(self, unit):
         """
         Get all units in the registry with the same physical type as
         the given unit.
@@ -299,10 +294,10 @@ class _UnitRegistry:
         self._equivalencies |= set(equivalencies)
 
     @property
-    def aliases(self) -> dict[str, UnitBase]:
+    def aliases(self):
         return self._aliases
 
-    def set_enabled_aliases(self, aliases: dict[str, UnitBase]) -> None:
+    def set_enabled_aliases(self, aliases):
         """
         Set aliases for units.
 
@@ -321,7 +316,7 @@ class _UnitRegistry:
         self._reset_aliases()
         self.add_enabled_aliases(aliases)
 
-    def add_enabled_aliases(self, aliases: dict[str, UnitBase]) -> None:
+    def add_enabled_aliases(self, aliases):
         """
         Add aliases for units.
 
@@ -358,22 +353,17 @@ class _UnitContext:
     def __init__(self, init=[], equivalencies=[]):
         _unit_registries.append(_UnitRegistry(init=init, equivalencies=equivalencies))
 
-    def __enter__(self) -> None:
+    def __enter__(self):
         pass
 
-    def __exit__(
-        self,
-        type: type[BaseException] | None,
-        value: BaseException | None,
-        tb: TracebackType | None,
-    ) -> None:
+    def __exit__(self, type, value, tb):
         _unit_registries.pop()
 
 
 _unit_registries = [_UnitRegistry()]
 
 
-def get_current_unit_registry() -> _UnitRegistry:
+def get_current_unit_registry():
     return _unit_registries[-1]
 
 
@@ -541,7 +531,7 @@ def add_enabled_equivalencies(equivalencies):
     return context
 
 
-def set_enabled_aliases(aliases: dict[str, UnitBase]) -> _UnitContext:
+def set_enabled_aliases(aliases):
     """
     Set aliases for units.
 
@@ -576,7 +566,7 @@ def set_enabled_aliases(aliases: dict[str, UnitBase]) -> _UnitContext:
     return context
 
 
-def add_enabled_aliases(aliases: dict[str, UnitBase]) -> _UnitContext:
+def add_enabled_aliases(aliases):
     """
     Add aliases for units.
 
@@ -626,15 +616,15 @@ class UnitBase:
 
     # Make sure that __rmul__ of units gets called over the __mul__ of Numpy
     # arrays to avoid element-wise multiplication.
-    __array_priority__: Final[Literal[1000]] = 1000
+    __array_priority__ = 1000
 
-    def __deepcopy__(self, memo: dict[int, Any] | None) -> Self:
+    def __deepcopy__(self, memo):
         # This may look odd, but the units conversion will be very
         # broken after deep-copying if we don't guarantee that a given
         # physical unit corresponds to only one instance
         return self
 
-    def _repr_latex_(self) -> str:
+    def _repr_latex_(self):
         """
         Generate latex representation of unit name.  This is used by
         the IPython notebook to print a unit with a nice layout.
@@ -645,15 +635,15 @@ class UnitBase:
         """
         return unit_format.Latex.to_string(self)
 
-    def __bytes__(self) -> bytes:
+    def __bytes__(self):
         """Return string representation for unit."""
         return unit_format.Generic.to_string(self).encode("unicode_escape")
 
-    def __str__(self) -> str:
+    def __str__(self):
         """Return string representation for unit."""
         return unit_format.Generic.to_string(self)
 
-    def __repr__(self) -> str:
+    def __repr__(self):
         string = unit_format.Generic.to_string(self)
 
         return f'Unit("{string}")'
@@ -670,7 +660,7 @@ class UnitBase:
         return tuple(zip((base.name for base in unit.bases), unit.powers))
 
     @property
-    def names(self) -> list[str]:
+    def names(self):
         """
         Returns all of the names associated with this unit.
         """
@@ -679,7 +669,7 @@ class UnitBase:
         )
 
     @property
-    def name(self) -> str:
+    def name(self):
         """
         Returns the canonical (short) name associated with this unit.
         """
@@ -688,7 +678,7 @@ class UnitBase:
         )
 
     @property
-    def aliases(self) -> list[str]:
+    def aliases(self):
         """
         Returns the alias (long) names for this unit.
         """
@@ -697,31 +687,27 @@ class UnitBase:
         )
 
     @property
-    def scale(self) -> UnitScale:
+    def scale(self):
         """
         Return the scale of the unit.
         """
         return 1.0
 
     @property
-    def bases(self) -> list[UnitBase]:
+    def bases(self):
         """
         Return the bases of the unit.
         """
         return [self]
 
     @property
-    def powers(self) -> list[UnitPower]:
+    def powers(self):
         """
         Return the powers of the unit.
         """
         return [1]
 
-    def to_string(
-        self,
-        format: type[unit_format.Base] | str | None = unit_format.Generic,
-        **kwargs,
-    ) -> str:
+    def to_string(self, format=unit_format.Generic, **kwargs):
         r"""Output the unit in the given format as a string.
 
         Parameters
@@ -766,7 +752,7 @@ class UnitBase:
         f = unit_format.get_format(format)
         return f.to_string(self, **kwargs)
 
-    def __format__(self, format_spec: str) -> str:
+    def __format__(self, format_spec):
         """Try to format units using a formatter."""
         try:
             return self.to_string(format=format_spec)
@@ -801,7 +787,7 @@ class UnitBase:
 
         return normalized
 
-    def __pow__(self, p: Real) -> CompositeUnit:
+    def __pow__(self, p):
         try:  # Handling scalars should be as quick as possible
             return CompositeUnit(1, [self], [sanitize_power(p)], _error_check=False)
         except Exception:
@@ -918,7 +904,7 @@ class UnitBase:
             (str(self.scale), *[x.name for x in self.bases], *map(str, self.powers))
         )
 
-    def __getstate__(self) -> dict[str, object]:
+    def __getstate__(self):
         # If we get pickled, we should *not* store the memoized members since
         # hashes of strings vary between sessions.
         state = self.__dict__.copy()
@@ -962,7 +948,7 @@ class UnitBase:
     def __gt__(self, other):
         return not (self <= other)
 
-    def __neg__(self) -> Quantity:
+    def __neg__(self):
         return self * -1.0
 
     def is_equivalent(self, other, equivalencies=[]):
@@ -1139,7 +1125,7 @@ class UnitBase:
 
             raise exc
 
-    def _to(self, other: UnitBase) -> UnitScale:
+    def _to(self, other):
         """
         Returns the scale to the specified unit.
 
@@ -1539,7 +1525,7 @@ class UnitBase:
         return composed
 
     @lazyproperty
-    def si(self) -> UnitBase:
+    def si(self):
         """
         Returns a copy of the current `Unit` instance in SI units.
         """
@@ -1548,7 +1534,7 @@ class UnitBase:
         return self.to_system(si)[0]
 
     @lazyproperty
-    def cgs(self) -> UnitBase:
+    def cgs(self):
         """
         Returns a copy of the current `Unit` instance with CGS units.
         """
@@ -1557,7 +1543,7 @@ class UnitBase:
         return self.to_system(cgs)[0]
 
     @property
-    def physical_type(self) -> PhysicalType:
+    def physical_type(self):
         """
         Physical type(s) dimensionally compatible with the unit.
 
@@ -1633,16 +1619,11 @@ class UnitBase:
         `find_equivalent_units`.
         """
 
-        HEADING_NAMES: Final[tuple[str, str, str]] = (
-            "Primary name",
-            "Unit definition",
-            "Aliases",
-        )
-        # len(HEADING_NAMES), but hard-code since it is constant
-        ROW_LEN: Final[Literal[3]] = 3
-        NO_EQUIV_UNITS_MSG: Final[str] = "There are no equivalent units"
+        HEADING_NAMES = ("Primary name", "Unit definition", "Aliases")
+        ROW_LEN = 3  # len(HEADING_NAMES), but hard-code since it is constant
+        NO_EQUIV_UNITS_MSG = "There are no equivalent units"
 
-        def __repr__(self) -> str:
+        def __repr__(self):
             if len(self) == 0:
                 return self.NO_EQUIV_UNITS_MSG
             else:
@@ -1658,7 +1639,7 @@ class UnitBase:
                 lines = lines[0:1] + ["["] + [f"{x} ," for x in lines[1:]] + ["]"]
                 return "\n".join(lines)
 
-        def _repr_html_(self) -> str:
+        def _repr_html_(self):
             """
             Outputs a HTML table representation within Jupyter notebooks.
             """
@@ -1738,7 +1719,7 @@ class UnitBase:
         }
         return self.EquivalentUnitsList(results)
 
-    def is_unity(self) -> bool:
+    def is_unity(self):
         """
         Returns `True` if the unit is unscaled and dimensionless.
         """
@@ -1821,7 +1802,7 @@ class NamedUnit(UnitBase):
 
         self._inject(namespace)
 
-    def _generate_doc(self) -> str:
+    def _generate_doc(self):
         """
         Generate a docstring for the unit if the user didn't supply
         one.  This is only used from the constructor and may be
@@ -1858,35 +1839,35 @@ class NamedUnit(UnitBase):
         return self._format.get(format, self.name)
 
     @property
-    def names(self) -> list[str]:
+    def names(self):
         """
         Returns all of the names associated with this unit.
         """
         return self._names
 
     @property
-    def name(self) -> str:
+    def name(self):
         """
         Returns the canonical (short) name associated with this unit.
         """
         return self._names[0]
 
     @property
-    def aliases(self) -> list[str]:
+    def aliases(self):
         """
         Returns the alias (long) names for this unit.
         """
         return self._names[1:]
 
     @property
-    def short_names(self) -> list[str]:
+    def short_names(self):
         """
         Returns all of the short names associated with this unit.
         """
         return self._short_names
 
     @property
-    def long_names(self) -> list[str]:
+    def long_names(self):
         """
         Returns all of the long names associated with this unit.
         """
@@ -1957,7 +1938,7 @@ class IrreducibleUnit(NamedUnit):
         )
 
     @property
-    def represents(self) -> Self:
+    def represents(self):
         """The unit that this named unit represents.
 
         For an irreducible unit, that is always itself.
@@ -2001,13 +1982,13 @@ class UnrecognizedUnit(IrreducibleUnit):
     # IrreducibleUnits.
     __reduce__ = object.__reduce__
 
-    def __repr__(self) -> str:
+    def __repr__(self):
         return f"UnrecognizedUnit({self})"
 
-    def __bytes__(self) -> bytes:
+    def __bytes__(self):
         return self.name.encode("ascii", "replace")
 
-    def __str__(self) -> str:
+    def __str__(self):
         return self.name
 
     def to_string(self, format=None):
@@ -2047,7 +2028,7 @@ class UnrecognizedUnit(IrreducibleUnit):
     def _get_format_name(self, format: str) -> str:
         return self.name
 
-    def is_unity(self) -> Literal[False]:
+    def is_unity(self):
         return False
 
 
@@ -2114,17 +2095,13 @@ class _UnitMetaClass(type):
                 # Return the NULL unit
                 return dimensionless_unscaled
 
+            if format is None:
+                format = unit_format.Generic
+
             f = unit_format.get_format(format)
             if isinstance(s, bytes):
                 s = s.decode("ascii")
 
-            try:
-                return f._parse_unit(s, detailed_exception=False)  # Try a shortcut
-            except (AttributeError, ValueError):
-                # No `f._parse_unit()` (AttributeError)
-                # or `s` was a composite unit (ValueError).
-                pass
-
             try:
                 return f.parse(s)
             except NotImplementedError:
@@ -2273,7 +2250,7 @@ class Unit(NamedUnit, metaclass=_UnitMetaClass):
     def decompose(self, bases=set()):
         return self._represents.decompose(bases=bases)
 
-    def is_unity(self) -> bool:
+    def is_unity(self):
         return self._represents.is_unity()
 
     @cached_property
@@ -2385,7 +2362,7 @@ class CompositeUnit(UnitBase):
             self._powers = powers
             self._expand_and_gather(decompose=decompose, bases=decompose_bases)
 
-    def __repr__(self) -> str:
+    def __repr__(self):
         if len(self._bases):
             return super().__repr__()
         else:
@@ -2395,21 +2372,21 @@ class CompositeUnit(UnitBase):
                 return "Unit(dimensionless)"
 
     @property
-    def scale(self) -> UnitScale:
+    def scale(self):
         """
         Return the scale of the composite unit.
         """
         return self._scale
 
     @property
-    def bases(self) -> list[UnitBase]:
+    def bases(self):
         """
         Return the bases of the composite unit.
         """
         return self._bases
 
     @property
-    def powers(self) -> list[UnitPower]:
+    def powers(self):
         """
         Return the powers of the composite unit.
         """
@@ -2456,7 +2433,7 @@ class CompositeUnit(UnitBase):
         self._powers = [sanitize_power(x[1]) for x in new_parts]
         self._scale = sanitize_scale_value(scale)
 
-    def __copy__(self) -> CompositeUnit:
+    def __copy__(self):
         """
         For compatibility with python copy module.
         """
@@ -2483,12 +2460,12 @@ class CompositeUnit(UnitBase):
             self._decomposed_cache = x
         return x
 
-    def is_unity(self) -> bool:
+    def is_unity(self):
         unit = self.decompose()
         return len(unit.bases) == 0 and unit.scale == 1.0
 
 
-si_prefixes: Final[list[tuple[list[str], list[str], float]]] = [
+si_prefixes = [
     (["Q"], ["quetta"], 1e30),
     (["R"], ["ronna"], 1e27),
     (["Y"], ["yotta"], 1e24),
@@ -2516,7 +2493,7 @@ si_prefixes: Final[list[tuple[list[str], list[str], float]]] = [
 ]
 
 
-binary_prefixes: Final[list[tuple[list[str], list[str], int]]] = [
+binary_prefixes = [
     (["Ki"], ["kibi"], 2**10),
     (["Mi"], ["mebi"], 2**20),
     (["Gi"], ["gibi"], 2**30),
@@ -2708,8 +2685,6 @@ def unit_scale_converter(val):
     return 1.0 * _condition_arg(val)
 
 
-dimensionless_unscaled: Final[CompositeUnit] = CompositeUnit(
-    1, [], [], _error_check=False
-)
+dimensionless_unscaled = CompositeUnit(1, [], [], _error_check=False)
 # Abbreviation of the above, see #1980
-one: Final[CompositeUnit] = dimensionless_unscaled
+one = dimensionless_unscaled
diff --git a/astropy/units/format/generic.py b/astropy/units/format/generic.py
index 9156a0dffe..32cc096f5c 100644
--- a/astropy/units/format/generic.py
+++ b/astropy/units/format/generic.py
@@ -572,12 +572,17 @@ class Generic(Base):
     @classmethod
     def _do_parse(cls, s: str, debug: bool = False) -> UnitBase:
         try:
-            return cls._parser.parse(s, lexer=cls._lexer, debug=debug)
+            # This is a short circuit for the case where the string
+            # is just a single unit name
+            return cls._parse_unit(s, detailed_exception=False)
         except ValueError as e:
-            if str(e):
-                raise
-            else:
-                raise ValueError(f"Syntax error parsing unit '{s}'")
+            try:
+                return cls._parser.parse(s, lexer=cls._lexer, debug=debug)
+            except ValueError as e:
+                if str(e):
+                    raise
+                else:
+                    raise ValueError(f"Syntax error parsing unit '{s}'")
 
     @classmethod
     def _get_unit_name(cls, unit: NamedUnit) -> str:
diff --git a/astropy/utils/iers/iers.py b/astropy/utils/iers/iers.py
index 4ba0fc6466..c5c86f0248 100644
--- a/astropy/utils/iers/iers.py
+++ b/astropy/utils/iers/iers.py
@@ -449,6 +449,9 @@ class IERS(QTable):
         if is_scalar:
             mjd = np.array([mjd])
             utc = np.array([utc])
+        elif mjd.size == 0:
+            # Short-cut empty input.
+            return np.array([])
 
         self._refresh_table_as_needed(mjd)
 
@@ -496,8 +499,7 @@ class IERS(QTable):
             results.append(status)
             return results
         else:
-            # Pass in initial to np.max to allow things to work for empty mjd.
-            self._check_interpolate_indices(i1, i, np.max(mjd, initial=50000))
+            self._check_interpolate_indices(i1, i, np.max(mjd))
             return results[0] if len(results) == 1 else results
 
     def _refresh_table_as_needed(self, mjd):
@@ -883,8 +885,7 @@ class IERS_Auto(IERS_A):
           In other words the IERS-A table was created by IERS long enough
           ago that it can be considered stale for predictions.
         """
-        # Pass in initial to np.max to allow things to work for empty mjd.
-        max_input_mjd = np.max(mjd, initial=50000)
+        max_input_mjd = np.max(mjd)
         now_mjd = self.time_now.mjd
 
         # IERS-A table contains predictive data out for a year after
diff --git a/docs/changes/coordinates/17013.bugfix.rst b/docs/changes/coordinates/17013.bugfix.rst
deleted file mode 100644
index 21b7292f36..0000000000
--- 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/io.fits/17097.feature.rst b/docs/changes/io.fits/17097.feature.rst
deleted file mode 100644
index 55da934863..0000000000
--- 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
new file mode 100644
index 0000000000..cff2187839
--- /dev/null
+++ b/docs/changes/io.fits/17099.bugfix.rst
@@ -0,0 +1,1 @@
+Fix a spurious exception when reading integer compressed images with blanks.
diff --git a/docs/changes/stats/17116.api.rst b/docs/changes/stats/17116.api.rst
deleted file mode 100644
index 0dcb3e59b7..0000000000
--- 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/table/17105.bugfix.rst b/docs/changes/table/17105.bugfix.rst
deleted file mode 100644
index 419e388dfc..0000000000
--- 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/17004.perf.rst b/docs/changes/units/17004.perf.rst
deleted file mode 100644
index 313adda814..0000000000
--- 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/utils/17013.bugfix.rst b/docs/changes/utils/17013.bugfix.rst
deleted file mode 100644
index 433013d88c..0000000000
--- 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/impact_health.rst b/docs/impact_health.rst
deleted file mode 100644
index 02f476744c..0000000000
--- a/docs/impact_health.rst
+++ /dev/null
@@ -1,71 +0,0 @@
-#################
-Impact and Health
-#################
-
-The figures on this page give a sense of the level of impact the ``astropy``
-codebase has on the astronomy community and research in the field, as well as
-the amount of development effort contributed over time (the codebase's "health").
-Assessments of The Astropy Project's engagement with the diverse astronomy
-community are an equally important but separate consideration and are detailed
-in:
-
-- `Astropy community engagement study <https://zenodo.org/records/10603049>`_
-
-- `Astropy diversity, equity and inclusion study <https://astropy-dei.orgmycology.com/>`_
-
-Concerning the codebase, a major positive trend is that citations to ``astropy``,
-as a proxy for its usage in research, have been consistently growing since The
-Astropy Project was created. However, the amount of developer resources has
-remained small, with a limited number of volunteer maintainers met with an
-increasing amount of development responsibility. Hence, we welcome any help
-that you can give!
-
-First considering the citations, this figure estimates ``astropy``'s usage in
-astronomy research by its yearly publication impact, using citations drawn from
-the NASA ADS database. The continual growth in citations shows the broad and
-increasing usage of ``astropy`` in astronomy and scientific research, as well as
-the high impact that contributions to ``astropy`` can have.
-
-|Citation figure|
-
-To next visualize the size of the developer community contributing to the
-``astropy`` core library, this figure shows the number of people authoring commits
-over time. While each year ``astropy`` is cited more in papers, the amount of
-developers and especially those contributing multiple times is modest and
-largely static.
-
-|Commits figure|
-
-The workload to maintain ``astropy`` can be traced through the number of issues and
-pull requests open and closed in the ``astropy`` core library over time. The
-long-term increase in overall pull requests, along with a comparable amount of
-opens and closes on average, shows that the small number of maintainers is
-increasingly taxed. The discrepancy between issue opens and closes over time
-reinforces this.
-
-|Issue PR history figure|
-
-In short, ``astropy`` would greatly benefit from more developers, whose
-contributions would reach a significant fraction of research in astronomy. This
-figure shows the number of open issues and pull requests for each subpackage in
-``astropy``. In addition to indicating which functionalities are used more heavily
-by the community at present, it gives a sense of where you could start if
-you're interested in contributing to ``astropy``.
-
-|Open issue PR figure|
-
-.. |Citation figure| image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_citations.png?raw=true
-  :width: 800
-  :alt: Astropy citations
-
-.. |Commits figure| image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_authors.png?raw=true
-  :width: 800
-  :alt: Astropy commit author history
-
-.. |Issue PR history figure| image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_issues_PRs.png?raw=true
-  :width: 800
-  :alt: Astropy issue and pull request history
-
-.. |Open issue PR figure| image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_open_items.png?raw=true
-  :width: 800
-  :alt: Astropy open issues and pull requests
diff --git a/docs/index.rst b/docs/index.rst
index 7416c9fc03..094eb43c43 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -118,15 +118,5 @@ processing, and data analysis.
 
             To the project details
 
-.. image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_user_stats_light.png?raw=true
-    :class: only-light
-    :target: https://docs.astropy.org/en/latest/impact_health.html
-    :alt: Astropy User Statistics
-
-.. image:: https://github.com/astropy/repo_stats/blob/cache/cache/astropy_user_stats_dark.png?raw=true
-    :class: only-dark
-    :target: https://docs.astropy.org/en/latest/impact_health.html
-    :alt: Astropy User Statistics
-
 .. _feedback@astropy.org: mailto:feedback@astropy.org
 .. _affiliated packages: https://www.astropy.org/affiliated/
diff --git a/docs/index_project_details.rst b/docs/index_project_details.rst
index 83bd706a0e..76861cbab7 100644
--- a/docs/index_project_details.rst
+++ b/docs/index_project_details.rst
@@ -12,5 +12,4 @@ Project Details
    lts_policy
    known_issues
    credits
-   impact_health
    license
diff --git a/docs/whatsnew/7.0.rst b/docs/whatsnew/7.0.rst
index 0fca9529aa..b66a7571aa 100644
--- a/docs/whatsnew/7.0.rst
+++ b/docs/whatsnew/7.0.rst
@@ -129,7 +129,7 @@ Converting units on dask and other array-like objects
 =====================================================
 
 It is now possible to pass in array-like objects such as dask arrays as the
-value in ``Unit.to`` and have those arrays not be converted to Numpy arrays:
+value in ``Unit.to`` and have those arrays not be converted to Numpy arrays::
 
 .. doctest-requires:: dask
 
diff --git a/pyproject.toml b/pyproject.toml
index 1f66a52023..bb2c874bed 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.8.27.10.28.29",
     "PyYAML>=6.0.0",
     "packaging>=22.0.0",
 ]


Test patch:
diff --git a/astropy/coordinates/tests/test_regression.py b/astropy/coordinates/tests/test_regression.py
index f046af7ea1..5c94e3aae9 100644
--- a/astropy/coordinates/tests/test_regression.py
+++ b/astropy/coordinates/tests/test_regression.py
@@ -787,14 +787,3 @@ def test_regression_16998(coord_cls, differential_type, diff_kwargs, extra_kwarg
     if coord_cls is SkyCoord:
         expected_entries.add("frame")
     assert set(coord.info._represent_as_dict()) == expected_entries
-
-
-def test_regression_17008():
-    """Test that one can transform a SkyCoord with empty data to other frames.
-
-    The underlying bug that caused the problem reported in gh-17008
-    is tested in utils/iers/tests/test_iers.py::test_empty_mjd
-    """
-    s = SkyCoord([] * u.deg, [] * u.deg, obstime=Time([], format="iso"))
-    itrs = s.itrs  # This failed before.
-    assert itrs.size == 0
diff --git a/astropy/io/fits/hdu/compressed/tests/test_tiled_compression.py b/astropy/io/fits/hdu/compressed/tests/test_tiled_compression.py
index 9d79ec3a35..4985ca1df0 100644
--- a/astropy/io/fits/hdu/compressed/tests/test_tiled_compression.py
+++ b/astropy/io/fits/hdu/compressed/tests/test_tiled_compression.py
@@ -8,6 +8,7 @@ from numpy.testing import assert_allclose, assert_equal
 from astropy.io import fits
 from astropy.io.fits.hdu.compressed._codecs import PLIO1
 from astropy.io.fits.hdu.compressed._compression import CfitsioException
+from astropy.utils.exceptions import AstropyUserWarning
 
 from .conftest import fitsio_param_to_astropy_param
 
@@ -65,6 +66,97 @@ def test_zblank_support(canonical_data_base_path, tmp_path):
         assert_equal(np.round(hdul[1].data), reference)
 
 
+def test_integer_blank_support(canonical_data_base_path, tmp_path):
+    # This uses a test 64x64 section of the m13 image to which three NaN values are added.
+    # It is converted to an integer image with blank values and compressed.
+    # Then 3 variations of the BLANK/ZBLANK specification are created by
+    # editing the header and compressed data table.
+    # When these are read in, the result is a float image that should be the same
+    # as the original (including NaNs for blanks).
+
+    with fits.open(canonical_data_base_path / "m13.fits") as hdul:
+        # pick a 64x64 pixel subset
+        data = hdul[0].data[116:180, 116:180]
+        header = hdul[0].header.copy()
+        del header["CHECKSUM"]
+        del header["DATASUM"]
+
+    # float version of image with NaNs as blanks
+    reference = data.astype(np.float32)
+    reference[[1, 2, 3], [1, 2, 3]] = np.nan
+
+    # set blanks in the int16 image and BLANK keyword in header
+    # choose a blank value that differs from the default
+    blank = -16384
+    data[np.isnan(reference)] = blank
+    header["BLANK"] = blank
+
+    # create the compressed file
+    cfile1 = tmp_path / "compressed_with_blank.fits"
+    hdu = fits.CompImageHDU(
+        data=data, header=header, compression_type="RICE_1", tile_shape=(16, 16)
+    )
+    hdu.writeto(cfile1, overwrite=True, checksum=False)
+
+    # replace BLANK keyword in header with ZBLANK keyword
+    cfile2 = tmp_path / "compressed_with_zblank.fits"
+    with fits.open(cfile1, disable_image_compression=True) as hdul:
+        assert "ZBLANK" not in hdul[1].header
+        hdul[1].header["ZBLANK"] = hdul[1].header["BLANK"]
+        del hdul[1].header["BLANK"]
+        hdul.writeto(cfile2, overwrite=True, checksum=False)
+
+    # replace ZBLANK in header with with ZBLANK in table column
+    # This creates a file structure that is unlikely to be encountered in the wild
+    # but that is apparently allowed by the FITS standard.
+    # Two versions are created, one without the BLANK keyword and one with it.
+    cfile3 = tmp_path / "compressed_with_zblank_column.fits"
+    cfile4 = tmp_path / "compressed_with_zblank_column_and_blank.fits"
+    with fits.open(cfile2, disable_image_compression=True) as hdul:
+        phdu = hdul[0]
+        thdu = hdul[1]
+        orig_table = hdul[1].data
+        orig_cols = orig_table.columns
+        zblank = thdu.header["ZBLANK"]
+        new_cols = fits.ColDefs(
+            [
+                fits.Column(
+                    name="COMPRESSED_DATA",
+                    format="1PB()",
+                    array=thdu.data.field("COMPRESSED_DATA"),
+                ),
+                fits.Column(
+                    name="ZBLANK",
+                    format="I",
+                    array=np.zeros(len(orig_table), dtype=np.int32) + zblank,
+                ),
+            ]
+        )
+        new_thdu = fits.BinTableHDU.from_columns(new_cols, header=thdu.header)
+        del new_thdu.header["ZBLANK"]
+        new_hdul = fits.HDUList([phdu, new_thdu])
+        new_hdul.writeto(cfile3, overwrite=True, checksum=False)
+        new_thdu.header["BLANK"] = blank
+        new_hdul = fits.HDUList([phdu, new_thdu])
+        new_hdul.writeto(cfile4, overwrite=True, checksum=False)
+
+    # now test the 4 files to confirm they all uncompress correctly
+    for filename in (cfile1, cfile2, cfile4):
+        with fits.open(canonical_data_base_path / filename) as hdul:
+            assert_equal(hdul[1].data, reference)
+            # ensure that the uncompressed header is created with BLANK keyword
+            assert hdul[1].header.get("BLANK") == blank
+
+    # this one generates an expected warning
+    with pytest.warns(
+        AstropyUserWarning,
+        match="Setting default value -32768 for missing BLANK keyword in compressed extension",
+    ):
+        with fits.open(cfile3) as hdul:
+            assert_equal(hdul[1].data, reference)
+            assert hdul[1].header.get("BLANK") == -32768
+
+
 @pytest.mark.parametrize(
     ("shape", "tile_shape"),
     (
diff --git a/astropy/io/fits/tests/test_fitsdiff.py b/astropy/io/fits/tests/test_fitsdiff.py
index 1f683352b5..152392da78 100644
--- a/astropy/io/fits/tests/test_fitsdiff.py
+++ b/astropy/io/fits/tests/test_fitsdiff.py
@@ -77,32 +77,28 @@ class TestFITSDiff_script(FitsTestCase):
         numdiff = fitsdiff.main([tmp_a, tmp_b])
         out, err = capsys.readouterr()
         assert numdiff == 1
-        assert out.splitlines()[-6:] == [
+        assert out.splitlines()[-4:] == [
             "        a> 9",
             "        b> 10",
             "     ...",
             "     100 different pixels found (100.00% different).",
-            "     Maximum relative difference: 1.0",
-            "     Maximum absolute difference: 1.0",
         ]
 
         numdiff = fitsdiff.main(["-n", "1", tmp_a, tmp_b])
         out, err = capsys.readouterr()
         assert numdiff == 1
-        assert out.splitlines()[-6:] == [
+        assert out.splitlines()[-4:] == [
             "        a> 0",
             "        b> 1",
             "     ...",
             "     100 different pixels found (100.00% different).",
-            "     Maximum relative difference: 1.0",
-            "     Maximum absolute difference: 1.0",
         ]
 
     def test_outputfile(self):
         a = np.arange(100).reshape(10, 10)
         hdu_a = PrimaryHDU(data=a)
         b = a.copy()
-        b[1, 0] = 20
+        b[1, 0] = 12
         hdu_b = PrimaryHDU(data=b)
         tmp_a = self.temp("testa.fits")
         tmp_b = self.temp("testb.fits")
@@ -113,13 +109,11 @@ class TestFITSDiff_script(FitsTestCase):
         assert numdiff == 1
         with open(self.temp("diff.txt")) as f:
             out = f.read()
-        assert out.splitlines()[-6:] == [
+        assert out.splitlines()[-4:] == [
             "     Data differs at [1, 2]:",
             "        a> 10",
-            "        b> 20",
+            "        b> 12",
             "     1 different pixels found (1.00% different).",
-            "     Maximum relative difference: 0.5",
-            "     Maximum absolute difference: 10.0",
         ]
 
     def test_atol(self):
@@ -156,7 +150,7 @@ class TestFITSDiff_script(FitsTestCase):
         a = np.arange(100, dtype=float).reshape(10, 10)
         hdu_a = PrimaryHDU(data=a)
         b = a.copy()
-        b[1, 0] = 20
+        b[1, 0] = 11
         hdu_b = PrimaryHDU(data=b)
         tmp_a = self.temp("testa.fits")
         tmp_b = self.temp("testb.fits")
@@ -179,12 +173,10 @@ Primary HDU:
    Data contains differences:
      Data differs at [1, 2]:
         a> 10.0
-         ? ^
-        b> 20.0
-         ? ^
+         ?  ^
+        b> 11.0
+         ?  ^
      1 different pixels found (1.00% different).
-     Maximum relative difference: 0.5
-     Maximum absolute difference: 10.0
 """
         )
         assert err == ""
diff --git a/astropy/stats/tests/test_sigma_clipping.py b/astropy/stats/tests/test_sigma_clipping.py
index 774cd31d93..077dd8dc26 100644
--- a/astropy/stats/tests/test_sigma_clipping.py
+++ b/astropy/stats/tests/test_sigma_clipping.py
@@ -515,11 +515,6 @@ def test_sigma_clip_dtypes(dtype):
     arr2 = sigma_clip(arr, cenfunc=np.mean, axis=0, masked=False)
     assert arr2.dtype == arr.dtype
 
-    # Check that int dtype is converted to float32, not float
-    arr = np.ones((10, 10), dtype=int)
-    arr2 = sigma_clip(arr, axis=0, masked=False)
-    assert arr2.dtype == np.float32
-
 
 def test_mad_std():
     # Check with a small array where we know how the result should differ from std
diff --git a/astropy/table/tests/test_table.py b/astropy/table/tests/test_table.py
index b599f4d702..02b19764ab 100644
--- a/astropy/table/tests/test_table.py
+++ b/astropy/table/tests/test_table.py
@@ -3541,18 +3541,3 @@ def test_qtable_with_explicit_units():
     # astropy/units/tests/test_units.py::test_comparison_dimensionless_with_np_ma_masked
     tt = QTable(data=[[1.0, 2.0, 3.0]], names=["weight"], units={"weight": u.one})
     assert tt["weight"].unit == u.dimensionless_unscaled
-
-
-@pytest.mark.parametrize("empty_table", [True, False])
-def test_table_replace_column_with_scalar(empty_table):
-    # Regression test for bug mentioned in
-    # https://github.com/astropy/astropy/pull/17102#issuecomment-2386963846
-    t = QTable() if empty_table else QTable([[5, 6, 7]], names=["0"])
-    t["a"] = np.arange(3.0)
-    t["a"] = 5.0
-    assert len(t) == 3
-    assert t["a"].shape == (3,)
-    assert np.all(t["a"] == 5.0)
-    # Direct replacement should never work.
-    with pytest.raises(ValueError, match="cannot replace.*with a scalar"):
-        t.replace_column("a", 2)
diff --git a/astropy/utils/iers/tests/test_iers.py b/astropy/utils/iers/tests/test_iers.py
index 7bbe06f253..fd32aaa5da 100644
--- a/astropy/utils/iers/tests/test_iers.py
+++ b/astropy/utils/iers/tests/test_iers.py
@@ -369,22 +369,6 @@ class TestIERS_Auto:
             assert dat["MJD"][-1] == (57539.0 + 60) * u.d
 
 
-@pytest.mark.parametrize("query", ["ut1_utc", "pm_xy"])
-@pytest.mark.parametrize("jd", [np.array([]), Time([], format="mjd")])
-@pytest.mark.parametrize("return_status", [False, True])
-def test_empty_mjd(query, jd, return_status):
-    # Regression test for gh-17008
-    iers_table = iers.IERS_Auto.open()
-    result = getattr(iers_table, query)(jd, return_status=return_status)
-    n_exp = (1 if query == "ut1_utc" else 2) + (1 if return_status else 0)
-    if n_exp == 1:
-        assert isinstance(result, np.ndarray)
-        assert result.size == 0
-    else:
-        assert len(result) == n_exp
-        assert all(r.size == 0 for r in result)
-
-
 def test_IERS_B_parameters_loading_into_IERS_Auto():
     # Make sure that auto downloading is off
     with iers.conf.set_temp("auto_download", False):