internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: sunpy/sunpy

Issue:
Cannot pass sortby keyword argument through to MapSequence from Map
`MapSequence` accepts a `sortby` keyword argument that can be used to optionally sort the maps in the sequence in different ways. However, if you try to pass this kwarg in through `Map`, eg.

```python
sunpy.map.Map(file_list, sequence=True, sortby=None)
```

you get the following exception,

```python-traceback
TypeError: __init__() got an unexpected keyword argument 'sortby'
```

Presumably, this is because `Map` does not properly deal with the `sortby` kwarg before it can be passed to `MapSequence`.


Discussion:
I believe the list of Maps will already be sorted by filename before the `MapSequence` is created, so passing through `sortby=None` may not work the way a user wants.  I'm thinking that we should actually have the equivalent of a `sortby` keyword that functions at the `MapFactory` level, regardless of whether the output is a list or a `MapSequence`.  (Then, if `sequence=true`, it creates a `MapSequence` with `sortby=None` to preserve the already chosen sorting.)
Interestingly, `sortby` works as expected if you pass a list of `Map`s to `Map` with `sequence=True`, but if you pass a list a filenames, the error is as above. I'm guessing this has to do with the way the factory handles files versus maps that have already been created.

Resolving patch:
diff --git a/sunpy/map/mapbase.py b/sunpy/map/mapbase.py
--- a/sunpy/map/mapbase.py
+++ b/sunpy/map/mapbase.py
@@ -3,6 +3,7 @@
 """
 import copy
 import html
+import inspect
 import numbers
 import textwrap
 import webbrowser
@@ -215,7 +216,9 @@ def __init__(self, data, header, plot_settings=None, **kwargs):
             warn_user("This file contains more than 2 dimensions. "
                       "Data will be truncated to the first two dimensions.")
 
-        super().__init__(data, meta=MetaDict(header), **kwargs)
+        params = list(inspect.signature(NDData).parameters)
+        nddata_kwargs = {x: kwargs.pop(x) for x in params & kwargs.keys()}
+        super().__init__(data, meta=MetaDict(header), **nddata_kwargs)
 
         # Correct possibly missing meta keywords
         self._fix_date()


Test patch:
diff --git a/sunpy/map/tests/test_map_factory.py b/sunpy/map/tests/test_map_factory.py
--- a/sunpy/map/tests/test_map_factory.py
+++ b/sunpy/map/tests/test_map_factory.py
@@ -60,6 +60,11 @@ def test_mapsequence(self):
         sequence = sunpy.map.Map(a_list_of_many, sequence=True)
         assert isinstance(sequence, sunpy.map.MapSequence)
 
+    def test_mapsequence_sortby(self):
+        # Test making a MapSequence with sortby kwarg
+        sequence = sunpy.map.Map(a_list_of_many, sequence=True, sortby=None)
+        assert isinstance(sequence, sunpy.map.MapSequence)
+
     def test_composite(self):
         # Test making a CompositeMap
         comp = sunpy.map.Map(AIA_171_IMAGE, RHESSI_IMAGE, composite=True)
Repository: sunpy/sunpy

Issue:
Document `QueryResponseTable.__init__
There is no documentation for `QueryResponseTable.__init__` (https://docs.sunpy.org/en/latest/api/sunpy.net.base_client.QueryResponseTable.html#sunpy.net.base_client.QueryResponseTable). It would be good to add some. 


Discussion:
Why does that not inherit the docstring of its parent class? There is no behaviour change in the constructor (only an astropy backport), so I expected the inherited docstring to work.
The inherited `__init__` method doesn't currently have a docstring, although I think you mean `QueryResponseTable.__doc__` is missing, not `QueryResponseTable.__init__.__doc__`. Should I add the following code to the top of `QueryResponseTable` to manually inherit the docstring?

```py
class QueryResponseTable(QTable):
    __doc__ = QTable.__doc__

    ...
```
That should mean that the top of https://docs.sunpy.org/en/latest/api/sunpy.net.base_client.QueryResponseTable.html contains the same text as https://docs.astropy.org/en/stable/api/astropy.table.QTable.html.
Worth a shot.

Resolving patch:
diff --git a/sunpy/net/base_client.py b/sunpy/net/base_client.py
--- a/sunpy/net/base_client.py
+++ b/sunpy/net/base_client.py
@@ -164,6 +164,8 @@ def as_table(self):
 
 
 class QueryResponseTable(QTable):
+    __doc__ = QTable.__doc__
+
     Row = QueryResponseRow
     Column = QueryResponseColumn
 


Test patch:
Repository: sunpy/sunpy

Issue:
Fix CROTA keyword in EUI maps
Currently EUI maps have a `CROTA` keyword, which by the FITS standard should really be a `CROTA2` keyword. This results in the warning
```python
/home/docs/checkouts/readthedocs.org/user_builds/solar-orbiter-python/envs/latest/lib/python3.8/site-packages/astropy/wcs/wcs.py:482: FITSFixedWarning: CROTA = 2.486914995997215 / [deg] rotation angle
keyword looks very much like CROTAn but isn't.
  wcsprm = _wcs.Wcsprm(header=header_bytes, key=key,
```
It would be good to
- Check if CROTA is in the header and CROTA2 isn't
- If so, rename the CROTA keyword to CROTA2


Discussion:
Ahh I just realized that because of the way I generated the test header for the EUI source tests, the CROTA key is already replaced by the PC_ij matrix. This should be fixed too. 

Resolving patch:
diff --git a/sunpy/map/sources/solo.py b/sunpy/map/sources/solo.py
--- a/sunpy/map/sources/solo.py
+++ b/sunpy/map/sources/solo.py
@@ -5,6 +5,7 @@
 from astropy.coordinates import CartesianRepresentation
 from astropy.visualization import ImageNormalize, LinearStretch
 
+from sunpy import log
 from sunpy.coordinates import HeliocentricInertial
 from sunpy.map import GenericMap
 from sunpy.map.sources.source_type import source_stretch
@@ -38,6 +39,10 @@ def __init__(self, data, header, **kwargs):
         self.plot_settings['norm'] = ImageNormalize(
             stretch=source_stretch(self.meta, LinearStretch()), clip=False)
 
+        if 'CROTA' in self.meta and 'CROTA2' not in self.meta:
+            log.debug("Renaming 'CROTA' to 'CROTA2'")
+            self.meta['CROTA2'] = self.meta.pop('CROTA')
+
     @property
     def processing_level(self):
         if self.meta.get('level'):


Test patch:
Repository: sunpy/sunpy

Issue:
XRT Map ScaleValueError
Hello, 
I am getting an error when trying to plot the HinodeXRT.fits file using: 
```
m = sunpy.map.Map('HinodeXRT.fits')
m.plot()
```
The error is:  `ScaleValueError: Scale 'utc (tbr)' is not in the allowed scales ['local', 'tai', 'tcb', 'tcg', 'tdb', 'tt', 'ut1', 'utc']`


 A similar fix was implemented for this issue but it was only match uppercase ‘UTC (TBR)’

https://github.com/sunpy/sunpy/blob/71b09358db2a08fa2aec34a9798c1cc7ed6368ac/sunpy/map/sources/hinode.py#L49-L51



Discussion:
This is very interesting as I've just checked our test data and, for me, the `TIMESYS` key is all uppercase, `"UTC (TBR)"`. I wonder if this is an OS-dependent thing. @criselsuarez which OS are you using? Is this on Windows?

Resolving patch:
diff --git a/sunpy/map/sources/hinode.py b/sunpy/map/sources/hinode.py
--- a/sunpy/map/sources/hinode.py
+++ b/sunpy/map/sources/hinode.py
@@ -1,18 +1,12 @@
 """Hinode XRT and SOT Map subclass definitions"""
+from sunpy import log
+from sunpy.map import GenericMap
 
 __author__ = ["Jack Ireland, Jose Ivan Campos-Rozo, David Perez-Suarez"]
 __email__ = "jack.ireland@nasa.gov"
 
-from sunpy import log
-from sunpy.map import GenericMap
-
 __all__ = ['XRTMap', 'SOTMap']
 
-# the following values comes from xrt_prep.pro
-# search for saturation in
-# http://darts.jaxa.jp/pub/ssw/hinode/xrt/idl/util/xrt_prep.pro
-# SATURATION_LIMIT = 2500
-
 
 def _lower_list(l):
     return [item.lower() for item in l]
@@ -46,13 +40,10 @@ def __init__(self, data, header, **kwargs):
 
         super().__init__(data, header, **kwargs)
 
-        if self.meta.get('timesys', '') == 'UTC (TBR)':
+        if self.meta.get('timesys', '').upper() == 'UTC (TBR)':
             log.debug('Modifying TIMESYS keyword from "UTC (TBR)" to "UTC"')
             self.meta['timesys'] = 'UTC'
 
-        # converting data array to masked array
-        # self.data = ma.masked_where(self.data > SATURATION_LIMIT, self.data)
-
         fw1 = header.get('EC_FW1_')
         if fw1.lower() not in _lower_list(self.filter_wheel1_measurements):
             raise ValueError('Unpexpected filter wheel 1 in header.')
@@ -64,7 +55,6 @@ def __init__(self, data, header, **kwargs):
         fw2 = fw2.replace("_", " ")
 
         self.meta['detector'] = "XRT"
-#        self.meta['instrume'] = "XRT"
         self.meta['telescop'] = "Hinode"
         self.plot_settings['cmap'] = 'hinodexrt'
 


Test patch:
Repository: sunpy/sunpy

Issue:
Adding units for CDF files of Solar Orbiter/EPD [sunpy.io.cdf] 
### Description

The nice new function `sunpy.io.cdf.read_cdf` has problems understanding the units for differential intensity from the CDF files of the Solar Orbiter EPD instrument suite (EPT, STEP, HET, SIS).

``` python
>>> read_cdf('solo_L2_epd-ept-sun-rates_20210728_V01.cdf')
WARNING: SunpyUserWarning: astropy did not recognize units of "particles / (s cm^2 sr MeV)". Assigning dimensionless units. If you think this unit should not be dimensionless, please raise an issue at https://github.com/sunpy/sunpy/issues [sunpy.io.cdf]
```

### Steps to Reproduce

<!--
Please include code that reproduces the issue whenever possible.
The best reproductions are self-contained scripts with minimal dependencies.
-->

```python
from sunpy.net import Fido
from sunpy.net import attrs as a
from sunpy.io.cdf import read_cdf

trange = a.Time('2021/07/28', '2021/07/28')
dataset = a.cdaweb.Dataset('SOLO_L2_EPD-EPT-SUN-RATES')
result = Fido.search(trange, dataset)
file = Fido.fetch(result[0])
read_cdf(file[0])
```

### System Details

<!--
We need to know the the package version you are using.
We provide a short function in sunpy (``sunpy.util.system_info()``) that will provide most of the below information.
-->

- SunPy Version: 3.1.3
- Astropy Version: 4.2.1
- Python Version: 3.9.5
- OS information: Ubuntu 20.04.3 LTS

Relates to  #5692



Discussion:

Resolving patch:
diff --git a/sunpy/io/cdf.py b/sunpy/io/cdf.py
--- a/sunpy/io/cdf.py
+++ b/sunpy/io/cdf.py
@@ -170,6 +170,9 @@ def read_cdf(fname):
                 '1/(cm**2-s-sr-MeV/nuc.)': 1 / (u.cm**2 * u.s * u.sr * u.MeV),
                 '1/(cm^2 sec ster MeV)': 1 / (u.cm**2 * u.s * u.sr * u.MeV),
 
+                'particles / (s cm^2 sr MeV)': 1 / (u.cm**2 * u.s * u.sr * u.MeV),
+                'particles / (s cm^2 sr MeV/n)': 1 / (u.cm**2 * u.s * u.sr * u.MeV),
+
                 '1/(cm**2-s-sr)': 1 / (u.cm**2 * u.s * u.sr),
                 '1/(SQcm-ster-s)': 1 / (u.cm**2 * u.s * u.sr),
                 '1/(SQcm-ster-s)..': 1 / (u.cm**2 * u.s * u.sr),


Test patch:
Repository: sunpy/sunpy

Issue:
HMIMap not selected on HMI image file
### Describe the bug

When attempting to parse the latest HMI images from [LMSAL](https://sdowww.lmsal.com/sdomedia/hv_jp2kwrite/v0.8/jp2/HMI/2022/02/15/magnetogram/) HMIMap is not selected by the Map factory.

HMIMap is checking:
```python
return (str(header.get('TELESCOP', '')).endswith('HMI') and ...
```

but the data in these HMI files only shows SDO as the telescope, so GenericMap is selected.
I propose that it should change to  
```python
return str(header.get('INSTRUME', '')).startswith('HMI') and ...
```
This way it is consistent with how AIAMap is selected (AIAMap checks that instrument starts with AIA)

### To Reproduce

Use any recent HMI image from lmsal such as [this one](https://sdowww.lmsal.com/sdomedia/hv_jp2kwrite/v0.8/jp2/HMI/2022/02/15/magnetogram/2022_02_15__06_43_54_500__SDO_HMI_HMI_magnetogram.jp2)

```python
from sunpy.map import Map

hmi_map = Map('2022_02_15__06_43_54_500__SDO_HMI_HMI_magnetogram.jp2')
print(type(hmi_map)) # observe that type is GenericMap and not HMIMap
```


### What happened?

GenericMap is selected over HMIMap because the "TELESCOP" field in the fits data is only "SDO" and not "SDO/HMI" like the current HMIMap expects.

### Expected behavior

HMIMap should be selected since the file being parsed is an HMI file.

### Screenshots

_No response_

### System Details

General
#######
OS: Ubuntu (20.04, Linux 5.10.47-linuxkit)
Arch: 64bit, (x86_64)
sunpy: 3.1.3
Installation path: /root/sunpy3_venv/lib/python3.8/site-packages

Required Dependencies
#####################
astropy: 5.0.1
numpy: 1.22.2
packaging: 21.3
parfive: 1.5.1

### Installation method

pip


Discussion:

Resolving patch:
diff --git a/sunpy/map/sources/sdo.py b/sunpy/map/sources/sdo.py
--- a/sunpy/map/sources/sdo.py
+++ b/sunpy/map/sources/sdo.py
@@ -131,7 +131,7 @@ def detector(self):
     @classmethod
     def is_datasource_for(cls, data, header, **kwargs):
         """Determines if header corresponds to an HMI image"""
-        return (str(header.get('TELESCOP', '')).endswith('HMI') and
+        return (str(header.get('INSTRUME', '')).startswith('HMI') and
                 not HMISynopticMap.is_datasource_for(data, header))
 
 


Test patch:
Repository: sunpy/sunpy

Issue:
Functionality to pass a dt (sampling frequency) to TimeRange and it would return list of astropy time objects
### Description

It would be nice if you could pass a start and end time and a sampling frequency to TimeRange and it would return a list of astropy time objects. 

The list could be an attribute of the TimeRange class

E.g. an equivalent to something like np.linspace but for a timerange


Discussion:
You mean something like this?

```
from astropy.time import Time, TimeDatetime, TimeDelta
def datetime_range(start, end, delta):
    delta = TimeDelta(delta, format='sec')
    while start < end:
        yield start        
        start += delta
        
```
```
t1 = Time('2010-01-01 00:00:00')
t2 = Time('2010-03-01 05:00:00')

t_list = list(datetime_range(t1, t2,3*60*60))
```
This works where `TimeDelta` in in seconds. In the above example the `t_list` will return a list of `Time` objects between `t1` and `t2` with a stepsize of `delta` (in sec).
Hi, I'd like to take this issue if no one's working on it
> Hi, I'd like to take this issue if no one's working on it

I am not aware that anyone is working on this issue. So please go ahead.

Resolving patch:
diff --git a/sunpy/time/timerange.py b/sunpy/time/timerange.py
--- a/sunpy/time/timerange.py
+++ b/sunpy/time/timerange.py
@@ -65,6 +65,17 @@ class TimeRange:
     True
     >>> time2 in time_range
     False
+
+    >>> import numpy as np
+    >>> from astropy.time import TimeDelta
+    >>> time_range = TimeRange('2014/05/05 12:00', '2014/05/10 12:00')
+    >>> np.arange(time_range.start, time_range.end, TimeDelta(24*60*60, format = "sec"))
+    array([<Time object: scale='utc' format='isot' value=2014-05-05T12:00:00.000>,
+       <Time object: scale='utc' format='isot' value=2014-05-06T12:00:00.000>,
+       <Time object: scale='utc' format='isot' value=2014-05-07T12:00:00.000>,
+       <Time object: scale='utc' format='isot' value=2014-05-08T12:00:00.000>,
+       <Time object: scale='utc' format='isot' value=2014-05-09T12:00:00.000>],
+      dtype=object)
     """
 
     def __init__(self, a, b=None, format=None):


