Repository: spacetelescope/jwst
Pull request #9290: JP-3858-scripts
Author: login: penaguerrero
Labels: name: FGS
name: scripts
name: installation
name: no-changelog-entry-needed
Created: 2025-03-17T16:54:18Z | Merged: 2025-04-30T16:20:12Z

<!-- 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)
Resolves [JP-3971](https://jira.stsci.edu/browse/JP-3971)

<!-- If this PR closes a GitHub issue, reference it here by its number -->
Closes #

<!-- describe the changes comprising this PR here -->
This PR partially addresses JP-3858, this time for the scripts directory.

<!-- if you can't perform these tasks due to permissions, please ask a maintainer to do them -->
## Tasks
- [ ] **request a review from someone specific**, to avoid making the maintainers review every PR
- [x] add a build milestone, i.e. `Build 11.3` (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 below for change types) 
  - [ ] 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)

<details><summary>news fragment change types...</summary>

- ``changes/<PR#>.general.rst``: infrastructure or miscellaneous change
- ``changes/<PR#>.docs.rst``
- ``changes/<PR#>.stpipe.rst``
- ``changes/<PR#>.datamodels.rst``
- ``changes/<PR#>.scripts.rst``
- ``changes/<PR#>.set_telescope_pointing.rst``
- ``changes/<PR#>.pipeline.rst``

## stage 1
- ``changes/<PR#>.group_scale.rst``
- ``changes/<PR#>.dq_init.rst``
- ``changes/<PR#>.emicorr.rst``
- ``changes/<PR#>.saturation.rst``
- ``changes/<PR#>.ipc.rst``
- ``changes/<PR#>.firstframe.rst``
- ``changes/<PR#>.lastframe.rst``
- ``changes/<PR#>.reset.rst``
- ``changes/<PR#>.superbias.rst``
- ``changes/<PR#>.refpix.rst``
- ``changes/<PR#>.linearity.rst``
- ``changes/<PR#>.rscd.rst``
- ``changes/<PR#>.persistence.rst``
- ``changes/<PR#>.dark_current.rst``
- ``changes/<PR#>.charge_migration.rst``
- ``changes/<PR#>.jump.rst``
- ``changes/<PR#>.clean_flicker_noise.rst``
- ``changes/<PR#>.ramp_fitting.rst``
- ``changes/<PR#>.gain_scale.rst``

## stage 2
- ``changes/<PR#>.assign_wcs.rst``
- ``changes/<PR#>.badpix_selfcal.rst``
- ``changes/<PR#>.msaflagopen.rst``
- ``changes/<PR#>.nsclean.rst``
- ``changes/<PR#>.imprint.rst``
- ``changes/<PR#>.background.rst``
- ``changes/<PR#>.extract_2d.rst``
- ``changes/<PR#>.master_background.rst``
- ``changes/<PR#>.wavecorr.rst``
- ``changes/<PR#>.srctype.rst``
- ``changes/<PR#>.straylight.rst``
- ``changes/<PR#>.wfss_contam.rst``
- ``changes/<PR#>.flatfield.rst``
- ``changes/<PR#>.fringe.rst``
- ``changes/<PR#>.pathloss.rst``
- ``changes/<PR#>.barshadow.rst``
- ``changes/<PR#>.photom.rst``
- ``changes/<PR#>.pixel_replace.rst``
- ``changes/<PR#>.resample_spec.rst``
- ``changes/<PR#>.residual_fringe.rst``
- ``changes/<PR#>.cube_build.rst``
- ``changes/<PR#>.extract_1d.rst``
- ``changes/<PR#>.resample.rst``

## stage 3
- ``changes/<PR#>.assign_mtwcs.rst``
- ``changes/<PR#>.mrs_imatch.rst``
- ``changes/<PR#>.tweakreg.rst``
- ``changes/<PR#>.skymatch.rst``
- ``changes/<PR#>.exp_to_source.rst``
- ``changes/<PR#>.outlier_detection.rst``
- ``changes/<PR#>.tso_photometry.rst``
- ``changes/<PR#>.stack_refs.rst``
- ``changes/<PR#>.align_refs.rst``
- ``changes/<PR#>.klip.rst``
- ``changes/<PR#>.spectral_leak.rst``
- ``changes/<PR#>.source_catalog.rst``
- ``changes/<PR#>.combine_1d.rst``
- ``changes/<PR#>.ami.rst``

## other
- ``changes/<PR#>.wfs_combine.rst``
- ``changes/<PR#>.white_light.rst``
- ``changes/<PR#>.cube_skymatch.rst``
- ``changes/<PR#>.engdb_tools.rst``
- ``changes/<PR#>.guider_cds.rst``
</details>



Diff:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d5b1420d06c..198ba7122dc 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -47,7 +47,6 @@ repos:
             jwst/mrs_imatch/.* |
             jwst/persistence/.* |
             jwst/photom/.* |
-            jwst/scripts/.* |
             jwst/tests/.* |
             docs/.* |
             jwst/regtest/test_.* |
diff --git a/.ruff.toml b/.ruff.toml
index 25ab4dad2dd..9998dacb79e 100644
--- a/.ruff.toml
+++ b/.ruff.toml
@@ -29,7 +29,6 @@ exclude = [
     "jwst/mrs_imatch/**.py",
     "jwst/persistence/**.py",
     "jwst/photom/**.py",
-    "jwst/scripts/**.py",
 ]
 
 [lint]
@@ -120,4 +119,3 @@ ignore-fully-untyped = true  # Turn off annotation checking for fully untyped co
 "jwst/mrs_imatch/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
 "jwst/persistence/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
 "jwst/photom/**.py" = ["D", "N", "A", "ARG", "B", "C4", "ICN", "INP", "ISC", "LOG", "NPY", "PGH", "PTH", "S", "SLF", "SLOT", "T20", "TRY", "UP", "YTT", "E501"]
-"jwst/scripts/**.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/scripts/__init__.py b/jwst/scripts/__init__.py
index e69de29bb2d..9417b8fbe87 100644
--- a/jwst/scripts/__init__.py
+++ b/jwst/scripts/__init__.py
@@ -0,0 +1 @@
+"""Auxiliary command-line scripts."""
diff --git a/jwst/scripts/adjust_wcs.py b/jwst/scripts/adjust_wcs.py
index 023d46971ee..935a3fd476d 100755
--- a/jwst/scripts/adjust_wcs.py
+++ b/jwst/scripts/adjust_wcs.py
@@ -1,53 +1,9 @@
 #!/usr/bin/env python
-# Copyright (C) 2023 Association of Universities for Research in Astronomy (AURA)
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-#     1. Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-#
-#     2. Redistributions in binary form must reproduce the above
-#       copyright notice, this list of conditions and the following
-#       disclaimer in the documentation and/or other materials provided
-#       with the distribution.
-#
-#     3. The name of AURA and its representatives may not be used to
-#       endorse or promote products derived from this software without
-#       specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
-# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-# DAMAGE.
-#
-"""
-Apply adjustments to an imaging JWST GWCS object. Input files can be
-``DataModel``s serialized to ASDF or ASDF-in-FITS files, or they could be
-"simple" ASDF files storing just the GWCS model in the root of the ASDF file
-under the key ``'wcs'``, i.e., ``asdf_file.tree['wcs']``.
-
-Examples
---------
-
-From command line::
-
-    % adjust_wcs [-h] (-u | --suffix SUFFIX | -f FILE) [--overwrite]
-                 [-r RA_DELTA [units]] [-d DEC_DELTA [units]]
-                 [-o ROLL_DELTA [units]] [-s SCALE_FACTOR]
-                 [-v] [input ...]
-
-    % adjust_wcs data_model_*_cal.fits -u -s 1.002
-
-    % adjust_wcs data_model_*_cal.fits --suffix wcsadj -s 1.002 -r 0.2 arcsec -o 0.0023 deg
-"""
+
+"""Apply adjustments to data model's WCS."""
+
+# Licensed under a 3-clause BSD style license - see LICENSE
+
 import argparse
 import glob
 import logging
@@ -62,7 +18,6 @@
 
 _ANGLE_PARS = ["-r", "--ra_delta", "-d", "--dec_delta", "-o", "--roll_delta"]
 
-
 # Configure logging
 logger = logging.getLogger(__name__)
 logger.setLevel(logging.INFO)
@@ -70,43 +25,58 @@
 
 
 def _replace_suffix(file, new_suffix):
-    sep = '_'
+    sep = "_"
     directory, base = os.path.split(file)
-    root, ext = os.path.splitext(base)
+    root, ext = os.path.splitext(base)  # noqa: PTH122
     if sep in root:
         name_parts = root.rpartition(sep)
-        root = ''.join(name_parts[:-1] + (new_suffix, ))
+        root = "".join(name_parts[:-1] + (new_suffix,))
     else:
         root = root + sep + new_suffix
     base = root + ext
 
-    new_file_name = os.path.join(directory, base)
+    new_file_name = os.path.join(directory, base)  # noqa: PTH118
 
     return new_file_name
 
 
 def _is_float(arg):
+    is_float = False
     try:
         float(arg)
-        return True
+        is_float = True
     except ValueError:
         pass
-    return False
+    return is_float
 
 
 def _is_unit(arg):
+    is_unit = False
     if arg.startswith("-"):
         return False
     try:
         units.Unit(arg)
-        return True
+        is_unit = True
     except ValueError:
         pass
-    return False
+    return is_unit
 
 
 def angle(arg):
-    args = arg.strip().split(' ')
+    """
+    Parse the angle argument to get units and convert to float.
+
+    Parameters
+    ----------
+    arg : str
+        User angle input
+
+    Returns
+    -------
+    value : float or astropy.whatever.Quantity
+        Parsed value and units for the given angle.
+    """
+    args = arg.strip().split(" ")
     if len(args) == 1:
         return float(args[0])
     elif len(args) == 2:
@@ -116,96 +86,62 @@ def angle(arg):
 
 
 def main():
+    """Apply adjustments to an imaging JWST GWCS object."""
     if len(sys.argv) <= 1:
         raise ValueError("Missing required arguments.")
 
     # Parse input parameters
     parser = argparse.ArgumentParser(
         prog="adjust_wcs",
-        description="Apply adjustments to data model's WCS"
+        description="""Apply adjustments to data model's WCS. Input files can
+        be DataModel's serialized to ASDF or ASDF-in-FITS files, or they could be "simple"
+        ASDF files storing just the GWCS model in the root of the ASDF file under the key
+        'wcs', i.e., asdf_file.tree['wcs'].
+
+        Examples from command line:
+
+        $ adjust_wcs data_model_*_cal.fits -u -s 1.002
+
+        $ adjust_wcs data_model_*_cal.fits --suffix wcsadj -s 1.002 -r 0.2 arcsec -o 0.0023 deg""",
     )
 
     parser.add_argument(
-        "arg0",
-        nargs='*',
-        metavar="input",
-        type=str,
-        help="Input data model file(s)."
+        "arg0", nargs="*", metavar="input", type=str, help="Input data model file(s)."
     )
 
     group = parser.add_mutually_exclusive_group(required=True)
-    group.add_argument(
-        "-u",
-        "--update",
-        action="store_true",
-        help="Update input data models."
-    )
-    group.add_argument(
-        "--suffix",
-        type=str,
-        help="Suffix for output files."
-    )
-    group.add_argument(
-        "-f",
-        "--file",
-        type=str,
-        help="Output file name."
-    )
+    group.add_argument("-u", "--update", action="store_true", help="Update input data models.")
+    group.add_argument("--suffix", type=str, help="Suffix for output files.")
+    group.add_argument("-f", "--file", type=str, help="Output file name.")
 
     parser.add_argument(
         "--overwrite",
         action="store_true",
-        help="Overwrite existing output files (ignored with -u/--update)."
+        help="Overwrite existing output files (ignored with -u/--update).",
     )
 
-    group_wcs = parser.add_argument_group(
-        title='WCS adjustment parameters'
-    )
+    group_wcs = parser.add_argument_group(title="WCS adjustment parameters")
     group_wcs.add_argument(
-        "-r",
-        "--ra_delta",
-        type=angle,
-        default=0.0,
-        help="Delta RA (longitude), degrees."
+        "-r", "--ra_delta", type=angle, default=0.0, help="Delta RA (longitude), degrees."
     )
     group_wcs.add_argument(
-        "-d",
-        "--dec_delta",
-        type=angle,
-        default=0.0,
-        help="Delta DEC (latitude), degrees."
+        "-d", "--dec_delta", type=angle, default=0.0, help="Delta DEC (latitude), degrees."
     )
     group_wcs.add_argument(
-        "-o",
-        "--roll_delta",
-        type=angle,
-        default=0.0,
-        help="Delta roll angle, degrees."
-    )
-    group_wcs.add_argument(
-        "-s",
-        "--scale_factor",
-        type=float,
-        default=1.0,
-        help="Scale factor."
+        "-o", "--roll_delta", type=angle, default=0.0, help="Delta roll angle, degrees."
     )
+    group_wcs.add_argument("-s", "--scale_factor", type=float, default=1.0, help="Scale factor.")
 
-    parser.add_argument(
-        "-v",
-        "--version",
-        action="version",
-        version="v{0}".format(jwst.__version__)
-    )
+    parser.add_argument("-v", "--version", action="version", version=f"v{jwst.__version__}")
 
     # pre-process argv for units and negative floats:
     argv = sys.argv[1:]
-    argv_new = [os.path.basename(sys.argv[0])]
+    argv_new = [os.path.basename(sys.argv[0])]  # noqa: PTH119
     while argv:
         argv_new.append(argv.pop(0))
 
         # check whether next argument is a float:
-        if (argv_new[-1] in _ANGLE_PARS and len(argv) >= 1 and
-                _is_float(argv[0])):
+        if (argv_new[-1] in _ANGLE_PARS) and (len(argv) >= 1) and (_is_float(argv[0])):
             angle_value = argv.pop(0)
             # check whether next argument is a unit:
             if len(argv) >= 1 and _is_unit(argv[0]):
@@ -219,19 +155,12 @@ def main():
 
     files = []
     for f in options.arg0:
-        files.extend(glob.glob(f))
+        files.extend(glob.glob(f))  # noqa: PTH207
 
-    if options.file and len(files) > 1 :
-        parser.error(
-            "argument -f/--file: not allowed with multiple input files"
-        )
+    if options.file and len(files) > 1:
+        parser.error("argument -f/--file: not allowed with multiple input files")
 
-    wcs_pars = [
-        options.ra_delta,
-        options.dec_delta,
-        options.roll_delta,
-        options.scale_factor
-    ]
+    wcs_pars = [options.ra_delta, options.dec_delta, options.roll_delta, options.scale_factor]
 
     if wcs_pars == [0.0, 0.0, 0.0, 1.0]:
         logger.info(
@@ -251,7 +180,7 @@ def main():
             delta_ra=options.ra_delta,
             delta_dec=options.dec_delta,
             delta_roll=options.roll_delta,
-            scale_factor=options.scale_factor
+            scale_factor=options.scale_factor,
         )
 
         # Save results:
@@ -259,14 +188,11 @@ def main():
         # Also update FITS representation in input exposures for
         # subsequent reprocessing by the end-user.
         try:
-            update_fits_wcsinfo(
-                data_model,
-                max_pix_error=0.001,
-                npoints=64
-            )
+            update_fits_wcsinfo(data_model, max_pix_error=0.001, npoints=64)
         except (ValueError, RuntimeError) as e:
-            logger.warning("Failed to update 'meta.wcsinfo' with "
-                           "FITS SIP approximation. Reported error is:")
+            logger.warning(
+                "Failed to update 'meta.wcsinfo' with FITS SIP approximation. Reported error is:"
+            )
             logger.warning(f'"{e.args[0]}"')
 
         if options.update:
@@ -282,5 +208,5 @@ def main():
             logger.info(f"Saved data model to '{output_file}'")
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/jwst/scripts/asn_edit.py b/jwst/scripts/asn_edit.py
index 82df1b7638c..503a7b3f0a3 100644
--- a/jwst/scripts/asn_edit.py
+++ b/jwst/scripts/asn_edit.py
@@ -1,32 +1,29 @@
 #!/usr/bin/env python
 
+"""Edit an association file from the command line."""
+
 import argparse
 
 from jwst.associations import asn_edit
 
 
 def main():
-    """
-    Parse command line, read, edit, write association file
-    """
-
+    """Edit Association File."""
     # Parse command line arguments
     description_text = """
-Edit Association File
-
-This script adds or removes filenames from an association file. The
-first argument is the name of the association file. Subsequent
-arguments are the filenames. Options determine which operation is
-performed: --add or --remove. If adding files the --type option sets
-the exposure type for the new files. If removing file, the --ignore
-option will not use the filename suffix when matching the filenames for
-removal. Normally the output association file is the same as the input.
-The --output option allows you to set a different filename. All options
-can be abbreviated to their first letter.
-
-When adding files to an association, the file must exist on the disk.
-When removing files, the filename must be found in the association. If
-not, no change will be made to the file.
+    This script adds or removes filenames from an association file. The
+    first argument is the name of the association file. Subsequent
+    arguments are the filenames. Options determine which operation is
+    performed: --add or --remove. If adding files the --type option sets
+    the exposure type for the new files. If removing file, the --ignore
+    option will not use the filename suffix when matching the filenames for
+    removal. Normally the output association file is the same as the input.
+    The --output option allows you to set a different filename. All options
+    can be abbreviated to their first letter.
+
+    When adding files to an association, the file must exist on the disk.
+    When removing files, the filename must be found in the association. If
+    not, no change will be made to the file.
     """
     parser = argparse.ArgumentParser(
         description=description_text,
@@ -34,30 +31,24 @@ def main():
     )
 
     arg_group = parser.add_mutually_exclusive_group()
+    arg_group.add_argument("-a", "--add", action="store_true", help="Add files to association")
     arg_group.add_argument(
-        '-a', '--add', action='store_true', help='Add files to association'
-    )
-    arg_group.add_argument(
-        '-r', '--remove', action='store_true', help='Remove files to association'
+        "-r", "--remove", action="store_true", help="Remove files to association"
     )
 
+    parser.add_argument("-t", "--type", default="science", help="Exptype, if adding filenames")
     parser.add_argument(
-        '-t', '--type', default='science', help='Exptype, if adding filenames'
-    )
-    parser.add_argument(
-        '-i',
-        '--ignore',
-        action='store_true',
-        help='Ignore suffix on filename when matching',
+        "-i",
+        "--ignore",
+        action="store_true",
+        help="Ignore suffix on filename when matching",
     )
 
-    parser.add_argument(
-        '-o', '--output', help='Output association name if different than input'
-    )
+    parser.add_argument("-o", "--output", help="Output association name if different than input")
 
-    parser.add_argument('association', help='The association file name')
+    parser.add_argument("association", help="The association file name")
 
-    parser.add_argument('filenames', nargs='+', help='The filenames to process')
+    parser.add_argument("filenames", nargs="+", help="The filenames to process")
 
     args = parser.parse_args()
 
@@ -78,5 +69,5 @@ def main():
     asn_edit.writer(asn, output_file)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/jwst/scripts/asn_gather.py b/jwst/scripts/asn_gather.py
index cc286947fff..8fc98bbbe81 100644
--- a/jwst/scripts/asn_gather.py
+++ b/jwst/scripts/asn_gather.py
@@ -1,9 +1,13 @@
 #!/usr/bin/env python
 
