From: "P. L. Lim" <2090236+pllim@users.noreply.github.com>
Date: Wed, 14 Sep 2022 08:38:04 -0500
Subject: Compatibility with Python 3.11

https://github.com/astropy/astropy/pull/13665

Backport of:
https://github.com/astropy/astropy/pull/13614

Closes: #1019708
---
 astropy/cosmology/tests/test_core.py |  7 +++++--
 astropy/io/ascii/core.py             | 20 ++++++++++++--------
 astropy/tests/helper.py              | 13 +++++++++----
 astropy/time/tests/test_mask.py      |  9 +++++++--
 astropy/utils/compat/misc.py         |  6 ++++--
 setup.cfg                            |  2 ++
 6 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/astropy/cosmology/tests/test_core.py b/astropy/cosmology/tests/test_core.py
index ebd5873..b32850d 100644
--- a/astropy/cosmology/tests/test_core.py
+++ b/astropy/cosmology/tests/test_core.py
@@ -17,10 +17,11 @@ import pytest
 # LOCAL
 import astropy.cosmology.units as cu
 import astropy.units as u
-from astropy.cosmology import Cosmology, CosmologyError, FlatCosmologyMixin
+from astropy.cosmology import Cosmology, FlatCosmologyMixin
 from astropy.cosmology.core import _COSMOLOGY_CLASSES
 from astropy.cosmology.parameter import Parameter
 from astropy.table import Column, QTable, Table
+from astropy.utils.compat import PYTHON_LT_3_11
 from astropy.utils.exceptions import AstropyDeprecationWarning
 from astropy.utils.metadata import MetaData
 
