Repository: astropy/astropy

Issue:
`world_to_array_index` misbehaves if out of bounds because of casting `NaN` to int
### Description

#16328 exposed an issue with `world_to_array_index`:

```python
from astropy.wcs import WCS

wcs = WCS(naxis=2)
wcs.wcs.crpix = (1, 1)
wcs.wcs.set()

wcs.pixel_bounds = [(-0.5, 3.5), None]

print(wcs.world_to_pixel(*wcs.pixel_to_world(5, 5)))
```
```
(array(nan), array(5.))
```
```python
print(wcs.world_to_array_index(*wcs.pixel_to_world(5, 5)))
```
```
[...]/astropy/wcs/wcsapi/high_level_api.py:49: RuntimeWarning: invalid value encountered in cast
  indx = np.asarray(np.floor(np.asarray(value) + 0.5), dtype=int)
(5, -9223372036854775808)
```

### Expected behavior

_No response_

### How to Reproduce

_No response_

### Versions

astropy main

Discussion:

Patch:
diff --git a/astropy/cosmology/_io/cosmology.py b/astropy/cosmology/_io/cosmology.py
index a92bb105a9..ef64b96523 100644
--- a/astropy/cosmology/_io/cosmology.py
+++ b/astropy/cosmology/_io/cosmology.py
@@ -13,34 +13,24 @@ are present mainly for completeness and testing.
     True
 """
 
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
 from astropy.cosmology.connect import convert_registry
 from astropy.cosmology.core import _COSMOLOGY_CLASSES, Cosmology
 
-if TYPE_CHECKING:
-    from astropy.cosmology._typing import _CosmoT
-
-__all__: list[str] = []  # nothing is publicly scoped
+__all__ = []  # nothing is publicly scoped
 
 
-def from_cosmology(
-    cosmo: _CosmoT, /, cosmology: type[_CosmoT] | str | None = None, **kwargs: object
-) -> _CosmoT:
+def from_cosmology(cosmo, /, **kwargs):
     """Return the |Cosmology| unchanged.
 
     Parameters
     ----------
     cosmo : `~astropy.cosmology.Cosmology`
         The cosmology to return.
-    cosmology : type[`~astropy.cosmology.Cosmology`] | str | None, optional
-        The |Cosmology| class to check against. If not `None`, ``cosmo`` is checked
-        for correctness.
     **kwargs
         This argument is required for compatibility with the standard set of
-        keyword arguments in format |Cosmology.from_format|.
+        keyword arguments in format `~astropy.cosmology.Cosmology.from_format`,
+        e.g. "cosmology". If "cosmology" is included and is not `None`,
+        ``cosmo`` is checked for correctness.
 
     Returns
     -------
