Repository: spacetelescope/jwst
Pull request #9359: JP-3858 background
Author: login: penaguerrero
Labels: name: background
name: testing
name: Wide Field Slitless Spec (WFSS)
name: no-changelog-entry-needed
Created: 2025-04-03T19:40:30Z | Merged: 2025-04-17T15:11:46Z

<!-- If this PR closes a JIRA ticket, make sure the title starts with the JIRA issue number,
for example JP-1234: <Fix a bug> -->
Resolves [JP-3858](https://jira.stsci.edu/browse/JP-3858)

<!-- If this PR closes a GitHub issue that is not attached to a Jira ticket, uncomment the line below, and reference it here by its number -->
<!-- Closes # -->

<!-- describe the changes comprising this PR here -->
This PR addresses style changes for Background step.

<!-- if you can't perform these tasks due to permissions, please ask a maintainer to do them -->
## Tasks
- [ ] If you have a specific reviewer in mind, tag them.
- [x] add a build milestone, i.e. `Build 12.0` (use the [latest build](https://github.com/spacetelescope/jwst/milestones) if not sure)
- [x] Does this PR change user-facing code / API? (if not, label with `no-changelog-entry-needed`)
  - [ ] write news fragment(s) in `changes/`: `echo "changed something" > changes/<PR#>.<changetype>.rst` (see [changelog readme](https://github.com/spacetelescope/jwst/blob/main/changes/README.rst) for instructions) 
  - [ ] update or add relevant tests
  - [ ] update relevant docstrings and / or `docs/` page
  - [x] [start a regression test](https://github.com/spacetelescope/RegressionTests/actions/workflows/jwst.yml) and include a link to the running job ([click here for instructions](https://github.com/spacetelescope/RegressionTests/blob/main/docs/running_regression_tests.md))
    - [ ] Do truth files need to be updated ("okified")?
      - [ ] **after the reviewer has approved these changes**, run `okify_regtests` to update the truth files
- [ ] if a JIRA ticket exists, [make sure it is resolved properly](https://github.com/spacetelescope/jwst/wiki/How-to-resolve-JIRA-issues)



Diff:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index baa2784855..0387869666 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -38,7 +38,6 @@ repos:
         exclude: |
           (?x)^(
             jwst/associations/.* |
-            jwst/background/.* |
             jwst/coron/.* |
             jwst/cube_build/.* |
             jwst/cube_skymatch/.* |
diff --git a/.ruff.toml b/.ruff.toml
index 597c80661b..c7413766aa 100644
--- a/.ruff.toml
+++ b/.ruff.toml
@@ -20,7 +20,6 @@ indent-style = "space"
 docstring-code-format = true
 exclude = [
     "jwst/associations/**.py",
-    "jwst/background/**.py",
     "jwst/coron/**.py",
     "jwst/cube_build/**.py",
     "jwst/cube_skymatch/**.py",
@@ -116,7 +115,6 @@ ignore-fully-untyped = true  # Turn off annotation checking for fully untyped co
     "PTH118", # `os.path.join()` should be replaced by `Path` with `/` operator
 ]
 "jwst/associations/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
-"jwst/background/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
 "jwst/coron/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
 "jwst/cube_build/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
 "jwst/cube_skymatch/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
diff --git a/jwst/background/__init__.py b/jwst/background/__init__.py
index ef3c2f1a84..eb5e74f6ce 100644
--- a/jwst/background/__init__.py
+++ b/jwst/background/__init__.py
@@ -1,4 +1,6 @@
+"""Subtraction of background signal depending on the observing mode."""
+
 from .background_step import BackgroundStep
 
 
-__all__ = ['BackgroundStep']
+__all__ = ["BackgroundStep"]
diff --git a/jwst/background/background_step.py b/jwst/background/background_step.py
index d3bca46f78..e8b568e164 100755
--- a/jwst/background/background_step.py
+++ b/jwst/background/background_step.py
@@ -5,13 +5,12 @@
 from .background_sub import background_sub
 from .background_sub_wfss import subtract_wfss_bkg
 import numpy as np
+
 __all__ = ["BackgroundStep"]
 
 
 class BackgroundStep(Step):
-    """
-    BackgroundStep:  Subtract background exposures from target exposures.
-    """
+    """Subtract background exposures from target exposures."""
 
     class_alias = "background"
 
@@ -23,62 +22,58 @@ class BackgroundStep(Step):
         wfss_maxiter = integer(default=5)  # WFSS iterative outlier rejection max iterations
         wfss_rms_stop = float(default=0)  # WFSS iterative outlier rejection RMS improvement threshold (percent)
         wfss_outlier_percent = float(default=1)  # WFSS outlier percentile to reject per iteration
-    """ # noqa: E501
+    """  # noqa: E501
 
     # These reference files are only used for WFSS/GRISM data.
     reference_file_types = ["wfssbkg", "wavelengthrange"]
 
     # Define a suffix for optional saved output of the combined background
-    bkg_suffix = 'combinedbackground'
+    bkg_suffix = "combinedbackground"
 
-    def process(self, input, bkg_list):
+    def process(self, step_input, bkg_list):
         """
-        Subtract the background signal from target exposures by subtracting
-        designated background images from them.
+        Subtract designated background images from target exposures.
 
         Parameters
         ----------
-        input: JWST data model
-            input target data model to which background subtraction is applied
+        step_input : ImageModel or IFUImageModel
+            Input target data model to which background subtraction is applied
 
-        bkg_list: filename list
-            list of background exposure file names
+        bkg_list : list
+            File name list of background exposures
 
         Returns
         -------
-        result: JWST data model
-            the background-subtracted target data model
+        result : ImageModel or IFUImageModel
+            The background-subtracted target data model
         """
-
         # Load the input data model
-        with datamodels.open(input) as input_model:
-
+        with datamodels.open(step_input) as input_model:
             if input_model.meta.exposure.type in ["NIS_WFSS", "NRC_WFSS"]:
-
                 # Get the reference file names
                 bkg_name = self.get_reference_file(input_model, "wfssbkg")
-                wlrange_name = self.get_reference_file(input_model,
-                                                       "wavelengthrange")
-                self.log.info('Using WFSSBKG reference file %s', bkg_name)
-                self.log.info('Using WavelengthRange reference file %s',
-                              wlrange_name)
+                wlrange_name = self.get_reference_file(input_model, "wavelengthrange")
+                self.log.info("Using WFSSBKG reference file %s", bkg_name)
+                self.log.info("Using WavelengthRange reference file %s", wlrange_name)
 
                 # Do the background subtraction for WFSS/GRISM data
-                rescaler_kwargs = {"p": self.wfss_outlier_percent,
-                                   "maxiter": self.wfss_maxiter,
-                                   "delta_rms_thresh": self.wfss_rms_stop/100,
-                                   }
+                rescaler_kwargs = {
+                    "p": self.wfss_outlier_percent,
+                    "maxiter": self.wfss_maxiter,
+                    "delta_rms_thresh": self.wfss_rms_stop / 100,
+                }
                 result = subtract_wfss_bkg(
                     input_model,
                     bkg_name,
                     wlrange_name,
                     self.wfss_mmag_extract,
-                    rescaler_kwargs=rescaler_kwargs)
+                    rescaler_kwargs=rescaler_kwargs,
+                )
                 if result is None:
                     result = input_model
-                    result.meta.cal_step.back_sub = 'SKIPPED'
+                    result.meta.cal_step.back_sub = "SKIPPED"
                 else:
-                    result.meta.cal_step.back_sub = 'COMPLETE'
+                    result.meta.cal_step.back_sub = "COMPLETE"
             else:
                 # check if input data is NRS_IFU
                 tolerance = 1.0e-8
@@ -92,28 +87,34 @@ def process(self, input, bkg_list):
                         with datamodels.open(bkg_file) as bkg_model:
                             bkg_xtilt = bkg_model.meta.instrument.gwa_xtilt
                             bkg_ytilt = bkg_model.meta.instrument.gwa_ytilt
-                            if np.allclose((input_xtilt, input_ytilt),
-                                           (bkg_xtilt, bkg_ytilt), atol=tolerance, rtol=0):
+                            if np.allclose(
+                                (input_xtilt, input_ytilt),
+                                (bkg_xtilt, bkg_ytilt),
+                                atol=tolerance,
+                                rtol=0,
+                            ):
                                 pass
                             else:
                                 do_sub = False
                                 break
                 # Do the background subtraction
                 if do_sub:
-                    bkg_model, result = background_sub(input_model,
-                                                       bkg_list,
-                                                       self.sigma,
-                                                       self.maxiters)
-                    result.meta.cal_step.back_sub = 'COMPLETE'
+                    bkg_model, result = background_sub(
+                        input_model, bkg_list, self.sigma, self.maxiters
+                    )
+                    result.meta.cal_step.back_sub = "COMPLETE"
                     if self.save_combined_background:
-                        comb_bkg_path = self.save_model(bkg_model, suffix=self.bkg_suffix, force=True)
-                        self.log.info(f'Combined background written to "{comb_bkg_path}".')
+                        comb_bkg_path = self.save_model(
+                            bkg_model, suffix=self.bkg_suffix, force=True
+                        )
+                        self.log.info(f"Combined background written to {comb_bkg_path}.")
 
                 else:
                     result = input_model.copy()
-                    result.meta.cal_step.back_sub = 'SKIPPED'
-                    self.log.warning('Skipping background subtraction')
-                    self.log.warning('GWA_XTIL and GWA_YTIL source values '
-                                     'are not the same as bkg values')
+                    result.meta.cal_step.back_sub = "SKIPPED"
+                    self.log.warning("Skipping background subtraction")
+                    self.log.warning(
+                        "GWA_XTIL and GWA_YTIL source values are not the same as bkg values"
+                    )
 
         return result
diff --git a/jwst/background/background_sub.py b/jwst/background/background_sub.py
index a04a03ea9c..5773e25a81 100755
--- a/jwst/background/background_sub.py
+++ b/jwst/background/background_sub.py
@@ -16,28 +16,39 @@
 
 class ImageSubsetArray:
     """
-    This class is intended to keep track of where different images
-    may relate to each other when using e.g. subarray observations
+    Keep track of where different images relate to each other.
+
+    This includes using e.g. subarray observations.
     """
 
     def __init__(self, model):
+        """
+        Initialize the class.
 
+        Parameters
+        ----------
+        model : ImageModel
+            Input datamodel
+        """
         im = datamodels.open(model)
 
         # Make sure xstart/ystart/xsize/ysize exist in the model metadata
-        if (im.meta.subarray.xstart is None or im.meta.subarray.ystart is None or
-                im.meta.subarray.xsize is None or im.meta.subarray.ysize is None):
-
+        if (
+            im.meta.subarray.xstart is None
+            or im.meta.subarray.ystart is None
+            or im.meta.subarray.xsize is None
+            or im.meta.subarray.ysize is None
+        ):
             # If the ref file is full-frame, set the missing params to
             # default values
-            if im.meta.instrument.name.upper() == 'MIRI':
+            if im.meta.instrument.name.upper() == "MIRI":
                 if im.data.shape[-1] == 1032 and im.data.shape[-2] == 1024:
                     im.meta.subarray.xstart = 1
                     im.meta.subarray.ystart = 1
                     im.meta.subarray.xsize = 1032
                     im.meta.subarray.ysize = 1024
                 else:
-                    raise ValueError('xstart or ystart metadata values not found in model')
+                    raise ValueError("xstart or ystart metadata values not found in model")
             else:
                 if im.data.shape[-1] == 2048 and im.data.shape[-2] == 2048:
                     im.meta.subarray.xstart = 1
@@ -45,7 +56,7 @@ def __init__(self, model):
                     im.meta.subarray.xsize = 2048
                     im.meta.subarray.ysize = 2048
                 else:
-                    raise ValueError('xstart or ystart metadata values not found in model')
+                    raise ValueError("xstart or ystart metadata values not found in model")
 
         # Account for the fact these things are 1-indexed
         self.jmin = im.meta.subarray.ystart - 1
@@ -63,20 +74,46 @@ def __init__(self, model):
         im.close()
 
     def overlaps(self, other):
-        """Find whether this subset and another overlap"""
+        """
+        Find whether this subset and another overlap.
+
+        Parameters
+        ----------
+        other : ImageModel
+            Input model
+
+        Returns
+        -------
+        bool
+            Return the boolean opposite of the condition met
+        """
         return not (
-                self.imax <= other.imin
-                or other.imax <= self.imin
-                or self.jmax <= other.jmin
-                or other.jmax <= self.jmin
+            self.imax <= other.imin
+            or other.imax <= self.imin
+            or self.jmax <= other.jmin
+            or other.jmax <= self.jmin
         )
 
     def get_subset_array(self, other):
         """
-        Pull out the overlapping part of two arrays, and put data/err/DQ
-        to match the science image pixels
-        """
+        Pull out the overlapping SCI, ERR, and DQ data for two models.
+
+        Parameters
+        ----------
+        other : ImageModel
+            Input model
 
+        Returns
+        -------
+        data_overlap : ndarray
+            Data overlap of the science arrays
+
+        err_overlap : ndarray
+            Overlap of the error arrays
+
+        dq_overlap : ndarray
+            Overlap of the DQ arrays
+        """
         imin = max(self.imin, other.imin)
         imax = min(self.imax, other.imax)
         jmin = max(self.jmin, other.jmin)
@@ -102,14 +139,16 @@ def get_subset_array(self, other):
         dq_overlap = np.zeros(data_shape, dtype=np.uint32)
 
         if self.im_dim == 2:
-            idx = (slice(jmin - other.jmin, jmax - other.jmin),
-                   slice(imin - other.imin, imax - other.imin),
-                   )
+            idx = (
+                slice(jmin - other.jmin, jmax - other.jmin),
+                slice(imin - other.imin, imax - other.imin),
+            )
         else:
-            idx = (slice(None, None),
-                   slice(jmin - other.jmin, jmax - other.jmin),
-                   slice(imin - other.imin, imax - other.imin),
-                   )
+            idx = (
+                slice(None, None),
+                slice(jmin - other.jmin, jmax - other.jmin),
+                slice(imin - other.imin, imax - other.imin),
+            )
 
         # Pull the values from the background obs
         data_cutout = other.data[idx]
@@ -117,55 +156,58 @@ def get_subset_array(self, other):
         dq_cutout = other.dq[idx]
 
         # Put them into the right place in the original image array shape
-        data_overlap[..., :data_cutout.shape[-2], :data_cutout.shape[-1]] = copy.deepcopy(data_cutout)
-        err_overlap[..., :data_cutout.shape[-2], :data_cutout.shape[-1]] = copy.deepcopy(err_cutout)
-        dq_overlap[..., :data_cutout.shape[-2], :data_cutout.shape[-1]] = copy.deepcopy(dq_cutout)
+        data_overlap[..., : data_cutout.shape[-2], : data_cutout.shape[-1]] = copy.deepcopy(
+            data_cutout
+        )
+        err_overlap[..., : data_cutout.shape[-2], : data_cutout.shape[-1]] = copy.deepcopy(
+            err_cutout
+        )
+        dq_overlap[..., : data_cutout.shape[-2], : data_cutout.shape[-1]] = copy.deepcopy(dq_cutout)
 
         return data_overlap, err_overlap, dq_overlap
 
 
 def background_sub(input_model, bkg_list, sigma, maxiters):
     """
-    Short Summary
-    -------------
-    Subtract the background signal from a JWST exposure by subtracting
-    the average of one or more background exposures from it.
+    Subtract background signal from an exposure.
+
+    This will be done by subtracting the average of one or more
+    background exposures from it.
 
     Parameters
     ----------
-    input_model : JWST data model
-        input target exposure data model
+    input_model : ImageModel
+        Input target exposure data model
 
-    bkg_list : filename list
-        list of background exposure file names
+    bkg_list : list
+        Filename list of background exposures
 
-    sigma : float, optional
+    sigma : float
         Number of standard deviations to use for both the lower
         and upper clipping limits.
 
-    maxiters : int or None, optional
+    maxiters : int or None
         Maximum number of sigma-clipping iterations to perform
 
     Returns
     -------
-    bkg_model : JWST data model
-        background data model
-
-    result : JWST data model
-        background-subtracted target data model
+    bkg_model : ImageModel
+        Background data model
 
+    result : ImageModel
+        Background-subtracted target data model
     """
-
     # Compute the average of the background images associated with
     # the target exposure
-    bkg_model = average_background(input_model,
-                                   bkg_list,
-                                   sigma,
-                                   maxiters,
-                                   )
+    bkg_model = average_background(
+        input_model,
+        bkg_list,
+        sigma,
+        maxiters,
+    )
 
     # Subtract the average background from the member
-    log.info('Subtracting avg bkg from {}'.format(input_model.meta.filename))
+    log.info(f"Subtracting avg bkg from {input_model.meta.filename}")
 
     result = subtract_images.subtract(input_model, bkg_model)
 
@@ -176,16 +218,17 @@ def background_sub(input_model, bkg_list, sigma, maxiters):
 def average_background(input_model, bkg_list, sigma, maxiters):
     """
     Average multiple background exposures into a combined data model.
+
     Processes backgrounds from various DataModel types, including those
     having 2D (rate) or 3D (rateints) backgrounds.
 
-    Parameters:
-    -----------
-    input_model : JWST data model
-        input target exposure data model
+    Parameters
+    ----------
+    input_model : ImageModel
+        Input target exposure data model
 
-    bkg_list : filename list
-        List of background exposure file names
+    bkg_list : list
+        File name list of background exposures
 
     sigma : float, optional
         Number of standard deviations to use for both the lower
@@ -194,12 +237,11 @@ def average_background(input_model, bkg_list, sigma, maxiters):
     maxiters : int or None, optional
         Maximum number of sigma-clipping iterations to perform
 
-    Returns:
-    --------
-    avg_bkg : data model
+    Returns
+    -------
+    avg_bkg : ImageModel
         The averaged background exposure
     """
-
     # Determine the dimensionality of the input file
     bkg_dim = len(input_model.data.shape)
     image_shape = input_model.data.shape[-2:]
@@ -208,7 +250,7 @@ def average_background(input_model, bkg_list, sigma, maxiters):
 
     avg_bkg = datamodels.ImageModel(image_shape)
     num_bkg = len(bkg_list)
-    cdata = np.zeros(((num_bkg,) + image_shape))
+    cdata = np.zeros((num_bkg,) + image_shape)
     cerr = cdata.copy()
 
     if bkg_dim == 3:
@@ -216,14 +258,14 @@ def average_background(input_model, bkg_list, sigma, maxiters):
 
     # Loop over the images to be used as background
     for i, bkg_file in enumerate(bkg_list):
-        log.info(f'Accumulate bkg from {bkg_file}')
+        log.info(f"Accumulate bkg from {bkg_file}")
 
         bkg_array = ImageSubsetArray(bkg_file)
 
         if not bkg_array.overlaps(im_array):
             # We don't overlap, so put in a bunch of NaNs so sigma-clip
             # isn't affected and move on
-            log.debug(f'{bkg_file} does not overlap input image')
+            log.debug(f"{bkg_file} does not overlap input image")
             cdata[i] = np.ones(image_shape) * np.nan
             cerr[i] = np.ones(image_shape) * np.nan
             continue
@@ -240,7 +282,9 @@ def average_background(input_model, bkg_list, sigma, maxiters):
             # Sigma clip the bkg model's data and err along the integration axis
             with warnings.catch_warnings():
                 # clipping NaNs and infs is the expected behavior
-                warnings.filterwarnings("ignore", category=AstropyUserWarning, message=".*automatically clipped.*")
+                warnings.filterwarnings(
+                    "ignore", category=AstropyUserWarning, message=".*automatically clipped.*"
+                )
                 sc_bkg_data = sigma_clip(bkg_data, sigma=sigma, maxiters=maxiters, axis=0)
                 sc_bkg_err = sigma_clip(bkg_err * bkg_err, sigma=sigma, maxiters=maxiters, axis=0)
 
@@ -254,7 +298,7 @@ def average_background(input_model, bkg_list, sigma, maxiters):
             avg_bkg.dq = np.bitwise_or(avg_bkg.dq, accum_dq_arr)
 
     # Clip the background data
-    log.debug('clip with sigma={} maxiters={}'.format(sigma, maxiters))
+    log.debug(f"clip with sigma={sigma} maxiters={maxiters}")
     mdata = sigma_clip(cdata, sigma=sigma, maxiters=maxiters, axis=0)
 
     # Compute the mean of the non-clipped values
diff --git a/jwst/background/background_sub_wfss.py b/jwst/background/background_sub_wfss.py
index e4768ccca5..67b12f048d 100644
--- a/jwst/background/background_sub_wfss.py
+++ b/jwst/background/background_sub_wfss.py
@@ -8,6 +8,7 @@
 from jwst.assign_wcs.util import create_grism_bbox
 
 import logging
+
 log = logging.getLogger(__name__)
 log.setLevel(logging.DEBUG)
 
@@ -17,31 +18,32 @@ def subtract_wfss_bkg(
     bkg_filename,
     wl_range_name,
     mmag_extract=None,
-    rescaler_kwargs={},
+    rescaler_kwargs=None,
 ):
-    """Scale and subtract a background reference image from WFSS/GRISM data.
+    """
+    Scale and subtract a background reference image from WFSS/GRISM data.
 
     Parameters
     ----------
-    input_model : JWST data model
-        input target exposure data model
+    input_model : ImageModel
+        Input target exposure data model
 
     bkg_filename : str
-        name of master background file for WFSS/GRISM
+        Name of master background file for WFSS/GRISM
 
     wl_range_name : str
-        name of wavelengthrange reference file
+        Name of wavelength range reference file
 
     mmag_extract : float, optional, default None
-        minimum abmag of grism objects to extract
+        Minimum abmag of grism objects to extract
 
-    rescaler_kwargs : dict, optional, default {}
+    rescaler_kwargs : dict, optional, default None
         Keyword arguments to pass to ScalingFactorComputer
 
     Returns
     -------
-    result : JWST data model
-        background-subtracted target data model
+    result : ImageModel
+        Background-subtracted target data model
     """
     bkg_ref = datamodels.open(bkg_filename)
 
@@ -49,10 +51,14 @@ def subtract_wfss_bkg(
     try:
         dispaxis = input_model.meta.wcsinfo.dispersion_direction
     except AttributeError:
-        log.warning("Dispersion axis not found in input science image metadata. "
-                    "Variance stopping criterion will have no effect for iterative "
-                    "outlier rejection (will run until maxiter).")
+        log.warning(
+            "Dispersion axis not found in input science image metadata. "
+            "Variance stopping criterion will have no effect for iterative "
+            "outlier rejection (will run until maxiter)."
+        )
         dispaxis = None
+    if rescaler_kwargs is None:
+        rescaler_kwargs = {}
     rescaler_kwargs["dispersion_axis"] = dispaxis
 
     # get the source catalog for masking
@@ -77,12 +83,12 @@ def subtract_wfss_bkg(
     log.info("Starting iterative outlier rejection for background subtraction.")
     rescaler = _ScalingFactorComputer(**rescaler_kwargs)
     with warnings.catch_warnings():
-        warnings.filterwarnings("ignore",
-                                category=RuntimeWarning,
-                                message="All-NaN slice encountered")
+        warnings.filterwarnings(
+            "ignore", category=RuntimeWarning, message="All-NaN slice encountered"
+        )
         # copy to avoid propagating NaNs from iterative clipping into final product
         sci = input_model.data.copy()
-        var = input_model.err.copy()**2
+        var = input_model.err.copy() ** 2
         bkg = bkg_ref.data.copy()
         factor, _ = rescaler(sci, bkg, var, mask=~bkg_mask)
 
@@ -102,31 +108,37 @@ def subtract_wfss_bkg(
 
 
 class _ScalingFactorComputer:
-
     def __init__(self, p=1.0, maxiter=5, delta_rms_thresh=0, dispersion_axis=None):
         """
+        Initialize the class.
+
         Parameters
         ----------
-        p: float, optional
+        p : float, optional
             Percentile for sigma clipping on both low and high ends per iteration, default 1.0.
             For example, with p=2.0, the middle 96% of the data is kept.
-        maxiter: int, optional
+
+        maxiter : int, optional
             Maximum number of iterations for outlier rejection. Default 5.
-        delta_rms_thresh: float, optional
+
+        delta_rms_thresh : float, optional
             Stopping criterion for outlier rejection; stops when the rms residuals
             change by less than this fractional threshold in a single iteration.
             For example, assuming delta_rms_thresh=0.1 and a residual RMS of 100
             in iteration 1, the iteration will stop if the RMS residual in iteration
             2 is greater than 90.
             Default 0.0, i.e., ignore this and only stop at maxiter.
-        dispersion_axis: int, optional
+
+        dispersion_axis : int, optional
             The index to select the along-dispersion axis. Used to compute the RMS
             residual, so must be set if rms_thresh > 0. Default None.
         """
-        if (delta_rms_thresh > 0) and (dispersion_axis not in [1,2]):
-            msg = (f"Unrecognized dispersion axis {dispersion_axis}. "
-                    "Dispersion axis must be specified if delta_rms_thresh "
-                    "is used as a stopping criterion.")
+        if (delta_rms_thresh > 0) and (dispersion_axis not in [1, 2]):
+            msg = (
+                f"Unrecognized dispersion axis {dispersion_axis}. "
+                "Dispersion axis must be specified if delta_rms_thresh "
+                "is used as a stopping criterion."
+            )
             raise ValueError(msg)
 
         self.p = p
@@ -134,26 +146,31 @@ def __init__(self, p=1.0, maxiter=5, delta_rms_thresh=0, dispersion_axis=None):
         self.delta_rms_thresh = delta_rms_thresh
         self.dispersion_axis = dispersion_axis
 
-
     def __call__(self, sci, bkg, var, mask=None):
         """
+        Call function for the class.
+
         Parameters
         ----------
-        sci: ndarray
+        sci : ndarray
             The science data.
-        bkg: ndarray
+
+        bkg : ndarray
             The reference background model.
-        var: ndarray
+
+        var : ndarray
             Total variance (error squared) of the science data.
-        mask: ndarray[bool], optional
+
+        mask : ndarray[bool], optional
             Initial mask to be applied to the data, True where bad.
-            Typically this would mask out the real sources in the data.
+            Typically, this would mask out the real sources in the data.
 
         Returns
         -------
         float
             Scaling factor that minimizes sci - factor*bkg,
             taking into account residuals and outliers.
+
         ndarray[bool]
             Outlier mask generated by the iterative clipping procedure.
         """
@@ -164,24 +181,27 @@ def __call__(self, sci, bkg, var, mask=None):
         # iteratively reject more and more outliers
         i = 0
         last_rms_resid = np.inf
-        while (i < self.maxiter):
-
+        while i < self.maxiter:
             # compute the factor that minimizes the residuals
             factor = self.err_weighted_mean(sci, bkg, var)
-            sci_sub = sci-factor*bkg
+            sci_sub = sci - factor * bkg
 
             # Check fractional improvement stopping criterion before incrementing.
             # Note this never passes in iteration 0 because last_rms_resid is inf.
             if self.delta_rms_thresh > 0:
                 rms_resid = self._compute_rms_residual(sci_sub)
                 with warnings.catch_warnings():
-                    warnings.filterwarnings("ignore",
-                                            category=RuntimeWarning,
-                                            message="invalid value encountered in scalar divide")
-                    fractional_diff = (last_rms_resid - rms_resid)/last_rms_resid
+                    warnings.filterwarnings(
+                        "ignore",
+                        category=RuntimeWarning,
+                        message="invalid value encountered in scalar divide",
+                    )
+                    fractional_diff = (last_rms_resid - rms_resid) / last_rms_resid
                 if fractional_diff < self.delta_rms_thresh:
-                    msg = (f"Stopping at iteration {i}; too little improvement "
-                           "since last iteration (hit delta_rms_thresh).")
+                    msg = (
+                        f"Stopping at iteration {i}; too little improvement "
+                        "since last iteration (hit delta_rms_thresh)."
+                    )
                     log.info(msg)
                     break
                 last_rms_resid = rms_resid
@@ -191,7 +211,7 @@ def __call__(self, sci, bkg, var, mask=None):
             # Reject outliers based on residual between sci and bkg.
             # Updating the sci, var, and bkg nan values means that
             # they are ignored by nanpercentile in the next iteration
-            limits = np.nanpercentile(sci_sub, (self.p, 100-self.p))
+            limits = np.nanpercentile(sci_sub, (self.p, 100 - self.p))
             mask += np.logical_or(sci_sub < limits[0], sci_sub > limits[1])
             self._update_nans(sci, bkg, var, mask)
 
@@ -201,52 +221,98 @@ def __call__(self, sci, bkg, var, mask=None):
         self._iters_run_last_call = i
         return self.err_weighted_mean(sci, bkg, var), mask
 
-
     def err_weighted_mean(self, sci, bkg, var):
-        """Remove any var=0 values, which can happen for real data"""
-        mask = (var == 0)
-        self._update_nans(sci, bkg, var, mask)
-        return np.nansum(sci*bkg/var, dtype='f8') / np.nansum(bkg*bkg/var, dtype='f8')
+        """
+        Remove any var=0 values, which can happen for real data.
 
+        Parameters
+        ----------
+        sci : ndarray
+            The science data.
+
+        bkg : ndarray
+            The reference background model.
+
+        var : ndarray
+            Total variance (error squared) of the science data.
+
+        Returns
+        -------
+        ndarray
+            New array with the weighted sum of array elements
+        """
+        mask = var == 0
+        self._update_nans(sci, bkg, var, mask)
+        return np.nansum(sci * bkg / var, dtype="f8") / np.nansum(bkg * bkg / var, dtype="f8")
 
     def _update_nans(self, sci, bkg, var, mask):
         sci[mask] = np.nan
         bkg[mask] = np.nan
         var[mask] = np.nan
 
-
     def _compute_rms_residual(self, sci_sub):
         """
-        Calculate the background-subtracted RMS along the dispersion axis, which is found
-        by taking the median profile of the image collapsed along the cross-dispersion axis.
-        Note meta.wcsinfo.dispersion_axis is 1-indexed coming out of assign_wcs, i.e., in [1,2].
-        So we need to """
+        Calculate the background-subtracted RMS along the dispersion axis.
+
+        This axis is found by taking the median profile of the image collapsed
+        along the cross-dispersion axis.
+
+        Note: meta.wcsinfo.dispersion_axis is 1-indexed coming out of assign_wcs, i.e., in [1,2].
+
+        Parameters
+        ----------
+        sci_sub : ndarray
+            Scaled down science data.
+
+        Returns
+        -------
+        float
+            Root mean square
+        """
         collapsing_axis = int(self.dispersion_axis - 1)
         sci_sub_profile = np.nanmedian(sci_sub, axis=collapsing_axis)
-        return np.sqrt(np.nanmean(sci_sub_profile**2, dtype='f8'))
+        return np.sqrt(np.nanmean(sci_sub_profile**2, dtype="f8"))
 
 
 def _sufficient_background_pixels(dq_array, bkg_mask, min_pixels=100):
-    """Count number of good pixels for background use.
+    """
+    Count number of good pixels for background use.
 
     Check DQ flags of pixels selected for bkg use - XOR the DQ values with
     the DO_NOT_USE flag to flip the DO_NOT_USE bit. Then count the number
     of pixels that AND with the DO_NOT_USE flag, i.e. initially did not have
     the DO_NOT_USE bit set.
+
+    Parameters
+    ----------
+    dq_array : ndarray
+        Subarray input DQ array
+
+    bkg_mask : ndarray
+        Boolean background mask
+
+    min_pixels : int, optional
+        Minimum number of pixels to use
+
+    Returns
+    -------
+    int or array of int
+        The number of good pixels for background use.
     """
-    return np.count_nonzero((dq_array[bkg_mask]
-                            ^ pixel['DO_NOT_USE'])
-                            & pixel['DO_NOT_USE']
-                            ) > min_pixels
+    return (
+        np.count_nonzero((dq_array[bkg_mask] ^ pixel["DO_NOT_USE"]) & pixel["DO_NOT_USE"])
+        > min_pixels
+    )
 
 
 def _mask_from_source_cat(input_model, wl_range_name, mmag_extract=None):
-    """Create a mask that is False within bounding boxes of sources.
+    """
+    Create a mask that is False within bounding boxes of sources.
 
     Parameters
     ----------
-    input_model : JWST data model
-        input target exposure data model
+    input_model : ImageModel
+        Input target exposure data model
 
     wl_range_name : str
         Name of the wavelengthrange reference file
@@ -261,7 +327,6 @@ def _mask_from_source_cat(input_model, wl_range_name, mmag_extract=None):
         inside at least one of the source regions defined in the source
         catalog.
     """
-
     shape = input_model.data.shape
     bkg_mask = np.ones(shape, dtype=bool)
 
diff --git a/jwst/background/subtract_images.py b/jwst/background/subtract_images.py
index 95bdbe01cf..0ec60914cc 100644
--- a/jwst/background/subtract_images.py
+++ b/jwst/background/subtract_images.py
@@ -1,31 +1,28 @@
 import numpy as np
 
 import logging
+
 log = logging.getLogger(__name__)
 log.setLevel(logging.DEBUG)
 
 
 def subtract(model1, model2):
     """
-    Short Summary
-    -------------
-    Subtract one data model from another, and include updated DQ
-    in output.
+    Subtract one data model from another, and include updated DQ in output.
 
     Parameters
     ----------
-    model1: JWST data model
-        input data model on which subtraction will be performed
+    model1 : ImageModel or IFUImageModel
+        Input data model on which subtraction will be performed
 
-    model2: JWST data model
-        input data model that will be subtracted from the first model
+    model2 : ImageModel or IFUImageModel
+        Input data model that will be subtracted from the first model
 
     Returns
     -------
-    output: JWST data model
-        subtracted data model
+    output : ImageModel or IFUImageModel
+        Subtracted data model
     """
-
     # Create the output model as a copy of the first input
     output = model1.copy()
 
diff --git a/jwst/background/tests/conftest.py b/jwst/background/tests/conftest.py
index 9628e19a0e..da7d4e9f9e 100644
--- a/jwst/background/tests/conftest.py
+++ b/jwst/background/tests/conftest.py
@@ -4,4 +4,12 @@
 
 @pytest.fixture(scope="module")
 def data_path():
+    """
+    Define the data path for testing.
+
+    Returns
+    -------
+    pathlib object
+        The path to where the data lives
+    """
     return pathlib.Path(__file__).parent / "data"


Reviews:
user: login: kmacdonald-stsci
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T22:56:11Z
user: login: melanieclarke
body: Looking good!  I noticed a few places where the Parameters section was missing in docstrings -- the linter doesn't catch that for some reason, but we should still add them.  A few other minor suggestions as well.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T21:04:56Z
user: login: melanieclarke
body: Looks like there is a formatting error that pre-commit is picking up.  Otherwise, I think all comments are addressed and this looks good now.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T14:35:40Z
user: login: melanieclarke
body: LGTM!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-17T14:49:20Z

Review comments:
diff_hunk: @@ -63,20 +66,36 @@ def __init__(self, model):
         im.close()
 
     def overlaps(self, other):
-        """Find whether this subset and another overlap"""
+        """
+        Find whether this subset and another overlap.
+
+        Returns
+        -------
+        bool
+            Return the boolean opposite of the condition met
+        """
         return not (
-                self.imax <= other.imin
-                or other.imax <= self.imin
-                or self.jmax <= other.jmin
-                or other.jmax <= self.jmin
+            self.imax <= other.imin
+            or other.imax <= self.imin
+            or self.jmax <= other.jmin
+            or other.jmax <= self.jmin
         )
 
     def get_subset_array(self, other):
         """
-        Pull out the overlapping part of two arrays, and put data/err/DQ
-        to match the science image pixels
-        """
+        Pull out the overlapping part of two arrays. Match err/DQ to science image pixels.
path: jwst/background/background_sub.py
user: login: melanieclarke
body: The summary line should be a single sentence.  Maybe like this:
```suggestion
        Pull out the overlapping SCI, ERR, and DQ data for two models.
```
created_at: 2025-04-07T20:51:39Z
author_association: COLLABORATOR
diff_hunk: @@ -16,36 +16,39 @@
 
 class ImageSubsetArray:
     """
-    This class is intended to keep track of where different images
-    may relate to each other when using e.g. subarray observations
+    Keep track of where different images relate to each other.
+
+    This includes using e.g. subarray observations.
     """
 
     def __init__(self, model):
path: jwst/background/background_sub.py
user: login: melanieclarke
body: Needs a docstring, including the `model` parameter.
created_at: 2025-04-07T20:52:49Z
author_association: COLLABORATOR
diff_hunk: @@ -63,20 +66,36 @@ def __init__(self, model):
         im.close()
 
     def overlaps(self, other):
-        """Find whether this subset and another overlap"""
+        """
+        Find whether this subset and another overlap.
+
path: jwst/background/background_sub.py
user: login: melanieclarke
body: Needs a `Parameters` section
created_at: 2025-04-07T20:53:05Z
author_association: COLLABORATOR
diff_hunk: @@ -63,20 +66,36 @@ def __init__(self, model):
         im.close()
 
     def overlaps(self, other):
-        """Find whether this subset and another overlap"""
+        """
+        Find whether this subset and another overlap.
+
+        Returns
+        -------
+        bool
+            Return the boolean opposite of the condition met
+        """
         return not (
-                self.imax <= other.imin
-                or other.imax <= self.imin
-                or self.jmax <= other.jmin
-                or other.jmax <= self.jmin
+            self.imax <= other.imin
+            or other.imax <= self.imin
+            or self.jmax <= other.jmin
+            or other.jmax <= self.jmin
         )
 
     def get_subset_array(self, other):
         """
-        Pull out the overlapping part of two arrays, and put data/err/DQ
-        to match the science image pixels
-        """
+        Pull out the overlapping part of two arrays. Match err/DQ to science image pixels.
+
path: jwst/background/background_sub.py
user: login: melanieclarke
body: Needs a `Parameters` section
created_at: 2025-04-07T20:53:18Z
author_association: COLLABORATOR
diff_hunk: @@ -102,42 +121,48 @@ def get_subset_array(self, other):
         dq_overlap = np.zeros(data_shape, dtype=np.uint32)
 
         if self.im_dim == 2:
-            idx = (slice(jmin - other.jmin, jmax - other.jmin),
-                   slice(imin - other.imin, imax - other.imin),
-                   )
+            idx = (
+                slice(jmin - other.jmin, jmax - other.jmin),
+                slice(imin - other.imin, imax - other.imin),
+            )
         else:
-            idx = (slice(None, None),
-                   slice(jmin - other.jmin, jmax - other.jmin),
-                   slice(imin - other.imin, imax - other.imin),
-                   )
+            idx = (
+                slice(None, None),
+                slice(jmin - other.jmin, jmax - other.jmin),
+                slice(imin - other.imin, imax - other.imin),
+            )
 
         # Pull the values from the background obs
         data_cutout = other.data[idx]
         err_cutout = other.err[idx]
         dq_cutout = other.dq[idx]
 
         # Put them into the right place in the original image array shape
-        data_overlap[..., :data_cutout.shape[-2], :data_cutout.shape[-1]] = copy.deepcopy(data_cutout)
-        err_overlap[..., :data_cutout.shape[-2], :data_cutout.shape[-1]] = copy.deepcopy(err_cutout)
-        dq_overlap[..., :data_cutout.shape[-2], :data_cutout.shape[-1]] = copy.deepcopy(dq_cutout)
+        data_overlap[..., : data_cutout.shape[-2], : data_cutout.shape[-1]] = copy.deepcopy(
+            data_cutout
+        )
+        err_overlap[..., : data_cutout.shape[-2], : data_cutout.shape[-1]] = copy.deepcopy(
+            err_cutout
+        )
+        dq_overlap[..., : data_cutout.shape[-2], : data_cutout.shape[-1]] = copy.deepcopy(dq_cutout)
 
         return data_overlap, err_overlap, dq_overlap
 
 
 def background_sub(input_model, bkg_list, sigma, maxiters):
     """
-    Short Summary
-    -------------
-    Subtract the background signal from a JWST exposure by subtracting
-    the average of one or more background exposures from it.
+    Subtract background signal from an exposure.
+
+    This will be done by subtracting the average of one or more
+    background exposures from it.
 
     Parameters
     ----------
-    input_model : JWST data model
-        input target exposure data model
+    input_model : ImageModel
+        Input target exposure data model
 
-    bkg_list : filename list
-        list of background exposure file names
+    bkg_list : list
+        Filename list of background exposures
 
     sigma : float, optional
path: jwst/background/background_sub.py
user: login: melanieclarke
body: Looks like sigma and maxiters are not actually optional, here and in the `average_background` function
created_at: 2025-04-07T20:55:26Z
author_association: COLLABORATOR
diff_hunk: @@ -201,52 +221,71 @@ def __call__(self, sci, bkg, var, mask=None):
         self._iters_run_last_call = i
         return self.err_weighted_mean(sci, bkg, var), mask
 
-
     def err_weighted_mean(self, sci, bkg, var):
-        """Remove any var=0 values, which can happen for real data"""
-        mask = (var == 0)
-        self._update_nans(sci, bkg, var, mask)
-        return np.nansum(sci*bkg/var) / np.nansum(bkg*bkg/var)
+        """
+        Remove any var=0 values, which can happen for real data.
 
+        Returns
path: jwst/background/background_sub_wfss.py
user: login: melanieclarke
body: Needs Parameters
created_at: 2025-04-07T21:02:05Z
author_association: COLLABORATOR
diff_hunk: @@ -201,52 +221,71 @@ def __call__(self, sci, bkg, var, mask=None):
         self._iters_run_last_call = i
         return self.err_weighted_mean(sci, bkg, var), mask
 
-
     def err_weighted_mean(self, sci, bkg, var):
-        """Remove any var=0 values, which can happen for real data"""
-        mask = (var == 0)
-        self._update_nans(sci, bkg, var, mask)
-        return np.nansum(sci*bkg/var) / np.nansum(bkg*bkg/var)
+        """
+        Remove any var=0 values, which can happen for real data.
 
+        Returns
+        -------
+        ndarray
+            New array with the weighted sum of array elements
+        """
+        mask = var == 0
+        self._update_nans(sci, bkg, var, mask)
+        return np.nansum(sci * bkg / var) / np.nansum(bkg * bkg / var)
 
     def _update_nans(self, sci, bkg, var, mask):
         sci[mask] = np.nan
         bkg[mask] = np.nan
         var[mask] = np.nan
 
-
     def _compute_rms_residual(self, sci_sub):
         """
-        Calculate the background-subtracted RMS along the dispersion axis, which is found
-        by taking the median profile of the image collapsed along the cross-dispersion axis.
-        Note meta.wcsinfo.dispersion_axis is 1-indexed coming out of assign_wcs, i.e., in [1,2].
-        So we need to """
+        Calculate the background-subtracted RMS along the dispersion axis.
+
+        This axis is found by taking the median profile of the image collapsed
+        along the cross-dispersion axis.
+
+        Note: meta.wcsinfo.dispersion_axis is 1-indexed coming out of assign_wcs, i.e., in [1,2].
+
path: jwst/background/background_sub_wfss.py
user: login: melanieclarke
body: Needs Parameters
created_at: 2025-04-07T21:02:16Z
author_association: COLLABORATOR
diff_hunk: @@ -1,31 +1,28 @@
 import numpy as np
 
 import logging
+
 log = logging.getLogger(__name__)
 log.setLevel(logging.DEBUG)
 
 
 def subtract(model1, model2):
     """
-    Short Summary
-    -------------
-    Subtract one data model from another, and include updated DQ
-    in output.
+    Subtract one data model from another, and include updated DQ in output.
 
     Parameters
     ----------
-    model1: JWST data model
-        input data model on which subtraction will be performed
+    model1 : ImageModel or IFUImageModel
+        Input data model on which subtraction will be performed
 
-    model2: JWST data model
-        input data model that will be subtracted from the first model
+    model2 : JWST data model
path: jwst/background/subtract_images.py
user: login: melanieclarke
body: ```suggestion
    model2 : ImageModel or IFUImageModel
```
created_at: 2025-04-07T21:03:13Z
author_association: COLLABORATOR

PR comments:
user: login: codecov[bot]
created_at: 2025-04-03T20:12:14Z
body: ## [Codecov](https://app.codecov.io/gh/spacetelescope/jwst/pull/9359?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) Report
Attention: Patch coverage is `84.61538%` with `8 lines` in your changes missing coverage. Please review.
> Project coverage is 75.37%. Comparing base [(`aa19dfa`)](https://app.codecov.io/gh/spacetelescope/jwst/commit/aa19dfa254aaf65335db530297298e6e8b29d0e9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) to head [(`233ff95`)](https://app.codecov.io/gh/spacetelescope/jwst/commit/233ff95b081195e3fd606c44435970cb72db795d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope).
> Report is 682 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/spacetelescope/jwst/pull/9359?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) | Patch % | Lines |
|---|---|---|
| [jwst/background/background\_sub.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9359?src=pr&el=tree&filepath=jwst%2Fbackground%2Fbackground_sub.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9iYWNrZ3JvdW5kL2JhY2tncm91bmRfc3ViLnB5) | 75.00% | [4 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9359?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/background/background\_step.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9359?src=pr&el=tree&filepath=jwst%2Fbackground%2Fbackground_step.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9iYWNrZ3JvdW5kL2JhY2tncm91bmRfc3RlcC5weQ==) | 82.35% | [3 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9359?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/background/background\_sub\_wfss.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9359?src=pr&el=tree&filepath=jwst%2Fbackground%2Fbackground_sub_wfss.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9iYWNrZ3JvdW5kL2JhY2tncm91bmRfc3ViX3dmc3MucHk=) | 94.44% | [1 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9359?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #9359      +/-   ##
==========================================
- Coverage   75.41%   75.37%   -0.04%     
==========================================
  Files         368      368              
  Lines       36856    36885      +29     
==========================================
+ Hits        27794    27802       +8     
- Misses       9062     9083      +21     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/spacetelescope/jwst/pull/9359?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: penaguerrero
created_at: 2025-04-04T16:47:36Z
body: reg test running: https://github.com/spacetelescope/RegressionTests/actions/runs/14270183009
author_association: CONTRIBUTOR
user: login: melanieclarke
created_at: 2025-04-10T15:16:31Z
body: @penaguerrero - can you please fix the conflict in background_sub_wfss.py?
author_association: COLLABORATOR