@@ -197,7 +198,9 @@ class TestCosmology(ParameterTestMixin, MetaTestMixin,
         assert cosmo.name == self.cls_kwargs["name"]  # test has expected value
 
         # immutable
-        with pytest.raises(AttributeError, match="can't set"):
+        match = ("can't set" if PYTHON_LT_3_11
+                 else f"property 'name' of {cosmo.__class__.__name__!r} object has no setter")
+        with pytest.raises(AttributeError, match=match):
             cosmo.name = None
 
     def test_is_flat(self, cosmo_cls, cosmo):
diff --git a/astropy/io/ascii/core.py b/astropy/io/ascii/core.py
index 0cb71a3..20278ff 100644
--- a/astropy/io/ascii/core.py
+++ b/astropy/io/ascii/core.py
@@ -1088,17 +1088,21 @@ class BaseOutputter:
                 try:
                     col.data = converter_func(col.str_vals)
                     col.type = converter_type
-                except (TypeError, ValueError) as err:
-                    col.converters.pop(0)
-                    last_err = err
-                except OverflowError as err:
+                except (OverflowError, TypeError, ValueError) as err:
                     # Overflow during conversion (most likely an int that
                     # doesn't fit in native C long). Put string at the top of
                     # the converters list for the next while iteration.
-                    warnings.warn(
-                        "OverflowError converting to {} in column {}, reverting to String."
-                        .format(converter_type.__name__, col.name), AstropyWarning)
-                    col.converters.insert(0, convert_numpy(numpy.str))
+                    # With python/cpython#95778 this has been supplemented with a
+                    # "ValueError: Exceeds the limit (4300) for integer string conversion"
+                    # so need to catch that as well.
+                    if isinstance(err, OverflowError) or (isinstance(err, ValueError) and
+                                                          str(err).startswith('Exceeds the limit')):
+                        warnings.warn(
+                            f'OverflowError converting to {converter_type.__name__} in column '
+                            f'{col.name}, reverting to String.', AstropyWarning)
+                        col.converters.insert(0, convert_numpy(numpy.str))
+                    else:
+                        col.converters.pop(0)
                     last_err = err
 
 
diff --git a/astropy/tests/helper.py b/astropy/tests/helper.py
index df02148..b636df4 100644
--- a/astropy/tests/helper.py
+++ b/astropy/tests/helper.py
@@ -6,6 +6,7 @@ from the installed astropy.  It makes use of the `pytest`_ testing framework.
 import os
 import sys
 import pickle
+import inspect
 import warnings
 import functools
 
@@ -15,7 +16,7 @@ from astropy.units import allclose as quantity_allclose  # noqa: F401
 from astropy.utils.decorators import deprecated
 from astropy.utils.exceptions import (AstropyDeprecationWarning,
                                       AstropyPendingDeprecationWarning)
-
+from astropy.utils.compat import PYTHON_LT_3_11
 
 # For backward-compatibility with affiliated packages
 from .runner import TestRunner  # pylint: disable=W0611  # noqa
@@ -409,11 +410,15 @@ def generic_recursive_equality_test(a, b, class_history):
     Check if the attributes of a and b are equal. Then,
     check if the attributes of the attributes are equal.
     """
-    dict_a = a.__getstate__() if hasattr(a, '__getstate__') else a.__dict__
+    if PYTHON_LT_3_11:
+        dict_a = a.__getstate__() if hasattr(a, '__getstate__') else a.__dict__
+    else:
+        # NOTE: The call may need to be adapted if other objects implementing a __getstate__
+        # with required argument(s) are passed to this function.
+        dict_a = a.__getstate__(a) if inspect.isclass(a) else a.__getstate__()
     dict_b = b.__dict__
     for key in dict_a:
-        assert key in dict_b,\
-          f"Did not pickle {key}"
+        assert key in dict_b, f"Did not pickle {key}"
         if hasattr(dict_a[key], '__eq__'):
             eq = (dict_a[key] == dict_b[key])
             if '__iter__' in dir(eq):
diff --git a/astropy/time/tests/test_mask.py b/astropy/time/tests/test_mask.py
index 6db8c7d..b000feb 100644
--- a/astropy/time/tests/test_mask.py
+++ b/astropy/time/tests/test_mask.py
@@ -7,6 +7,7 @@ import pytest
 
 from astropy import units as u
 from astropy.utils import iers
+from astropy.utils.compat import PYTHON_LT_3_11
 from astropy.time import Time
 from astropy.table import Table
 from astropy.utils.compat.optional_deps import HAS_H5PY
@@ -15,6 +16,11 @@ allclose_sec = functools.partial(np.allclose, rtol=2. ** -52,
                                  atol=2. ** -52 * 24 * 3600)  # 20 ps atol
 is_masked = np.ma.is_masked
 
+# The first form is expanded to r"can't set attribute '{0}'" in Python 3.10, and replaced
+# with the more informative second form as of 3.11 (python/cpython#31311).
+no_setter_err = (r"can't set attribute" if PYTHON_LT_3_11
+                 else r"property '{0}' of '{1}' object has no setter")
+
 
 def test_simple():
     t = Time([1, 2, 3], format='cxcsec')
@@ -47,9 +53,8 @@ def test_scalar_init():
 
 def test_mask_not_writeable():
     t = Time('2000:001')
-    with pytest.raises(AttributeError) as err:
+    with pytest.raises(AttributeError, match=no_setter_err.format('mask', t.__class__.__name__)):
         t.mask = True
-    assert "can't set attribute" in str(err.value)
 
     t = Time(['2000:001'])
     with pytest.raises(ValueError) as err:
diff --git a/astropy/utils/compat/misc.py b/astropy/utils/compat/misc.py
index 0462bbe..51f041b 100644
--- a/astropy/utils/compat/misc.py
+++ b/astropy/utils/compat/misc.py
@@ -10,9 +10,11 @@ import sys
 import functools
 from contextlib import suppress
 
-
 __all__ = ['override__dir__', 'suppress',
-           'possible_filename', 'namedtuple_asdict']
+           'possible_filename', 'namedtuple_asdict',
+           'PYTHON_LT_3_11']
+
+PYTHON_LT_3_11 = sys.version_info < (3, 11)
 
 
 def possible_filename(filename):
diff --git a/setup.cfg b/setup.cfg
index d22b033..3f666e6 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -148,6 +148,8 @@ filterwarnings =
 	ignore::DeprecationWarning
         ignore::astropy.utils.iers.iers.IERSStaleWarning
 	ignore:invalid value encountered*:RuntimeWarning
+	ignore:Distutils was imported before Setuptools*:UserWarning
+	ignore:Setuptools*:UserWarning
 doctest_norecursedirs = 
 	*/setup_package.py
 doctest_subpackage_requires = 