@@ -60,27 +50,24 @@ def from_cosmology(
     FlatLambdaCDM(name="Planck18", H0=67.66 km / (Mpc s), Om0=0.30966,
                   Tcmb0=2.7255 K, Neff=3.046, m_nu=[0. 0. 0.06] eV, Ob0=0.04897)
     """
-    # hot path
-    if cosmology is None:
-        return cosmo
-
-    cosmo_cls = (
-        _COSMOLOGY_CLASSES[cosmology] if isinstance(cosmology, str) else cosmology
-    )
-    if not isinstance(cosmo, cosmo_cls):
-        raise TypeError(f"cosmology {cosmo} is not an {cosmo_cls} instance.")
+    # Check argument `cosmology`
+    cosmology = kwargs.get("cosmology")
+    if isinstance(cosmology, str):
+        cosmology = _COSMOLOGY_CLASSES[cosmology]
+    if cosmology is not None and not isinstance(cosmo, cosmology):
+        raise TypeError(f"cosmology {cosmo} is not an {cosmology} instance.")
 
     return cosmo
 
 
-def to_cosmology(cosmo: _CosmoT, *args: object) -> _CosmoT:
+def to_cosmology(cosmo, *args):
     """Return the |Cosmology| unchanged.
 
     Parameters
     ----------
     cosmo : `~astropy.cosmology.Cosmology`
         The cosmology to return.
-    *args : object
+    *args
         Not used.
 
     Returns
@@ -97,14 +84,9 @@ def to_cosmology(cosmo: _CosmoT, *args: object) -> _CosmoT:
     return cosmo
 
 
-def cosmology_identify(
-    origin: str, format: str | None, *args: object, **kwargs: object
-) -> bool:
+def cosmology_identify(origin, format, *args, **kwargs):
     """Identify if object is a `~astropy.cosmology.Cosmology`.
 
-    This checks if the 2nd argument is a |Cosmology| instance and the format is
-    "astropy.cosmology" or `None`.
-
     Returns
     -------
     bool
diff --git a/astropy/cosmology/_io/ecsv.py b/astropy/cosmology/_io/ecsv.py
index 6aac8dfe3c..69526d9917 100644
--- a/astropy/cosmology/_io/ecsv.py
+++ b/astropy/cosmology/_io/ecsv.py
@@ -159,10 +159,6 @@ Additional keyword arguments are passed to ``QTable.read`` and ``QTable.write``.
     >>> temp_dir.cleanup()
 """
 
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, TypeVar
-
 import astropy.cosmology.units as cu
 import astropy.units as u
 from astropy.cosmology.connect import readwrite_registry
@@ -171,25 +167,10 @@ from astropy.table import QTable
 
 from .table import from_table, to_table
 
-if TYPE_CHECKING:
-    from collections.abc import Mapping
-
-    from astropy.cosmology._typing import _CosmoT
-    from astropy.io.typing import PathLike, ReadableFileLike, WriteableFileLike
-    from astropy.table import Table
-
-    _TableT = TypeVar("_TableT", "Table")
-
 
 def read_ecsv(
-    filename: PathLike | ReadableFileLike[Table],
-    index: int | str | None = None,
-    *,
-    move_to_meta: bool = False,
-    cosmology: str | type[_CosmoT] | None = None,
-    rename: Mapping[str, str] | None = None,
-    **kwargs: Any,
-) -> _CosmoT:
+    filename, index=None, *, move_to_meta=False, cosmology=None, rename=None, **kwargs
+):
     r"""Read a `~astropy.cosmology.Cosmology` from an ECSV file.
 
     Parameters
@@ -210,14 +191,9 @@ def read_ecsv(
         the case of a merge conflict (e.g. for ``Cosmology(meta={'key':10}, key=42)``,
         the ``Cosmology.meta`` will be ``{'key': 10}``).
 
-    cosmology : str or type or None (optional, keyword-only)
-        The cosmology class (or string name thereof) to use when constructing the
-        cosmology instance. The class also provides default parameter values, filling in
-        any non-mandatory arguments missing in 'table'.
-
-    rename : dict or None (optional, keyword-only)
-        A dictionary mapping column names in 'table' to fields of the
-        `~astropy.cosmology.Cosmology` class.
+    rename : dict or None (optional keyword-only)
+        A dictionary mapping column names to fields of the
+        `~astropy.cosmology.Cosmology`.
 
     **kwargs
         Passed to ``QTable.read``
@@ -360,15 +336,15 @@ def read_ecsv(
 
 
 def write_ecsv(
-    cosmology: Cosmology,
-    file: PathLike | WriteableFileLike[_TableT],
+    cosmology,
+    file,
     *,
-    overwrite: bool = False,
-    cls: type[_TableT] = QTable,
-    cosmology_in_meta: bool = True,
-    rename: Mapping[str, str] | None = None,
-    **kwargs: Any,
-) -> None:
+    overwrite=False,
+    cls=QTable,
+    cosmology_in_meta=True,
+    rename=None,
+    **kwargs,
+):
     """Serialize the cosmology into a ECSV.
 
     Parameters