-"""asn_gather: Copy or Move data that is listed in an association"""
-
 from jwst.associations import asn_gather
 
-if __name__ == '__main__':
+
+def main():
+    """Copy or move data that is listed in an association."""
     kwargs = asn_gather.from_cmdline()
     asn_gather.asn_gather(**kwargs)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/jwst/scripts/asn_make_pool.py b/jwst/scripts/asn_make_pool.py
index 2d2e8c08c84..c6b3d3fa250 100644
--- a/jwst/scripts/asn_make_pool.py
+++ b/jwst/scripts/asn_make_pool.py
@@ -4,14 +4,25 @@
 
 
 def main():
+    """
+    Create an association pool from a list of FITS files.
+
+    Implement the command line functionality of `mkpool`. For full information on
+    the associtation tool use `asn_make_pool --help`.
+
+    Returns
+    -------
+    pool : jwst.associations.AssociationPool
+       The association pool.
+    """
     kwargs = mkpool.from_cmdline()
-    pool_file = kwargs['pool']
-    del kwargs['pool']
+    pool_file = kwargs["pool"]
+    del kwargs["pool"]
 
     pool = mkpool.mkpool(**kwargs)
 
     pool.write(pool_file, overwrite=True)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/jwst/scripts/collect_pipeline_cfgs.py b/jwst/scripts/collect_pipeline_cfgs.py
index ce7cf95cc3e..a584aa618b0 100755
--- a/jwst/scripts/collect_pipeline_cfgs.py
+++ b/jwst/scripts/collect_pipeline_cfgs.py
@@ -1,42 +1,48 @@
 #!/usr/bin/env python
 
-import os
 import sys
+import logging
+from pathlib import Path
 
 from jwst.pipeline.collect_pipeline_cfgs import collect_pipeline_cfgs
 
 
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+
+
 def main():
+    """
+    Get copies of all the cfg files currently in use by the jwst pipeline software.
+
+    Use from terminal as follows:
+    $ collect_pipeline_cfgs .
+    """
     if len(sys.argv) < 2:
-        print('ERROR: missing argument (destination directory')
+        logging.error("ERROR: missing argument (destination directory")
         sys.exit(1)
 
     if len(sys.argv) > 2:
-        print('ERROR: too many arguments')
+        logging.error("ERROR: too many arguments")
         sys.exit(1)
 
     dst = sys.argv[1]
 
-    if os.path.exists(dst):
+    if Path(dst).exists():
         # if the destination directory already exists, make sure the user wants to
-        # overwrite it's contents
+        # overwrite its contents
         count = 0
         while True:
             msg = (
-                f'WARNING: {dst} already exists, do you want to overwrite the \n'
-                f'contents of this directory?  Enter yes or no [yes]: '
+                f"WARNING: {dst} already exists, do you want to overwrite the \n"
+                f"contents of this directory?  Enter yes or no [yes]: "
             )
 
-            sys_vrs = sys.version[:3]
-            if sys_vrs == '2.7':
-                answer = raw_input(msg).strip().lower()  # noqa
-            else:
-                answer = input(msg).strip().lower()
-            if answer not in ['yes', 'no', '']:
-                print('%s is not a valid response\n' % answer)
+            answer = input(msg).strip().lower()
+            if answer not in ["yes", "no", ""]:
+                logging.info(f"\n{answer} is not a valid response\n")
             else:
-                print('')
-                if answer in ['yes', '']:
+                if answer in ["yes", ""]:
                     break
                 else:
                     sys.exit(1)
@@ -44,11 +50,10 @@ def main():
             # exit after 10 tries
             count += 1
             if count > 10:
-                print()
                 sys.exit(1)
 
     collect_pipeline_cfgs(dst)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/jwst/scripts/pointing_summary.py b/jwst/scripts/pointing_summary.py
index 01be3109f95..d45b295c913 100755
--- a/jwst/scripts/pointing_summary.py
+++ b/jwst/scripts/pointing_summary.py
@@ -1,15 +1,7 @@
 #!/usr/bin/env python
 
-"""Pointing verification
+"""Pointing verification."""
 
-Review contents of a set of given models for pointing information.
-Compare the calculated V1 and REFPOINT pointing with the proposed TARGET pointing.
-
-% pointing_summary exp1.fits
-
-% pointing_summary *.fits
-
-"""
 import argparse
 import logging
 from sys import stdout
@@ -17,32 +9,45 @@
 from jwst.lib import pointing_summary
 
 log_handler = logging.StreamHandler()
-logger = logging.getLogger('jwst')
+logger = logging.getLogger("jwst")
 logger.addHandler(log_handler)
 LogLevels = [logging.WARNING, logging.INFO, logging.DEBUG]
 
 
 # Begin execution
 def main():
+    """Run pointing verification."""
     parser = argparse.ArgumentParser(
-        description='Summarize various pointing information in a table.'
+        description="""Summarize various pointing information in a table. Compare
+                    the calculated V1 and REFPOINT pointing with the proposed TARGET pointing.
+                    E.g.
+                    $ pointing_summary exp1.fits
+                    $ pointing_summary *.fits
+                    """
     )
 
     parser.add_argument(
-        'exposures', type=str, nargs='+',
-        help='List of JWST data files to examine.'
+        "exposures", type=str, nargs="+", help="List of JWST data files to examine."
     )
     parser.add_argument(
-        '-o', '--output', type=argparse.FileType('w'), default=stdout,
-        help='File to write summary table to. Default is standard output.'
+        "-o",
+        "--output",
+        type=argparse.FileType("w"),
+        default=stdout,
+        help="File to write summary table to. Default is standard output.",
     )
     parser.add_argument(
-        '-v', '--verbose', action='count', default=0,
-        help='Increase verbosity. Specifying multiple times adds more output.'
+        "-v",
+        "--verbose",
+        action="count",
+        default=0,
+        help="Increase verbosity. Specifying multiple times adds more output.",
     )
     parser.add_argument(
-        '--extra-meta', type=str, nargs='+',
-        help='Extra meta information from the exposures to add to the result table'
+        "--extra-meta",
+        type=str,
+        nargs="+",
+        help="Extra meta information from the exposures to add to the result table",
     )
 
     args = parser.parse_args()
@@ -52,11 +57,11 @@ def main():
     logger.setLevel(level)
 
     # Process the file list.
-    logger.info('Starting pointing summary.')
+    logger.info("Starting pointing summary.")
     deltas = pointing_summary.calc_deltas(args.exposures, extra_meta=args.extra_meta)
-    deltas.write(args.output, format='ascii.ecsv')
-    logger.info('........Summary completed.')
+    deltas.write(args.output, format="ascii.ecsv")
+    logger.info("........Summary completed.")
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/jwst/scripts/schemadoc.py b/jwst/scripts/schemadoc.py
index 035e0a23214..38ef9d913ab 100755
--- a/jwst/scripts/schemadoc.py
+++ b/jwst/scripts/schemadoc.py
@@ -1,45 +1,25 @@
 #!/usr/bin/env python
 
-# Copyright (C) 2018 Association of Universities for Research in Astronomy (AURA)
+"""Create documentation from the schema file of a datamodel class."""
 
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-
-#     1. Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-
-#     2. Redistributions in binary form must reproduce the above
-#       copyright notice, this list of conditions and the following
-#       disclaimer in the documentation and/or other materials provided
-#       with the distribution.
-
-#     3. The name of AURA and its representatives may not be used to
-#       endorse or promote products derived from this software without
-#       specific prior written permission.
-
-# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
-# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-# DAMAGE.
+# Licensed under a 3-clause BSD style license - see LICENSE
 
 import argparse
-import sys
+import logging
+from pathlib import Path
 
 from stdatamodels.schema import build_docstring
+from stdatamodels.jwst.datamodels import _defined_models as defined_models
+
 
+# Set logger to only print to screen
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
 
-def get_docstrings(template, model_names, all=False):
-    # Get the docstring for every model class
-    from stdatamodels.jwst.datamodels import _defined_models as defined_models
 
-    if all:
+def get_docstrings(template, model_names, all_models=False):
+    """Get the docstring for every model class."""
+    if all_models:
         klasses = defined_models
     else:
         klasses = {}
@@ -50,35 +30,34 @@ def get_docstrings(template, model_names, all=False):
         try:
             docstring = build_docstring(klass, template)
         except Exception as err:
-            print(klassname, ':', str(err), file=sys.stderr)
+            logging.error(f"{klassname} : {err}")
         else:
-            print('.. ' + klassname + ' ..')
-            print(docstring, end='')
+            logging.info(f".. {klassname} ..")
+            logging.info(docstring)
 
 
 def main():
+    """Get docstrings from a specific datamodel or all datamodel(s)."""
     long_description = """
-    Create documentation from the schema file of a datamodel class
+    Create documentation from the schema file of a datamodel class.
     """
-
     parser = argparse.ArgumentParser(description=long_description)
-    parser.add_argument('-a', '--all', action='store_true',
-                        help='generate docstring for all models')
-    parser.add_argument('-t', '--template', type=str,
-                        help='input file containing templates')
-    parser.add_argument('models', nargs='*',
-                        help='Models to generate docstrings for')
+    parser.add_argument(
+        "-a", "--all_models", action="store_true", help="generate docstring for all models"
+    )
+    parser.add_argument("-t", "--template", type=str, help="input file containing templates")
+    parser.add_argument("models", nargs="*", help="Models to generate docstrings for")
     args = parser.parse_args()
 
     if args.template is None:
-        template = '{path} : {title} ({datatype})\n'
+        template = "{path} : {title} ({datatype})\n"
     else:
-        with open(args.template, 'r') as fd:
+        with Path.open(args.template, "r") as fd:
             template = fd.readlines()
-            template = ''.join(template)
+            template = "".join(template)
 
-    get_docstrings(template, args.models, all=args.all)
+    get_docstrings(template, args.models, all_models=args.all_models)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/jwst/scripts/set_telescope_pointing.py b/jwst/scripts/set_telescope_pointing.py
index 0b75ca09754..6ed4be0f911 100755
--- a/jwst/scripts/set_telescope_pointing.py
+++ b/jwst/scripts/set_telescope_pointing.py
@@ -1,44 +1,15 @@
 #!/usr/bin/env python
 
 """
-Set the initial world coordinate system for JWST exposures
+Set the initial world coordinate system for JWST exposures.
 
 The JWST engineering database is queried for the JWST observatory
 orientation parameters, and converts that orientation to a WCS
 for a list of exposures.
-
-Copyright
----------
-Copyright (C) 2010-2022 Association of Universities for Research in Astronomy (AURA)
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-    1. Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-
-    2. Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials provided
-      with the distribution.
-
-    3. The name of AURA and its representatives may not be used to
-      endorse or promote products derived from this software without
-      specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
 """
 
+# Licensed under a 3-clause BSD style license - see LICENSE
+
 import argparse
 import logging
 import warnings
@@ -47,108 +18,104 @@
 import jwst.lib.set_telescope_pointing as stp
 
 # Configure logging
-logger = logging.getLogger('jwst')
+logger = logging.getLogger("jwst")
 logger.propagate = False
 logger_handler = logging.StreamHandler()
 logger.addHandler(logger_handler)
-logger_format_debug = logging.Formatter(
-    '%(levelname)s:%(filename)s::%(funcName)s: %(message)s'
-)
+logger_format_debug = logging.Formatter("%(levelname)s:%(filename)s::%(funcName)s: %(message)s")
 
 
 def main():
+    """Set the initial world coordinate system."""
     parser = argparse.ArgumentParser(
         description=(
-            'Update basic WCS information in JWST exposures from the engineering database.'
-            ' For detailed information, see'
-            ' https://jwst-pipeline.readthedocs.io/en/latest/jwst/lib/set_telescope_pointing.html'
+            "Update basic WCS information in JWST exposures from the engineering database."
+            " For detailed information, see"
+            " https://jwst-pipeline.readthedocs.io/en/latest/jwst/lib/set_telescope_pointing.html"
         )
     )
+    parser.add_argument("exposure", type=str, nargs="+", help="List of JWST exposures to update.")
     parser.add_argument(
-        'exposure', type=str, nargs='+', help='List of JWST exposures to update.'
-    )
-    parser.add_argument(
-        '-v',
-        '--verbose',
-        action='count',
+        "-v",
+        "--verbose",
+        action="count",
         default=0,
-        help='Increase verbosity. Specifying multiple times adds more output.',
+        help="Increase verbosity. Specifying multiple times adds more output.",
     )
     parser.add_argument(
-        '--allow-any-file',
-        action='store_true',
+        "--allow-any-file",
+        action="store_true",
         default=False,
-        help='Attempt to update WCS for any file or model. Default: False',
+        help="Attempt to update WCS for any file or model. Default: False",
     )
     parser.add_argument(
-        '--force-level1bmodel',
-        action='store_true',
+        "--force-level1bmodel",
+        action="store_true",
         default=False,
-        help='Force unrecognized files to be opened as Level1bModel. Default: False',
+        help="Force unrecognized files to be opened as Level1bModel. Default: False",
     )
     parser.add_argument(
-        '--allow-default',
-        action='store_true',
-        help='If pointing information cannot be determine, use header information.',
+        "--allow-default",
+        action="store_true",
+        help="If pointing information cannot be determine, use header information.",
     )
     parser.add_argument(
-        '--dry-run',
-        action='store_true',
-        help='Perform all actions but do not save the results',
+        "--dry-run",
+        action="store_true",
+        help="Perform all actions but do not save the results",
     )
     parser.add_argument(
-        '--method',
+        "--method",
         type=stp.Methods,
         choices=list(stp.Methods),
         default=stp.Methods.default,
-        help='Algorithmic method to use. Default: %(default)s',
+        help="Algorithm to use. Default: %(default)s",
     )
+    parser.add_argument("--save-transforms", action="store_true", help="Save transforms.")
     parser.add_argument(
-        '--save-transforms', action='store_true', help='Save transforms.'
-    )
-    parser.add_argument(
-        '--override-transforms',
+        "--override-transforms",
         type=str,
         default=None,
-        help='Transform matrices to use instead of calculated',
+        help="Transform matrices to use instead of calculated",
     )
     parser.add_argument(
-        '--fgsid',
+        "--fgsid",
         type=int,
         default=1,
         choices=stp.FGSIDS,
-        help='FGS to use for COARSE mode calculations. Default: %(default)s Choices: %(choices)s',
+        help="FGS to use for COARSE mode calculations. Default: %(default)s Choices: %(choices)s",
     )
     parser.add_argument(
-        '--tolerance',
+        "--tolerance",
         type=int,
         default=60,
-        help='Seconds beyond the observation time to search for telemetry. Default: %(default)s',
+        help="Seconds beyond the observation time to search for telemetry. Default: %(default)s",
     )
     parser.add_argument(
-        '--siaf',
+        "--siaf",
         type=str,
         default=None,
-        help='SIAF PRD XML folder or file as defined by the `pysiaf` package. Overrides the `prd` option',
+        help="SIAF PRD XML folder or file as defined by the `pysiaf` package. "
+        "Overrides the `prd` option",
     )
     parser.add_argument(
-        '--prd',
+        "--prd",
         type=str,
         default=None,
-        help='The PRD version to use, as delivered in the `pysiaf` package.',
+        help="The PRD version to use, as delivered in the `pysiaf` package.",
     )
     parser.add_argument(
-        '--engdb_url',
+        "--engdb_url",
         type=str,
         default=None,
         help=(
-            'URL of the engineering database.'
-            ' If not specified, the environmental variable \'ENG_BASE_URL\' is used.'
-            ' Otherwise, a hardwired default is used.'
+            "URL of the engineering database."
+            " If not specified, the environment variable 'ENG_BASE_URL' is used."
+            " Otherwise, a hardwired default is used."
         ),
     )
     parser.add_argument(
-        '--transpose_j2fgs', action='store_false', help='Transpose the J2FGS matrix'
+        "--transpose_j2fgs", action="store_false", help="Transpose the J2FGS matrix"
     )
 
     args = parser.parse_args()
@@ -158,7 +125,7 @@ def main():
     logger.setLevel(level)
     if level <= logging.DEBUG:
         logger_handler.setFormatter(logger_format_debug)
-    logger.info('set_telescope_pointing called with args %s', args)
+    logger.info("set_telescope_pointing called with args %s", args)
 
     override_transforms = args.override_transforms
     if override_transforms:
@@ -167,14 +134,14 @@ def main():
     # Calculate WCS for all inputs.
     for filename in args.exposure:
         logger.info("")
-        logger.info('------')
-        logger.info('Setting pointing for {}'.format(filename))
+        logger.info("------")
+        logger.info(f"Setting pointing for {filename}")
 
         # Create path for saving the transforms.
         transform_path = None
         if args.save_transforms:
             path = Path(filename)
-            transform_path = path.with_name(f'{path.stem}_transforms.asdf')
+            transform_path = path.with_name(f"{path.stem}_transforms.asdf")
 
         try:
             stp.add_wcs(
@@ -194,20 +161,19 @@ def main():
                 override_transforms=override_transforms,
             )
         except (TypeError, ValueError) as exception:
-            logger.warning('Cannot determine pointing information: %s', str(exception))
-            logger.debug('Full exception:', exc_info=exception)
+            logger.warning("Cannot determine pointing information: %s", str(exception))
+            logger.debug("Full exception:", exc_info=exception)
 
 
 def deprecated_name():
-    from pathlib import Path
-
+    """Raise warning if filename.* is no longer used, and provide correct one."""
     filename = Path(__file__)
     warnings.warn(
-        f'usage of `{filename.name}` is deprecated; use `{filename.stem}` instead'
+        f"usage of `{filename.name}` is deprecated; use `{filename.stem}` instead", stacklevel=2
     )
 
     main()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/jwst/scripts/set_velocity_aberration.py b/jwst/scripts/set_velocity_aberration.py
index 8a0f0792bc7..3036b049d64 100755
--- a/jwst/scripts/set_velocity_aberration.py
+++ b/jwst/scripts/set_velocity_aberration.py
@@ -1,28 +1,12 @@
 #!/usr/bin/env python
 
-"""
-This script adds velocity aberration correction information to the FITS
-files provided to it on the command line (one or more).
+"""Add velocity aberration correction information to the FITS files provided."""
 
-It assumes the following keywords are present in the file header:
-
-JWST_DX (km/sec)
-JWST_DY (km/sec)
-JWST_DZ (km/sec)
-RA_REF (deg)
-DEC_REF (deg)
-
-The keywords added are:
-
-VA_SCALE (dimensionless scale factor)
-
-It does not currently place the new keywords in any particular location
-in the header other than what is required by the standard.
-"""
 import logging
 import sys
 import warnings
 import argparse
+from pathlib import Path
 
 from jwst.lib.set_velocity_aberration import add_dva
 
@@ -32,41 +16,72 @@
 
 
 def parse_args(args):
