Repository: astropy/astropy

Issue:
`FITS_record._get_indx()` is called but not defined
I stumbled upon a suspicious method call in `io.fits`: https://github.com/astropy/astropy/blob/96dc3d6b1f85a796fbe3359ee303ef02f8081580/astropy/io/fits/fitsrec.py#L96
According to `git grep` the `_get_indx()` method is not defined anywhere and this is most likely a misspelling of `_get_index()`. The line in question was introduced in 1337c18ce53e076a7642f1b2d3349d1f5108e20d, so it has been in `astropy` since 2012 and apparently in `pyfits` even before that. The fact that no one has reported any problems caused by this in all this time means that it might not be a bug that should be fixed, but dead code that should be removed.

I am not familiar with `io.fits`, so I can't provide code that would trigger this, if triggering it possible at all.

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.

Patch:
diff --git a/astropy/io/fits/fitsrec.py b/astropy/io/fits/fitsrec.py
index 1360e39231..a713109d50 100644
--- a/astropy/io/fits/fitsrec.py
+++ b/astropy/io/fits/fitsrec.py
@@ -92,9 +92,11 @@ class FITS_record:
             if indx < self.start or indx > self.end - 1:
                 raise KeyError(f"Key '{key}' does not exist.")
         elif isinstance(key, slice):
-            for indx in range(slice.start, slice.stop, slice.step):
-                indx = self._get_indx(indx)
-                self.array.field(indx)[self.row] = value
+            start, stop, step = key.indices(self.array._nfields)
+            for i, val in zip(range(start, stop, step), value, strict=True):
+                indx = self._get_index(i)
+                self.array.field(indx)[self.row] = val
+            return
         else:
             indx = self._get_index(key)
             if indx > self.array._nfields - 1:
diff --git a/astropy/timeseries/binned.py b/astropy/timeseries/binned.py
index bbe146f49b..b85fdad80c 100644
--- a/astropy/timeseries/binned.py
+++ b/astropy/timeseries/binned.py
@@ -394,7 +394,7 @@ class BinnedTimeSeries(BaseTimeSeries):
                 time_bin_end = None
 
             if time_bin_start.isscalar and time_bin_size.isscalar:
-                return cls(
+                return BinnedTimeSeries(
                     data=table,
                     time_bin_start=time_bin_start,
                     time_bin_end=time_bin_end,
@@ -402,7 +402,7 @@ class BinnedTimeSeries(BaseTimeSeries):
                     n_bins=len(table),
                 )
             else:
-                return cls(
+                return BinnedTimeSeries(
                     data=table,
                     time_bin_start=time_bin_start,
                     time_bin_end=time_bin_end,
diff --git a/astropy/timeseries/sampled.py b/astropy/timeseries/sampled.py
index 010a8bc065..bfa91f4ba7 100644
--- a/astropy/timeseries/sampled.py
+++ b/astropy/timeseries/sampled.py
@@ -338,7 +338,7 @@ class TimeSeries(BaseTimeSeries):
         time = Time(df.index, scale=time_scale)
         table = Table.from_pandas(df)
 
-        return cls(time=time, data=table)
+        return TimeSeries(time=time, data=table)
 
     def to_pandas(self):
         """
@@ -431,4 +431,4 @@ class TimeSeries(BaseTimeSeries):
                     f"Time column '{time_column}' not found in the input data."
                 )
 
-            return cls(time=time, data=table)
+            return TimeSeries(time=time, data=table)
diff --git a/docs/changes/io.fits/17737.bugfix.rst b/docs/changes/io.fits/17737.bugfix.rst
new file mode 100644
index 0000000000..6e4448825f
--- /dev/null
+++ b/docs/changes/io.fits/17737.bugfix.rst
@@ -0,0 +1,1 @@
+Fix setting a slice on table rows (``FITS_record``).
diff --git a/docs/changes/timeseries/17351.bugfix.rst b/docs/changes/timeseries/17351.bugfix.rst
deleted file mode 100644
index 29e4013e19..0000000000
--- a/docs/changes/timeseries/17351.bugfix.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-Made ``TimeSeries.from_pandas`` and ``BinnedTimeSeries.read`` more robust to
-subclassing.
diff --git a/tox.ini b/tox.ini
index 139e7e56ab..dadae4bd04 100644
--- a/tox.ini
+++ b/tox.ini
@@ -108,7 +108,7 @@ commands =
     !cov-!double: pytest --pyargs astropy {toxinidir}/docs {env:MPLFLAGS} {posargs}
     cov-!double: pytest --pyargs astropy {toxinidir}/docs {env:MPLFLAGS} --cov astropy --cov-config={toxinidir}/pyproject.toml --cov-report xml:{toxinidir}/coverage.xml {posargs}
 
-    double: pytest --keep-duplicates --pyargs astropy astropy
+    double: python -c 'import sys; from astropy import test; test(); sys.exit(test())'
 
 pip_pre =
     devdeps: true


Test patch:
diff --git a/astropy/io/fits/tests/test_table.py b/astropy/io/fits/tests/test_table.py
index 94af1e1d0c..74bc118a9c 100644
--- a/astropy/io/fits/tests/test_table.py
+++ b/astropy/io/fits/tests/test_table.py
@@ -1156,6 +1156,24 @@ class TestTableFunctions(FitsTestCase):
 
         t1.close()
 
+    def test_row_setitem(self):
+        tbdata = fits.getdata(self.data("tb.fits"))
+        row = tbdata[0]
+        assert row["c1"] == 1
+        assert row["c2"] == "abc"
+
+        row[0] = 2
+        assert row["c1"] == 2
+        row["c1"] = 3
+        assert row["c1"] == 3
+
+        with pytest.raises(IndexError):
+            row[5] = 2
+
+        row[:2] = (12, "xyz")
+        assert row["c1"] == 12
+        assert row["c2"] == "xyz"
+
     def test_fits_record_len(self):
         counts = np.array([312, 334, 308, 317])
         names = np.array(["NGC1", "NGC2", "NGC3", "NCG4"])