@@ -378,7 +354,7 @@ def write_ecsv(
     file : path-like or file-like
         Location to save the serialized cosmology.
 
-    overwrite : bool (optional, keyword-only)
+    overwrite : bool
         Whether to overwrite the file, if it exists.
     cls : type (optional, keyword-only)
         Astropy :class:`~astropy.table.Table` (sub)class to use when writing. Default is
@@ -386,9 +362,9 @@ def write_ecsv(
     cosmology_in_meta : bool (optional, keyword-only)
         Whether to put the cosmology class in the Table metadata (if `True`, default) or
         as the first column (if `False`).
-    rename : Mapping[str, str] or None (optional keyword-only)
-        A mapping of field names on the `~astropy.cosmology.Cosmology` to column names
-        of the table.
+    rename : dict or None (optional keyword-only)
+        A dictionary mapping fields of the `~astropy.cosmology.Cosmology` to columns of
+        the table.
 
     **kwargs
         Passed to ``cls.write``
@@ -497,9 +473,7 @@ def write_ecsv(
     table.write(file, overwrite=overwrite, **kwargs)
 
 
-def ecsv_identify(
-    origin: object, filepath: str | None, *args: object, **kwargs: object
-) -> bool:
+def ecsv_identify(origin, filepath, fileobj, *args, **kwargs):
     """Identify if object uses the Table format.
 
     Returns
diff --git a/astropy/cosmology/_io/html.py b/astropy/cosmology/_io/html.py
index a85cff42f4..6016e8bb14 100644
--- a/astropy/cosmology/_io/html.py
+++ b/astropy/cosmology/_io/html.py
@@ -85,10 +85,6 @@ enable this, set ``latex_names=True``.
     >>> temp_dir.cleanup()
 """
 
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, TypeVar
-
 import astropy.cosmology.units as cu
 import astropy.units as u
 from astropy.cosmology.connect import readwrite_registry
@@ -98,13 +94,6 @@ from astropy.table import QTable
 
 from .table import from_table, to_table
 
-if TYPE_CHECKING:
-    from astropy.cosmology._typing import _CosmoT
-    from astropy.io.typing import PathLike, ReadableFileLike, WriteableFileLike
-    from astropy.table import Table
-
-    _TableT = TypeVar("_TableT", "Table")
-
 # Format look-up for conversion, {original_name: new_name}
 # TODO! move this information into the Parameters themselves
 _FORMAT_TABLE = {
@@ -124,14 +113,14 @@ _FORMAT_TABLE = {
 
 
 def read_html_table(
-    filename: PathLike | ReadableFileLike[Table],
-    index: int | str | None = None,
+    filename,
+    index=None,
     *,
-    move_to_meta: bool = False,
-    cosmology: str | type[_CosmoT] | None = None,
-    latex_names: bool = True,
-    **kwargs: Any,
-) -> _CosmoT:
+    move_to_meta=False,
+    cosmology=None,
+    latex_names=True,
+    **kwargs,
+):
     r"""Read a |Cosmology| from an HTML file.
 
     Parameters
@@ -199,20 +188,14 @@ def read_html_table(
 
 
 def write_html_table(
-    cosmology: Cosmology,
-    file: PathLike | WriteableFileLike[_TableT],
-    *,
-    overwrite: bool = False,
-    cls: type[_TableT] = QTable,
-    latex_names: bool = False,
-    **kwargs: Any,
-) -> None:
+    cosmology, file, *, overwrite=False, cls=QTable, latex_names=False, **kwargs
+):
     r"""Serialize the |Cosmology| into a HTML table.
 
     Parameters
     ----------
-    cosmology : |Cosmology| subclass instance
-        The cosmology to serialize.
+    cosmology : |Cosmology| subclass instance file : path-like or file-like
+        Location to save the serialized cosmology.
     file : path-like or file-like
         Where to write the html table.
 
@@ -342,20 +325,20 @@ def write_html_table(
     table.write(file, overwrite=overwrite, format="ascii.html", **kwargs)
 
 
-def html_identify(
-    origin: object, filepath: object, *args: object, **kwargs: object
-) -> bool:
+def html_identify(origin, filepath, fileobj, *args, **kwargs):
     """Identify if an object uses the HTML Table format.
 
     Parameters
     ----------
-    origin : object
+    origin : Any
         Not used.
-    filepath : object
+    filepath : str or Any
         From where to read the Cosmology.
-    *args : object
+    fileobj : Any
+        Not used.
+    *args : Any
         Not used.
-    **kwargs : object
+    **kwargs : Any
         Not used.
 
     Returns
diff --git a/astropy/cosmology/_io/latex.py b/astropy/cosmology/_io/latex.py
index 42acbed4ed..70644dd156 100644
--- a/astropy/cosmology/_io/latex.py
+++ b/astropy/cosmology/_io/latex.py
@@ -54,10 +54,6 @@ By default the parameter names are converted to LaTeX format. To disable this, s
     >>> temp_dir.cleanup()
 """
 
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, Any, TypeVar
-
 import astropy.units as u
 from astropy.cosmology.connect import readwrite_registry
 from astropy.cosmology.core import Cosmology
@@ -66,12 +62,6 @@ from astropy.table import QTable
 
 from .table import to_table
 
-if TYPE_CHECKING:
-    from astropy.io.typing import PathLike, WriteableFileLike
-    from astropy.table import Table
-
-    _TableT = TypeVar("_TableT", Table)
-
 _FORMAT_TABLE = {
     "H0": "$H_0$",
     "Om0": r"$\Omega_{m,0}$",
@@ -89,14 +79,8 @@ _FORMAT_TABLE = {
 
 
 def write_latex(
-    cosmology: Cosmology,
-    file: PathLike | WriteableFileLike[_TableT],
-    *,
-    overwrite: bool = False,
-    cls: type[_TableT] = QTable,
-    latex_names: bool = True,
-    **kwargs: Any,
-) -> None:
+    cosmology, file, *, overwrite=False, cls=QTable, latex_names=True, **kwargs
+):
     r"""Serialize the |Cosmology| into a LaTeX.
 
     Parameters
@@ -200,9 +184,7 @@ def write_latex(
     table.write(file, overwrite=overwrite, format="ascii.latex", **kwargs)
 
 
-def latex_identify(
-    origin: object, filepath: str | None, *args: object, **kwargs: object
-) -> bool:
+def latex_identify(origin, filepath, fileobj, *args, **kwargs):
     """Identify if object uses the Table format.
 
     Returns
diff --git a/astropy/cosmology/_io/mapping.py b/astropy/cosmology/_io/mapping.py
index 2e09a1c8e5..c7b4844a42 100644
--- a/astropy/cosmology/_io/mapping.py
+++ b/astropy/cosmology/_io/mapping.py
@@ -134,27 +134,17 @@ Lastly, the keys in the mapping may be renamed with the ``rename`` keyword.
      'cosmo_name': 'Planck18', ...
 """
 
-from __future__ import annotations
-
-__all__: list[str] = []  # nothing is publicly scoped
+__all__ = []  # nothing is publicly scoped
 
 import copy
 import inspect
-from collections.abc import Mapping, MutableMapping
-from typing import TYPE_CHECKING, Any, TypeVar
+from collections.abc import Mapping
 
 from astropy.cosmology.connect import convert_registry
 from astropy.cosmology.core import _COSMOLOGY_CLASSES, Cosmology
 
-if TYPE_CHECKING:
-    from astropy.cosmology._typing import _CosmoT
-
-    _MapT = TypeVar("_MapT", MutableMapping[str, Any])
-
 
-def _rename_map(
-    map: Mapping[str, Any], /, renames: Mapping[str, str]
-) -> dict[str, Any]:
+def _rename_map(map, /, renames):
     """Apply rename to map."""
     if common_names := set(renames.values()).intersection(map):
         raise ValueError(
@@ -164,9 +154,7 @@ def _rename_map(
     return {renames.get(k, k): v for k, v in map.items()}  # dict separate from input
 
 
-def _get_cosmology_class(
-    cosmology: type[_CosmoT] | str | None, params: dict[str, Any], /
-) -> type[_CosmoT]:
+def _get_cosmology_class(cosmology, params, /):
     # get cosmology
     # 1st from argument. Allows for override of the cosmology, if on file.
     # 2nd from params. This MUST have the cosmology if 'kwargs' did not.
@@ -178,14 +166,7 @@ def _get_cosmology_class(
     return _COSMOLOGY_CLASSES[cosmology] if isinstance(cosmology, str) else cosmology
 
 
-def from_mapping(
-    mapping: Mapping[str, Any],
-    /,
-    *,
-    move_to_meta: bool = False,
-    cosmology: str | type[_CosmoT] | None = None,
-    rename: Mapping[str, str] | None = None,
-) -> _CosmoT:
+def from_mapping(mapping, /, *, move_to_meta=False, cosmology=None, rename=None):
     """Load `~astropy.cosmology.Cosmology` from mapping object.
 
     Parameters
@@ -203,13 +184,15 @@ def from_mapping(
         the case of a merge conflict (e.g. for ``Cosmology(meta={'key':10}, key=42)``,
         the ``Cosmology.meta`` will be ``{'key': 10}``).
 
-    cosmology : str, |Cosmology| class, or None (optional, keyword-only)
+    cosmology : str, `~astropy.cosmology.Cosmology` class, or None (optional,
+    keyword-only)
         The cosmology class (or string name thereof) to use when constructing the
         cosmology instance. The class also provides default parameter values, filling in
         any non-mandatory arguments missing in 'map'.
 
-    rename : Mapping[str, str] or None (optional, keyword-only)
-        A mapping of keys in ``map`` to fields of the `~astropy.cosmology.Cosmology`.
+    rename : dict or None (optional, keyword-only)
+        A dictionary mapping keys in ``map`` to fields of the
+        `~astropy.cosmology.Cosmology`.
 
     Returns
     -------
@@ -314,20 +297,20 @@ def from_mapping(
 
 
 def to_mapping(
-    cosmology: Cosmology,
-    *args: object,
-    cls: type[_MapT] = dict,
-    cosmology_as_str: bool = False,
-    move_from_meta: bool = False,
-    rename: Mapping[str, str] | None = None,
-) -> _MapT:
+    cosmology,
+    *args,
+    cls=dict,
+    cosmology_as_str=False,
+    move_from_meta=False,
+    rename=None,
+):
     """Return the cosmology class, parameters, and metadata as a `dict`.
 
     Parameters
     ----------
     cosmology : :class:`~astropy.cosmology.Cosmology`
         The cosmology instance to convert to a mapping.
-    *args : object
+    *args
         Not used. Needed for compatibility with
         `~astropy.io.registry.UnifiedReadWriteMethod`
     cls : type (optional, keyword-only)
@@ -340,13 +323,13 @@ def to_mapping(
         Whether to add the Cosmology's metadata as an item to the mapping (if
         `False`, default) or to merge with the rest of the mapping, preferring
         the original values (if `True`)
-    rename : Mapping[str, str] or None (optional, keyword-only)
-        A mapping of field names of the :class:`~astropy.cosmology.Cosmology` to keys in
+    rename : dict or None (optional, keyword-only)
+        A `dict` mapping fields of the :class:`~astropy.cosmology.Cosmology` to keys in
         the map.
 
     Returns
     -------
-    MutableMapping[str, Any]
+    Mapping
         A mapping of type ``cls``, by default a `dict`.
         Has key-values for the cosmology parameters and also:
         - 'cosmology' : the class
@@ -430,9 +413,7 @@ def to_mapping(
     return m if rename is None else _rename_map(m, rename)
 
 
-def mapping_identify(
-    origin: str, format: str | None, *args: object, **kwargs: object
-) -> bool:
+def mapping_identify(origin, format, *args, **kwargs):
     """Identify if object uses the mapping format.
 
     Returns
diff --git a/astropy/cosmology/_io/model.py b/astropy/cosmology/_io/model.py
index 21632d86ab..27422c203c 100644
--- a/astropy/cosmology/_io/model.py
+++ b/astropy/cosmology/_io/model.py
@@ -27,17 +27,13 @@ The |Planck18| cosmology can be recovered with |Cosmology.from_format|.
                   Tcmb0=2.7255 K, Neff=3.046, m_nu=[0. 0. 0.06] eV, Ob0=0.04897)
 """
 
-from __future__ import annotations
-
 import abc
 import copy
 import inspect
 from dataclasses import replace
-from typing import Generic
 
 import numpy as np
 
-from astropy.cosmology._typing import _CosmoT
 from astropy.cosmology.connect import convert_registry
 from astropy.cosmology.core import Cosmology
 from astropy.modeling import FittableModel, Model
@@ -45,10 +41,10 @@ from astropy.utils.decorators import classproperty
 
 from .utils import convert_parameter_to_model_parameter
 
-__all__: list[str] = []  # nothing is publicly scoped
+__all__ = []  # nothing is publicly scoped
 
 
-class _CosmologyModel(FittableModel, Generic[_CosmoT]):
+class _CosmologyModel(FittableModel):
     """Base class for Cosmology redshift-method Models.
 
     .. note::
@@ -69,21 +65,21 @@ class _CosmologyModel(FittableModel, Generic[_CosmoT]):
     """
 
     @abc.abstractmethod
-    def _cosmology_class(self) -> type[_CosmoT]:
+    def _cosmology_class(self):
         """Cosmology class as a private attribute.
 
         Set in subclasses.
         """
 
     @abc.abstractmethod
-    def _method_name(self) -> str:
+    def _method_name(self):
         """Cosmology method name as a private attribute.
 
         Set in subclasses.
         """
 
     @classproperty
-    def cosmology_class(cls) -> type[_CosmoT]:
+    def cosmology_class(cls):
         """|Cosmology| class."""
         return cls._cosmology_class
 
@@ -93,7 +89,7 @@ class _CosmologyModel(FittableModel, Generic[_CosmoT]):
         return inspect.signature(cls._cosmology_class)
 
     @property
-    def cosmology(self) -> _CosmoT:
+    def cosmology(self):
         """Return |Cosmology| using `~astropy.modeling.Parameter` values."""
         cosmo = self._cosmology_class(
             name=self.name,
@@ -105,13 +101,12 @@ class _CosmologyModel(FittableModel, Generic[_CosmoT]):
         return cosmo
 
     @classproperty
-    def method_name(self) -> str:
+    def method_name(self):
         """Redshift-method name on |Cosmology| instance."""
         return self._method_name
 
     # ---------------------------------------------------------------
 
-    # NOTE: cannot add type annotations b/c of how Model introspects
     def evaluate(self, *args, **kwargs):
         """Evaluate method {method!r} of {cosmo_cls!r} Cosmology.
 
@@ -167,7 +162,7 @@ class _CosmologyModel(FittableModel, Generic[_CosmoT]):
 ##############################################################################
 
 
-def from_model(model: _CosmologyModel[_CosmoT]) -> _CosmoT:
+def from_model(model):
     """Load |Cosmology| from `~astropy.modeling.Model` object.
 
     Parameters
@@ -201,7 +196,7 @@ def from_model(model: _CosmologyModel[_CosmoT]) -> _CosmoT:
     return replace(cosmo, meta=meta)
 
 
-def to_model(cosmology: _CosmoT, *_: object, method: str) -> _CosmologyModel[_CosmoT]:
+def to_model(cosmology, *_, method):
     """Convert a `~astropy.cosmology.Cosmology` to a `~astropy.modeling.Model`.
 
     Parameters
@@ -278,9 +273,7 @@ def to_model(cosmology: _CosmoT, *_: object, method: str) -> _CosmologyModel[_Co
     return model
 
 
-def model_identify(
-    origin: str, format: str | None, *args: object, **kwargs: object
-) -> bool:
+def model_identify(origin, format, *args, **kwargs):
     """Identify if object uses the :class:`~astropy.modeling.Model` format.
 
     Returns
diff --git a/astropy/cosmology/_io/row.py b/astropy/cosmology/_io/row.py
index d3ffbff28e..9bcc22905f 100644
--- a/astropy/cosmology/_io/row.py
+++ b/astropy/cosmology/_io/row.py
@@ -31,11 +31,8 @@ to the ``Planck18`` cosmology from which it was generated.
 For more information on the argument options, see :ref:`cosmology_io_builtin-table`.
 """
 
-from __future__ import annotations
-
 import copy
 from collections import defaultdict
-from typing import TYPE_CHECKING
 
 from astropy.cosmology.connect import convert_registry
 from astropy.cosmology.core import Cosmology
@@ -43,20 +40,8 @@ from astropy.table import QTable, Row
 
 from .mapping import from_mapping
 
-if TYPE_CHECKING:
-    from collections.abc import Mapping
-
-    from astropy.cosmology._typing import _CosmoT
-    from astropy.table import Table
-
 
-def from_row(
-    row: Row,
-    *,
-    move_to_meta: bool = False,
-    cosmology: str | type[_CosmoT] | None = None,
-    rename: Mapping[str, str] | None = None,
-) -> _CosmoT:
+def from_row(row, *, move_to_meta=False, cosmology=None, rename=None):
     """Instantiate a `~astropy.cosmology.Cosmology` from a `~astropy.table.Row`.
 
     Parameters
@@ -77,8 +62,8 @@ def from_row(
         the cosmology instance. The class also provides default parameter values,
         filling in any non-mandatory arguments missing in 'table'.
 
-    rename : Mapping[str, str] or None (optional, keyword-only)
-        A mapping of column names in the row to field names of the |Cosmology|.
+    rename : dict or None (optional, keyword-only)
+        A dictionary mapping columns in the row to fields of the `~astropy.cosmology.Cosmology`.
 
     Returns
     -------
@@ -178,13 +163,7 @@ def from_row(
     )
 
 
-def to_row(
-    cosmology: Cosmology,
-    *args: object,
-    cosmology_in_meta: bool = False,
-    table_cls: type[Table] = QTable,
-    rename: Mapping[str, str] | None = None,
-) -> Row:
+def to_row(cosmology, *args, cosmology_in_meta=False, table_cls=QTable, rename=None):
     """Serialize the cosmology into a `~astropy.table.Row`.
 
     Parameters
@@ -200,8 +179,6 @@ def to_row(
     cosmology_in_meta : bool
         Whether to put the cosmology class in the Table metadata (if `True`) or as the
         first column (if `False`, default).
-    rename : Mapping[str, str] or None (optional, keyword-only)
-        A mapping of field names of the |Cosmology| to column names in the row.
 
     Returns
     -------
@@ -270,9 +247,7 @@ def to_row(
     return table[0]  # extract row from table
 
 
-def row_identify(
-    origin: str, format: str | None, *args: object, **kwargs: object
-) -> bool:
+def row_identify(origin, format, *args, **kwargs):
     """Identify if object uses the `~astropy.table.Row` format.
 
     Returns
diff --git a/astropy/cosmology/_io/table.py b/astropy/cosmology/_io/table.py
index 058aa4c3e5..7b11bd348b 100644
--- a/astropy/cosmology/_io/table.py
+++ b/astropy/cosmology/_io/table.py
@@ -154,10 +154,6 @@ not match the class' parameter names.
     True
 """
 
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
 import numpy as np
 
 from astropy.cosmology.connect import convert_registry
@@ -168,23 +164,8 @@ from .mapping import to_mapping
 from .row import from_row
 from .utils import convert_parameter_to_column
 
-if TYPE_CHECKING:
-    from collections.abc import Mapping
-    from typing import TypeVar
-
-    from astropy.cosmology._typing import _CosmoT
-
-    _TableT = TypeVar("_TableT", Table)
-
 
-def from_table(
-    table: Table,
-    index: int | str | None = None,
-    *,
-    move_to_meta: bool = False,
-    cosmology: str | type[_CosmoT] | None = None,
-    rename: Mapping[str, str] | None = None,
-) -> _CosmoT:
+def from_table(table, index=None, *, move_to_meta=False, cosmology=None, rename=None):
     """Instantiate a `~astropy.cosmology.Cosmology` from a |QTable|.
 
     Parameters
@@ -210,8 +191,9 @@ def from_table(
         cosmology instance. The class also provides default parameter values, filling in
         any non-mandatory arguments missing in 'table'.
 
-    rename : Mapping[str, str] or None (optional, keyword-only)
-        A mapping of column names in 'table' to field names of the |Cosmology| class.
+    rename : dict or None (optional, keyword-only)
+        A dictionary mapping columns in 'table' to fields of the
+        `~astropy.cosmology.Cosmology` class.
 
     Returns
     -------
@@ -350,20 +332,14 @@ def from_table(
     return from_row(row, move_to_meta=move_to_meta, cosmology=cosmology, rename=rename)
 
 
-def to_table(
-    cosmology: Cosmology,
-    *args: object,
-    cls: type[_TableT] = QTable,
-    cosmology_in_meta: bool = True,
-    rename: Mapping[str, str] | None = None,
-) -> _TableT:
+def to_table(cosmology, *args, cls=QTable, cosmology_in_meta=True, rename=None):
     """Serialize the cosmology into a `~astropy.table.QTable`.
 
     Parameters
     ----------
     cosmology : `~astropy.cosmology.Cosmology`
         The cosmology instance to convert to a table.
-    *args : object
+    *args
         Not used. Needed for compatibility with
         `~astropy.io.registry.UnifiedReadWriteMethod`
     cls : type (optional, keyword-only)
@@ -372,9 +348,6 @@ def to_table(
     cosmology_in_meta : bool (optional, keyword-only)
         Whether to put the cosmology class in the Table metadata (if `True`,
         default) or as the first column (if `False`).
-    rename : Mapping[str, str] or None (optional, keyword-only)
-        A mapping of field names of the |Cosmology| class to column names in the
-        |Table|.
 
     Returns
     -------
@@ -475,9 +448,7 @@ def to_table(
     return tbl
 
 
-def table_identify(
-    origin: str, format: str | None, *args: object, **kwargs: object
-) -> bool:
+def table_identify(origin, format, *args, **kwargs):
     """Identify if object uses the Table format.
 
     Returns
diff --git a/astropy/cosmology/_io/yaml.py b/astropy/cosmology/_io/yaml.py
index e20ea9c117..dc4f17db94 100644
--- a/astropy/cosmology/_io/yaml.py
+++ b/astropy/cosmology/_io/yaml.py
@@ -17,10 +17,6 @@ require YAML serialization.
                   Tcmb0=2.7255 K, Neff=3.046, m_nu=[0. 0. 0.06] eV, Ob0=0.04897)
 """  # this is shown in the docs.
 
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
 import astropy.cosmology.units as cu
 import astropy.units as u
 from astropy.cosmology.connect import convert_registry
@@ -30,14 +26,7 @@ from astropy.io.misc.yaml import AstropyDumper, AstropyLoader, dump, load
 from .mapping import from_mapping
 from .utils import FULLQUALNAME_SUBSTITUTIONS as QNS
 
-if TYPE_CHECKING:
-    from collections.abc import Callable
-
-    from yaml import MappingNode
-
-    from astropy.cosmology._typing import _CosmoT
-
-__all__: list[str] = []  # nothing is publicly scoped
+__all__ = []  # nothing is publicly scoped
 
 
 ##############################################################################
@@ -47,23 +36,7 @@ __all__: list[str] = []  # nothing is publicly scoped
 # that calls these functions.
 
 
-_representer_doc = """Cosmology yaml representer function for {}.
-
-Parameters
-----------
-dumper : :class:`~astropy.io.misc.yaml.AstropyDumper`
-    The dumper object with which to serialize the |Cosmology| object.
-obj : :class:`~astropy.cosmology.Cosmology`
-    The |Cosmology| object to serialize.
-
-Returns
--------
-str
-    :mod:`yaml` representation of |Cosmology| object.
-"""
-
-
-def yaml_representer(tag: str) -> Callable[[AstropyDumper, Cosmology], str]:
+def yaml_representer(tag):
     """`yaml <https://yaml.org>`_ representation of |Cosmology| object.
 
     Parameters
@@ -77,7 +50,19 @@ def yaml_representer(tag: str) -> Callable[[AstropyDumper, Cosmology], str]:
         Function to construct :mod:`yaml` representation of |Cosmology| object.
     """
 
-    def representer(dumper: AstropyDumper, obj: Cosmology) -> str:
+    def representer(dumper, obj):
+        """Cosmology yaml representer function for {}.
+
+        Parameters
+        ----------
+        dumper : `~astropy.io.misc.yaml.AstropyDumper`
+        obj : `~astropy.cosmology.Cosmology`
+
+        Returns
+        -------
+        str
+            :mod:`yaml` representation of |Cosmology| object.
+        """
         # convert to mapping
         map = obj.to_format("mapping")
         # remove the cosmology class info. It's already recorded in `tag`
@@ -87,14 +72,12 @@ def yaml_representer(tag: str) -> Callable[[AstropyDumper, Cosmology], str]:
 
         return dumper.represent_mapping(tag, map)
 
-    representer.__doc__ = _representer_doc.format(tag)
+    representer.__doc__ = representer.__doc__.format(tag)
 
     return representer
 
 
-def yaml_constructor(
-    cls: type[_CosmoT],
-) -> Callable[[AstropyLoader, MappingNode], _CosmoT]:
+def yaml_constructor(cls):
     """Cosmology| object from :mod:`yaml` representation.
 
     Parameters
@@ -108,7 +91,7 @@ def yaml_constructor(
         Function to construct |Cosmology| object from :mod:`yaml` representation.
     """
 
-    def constructor(loader: AstropyLoader, node: MappingNode) -> _CosmoT:
+    def constructor(loader, node):
         """Cosmology yaml constructor function.
 
         Parameters
@@ -133,7 +116,7 @@ def yaml_constructor(
     return constructor
 
 
-def register_cosmology_yaml(cosmo_cls: type[Cosmology]) -> None:
+def register_cosmology_yaml(cosmo_cls):
     """Register :mod:`yaml` for Cosmology class.
 
     Parameters
@@ -153,14 +136,14 @@ def register_cosmology_yaml(cosmo_cls: type[Cosmology]) -> None:
 # Unified-I/O Functions
 
 
-def from_yaml(yml: str, *, cosmology: type[_CosmoT] | None = None) -> _CosmoT:
+def from_yaml(yml, *, cosmology=None):
     """Load `~astropy.cosmology.Cosmology` from :mod:`yaml` object.
 
     Parameters
     ----------
     yml : str
         :mod:`yaml` representation of |Cosmology| object
-    cosmology : str, |Cosmology| class, or None (optional, keyword-only)
+    cosmology : str, `~astropy.cosmology.Cosmology` class, or None (optional, keyword-only)
         The expected cosmology class (or string name thereof). This argument is
         is only checked for correctness if not `None`.
 
@@ -195,14 +178,14 @@ def from_yaml(yml: str, *, cosmology: type[_CosmoT] | None = None) -> _CosmoT:
     return cosmo
 
 
-def to_yaml(cosmology: Cosmology, *args: object) -> str:
+def to_yaml(cosmology, *args):
     r"""Return the cosmology class, parameters, and metadata as a :mod:`yaml` object.
 
     Parameters
     ----------
     cosmology : `~astropy.cosmology.Cosmology` subclass instance
         The cosmology to serialize.
-    *args : Any
+    *args
         Not used. Needed for compatibility with
         `~astropy.io.registry.UnifiedReadWriteMethod`
 
diff --git a/astropy/cosmology/_typing.py b/astropy/cosmology/_typing.py
deleted file mode 100644
index bcf031e006..0000000000
--- a/astropy/cosmology/_typing.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-"""Static typing for :mod:`astropy.cosmology`."""
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING, TypeVar
-
-if TYPE_CHECKING:
-    from astropy.cosmology import Cosmology
-
-_CosmoT = TypeVar("_CosmoT", bound="Cosmology")  # noqa: PYI018
-"""Type variable for :class:`~astropy.cosmology.Cosmology` and subclasses."""
diff --git a/astropy/cosmology/connect.py b/astropy/cosmology/connect.py
index 95a20c4d0b..c8fd75d12a 100644
--- a/astropy/cosmology/connect.py
+++ b/astropy/cosmology/connect.py
@@ -14,7 +14,6 @@ if TYPE_CHECKING:
 
     from astropy.cosmology import Cosmology
     from astropy.cosmology._io.model import _CosmologyModel
-    from astropy.cosmology._typing import _CosmoT
     from astropy.table import Row, Table
 
 __all__ = [
@@ -27,6 +26,7 @@ __doctest_skip__ = __all__
 
 
 # NOTE: private b/c RTD error
+_CosmoT = TypeVar("_CosmoT", bound="Cosmology")
 _MT = TypeVar("_MT", bound="Mapping")  # type: ignore[type-arg]
 
 
diff --git a/astropy/cosmology/core.py b/astropy/cosmology/core.py
index 7c38062d30..3f1a3eb08f 100644
--- a/astropy/cosmology/core.py
+++ b/astropy/cosmology/core.py
@@ -29,7 +29,6 @@ if TYPE_CHECKING:
     from collections.abc import Mapping
     from typing import Self
 
-    from astropy.cosmology._typing import _CosmoT
     from astropy.cosmology.funcs.comparison import _FormatType
 
 # Originally authored by Andrew Becker (becker@astro.washington.edu),
@@ -46,10 +45,11 @@ __all__ = ["Cosmology", "CosmologyError", "FlatCosmologyMixin"]
 # Parameters
 
 # registry of cosmology classes with {key=name : value=class}
-_COSMOLOGY_CLASSES: dict[str, type[Cosmology]] = dict()
+_COSMOLOGY_CLASSES = dict()
 
 # typing
 # NOTE: private b/c RTD error
+_CosmoT = TypeVar("_CosmoT", bound="Cosmology")
 _FlatCosmoT = TypeVar("_FlatCosmoT", bound="FlatCosmologyMixin")
 
 
diff --git a/astropy/wcs/wcsapi/high_level_api.py b/astropy/wcs/wcsapi/high_level_api.py
index 1b07dbe04a..082eaf898a 100644
--- a/astropy/wcs/wcsapi/high_level_api.py
+++ b/astropy/wcs/wcsapi/high_level_api.py
@@ -46,7 +46,15 @@ def _toindex(value):
     >>> _toindex(np.array([1.5, 2.49999]))
     array([2, 2])
     """
-    indx = np.asarray(np.floor(np.asarray(value) + 0.5), dtype=int)
+    arr = np.floor(np.asarray(value) + 0.5)
+
+    fill_value = np.iinfo(int).min
+    if np.isscalar(arr):
+        if np.isnan(arr):
+            arr = fill_value
+    else:
+        arr[np.isnan(arr)] = fill_value
+    indx = np.asarray(arr, dtype=int)
     return indx
 
 
diff --git a/docs/changes/wcs/17236.bugfix.rst b/docs/changes/wcs/17236.bugfix.rst
new file mode 100644
index 0000000000..8f391288c5
--- /dev/null
+++ b/docs/changes/wcs/17236.bugfix.rst
@@ -0,0 +1,2 @@
+Avoid a ``RuntimeWarning`` in ``WCS.world_to_array_index`` by converting
+NaN inputs to int.


Test patch:
diff --git a/astropy/wcs/wcsapi/tests/test_high_level_api.py b/astropy/wcs/wcsapi/tests/test_high_level_api.py
index 57c3b532ee..d42467a53a 100644
--- a/astropy/wcs/wcsapi/tests/test_high_level_api.py
+++ b/astropy/wcs/wcsapi/tests/test_high_level_api.py
@@ -282,3 +282,27 @@ def test_minimal_mixin_subclass():
     pixel = high_level_wcs.world_to_array_index(*coord)
 
     assert_allclose(pixel, (1, 2))
+
+
+def test_world_to_array_index_nan():
+    # see https://github.com/astropy/astropy/issues/17227
+    wcs1 = WCS(naxis=1)
+    wcs1.wcs.crpix = (1,)
+    wcs1.wcs.set()
+    wcs1.pixel_bounds = [None]
+
+    res1 = wcs1.world_to_array_index(*wcs1.pixel_to_world((5,)))
+    assert not np.any(np.isnan(res1))
+    assert res1.ndim == 0
+    assert res1.item() == 5
+
+    wcs2 = WCS(naxis=2)
+    wcs2.wcs.crpix = (1, 1)
+    wcs2.wcs.set()
+    wcs2.pixel_bounds = [None, (-0.5, 3.5)]
+
+    res2 = wcs2.world_to_array_index(*wcs2.pixel_to_world(5, 5))
+    assert not np.any(np.isnan(res2))
+    assert isinstance(res2, tuple)
+    assert len(res2) == 2
+    assert res2 == (np.iinfo(int).min, 5)