Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-sunpy for openSUSE:Factory 
checked in at 2025-08-03 13:38:05
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-sunpy (Old)
 and      /work/SRC/openSUSE:Factory/.python-sunpy.new.1085 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-sunpy"

Sun Aug  3 13:38:05 2025 rev:37 rq:1297171 version:7.0.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-sunpy/python-sunpy.changes        
2025-07-21 20:01:41.290551096 +0200
+++ /work/SRC/openSUSE:Factory/.python-sunpy.new.1085/python-sunpy.changes      
2025-08-03 13:38:44.794775433 +0200
@@ -1,0 +2,14 @@
+Sat Aug  2 17:19:32 UTC 2025 - Ben Greiner <c...@bnavigator.de>
+
+- Update to 7.0.1
+  * Fixed a bug where the time format 2001-02-03T04:05:06Z was
+    being parsed through different code than 2001-02-03T04:05:06 or
+    2001-02-03T04:05:06.0Z. (#8265)
+  * Fixed a bug where sunpy.util.system_info would report sunpy as
+    an optional dependency of itself instead of properly reporting
+    all of the optional dependencies. (#8294)
+  * Fixed sunpy.util.system_info so that the version reported for a
+    development installation of sunpy itself or of a dependency is
+    accurate. (#8297)
+
+-------------------------------------------------------------------

Old:
----
  sunpy-7.0.0.tar.gz

New:
----
  sunpy-7.0.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-sunpy.spec ++++++
--- /var/tmp/diff_new_pack.ehX33a/_old  2025-08-03 13:38:45.498804629 +0200
+++ /var/tmp/diff_new_pack.ehX33a/_new  2025-08-03 13:38:45.498804629 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-sunpy
-Version:        7.0.0
+Version:        7.0.1
 Release:        0
 Summary:        SunPy core package: Python for Solar Physics
 License:        Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND MIT
@@ -49,7 +49,7 @@
 Requires:       python-parfive >= 2.1.0
 Requires:       python-pyerfa >= 2.0.1.1
 Requires:       python-requests >= 2.32.0
-# pafived[ftp], ignore rpmlint's python-leftover-require
+# parfive[ftp], ignore rpmlint's python-leftover-require
 Requires:       python-aioftp >= 0.17.1
 # SECTION project.optional-dependencies:asdf
 Recommends:     python-asdf >= 3

++++++ sunpy-7.0.0.tar.gz -> sunpy-7.0.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/.cruft.json new/sunpy-7.0.1/.cruft.json
--- old/sunpy-7.0.0/.cruft.json 2025-06-18 17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/.cruft.json 2025-07-31 16:11:53.000000000 +0200
@@ -1,6 +1,6 @@
 {
   "template": "https://github.com/sunpy/package-template";,
-  "commit": "15fdf534198e4f67f0a667af7d2367e93de181c5",
+  "commit": "88a0a31eadf2be84895e32ffd792768c2863f7ca",
   "checkout": null,
   "context": {
     "cookiecutter": {
@@ -32,7 +32,7 @@
         ".github/workflows/sub_package_update.yml"
       ],
       "_template": "https://github.com/sunpy/package-template";,
-      "_commit": "15fdf534198e4f67f0a667af7d2367e93de181c5"
+      "_commit": "88a0a31eadf2be84895e32ffd792768c2863f7ca"
     }
   },
   "directory": null
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/CHANGELOG.rst 
new/sunpy-7.0.1/CHANGELOG.rst
--- old/sunpy-7.0.0/CHANGELOG.rst       2025-06-18 17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/CHANGELOG.rst       2025-07-31 16:11:53.000000000 +0200
@@ -1,3 +1,20 @@
+7.0.1 (2025-07-31)
+==================
+
+Bug Fixes
+---------
+
+- Fixed a bug where the time format ``2001-02-03T04:05:06Z`` was being parsed 
through different code than ``2001-02-03T04:05:06`` or 
``2001-02-03T04:05:06.0Z``. (`#8265 
<https://github.com/sunpy/sunpy/pull/8265>`__)
+- Fixed a bug where :func:`sunpy.util.system_info` would report `sunpy` as an 
optional dependency of itself instead of properly reporting all of the optional 
dependencies. (`#8294 <https://github.com/sunpy/sunpy/pull/8294>`__)
+- Fixed :func:`sunpy.util.system_info` so that the version reported for a 
development installation of `sunpy` itself or of a dependency is accurate. 
(`#8297 <https://github.com/sunpy/sunpy/pull/8297>`__)
+
+
+Documentation
+-------------
+
+- Fixed errors and added elaborations to the docstring for 
`~sunpy.coordinates.Helioprojective`. (`#8293 
<https://github.com/sunpy/sunpy/pull/8293>`__)
+
+
 7.0.0 (2025-06-18)
 ==================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/PKG-INFO new/sunpy-7.0.1/PKG-INFO
--- old/sunpy-7.0.0/PKG-INFO    2025-06-18 17:14:50.476620200 +0200
+++ new/sunpy-7.0.1/PKG-INFO    2025-07-31 16:12:05.466531300 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: sunpy
-Version: 7.0.0
+Version: 7.0.1
 Summary: SunPy core package: Python for Solar Physics
 Author-email: The SunPy Community <su...@googlegroups.com>
 License: Copyright (c) 2013-2025 The SunPy developers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/_typos.toml new/sunpy-7.0.1/_typos.toml
--- old/sunpy-7.0.0/_typos.toml 2025-06-18 17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/_typos.toml 2025-07-31 16:11:53.000000000 +0200
@@ -25,4 +25,5 @@
     "pn",
     "lightyear",
     "PNGs",
+    "setp",
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/_version.py 
new/sunpy-7.0.1/sunpy/_version.py
--- old/sunpy-7.0.0/sunpy/_version.py   2025-06-18 17:14:49.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/_version.py   2025-07-31 16:12:05.000000000 +0200
@@ -17,5 +17,5 @@
 __version_tuple__: VERSION_TUPLE
 version_tuple: VERSION_TUPLE
 
-__version__ = version = '7.0.0'
-__version_tuple__ = version_tuple = (7, 0, 0)
+__version__ = version = '7.0.1'
+__version_tuple__ = version_tuple = (7, 0, 1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/coordinates/frames.py 
new/sunpy-7.0.1/sunpy/coordinates/frames.py
--- old/sunpy-7.0.0/sunpy/coordinates/frames.py 2025-06-18 17:14:41.000000000 
+0200
+++ new/sunpy-7.0.1/sunpy/coordinates/frames.py 2025-07-31 16:11:53.000000000 
+0200
@@ -440,18 +440,35 @@
 @add_common_docstring(**_frame_parameters())
 class Helioprojective(SunPyBaseCoordinateFrame):
     """
-    A coordinate or frame in the Helioprojective Cartesian (HPC) system, which 
is observer-based.
+    A coordinate or frame in the Helioprojective Cartesian (HPC) system.
 
-    - The origin is the location of the observer.
-    - ``Tx`` (aka "theta_x") is the angle relative to the plane containing the 
Sun-observer line
-      and the Sun's rotation axis, with positive values in the direction of 
the Sun's west limb.
-    - ``Ty`` (aka "theta_y") is the angle relative to the Sun's equatorial 
plane, with positive
-      values in the direction of the Sun's north pole.
-    - ``distance`` is the Sun-observer distance.
-
-    This system is frequently used in a projective form without ``distance`` 
specified. For
-    observations looking very close to the center of the Sun, where the 
small-angle approximation
-    is appropriate, ``Tx`` and ``Ty`` can be approximated as Cartesian 
components.
+    This is an observer-based spherical coordinate system, with:
+
+    - The origin is the observer location.
+    - The line connecting the poles of the frame is parallel to the component 
of the Sun's
+      rotation axis that is perpendicular to the observer-Sun line.
+    - ``Tx`` (short for "theta_x", or :math:`\\theta_x`) is the longitude, the 
angle relative to
+      the plane containing the observer-Sun line and the poles of the frame, 
with positive values
+      in the direction of the Sun's west limb.
+    - ``Ty`` (short for "theta_y", or :math:`\\theta_y`) is the latitude, the 
angle relative to the
+      plane that is perpendicular to the poles of the frame, with positive 
values in the direction
+      of the Sun's north pole.
+    - ``distance`` is the observer-object distance.
+
+    .. note::
+        It can be confusing that the name of this coordinate system uses the 
adjective "Cartesian"
+        despite being a spherical coordinate system. The reason for this name 
is because close to
+        the center of the Sun where the small-angle approximation is 
appropriate, ``Tx`` and ``Ty``
+        can be thought of as two components of a Cartesian-like coordinate 
system. The corresponding
+        third Cartesian-like component, sometimes called zeta 
(:math:`\\zeta`), is defined to be the
+        Sun-observer distance (``observer.radius``) minus the observer-object 
distance (``distance``).
+
+    This system is frequently used in a projective form without ``distance`` 
specified. When
+    transforming such a 2D coordinate to another frame, the object location 
usually needs to be
+    fully 3D, which is achieved by calling :meth:`.make_3d` to generate the 
``distance`` component.
+    The default assumption is that the object lies on the surface of the Sun 
if the 2D coordinate is
+    on the solar disk, but is otherwise undefined if the 2D coordinate is 
beyond the solar limb.
+    This assumption can be modified using a screen (e.g., 
:func:`~sunpy.coordinates.SphericalScreen`).
 
     A new instance can be created using the following signatures
     (note that if supplied, ``obstime`` and ``observer`` must be keyword 
arguments)::
@@ -463,16 +480,20 @@
     ----------
     {data}
     Tx : `~astropy.coordinates.Angle` or `~astropy.units.Quantity`
-        The theta_x coordinate for this object. Not needed if ``data`` is 
given.
+        The theta_x (:math:`\\theta_x`) component for this object. Not needed 
if ``data`` is given.
     Ty : `~astropy.coordinates.Angle` or `~astropy.units.Quantity`
-        The theta_y coordinate for this object. Not needed if ``data`` is 
given.
+        The theta_y (:math:`\\theta_y`) component for this object. Not needed 
if ``data`` is given.
     distance : `~astropy.units.Quantity`
-        The distance coordinate from the observer for this object.
-        Not needed if ``data`` is given.
+        The distance component from the observer for this object. Not needed 
if ``data`` is given.
     {observer}
     {rsun}
     {common}
 
+    See Also
+    --------
+    HelioprojectiveRadial
+    ~sunpy.coordinates.PlanarScreen, ~sunpy.coordinates.SphericalScreen
+
     Examples
     --------
     >>> from astropy.coordinates import SkyCoord
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/map/mapbase.py 
new/sunpy-7.0.1/sunpy/map/mapbase.py
--- old/sunpy-7.0.0/sunpy/map/mapbase.py        2025-06-18 17:14:41.000000000 
+0200
+++ new/sunpy-7.0.1/sunpy/map/mapbase.py        2025-07-31 16:11:53.000000000 
+0200
@@ -3004,7 +3004,7 @@
         """
         Returns coordinates of the contours for a given level value.
 
-        For details of the contouring algorithm, see 
:func:`contourpy.contour_generator` or :func:`contourpy.contour_generator`.
+        For details of the contouring algorithm, see 
:func:`contourpy.contour_generator` or :func:`skimage.measure.find_contours`.
 
         Parameters
         ----------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/map/tests/test_map_factory.py 
new/sunpy-7.0.1/sunpy/map/tests/test_map_factory.py
--- old/sunpy-7.0.0/sunpy/map/tests/test_map_factory.py 2025-06-18 
17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/map/tests/test_map_factory.py 2025-07-31 
16:11:53.000000000 +0200
@@ -288,7 +288,7 @@
     """
     with pytest.warns(SunpyUserWarning, match='Failed to read'):
         amap = sunpy.map.Map('s3://data.sunpy.org/aiapy', 
fsspec_kwargs={'anon':True}, allow_errors=True)
-        assert all(isinstance(am, sunpy.map.GenericMap) for am in amap)
+    assert all(isinstance(am, sunpy.map.GenericMap) for am in amap)
 
 
 def test_save():
@@ -396,16 +396,16 @@
     files = [AIA_171_IMAGE, get_test_filepath('not_actually_fits.fits')]
     with pytest.warns(SunpyUserWarning, match='Failed to read'):
         amap = sunpy.map.Map(files, allow_errors=True)
-        assert amap.data.shape == (128, 128)
+    assert amap.data.shape == (128, 128)
 
     files = [AIA_171_IMAGE, get_test_filepath('not_actually_fits.fits'), 
AIA_171_IMAGE]
     with pytest.warns(SunpyUserWarning, match='Failed to read'):
         amap = sunpy.map.Map(files, allow_errors=True)
-        assert len(amap) == 2
+    assert len(amap) == 2
 
     with pytest.warns(SunpyUserWarning, match='Failed to read'):
         amap = sunpy.map.Map(files, allow_errors=True, sequence=True)
-        assert len(amap) == 2
+    assert len(amap) == 2
 
     with pytest.raises(OSError, match='Failed to read'):
         sunpy.map.Map(files, allow_errors=False)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/map/tests/test_mapbase.py 
new/sunpy-7.0.1/sunpy/map/tests/test_mapbase.py
--- old/sunpy-7.0.0/sunpy/map/tests/test_mapbase.py     2025-06-18 
17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/map/tests/test_mapbase.py     2025-07-31 
16:11:53.000000000 +0200
@@ -1229,28 +1229,28 @@
 
 def test_validate_meta(generic_map):
     """Check to see if_validate_meta displays an appropriate error"""
+    bad_header = {
+        'CRVAL1': 0,
+        'CRVAL2': 0,
+        'CRPIX1': 5,
+        'CRPIX2': 5,
+        'CDELT1': 10,
+        'CDELT2': 10,
+        'CUNIT1': 'ARCSEC',
+        'CUNIT2': 'ARCSEC',
+        'PC1_1': 0,
+        'PC1_2': -1,
+        'PC2_1': 1,
+        'PC2_2': 0,
+        'NAXIS1': 6,
+        'NAXIS2': 6,
+        'date-obs': '1970/01/01T00:00:00',
+        'obsrvtry': 'Foo',
+        'detector': 'bar',
+        'wavelnth': 10,
+        'waveunit': 'ANGSTROM'
+    }
     with pytest.warns(SunpyMetadataWarning) as w:
-        bad_header = {
-            'CRVAL1': 0,
-            'CRVAL2': 0,
-            'CRPIX1': 5,
-            'CRPIX2': 5,
-            'CDELT1': 10,
-            'CDELT2': 10,
-            'CUNIT1': 'ARCSEC',
-            'CUNIT2': 'ARCSEC',
-            'PC1_1': 0,
-            'PC1_2': -1,
-            'PC2_1': 1,
-            'PC2_2': 0,
-            'NAXIS1': 6,
-            'NAXIS2': 6,
-            'date-obs': '1970/01/01T00:00:00',
-            'obsrvtry': 'Foo',
-            'detector': 'bar',
-            'wavelnth': 10,
-            'waveunit': 'ANGSTROM'
-        }
         sunpy.map.Map((generic_map.data, bad_header))
 
     assert 'waveunit'.upper() in str(w[0].message)
@@ -1310,13 +1310,13 @@
 
 def test_missing_metadata_warnings():
     # Checks that warnings for missing metadata are only raised once
-    with pytest.warns(Warning) as record:
-        header = {
-            'cunit1': 'arcsec',
-            'cunit2': 'arcsec',
-            'ctype1': 'HPLN-TAN',
-            'ctype2': 'HPLT-TAN',
-        }
+    header = {
+        'cunit1': 'arcsec',
+        'cunit2': 'arcsec',
+        'ctype1': 'HPLN-TAN',
+        'ctype2': 'HPLT-TAN',
+    }
+    with pytest.warns(Warning) as record:  # NOQA: PT030,PT031
         array_map = sunpy.map.Map(np.random.rand(20, 15), header)
         array_map.peek()
     # There should be 2 warnings for missing metadata (obstime and observer 
location)
@@ -1795,7 +1795,7 @@
     check_arithmetic_value_and_units(new_map, value * aia171_test_map.quantity)
     new_map = aia171_test_map / value
     check_arithmetic_value_and_units(new_map, aia171_test_map.quantity / value)
-    with pytest.warns(RuntimeWarning, match='divide by zero encountered in'):
+    with pytest.warns(RuntimeWarning, match='divide by zero encountered in'):  
# NOQA: PT031
         new_map = value / aia171_test_map
         check_arithmetic_value_and_units(new_map, value / 
aia171_test_map.quantity)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/map/tests/test_mapbase_dask.py 
new/sunpy-7.0.1/sunpy/map/tests/test_mapbase_dask.py
--- old/sunpy-7.0.0/sunpy/map/tests/test_mapbase_dask.py        2025-06-18 
17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/map/tests/test_mapbase_dask.py        2025-07-31 
16:11:53.000000000 +0200
@@ -31,7 +31,7 @@
 
 
 # This is needed for the reproject_to function
-with pytest.warns(VerifyWarning, match="Invalid 'BLANK' keyword in header."):
+with pytest.warns(VerifyWarning, match="Invalid 'BLANK' keyword in header."):  
# NOQA: PT031
     with fits.open(get_test_filepath('aia_171_level1.fits')) as hdu:
         with pytest.warns(FITSFixedWarning, match="'datfix' made the change"):
             aia_wcs = astropy.wcs.WCS(header=hdu[0].header)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/map/tests/test_reproject_to.py 
new/sunpy-7.0.1/sunpy/map/tests/test_reproject_to.py
--- old/sunpy-7.0.0/sunpy/map/tests/test_reproject_to.py        2025-06-18 
17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/map/tests/test_reproject_to.py        2025-07-31 
16:11:53.000000000 +0200
@@ -126,10 +126,9 @@
 
 
 def test_rsun_mismatch_warning(aia171_test_map, hpc_header):
+    # Modifying the `hpc_header` rsun value to create a mismatch
+    hpc_header["rsun_ref"] += 1
     with pytest.warns(SunpyUserWarning, match="rsun mismatch detected: "):
-        # Modifying the `hpc_header` rsun value to create a mismatch
-        hpc_header["rsun_ref"] += 1
-
         # Reproject with the mismatched rsun
         aia171_test_map.reproject_to(hpc_header)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/net/tests/test_scraper.py 
new/sunpy-7.0.1/sunpy/net/tests/test_scraper.py
--- old/sunpy-7.0.0/sunpy/net/tests/test_scraper.py     2025-06-18 
17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/net/tests/test_scraper.py     2025-07-31 
16:11:53.000000000 +0200
@@ -16,9 +16,9 @@
 def test_directory_date_pattern():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('%Y/%m/%d/%Y%m%d_%H%M%S_59.fit.gz')
-        testpath = '2014/03/05/20140305_013000_59.fit.gz'
-        d = parse_time((2014, 3, 5, 1, 30))
-        assert s.matches(testpath, d)
+    testpath = '2014/03/05/20140305_013000_59.fit.gz'
+    d = parse_time((2014, 3, 5, 1, 30))
+    assert s.matches(testpath, d)
 
 
 def test_directory_date_pattern_new_format():
@@ -31,9 +31,9 @@
 def test_directory_date_pattern_false():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('%Y/%m/%d/%Y%m%d_%H%M%S_59.fit.gz')
-        testpath = '2013/03/05/20140305_013000_59.fit.gz'
-        d = parse_time((2014, 3, 5, 1, 30))
-        assert not s.matches(testpath, d)
+    testpath = '2013/03/05/20140305_013000_59.fit.gz'
+    d = parse_time((2014, 3, 5, 1, 30))
+    assert not s.matches(testpath, d)
 
 
 def test_directory_date_patternFalse_new_format():
@@ -46,9 +46,9 @@
 def test_directory_obs_pattern():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('%y%m%d/{observatory}_%Y%m%d.fits', observatory='SDO')
-        testpath = '140305/SDO_20140305.fits'
-        d = parse_time((2014, 3, 5))
-        assert s.matches(testpath, d)
+    testpath = '140305/SDO_20140305.fits'
+    d = parse_time((2014, 3, 5))
+    assert s.matches(testpath, d)
 
 
 def test_directory_obs_pattern_new_format():
@@ -61,10 +61,10 @@
 def test_directory_range():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('%Y/%m/%d/%Y%m%d_%H.fit.gz')
-        directory_list = ['2009/12/30/', '2009/12/31/', '2010/01/01/',
-                        '2010/01/02/', '2010/01/03/']
-        timerange = TimeRange('2009-12-30', '2010-01-03')
-        assert s.range(timerange) == directory_list
+    directory_list = ['2009/12/30/', '2009/12/31/', '2010/01/01/',
+                    '2010/01/02/', '2010/01/03/']
+    timerange = TimeRange('2009-12-30', '2010-01-03')
+    assert s.range(timerange) == directory_list
 
 
 def test_directory_range_new_format():
@@ -79,9 +79,9 @@
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         # Test for Windows where '\' is a path separator and not part of the 
regex
         s = 
Scraper('scheme://a.url.with/a/few/forward/slashes/andbacklash\\inthename.ext', 
regex=True)
-        timerange = TimeRange('2019-02-01', '2019-02-03')
-        directory = s.range(timerange)
-        assert directory == ['scheme://a.url.with/a/few/forward/slashes/']
+    timerange = TimeRange('2019-02-01', '2019-02-03')
+    directory = s.range(timerange)
+    assert directory == ['scheme://a.url.with/a/few/forward/slashes/']
 
 
 def test_directory_regex_new_format():
@@ -95,10 +95,10 @@
 def test_directory_rangeFalse():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('%Y%m%d/%Y%m%d_%H.fit.gz')
-        directory_list = ['20091230/', '20091231/', '20100101/',
-                        '20090102/', '20090103/']
-        timerange = TimeRange('2009/12/30', '2010/01/03')
-        assert s.range(timerange) != directory_list
+    directory_list = ['20091230/', '20091231/', '20100101/',
+                    '20090102/', '20090103/']
+    timerange = TimeRange('2009/12/30', '2010/01/03')
+    assert s.range(timerange) != directory_list
 
 
 def test_directory_range_false_new_format():
@@ -112,9 +112,9 @@
 def test_no_date_directory():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('mySpacecraft/myInstrument/xMinutes/aaa%y%b.ext')
-        directory_list = ['mySpacecraft/myInstrument/xMinutes/']
-        timerange = TimeRange('2009/11/20', '2010/01/03')
-        assert s.range(timerange) == directory_list
+    directory_list = ['mySpacecraft/myInstrument/xMinutes/']
+    timerange = TimeRange('2009/11/20', '2010/01/03')
+    assert s.range(timerange) == directory_list
 
 
 def testNoDateDirectory_new_format():
@@ -127,8 +127,8 @@
 def test_directory_range_hours():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('%Y%m%d_%H/%H%M.csv')
-        timerange = TimeRange('2009-12-31T23:40:00', '2010-01-01T01:15:00')
-        assert len(s.range(timerange)) == 3  # 3 directories (1 per hour)
+    timerange = TimeRange('2009-12-31T23:40:00', '2010-01-01T01:15:00')
+    assert len(s.range(timerange)) == 3  # 3 directories (1 per hour)
 
 
 def test_directory_range_hours_new_format():
@@ -140,10 +140,10 @@
 def test_directory_range_single():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('%Y%m%d/%H_%M.csv')
-        startdate = parse_time((2010, 10, 10, 5, 0))
-        enddate = parse_time((2010, 10, 10, 7, 0))
-        timerange = TimeRange(startdate, enddate)
-        assert len(s.range(timerange)) == 1
+    startdate = parse_time((2010, 10, 10, 5, 0))
+    enddate = parse_time((2010, 10, 10, 7, 0))
+    timerange = TimeRange(startdate, enddate)
+    assert len(s.range(timerange)) == 1
 
 
 def test_directory_range_single_new_format():
@@ -157,14 +157,14 @@
 def test_directory_range_month():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('%Y%m/%d/%j_%H.txt')
-        startdate = parse_time((2008, 2, 20, 10))
-        enddate = parse_time((2008, 3, 2, 5))
-        timerange = TimeRange(startdate, enddate)
-        assert len(s.range(timerange)) == 12
-        startdate = parse_time((2009, 2, 20, 10))
-        enddate = parse_time((2009, 3, 2, 5))
-        timerange = TimeRange(startdate, enddate)
-        assert len(s.range(timerange)) == 11
+    startdate = parse_time((2008, 2, 20, 10))
+    enddate = parse_time((2008, 3, 2, 5))
+    timerange = TimeRange(startdate, enddate)
+    assert len(s.range(timerange)) == 12
+    startdate = parse_time((2009, 2, 20, 10))
+    enddate = parse_time((2009, 3, 2, 5))
+    timerange = TimeRange(startdate, enddate)
+    assert len(s.range(timerange)) == 11
 
 
 def test_directory_range_month_new_format():
@@ -183,54 +183,58 @@
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         # Standard pattern
         s = 
Scraper('data/%Y/%m/%d/fits/swap/swap_00174_fd_%Y%m%d_%H%M%S.fts.gz')
-        test_url = 
'data/2014/05/14/fits/swap/swap_00174_fd_20140514_200135.fts.gz'
-        timeURL = parse_time((2014, 5, 14, 20, 1, 35))
-        assert s._extract_date(test_url) == timeURL
+    test_url = 'data/2014/05/14/fits/swap/swap_00174_fd_20140514_200135.fts.gz'
+    timeURL = parse_time((2014, 5, 14, 20, 1, 35))
+    assert s._extract_date(test_url) == timeURL
+
+    with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         # Not-full repeated pattern
         s = Scraper('data/%Y/fits/swap/swap_00174_fd_%Y%m%d_%H%M%S.fts.gz')
-        test_url = 'data/2014/fits/swap/swap_00174_fd_20140514_200135.fts.gz'
-        timeURL = parse_time((2014, 5, 14, 20, 1, 35))
-        assert s._extract_date(test_url) == timeURL
+    test_url = 'data/2014/fits/swap/swap_00174_fd_20140514_200135.fts.gz'
+    timeURL = parse_time((2014, 5, 14, 20, 1, 35))
+    assert s._extract_date(test_url) == timeURL
 
 
 def test_extract_dates_not_separators():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('data/%Y/%m/swap%m%d_%H%M%S')
-        test_url = 'data/2014/05/swap0514_200135'
-        timeURL = parse_time((2014, 5, 14, 20, 1, 35))
-        assert s._extract_date(test_url) == timeURL
+    test_url = 'data/2014/05/swap0514_200135'
+    timeURL = parse_time((2014, 5, 14, 20, 1, 35))
+    assert s._extract_date(test_url) == timeURL
 
 
 def test_extract_dates_not_separators_and_similar():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('data/%Y/Jun%b%d_%H%M%S')
-        test_url = 'data/2014/JunJune14_200135'
-        timeURL = parse_time((2014, 6, 14, 20, 1, 35))
-        assert s._extract_date(test_url) == timeURL
-        test_url = 'data/2014/JunMay14_200135'
-        timeURL = parse_time((2014, 5, 14, 20, 1, 35))
-        assert s._extract_date(test_url) == timeURL
+    test_url = 'data/2014/JunJune14_200135'
+    timeURL = parse_time((2014, 6, 14, 20, 1, 35))
+    assert s._extract_date(test_url) == timeURL
+    test_url = 'data/2014/JunMay14_200135'
+    timeURL = parse_time((2014, 5, 14, 20, 1, 35))
+    assert s._extract_date(test_url) == timeURL
+
+    with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         # and testing with the month afterwards
         s = Scraper('data/%Y/%dJun%b_%H%M%S')
-        test_url = 'data/2014/14JunJune_200135'
-        timeURL = parse_time((2014, 6, 14, 20, 1, 35))
-        assert s._extract_date(test_url) == timeURL
+    test_url = 'data/2014/14JunJune_200135'
+    timeURL = parse_time((2014, 6, 14, 20, 1, 35))
+    assert s._extract_date(test_url) == timeURL
 
 
 def test_url():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('fd_%Y%m%d_%H%M%S.fts')
-        assert s._url_follows_pattern('fd_20130410_231211.fts')
-        assert not s._url_follows_pattern('fd_20130410_231211.fts.gz')
-        assert not s._url_follows_pattern('fd_20130410_ar_231211.fts.gz')
+    assert s._url_follows_pattern('fd_20130410_231211.fts')
+    assert not s._url_follows_pattern('fd_20130410_231211.fts.gz')
+    assert not s._url_follows_pattern('fd_20130410_ar_231211.fts.gz')
 
 
 def test_url_pattern():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('fd_%Y%m%d_%H%M%S.fts')
-        assert s._url_follows_pattern('fd_20130410_231211.fts')
-        assert not s._url_follows_pattern('fd_20130410_231211.fts.gz')
-        assert not s._url_follows_pattern('fd_20130410_ar_231211.fts.gz')
+    assert s._url_follows_pattern('fd_20130410_231211.fts')
+    assert not s._url_follows_pattern('fd_20130410_231211.fts.gz')
+    assert not s._url_follows_pattern('fd_20130410_ar_231211.fts.gz')
 
 
 @pytest.mark.parametrize(('pattern', 'filename', 'metadict'), [
@@ -246,20 +250,20 @@
 def test_url_pattern_milliseconds_generic():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('fd_%Y%m%d_%H%M%S_%e.fts')
-        assert s._url_follows_pattern('fd_20130410_231211_119.fts')
-        assert not s._url_follows_pattern('fd_20130410_231211.fts.gz')
-        assert not s._url_follows_pattern('fd_20130410_ar_231211.fts.gz')
+    assert s._url_follows_pattern('fd_20130410_231211_119.fts')
+    assert not s._url_follows_pattern('fd_20130410_231211.fts.gz')
+    assert not s._url_follows_pattern('fd_20130410_ar_231211.fts.gz')
 
 
 def test_url_pattern_milliseconds_zero_padded():
+    now_mock = Mock(return_value=datetime.datetime(2019, 4, 19, 0, 0, 0, 4009))
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         # Asserts solution to ticket #1954.
         # Milliseconds must be zero-padded in order to match URL lengths.
-        now_mock = Mock(return_value=datetime.datetime(2019, 4, 19, 0, 0, 0, 
4009))
         with patch('sunpy.net.scraper.datetime', now=now_mock):
             s = Scraper('fd_%Y%m%d_%H%M%S_%e.fts')
-        now_mock.assert_called_once()
-        assert s.now == 'fd_20190419_000000_004.fts'
+    now_mock.assert_called_once()
+    assert s.now == 'fd_20190419_000000_004.fts'
 
 
 def test_url_pattern_milliseconds_zero_padded_new_format():
@@ -276,12 +280,12 @@
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('/'.join(['file:/', str(rootdir),
                             'EIT_header', 'efz%Y%m%d.%H%M%S_s.header']))
-        startdate = parse_time((2004, 3, 1, 4, 0))
-        enddate = parse_time((2004, 3, 1, 6, 30))
-        assert len(s.filelist(TimeRange(startdate, enddate))) == 3
-        startdate = parse_time((2010, 1, 10, 20, 30))
-        enddate = parse_time((2010, 1, 20, 20, 30))
-        assert len(s.filelist(TimeRange(startdate, enddate))) == 0
+    startdate = parse_time((2004, 3, 1, 4, 0))
+    enddate = parse_time((2004, 3, 1, 6, 30))
+    assert len(s.filelist(TimeRange(startdate, enddate))) == 3
+    startdate = parse_time((2010, 1, 10, 20, 30))
+    enddate = parse_time((2010, 1, 20, 20, 30))
+    assert len(s.filelist(TimeRange(startdate, enddate))) == 0
 
 
 def test_files_range_same_directory_local_new_format():
@@ -296,18 +300,18 @@
 
 @pytest.mark.remote_data
 def test_files_range_same_directory_remote():
+    pattern = ('http://proba2.oma.be/{instrument}/data/bsd/%Y/%m/%d/'
+            '{instrument}_lv1_%Y%m%d_%H%M%S.fits')
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
-        pattern = ('http://proba2.oma.be/{instrument}/data/bsd/%Y/%m/%d/'
-                '{instrument}_lv1_%Y%m%d_%H%M%S.fits')
         s = Scraper(pattern, instrument='swap')
-        startdate = parse_time((2014, 5, 14, 0, 0))
-        enddate = parse_time((2014, 5, 14, 0, 5))
-        timerange = TimeRange(startdate, enddate)
-        assert len(s.filelist(timerange)) == 2
-        startdate = parse_time((2014, 5, 14, 0, 6))
-        enddate = parse_time((2014, 5, 14, 0, 7))
-        timerange = TimeRange(startdate, enddate)
-        assert len(s.filelist(timerange)) == 0
+    startdate = parse_time((2014, 5, 14, 0, 0))
+    enddate = parse_time((2014, 5, 14, 0, 5))
+    timerange = TimeRange(startdate, enddate)
+    assert len(s.filelist(timerange)) == 2
+    startdate = parse_time((2014, 5, 14, 0, 6))
+    enddate = parse_time((2014, 5, 14, 0, 7))
+    timerange = TimeRange(startdate, enddate)
+    assert len(s.filelist(timerange)) == 0
 
 
 @pytest.mark.remote_data
@@ -327,17 +331,17 @@
 
 @pytest.mark.remote_data
 def test_files_range_same_directory_months_remote():
+    pattern = ('http://www.srl.caltech.edu/{spacecraft}/DATA/{instrument}/'
+            'Ahead/1minute/AeH%y%b.1m')
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
-        pattern = ('http://www.srl.caltech.edu/{spacecraft}/DATA/{instrument}/'
-                'Ahead/1minute/AeH%y%b.1m')
         s = Scraper(pattern, spacecraft='STEREO', instrument='HET')
-        startdate = parse_time((2007, 8, 1))
-        enddate = parse_time((2007, 9, 10))
-        timerange = TimeRange(startdate, enddate)
-        files = s.filelist(timerange)
-        assert files == 
['http://www.srl.caltech.edu/STEREO/DATA/HET/Ahead/1minute/AeH07Aug.1m',
-                        
'http://www.srl.caltech.edu/STEREO/DATA/HET/Ahead/1minute/AeH07Jul.1m',
-                        
'http://www.srl.caltech.edu/STEREO/DATA/HET/Ahead/1minute/AeH07Sep.1m']
+    startdate = parse_time((2007, 8, 1))
+    enddate = parse_time((2007, 9, 10))
+    timerange = TimeRange(startdate, enddate)
+    files = s.filelist(timerange)
+    assert files == 
['http://www.srl.caltech.edu/STEREO/DATA/HET/Ahead/1minute/AeH07Aug.1m',
+                     
'http://www.srl.caltech.edu/STEREO/DATA/HET/Ahead/1minute/AeH07Jul.1m',
+                     
'http://www.srl.caltech.edu/STEREO/DATA/HET/Ahead/1minute/AeH07Sep.1m']
 
 
 @pytest.mark.remote_data
@@ -356,13 +360,13 @@
 @pytest.mark.xfail
 @pytest.mark.remote_data
 def test_ftp():
+    pattern = 
'ftp://ftp.ngdc.noaa.gov/STP/swpc_products/daily_reports/solar_region_summaries/%Y/%m/%Y%m%dSRS.txt'
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
-        pattern = 
'ftp://ftp.ngdc.noaa.gov/STP/swpc_products/daily_reports/solar_region_summaries/%Y/%m/%Y%m%dSRS.txt'
         s = Scraper(pattern)
-        timerange = TimeRange('2024/5/18', '2024/5/20')
-        urls = s.filelist(timerange)
-        assert urls[0] == 
('ftp://ftp.ngdc.noaa.gov/STP/swpc_products/daily_reports/solar_region_summaries/2024/05/20240517SRS.txt')
-        assert len(urls) == 4
+    timerange = TimeRange('2024/5/18', '2024/5/20')
+    urls = s.filelist(timerange)
+    assert urls[0] == 
('ftp://ftp.ngdc.noaa.gov/STP/swpc_products/daily_reports/solar_region_summaries/2024/05/20240517SRS.txt')
+    assert len(urls) == 4
 
 
 @pytest.mark.xfail
@@ -388,27 +392,27 @@
 
 @pytest.mark.remote_data
 def test_filelist_url_missing_directory():
+    # Asserts solution to ticket #2684.
+    # Attempting to access data for the year 1960 results in a 404, so no 
files are returned.
+    pattern = 
'http://lasp.colorado.edu/eve/data_access/evewebdataproducts/level2/%Y/%j/'
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
-        # Asserts solution to ticket #2684.
-        # Attempting to access data for the year 1960 results in a 404, so no 
files are returned.
-        pattern = 
'http://lasp.colorado.edu/eve/data_access/evewebdataproducts/level2/%Y/%j/'
         s = Scraper(pattern)
-        timerange = TimeRange('1960/01/01 00:00:00', '1960/01/02 00:00:00')
-        assert len(s.filelist(timerange)) == 0
+    timerange = TimeRange('1960/01/01 00:00:00', '1960/01/02 00:00:00')
+    assert len(s.filelist(timerange)) == 0
 
 
 @pytest.mark.remote_data
 def test_filelist_relative_hrefs():
+    # the url opened by the scraper from below pattern contains some links 
which don't have hrefs
+    pattern = 
'http://www.bbso.njit.edu/pub/archive/%Y/%m/%d/bbso_halph_fr_%Y%m%d_%H%M%S.fts'
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
-        # the url opened by the scraper from below pattern contains some links 
which don't have hrefs
-        pattern = 
'http://www.bbso.njit.edu/pub/archive/%Y/%m/%d/bbso_halph_fr_%Y%m%d_%H%M%S.fts'
         s = Scraper(pattern)
-        timerange = TimeRange('2016/5/18 15:28:00', '2016/5/18 16:30:00')
-        assert s.domain == 'http://www.bbso.njit.edu/'
-        # hrefs are relative to domain here, not to the directory they are 
present in
-        # this checks that `scraper.filelist` returns fileurls relative to the 
domain
-        fileurls = s.filelist(timerange)
-        assert fileurls[1] == s.domain + 
'pub/archive/2016/05/18/bbso_halph_fr_20160518_160033.fts'
+    timerange = TimeRange('2016/5/18 15:28:00', '2016/5/18 16:30:00')
+    assert s.domain == 'http://www.bbso.njit.edu/'
+    # hrefs are relative to domain here, not to the directory they are present 
in
+    # this checks that `scraper.filelist` returns fileurls relative to the 
domain
+    fileurls = s.filelist(timerange)
+    assert fileurls[1] == s.domain + 
'pub/archive/2016/05/18/bbso_halph_fr_20160518_160033.fts'
 
 
 @pytest.mark.remote_data
@@ -431,33 +435,33 @@
 def test_regex(pattern, check_file):
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper(pattern, regex=True)
-        assert s._url_follows_pattern(check_file)
+    assert s._url_follows_pattern(check_file)
 
 
 @pytest.mark.remote_data
 def test_regex_data():
+    prefix = r'https://gong2.nso.edu/oQR/zqs/'
+    pattern = prefix + 
r'%Y%m/mrzqs%y%m%d/mrzqs%y%m%dt%H%Mc(\d){4}_(\d){3}\.fits.gz'
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
-        prefix = r'https://gong2.nso.edu/oQR/zqs/'
-        pattern = prefix + 
r'%Y%m/mrzqs%y%m%d/mrzqs%y%m%dt%H%Mc(\d){4}_(\d){3}\.fits.gz'
         s = Scraper(pattern, regex=True)
-        timerange = TimeRange('2020-01-05', '2020-01-06T16:00:00')
-        assert s._url_follows_pattern(prefix + 
'202001/mrzqs200106/mrzqs200106t1514c2226_297.fits.gz')
-        assert len(s.filelist(timerange)) == 37
+    timerange = TimeRange('2020-01-05', '2020-01-06T16:00:00')
+    assert s._url_follows_pattern(prefix + 
'202001/mrzqs200106/mrzqs200106t1514c2226_297.fits.gz')
+    assert len(s.filelist(timerange)) == 37
 
 
 @pytest.mark.remote_data
 def test_extract_files_meta():
+    prefix = r'https://gong2.nso.edu/oQR/zqs/'
+    baseurl1 = prefix + 
r'%Y%m/mrzqs%y%m%d/mrzqs%y%m%dt%H%Mc(\d){4}_(\d){3}\.fits.gz'
+    extractpattern1 = 
('{}/zqs/{year:4d}{month:2d}/mrzqs{:4d}{day:2d}/mrzqs{:6d}t'
+                       '{hour:2d}{minute:2d}c{CAR_ROT:4d}_{:3d}.fits.gz')
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
-        prefix = r'https://gong2.nso.edu/oQR/zqs/'
-        baseurl1 = prefix + 
r'%Y%m/mrzqs%y%m%d/mrzqs%y%m%dt%H%Mc(\d){4}_(\d){3}\.fits.gz'
-        extractpattern1 = 
('{}/zqs/{year:4d}{month:2d}/mrzqs{:4d}{day:2d}/mrzqs{:6d}t'
-                        '{hour:2d}{minute:2d}c{CAR_ROT:4d}_{:3d}.fits.gz')
         s1 = Scraper(baseurl1, regex=True)
-        timerange1 = TimeRange('2020-01-05', '2020-01-05T16:00:00')
-        metalist1 = s1._extract_files_meta(timerange1, extractpattern1)
-        urls = s1.filelist(timerange1)
-        assert metalist1[3]['CAR_ROT'] == 2226
-        assert metalist1[-1]['url'] == urls[-1]
+    timerange1 = TimeRange('2020-01-05', '2020-01-05T16:00:00')
+    metalist1 = s1._extract_files_meta(timerange1, extractpattern1)
+    urls = s1.filelist(timerange1)
+    assert metalist1[3]['CAR_ROT'] == 2226
+    assert metalist1[-1]['url'] == urls[-1]
 
 
 @pytest.mark.remote_data
@@ -484,10 +488,10 @@
 def test_no_directory():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('files/%Y%m%d_%H%M.dat')
-        startdate = parse_time((2010, 1, 10, 20, 30))
-        enddate = parse_time((2010, 1, 20, 20, 30))
-        timerange = TimeRange(startdate, enddate)
-        assert len(s.range(timerange)) == 1
+    startdate = parse_time((2010, 1, 10, 20, 30))
+    enddate = parse_time((2010, 1, 20, 20, 30))
+    timerange = TimeRange(startdate, enddate)
+    assert len(s.range(timerange)) == 1
 
 def test_no_directory_new_format():
     s = 
Scraper(format='files/{{year:4d}}{{month:2d}}{{day:2d}}_{{hour:2d}}{{month:2d}}.dat')
@@ -519,15 +523,15 @@
 
 @pytest.mark.remote_data
 def test_yearly_overlap():
+    # Check that a time range that falls within the interval that a file 
represents
+    # returns a single result.
+    pattern = 
"https://www.ngdc.noaa.gov/stp/space-weather/solar-data/solar-features/solar-flares/x-rays/goes/xrs/goes-xrs-report_%Y.txt";
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
-        # Check that a time range that falls within the interval that a file 
represents
-        # returns a single result.
-        pattern = 
"https://www.ngdc.noaa.gov/stp/space-weather/solar-data/solar-features/solar-flares/x-rays/goes/xrs/goes-xrs-report_%Y.txt";
         scraper = Scraper(pattern)
 
-        # Should return a single file for 2013
-        trange = TimeRange("2013-01-02", "2013-01-03")
-        assert len(scraper.filelist(trange)) == 1
+    # Should return a single file for 2013
+    trange = TimeRange("2013-01-02", "2013-01-03")
+    assert len(scraper.filelist(trange)) == 1
 
 @pytest.mark.remote_data
 def test_yearly_overlap_new_format():
@@ -592,26 +596,26 @@
 def test_check_timerange():
     with pytest.warns(SunpyDeprecationWarning, match="pattern has been 
replaced with the format keyword"):
         s = Scraper('%Y.fits')
-        # Valid time range for 2014.fits is the whole of 2014
-        # Test different cases to make sure check_timerange is working as 
expected
+    # Valid time range for 2014.fits is the whole of 2014
+    # Test different cases to make sure check_timerange is working as expected
 
-        # Interval exactly on lower boundary
-        assert s._check_timerange('2014.fits', TimeRange("2013-06-01", 
"2014-01-01"))
-        # Overlaps lower boundary
-        assert s._check_timerange('2014.fits', TimeRange("2013-06-01", 
"2014-01-02"))
-        # Overlaps upper and lower boundary
-        assert s._check_timerange('2014.fits', TimeRange("2013-06-01", 
"2015-01-02"))
-        # Entirely within both boundaries
-        assert s._check_timerange('2014.fits', TimeRange("2014-06-01", 
"2014-07-02"))
-        # Overlaps upper boundary
-        assert s._check_timerange('2014.fits', TimeRange("2014-06-01", 
"2015-01-02"))
-        # Interval exactly on upper boundary
-        assert s._check_timerange('2014.fits', TimeRange("2015-01-01", 
"2015-01-02"))
-
-        # Interval below both boundaries
-        assert not s._check_timerange('2014.fits', TimeRange("2002-01-01", 
"2013-01-02"))
-        # Interval above both boundaries
-        assert not s._check_timerange('2014.fits', TimeRange("2022-01-01", 
"2025-01-02"))
+    # Interval exactly on lower boundary
+    assert s._check_timerange('2014.fits', TimeRange("2013-06-01", 
"2014-01-01"))
+    # Overlaps lower boundary
+    assert s._check_timerange('2014.fits', TimeRange("2013-06-01", 
"2014-01-02"))
+    # Overlaps upper and lower boundary
+    assert s._check_timerange('2014.fits', TimeRange("2013-06-01", 
"2015-01-02"))
+    # Entirely within both boundaries
+    assert s._check_timerange('2014.fits', TimeRange("2014-06-01", 
"2014-07-02"))
+    # Overlaps upper boundary
+    assert s._check_timerange('2014.fits', TimeRange("2014-06-01", 
"2015-01-02"))
+    # Interval exactly on upper boundary
+    assert s._check_timerange('2014.fits', TimeRange("2015-01-01", 
"2015-01-02"))
+
+    # Interval below both boundaries
+    assert not s._check_timerange('2014.fits', TimeRange("2002-01-01", 
"2013-01-02"))
+    # Interval above both boundaries
+    assert not s._check_timerange('2014.fits', TimeRange("2022-01-01", 
"2025-01-02"))
 
 def test_check_timerange_new_pattern():
     s = Scraper(format='{{year:4d}}.fits')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/sunpy-7.0.0/sunpy/physics/tests/test_differential_rotation.py 
new/sunpy-7.0.1/sunpy/physics/tests/test_differential_rotation.py
--- old/sunpy-7.0.0/sunpy/physics/tests/test_differential_rotation.py   
2025-06-18 17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/physics/tests/test_differential_rotation.py   
2025-07-31 16:11:53.000000000 +0200
@@ -345,9 +345,12 @@
 
     with pytest.warns(RuntimeWarning, match='All-NaN axis encountered'):
         assert _get_extreme_position(coords, 'Tx', operator=np.nanmin) == -1
+    with pytest.warns(RuntimeWarning, match='All-NaN axis encountered'):
         assert _get_extreme_position(coords, 'Ty', operator=np.nanmin) == -2
 
+    with pytest.warns(RuntimeWarning, match='All-NaN axis encountered'):
         assert _get_extreme_position(coords, 'Tx', operator=np.nanmax) == 1
+    with pytest.warns(RuntimeWarning, match='All-NaN axis encountered'):
         assert _get_extreme_position(coords, 'Ty', operator=np.nanmax) == 2
 
     with pytest.raises(ValueError, match="The \"axis\" argument must be either 
\"Tx\" or \"Ty\""):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/sunpy-7.0.0/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev_animators_dev.json
 
new/sunpy-7.0.1/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev_animators_dev.json
--- 
old/sunpy-7.0.0/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev_animators_dev.json
     2025-06-18 17:14:41.000000000 +0200
+++ 
new/sunpy-7.0.1/sunpy/tests/figure_hashes_mpl_dev_ft_261_astropy_dev_animators_dev.json
     2025-07-31 16:11:53.000000000 +0200
@@ -54,12 +54,12 @@
   "sunpy.map.tests.test_plotting.test_draw_limb_different_observer": 
"bba8a5d3d8e5e3e3a7d36060d9730e4545c5250367902cf97048c33049f330f3",
   "sunpy.map.tests.test_plotting.test_draw_limb_heliographic_stonyhurst": 
"fcbf6be2894be891f458d2bc305702564cb451052bc727b0175e7b6ad527801c",
   "sunpy.map.tests.test_plotting.test_map_draw_extent": 
"c51113113a46c838b07b049a3f17940184b99b0a56200fbd63e6cf2bf1e3c86c",
-  "sunpy.map.tests.test_plotting.test_plot_autoalign[True]": 
"800c1148cc30e8ddc540aa555a488f9ed8ba654cd3e055bdd4a08238c2542c9e",
+  "sunpy.map.tests.test_plotting.test_plot_autoalign[True]": 
"c86e4af4907f01f104f38bb56746744ee99a95fe85dfd843d7baa56723265f3e",
   "sunpy.map.tests.test_plotting.test_plot_autoalign[mesh]": 
"197f06eced7fe4bf8496cf5f3ba4e4cc379ac108be0f212d605007c1e3db062c",
-  "sunpy.map.tests.test_plotting.test_plot_autoalign[image]": 
"800c1148cc30e8ddc540aa555a488f9ed8ba654cd3e055bdd4a08238c2542c9e",
+  "sunpy.map.tests.test_plotting.test_plot_autoalign[image]": 
"c86e4af4907f01f104f38bb56746744ee99a95fe85dfd843d7baa56723265f3e",
   "sunpy.map.tests.test_plotting.test_plot_autoalign_datalim": 
"a5af424f5d70394d4e39a7066793463bec0adae17c9ab46a451640957822bc8d",
-  "sunpy.map.tests.test_plotting.test_plot_autoalign_pixel_alignment": 
"88d04e77285edf867f2cc3a77a19adbf75a61df68a34bb57359397b5dab0f4d1",
-  "sunpy.map.tests.test_plotting.test_plot_autoalign_image_incomplete": 
"325ee598aa6f2cd4ab8e036d4266f408ea9b5988e1823c3ff22e0eab8584ce5c",
+  "sunpy.map.tests.test_plotting.test_plot_autoalign_pixel_alignment": 
"411b3ea8353e00f644225c866c55fc6532c855ea8e19dd8355965fdbbef2de20",
+  "sunpy.map.tests.test_plotting.test_plot_autoalign_image_incomplete": 
"fc2520d17414d0f742e7c62ed0a189f311f5f468329b20dbf714e71b0a5fa7fa",
   "sunpy.map.tests.test_reproject_to.test_reproject_to_hgs": 
"90452e9e8f248f8798a794ba70e157bf0eb87a083bda55a8ffc846c2024b1363",
   "sunpy.map.tests.test_reproject_to.test_reproject_to_hpc_interpolation": 
"adca79ed26b0139f29c5c0bec63d1a818fe8ba0cd718923d952210e6e8ca801c",
   "sunpy.map.tests.test_reproject_to.test_reproject_to_hpc_exact": 
"a772b4ba8deeb611c683d86911bf0b5acb90a3b307b1064c1d04511050810eed",
@@ -90,4 +90,4 @@
   "sunpy.visualization.tests.test_drawing.test_draw_extent": 
"d9e5182e20feff6257c3209c2202727a8e628842ca3c0c80fd1cafbc64376388",
   "sunpy.visualization.tests.test_drawing.test_draw_extent_3d": 
"1d329ecd0e31f4d4ecfb3b75e255c117dc631bd39be9a8a862fb7974004ff07c",
   "sunpy.visualization.tests.test_visualization.test_show_hpr_impact_angle": 
"43f6c3fea4238064ce503d699cba6457b3eb07ee58ea2a52f43556f42ee7efc8"
-}
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/time/tests/test_time.py 
new/sunpy-7.0.1/sunpy/time/tests/test_time.py
--- old/sunpy-7.0.0/sunpy/time/tests/test_time.py       2025-06-18 
17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/time/tests/test_time.py       2025-07-31 
16:11:53.000000000 +0200
@@ -206,6 +206,7 @@
     ('2007-05-04T21:08:12.999999', '2007:124:21:08:12.999999'),
     ('2007-05-04T21:08:12', '2007-05-04T21:08:12'),
     ('2007-05-04T21:08:12', '2007/05/04T21:08:12'),
+    ('2007-05-04T21:08:12', '2007-05-04T21:08:12Z'),
     ('2007-05-04T21:08:12', '20070504T210812'),
     ('2007-05-04T21:08:12', '2007/05/04 21:08:12'),
     ('2007-05-04T21:08:12', '2007-05-04 21:08:12'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/time/time.py 
new/sunpy-7.0.1/sunpy/time/time.py
--- old/sunpy-7.0.0/sunpy/time/time.py  2025-06-18 17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/time/time.py  2025-07-31 16:11:53.000000000 +0200
@@ -47,6 +47,7 @@
     "%Y-%m-%dT%H:%M:%S.%fZ",  # Example 2007-05-04T21:08:12.999Z
     "%Y-%m-%dT%H:%M:%S",  # Example 2007-05-04T21:08:12
     "%Y/%m/%dT%H:%M:%S",  # Example 2007/05/04T21:08:12
+    "%Y-%m-%dT%H:%M:%SZ",  # Example 2007-05-04T21:08:12Z
     "%Y%m%dT%H%M%S.%f",  # Example 20070504T210812.999999
     "%Y%m%dT%H%M",  # Example 20070504T2108 , Should precede "%Y%m%dT%H%M%S".
     "%Y%m%dT%H%M%S",  # Example 20070504T210812
@@ -252,6 +253,8 @@
             except ValueError:
                 pass
 
+        log.debug("No matching sunpy format found for %s, so falling back to 
astropy formats", first_item)
+
     # If the string format does not match one of ours, we need to protect 
against a bad interaction
     # between astropy's C fast parser and numpy>=2.3
     # https://github.com/astropy/astropy/issues/18254
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/sunpy-7.0.0/sunpy/timeseries/tests/test_timeseriesbase.py 
new/sunpy-7.0.1/sunpy/timeseries/tests/test_timeseriesbase.py
--- old/sunpy-7.0.0/sunpy/timeseries/tests/test_timeseriesbase.py       
2025-06-18 17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/timeseries/tests/test_timeseriesbase.py       
2025-07-31 16:11:53.000000000 +0200
@@ -553,7 +553,7 @@
     data = np.array([times, intensity]).T
     with pytest.warns(SunpyUserWarning, match='Unknown units'):
         ts = sunpy.timeseries.TimeSeries(data, {})
-        assert isinstance(ts, sunpy.timeseries.GenericTimeSeries)
+    assert isinstance(ts, sunpy.timeseries.GenericTimeSeries)
 
 
 # TODO:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/sunpy-7.0.0/sunpy/timeseries/tests/test_timeseriesmetadata.py 
new/sunpy-7.0.1/sunpy/timeseries/tests/test_timeseriesmetadata.py
--- old/sunpy-7.0.0/sunpy/timeseries/tests/test_timeseriesmetadata.py   
2025-06-18 17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/timeseries/tests/test_timeseriesmetadata.py   
2025-07-31 16:11:53.000000000 +0200
@@ -367,7 +367,7 @@
     assert updated_dict_md == updated_ordereddict_md == updated_metadict_md
 
 
-def test_update_overwrite(complex_append_md, overwrite=True):
+def test_update_overwrite(complex_append_md):
     updated_not_overwritten_md = copy.deepcopy(complex_append_md)
     updated_not_overwritten_md.update({'all_same': 'updated'})
     updated_overwritten_md = copy.deepcopy(complex_append_md)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/util/sysinfo.py 
new/sunpy-7.0.1/sunpy/util/sysinfo.py
--- old/sunpy-7.0.0/sunpy/util/sysinfo.py       2025-06-18 17:14:41.000000000 
+0200
+++ new/sunpy-7.0.1/sunpy/util/sysinfo.py       2025-07-31 16:11:53.000000000 
+0200
@@ -1,6 +1,7 @@
 """
 This module provides functions to retrieve system information.
 """
+import sys
 import platform
 from collections import defaultdict
 from importlib.metadata import PackageNotFoundError, version, requires, 
distribution
@@ -14,7 +15,19 @@
 __all__ = ['system_info', 'find_dependencies', 'missing_dependencies_by_extra']
 
 
-def get_requirements(package):
+def _trusted_version(package_name):
+    """
+    If the package has already been imported, trust its __version__ attribute
+    over the version retrievable by importlib.
+    """
+    if package_name in sys.modules:
+        package = sys.modules[package_name]
+        if hasattr(package, "__version__"):
+            return package.__version__
+    return version(package_name)
+
+
+def get_requirements(package, *, expand_groups=False):
     """
     This wraps `importlib.metadata.requires` to not be garbage.
 
@@ -23,6 +36,9 @@
     package : str
         Package you want requirements for.
 
+    expand_groups : bool
+        If `True`, expand any requirement that is a group of the specified 
package.
+
     Returns
     -------
     `dict`
@@ -43,9 +59,34 @@
         if package_name in requires_dict[group]:
             continue
         requires_dict[group][package_name] = req
+
+    if expand_groups:
+        # First expand each self-referential package requirement into the 
individual groups
+        for group, group_reqs in requires_dict.items():
+            if package in group_reqs and (extras := 
group_reqs[package].extras):
+                requires_dict[group].update(dict.fromkeys(extras))
+                del group_reqs[package]
+
+        # Resolve each group, recursing as necessary
+        for group in requires_dict:
+            _resolve_group(group, requires_dict)
+
     return requires_dict
 
 
+def _resolve_group(group, requires_dict):
+    """
+    Return a fully resolved list of requirements for a group.
+    If the group requires another group, recurse to fully resolve that group 
first.
+    The dictionary is permanently updated with the fully resolved requirements.
+    """
+    for req_name, req in requires_dict[group].copy().items():
+        if req is None:  # req==None means that req_name is an unresolved group
+            requires_dict[group].update(_resolve_group(req_name, 
requires_dict))
+            del requires_dict[group][req_name]
+    return requires_dict[group]
+
+
 def resolve_requirement_versions(package_versions):
     """
     Resolves a list of requirements for the same package.
@@ -82,7 +123,7 @@
     which should be installed to match the requirements and return any which 
are
     missing.
     """
-    requirements = get_requirements(package)
+    requirements = get_requirements(package, expand_groups=True)
     installed_requirements = {}
     missing_requirements = defaultdict(list)
     extras = extras or ["required"]
@@ -91,7 +132,7 @@
             continue
         for package, package_details in requirements[group].items():
             try:
-                package_version = version(package)
+                package_version = _trusted_version(package)
                 installed_requirements[package] = package_version
             except PackageNotFoundError:
                 missing_requirements[package].append(package_details)
@@ -125,7 +166,7 @@
     dependencies associated with no extras.
     """
     exclude_extras = exclude_extras or []
-    requirements = get_requirements(package)
+    requirements = get_requirements(package)  # groups do not need to be 
expanded here
     missing_dependencies = {}
     for group in requirements.keys():
         if group in exclude_extras:
@@ -149,7 +190,7 @@
     """
     Prints ones' system info in an "attractive" fashion.
     """
-    requirements = get_requirements("sunpy")
+    requirements = get_requirements("sunpy", expand_groups=True)
     groups = get_keys_list(requirements)
     extra_groups = get_extra_groups(groups, ['all', 'dev'])
     base_reqs = get_keys_list(requirements['required'])
@@ -158,7 +199,7 @@
     extra_prop = {"System": platform.system(),
                   "Arch": f"{platform.architecture()[0]}, 
({platform.processor()})",
                   "Python": platform.python_version(),
-                  "sunpy": version("sunpy")}
+                  "sunpy": _trusted_version("sunpy")}
     sys_prop = {**installed_packages, **missing_packages, **extra_prop}
     print("==============================")
     print("sunpy Installation Information")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/util/tests/test_decorators.py 
new/sunpy-7.0.1/sunpy/util/tests/test_decorators.py
--- old/sunpy-7.0.0/sunpy/util/tests/test_decorators.py 2025-06-18 
17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/util/tests/test_decorators.py 2025-07-31 
16:11:53.000000000 +0200
@@ -21,7 +21,7 @@
     @deprecated(since, message=message)
     def foo():
         pass
-    with pytest.warns(warning, match=warning_message):
+    with pytest.warns(warning, match=warning_message):  # NOQA: PT031
         warnings.simplefilter('always')
         foo()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/util/tests/test_logger.py 
new/sunpy-7.0.1/sunpy/util/tests/test_logger.py
--- old/sunpy-7.0.0/sunpy/util/tests/test_logger.py     2025-06-18 
17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/util/tests/test_logger.py     2025-07-31 
16:11:53.000000000 +0200
@@ -100,7 +100,7 @@
     assert len(warn_list) == 1
 
     # With warnings logging, making sure that Astropy warnings are not 
intercepted
-    with pytest.warns(AstropyUserWarning, match="This warning should not be 
captured") as warn_list:
+    with pytest.warns(AstropyUserWarning, match="This warning should not be 
captured") as warn_list:  # NOQA: PT031
         log.enable_warnings_logging()
         with log.log_to_list() as log_list:
             warnings.warn("This warning should be captured", SunpyUserWarning)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy/util/tests/test_sysinfo.py 
new/sunpy-7.0.1/sunpy/util/tests/test_sysinfo.py
--- old/sunpy-7.0.0/sunpy/util/tests/test_sysinfo.py    2025-06-18 
17:14:41.000000000 +0200
+++ new/sunpy-7.0.1/sunpy/util/tests/test_sysinfo.py    2025-07-31 
16:11:53.000000000 +0200
@@ -59,6 +59,9 @@
     for package in EXTRA_DEPS:
         assert package in installed
 
+    # There should not be any self-referential dependency on sunpy
+    assert "sunpy" not in installed
+
 
 def test_missing_dependencies_by_extra():
     missing = missing_dependencies_by_extra()
@@ -98,4 +101,10 @@
 def test_system_info(capsys):
     system_info()
     captured = capsys.readouterr()
-    assert "\nsunpy Installation Information\n" in captured.out
+    lines = captured.out.splitlines()
+    assert "sunpy Installation Information" in lines
+
+    # sunpy should not be listed as an optional dependency
+    index = lines.index("Optional Dependencies")
+    for line in lines[index + 2:]:
+        assert not line.startswith("sunpy:")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sunpy-7.0.0/sunpy.egg-info/PKG-INFO 
new/sunpy-7.0.1/sunpy.egg-info/PKG-INFO
--- old/sunpy-7.0.0/sunpy.egg-info/PKG-INFO     2025-06-18 17:14:50.000000000 
+0200
+++ new/sunpy-7.0.1/sunpy.egg-info/PKG-INFO     2025-07-31 16:12:05.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: sunpy
-Version: 7.0.0
+Version: 7.0.1
 Summary: SunPy core package: Python for Solar Physics
 Author-email: The SunPy Community <su...@googlegroups.com>
 License: Copyright (c) 2013-2025 The SunPy developers

Reply via email to