Test patch:
Repository: sunpy/sunpy

Issue:
Relax conditions for instantiating an `HMISynopticMap` source
Currently, we require that for a map to be parsed as an `HMISynopticMap`, it has to meet the condition

https://github.com/sunpy/sunpy/blob/e45b9b2bfbf38c8b800ee04b51e37d69a34df3e0/sunpy/map/sources/sdo.py#L207-L209

Checking the content string this strictly means that files like http://jsoc.stanford.edu/data/hmi/synoptic/hmi.Synoptic_Mr.2111.fits, where the `CONTENT = 'Carrington Synoptic Chart'` are not identified as synoptic maps and actually throw an exception due to the "sine latitude" unit issue (see [this chat exchange for more details](https://matrix.to/#/!MeRdFpEonLoCwhoHeT:matrix.org/$1647593477284yBRsZ:openastronomy.org?via=matrix.org&via=openastronomy.org&via=cadair.com)).

Could we relax the condition for checking `CONTENT`, e.g. `if 'carrington synoptic chart' in header.get('content', '').lower()`? Is there a particular reason to require the "Of Br Field" as well?


Discussion:
I think changing the check as you described is a good idea 👍 
Hello, I am new to Sunpy but I am interesting in contributing and this looks like a decent issue to start with. Would it be all right for me to work on a fix for this issue?
That would be fine.