+    """
+    Parse given arguments.
+
+    Parameters
+    ----------
+    args : command-line input
+        The arguments to parse
+
+    Returns
+    -------
+    parser : object
+        Parsed arguments
+    """
+    description_text = """
+    Add velocity aberration correction information to FITS files.
+    Input via command line can be one or more FITS files.
+    It assumes the following keywords are present in the file header:
+    JWST_DX (km/sec),
+    JWST_DY (km/sec),
+    JWST_DZ (km/sec),
+    RA_REF (deg),
+    DEC_REF (deg).
+
+    The keywords added are:
+    VA_SCALE (dimensionless scale factor)
+
+    It does not currently place the new keywords in any particular location
+    in the header other than what is required by the standard.
+    """
     parser = argparse.ArgumentParser(
-        prog='set_velocity_aberration',
-        description='Add velocity aberration correction information to FITS files.',
+        prog="set_velocity_aberration",
+        description=description_text,
     )
     parser.add_argument(
-        'filename',
-        nargs='+',
-        help='FITS file(s) to which to add the velocity aberration correction information.'
+        "filename",
+        nargs="+",
+        help="FITS file(s) to which to add the velocity aberration correction information.",
     )
     parser.add_argument(
-        '-f', '--force_level1bmodel',
-        choices=[1,0],
+        "-f",
+        "--force_level1bmodel",
+        choices=[1, 0],
         default=1,
         type=int,
-        help='Force the input file to be treated as a Level1bModel. Options 0 (do not force) or 1 (yes, force). Default is 1.'
+        help="Force the input file to be treated as a Level1bModel. Options 0 (do not force)"
+        "or 1 (yes, force). Default is 1.",
     )
     return parser.parse_args(args)
 
 
 def main():
+    """Parse arguments and add velocity aberration correction information to the files provided."""
     args = parse_args(sys.argv[1:])
     for filename in args.filename:
         add_dva(filename)
 
 
 def deprecated_name():
-    from pathlib import Path
-
+    """Raise warning if filename.* is no longer used, and provide correct one."""
     filename = Path(__file__)
     warnings.warn(
-        f'usage of `{filename.name}` is deprecated; use `{filename.stem}` instead'
+        f"usage of `{filename.name}` is deprecated; use `{filename.stem}` instead", stacklevel=2
     )
 
     main()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/jwst/scripts/stfitsdiff.py b/jwst/scripts/stfitsdiff.py
index ef96bcabf62..3bfaf9a8d2c 100755
--- a/jwst/scripts/stfitsdiff.py
+++ b/jwst/scripts/stfitsdiff.py
@@ -3,12 +3,18 @@
 """Allow command stfitsdiff be used from terminal."""
 
 import ast
+import logging
 from argparse import ArgumentParser
 
 from jwst.regtest.st_fitsdiff import STFITSDiff
 
 
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+
+
 def main():
+    """Find the differences between the files and report."""
     # Parse command line arguments.
     parser = ArgumentParser(
         description="Get the differences between two fits files and report,",
@@ -69,9 +75,10 @@ def main():
         default=10,
         type=int,
         help="""The number of pixel/table values to output when reporting HDU data differences.
-                             Though the count of differences is the same either way, this allows controlling
-                             the number of different values that are kept in memory or output. If a negative
-                             value is given, then numdiffs is treated as unlimited.""",
+                             Though the count of differences is the same either way, this allows
+                             controlling the number of different values that are kept in memory
+                             or output. If a negative value is given, then numdiffs is treated
+                             as unlimited.""",
     )
 
     parser.add_argument(
@@ -131,12 +138,13 @@ def main():
         help="""Provide a different relative and absolute tolerance for the given extensions
                              (use no spaces and double quotes encasing the whole dictionary), e.g.
                              --extension_tolerances="{'sci':{'rtol':1e-3,'atol':1e-2}}}"
-                             It does not matter if the keys in the dictionary are upper or lower case.
-                             The key for only providing main header tolerances is 'primary'.
+                             It does not matter if the keys in the dictionary are upper or lower
+                             case. The key for only providing main header tolerances is 'primary'.
                              The key 'headers' can be used to provide a special tolerance for all
                              extension headers in the HDU list.
-                             The key 'default' is optional, i.e. if it is not provided then the default
-                             values will be used, otherwise the default value will be the one in the dictionary.""",
+                             The key 'default' is optional, i.e. if it is not provided then the
+                             default values will be used, otherwise the default value will be the
+                             one in the dictionary.""",
     )
 
     # Get the arguments
@@ -158,24 +166,26 @@ def main():
     }
 
     # If provided, make sure the extension_tolerances is a dictionary and not a string
-    err_msg = """Dictionary format error. Use no spaces and double quotes encasing the whole dictionary, e.g.
-                  --extension_tolerances="{'sci':{'rtol':1e-3,'atol':1e-2},'err':{'rtol':1e-1,'atol':1e-2}}" """
+    err_msg = """
+    Dictionary format error. Use no spaces and double quotes encasing the whole dictionary, e.g.
+    --extension_tolerances="{'sci':{'rtol':1e-3,'atol':1e-2},'err':{'rtol':1e-1,'atol':1e-2}}"
+    """
     if args.extension_tolerances is not None:
         try:
             stfitsdiff_default_kwargs["extension_tolerances"] = ast.literal_eval(
                 args.extension_tolerances
             )
         except (NameError, TypeError, ValueError, SyntaxError):
-            print(err_msg)
+            logging.error(err_msg)
             exit()
 
     # Find the differences
-    print("\n STScI Custom FITSDiff")
+    logging.info("\n STScI Custom FITSDiff")
     try:
         diff = STFITSDiff(file_a, file_b, **stfitsdiff_default_kwargs)
-        print(diff.report())
+        logging.info(diff.report())
     except (NameError, TypeError, ValueError, SyntaxError):
-        print(err_msg)
+        logging.error(err_msg)
 
 
 if __name__ == "__main__":
diff --git a/jwst/scripts/v1_calculate.py b/jwst/scripts/v1_calculate.py
index 567fa577fbc..4e3a87e4d32 100755
--- a/jwst/scripts/v1_calculate.py
+++ b/jwst/scripts/v1_calculate.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 
-"""V1 Calculation based on time and engineering database info
-"""
+"""V1 Calculation based on time and engineering database info."""
+
 import argparse
 import logging
 from sys import stdout
@@ -12,63 +12,82 @@
 from jwst.lib import v1_calculate
 
 # Configure logging
-logger = logging.getLogger('jwst')
+logger = logging.getLogger("jwst")
 logger.propagate = False
 logger_handler = logging.StreamHandler()
 logger.addHandler(logger_handler)
-logger_format_debug = logging.Formatter('%(levelname)s:%(filename)s::%(funcName)s: %(message)s')
+logger_format_debug = logging.Formatter("%(levelname)s:%(filename)s::%(funcName)s: %(message)s")
 
 # Available reduce functions
 REDUCE_FUNCS_MAPPING = {
-    'all': stp.all_pointings,
-    'first': stp.first_pointing,
-    'average': stp.pointing_from_average
+    "all": stp.all_pointings,
+    "first": stp.first_pointing,
+    "average": stp.pointing_from_average,
 }
 REDUCE_FUNCS = list(REDUCE_FUNCS_MAPPING.keys())
 
 
 # Begin execution
 def main():
-    parser = argparse.ArgumentParser(
-        description='Calculate V1 over a time period.'
-    )
+    """Calculate V1, the telescope's boresight axis, the direction the telescope is pointing."""
+    parser = argparse.ArgumentParser(description="Calculate V1 over a time period.")
 
     parser.add_argument(
-        'time_sources', type=str, nargs='+',
-        help=('Either a list of JWST data files to retrieve the timing from'
-              ' or a start and end time to retrieve pointing information for.'
-              )
+        "time_sources",
+        type=str,
+        nargs="+",
+        help=(
+            "Either a list of JWST data files to retrieve the timing from"
+            " or a start and end time to retrieve pointing information for."
+        ),
     )
     parser.add_argument(
-        '-o', '--output', type=argparse.FileType('w'), default=stdout,
-        help='File to write V1 calculation table to. Default is standard output.'
+        "-o",
+        "--output",
+        type=argparse.FileType("w"),
+        default=stdout,
+        help="File to write V1 calculation table to. Default is standard output.",
     )
     parser.add_argument(
-        '--pointing', type=str, choices=REDUCE_FUNCS, default='average',
-        help=('Which pointing(s) to use within the specified time interval.'
-              f' Choices: {REDUCE_FUNCS}'
-              ' (default: %(default)s)'
-              )
+        "--pointing",
+        type=str,
+        choices=REDUCE_FUNCS,
+        default="average",
+        help=(
+            "Which pointing(s) to use within the specified time interval."
+            f" Choices: {REDUCE_FUNCS}"
+            " (default: %(default)s)"
+        ),
     )
     parser.add_argument(
-        '--method',
-        type=stp.Methods, choices=list(stp.Methods), default=stp.Methods.default,
-        help='Algorithmic method to use. Default: %(default)s'
+        "--method",
+        type=stp.Methods,
+        choices=list(stp.Methods),
+        default=stp.Methods.default,
+        help="Algorithm to use. Default: %(default)s",
     )
     parser.add_argument(
-        '-v', '--verbose', action='count', default=0,
-        help='Increase verbosity. Specifying multiple times adds more output.'
+        "-v",
+        "--verbose",
+        action="count",
+        default=0,
+        help="Increase verbosity. Specifying multiple times adds more output.",
     )
     parser.add_argument(
-        '--override-transforms', type=str, default=None,
-        help='Transform matrices to use instead of calculated'
+        "--override-transforms",
+        type=str,
+        default=None,
+        help="Transform matrices to use instead of calculated",
     )
     parser.add_argument(
-        '--engdb-url', type=str, default=None,
-        help=('The engineering database to use. If unspecified, the value'
-              ' of the environment ENG_BASE_URL is used. Otherwise, the production'
-              ' system is used.'
-              )
+        "--engdb-url",
+        type=str,
+        default=None,
+        help=(
+            "The engineering database to use. If unspecified, the value"
+            " of the environment ENG_BASE_URL is used. Otherwise, the production"
+            " system is used."
+        ),
     )
 
     args = parser.parse_args()
@@ -91,36 +110,44 @@ def main():
         except ValueError:
             input_as_files = True
         else:
-            logger.info(f'Retrieving V1 over the time span {obsstart.isot} - {obsend.isot}')
+            logger.info(f"Retrieving V1 over the time span {obsstart.isot} - {obsend.isot}")
             input_as_files = False
-            if args.pointing != 'all':
-                logger.warning("V1 pointings have been requested over a time range. "
-                               "However, the 'pointing' option is not 'all'.")
-                logger.warning('There will only be a single result returned. '
-                               'Is this what was desired?')
+            if args.pointing != "all":
+                logger.warning(
+                    "V1 pointings have been requested over a time range. "
+                    "However, the 'pointing' option is not 'all'."
+                )
+                logger.warning(
+                    "There will only be a single result returned. Is this what was desired?"
+                )
                 logger.warning("Suggestion: Use '--pointing=all'")
     else:
         input_as_files = True
 
     # Process the file list.
-    logger.info('Starting V1 calculation...')
+    logger.info("Starting V1 calculation...")
     if input_as_files:
         v1s = v1_calculate.v1_calculate_from_models(
-            args.time_sources, engdb_url=args.engdb_url,
+            args.time_sources,
+            engdb_url=args.engdb_url,
             reduce_func=REDUCE_FUNCS_MAPPING[args.pointing],
-            method=args.method, override_transforms=override_transforms
+            method=args.method,
+            override_transforms=override_transforms,
         )
     else:
         v1s = v1_calculate.v1_calculate_over_time(
-            obsstart.mjd, obsend.mjd, engdb_url=args.engdb_url,
+            obsstart.mjd,
+            obsend.mjd,
+            engdb_url=args.engdb_url,
             reduce_func=REDUCE_FUNCS_MAPPING[args.pointing],
-            method=args.method, override_transforms=override_transforms
+            method=args.method,
+            override_transforms=override_transforms,
         )
 
     formatted = v1_calculate.simplify_table(v1s)
-    formatted.write(args.output, format='ascii.ecsv')
-    logger.info('...V1 calculation completed.')
+    formatted.write(args.output, format="ascii.ecsv")
+    logger.info("...V1 calculation completed.")
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/jwst/scripts/world_coords.py b/jwst/scripts/world_coords.py
index a10e138d8fa..132d4df6601 100755
--- a/jwst/scripts/world_coords.py
+++ b/jwst/scripts/world_coords.py
@@ -1,51 +1,22 @@
 #!/usr/bin/env python
 
-# Copyright (C) 2018 Association of Universities for Research in Astronomy (AURA)
-
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-
-#     1. Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-
-#     2. Redistributions in binary form must reproduce the above
-#       copyright notice, this list of conditions and the following
-#       disclaimer in the documentation and/or other materials provided
-#       with the distribution.
-
-#     3. The name of AURA and its representatives may not be used to
-#       endorse or promote products derived from this software without
-#       specific prior written permission.
+"""
+Read in the output of extract_2d and apply the WCS transforms to all pixels in a slit.
 
-# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
-# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-# DAMAGE.
+The extract_2d file can be (FS and MOS) or assign_wcs (IFU).
 
+For each slit it writes the results as a cube with four planes (wavelength, ra, dec, y_slit)
+in a separate fits extension.
 
-"""
-A tool to read in the output of extract2d (FS and MOS) or assign_wcs (IFU) and
-apply the WCS transforms to all pixels in a slit. For each slit it writes the results
-as a cube with four planes (wavelength, ra, dec, y_slit) in a separate fits extension.
 The file is saved with a suffix 'world_coordinates'.
-
-Requested by the NIRSPEC team.
-
-Build 7.1 testing.
 """
 
+# Licensed under a 3-clause BSD style license - see LICENSE
+
 import argparse
 import os
-import os.path
-import sys
 import warnings
+import logging
 
 import numpy as np
 from astropy.io import fits
@@ -57,47 +28,89 @@
 from jwst.assign_wcs import nirspec
 
 
-def main(filenames, mode):
-    """
-    Loop over the input files and apply the proper method to
-    create the output file.
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
 
-    Parameters
-    ----------
-    filenames : list of strings
-        A list of input and output file names
-    mode : str or None
-        The exposure mode to use if exposure type
-        is not in the image header
+
+def main():
+    """Loop over the input files and apply the proper method to create the output file."""
+    short_description = "Create NIRSPEC world coordinates file"
+    long_description = """
+
+    A tool to read in the output of extract_2d (FS and MOS) or assign_wcs
+    (IFU) and apply the WCS transforms to all pixels in a slit. For each
+    slit it writes the results as a cube with four planes (wavelength, ra,
+    dec, y_slit) in a separate fits extension. The file is saved with a
+    suffix 'world_coordinates'.
+
+    This script can be run in one of three ways:
+
+    With a single input and output filename.
+
+    > world_coord input output
+
+    With one or more input filenames. The output names are generated from
+    the inputs.
+
+    > world_coord input_1 input_2 ... input_n
+
+    With an output directory name.
+
+    > world_coord input_1 input_2 ... output_directory
+
+    The output filename cannot be the name of an existing file. The output
+    directory must exist.
+
+    Optionally you can choose the exposure type with the -mode flag. This
+    flag can be abbreviated to -m. The -mode is only used if exposure type
+    is not found in the primary header. The mode is the exposure type
+    without the nrs_ prefix or any unique prefix of it.
     """
+
+    parser = argparse.ArgumentParser(
+        description=short_description,
+        epilog=long_description,
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+    )
+
+    parser.add_argument("filenames", help="Name of input and/or output files", nargs="*")
+
+    parser.add_argument("-m", "--mode", default=None, help="set exposure mode")
+
+    res = parser.parse_args()
+    filenames = res.filenames
+    mode = res.mode
+
     methods = {
-        'nrs_ifu': ifu_coords,
-        'nrs_fixedslit': compute_world_coordinates,
-        'nrs_msaspec': compute_world_coordinates,
-        'nrs_taconfirm': imaging_coords,
-        'nrs_brightobj': imaging_coords,
-        'nrs_bota': imaging_coords,
-        'nrs_tacq': imaging_coords,
-        'nrs_focus': imaging_coords,
-        'nrs_lamp': imaging_coords,
-        'nrs_mimf': imaging_coords,
-        'nrs_image': imaging_coords,
-        'nrs_confirm': imaging_coords,
-        'nrs_taslit': imaging_coords,
+        "nrs_ifu": ifu_coords,
+        "nrs_fixedslit": compute_world_coordinates,
+        "nrs_msaspec": compute_world_coordinates,
+        "nrs_taconfirm": imaging_coords,
+        "nrs_brightobj": imaging_coords,
+        "nrs_bota": imaging_coords,
+        "nrs_tacq": imaging_coords,
+        "nrs_focus": imaging_coords,
+        "nrs_lamp": imaging_coords,
+        "nrs_mimf": imaging_coords,
+        "nrs_image": imaging_coords,
+        "nrs_confirm": imaging_coords,
+        "nrs_taslit": imaging_coords,
     }
 
     ok = True
     outputs = build_output_files(filenames)
-    for (input_file, output_file) in zip(filenames, outputs):
-        if not os.path.exists(input_file):
-            warn_user(input_file, 'is not found')
+    for input_file, output_file in zip(filenames, outputs, strict=True):
+        if not os.path.exists(input_file):  # noqa: PTH110
+            warn_user(input_file, "is not found")
             ok = False
             continue
 
         try:
             model = dmopen(input_file, pass_invalid_values=True)
-        except IOError:
-            warn_user(input_file, 'is not valid fits')
+            if mode is None:
+                mode = model.meta.exposure.type
+        except OSError:
+            warn_user(input_file, "is not valid fits")
             ok = False
             continue
 
@@ -107,40 +120,41 @@ def main(filenames, mode):
         else:
             hdulist = method(model)
             hdulist.writeto(output_file, overwrite=True)
+            logging.info(f"File written: {output_file}")
             del hdulist
         model.close()
 
     if not ok:
-        msg = 'Error in input file'
+        msg = "Error in input file"
         if len(filenames) > 1:
-            msg += 's'
+            msg += "s"
         raise ValueError(msg)
 
 
 def build_output_files(filenames):
     """
-    Create a list of output files of the same length
-    as the list of input files
+    Create a list of output files of the same length as the list of input files.
 
     Parameters
     ----------
     filenames : list of strings
         A list of input and output filenames
 
-    returns a list of output file names
-    removes output file from filenames
+    Returns
+    -------
+    outputs : list
+        Output file names
     """
-
     # Check to see if the last file is an output file
     output = filenames[-1]
-    if os.path.exists(output) and not os.path.isdir(output):
+    if os.path.exists(output) and not os.path.isdir(output):  # noqa: PTH110, PTH112
         output = None
     else:
         filenames.pop()
 
     # Handle the case of a single input and output
     # Punt the other cases to the code below
-    if output is not None and not os.path.isdir(output):
+    if output is not None and not os.path.isdir(output):  # noqa: PTH112
         if len(filenames) == 1:
             return [output]
         else:
@@ -151,7 +165,7 @@ def build_output_files(filenames):
         outputs = []
 
     # Track if output file was a directory
-    if output is not None and os.path.isdir(output):
+    if output is not None and os.path.isdir(output):  # noqa: PTH112
         output_dir = output
     else:
         output_dir = None
@@ -160,38 +174,40 @@ def build_output_files(filenames):
     for filename in filenames:
         # Build output base and extensions
         dirname, root = os.path.split(filename)