Resolving patch:
diff --git a/sunpy/map/sources/sdo.py b/sunpy/map/sources/sdo.py
--- a/sunpy/map/sources/sdo.py
+++ b/sunpy/map/sources/sdo.py
@@ -207,6 +207,4 @@ def is_datasource_for(cls, data, header, **kwargs):
         """
         Determines if header corresponds to an HMI synoptic map.
         """
-        return (str(header.get('TELESCOP', '')).endswith('HMI') and
-                str(header.get('CONTENT', '')) ==
-                'Carrington Synoptic Chart Of Br Field')
+        return str(header.get('TELESCOP', '')).endswith('HMI') and 'carrington synoptic chart' in str(header.get('CONTENT', '')).lower()


Test patch:
Repository: sunpy/sunpy

Issue:
Better document how to save maps using compression.
### Describe the feature

https://docs.sunpy.org/en/stable/generated/api/sunpy.map.GenericMap.html#sunpy.map.GenericMap.save should either explain how to save maps with compression, or should link to some external documentation (astropy?) that helps with this.

### Proposed solution

_No response_


Discussion:

Resolving patch:
diff --git a/sunpy/map/mapbase.py b/sunpy/map/mapbase.py
--- a/sunpy/map/mapbase.py
+++ b/sunpy/map/mapbase.py
@@ -1489,6 +1489,7 @@ def save(self, filepath, filetype='auto', **kwargs):
             If a type is given, a new HDU of this type will be created.
             If a HDU instance is given, its data and header will be updated from the map.
             Then that HDU instance will be written to the file.
+            The example below uses `astropy.io.fits.CompImageHDU` to compress the map.
         kwargs :
             Any additional keyword arguments are passed to
             `~sunpy.io.write_file`.
@@ -1498,6 +1499,14 @@ def save(self, filepath, filetype='auto', **kwargs):
         Saving with the jp2 extension will write a modified version
         of the given data casted to uint8 values in order to support
         the JPEG2000 format.
+
+        Examples
+        --------
+        >>> from astropy.io.fits import CompImageHDU
+        >>> from sunpy.map import Map
+        >>> import sunpy.data.sample  # doctest: +REMOTE_DATA
+        >>> aia_map = Map(sunpy.data.sample.AIA_171_IMAGE)  # doctest: +REMOTE_DATA
+        >>> aia_map.save("aia171.fits", hdu_type=CompImageHDU)  # doctest: +REMOTE_DATA
         """
         io.write_file(filepath, self.data, self.meta, filetype=filetype,
                       **kwargs)


Test patch:
Repository: sunpy/sunpy

Issue:
Document `sunpy.net.attr.or_` somewhere
### Describe the feature

The hidden feature of `sunpy.net.attr.or_` could be useful to some, should be on show somewhere


e.g. 

```python
wavelengths = [94*u.angstrom, 131*u.angstrom, 171*u.angstrom, 193*u.angstrom, 211*u.angstrom]
aia_search= Fido.search(a.Time("2022-02-15 21:40", "2022-02-15 23:00"), 
                        a.Instrument("AIA"), 
                        attr.or_(*[a.Wavelength(wav) for wav in wavelengths]))

```
### Proposed solution

add it to docs


Discussion:
There's also `and_` but it's slightly less useful.
I think this is a good candidate for a short example called something like "Searching for multiple wavelengths"

Resolving patch:
diff --git a/examples/acquiring_data/searching_multiple_wavelengths.py b/examples/acquiring_data/searching_multiple_wavelengths.py
new file mode 100644
--- /dev/null
+++ b/examples/acquiring_data/searching_multiple_wavelengths.py
@@ -0,0 +1,44 @@
+"""
+==============================================
+Searching for multiple wavelengths with Fido
+==============================================
+
+This example shows how you can search for several wavelengths of AIA data with Fido.
+"""
+from astropy import units as u
+
+from sunpy.net import Fido
+from sunpy.net import attrs as a
+
+###############################################################################
+# Here we are demonstrating how you can search for specific wavelengths of
+# AIA data using `Fido <sunpy.net.fido_factory.UnifiedDownloaderFactory>`
+# and the `sunpy.net.attrs.AttrOr` function.
+# For example, you may only want a single wavelength, say 171 Angstrom:
+
+aia_search = Fido.search(a.Time("2022-02-20 00:00", "2022-02-20 00:01"),
+                         a.Instrument("AIA"),
+                         a.Wavelength(171*u.angstrom))
+
+print(aia_search)
+
+###############################################################################
+# But say you actually want to search for several wavelengths, rather than just one.
+# You could use the "|" operator, or instead you can use the `sunpy.net.attrs.AttrOr`
+# function.
+
+wavelengths = [94, 131, 171, 193, 211]*u.angstrom
+aia_search = Fido.search(a.Time("2022-02-20 00:00", "2022-02-20 00:01"),
+                         a.Instrument("AIA"),
+                         a.AttrOr([a.Wavelength(wav) for wav in wavelengths]))
+
+print(aia_search)
+
+# This returns several searches for each of the wavelengths, which can be indexed.
+# Here the first index is that of 94 angstrom.
+print(aia_search[0])
+
+###############################################################################
+# You can then pass the `Fido <sunpy.net.fido_factory.UnifiedDownloaderFactory>`
+# result to :meth:`Fido.fetch <sunpy.net.fido_factory.UnifiedDownloaderFactory.fetch>`
+# to download the data, i.e., ``Fido.fetch(aia_search)``.


Test patch:
Repository: sunpy/sunpy

Issue:
improve gzipped-FITS files detection
### Describe the feature

Currently the file detection for gzipped-FITS files relies on checking if they are named in the `filename.fits.gz` format. However since this condition isn't a necessity for gzipped files, this approach misses those gzipped-FITS files which do not end with the `.gz` extension.

### Proposed solution

Use magic bytes / file signatures to check whether the file is gzipped or not. If it is, decompress and read its first few bytes to determine whether it's a FITS file.


Discussion:

Resolving patch:
diff --git a/sunpy/io/file_tools.py b/sunpy/io/file_tools.py
--- a/sunpy/io/file_tools.py
+++ b/sunpy/io/file_tools.py
@@ -1,8 +1,8 @@
 """
 This module provides a generic file reader.
 """
-import os
 import re
+import gzip
 import pathlib
 
 try:
@@ -214,14 +214,11 @@ def detect_filetype(filepath):
         cdf_magic_number = fp.read(4).hex()
 
     # FITS
-    # Check the extensions to see if it is a gzipped FITS file
-    filepath_rest_ext1, ext1 = os.path.splitext(filepath)
-    _, ext2 = os.path.splitext(filepath_rest_ext1)
-
-    gzip_extensions = [".gz"]
-    fits_extensions = [".fts", ".fit", ".fits"]
-    if (ext1 in gzip_extensions and ext2 in fits_extensions):
-        return 'fits'
+    # Checks for gzip signature.
+    # If found, decompresses first few bytes and checks for FITS
+    if first80[:3] == b"\x1f\x8b\x08":
+        with gzip.open(filepath, 'rb') as fp:
+            first80 = fp.read(80)
 
     # Check for "KEY_WORD  =" at beginning of file
     match = re.match(br"[A-Z0-9_]{0,8} *=", first80)


Test patch:
diff --git a/sunpy/data/test/gzip_fits_test.file b/sunpy/data/test/gzip_fits_test.file
new file mode 100644
Binary files /dev/null and b/sunpy/data/test/gzip_fits_test.file differ
diff --git a/sunpy/io/tests/test_filetools.py b/sunpy/io/tests/test_filetools.py
--- a/sunpy/io/tests/test_filetools.py
+++ b/sunpy/io/tests/test_filetools.py
@@ -49,8 +49,9 @@ def test_read_file_fits_multple_hdu():
 
 def test_read_file_fits_gzip():
     # Test read gzipped fits file
-    for fits_extension in [".fts", ".fit", ".fits"]:
-        pair = sunpy.io.read_file(get_test_filepath(f"gzip_test{fits_extension}.gz"))
+    gzip_fits_files = ["gzip_test.fts.gz", "gzip_test.fits.gz", "gzip_test.fit.gz", "gzip_fits_test.file"]
+    for filename in gzip_fits_files:
+        pair = sunpy.io.read_file(get_test_filepath(filename))
         assert isinstance(pair, list)
         assert len(pair) == 1
         assert len(pair[0]) == 2