-        base, ext = os.path.splitext(root)
-        if ext != '.fits':
-            ext = '.fits'
-        if not base.endswith('world_coordinates'):
-            base = '_'.join([base, 'world_coordinates'])
+        base, ext = os.path.splitext(root)  # noqa: PTH122
+        if ext != ".fits":
+            ext = ".fits"
+        if not base.endswith("world_coordinates"):
+            base = "_".join([base, "world_coordinates"])
 
         # Substitute output directory if present
         if output_dir is not None:
             dirname = output_dir
 
         # Combine directory, base, and extension
-        outputs.append(os.path.join(dirname, base + ext))
+        outputs.append(os.path.join(dirname, base + ext))  # noqa: PTH118
 
     return outputs
 
 
 def compute_world_coordinates(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    FS or MOS observation after running extract_2d.
+    Compute wavelengths and space coordinates of an NRS FS or MOS observation after extract_2d.
 
     Parameters
     ----------
     model : ImageModel
-        An image with extracted slits, i.e. the output of extract2d.
+        An image with extracted slits, i.e. the output of extract_2d.
 
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits.HDUList
+        The list of HDUs object.
     """
     hdulist = fits.HDUList()
     phdu = fits.PrimaryHDU()
-    phdu.header['filename'] = model.meta.filename
-    phdu.header['data'] = 'world coordinates'
+    phdu.header["filename"] = model.meta.filename
+    phdu.header["data"] = "world coordinates"
     hdulist.append(phdu)
     output_frame = model.slits[0].meta.wcs.available_frames[-1]
 
@@ -200,30 +216,28 @@ def compute_world_coordinates(model):
         # xstart, xend = slit.xstart - 1, slit.xstart -1 + slit.xsize
         # ystart, yend = slit.ystart - 1, slit.ystart -1 + slit.ysize
         # y, x = np.mgrid[ystart: yend, xstart: xend]
-        x, y = wcstools.grid_from_bounding_box(
-            slit.meta.wcs.bounding_box, step=(1, 1), center=True
-        )
+        x, y = wcstools.grid_from_bounding_box(slit.meta.wcs.bounding_box, step=(1, 1), center=True)
 
         ra, dec, lam = slit.meta.wcs(x, y)
-        detector2slit = slit.meta.wcs.get_transform('detector', 'slit_frame')
+        detector2slit = slit.meta.wcs.get_transform("detector", "slit_frame")
 
         sx, sy, ls = detector2slit(x, y)
         world_coordinates = np.array([lam, ra, dec, sy])  # , x, y])
         imhdu = fits.ImageHDU(data=world_coordinates)
-        imhdu.header['PLANE1'] = 'lambda, microns'
-        imhdu.header['PLANE2'] = '{0}_x, arcsec'.format(output_frame)
-        imhdu.header['PLANE3'] = '{0}_y, arcsec'.format(output_frame)
-        imhdu.header['PLANE4'] = 'slit_y, relative to center (0, 0)'
-        imhdu.header['SLIT'] = slit.name
+        imhdu.header["PLANE1"] = "lambda, microns"
+        imhdu.header["PLANE2"] = f"{output_frame}_x, arcsec"
+        imhdu.header["PLANE3"] = f"{output_frame}_y, arcsec"
+        imhdu.header["PLANE4"] = "slit_y, relative to center (0, 0)"
+        imhdu.header["SLIT"] = slit.name
 
         # add the overall subarray offset
-        imhdu.header['CRVAL1'] = slit.xstart - 1 + model.meta.subarray.xstart
-        imhdu.header['CRVAL2'] = slit.ystart - 1 + model.meta.subarray.ystart
+        imhdu.header["CRVAL1"] = slit.xstart - 1 + model.meta.subarray.xstart
+        imhdu.header["CRVAL2"] = slit.ystart - 1 + model.meta.subarray.ystart
 
-        imhdu.header['CRPIX1'] = 1
-        imhdu.header['CRPIX2'] = 1
-        imhdu.header['CTYPE1'] = 'pixel'
-        imhdu.header['CTYPE2'] = 'pixel'
+        imhdu.header["CRPIX1"] = 1
+        imhdu.header["CRPIX2"] = 1
+        imhdu.header["CTYPE1"] = "pixel"
+        imhdu.header["CTYPE2"] = "pixel"
         hdulist.append(imhdu)
 
     return hdulist
@@ -231,7 +245,7 @@ def compute_world_coordinates(model):
 
 def get_update_method(methods, model, mode):
     """
-    Find the method to convert the input file to output
+    Find the method to convert the input file to output.
 
     Parameters
     ----------
@@ -244,11 +258,14 @@ def get_update_method(methods, model, mode):
         A mode used to select the method if the exposure type
         is not found in the model
 
-    returns the update function
+    Returns
+    -------
+    method : function
+        Correct update function to call
     """
     exp_type = model.meta.exposure.type
     if exp_type is None and mode is not None:
-        mode = 'nrs_' + mode.lower()
+        mode = "nrs_" + mode.lower()
         for candidate_type in methods.keys():
             if candidate_type.startswith(mode):
                 if exp_type is None:
@@ -258,172 +275,122 @@ def get_update_method(methods, model, mode):
                     break
 
         if exp_type is None:
-            warn_user(mode, 'does not match an exposure type')
+            warn_user(mode, "does not match an exposure type")
         else:
             model.meta.exposure.type = exp_type.upper()
 
     exp_type = exp_type.lower()
     method = methods.get(exp_type)
     if method is None:
-        warnings.warn(exp_type, 'is not a supported exposure type')
+        warnings.warn(exp_type, "is not a supported exposure type", stacklevel=2)
 
     return method
 
 
 def ifu_coords(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    IFU exposure after running assign_wcs.
+    Compute wavelengths, and space coordinates of a NIRSPEC IFU exposure after running assign_wcs.
 
     Parameters
     ----------
     model : ImageModel
-        An image with extracted slits, i.e. the output of extract2d.
-
+        An image with extracted slits, i.e. the output of extract_2d.
 
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits.HDUList
+        Fits HDU list object
     """
     ifu_slits = nirspec.nrs_ifu_wcs(model)
 
     hdulist = fits.HDUList()
     phdu = fits.PrimaryHDU()
-    phdu.header['filename'] = model.meta.filename
-    phdu.header['data'] = 'world coordinates'
+    phdu.header["filename"] = model.meta.filename
+    phdu.header["data"] = "world coordinates"
     hdulist.append(phdu)
     output_frame = ifu_slits[0].available_frames[-1]
 
     for i, slit in enumerate(ifu_slits):
         x, y = wcstools.grid_from_bounding_box(slit.bounding_box, (1, 1), center=True)
         ra, dec, lam = slit(x, y)
-        detector2slit = slit.get_transform('detector', 'slit_frame')
+        detector2slit = slit.get_transform("detector", "slit_frame")
         sx, sy, ls = detector2slit(x, y)
         world_coordinates = np.array([lam, ra, dec, sy])
 
         imhdu = fits.ImageHDU(data=world_coordinates)
-        imhdu.header['PLANE1'] = 'lambda, microns'
-        imhdu.header['PLANE2'] = '{0}_x, arcsec'.format(output_frame)
-        imhdu.header['PLANE3'] = '{0}_y, arcsec'.format(output_frame)
-        imhdu.header['PLANE4'] = 'slit_y, relative to center (0, 0)'
-        imhdu.header['SLIT'] = 'SLIT_{0}'.format(i)
+        imhdu.header["PLANE1"] = "lambda, microns"
+        imhdu.header["PLANE2"] = f"{output_frame}_x, arcsec"
+        imhdu.header["PLANE3"] = f"{output_frame}_y, arcsec"
+        imhdu.header["PLANE4"] = "slit_y, relative to center (0, 0)"
+        imhdu.header["SLIT"] = f"SLIT_{i}"
 
         # -1 and +1 are to express this in 1-based coordinates
-        imhdu.header['CRVAL1'] = (
+        imhdu.header["CRVAL1"] = (
             model.meta.subarray.xstart - 1 + int(_toindex(slit.bounding_box[0][0])) + 1
         )
-        imhdu.header['CRVAL2'] = (
+        imhdu.header["CRVAL2"] = (
             model.meta.subarray.xstart - 1 + int(_toindex(slit.bounding_box[1][0])) + 1
         )
 
         # Input coordinates will be 1-based.
-        imhdu.header['CRPIX1'] = 1
-        imhdu.header['CRPIX2'] = 1
-        imhdu.header['CTYPE1'] = 'pixel'
-        imhdu.header['CTYPE2'] = 'pixel'
+        imhdu.header["CRPIX1"] = 1
+        imhdu.header["CRPIX2"] = 1
+        imhdu.header["CTYPE1"] = "pixel"
+        imhdu.header["CTYPE2"] = "pixel"
         hdulist.append(imhdu)
     return hdulist
 
 
 def imaging_coords(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    imaging exposure after running assign_wcs.
+    Compute wavelengths and space coordinates of a NIRSPEC imaging exposure after assign_wcs.
 
     Parameters
     ----------
     model : ImageModel
-        An image with extracted slits, i.e. the output of extract2d.
-
+        An image with extracted slits, i.e. the output of extract_2d.
 
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits.HDUList
+        Fits HDU list object
     """
     hdulist = fits.HDUList()
     phdu = fits.PrimaryHDU()
-    phdu.header['filename'] = model.meta.filename
-    phdu.header['data'] = 'world coordinates'
+    phdu.header["filename"] = model.meta.filename
+    phdu.header["data"] = "world coordinates"
     hdulist.append(phdu)
 
     output_frame = model.available_frames[-1]
-    bb = model.meta.wcs.bounding_box
-    x, y = wcstools.grid_from_bounding_box(bb, step=(1, 1), center=True)
-    ra, dec, lam = slit(x + 1, y + 1)  # noqa
-    world_coordinates = np.array([lam, ra, dec])
-
-    imhdu = fits.ImageHDU(data=world_coordinates)
-    imhdu.header['PLANE1'] = 'lambda, microns'
-    imhdu.header['PLANE2'] = '{0}_x, deg'.format(output_frame)
-    imhdu.header['PLANE3'] = '{0}_y, deg'.format(output_frame)
-    imhdu.header['SLIT'] = 'SLIT_{0}'.format(i)  # noqa
-
-    # add the overall subarray offset
-    imhdu.header['CRVAL1'] = model.meta.subarray.xstart - 1 + int(_toindex(bb[0][0]))
-    imhdu.header['CRVAL2'] = model.meta.subarray.ystart - 1 + int(_toindex(bb[1][0]))
-
-    # Input coordinates will be 1-based.
-    imhdu.header['CRPIX1'] = 1
-    imhdu.header['CRPIX2'] = 1
-    imhdu.header['CTYPE1'] = 'pixel'
-    imhdu.header['CTYPE2'] = 'pixel'
-    hdulist.append(imhdu)
-    return hdulist
-
-
-def warn_user(*argv):
-    """
-    Send a warning message to stderr
-
-    Parameters
-    ----------
-    argv : strings
-        One or more strings to be printed
-    """
-    print(' '.join(argv), file=sys.stderr)
-
-
-if __name__ == '__main__':
-    short_description = 'Create NIRSPEC world coordinates file'
-    long_description = """
-
-A tool to read in the output of extract2d (FS and MOS) or assign_wcs
-(IFU) and apply the WCS transforms to all pixels in a slit. For each
-slit it writes the results as a cube with four planes (wavelength, ra,
-dec, y_slit) in a separate fits extension. The file is saved with a
-suffix 'world_coordinates'.
-
-This script can be run in one of three ways:
-
-With a single input and output filename.
+    for i, slit in enumerate(output_frame):
+        bb = model.meta.wcs.bounding_box
+        x, y = wcstools.grid_from_bounding_box(bb, step=(1, 1), center=True)
+        ra, dec, lam = slit(x + 1, y + 1)
+        world_coordinates = np.array([lam, ra, dec])
 
-> world_coord input output
-
-With one or more input filenames. The output names are generated from
-the inputs.
-
-> world_coord input_1 input_2 ... input_n
-
-With an output directory name.
-
-> world_coord input_1 input_2 ... output_directory
+        imhdu = fits.ImageHDU(data=world_coordinates)
+        imhdu.header["PLANE1"] = "lambda, microns"
+        imhdu.header["PLANE2"] = f"{output_frame}_x, deg"
+        imhdu.header["PLANE3"] = f"{output_frame}_y, deg"
+        imhdu.header["SLIT"] = f"SLIT_{i}"
 
-The output filename cannot be the name of an existing file. The output
-directory must exist.
+        # add the overall subarray offset
+        imhdu.header["CRVAL1"] = model.meta.subarray.xstart - 1 + int(_toindex(bb[0][0]))
+        imhdu.header["CRVAL2"] = model.meta.subarray.ystart - 1 + int(_toindex(bb[1][0]))
 
-Optionally you can choose the exposure type with the -mode flag. This
-flag can be abbreviated to -m. The -mode is only used if exposure type
-is not found in the primary header. The mode is the exposure type
-without the nrs_ prefix or any unique prefix of it.
-"""
+        # Input coordinates will be 1-based.
+        imhdu.header["CRPIX1"] = 1
+        imhdu.header["CRPIX2"] = 1
+        imhdu.header["CTYPE1"] = "pixel"
+        imhdu.header["CTYPE2"] = "pixel"
+        hdulist.append(imhdu)
+    return hdulist
 
-    parser = argparse.ArgumentParser(
-        description=short_description,
-        epilog=long_description,
-        formatter_class=argparse.RawDescriptionHelpFormatter,
-    )
 
-    parser.add_argument('-mode', help='set exposure mode')
+def warn_user(*argv):
+    """Send a warning message to stderr."""
+    logging.warning(*argv)
 
-    parser.add_argument(
-        'filenames', help='Name of input and/or output files', nargs='*'
-    )
 
-    res = parser.parse_args()
-    main(res.filenames, res.mode)
+if __name__ == "__main__":
+    main()
diff --git a/pyproject.toml b/pyproject.toml
index 32ca1ed3721..d8a80b325c8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -68,8 +68,6 @@ asn_gather = "jwst.scripts.asn_gather:main"
 asn_generate = "jwst.associations.main:main"
 asn_make_pool = "jwst.scripts.asn_make_pool:main"
 collect_pipeline_cfgs = "jwst.scripts.collect_pipeline_cfgs:main"
-create_data = "jwst.scripts.create_data:main"
-csvconvert = "jwst.csv_tools.csvconvert:CSVConvertScript"
 exp_to_source = "jwst.exp_to_source.main:Main"
 pointing_summary = "jwst.scripts.pointing_summary:main"
 schemadoc = "jwst.scripts.schemadoc:main"


Reviews:
user: login: emolter
body: Thanks very much for taking on this large project Maria!  I left some questions and comments.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-18T15:30:05Z
user: login: tapastro
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T18:44:26Z
user: login: pllim
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-25T18:47:25Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T19:47:42Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T15:38:21Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T15:45:22Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T16:09:33Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T16:25:34Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T20:44:34Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T20:51:19Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:19:32Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:23:45Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:28:10Z
user: login: emolter
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-29T05:52:22Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T16:12:34Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T16:22:53Z
user: login: penaguerrero
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T17:43:31Z
user: login: emolter
body: Thanks very much for your work on this Maria!  I left another round of a few very small comments, but the majority of my questions are answered now and this looks good to me overall.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-17T14:42:02Z
user: login: melanieclarke
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-28T19:16:52Z
user: login: tapastro
body: Looks good to me - we'll have to get some INS feedback on which scripts are being used so that JP-3972 and JP-3974 can proceed.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T15:49:23Z

Review comments:
diff_hunk: @@ -0,0 +1 @@
+"""Auxiliary scripts."""
path: jwst/scripts/__init__.py
user: login: emolter
body: ```suggestion
"""Auxiliary command-line scripts."""
```
created_at: 2025-03-18T14:44:52Z
author_association: COLLABORATOR
diff_hunk: @@ -1,4 +1,7 @@
 #!/usr/bin/env python
+
+"""Apply adjustments to data model's WCS."""
+
 # Copyright (C) 2023 Association of Universities for Research in Astronomy (AURA)
path: jwst/scripts/adjust_wcs.py
user: login: emolter
body: We have a license in the top level of the repository, so I believe this is unnecessary and can be removed.  I'd check with @tapastro and @melanieclarke though
created_at: 2025-03-18T14:45:54Z
author_association: COLLABORATOR
diff_hunk: @@ -62,51 +45,67 @@
 
 _ANGLE_PARS = ["-r", "--ra_delta", "-d", "--dec_delta", "-o", "--roll_delta"]
 
-
 # Configure logging
 logger = logging.getLogger(__name__)
 logger.setLevel(logging.INFO)
 logger.addHandler(logging.NullHandler())
 
 
 def _replace_suffix(file, new_suffix):
path: jwst/scripts/adjust_wcs.py
user: login: emolter
body: Is there any unit test coverage for this function?  The switch to pathlib appears to be a relatively major refactor here, and I know from experience that it can be relatively challenging to preserve all behaviors with these types of changes, especially, e.g., if the filename has multiple suffixes.  See e.g. https://github.com/spacetelescope/stdatamodels/pull/422/files and how many different file paths had to be tested - I definitely didn't get it right the first time.

In stdatamodels we ended up doing some separate PRs for "unsafe" changes like this.  I'm not sure whether that's what I would recommend here, mostly because I don't actually know how these scripts are tested or used.
created_at: 2025-03-18T14:52:54Z
author_association: COLLABORATOR
diff_hunk: @@ -62,51 +45,67 @@
 
 _ANGLE_PARS = ["-r", "--ra_delta", "-d", "--dec_delta", "-o", "--roll_delta"]
 
-
 # Configure logging
 logger = logging.getLogger(__name__)
 logger.setLevel(logging.INFO)
 logger.addHandler(logging.NullHandler())
 
 
 def _replace_suffix(file, new_suffix):
-    sep = '_'
-    directory, base = os.path.split(file)
-    root, ext = os.path.splitext(base)
-    if sep in root:
-        name_parts = root.rpartition(sep)
-        root = ''.join(name_parts[:-1] + (new_suffix, ))
+    sep = "_"
+    path_object = Path(file)
+    directory, base = path_object.parent, path_object.name
+    ext = path_object.suffix
+
+    if sep in str(base):
+        name_parts = str(base).rpartition(sep)
+        root = "".join([name_parts[:-1], new_suffix])
     else:
-        root = root + sep + new_suffix
-    base = root + ext
+        root = "".join([str(base), sep, new_suffix])
+    base = Path("".join([root, ext]))
 
-    new_file_name = os.path.join(directory, base)
+    new_file_name = directory / base
 
     return new_file_name
 
 
 def _is_float(arg):
path: jwst/scripts/adjust_wcs.py
user: login: emolter
body: just curious, what style rule was violated by the original code?
created_at: 2025-03-18T14:58:04Z
author_association: COLLABORATOR
diff_hunk: @@ -62,51 +45,67 @@
 
 _ANGLE_PARS = ["-r", "--ra_delta", "-d", "--dec_delta", "-o", "--roll_delta"]
 
-
 # Configure logging
 logger = logging.getLogger(__name__)
 logger.setLevel(logging.INFO)
 logger.addHandler(logging.NullHandler())
 
 
 def _replace_suffix(file, new_suffix):
-    sep = '_'
-    directory, base = os.path.split(file)
-    root, ext = os.path.splitext(base)
-    if sep in root:
-        name_parts = root.rpartition(sep)
-        root = ''.join(name_parts[:-1] + (new_suffix, ))
+    sep = "_"
+    path_object = Path(file)
+    directory, base = path_object.parent, path_object.name
+    ext = path_object.suffix
+
+    if sep in str(base):
+        name_parts = str(base).rpartition(sep)
+        root = "".join([name_parts[:-1], new_suffix])
     else:
-        root = root + sep + new_suffix
-    base = root + ext
+        root = "".join([str(base), sep, new_suffix])
+    base = Path("".join([root, ext]))
 
-    new_file_name = os.path.join(directory, base)
+    new_file_name = directory / base
 
     return new_file_name
 
 
 def _is_float(arg):
+    is_float = False
     try:
         float(arg)
-        return True
+        is_float = True
     except ValueError:
         pass
-    return False
+    return is_float
 
 
 def _is_unit(arg):
+    is_unit = False
     if arg.startswith("-"):
         return False
     try:
         units.Unit(arg)
-        return True
+        is_unit = True
     except ValueError:
         pass
-    return False
+    return is_unit
 
 
 def angle(arg):
-    args = arg.strip().split(' ')
+    """
+    Parse the angle argument to get units and convert to float.
+
+    Parameters
+    ----------
+    arg : str
+        User angle input
+
+    Returns
+    -------
+    value : float or float and unit
path: jwst/scripts/adjust_wcs.py
user: login: emolter
body: ```suggestion
    value : float or astropy.whatever.Quantity
```
created_at: 2025-03-18T14:58:43Z
author_association: COLLABORATOR
diff_hunk: @@ -116,96 +115,71 @@ def angle(arg):
 
 
 def main():
+    """
+    Standalone script to apply adjustments to an imaging JWST GWCS object.
path: jwst/scripts/adjust_wcs.py
user: login: emolter
body: ```suggestion
    Apply adjustments to an imaging JWST GWCS object.
```
created_at: 2025-03-18T14:59:35Z
author_association: COLLABORATOR
diff_hunk: @@ -116,96 +115,71 @@ def angle(arg):
 
 
 def main():
+    """
+    Standalone script to apply adjustments to an imaging JWST GWCS object.
+
+    Input files can be ``DataModel``s serialized to ASDF or ASDF-in-FITS files,
+    or they could be "simple" ASDF files storing just the GWCS model in the root
+    of the ASDF file under the key ``'wcs'``, i.e., ``asdf_file.tree['wcs']``.
+
+    Examples
+    --------
+    From command line::
+
+        % adjust_wcs [-h] (-u | --suffix SUFFIX | -f FILE) [--overwrite]
+                     [-r RA_DELTA [units]] [-d DEC_DELTA [units]]
+                     [-o ROLL_DELTA [units]] [-s SCALE_FACTOR]
+                     [-v] [input ...]
+
+        % adjust_wcs data_model_*_cal.fits -u -s 1.002
+
+        % adjust_wcs data_model_*_cal.fits --suffix wcsadj -s 1.002 -r 0.2 arcsec -o 0.0023 deg
+    """
     if len(sys.argv) <= 1:
         raise ValueError("Missing required arguments.")
 
     # Parse input parameters
     parser = argparse.ArgumentParser(
-        prog="adjust_wcs",
-        description="Apply adjustments to data model's WCS"
+        prog="adjust_wcs", description="Apply adjustments to data model's WCS"
     )
 
     parser.add_argument(
-        "arg0",
-        nargs='*',
-        metavar="input",
-        type=str,
-        help="Input data model file(s)."
+        "arg0", nargs="*", metavar="input", type=str, help="Input data model file(s)."
     )
 
     group = parser.add_mutually_exclusive_group(required=True)
-    group.add_argument(
-        "-u",
-        "--update",
-        action="store_true",
-        help="Update input data models."
-    )
-    group.add_argument(
-        "--suffix",
-        type=str,
-        help="Suffix for output files."
-    )
-    group.add_argument(
-        "-f",
-        "--file",
-        type=str,
-        help="Output file name."
-    )
+    group.add_argument("-u", "--update", action="store_true", help="Update input data models.")
+    group.add_argument("--suffix", type=str, help="Suffix for output files.")
+    group.add_argument("-f", "--file", type=str, help="Output file name.")
 
     parser.add_argument(
         "--overwrite",
         action="store_true",
-        help="Overwrite existing output files (ignored with -u/--update)."
+        help="Overwrite existing output files (ignored with -u/--update).",
     )
 
-    group_wcs = parser.add_argument_group(
-        title='WCS adjustment parameters'
-    )
-    group_wcs.add_argument(
-        "-r",
-        "--ra_delta",
-        type=angle,
-        default=0.0,
-        help="Delta RA (longitude), degrees."
-    )
+    group_wcs = parser.add_argument_group(title="WCS adjustment parameters")
     group_wcs.add_argument(
-        "-d",
-        "--dec_delta",
-        type=angle,
-        default=0.0,
-        help="Delta DEC (latitude), degrees."
+        "-r", "--ra_delta", type=angle, default=0.0, help="Delta RA (longitude), degrees."
     )
     group_wcs.add_argument(
-        "-o",
-        "--roll_delta",
-        type=angle,
-        default=0.0,
-        help="Delta roll angle, degrees."
+        "-d", "--dec_delta", type=angle, default=0.0, help="Delta DEC (latitude), degrees."
     )
     group_wcs.add_argument(
-        "-s",
-        "--scale_factor",
-        type=float,
-        default=1.0,
-        help="Scale factor."
+        "-o", "--roll_delta", type=angle, default=0.0, help="Delta roll angle, degrees."
     )
+    group_wcs.add_argument("-s", "--scale_factor", type=float, default=1.0, help="Scale factor.")
 
-    parser.add_argument(
-        "-v",
-        "--version",
-        action="version",
-        version="v{0}".format(jwst.__version__)
-    )
+    parser.add_argument("-v", "--version", action="version", version=f"v{jwst.__version__}")
 
     # pre-process argv for units and negative floats:
     argv = sys.argv[1:]
-    argv_new = [os.path.basename(sys.argv[0])]
+    argv_new = [Path(sys.argv[0]).name]
     while argv:
         argv_new.append(argv.pop(0))
 
         # check whether next argument is a float:
-        if (argv_new[-1] in _ANGLE_PARS and len(argv) >= 1 and
-                _is_float(argv[0])):
+        if argv_new[-1] in _ANGLE_PARS and len(argv) >= 1 and _is_float(argv[0]):
path: jwst/scripts/adjust_wcs.py
user: login: emolter
body: It might enhance readability to add parentheses around each of the three conditionals
created_at: 2025-03-18T15:02:03Z
author_association: COLLABORATOR
diff_hunk: @@ -1,63 +1,54 @@
 #!/usr/bin/env python
 
+"""Parse command line, read, edit, write association file."""
path: jwst/scripts/asn_edit.py
user: login: emolter
body: This is unclear; what does "parse command line" mean?  Suggest something like "Edit an association file from the command line."
created_at: 2025-03-18T15:04:38Z
author_association: COLLABORATOR
diff_hunk: @@ -1,9 +1,9 @@
 #!/usr/bin/env python
 
-"""asn_gather: Copy or Move data that is listed in an association"""
+"""Copy or Move data that is listed in an association."""
path: jwst/scripts/asn_gather.py
user: login: emolter
body: ```suggestion
"""Copy or move data that is listed in an association."""
```
created_at: 2025-03-18T15:05:27Z
author_association: COLLABORATOR
diff_hunk: @@ -1,54 +1,63 @@
 #!/usr/bin/env python
 
-import os
 import sys
+import logging
+from pathlib import Path
 
 from jwst.pipeline.collect_pipeline_cfgs import collect_pipeline_cfgs
 
 
+# Set logger to only print to screen
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+stream_handle = logging.StreamHandler()
+stream_handle.setLevel(logging.INFO)
+logger.addHandler(stream_handle)
path: jwst/scripts/collect_pipeline_cfgs.py
user: login: emolter
body: This appears to go beyond just a style change, what was the impetus for making this change?  Why is it necessary here but not in other scripts?
created_at: 2025-03-18T15:07:26Z
author_association: COLLABORATOR
diff_hunk: @@ -1,48 +1,57 @@
 #!/usr/bin/env python
 
-"""Pointing verification
+"""
+Pointing verification.
 
 Review contents of a set of given models for pointing information.
path: jwst/scripts/pointing_summary.py
user: login: emolter
body: For the other scripts, it looks like you moved the extended description into `main()`.  Would it make sense to do the same thing here?

FWIW I'm not sure what the "best practice" is.  If you put this into "main", is the extended description accessible via `pointing_summary --help`?

As a more general question, I don't see where these scripts are documented at https://jwst-pipeline.readthedocs.io/en/latest/jwst/package_index.html.  Do you happen to know where, if anywhere, the docs exist in our readthedocs build?
created_at: 2025-03-18T15:11:19Z
author_association: COLLABORATOR
diff_hunk: @@ -1,5 +1,7 @@
 #!/usr/bin/env python
 
+"""Create documentation from the schema file of a datamodel class."""
+
 # Copyright (C) 2018 Association of Universities for Research in Astronomy (AURA)
path: jwst/scripts/schemadoc.py
user: login: emolter
body: same comment here as before RE licenses
created_at: 2025-03-18T15:12:01Z
author_association: COLLABORATOR
diff_hunk: @@ -30,16 +32,25 @@
 # DAMAGE.
 
 import argparse
-import sys
+import logging
+from pathlib import Path
 
 from stdatamodels.schema import build_docstring
 
 
-def get_docstrings(template, model_names, all=False):
-    # Get the docstring for every model class
+# Set logger to only print to screen
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+stream_handle = logging.StreamHandler()
+stream_handle.setLevel(logging.INFO)
+logger.addHandler(stream_handle)
+
+
+def get_docstrings(template, model_names, all_models=False):
+    """Get the docstring for every model class."""
     from stdatamodels.jwst.datamodels import _defined_models as defined_models
path: jwst/scripts/schemadoc.py
user: login: emolter
body: I know you didn't write this, but why is this import inside the function?
created_at: 2025-03-18T15:12:53Z
author_association: COLLABORATOR
diff_hunk: @@ -50,35 +61,34 @@ def get_docstrings(template, model_names, all=False):
         try:
             docstring = build_docstring(klass, template)
         except Exception as err:
-            print(klassname, ':', str(err), file=sys.stderr)
+            logging.error(f"{klassname} : {err}")
         else:
-            print('.. ' + klassname + ' ..')
-            print(docstring, end='')
+            logging.info(f".. {klassname} ..")
+            logging.info(docstring)
 
 
 def main():
+    """Get docsitrings from a specific or all datamodel(s)."""
path: jwst/scripts/schemadoc.py
user: login: emolter
body: ```suggestion
    """Get docstrings from a specific datamodel or all datamodel(s)."""
```
created_at: 2025-03-18T15:13:21Z
author_association: COLLABORATOR
diff_hunk: @@ -1,44 +1,44 @@
 #!/usr/bin/env python
 
 """
-Set the initial world coordinate system for JWST exposures
+Set the initial world coordinate system for JWST exposures.
 
 The JWST engineering database is queried for the JWST observatory
 orientation parameters, and converts that orientation to a WCS
 for a list of exposures.
-
-Copyright
----------
-Copyright (C) 2010-2022 Association of Universities for Research in Astronomy (AURA)
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-    1. Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-
-    2. Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials provided
-      with the distribution.
-
-    3. The name of AURA and its representatives may not be used to
-      endorse or promote products derived from this software without
-      specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
 """
 
+# Copyright
path: jwst/scripts/set_telescope_pointing.py
user: login: emolter
body: same here
created_at: 2025-03-18T15:14:11Z
author_association: COLLABORATOR
diff_hunk: @@ -47,108 +47,104 @@
 import jwst.lib.set_telescope_pointing as stp
 
 # Configure logging
-logger = logging.getLogger('jwst')
+logger = logging.getLogger("jwst")
 logger.propagate = False
 logger_handler = logging.StreamHandler()
 logger.addHandler(logger_handler)
-logger_format_debug = logging.Formatter(
-    '%(levelname)s:%(filename)s::%(funcName)s: %(message)s'
-)
+logger_format_debug = logging.Formatter("%(levelname)s:%(filename)s::%(funcName)s: %(message)s")
 
 
 def main():
+    """Set the initial world coordinate system."""
     parser = argparse.ArgumentParser(
         description=(
-            'Update basic WCS information in JWST exposures from the engineering database.'
-            ' For detailed information, see'
-            ' https://jwst-pipeline.readthedocs.io/en/latest/jwst/lib/set_telescope_pointing.html'
+            "Update basic WCS information in JWST exposures from the engineering database."
+            " For detailed information, see"
+            " https://jwst-pipeline.readthedocs.io/en/latest/jwst/lib/set_telescope_pointing.html"
         )
     )
+    parser.add_argument("exposure", type=str, nargs="+", help="List of JWST exposures to update.")
     parser.add_argument(
-        'exposure', type=str, nargs='+', help='List of JWST exposures to update.'
-    )
-    parser.add_argument(
-        '-v',
-        '--verbose',
-        action='count',
+        "-v",
+        "--verbose",
+        action="count",
         default=0,
-        help='Increase verbosity. Specifying multiple times adds more output.',
+        help="Increase verbosity. Specifying multiple times adds more output.",
     )
     parser.add_argument(
-        '--allow-any-file',
-        action='store_true',
+        "--allow-any-file",
+        action="store_true",
         default=False,
-        help='Attempt to update WCS for any file or model. Default: False',
+        help="Attempt to update WCS for any file or model. Default: False",
     )
     parser.add_argument(
-        '--force-level1bmodel',
-        action='store_true',
+        "--force-level1bmodel",
+        action="store_true",
         default=False,
-        help='Force unrecognized files to be opened as Level1bModel. Default: False',
+        help="Force unrecognized files to be opened as Level1bModel. Default: False",
     )
     parser.add_argument(
-        '--allow-default',
-        action='store_true',
-        help='If pointing information cannot be determine, use header information.',
+        "--allow-default",
+        action="store_true",
+        help="If pointing information cannot be determine, use header information.",
     )
     parser.add_argument(
-        '--dry-run',
-        action='store_true',
-        help='Perform all actions but do not save the results',
+        "--dry-run",
+        action="store_true",
+        help="Perform all actions but do not save the results",
     )
     parser.add_argument(
-        '--method',
+        "--method",
         type=stp.Methods,
         choices=list(stp.Methods),
         default=stp.Methods.default,
-        help='Algorithmic method to use. Default: %(default)s',
-    )
-    parser.add_argument(
-        '--save-transforms', action='store_true', help='Save transforms.'
+        help="Algorithmic method to use. Default: %(default)s",
path: jwst/scripts/set_telescope_pointing.py
user: login: emolter
body: ```suggestion
        help="Algorithm to use. Default: %(default)s",
```
created_at: 2025-03-18T15:14:49Z
author_association: COLLABORATOR
diff_hunk: @@ -47,108 +47,104 @@
 import jwst.lib.set_telescope_pointing as stp
 
 # Configure logging
-logger = logging.getLogger('jwst')
+logger = logging.getLogger("jwst")
 logger.propagate = False
 logger_handler = logging.StreamHandler()
 logger.addHandler(logger_handler)
-logger_format_debug = logging.Formatter(
-    '%(levelname)s:%(filename)s::%(funcName)s: %(message)s'
-)
+logger_format_debug = logging.Formatter("%(levelname)s:%(filename)s::%(funcName)s: %(message)s")
 
 
 def main():
+    """Set the initial world coordinate system."""
     parser = argparse.ArgumentParser(
         description=(
-            'Update basic WCS information in JWST exposures from the engineering database.'
-            ' For detailed information, see'
-            ' https://jwst-pipeline.readthedocs.io/en/latest/jwst/lib/set_telescope_pointing.html'
+            "Update basic WCS information in JWST exposures from the engineering database."
+            " For detailed information, see"
+            " https://jwst-pipeline.readthedocs.io/en/latest/jwst/lib/set_telescope_pointing.html"
         )
     )
+    parser.add_argument("exposure", type=str, nargs="+", help="List of JWST exposures to update.")
     parser.add_argument(
-        'exposure', type=str, nargs='+', help='List of JWST exposures to update.'
-    )
-    parser.add_argument(
-        '-v',
-        '--verbose',
-        action='count',
+        "-v",
+        "--verbose",
+        action="count",
         default=0,
-        help='Increase verbosity. Specifying multiple times adds more output.',
+        help="Increase verbosity. Specifying multiple times adds more output.",
     )
     parser.add_argument(
-        '--allow-any-file',
-        action='store_true',
+        "--allow-any-file",
+        action="store_true",
         default=False,
-        help='Attempt to update WCS for any file or model. Default: False',
+        help="Attempt to update WCS for any file or model. Default: False",
     )
     parser.add_argument(
-        '--force-level1bmodel',
-        action='store_true',
+        "--force-level1bmodel",
+        action="store_true",
         default=False,
-        help='Force unrecognized files to be opened as Level1bModel. Default: False',
+        help="Force unrecognized files to be opened as Level1bModel. Default: False",
     )
     parser.add_argument(
-        '--allow-default',
-        action='store_true',
-        help='If pointing information cannot be determine, use header information.',
+        "--allow-default",
+        action="store_true",
+        help="If pointing information cannot be determine, use header information.",
     )
     parser.add_argument(
-        '--dry-run',
-        action='store_true',
-        help='Perform all actions but do not save the results',
+        "--dry-run",
+        action="store_true",
+        help="Perform all actions but do not save the results",
     )
     parser.add_argument(
-        '--method',
+        "--method",
         type=stp.Methods,
         choices=list(stp.Methods),
         default=stp.Methods.default,
-        help='Algorithmic method to use. Default: %(default)s',
-    )
-    parser.add_argument(
-        '--save-transforms', action='store_true', help='Save transforms.'
+        help="Algorithmic method to use. Default: %(default)s",
     )
+    parser.add_argument("--save-transforms", action="store_true", help="Save transforms.")
     parser.add_argument(
-        '--override-transforms',
+        "--override-transforms",
         type=str,
         default=None,
-        help='Transform matrices to use instead of calculated',
+        help="Transform matrices to use instead of calculated",
     )
     parser.add_argument(
-        '--fgsid',
+        "--fgsid",
         type=int,
         default=1,
         choices=stp.FGSIDS,
-        help='FGS to use for COARSE mode calculations. Default: %(default)s Choices: %(choices)s',
+        help="FGS to use for COARSE mode calculations. Default: %(default)s Choices: %(choices)s",
     )
     parser.add_argument(
-        '--tolerance',
+        "--tolerance",
         type=int,
         default=60,
-        help='Seconds beyond the observation time to search for telemetry. Default: %(default)s',
+        help="Seconds beyond the observation time to search for telemetry. Default: %(default)s",
     )
     parser.add_argument(
-        '--siaf',
+        "--siaf",
         type=str,
         default=None,
-        help='SIAF PRD XML folder or file as defined by the `pysiaf` package. Overrides the `prd` option',
+        help="SIAF PRD XML folder or file as defined by the `pysiaf` package. "
+        "Overrides the `prd` option",
     )
     parser.add_argument(
-        '--prd',
+        "--prd",
         type=str,
         default=None,
-        help='The PRD version to use, as delivered in the `pysiaf` package.',
+        help="The PRD version to use, as delivered in the `pysiaf` package.",
     )
     parser.add_argument(
-        '--engdb_url',
+        "--engdb_url",
         type=str,
         default=None,
         help=(
-            'URL of the engineering database.'
-            ' If not specified, the environmental variable \'ENG_BASE_URL\' is used.'
-            ' Otherwise, a hardwired default is used.'
+            "URL of the engineering database."
+            " If not specified, the environmental variable 'ENG_BASE_URL' is used."
path: jwst/scripts/set_telescope_pointing.py
user: login: emolter
body: ```suggestion
            " If not specified, the environment variable 'ENG_BASE_URL' is used."
```
created_at: 2025-03-18T15:15:12Z
author_association: COLLABORATOR
diff_hunk: @@ -194,20 +190,21 @@ def main():
                 override_transforms=override_transforms,
             )
         except (TypeError, ValueError) as exception:
-            logger.warning('Cannot determine pointing information: %s', str(exception))
-            logger.debug('Full exception:', exc_info=exception)
+            logger.warning("Cannot determine pointing information: %s", str(exception))
+            logger.debug("Full exception:", exc_info=exception)
 
 
 def deprecated_name():
+    """Raise warning if filename.* is no longer used, and provide correct one."""
     from pathlib import Path
path: jwst/scripts/set_telescope_pointing.py
user: login: emolter
body: again why is this inside the function?
created_at: 2025-03-18T15:15:55Z
author_association: COLLABORATOR
diff_hunk: @@ -1,8 +1,9 @@
 #!/usr/bin/env python
 
 """
-This script adds velocity aberration correction information to the FITS
-files provided to it on the command line (one or more).
+Add velocity aberration correction information to the FITS files provided.
path: jwst/scripts/set_velocity_aberration.py
user: login: emolter
body: same question here about whether it's best practice to have extended description at top of file or in main, or if it doesn't matter.  (Genuinely have no idea which one is better but it would be good to find out and then be consistent)
created_at: 2025-03-18T15:17:01Z
author_association: COLLABORATOR
diff_hunk: @@ -32,41 +34,58 @@
 
 
 def parse_args(args):
+    """
+    Parse given arguments.
+
+    Parameters
+    ----------
+    args : command-line input
+        The arguments to parse
+
+    Returns
+    -------
+    parser : object
+        Parsed arguments
+    """
     parser = argparse.ArgumentParser(
-        prog='set_velocity_aberration',
-        description='Add velocity aberration correction information to FITS files.',
+        prog="set_velocity_aberration",
+        description="Add velocity aberration correction information to FITS files.",
     )
     parser.add_argument(
-        'filename',
-        nargs='+',
-        help='FITS file(s) to which to add the velocity aberration correction information.'
+        "filename",
+        nargs="+",
+        help="FITS file(s) to which to add the velocity aberration correction information.",
     )
     parser.add_argument(
-        '-f', '--force_level1bmodel',
-        choices=[1,0],
+        "-f",
+        "--force_level1bmodel",
+        choices=[1, 0],
         default=1,
         type=int,
-        help='Force the input file to be treated as a Level1bModel. Options 0 (do not force) or 1 (yes, force). Default is 1.'
+        help="Force the input file to be treated as a Level1bModel. Options 0 (do not force)"
+        "or 1 (yes, force). Default is 1.",
     )
     return parser.parse_args(args)
 
 
 def main():
+    """Parse arguments and add velocity aberration correction information to the files provided."""
     args = parse_args(sys.argv[1:])
     for filename in args.filename:
         add_dva(filename)
 
 
 def deprecated_name():
+    """Raise warning if filename.* is no longer used, and provide correct one."""
     from pathlib import Path
path: jwst/scripts/set_velocity_aberration.py
user: login: emolter
body: same here
created_at: 2025-03-18T15:17:59Z
author_association: COLLABORATOR
diff_hunk: @@ -12,63 +12,82 @@
 from jwst.lib import v1_calculate
 
 # Configure logging
-logger = logging.getLogger('jwst')
+logger = logging.getLogger("jwst")
 logger.propagate = False
 logger_handler = logging.StreamHandler()
 logger.addHandler(logger_handler)
-logger_format_debug = logging.Formatter('%(levelname)s:%(filename)s::%(funcName)s: %(message)s')
+logger_format_debug = logging.Formatter("%(levelname)s:%(filename)s::%(funcName)s: %(message)s")
 
 # Available reduce functions
 REDUCE_FUNCS_MAPPING = {
-    'all': stp.all_pointings,
-    'first': stp.first_pointing,
-    'average': stp.pointing_from_average
+    "all": stp.all_pointings,
+    "first": stp.first_pointing,
+    "average": stp.pointing_from_average,
 }
 REDUCE_FUNCS = list(REDUCE_FUNCS_MAPPING.keys())
 
 
 # Begin execution
 def main():
-    parser = argparse.ArgumentParser(
-        description='Calculate V1 over a time period.'
-    )
+    """Do calculation of V1."""
path: jwst/scripts/v1_calculate.py
user: login: emolter
body: ```suggestion
    """Calculate ... (V1)"""
```
What is V1?  It seems like at least in one of these docstrings this should be defined.
created_at: 2025-03-18T15:19:41Z
author_association: COLLABORATOR
diff_hunk: @@ -12,63 +12,82 @@
 from jwst.lib import v1_calculate
 
 # Configure logging
-logger = logging.getLogger('jwst')
+logger = logging.getLogger("jwst")
 logger.propagate = False
 logger_handler = logging.StreamHandler()
 logger.addHandler(logger_handler)
-logger_format_debug = logging.Formatter('%(levelname)s:%(filename)s::%(funcName)s: %(message)s')
+logger_format_debug = logging.Formatter("%(levelname)s:%(filename)s::%(funcName)s: %(message)s")
 
 # Available reduce functions
 REDUCE_FUNCS_MAPPING = {
-    'all': stp.all_pointings,
-    'first': stp.first_pointing,
-    'average': stp.pointing_from_average
+    "all": stp.all_pointings,
+    "first": stp.first_pointing,
+    "average": stp.pointing_from_average,
 }
 REDUCE_FUNCS = list(REDUCE_FUNCS_MAPPING.keys())
 
 
 # Begin execution
 def main():
-    parser = argparse.ArgumentParser(
-        description='Calculate V1 over a time period.'
-    )
+    """Do calculation of V1."""
+    parser = argparse.ArgumentParser(description="Calculate V1 over a time period.")
 
     parser.add_argument(
-        'time_sources', type=str, nargs='+',
-        help=('Either a list of JWST data files to retrieve the timing from'
-              ' or a start and end time to retrieve pointing information for.'
-              )
+        "time_sources",
+        type=str,
+        nargs="+",
+        help=(
+            "Either a list of JWST data files to retrieve the timing from"
+            " or a start and end time to retrieve pointing information for."
+        ),
     )
     parser.add_argument(
-        '-o', '--output', type=argparse.FileType('w'), default=stdout,
-        help='File to write V1 calculation table to. Default is standard output.'
+        "-o",
+        "--output",
+        type=argparse.FileType("w"),
+        default=stdout,
+        help="File to write V1 calculation table to. Default is standard output.",
     )
     parser.add_argument(
-        '--pointing', type=str, choices=REDUCE_FUNCS, default='average',
-        help=('Which pointing(s) to use within the specified time interval.'
-              f' Choices: {REDUCE_FUNCS}'
-              ' (default: %(default)s)'
-              )
+        "--pointing",
+        type=str,
+        choices=REDUCE_FUNCS,
+        default="average",
+        help=(
+            "Which pointing(s) to use within the specified time interval."
+            f" Choices: {REDUCE_FUNCS}"
+            " (default: %(default)s)"
path: jwst/scripts/v1_calculate.py
user: login: emolter
body: Does this correctly print the default?  I've not seen this exact syntax before
created_at: 2025-03-18T15:21:08Z
author_association: COLLABORATOR
diff_hunk: @@ -12,63 +12,82 @@
 from jwst.lib import v1_calculate
 
 # Configure logging
-logger = logging.getLogger('jwst')
+logger = logging.getLogger("jwst")
 logger.propagate = False
 logger_handler = logging.StreamHandler()
 logger.addHandler(logger_handler)
-logger_format_debug = logging.Formatter('%(levelname)s:%(filename)s::%(funcName)s: %(message)s')
+logger_format_debug = logging.Formatter("%(levelname)s:%(filename)s::%(funcName)s: %(message)s")
 
 # Available reduce functions
 REDUCE_FUNCS_MAPPING = {
-    'all': stp.all_pointings,
-    'first': stp.first_pointing,
-    'average': stp.pointing_from_average
+    "all": stp.all_pointings,
+    "first": stp.first_pointing,
+    "average": stp.pointing_from_average,
 }
 REDUCE_FUNCS = list(REDUCE_FUNCS_MAPPING.keys())
 
 
 # Begin execution
 def main():
-    parser = argparse.ArgumentParser(
-        description='Calculate V1 over a time period.'
-    )
+    """Do calculation of V1."""
+    parser = argparse.ArgumentParser(description="Calculate V1 over a time period.")
 
     parser.add_argument(
-        'time_sources', type=str, nargs='+',
-        help=('Either a list of JWST data files to retrieve the timing from'
-              ' or a start and end time to retrieve pointing information for.'
-              )
+        "time_sources",
+        type=str,
+        nargs="+",
+        help=(
+            "Either a list of JWST data files to retrieve the timing from"
+            " or a start and end time to retrieve pointing information for."
+        ),
     )
     parser.add_argument(
-        '-o', '--output', type=argparse.FileType('w'), default=stdout,
-        help='File to write V1 calculation table to. Default is standard output.'
+        "-o",
+        "--output",
+        type=argparse.FileType("w"),
+        default=stdout,
+        help="File to write V1 calculation table to. Default is standard output.",
     )
     parser.add_argument(
-        '--pointing', type=str, choices=REDUCE_FUNCS, default='average',
-        help=('Which pointing(s) to use within the specified time interval.'
-              f' Choices: {REDUCE_FUNCS}'
-              ' (default: %(default)s)'
-              )
+        "--pointing",
+        type=str,
+        choices=REDUCE_FUNCS,
+        default="average",
+        help=(
+            "Which pointing(s) to use within the specified time interval."
+            f" Choices: {REDUCE_FUNCS}"
+            " (default: %(default)s)"
+        ),
     )
     parser.add_argument(
-        '--method',
-        type=stp.Methods, choices=list(stp.Methods), default=stp.Methods.default,
-        help='Algorithmic method to use. Default: %(default)s'
+        "--method",
+        type=stp.Methods,
+        choices=list(stp.Methods),
+        default=stp.Methods.default,
+        help="Algorithmic method to use. Default: %(default)s",
path: jwst/scripts/v1_calculate.py
user: login: emolter
body: same question here
created_at: 2025-03-18T15:21:35Z
author_association: COLLABORATOR
path: jwst/scripts/verify_install_requires.py
user: login: emolter
body: What is the history of this script and is it still relevant?
created_at: 2025-03-18T15:23:05Z
author_association: COLLABORATOR
diff_hunk: @@ -1,5 +1,20 @@
 #!/usr/bin/env python
 
+"""
+Read in the output of extract2d and apply the WCS transforms to all pixels in a slit.
+
+The extract2d file can be (FS and MOS) or assign_wcs (IFU).
+
+For each slit it writes the results as a cube with four planes (wavelength, ra, dec, y_slit)
+in a separate fits extension.
+
+The file is saved with a suffix 'world_coordinates'.
+
+Requested by the NIRSPEC team.
+
+Build 7.1 testing.
path: jwst/scripts/world_coords.py
user: login: emolter
body: ```suggestion
```
created_at: 2025-03-18T15:23:49Z
author_association: COLLABORATOR
diff_hunk: @@ -71,33 +80,33 @@ def main(filenames, mode):
         is not in the image header
     """
     methods = {
-        'nrs_ifu': ifu_coords,
-        'nrs_fixedslit': compute_world_coordinates,
-        'nrs_msaspec': compute_world_coordinates,
-        'nrs_taconfirm': imaging_coords,
-        'nrs_brightobj': imaging_coords,
-        'nrs_bota': imaging_coords,
-        'nrs_tacq': imaging_coords,
-        'nrs_focus': imaging_coords,
-        'nrs_lamp': imaging_coords,
-        'nrs_mimf': imaging_coords,
-        'nrs_image': imaging_coords,
-        'nrs_confirm': imaging_coords,
-        'nrs_taslit': imaging_coords,
+        "nrs_ifu": ifu_coords,
+        "nrs_fixedslit": compute_world_coordinates,
+        "nrs_msaspec": compute_world_coordinates,
+        "nrs_taconfirm": imaging_coords,
+        "nrs_brightobj": imaging_coords,
+        "nrs_bota": imaging_coords,
+        "nrs_tacq": imaging_coords,
+        "nrs_focus": imaging_coords,
+        "nrs_lamp": imaging_coords,
+        "nrs_mimf": imaging_coords,
+        "nrs_image": imaging_coords,
+        "nrs_confirm": imaging_coords,
+        "nrs_taslit": imaging_coords,
     }
 
     ok = True
     outputs = build_output_files(filenames)
-    for (input_file, output_file) in zip(filenames, outputs):
-        if not os.path.exists(input_file):
-            warn_user(input_file, 'is not found')
+    for input_file, output_file in zip(filenames, outputs, strict=False):
path: jwst/scripts/world_coords.py
user: login: emolter
body: Do we ever expect these to be different lengths?  If not, I think strict should be set to True
created_at: 2025-03-18T15:24:35Z
author_association: COLLABORATOR
diff_hunk: @@ -151,47 +160,51 @@ def build_output_files(filenames):
         outputs = []
 
     # Track if output file was a directory
-    if output is not None and os.path.isdir(output):
+    if output is not None and Path(output).is_dir():
         output_dir = output
     else:
         output_dir = None
 
     # Build an output filename for each input
     for filename in filenames:
         # Build output base and extensions
-        dirname, root = os.path.split(filename)
-        base, ext = os.path.splitext(root)
-        if ext != '.fits':
-            ext = '.fits'
-        if not base.endswith('world_coordinates'):
-            base = '_'.join([base, 'world_coordinates'])
+        path_object = Path(filename)
path: jwst/scripts/world_coords.py
user: login: emolter
body: I have similar potential concern about this refactor as the other major pathlib refactor.  How well-covered is this by tests?
created_at: 2025-03-18T15:25:49Z
author_association: COLLABORATOR
diff_hunk: @@ -151,47 +160,51 @@ def build_output_files(filenames):
         outputs = []
 
     # Track if output file was a directory
-    if output is not None and os.path.isdir(output):
+    if output is not None and Path(output).is_dir():
         output_dir = output
     else:
         output_dir = None
 
     # Build an output filename for each input
     for filename in filenames:
         # Build output base and extensions
-        dirname, root = os.path.split(filename)
-        base, ext = os.path.splitext(root)
-        if ext != '.fits':
-            ext = '.fits'
-        if not base.endswith('world_coordinates'):
-            base = '_'.join([base, 'world_coordinates'])
+        path_object = Path(filename)
+        dirname, base = path_object.parent, path_object.name
+        base = str(base)
+        ext = path_object.suffix
+        if ext != ".fits":
+            ext = ".fits"
+        if not base.endswith("world_coordinates"):
+            base = "_".join([base, "world_coordinates"])
 
         # Substitute output directory if present
         if output_dir is not None:
             dirname = output_dir
 
         # Combine directory, base, and extension
-        outputs.append(os.path.join(dirname, base + ext))
+        outputs.append(dirname / Path("".join([base, ext])))
 
     return outputs
 
 
 def compute_world_coordinates(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    FS or MOS observation after running extract_2d.
+    Compute wavelengths and space coordinates of an NRS FS or MOS obse after running extract_2d.
path: jwst/scripts/world_coords.py
user: login: emolter
body: ```suggestion
    Compute wavelengths and space coordinates of an NRS FS or MOS observation after running extract_2d.
```
created_at: 2025-03-18T15:26:24Z
author_association: COLLABORATOR
diff_hunk: @@ -151,47 +160,51 @@ def build_output_files(filenames):
         outputs = []
 
     # Track if output file was a directory
-    if output is not None and os.path.isdir(output):
+    if output is not None and Path(output).is_dir():
         output_dir = output
     else:
         output_dir = None
 
     # Build an output filename for each input
     for filename in filenames:
         # Build output base and extensions
-        dirname, root = os.path.split(filename)
-        base, ext = os.path.splitext(root)
-        if ext != '.fits':
-            ext = '.fits'
-        if not base.endswith('world_coordinates'):
-            base = '_'.join([base, 'world_coordinates'])
+        path_object = Path(filename)
+        dirname, base = path_object.parent, path_object.name
+        base = str(base)
+        ext = path_object.suffix
+        if ext != ".fits":
+            ext = ".fits"
+        if not base.endswith("world_coordinates"):
+            base = "_".join([base, "world_coordinates"])
 
         # Substitute output directory if present
         if output_dir is not None:
             dirname = output_dir
 
         # Combine directory, base, and extension
-        outputs.append(os.path.join(dirname, base + ext))
+        outputs.append(dirname / Path("".join([base, ext])))
 
     return outputs
 
 
 def compute_world_coordinates(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    FS or MOS observation after running extract_2d.
+    Compute wavelengths and space coordinates of an NRS FS or MOS obse after running extract_2d.
 
     Parameters
     ----------
     model : ImageModel
         An image with extracted slits, i.e. the output of extract2d.
 
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits object
path: jwst/scripts/world_coords.py
user: login: emolter
body: Can you be more specific here?  Is it a `fits.HDUList`?
created_at: 2025-03-18T15:27:37Z
author_association: COLLABORATOR
diff_hunk: @@ -258,129 +272,125 @@ def get_update_method(methods, model, mode):
                     break
 
         if exp_type is None:
-            warn_user(mode, 'does not match an exposure type')
+            warn_user(mode, "does not match an exposure type")
         else:
             model.meta.exposure.type = exp_type.upper()
 
     exp_type = exp_type.lower()
     method = methods.get(exp_type)
     if method is None:
-        warnings.warn(exp_type, 'is not a supported exposure type')
+        warnings.warn(exp_type, "is not a supported exposure type", stacklevel=2)
 
     return method
 
 
 def ifu_coords(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    IFU exposure after running assign_wcs.
+    Compute wavelengths, and space coordinates of a NIRSPEC IFU exposure after running assign_wcs.
 
     Parameters
     ----------
     model : ImageModel
         An image with extracted slits, i.e. the output of extract2d.
 
-
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits object
+        Fits hdulist
     """
     ifu_slits = nirspec.nrs_ifu_wcs(model)
 
     hdulist = fits.HDUList()
     phdu = fits.PrimaryHDU()
-    phdu.header['filename'] = model.meta.filename
-    phdu.header['data'] = 'world coordinates'
+    phdu.header["filename"] = model.meta.filename
+    phdu.header["data"] = "world coordinates"
     hdulist.append(phdu)
     output_frame = ifu_slits[0].available_frames[-1]
 
     for i, slit in enumerate(ifu_slits):
         x, y = wcstools.grid_from_bounding_box(slit.bounding_box, (1, 1), center=True)
         ra, dec, lam = slit(x, y)
-        detector2slit = slit.get_transform('detector', 'slit_frame')
+        detector2slit = slit.get_transform("detector", "slit_frame")
         sx, sy, ls = detector2slit(x, y)
         world_coordinates = np.array([lam, ra, dec, sy])
 
         imhdu = fits.ImageHDU(data=world_coordinates)
-        imhdu.header['PLANE1'] = 'lambda, microns'
-        imhdu.header['PLANE2'] = '{0}_x, arcsec'.format(output_frame)
-        imhdu.header['PLANE3'] = '{0}_y, arcsec'.format(output_frame)
-        imhdu.header['PLANE4'] = 'slit_y, relative to center (0, 0)'
-        imhdu.header['SLIT'] = 'SLIT_{0}'.format(i)
+        imhdu.header["PLANE1"] = "lambda, microns"
+        imhdu.header["PLANE2"] = f"{output_frame}_x, arcsec"
+        imhdu.header["PLANE3"] = f"{output_frame}_y, arcsec"
+        imhdu.header["PLANE4"] = "slit_y, relative to center (0, 0)"
+        imhdu.header["SLIT"] = f"SLIT_{i}"
 
         # -1 and +1 are to express this in 1-based coordinates
-        imhdu.header['CRVAL1'] = (
+        imhdu.header["CRVAL1"] = (
             model.meta.subarray.xstart - 1 + int(_toindex(slit.bounding_box[0][0])) + 1
         )
-        imhdu.header['CRVAL2'] = (
+        imhdu.header["CRVAL2"] = (
             model.meta.subarray.xstart - 1 + int(_toindex(slit.bounding_box[1][0])) + 1
         )
 
         # Input coordinates will be 1-based.
-        imhdu.header['CRPIX1'] = 1
-        imhdu.header['CRPIX2'] = 1
-        imhdu.header['CTYPE1'] = 'pixel'
-        imhdu.header['CTYPE2'] = 'pixel'
+        imhdu.header["CRPIX1"] = 1
+        imhdu.header["CRPIX2"] = 1
+        imhdu.header["CTYPE1"] = "pixel"
+        imhdu.header["CTYPE2"] = "pixel"
         hdulist.append(imhdu)
     return hdulist
 
 
 def imaging_coords(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    imaging exposure after running assign_wcs.
+    Compute wavelengths and space coordinates of a NIRSPEC imaging exposure after assign_wcs.
 
     Parameters
     ----------
     model : ImageModel
         An image with extracted slits, i.e. the output of extract2d.
 
-
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits object
+        Fits hdulist
     """
     hdulist = fits.HDUList()
     phdu = fits.PrimaryHDU()
-    phdu.header['filename'] = model.meta.filename
-    phdu.header['data'] = 'world coordinates'
+    phdu.header["filename"] = model.meta.filename
+    phdu.header["data"] = "world coordinates"
     hdulist.append(phdu)
 
     output_frame = model.available_frames[-1]
-    bb = model.meta.wcs.bounding_box
-    x, y = wcstools.grid_from_bounding_box(bb, step=(1, 1), center=True)
-    ra, dec, lam = slit(x + 1, y + 1)  # noqa
-    world_coordinates = np.array([lam, ra, dec])
-
-    imhdu = fits.ImageHDU(data=world_coordinates)
-    imhdu.header['PLANE1'] = 'lambda, microns'
-    imhdu.header['PLANE2'] = '{0}_x, deg'.format(output_frame)
-    imhdu.header['PLANE3'] = '{0}_y, deg'.format(output_frame)
-    imhdu.header['SLIT'] = 'SLIT_{0}'.format(i)  # noqa
-
-    # add the overall subarray offset
-    imhdu.header['CRVAL1'] = model.meta.subarray.xstart - 1 + int(_toindex(bb[0][0]))
-    imhdu.header['CRVAL2'] = model.meta.subarray.ystart - 1 + int(_toindex(bb[1][0]))
-
-    # Input coordinates will be 1-based.
-    imhdu.header['CRPIX1'] = 1
-    imhdu.header['CRPIX2'] = 1
-    imhdu.header['CTYPE1'] = 'pixel'
-    imhdu.header['CTYPE2'] = 'pixel'
-    hdulist.append(imhdu)
+    for i, slit in enumerate(output_frame):
path: jwst/scripts/world_coords.py
user: login: emolter
body: Why was this for loop added?
created_at: 2025-03-18T15:29:00Z
author_association: COLLABORATOR
diff_hunk: @@ -1,4 +1,7 @@
 #!/usr/bin/env python
+
+"""Apply adjustments to data model's WCS."""
+
 # Copyright (C) 2023 Association of Universities for Research in Astronomy (AURA)
path: jwst/scripts/adjust_wcs.py
user: login: tapastro
body: I agree that we probably don't need this license here - @nden okay to remove?
created_at: 2025-03-25T18:44:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,4 +1,7 @@
 #!/usr/bin/env python
+
+"""Apply adjustments to data model's WCS."""
+
 # Copyright (C) 2023 Association of Universities for Research in Astronomy (AURA)
path: jwst/scripts/adjust_wcs.py
user: login: pllim
body: FWIW it is overkill to repeat the full license here. If you want, you can do something like this instead though even that is meaningless unless it is consistent across the repo.

```python
# Licensed under a 3-clause BSD style license - see LICENSE
```
created_at: 2025-03-25T18:47:24Z
author_association: COLLABORATOR
diff_hunk: @@ -62,51 +45,67 @@
 
 _ANGLE_PARS = ["-r", "--ra_delta", "-d", "--dec_delta", "-o", "--roll_delta"]
 
-
 # Configure logging
 logger = logging.getLogger(__name__)
 logger.setLevel(logging.INFO)
 logger.addHandler(logging.NullHandler())
 
 
 def _replace_suffix(file, new_suffix):
-    sep = '_'
-    directory, base = os.path.split(file)
-    root, ext = os.path.splitext(base)
-    if sep in root:
-        name_parts = root.rpartition(sep)
-        root = ''.join(name_parts[:-1] + (new_suffix, ))
+    sep = "_"
+    path_object = Path(file)
+    directory, base = path_object.parent, path_object.name
+    ext = path_object.suffix
+
+    if sep in str(base):
+        name_parts = str(base).rpartition(sep)
+        root = "".join([name_parts[:-1], new_suffix])
     else:
-        root = root + sep + new_suffix
-    base = root + ext
+        root = "".join([str(base), sep, new_suffix])
+    base = Path("".join([root, ext]))
 
-    new_file_name = os.path.join(directory, base)
+    new_file_name = directory / base
 
     return new_file_name
 
 
 def _is_float(arg):
path: jwst/scripts/adjust_wcs.py
user: login: penaguerrero
body: It triggers rule PTH118.
created_at: 2025-03-25T19:47:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -62,51 +45,67 @@
 
 _ANGLE_PARS = ["-r", "--ra_delta", "-d", "--dec_delta", "-o", "--roll_delta"]
 
-
 # Configure logging
 logger = logging.getLogger(__name__)
 logger.setLevel(logging.INFO)
 logger.addHandler(logging.NullHandler())
 
 
 def _replace_suffix(file, new_suffix):
path: jwst/scripts/adjust_wcs.py
user: login: penaguerrero
body: For now, I will revert the path changes so that nothing breaks.
created_at: 2025-03-26T15:38:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,54 +1,63 @@
 #!/usr/bin/env python
 
-import os
 import sys
+import logging
+from pathlib import Path
 
 from jwst.pipeline.collect_pipeline_cfgs import collect_pipeline_cfgs
 
 
+# Set logger to only print to screen
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+stream_handle = logging.StreamHandler()
+stream_handle.setLevel(logging.INFO)
+logger.addHandler(stream_handle)
path: jwst/scripts/collect_pipeline_cfgs.py
user: login: penaguerrero
body: Ruff is complaining about the print statements, so I added this to remove them and still print to screen.
created_at: 2025-03-26T15:45:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,48 +1,57 @@
 #!/usr/bin/env python
 
-"""Pointing verification
+"""
+Pointing verification.
 
 Review contents of a set of given models for pointing information.
path: jwst/scripts/pointing_summary.py
user: login: penaguerrero
body: yep, the idea is that the description shows up with --help. 

I only found it here: https://jwst-pipeline.readthedocs.io/en/latest/jwst/lib/set_telescope_pointing.html
created_at: 2025-03-26T16:09:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -30,16 +32,25 @@
 # DAMAGE.
 
 import argparse
-import sys
+import logging
+from pathlib import Path
 
 from stdatamodels.schema import build_docstring
 
 
-def get_docstrings(template, model_names, all=False):
-    # Get the docstring for every model class
+# Set logger to only print to screen
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+stream_handle = logging.StreamHandler()
+stream_handle.setLevel(logging.INFO)
+logger.addHandler(stream_handle)
+
+
+def get_docstrings(template, model_names, all_models=False):
+    """Get the docstring for every model class."""
     from stdatamodels.jwst.datamodels import _defined_models as defined_models
path: jwst/scripts/schemadoc.py
user: login: penaguerrero
body: yep, I didn't like this either so I moved it up with the other imports.
created_at: 2025-03-26T16:25:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,63 +12,82 @@
 from jwst.lib import v1_calculate
 
 # Configure logging
-logger = logging.getLogger('jwst')
+logger = logging.getLogger("jwst")
 logger.propagate = False
 logger_handler = logging.StreamHandler()
 logger.addHandler(logger_handler)
-logger_format_debug = logging.Formatter('%(levelname)s:%(filename)s::%(funcName)s: %(message)s')
+logger_format_debug = logging.Formatter("%(levelname)s:%(filename)s::%(funcName)s: %(message)s")
 
 # Available reduce functions
 REDUCE_FUNCS_MAPPING = {
-    'all': stp.all_pointings,
-    'first': stp.first_pointing,
-    'average': stp.pointing_from_average
+    "all": stp.all_pointings,
+    "first": stp.first_pointing,
+    "average": stp.pointing_from_average,
 }
 REDUCE_FUNCS = list(REDUCE_FUNCS_MAPPING.keys())
 
 
 # Begin execution
 def main():
-    parser = argparse.ArgumentParser(
-        description='Calculate V1 over a time period.'
-    )
+    """Do calculation of V1."""
+    parser = argparse.ArgumentParser(description="Calculate V1 over a time period.")
 
     parser.add_argument(
-        'time_sources', type=str, nargs='+',
-        help=('Either a list of JWST data files to retrieve the timing from'
-              ' or a start and end time to retrieve pointing information for.'
-              )
+        "time_sources",
+        type=str,
+        nargs="+",
+        help=(
+            "Either a list of JWST data files to retrieve the timing from"
+            " or a start and end time to retrieve pointing information for."
+        ),
     )
     parser.add_argument(
-        '-o', '--output', type=argparse.FileType('w'), default=stdout,
-        help='File to write V1 calculation table to. Default is standard output.'
+        "-o",
+        "--output",
+        type=argparse.FileType("w"),
+        default=stdout,
+        help="File to write V1 calculation table to. Default is standard output.",
     )
     parser.add_argument(
-        '--pointing', type=str, choices=REDUCE_FUNCS, default='average',
-        help=('Which pointing(s) to use within the specified time interval.'
-              f' Choices: {REDUCE_FUNCS}'
-              ' (default: %(default)s)'
-              )
+        "--pointing",
+        type=str,
+        choices=REDUCE_FUNCS,
+        default="average",
+        help=(
+            "Which pointing(s) to use within the specified time interval."
+            f" Choices: {REDUCE_FUNCS}"
+            " (default: %(default)s)"
path: jwst/scripts/v1_calculate.py
user: login: penaguerrero
body: me either, but it works
created_at: 2025-03-26T20:44:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,5 +1,20 @@
 #!/usr/bin/env python
 
+"""
+Read in the output of extract2d and apply the WCS transforms to all pixels in a slit.
+
+The extract2d file can be (FS and MOS) or assign_wcs (IFU).
+
+For each slit it writes the results as a cube with four planes (wavelength, ra, dec, y_slit)
+in a separate fits extension.
+
+The file is saved with a suffix 'world_coordinates'.
+
+Requested by the NIRSPEC team.
+
+Build 7.1 testing.
path: jwst/scripts/world_coords.py
user: login: penaguerrero
body: I decided to re-word but leave it there to preserve history.
created_at: 2025-03-26T20:51:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -151,47 +160,51 @@ def build_output_files(filenames):
         outputs = []
 
     # Track if output file was a directory
-    if output is not None and os.path.isdir(output):
+    if output is not None and Path(output).is_dir():
         output_dir = output
     else:
         output_dir = None
 
     # Build an output filename for each input
     for filename in filenames:
         # Build output base and extensions
-        dirname, root = os.path.split(filename)
-        base, ext = os.path.splitext(root)
-        if ext != '.fits':
-            ext = '.fits'
-        if not base.endswith('world_coordinates'):
-            base = '_'.join([base, 'world_coordinates'])
+        path_object = Path(filename)
path: jwst/scripts/world_coords.py
user: login: penaguerrero
body: It is not covered by tests, but I feel more confident with this script because the code is easier to follow and the path changes are not as extensive. 
created_at: 2025-03-27T15:19:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -258,129 +272,125 @@ def get_update_method(methods, model, mode):
                     break
 
         if exp_type is None:
-            warn_user(mode, 'does not match an exposure type')
+            warn_user(mode, "does not match an exposure type")
         else:
             model.meta.exposure.type = exp_type.upper()
 
     exp_type = exp_type.lower()
     method = methods.get(exp_type)
     if method is None:
-        warnings.warn(exp_type, 'is not a supported exposure type')
+        warnings.warn(exp_type, "is not a supported exposure type", stacklevel=2)
 
     return method
 
 
 def ifu_coords(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    IFU exposure after running assign_wcs.
+    Compute wavelengths, and space coordinates of a NIRSPEC IFU exposure after running assign_wcs.
 
     Parameters
     ----------
     model : ImageModel
         An image with extracted slits, i.e. the output of extract2d.
 
-
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits object
+        Fits hdulist
     """
     ifu_slits = nirspec.nrs_ifu_wcs(model)
 
     hdulist = fits.HDUList()
     phdu = fits.PrimaryHDU()
-    phdu.header['filename'] = model.meta.filename
-    phdu.header['data'] = 'world coordinates'
+    phdu.header["filename"] = model.meta.filename
+    phdu.header["data"] = "world coordinates"
     hdulist.append(phdu)
     output_frame = ifu_slits[0].available_frames[-1]
 
     for i, slit in enumerate(ifu_slits):
         x, y = wcstools.grid_from_bounding_box(slit.bounding_box, (1, 1), center=True)
         ra, dec, lam = slit(x, y)
-        detector2slit = slit.get_transform('detector', 'slit_frame')
+        detector2slit = slit.get_transform("detector", "slit_frame")
         sx, sy, ls = detector2slit(x, y)
         world_coordinates = np.array([lam, ra, dec, sy])
 
         imhdu = fits.ImageHDU(data=world_coordinates)
-        imhdu.header['PLANE1'] = 'lambda, microns'
-        imhdu.header['PLANE2'] = '{0}_x, arcsec'.format(output_frame)
-        imhdu.header['PLANE3'] = '{0}_y, arcsec'.format(output_frame)
-        imhdu.header['PLANE4'] = 'slit_y, relative to center (0, 0)'
-        imhdu.header['SLIT'] = 'SLIT_{0}'.format(i)
+        imhdu.header["PLANE1"] = "lambda, microns"
+        imhdu.header["PLANE2"] = f"{output_frame}_x, arcsec"
+        imhdu.header["PLANE3"] = f"{output_frame}_y, arcsec"
+        imhdu.header["PLANE4"] = "slit_y, relative to center (0, 0)"
+        imhdu.header["SLIT"] = f"SLIT_{i}"
 
         # -1 and +1 are to express this in 1-based coordinates
-        imhdu.header['CRVAL1'] = (
+        imhdu.header["CRVAL1"] = (
             model.meta.subarray.xstart - 1 + int(_toindex(slit.bounding_box[0][0])) + 1
         )
-        imhdu.header['CRVAL2'] = (
+        imhdu.header["CRVAL2"] = (
             model.meta.subarray.xstart - 1 + int(_toindex(slit.bounding_box[1][0])) + 1
         )
 
         # Input coordinates will be 1-based.
-        imhdu.header['CRPIX1'] = 1
-        imhdu.header['CRPIX2'] = 1
-        imhdu.header['CTYPE1'] = 'pixel'
-        imhdu.header['CTYPE2'] = 'pixel'
+        imhdu.header["CRPIX1"] = 1
+        imhdu.header["CRPIX2"] = 1
+        imhdu.header["CTYPE1"] = "pixel"
+        imhdu.header["CTYPE2"] = "pixel"
         hdulist.append(imhdu)
     return hdulist
 
 
 def imaging_coords(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    imaging exposure after running assign_wcs.
+    Compute wavelengths and space coordinates of a NIRSPEC imaging exposure after assign_wcs.
 
     Parameters
     ----------
     model : ImageModel
         An image with extracted slits, i.e. the output of extract2d.
 
-
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits object
+        Fits hdulist
     """
     hdulist = fits.HDUList()
     phdu = fits.PrimaryHDU()
-    phdu.header['filename'] = model.meta.filename
-    phdu.header['data'] = 'world coordinates'
+    phdu.header["filename"] = model.meta.filename
+    phdu.header["data"] = "world coordinates"
     hdulist.append(phdu)
 
     output_frame = model.available_frames[-1]
-    bb = model.meta.wcs.bounding_box
-    x, y = wcstools.grid_from_bounding_box(bb, step=(1, 1), center=True)
-    ra, dec, lam = slit(x + 1, y + 1)  # noqa
-    world_coordinates = np.array([lam, ra, dec])
-
-    imhdu = fits.ImageHDU(data=world_coordinates)
-    imhdu.header['PLANE1'] = 'lambda, microns'
-    imhdu.header['PLANE2'] = '{0}_x, deg'.format(output_frame)
-    imhdu.header['PLANE3'] = '{0}_y, deg'.format(output_frame)
-    imhdu.header['SLIT'] = 'SLIT_{0}'.format(i)  # noqa
-
-    # add the overall subarray offset
-    imhdu.header['CRVAL1'] = model.meta.subarray.xstart - 1 + int(_toindex(bb[0][0]))
-    imhdu.header['CRVAL2'] = model.meta.subarray.ystart - 1 + int(_toindex(bb[1][0]))
-
-    # Input coordinates will be 1-based.
-    imhdu.header['CRPIX1'] = 1
-    imhdu.header['CRPIX2'] = 1
-    imhdu.header['CTYPE1'] = 'pixel'
-    imhdu.header['CTYPE2'] = 'pixel'
-    hdulist.append(imhdu)
+    for i, slit in enumerate(output_frame):
path: jwst/scripts/world_coords.py
user: login: penaguerrero
body: Because "slit" and "i" were not defined and the code was breaking.
created_at: 2025-03-27T15:23:45Z
author_association: CONTRIBUTOR
path: jwst/scripts/verify_install_requires.py
user: login: penaguerrero
body: I don't know, maybe @tapastro or @nden can comment?
created_at: 2025-03-27T15:28:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -62,51 +45,67 @@
 
 _ANGLE_PARS = ["-r", "--ra_delta", "-d", "--dec_delta", "-o", "--roll_delta"]
 
-
 # Configure logging
 logger = logging.getLogger(__name__)
 logger.setLevel(logging.INFO)
 logger.addHandler(logging.NullHandler())
 
 
 def _replace_suffix(file, new_suffix):
-    sep = '_'
-    directory, base = os.path.split(file)
-    root, ext = os.path.splitext(base)
-    if sep in root:
-        name_parts = root.rpartition(sep)
-        root = ''.join(name_parts[:-1] + (new_suffix, ))
+    sep = "_"
+    path_object = Path(file)
+    directory, base = path_object.parent, path_object.name
+    ext = path_object.suffix
+
+    if sep in str(base):
+        name_parts = str(base).rpartition(sep)
+        root = "".join([name_parts[:-1], new_suffix])
     else:
-        root = root + sep + new_suffix
-    base = root + ext
+        root = "".join([str(base), sep, new_suffix])
+    base = Path("".join([root, ext]))
 
-    new_file_name = os.path.join(directory, base)
+    new_file_name = directory / base
 
     return new_file_name
 
 
 def _is_float(arg):
path: jwst/scripts/adjust_wcs.py
user: login: emolter
body: wait I'm confused. This comment was referring to the changes in function `_is_float()` which doesn't contain paths. Same for `_is_unit()`
created_at: 2025-03-27T15:41:06Z
author_association: COLLABORATOR
diff_hunk: @@ -1,54 +1,63 @@
 #!/usr/bin/env python
 
-import os
 import sys
+import logging
+from pathlib import Path
 
 from jwst.pipeline.collect_pipeline_cfgs import collect_pipeline_cfgs
 
 
+# Set logger to only print to screen
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+stream_handle = logging.StreamHandler()
+stream_handle.setLevel(logging.INFO)
+logger.addHandler(stream_handle)
path: jwst/scripts/collect_pipeline_cfgs.py
user: login: emolter
body: I guess that seems reasonable, but wouldn't the logger print to the terminal by default anyway, unless you've specified something else in the log config?  Does this override the log config?
created_at: 2025-03-27T15:46:24Z
author_association: COLLABORATOR
diff_hunk: @@ -1,45 +1,28 @@
 #!/usr/bin/env python
 
-# Copyright (C) 2018 Association of Universities for Research in Astronomy (AURA)
+"""Create documentation from the schema file of a datamodel class."""
 
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-
-#     1. Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-
-#     2. Redistributions in binary form must reproduce the above
-#       copyright notice, this list of conditions and the following
-#       disclaimer in the documentation and/or other materials provided
-#       with the distribution.
-
-#     3. The name of AURA and its representatives may not be used to
-#       endorse or promote products derived from this software without
-#       specific prior written permission.
-
-# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
-# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-# DAMAGE.
+# Licensed under a 3-clause BSD style license - see LICENSE
path: jwst/scripts/schemadoc.py
user: login: emolter
body: just flagging this again to see if this is the "final answer" we're going with RE licensing.
created_at: 2025-03-27T15:47:50Z
author_association: COLLABORATOR
diff_hunk: @@ -1,51 +1,24 @@
 #!/usr/bin/env python
 
-# Copyright (C) 2018 Association of Universities for Research in Astronomy (AURA)
-
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-
-#     1. Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-
-#     2. Redistributions in binary form must reproduce the above
-#       copyright notice, this list of conditions and the following
-#       disclaimer in the documentation and/or other materials provided
-#       with the distribution.
-
-#     3. The name of AURA and its representatives may not be used to
-#       endorse or promote products derived from this software without
-#       specific prior written permission.
+"""
+Read in the output of extract_2d and apply the WCS transforms to all pixels in a slit.
 
-# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
-# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-# DAMAGE.
+The extract_2d file can be (FS and MOS) or assign_wcs (IFU).
 
+For each slit it writes the results as a cube with four planes (wavelength, ra, dec, y_slit)
+in a separate fits extension.
 
-"""
-A tool to read in the output of extract2d (FS and MOS) or assign_wcs (IFU) and
-apply the WCS transforms to all pixels in a slit. For each slit it writes the results
-as a cube with four planes (wavelength, ra, dec, y_slit) in a separate fits extension.
 The file is saved with a suffix 'world_coordinates'.
 
-Requested by the NIRSPEC team.
-
-Build 7.1 testing.
+Requested by the NIRSPEC team for build 7.1 testing.
path: jwst/scripts/world_coords.py
user: login: emolter
body: I'm still not convinced this is a helpful comment, build 7.1 testing happened 7 years ago
created_at: 2025-03-27T15:51:26Z
author_association: COLLABORATOR
diff_hunk: @@ -258,129 +272,125 @@ def get_update_method(methods, model, mode):
                     break
 
         if exp_type is None:
-            warn_user(mode, 'does not match an exposure type')
+            warn_user(mode, "does not match an exposure type")
         else:
             model.meta.exposure.type = exp_type.upper()
 
     exp_type = exp_type.lower()
     method = methods.get(exp_type)
     if method is None:
-        warnings.warn(exp_type, 'is not a supported exposure type')
+        warnings.warn(exp_type, "is not a supported exposure type", stacklevel=2)
 
     return method
 
 
 def ifu_coords(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    IFU exposure after running assign_wcs.
+    Compute wavelengths, and space coordinates of a NIRSPEC IFU exposure after running assign_wcs.
 
     Parameters
     ----------
     model : ImageModel
         An image with extracted slits, i.e. the output of extract2d.
 
-
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits object
+        Fits hdulist
     """
     ifu_slits = nirspec.nrs_ifu_wcs(model)
 
     hdulist = fits.HDUList()
     phdu = fits.PrimaryHDU()
-    phdu.header['filename'] = model.meta.filename
-    phdu.header['data'] = 'world coordinates'
+    phdu.header["filename"] = model.meta.filename
+    phdu.header["data"] = "world coordinates"
     hdulist.append(phdu)
     output_frame = ifu_slits[0].available_frames[-1]
 
     for i, slit in enumerate(ifu_slits):
         x, y = wcstools.grid_from_bounding_box(slit.bounding_box, (1, 1), center=True)
         ra, dec, lam = slit(x, y)
-        detector2slit = slit.get_transform('detector', 'slit_frame')
+        detector2slit = slit.get_transform("detector", "slit_frame")
         sx, sy, ls = detector2slit(x, y)
         world_coordinates = np.array([lam, ra, dec, sy])
 
         imhdu = fits.ImageHDU(data=world_coordinates)
-        imhdu.header['PLANE1'] = 'lambda, microns'
-        imhdu.header['PLANE2'] = '{0}_x, arcsec'.format(output_frame)
-        imhdu.header['PLANE3'] = '{0}_y, arcsec'.format(output_frame)
-        imhdu.header['PLANE4'] = 'slit_y, relative to center (0, 0)'
-        imhdu.header['SLIT'] = 'SLIT_{0}'.format(i)
+        imhdu.header["PLANE1"] = "lambda, microns"
+        imhdu.header["PLANE2"] = f"{output_frame}_x, arcsec"
+        imhdu.header["PLANE3"] = f"{output_frame}_y, arcsec"
+        imhdu.header["PLANE4"] = "slit_y, relative to center (0, 0)"
+        imhdu.header["SLIT"] = f"SLIT_{i}"
 
         # -1 and +1 are to express this in 1-based coordinates
-        imhdu.header['CRVAL1'] = (
+        imhdu.header["CRVAL1"] = (
             model.meta.subarray.xstart - 1 + int(_toindex(slit.bounding_box[0][0])) + 1
         )
-        imhdu.header['CRVAL2'] = (
+        imhdu.header["CRVAL2"] = (
             model.meta.subarray.xstart - 1 + int(_toindex(slit.bounding_box[1][0])) + 1
         )
 
         # Input coordinates will be 1-based.
-        imhdu.header['CRPIX1'] = 1
-        imhdu.header['CRPIX2'] = 1
-        imhdu.header['CTYPE1'] = 'pixel'
-        imhdu.header['CTYPE2'] = 'pixel'
+        imhdu.header["CRPIX1"] = 1
+        imhdu.header["CRPIX2"] = 1
+        imhdu.header["CTYPE1"] = "pixel"
+        imhdu.header["CTYPE2"] = "pixel"
         hdulist.append(imhdu)
     return hdulist
 
 
 def imaging_coords(model):
     """
-    Computes wavelengths, and space coordinates of a NIRSPEC
-    imaging exposure after running assign_wcs.
+    Compute wavelengths and space coordinates of a NIRSPEC imaging exposure after assign_wcs.
 
     Parameters
     ----------
     model : ImageModel
         An image with extracted slits, i.e. the output of extract2d.
 
-
-    returns a fits hdulist
+    Returns
+    -------
+    hdulist : fits object
+        Fits hdulist
     """
     hdulist = fits.HDUList()
     phdu = fits.PrimaryHDU()
-    phdu.header['filename'] = model.meta.filename
-    phdu.header['data'] = 'world coordinates'
+    phdu.header["filename"] = model.meta.filename
+    phdu.header["data"] = "world coordinates"
     hdulist.append(phdu)
 
     output_frame = model.available_frames[-1]
-    bb = model.meta.wcs.bounding_box
-    x, y = wcstools.grid_from_bounding_box(bb, step=(1, 1), center=True)
-    ra, dec, lam = slit(x + 1, y + 1)  # noqa
-    world_coordinates = np.array([lam, ra, dec])
-
-    imhdu = fits.ImageHDU(data=world_coordinates)
-    imhdu.header['PLANE1'] = 'lambda, microns'
-    imhdu.header['PLANE2'] = '{0}_x, deg'.format(output_frame)
-    imhdu.header['PLANE3'] = '{0}_y, deg'.format(output_frame)
-    imhdu.header['SLIT'] = 'SLIT_{0}'.format(i)  # noqa
-
-    # add the overall subarray offset
-    imhdu.header['CRVAL1'] = model.meta.subarray.xstart - 1 + int(_toindex(bb[0][0]))
-    imhdu.header['CRVAL2'] = model.meta.subarray.ystart - 1 + int(_toindex(bb[1][0]))
-
-    # Input coordinates will be 1-based.
-    imhdu.header['CRPIX1'] = 1
-    imhdu.header['CRPIX2'] = 1
-    imhdu.header['CTYPE1'] = 'pixel'
-    imhdu.header['CTYPE2'] = 'pixel'
-    hdulist.append(imhdu)
+    for i, slit in enumerate(output_frame):
path: jwst/scripts/world_coords.py
user: login: emolter
body: Gotcha. Thanks for finding and fixing the bug.  It still doesn't make me feel very comfortable that there are no tests of these changes (or for the original script), but having something that at least runs, and looks like it works properly, is probably better than an error.
created_at: 2025-03-27T15:56:59Z
author_association: COLLABORATOR
diff_hunk: @@ -1,48 +1,57 @@
 #!/usr/bin/env python
 
-"""Pointing verification
+"""
+Pointing verification.
 
 Review contents of a set of given models for pointing information.
path: jwst/scripts/pointing_summary.py
user: login: penaguerrero
body: Depending on what INS says regards usage of this particular script, we can add more documentation later
created_at: 2025-04-04T16:12:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,51 +1,24 @@
 #!/usr/bin/env python
 
-# Copyright (C) 2018 Association of Universities for Research in Astronomy (AURA)
-
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-
-#     1. Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-
-#     2. Redistributions in binary form must reproduce the above
-#       copyright notice, this list of conditions and the following
-#       disclaimer in the documentation and/or other materials provided
-#       with the distribution.
-
-#     3. The name of AURA and its representatives may not be used to
-#       endorse or promote products derived from this software without
-#       specific prior written permission.
+"""
+Read in the output of extract_2d and apply the WCS transforms to all pixels in a slit.
 
-# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
-# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-# DAMAGE.
+The extract_2d file can be (FS and MOS) or assign_wcs (IFU).
 
+For each slit it writes the results as a cube with four planes (wavelength, ra, dec, y_slit)
+in a separate fits extension.
 
-"""
-A tool to read in the output of extract2d (FS and MOS) or assign_wcs (IFU) and
-apply the WCS transforms to all pixels in a slit. For each slit it writes the results
-as a cube with four planes (wavelength, ra, dec, y_slit) in a separate fits extension.
 The file is saved with a suffix 'world_coordinates'.
 
-Requested by the NIRSPEC team.
-
-Build 7.1 testing.
+Requested by the NIRSPEC team for build 7.1 testing.
path: jwst/scripts/world_coords.py
user: login: penaguerrero
body: Ok, I removed it.
created_at: 2025-04-04T16:22:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -151,47 +160,51 @@ def build_output_files(filenames):
         outputs = []
 
     # Track if output file was a directory
-    if output is not None and os.path.isdir(output):
+    if output is not None and Path(output).is_dir():
         output_dir = output
     else:
         output_dir = None
 
     # Build an output filename for each input
     for filename in filenames:
         # Build output base and extensions
-        dirname, root = os.path.split(filename)
-        base, ext = os.path.splitext(root)
-        if ext != '.fits':
-            ext = '.fits'
-        if not base.endswith('world_coordinates'):
-            base = '_'.join([base, 'world_coordinates'])
+        path_object = Path(filename)
path: jwst/scripts/world_coords.py
user: login: penaguerrero
body: you are right, I reverted the path changes
created_at: 2025-04-04T17:43:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,54 +1,60 @@
 #!/usr/bin/env python
 
-import os
 import sys
+import logging
+from pathlib import Path
 
 from jwst.pipeline.collect_pipeline_cfgs import collect_pipeline_cfgs
 
 
+# Set logger to only print to screen
path: jwst/scripts/collect_pipeline_cfgs.py
user: login: emolter
body: Is this comment still relevant?
created_at: 2025-04-17T14:37:50Z
author_association: COLLABORATOR
diff_hunk: @@ -1,54 +1,60 @@
 #!/usr/bin/env python
 
-import os
 import sys
+import logging
+from pathlib import Path
 
 from jwst.pipeline.collect_pipeline_cfgs import collect_pipeline_cfgs
 
 
+# Set logger to only print to screen
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+
+
 def main():
+    """
+    Get copies of all the cfg files currently in use by the jwst pipeline software.
+
+    Use from terminal as follows:
+    $ collect_pipeline_cfgs .
+    """
     if len(sys.argv) < 2:
-        print('ERROR: missing argument (destination directory')
+        logging.info("ERROR: missing argument (destination directory")
path: jwst/scripts/collect_pipeline_cfgs.py
user: login: emolter
body: Given that this is an ERROR, is the correct log level `logging.info`?
created_at: 2025-04-17T14:38:33Z
author_association: COLLABORATOR
diff_hunk: @@ -3,12 +3,19 @@
 """Allow command stfitsdiff be used from terminal."""
 
 import ast
+import logging
 from argparse import ArgumentParser
 
 from jwst.regtest.st_fitsdiff import STFITSDiff
 
 
+# Set logger to only print to screen
path: jwst/scripts/stfitsdiff.py
user: login: emolter
body: same here
created_at: 2025-04-17T14:40:22Z
author_association: COLLABORATOR
diff_hunk: @@ -57,47 +28,90 @@
 from jwst.assign_wcs import nirspec
 
 
-def main(filenames, mode):
-    """
-    Loop over the input files and apply the proper method to
-    create the output file.
+# Set logger to only print to screen
path: jwst/scripts/world_coords.py
user: login: emolter
body: and here
created_at: 2025-04-17T14:41:17Z
author_association: COLLABORATOR
diff_hunk: @@ -72,7 +72,6 @@ asn_gather = "jwst.scripts.asn_gather:main"
 asn_generate = "jwst.associations.main:main"
 asn_make_pool = "jwst.scripts.asn_make_pool:main"
 collect_pipeline_cfgs = "jwst.scripts.collect_pipeline_cfgs:main"
-create_data = "jwst.scripts.create_data:main"
 csvconvert = "jwst.csv_tools.csvconvert:CSVConvertScript"
path: pyproject.toml
user: login: melanieclarke
body: @penaguerrero - csvconvert needs to be removed here too since it no longer exists.
created_at: 2025-04-28T19:16:52Z
author_association: COLLABORATOR

PR comments:
user: login: codecov[bot]
created_at: 2025-03-17T17:29:11Z
body: ## [Codecov](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) Report
:x: Patch coverage is `19.17098%` with `156 lines` in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 75.60%. Comparing base ([`5a78952`](https://app.codecov.io/gh/spacetelescope/jwst/commit/5a789521042f126306b3e82e05f82a7738a5b6e2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope)) to head ([`6d65bd5`](https://app.codecov.io/gh/spacetelescope/jwst/commit/6d65bd5d856814ddbf6eb69b41b3a438c2ae8082?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope)).
:warning: Report is 692 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?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/scripts/world\_coords.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fworld_coords.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL3dvcmxkX2Nvb3Jkcy5weQ==) | 6.89% | [81 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/scripts/adjust\_wcs.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fadjust_wcs.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL2FkanVzdF93Y3MucHk=) | 3.84% | [25 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/scripts/schemadoc.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fschemadoc.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL3NjaGVtYWRvYy5weQ==) | 38.88% | [11 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/scripts/set\_telescope\_pointing.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fset_telescope_pointing.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL3NldF90ZWxlc2NvcGVfcG9pbnRpbmcucHk=) | 27.27% | [8 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/scripts/v1\_calculate.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fv1_calculate.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL3YxX2NhbGN1bGF0ZS5weQ==) | 27.27% | [8 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/scripts/collect\_pipeline\_cfgs.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fcollect_pipeline_cfgs.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL2NvbGxlY3RfcGlwZWxpbmVfY2Zncy5weQ==) | 41.66% | [7 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/scripts/asn\_edit.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fasn_edit.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL2Fzbl9lZGl0LnB5) | 16.66% | [5 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/scripts/stfitsdiff.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fstfitsdiff.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL3N0Zml0c2RpZmYucHk=) | 37.50% | [5 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/scripts/pointing\_summary.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fpointing_summary.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL3BvaW50aW5nX3N1bW1hcnkucHk=) | 40.00% | [3 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| [jwst/scripts/asn\_make\_pool.py](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&filepath=jwst%2Fscripts%2Fasn_make_pool.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope#diff-andzdC9zY3JpcHRzL2Fzbl9tYWtlX3Bvb2wucHk=) | 33.33% | [2 Missing :warning: ](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spacetelescope) |
| ... and [1 more](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?src=pr&el=tree-more&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    #9290      +/-   ##
==========================================
- Coverage   75.62%   75.60%   -0.02%     
==========================================
  Files         368      368              
  Lines       36894    36944      +50     
==========================================
+ Hits        27902    27933      +31     
- Misses       8992     9011      +19     
```
</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/spacetelescope/jwst/pull/9290?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-03-28T16:57:45Z
body: reg test running: https://github.com/spacetelescope/RegressionTests/actions/runs/14133995716
author_association: CONTRIBUTOR
user: login: penaguerrero
created_at: 2025-04-04T16:38:51Z
body: I do not believe the set up for the logger to print on-screen for the collect_pipeline_cfgs script is an issue because that is a stand-alone script for the users and otherwise is only called in retestdata.py
author_association: CONTRIBUTOR
user: login: penaguerrero
created_at: 2025-04-04T17:44:11Z
body: reg test running: https://github.com/spacetelescope/RegressionTests/actions/runs/14271103120
author_association: CONTRIBUTOR