From 603f285e9d400c32519b7ce150f0c68df477879f Mon Sep 17 00:00:00 2001 From: William Jamieson Date: Mon, 16 Dec 2024 13:33:01 -0500 Subject: [PATCH] Clean up linting and tool configs (#1547) --- .pre-commit-config.yaml | 28 +-------- docs/conf.py | 15 +---- docs/exts/numfig.py | 3 +- pyproject.toml | 58 +++++++++---------- romancal/assign_wcs/assign_wcs_step.py | 8 ++- romancal/assign_wcs/utils.py | 13 ++--- romancal/associations/association.py | 2 +- romancal/associations/association_io.py | 8 ++- romancal/associations/lib/constraint.py | 15 +++-- romancal/associations/lib/decorators.py | 2 +- romancal/associations/lib/diff.py | 6 +- romancal/associations/lib/dms_base.py | 12 ++-- .../associations/lib/keyvalue_registry.py | 4 +- romancal/associations/lib/process_list.py | 8 +-- romancal/associations/lib/product_utils.py | 15 +++-- romancal/associations/lib/rules_elpp_base.py | 32 +++++----- romancal/associations/lib/rules_level2.py | 11 ++-- romancal/associations/lib/update_path.py | 14 +++-- romancal/associations/main.py | 10 +--- romancal/associations/registry.py | 6 +- .../associations/tests/data/rules_basic.py | 3 +- romancal/associations/tests/helpers.py | 14 ++--- .../associations/tests/test_constraints.py | 6 +- romancal/associations/tests/test_registry.py | 2 +- .../associations/tests/test_skycell_asn.py | 3 +- romancal/conftest.py | 2 +- romancal/dark_current/dark_current_step.py | 8 ++- romancal/datamodels/filetype.py | 9 ++- romancal/dq_init/dq_init_step.py | 8 ++- romancal/flatfield/flat_field_step.py | 9 ++- romancal/flux/flux_step.py | 12 +++- romancal/lib/dqflags.py | 2 +- romancal/lib/psf.py | 2 +- romancal/lib/signal_slot.py | 3 +- romancal/lib/suffix.py | 5 +- romancal/lib/tests/helpers.py | 2 +- romancal/lib/tests/test_psf.py | 3 +- romancal/linearity/linearity_step.py | 9 ++- romancal/multiband_catalog/background.py | 2 +- .../multiband_catalog_step.py | 8 ++- .../outlier_detection_step.py | 2 +- .../tests/test_outlier_detection.py | 2 +- romancal/outlier_detection/utils.py | 6 +- romancal/patch_match/patch_match.py | 6 +- romancal/patch_match/patch_plot.py | 4 +- romancal/photom/photom.py | 3 +- romancal/photom/photom_step.py | 8 ++- romancal/photom/tests/test_photom.py | 2 +- romancal/pipeline/exposure_pipeline.py | 8 ++- romancal/pipeline/mosaic_pipeline.py | 8 ++- romancal/ramp_fitting/ramp_fit_step.py | 10 +++- .../ramp_fitting/tests/test_ramp_fit_cas22.py | 2 +- romancal/refpix/data.py | 12 ++-- romancal/refpix/refpix_step.py | 8 ++- romancal/refpix/tests/reference_utils.py | 4 +- romancal/refpix/tests/test_data.py | 2 +- romancal/regtest/regtestdata.py | 2 +- romancal/regtest/test_catalog.py | 2 +- romancal/regtest/test_mos_pipeline.py | 6 +- romancal/regtest/test_multiband_catalog.py | 2 +- romancal/regtest/test_ramp_fitting.py | 4 +- romancal/regtest/test_resample.py | 4 +- romancal/regtest/test_skycell_generation.py | 2 +- .../regtest/test_wfi_grism_16resultants.py | 2 +- .../regtest/test_wfi_image_16resultants.py | 2 +- romancal/regtest/test_wfi_saturation.py | 2 +- romancal/resample/gwcs_drizzle.py | 6 +- romancal/resample/resample.py | 13 ++--- romancal/resample/resample_step.py | 17 ++++-- romancal/resample/resample_utils.py | 13 +++-- romancal/resample/tests/test_resample_step.py | 3 +- romancal/saturation/saturation_step.py | 8 ++- romancal/scripts/static_preview.py | 20 +++---- romancal/skymatch/region.py | 9 ++- romancal/skymatch/skyimage.py | 24 ++++---- romancal/skymatch/skymatch.py | 54 +++++------------ romancal/skymatch/skymatch_step.py | 14 +++-- romancal/skymatch/tests/test_skymatch.py | 15 ++--- romancal/source_catalog/reference_data.py | 2 +- romancal/source_catalog/source_catalog.py | 16 ++--- .../source_catalog/source_catalog_step.py | 11 +++- romancal/step.py | 4 +- romancal/tests/test_dms_requirements.py | 4 +- romancal/tweakreg/astrometric_utils.py | 14 +++-- .../tweakreg/tests/test_astrometric_utils.py | 18 +++--- romancal/tweakreg/tests/test_tweakreg.py | 31 ++++++---- romancal/tweakreg/tweakreg_step.py | 13 +++-- 87 files changed, 431 insertions(+), 375 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8f23b1666..17234c053 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,37 +21,13 @@ repos: - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - - id: python-check-blanket-noqa - - id: python-check-mock-methods - id: rst-directive-colons - id: rst-inline-touching-normal - id: text-unicode-replacement-char -- repo: https://github.com/asottile/pyupgrade - rev: 'v3.19.0' - hooks: - - id: pyupgrade - args: ["--py38-plus"] - - repo: https://github.com/astral-sh/ruff-pre-commit rev: 'v0.8.0' hooks: - id: ruff - args: ["--fix"] - -- repo: https://github.com/pycqa/isort - rev: 5.13.2 - hooks: - - id: isort - -- repo: https://github.com/psf/black - rev: 24.10.0 - hooks: - - id: black - -- repo: https://github.com/PyCQA/bandit - rev: 1.7.10 - hooks: - - id: bandit - args: ["-c", "pyproject.toml"] - additional_dependencies: ["bandit[toml]"] + args: ["--fix", "--show-fixes"] + - id: ruff-format diff --git a/docs/conf.py b/docs/conf.py index cdcfd66ec..f27d86d5b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,17 +14,12 @@ import importlib import os import sys +import tomllib from distutils.version import LooseVersion from pathlib import Path import sphinx import stsci_rtd_theme - -if sys.version_info < (3, 11): - import tomli as tomllib -else: - import tomllib - from sphinx.ext.autodoc import AttributeDocumenter from romancal.stpipe import RomanStep @@ -106,14 +101,6 @@ def check_sphinx_version(expected_version): intersphinx_disabled_reftypes = ["*"] -if sys.version_info[0] == 2: - intersphinx_mapping["python"] = ("http://docs.python.org/2/", None) - intersphinx_mapping["pythonloc"] = ( - "http://docs.python.org/", - os.path.abspath( - os.path.join(os.path.dirname(__file__), "local/python2_local_links.inv") - ), - ) # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom diff --git a/docs/exts/numfig.py b/docs/exts/numfig.py index 346b466d3..cac46dcec 100644 --- a/docs/exts/numfig.py +++ b/docs/exts/numfig.py @@ -56,7 +56,8 @@ def doctree_resolved(app, doctree, docname): if app.builder.name != "latex" and app.config.number_figures: for cap in figure_info.traverse(caption): cap[0] = Text( - "%s %d: %s" % (app.config.figure_caption_prefix, i, cap[0]) + "%s %d: %s" # noqa: UP031 + % (app.config.figure_caption_prefix, i, cap[0]) ) for id in figure_info["ids"]: diff --git a/pyproject.toml b/pyproject.toml index 071679d24..e4b833e8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,9 +15,10 @@ classifiers = [ dependencies = [ "asdf >=3.3.0", "asdf-astropy >=0.5.0", - "astropy >=5.3.0", + "astropy >=6.0.0", "crds >=11.16.16", - "gwcs >=0.21.0", + # "gwcs >=0.21.0", + "gwcs @ git+https://github.com/spacetelescope/gwcs.git@master", "jsonschema >=4.8", "numpy >=1.24", "photutils >=1.13.0", @@ -25,7 +26,7 @@ dependencies = [ "requests >=2.26", # "roman_datamodels>=0.22.0,<0.23.0", "roman_datamodels @ git+https://github.com/spacetelescope/roman_datamodels.git", - "scipy >=1.11", + "scipy >=1.14.1", # "stcal>=1.10.0,<1.11.0", "stcal @ git+https://github.com/spacetelescope/stcal.git@main", # "stpipe >=0.7.0,<0.8.0", @@ -57,7 +58,6 @@ docs = [ "sphinx-rtd-theme", "stsci-rtd-theme", "sphinx-autobuild", - "tomli; python_version <=\"3.11\"", ] test = [ "ci-watson >=0.5.0", @@ -121,7 +121,7 @@ zip-safe = false ] [tool.pytest.ini_options] -minversion = 4.6 +minversion = 6.0 norecursedirs = [ "docs/_build", "romancal/scripts", @@ -139,9 +139,14 @@ doctest_plus = "enabled" doctest_rst = "enabled" text_file_format = "rst" log_cli_level = "info" +xfail_strict = true addopts = [ + "--color=yes", # color test output + "--doctest-rst", # enable doctests "--doctest-ignore-import-errors", - "--color=yes", + "--strict-config", # fail on unknown config options + "--strict-markers", # fail on unknown markers + "-ra", # Show summary of all failures/errorsproduct_names ] markers = [ "soctests: run only the SOC tests in the suite.", @@ -173,30 +178,19 @@ exclude_lines = [ "if __name__ == \"__main__\":", ] -[tool.bandit] -skips = [ - "B101", - "B307", - "B404", - "B603", -] -exclude_dirs = [ - "romancal/regtest/scripts", -] - -[tool.isort] -profile = "black" -filter_files = true -line_length = 88 - -[tool.black] -line-length = 88 -force-exclude = "^/(\n (\n \\.eggs\n | \\.git\n | \\.pytest_cache\n | \\.tox\n )/\n)\n" - [tool.ruff] line-length = 88 + [tool.ruff.lint] +extend-select = [ + "UP", # PyUpgrade + "I", # isort + "B", # BugBear + "S", # Bandit + "RUF", # ruff specific + "NPY", # Numpy lint +] exclude = [ "jdocs", ".tox", @@ -206,14 +200,20 @@ exclude = [ ignore = [ "E741", # ambiguous variable name ] -extend-select = [ - "NPY", -] [tool.ruff.lint.extend-per-file-ignores] "romancal/associations/__init__.py" = [ "E402", ] +"romancal/**/tests/**.py" = [ + "S101" # Bandit: Use of assert detected (fine in test files) +] +"romancal/**/test/**.py" = [ + "S101" # Bandit: Use of assert detected (fine in test files) +] +"romancal/regtest/**.py" = [ + "S101" # Bandit: Use of assert detected (fine in test files) +] [tool.cibuildwheel.macos] archs = [ diff --git a/romancal/assign_wcs/assign_wcs_step.py b/romancal/assign_wcs/assign_wcs_step.py index bf2729b0c..c0af07022 100644 --- a/romancal/assign_wcs/assign_wcs_step.py +++ b/romancal/assign_wcs/assign_wcs_step.py @@ -3,7 +3,10 @@ """ +from __future__ import annotations + import logging +from typing import TYPE_CHECKING import gwcs.coordinate_frames as cf from astropy import coordinates as coord @@ -16,6 +19,9 @@ from . import pointing from .utils import add_s_region, wcs_bbox_from_shape +if TYPE_CHECKING: + from typing import ClassVar + log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) @@ -28,7 +34,7 @@ class AssignWcsStep(RomanStep): class_alias = "assign_wcs" - reference_file_types = ["distortion"] + reference_file_types: ClassVar = ["distortion"] def process(self, input): reference_file_names = {} diff --git a/romancal/assign_wcs/utils.py b/romancal/assign_wcs/utils.py index 9cff793fc..02af88fa6 100644 --- a/romancal/assign_wcs/utils.py +++ b/romancal/assign_wcs/utils.py @@ -1,6 +1,5 @@ import functools import logging -from typing import List, Tuple, Union import numpy as np from astropy.coordinates import SkyCoord @@ -44,8 +43,8 @@ def wcs_from_footprints( pscale=None, rotation=None, shape=None, - ref_pixel: Tuple[float, float] = None, - ref_coord: Tuple[float, float] = None, + ref_pixel: tuple[float, float] | None = None, + ref_coord: tuple[float, float] | None = None, ): """ Create a WCS from a list of input data models. @@ -217,9 +216,9 @@ def wcs_from_footprints( def compute_scale( wcs: WCS, - fiducial: Union[tuple, np.ndarray], - disp_axis: int = None, - pscale_ratio: float = None, + fiducial: tuple | np.ndarray, + disp_axis: int | None = None, + pscale_ratio: float | None = None, ) -> float: """Compute scaling transform. @@ -282,7 +281,7 @@ def compute_scale( def calc_rotation_matrix( roll_ref: float, v3i_yang: float, vparity: int = 1 -) -> List[float]: +) -> list[float]: """Calculate the rotation matrix. Parameters diff --git a/romancal/associations/association.py b/romancal/associations/association.py index bc270cab6..e63d1f02a 100644 --- a/romancal/associations/association.py +++ b/romancal/associations/association.py @@ -1,4 +1,4 @@ -""" Main module for associations """ +"""Main module for associations""" import json import logging diff --git a/romancal/associations/association_io.py b/romancal/associations/association_io.py index fee54da5c..ebb0652f8 100644 --- a/romancal/associations/association_io.py +++ b/romancal/associations/association_io.py @@ -66,7 +66,9 @@ def load(cls, serialized): asn = loader(serialized) except Exception as err: logger.debug(f'Error unserializing: "{err}"') - raise AssociationNotValidError(f'Container is not JSON: "{serialized}"') + raise AssociationNotValidError( + f'Container is not JSON: "{serialized}"' + ) from err return asn @@ -132,7 +134,9 @@ def load(cls, serialized): asn = yaml_lib.safe_load(serialized) except Exception as err: logger.debug(f'Error unserializing: "{err}"') - raise AssociationNotValidError(f'Container is not YAML: "{serialized}"') + raise AssociationNotValidError( + f'Container is not YAML: "{serialized}"' + ) from err return asn @staticmethod diff --git a/romancal/associations/lib/constraint.py b/romancal/associations/lib/constraint.py index 285a23a95..3a76d95f6 100644 --- a/romancal/associations/lib/constraint.py +++ b/romancal/associations/lib/constraint.py @@ -1,5 +1,4 @@ -"""Constraints -""" +"""Constraints""" import abc import collections @@ -139,7 +138,7 @@ def __iter__(self): yield self def __repr__(self): - result = f"{self.__class__.__name__}({str(self.__dict__)})" + result = f"{self.__class__.__name__}({self.__dict__!s})" return result def __str__(self): @@ -600,7 +599,7 @@ class Constraint: 'value': 'a_value', 'name': 'simple', 'matched': False}) - """ # noqa: E501 + """ def __init__( self, @@ -639,8 +638,8 @@ def __init__( self.constraints = [init] else: raise TypeError( - "Invalid initialization value type {}.\nValid types are" - " `SimpleConstraint`, `Constraint`,\nor subclass.".format(type(init)) + f"Invalid initialization value type {type(init)}.\nValid types are" + " `SimpleConstraint`, `Constraint`,\nor subclass." ) # Give some defaults real meaning. @@ -664,10 +663,10 @@ def dup_names( and all the constraints that define that name. """ attrs = self.get_all_attr("name") - constraints, names = zip(*attrs) + constraints, names = zip(*attrs, strict=False) dups = [name for name, count in collections.Counter(names).items() if count > 1] result = collections.defaultdict(list) - for name, constraint in zip(names, constraints): + for name, constraint in zip(names, constraints, strict=False): if name in dups: result[name].append(constraint) diff --git a/romancal/associations/lib/decorators.py b/romancal/associations/lib/decorators.py index 59ef60e1f..55c257739 100644 --- a/romancal/associations/lib/decorators.py +++ b/romancal/associations/lib/decorators.py @@ -10,7 +10,7 @@ from functools import wraps -__all__ = ["memoize", "singleton", "memoize_attr_check"] +__all__ = ["memoize", "memoize_attr_check", "singleton"] def _make_key(args, kwargs): diff --git a/romancal/associations/lib/diff.py b/romancal/associations/lib/diff.py index db1a8c53d..95ce5da6d 100644 --- a/romancal/associations/lib/diff.py +++ b/romancal/associations/lib/diff.py @@ -1,4 +1,4 @@ -""""Diff and compare associations""" +""" "Diff and compare associations""" import logging import re @@ -267,9 +267,9 @@ def compare_membership(left, right): ) ) - for left_idx, left_product in enumerate(products_left): + for left_product in products_left: left_product_name = components(left_product["name"]) - for right_idx, right_product in enumerate(products_right): + for right_product in products_right: if components(right_product["name"]) != left_product_name: continue try: diff --git a/romancal/associations/lib/dms_base.py b/romancal/associations/lib/dms_base.py index 660b30804..e2ca2d379 100644 --- a/romancal/associations/lib/dms_base.py +++ b/romancal/associations/lib/dms_base.py @@ -424,8 +424,8 @@ def validate(cls, asn): result = False try: result = all(test["validated"] for test in asn.validity.values()) - except (AttributeError, KeyError): - raise AssociationNotValidError("Validation failed") + except (AttributeError, KeyError) as err: + raise AssociationNotValidError("Validation failed") from err if not result: raise AssociationNotValidError("Validation failed validity tests.") return True @@ -526,7 +526,7 @@ def _get_target(self): of the target or source ID. """ target_id = format_list(self.constraints["target"].found_values) - target = f"t{str(target_id):0>3s}" + target = f"t{target_id!s:0>3s}" return target def _get_grating(self): @@ -539,7 +539,7 @@ def _get_grating(self): of the grating in use. """ grating_id = format_list(self.constraints["grating"].found_values) - grating = f"{str(grating_id):0>3s}" + grating = f"{grating_id!s:0>3s}" return grating @@ -667,8 +667,8 @@ def _item_attr(item, sources): # Base type off of exposure type. try: exp_type = _item_attr(item, ["exp_type"]) - except KeyError: - raise LookupError("Exposure type cannot be determined") + except KeyError as err: + raise LookupError("Exposure type cannot be determined") from err result = EXPTYPE_MAP.get(exp_type, default) diff --git a/romancal/associations/lib/keyvalue_registry.py b/romancal/associations/lib/keyvalue_registry.py index 1435c48ee..01040403f 100644 --- a/romancal/associations/lib/keyvalue_registry.py +++ b/romancal/associations/lib/keyvalue_registry.py @@ -101,8 +101,8 @@ def make_dict(item): except (TypeError, ValueError): try: key = item.__name__ - except (AttributeError, SyntaxError): - raise KeyValueRegistryNoKeyFound + except (AttributeError, SyntaxError) as err: + raise KeyValueRegistryNoKeyFound from err else: value = item diff --git a/romancal/associations/lib/process_list.py b/romancal/associations/lib/process_list.py index 148a9b80f..095a0c1db 100644 --- a/romancal/associations/lib/process_list.py +++ b/romancal/associations/lib/process_list.py @@ -6,8 +6,8 @@ __all__ = [ "ListCategory", - "ProcessList", "ProcessItem", + "ProcessList", "ProcessQueue", "ProcessQueueSorted", ] @@ -154,11 +154,7 @@ def update(self, process_list, full=False): self.only_on_match = process_list.only_on_match def __str__(self): - result = "{}(n_items: {}, {})".format( - self.__class__.__name__, - len(self.items), - {str_attr: getattr(self, str_attr) for str_attr in self._str_attrs}, - ) + result = f"{self.__class__.__name__}(n_items: {len(self.items)}, {({str_attr: getattr(self, str_attr) for str_attr in self._str_attrs})})" return result diff --git a/romancal/associations/lib/product_utils.py b/romancal/associations/lib/product_utils.py index d391fc0c6..df06c3560 100644 --- a/romancal/associations/lib/product_utils.py +++ b/romancal/associations/lib/product_utils.py @@ -1,4 +1,4 @@ -""" Utilities for product manipulation.""" +"""Utilities for product manipulation.""" import copy import logging @@ -115,19 +115,22 @@ def prune_duplicate_products(asns): Pruned list of associations """ - product_names, dups = get_product_names(asns) + _, dups = get_product_names(asns) if not dups: return asns - warnings.warn(f"Duplicate associations exist: {dups}", RuntimeWarning) + warnings.warn(f"Duplicate associations exist: {dups}", RuntimeWarning, stacklevel=2) if config.DEBUG: warnings.warn( 'Duplicate associations will have "dupXXX" prepended to their names, where' - ' "XXX" is a 3-digit sequence.' + ' "XXX" is a 3-digit sequence.', + stacklevel=2, ) else: warnings.warn( - "Duplicates will be removed, leaving only one of each.", RuntimeWarning + "Duplicates will be removed, leaving only one of each.", + RuntimeWarning, + stacklevel=2, ) pruned = copy.copy(asns) @@ -138,7 +141,7 @@ def prune_duplicate_products(asns): to_prune[product_name].append(asn) dup_count = 0 - for product_name, asns_to_prune in to_prune.items(): + for asns_to_prune in to_prune.values(): asns_to_prune = sort_by_candidate(asns_to_prune) for asn in asns_to_prune[1:]: if config.DEBUG: diff --git a/romancal/associations/lib/rules_elpp_base.py b/romancal/associations/lib/rules_elpp_base.py index 6640fd1e8..6a5f25111 100644 --- a/romancal/associations/lib/rules_elpp_base.py +++ b/romancal/associations/lib/rules_elpp_base.py @@ -1,10 +1,13 @@ """Base classes which define the ELPP Associations""" +from __future__ import annotations + import copy import logging import re from collections import defaultdict from os.path import basename, split, splitext +from typing import TYPE_CHECKING from stpipe.format_template import FormatTemplate @@ -33,35 +36,38 @@ from romancal.associations.lib.utilities import evaluate, is_iterable from romancal.associations.registry import RegistryMarker +if TYPE_CHECKING: + from typing import ClassVar + __all__ = [ "ASN_SCHEMA", "AsnMixin_AuxData", - "AsnMixin_Science", - "AsnMixin_Spectrum", "AsnMixin_Lv2FOV", - "AsnMixin_Lv2Image", "AsnMixin_Lv2GBTDSfull", "AsnMixin_Lv2GBTDSpass", + "AsnMixin_Lv2Image", + "AsnMixin_Science", + "AsnMixin_Spectrum", "Constraint", "Constraint_Base", "Constraint_Category", "Constraint_Expos", + "Constraint_Filename", "Constraint_Image", + "Constraint_Image_Science", "Constraint_Instrument", "Constraint_Obsnum", "Constraint_Optical_Path", "Constraint_Pass", - "Constraint_Spectral", - "Constraint_SubCategory", - "Constraint_Tile", - "Constraint_Image_Science", "Constraint_Sequence", "Constraint_Single_Science", + "Constraint_Spectral", "Constraint_Spectral_Science", + "Constraint_SubCategory", "Constraint_Target", - "Constraint_Filename", - "DMS_ELPP_Base", + "Constraint_Tile", "DMSAttrConstraint", + "DMS_ELPP_Base", "ProcessList", "SimpleConstraint", "Utility", @@ -101,7 +107,7 @@ class DMS_ELPP_Base(DMSBaseMixin, Association): INVALID_VALUES = _EMPTY # Make sequences type-dependent - _sequences = defaultdict(Counter) + _sequences: ClassVar = defaultdict(Counter) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -510,10 +516,8 @@ def rename_to_level2(level1b_name, exp_type=None, member_exptype="science"): match = re.match(_LEVEL1B_REGEX, level1b_name) if match is None or match.group("type") != "_uncal": logger.warning( - ( - 'Item FILENAME="{}" is not a Level 1b name. Cannot transform to' - " Level 2b." - ).format(level1b_name) + f'Item FILENAME="{level1b_name}" is not a Level 1b name. Cannot transform to' + " Level 2b." ) return level1b_name diff --git a/romancal/associations/lib/rules_level2.py b/romancal/associations/lib/rules_level2.py index 5444a6463..c06f8008f 100644 --- a/romancal/associations/lib/rules_level2.py +++ b/romancal/associations/lib/rules_level2.py @@ -1,5 +1,4 @@ -"""Association Definitions: DMS Level2b product associations -""" +"""Association Definitions: DMS Level2b product associations""" import logging @@ -8,12 +7,12 @@ from romancal.associations.registry import RegistryMarker __all__ = [ + "AsnMinxin_Lv2FOV", + "AsnMixin_Lv2Image", "Asn_Lv2FOV", - "Asn_Lv2Image", - "Asn_Lv2GBTDSPass", "Asn_Lv2GBTDSFull", - "AsnMixin_Lv2Image", - "AsnMinxin_Lv2FOV", + "Asn_Lv2GBTDSPass", + "Asn_Lv2Image", ] # Configure logging diff --git a/romancal/associations/lib/update_path.py b/romancal/associations/lib/update_path.py index 90ff09374..97ba1ba3b 100644 --- a/romancal/associations/lib/update_path.py +++ b/romancal/associations/lib/update_path.py @@ -103,7 +103,8 @@ def test_update_key_value_default(): new_value = "changed" update_key_value(obj, target, (new_value,)) for value in _gen_dict_extract(target, obj): - assert value == new_value + if not value == new_value: + raise AssertionError(f"{value} != {new_value}") def test_update_key_value_mod_func(): @@ -121,15 +122,20 @@ def mod_func(v, suffix): mod_func(value, new_value) for value in _gen_dict_extract(target, _test_obj) ] for value in _gen_dict_extract(target, obj): - assert assert_values.pop(0) == value + if not (assert_value := assert_values.pop(0)) == value: + raise AssertionError(f"{value} != {assert_value}") def test_replace_path(): new_path = join("hello", "there") base = "base.me" assert_value = join(new_path, base) - assert assert_value == _replace_path(base, new_path) - assert assert_value == _replace_path(join("somepath", base), new_path) + if assert_value == _replace_path(base, new_path): + raise AssertionError(f"{assert_value} != {_replace_path(base, new_path)}") + if assert_value == _replace_path(join("somepath", base), new_path): + raise AssertionError( + f"{assert_value} != {_replace_path(join('somepath', base), new_path)}" + ) def _gen_dict_extract(key, var): diff --git a/romancal/associations/main.py b/romancal/associations/main.py index 9d6e2ccf2..ff0beafd8 100644 --- a/romancal/associations/main.py +++ b/romancal/associations/main.py @@ -201,9 +201,7 @@ def generate(self): ) if parsed.discover: logger.debug( - "# asns found before discover filtering={}".format( - len(self.associations) - ) + f"# asns found before discover filtering={len(self.associations)}" ) self.associations = filter_discovered_only( self.associations, @@ -404,10 +402,8 @@ def save(self): def __str__(self): result = [] result.append( - ( - "There where {:d} associations and {:d} orphaned items" - " found.\nAssociations found are:" - ).format(len(self.associations), len(self.orphaned)) + f"There where {len(self.associations):d} associations and {len(self.orphaned):d} orphaned items" + " found.\nAssociations found are:" ) for assocs in self.associations: result.append(assocs.__str__()) diff --git a/romancal/associations/registry.py b/romancal/associations/registry.py index c1f8c12ec..427aa7b3f 100644 --- a/romancal/associations/registry.py +++ b/romancal/associations/registry.py @@ -57,7 +57,7 @@ class AssociationRegistry(dict): In practice, this is one step in a larger loop over all items to be associated. This does not account for adding items to already existing associations. See :py:func:`~romancal.associations.generate` for more information. - """ # noqa: E501 + """ def __init__( self, @@ -136,7 +136,7 @@ def match(self, item, version_id=None, allow=None, ignore=None): ignore = [] associations = [] process_list = [] - for name, rule in self.items(): + for rule in self.values(): if rule not in ignore and rule in allow: asn, reprocess = rule.create(item, version_id) process_list.extend(reprocess) @@ -214,7 +214,7 @@ def load(self, serialized, format=None, validate=True, first=True, **kwargs): Cannot create or validate the association. """ results = [] - for rule_name, rule in self.items(): + for rule in self.values(): try: results.append( rule.load(serialized, format=format, validate=validate, **kwargs) diff --git a/romancal/associations/tests/data/rules_basic.py b/romancal/associations/tests/data/rules_basic.py index e5244adda..ae0806346 100644 --- a/romancal/associations/tests/data/rules_basic.py +++ b/romancal/associations/tests/data/rules_basic.py @@ -1,5 +1,4 @@ -"""Association Rules: Basic -""" +"""Association Rules: Basic""" from romancal.associations import Association from romancal.associations.lib.constraint import ConstraintTrue diff --git a/romancal/associations/tests/helpers.py b/romancal/associations/tests/helpers.py index 00d2418f9..0b0b35953 100644 --- a/romancal/associations/tests/helpers.py +++ b/romancal/associations/tests/helpers.py @@ -65,10 +65,9 @@ def test_run_generate(self): for ppars in self.pools: pool = combine_pools(ppars.path, **ppars.kwargs) asns = generate(pool, rules) - assert ( - len(asns) == ppars.n_asns - ), ppars.path + ": n_asns not expected {} {}".format( - len(asns), ppars.n_asns + assert len(asns) == ppars.n_asns, ( + ppars.path + + ": n_asns not expected {} {}".format(len(asns), ppars.n_asns) ) for asn, candidates in zip(asns, ppars.candidates): assert set(asn.candidates) == set(candidates) @@ -78,10 +77,9 @@ def test_run_generate(self): for member in product["members"]: if member["exptype"] == "science": match = file_regex.match(member["expname"]) - assert ( - match is not None - ), ppars.path + ": No suffix match for {}".format( - member["expname"] + assert match is not None, ( + ppars.path + + ": No suffix match for {}".format(member["expname"]) ) assert ( match.groupdict()["suffix"] in ppars.valid_suffixes diff --git a/romancal/associations/tests/test_constraints.py b/romancal/associations/tests/test_constraints.py index e4606b453..e559c1eca 100644 --- a/romancal/associations/tests/test_constraints.py +++ b/romancal/associations/tests/test_constraints.py @@ -41,7 +41,7 @@ def test_dups(self, constraints, expected): c = Constraint(constraints) dups = c.dup_names assert set(dups.keys()) == set(expected.keys()) - for name, constraints in dups.items(): + for name in dups: assert set(dups[name]) == set(expected[name]) @@ -242,10 +242,10 @@ def test_name_index(): assert c2["sc4"].value with pytest.raises(KeyError): - c2["nonexistent"].value + _ = c2["nonexistent"].value with pytest.raises(AttributeError): - c2["sc1"].nonexistent + _ = c2["sc1"].nonexistent def test_copy(): diff --git a/romancal/associations/tests/test_registry.py b/romancal/associations/tests/test_registry.py index fefae0ef5..8c9f43f00 100644 --- a/romancal/associations/tests/test_registry.py +++ b/romancal/associations/tests/test_registry.py @@ -33,7 +33,7 @@ def test_dict_like(): assert kvr.get("a") == 1 assert kvr.get("c", 3) == 3 - keys, values = zip(*kvr.items()) + keys, values = zip(*kvr.items(), strict=False) assert set(data.keys()) == set(keys) assert set(data.values()) == set(values) diff --git a/romancal/associations/tests/test_skycell_asn.py b/romancal/associations/tests/test_skycell_asn.py index a4e0418e5..bc01653bb 100644 --- a/romancal/associations/tests/test_skycell_asn.py +++ b/romancal/associations/tests/test_skycell_asn.py @@ -20,14 +20,13 @@ def test_cmdline_fails(): def test_parse_visitID(): - filelist1 = [ "r0000101002003004005_0001_wfi10_cal.asdf", ] visitid_parts = skycell_asn.parse_visitID(filelist1[0][1:20]) assert visitid_parts["Program"] == "00001" assert visitid_parts["Execution"] == "01" - assert visitid_parts["Pass"] == "002" + assert visitid_parts["Pass"] == "002" # noqa: S105 assert visitid_parts["Segment"] == "003" assert visitid_parts["Observation"] == "004" assert visitid_parts["Visit"] == "005" diff --git a/romancal/conftest.py b/romancal/conftest.py index 6917a283d..1efd4a6a2 100644 --- a/romancal/conftest.py +++ b/romancal/conftest.py @@ -113,7 +113,7 @@ def pytest_runtest_logstart(self, nodeid, location): @pytest.fixture(scope="function") def create_mock_asn_file(): - def _create_asn_file(tmp_path: str, members_mapping: dict = None) -> str: + def _create_asn_file(tmp_path: str, members_mapping: dict | None = None) -> str: """ Create a mock association file with the provided members mapping. diff --git a/romancal/dark_current/dark_current_step.py b/romancal/dark_current/dark_current_step.py index 861b43e94..b938acca2 100644 --- a/romancal/dark_current/dark_current_step.py +++ b/romancal/dark_current/dark_current_step.py @@ -1,9 +1,15 @@ #! /usr/bin/env python +from __future__ import annotations + +from typing import TYPE_CHECKING from roman_datamodels import datamodels as rdm from romancal.stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["DarkCurrentStep"] @@ -19,7 +25,7 @@ class DarkCurrentStep(RomanStep): dark_output = output_file(default = None) # Dark corrected model """ - reference_file_types = ["dark"] + reference_file_types: ClassVar = ["dark"] def process(self, input): if isinstance(input, rdm.DataModel): diff --git a/romancal/datamodels/filetype.py b/romancal/datamodels/filetype.py index e6e2f1b4a..3d1e6271a 100644 --- a/romancal/datamodels/filetype.py +++ b/romancal/datamodels/filetype.py @@ -1,14 +1,13 @@ import io import os from pathlib import Path -from typing import Union import roman_datamodels as rdm from romancal.datamodels import ModelLibrary -def check(init: Union[os.PathLike, Path, io.FileIO]) -> str: +def check(init: os.PathLike | Path | io.FileIO) -> str: """ Determine the type of a file and return it as a string @@ -27,7 +26,7 @@ def check(init: Union[os.PathLike, Path, io.FileIO]) -> str: supported = ("asdf", "json", "DataModel") - if isinstance(init, (str, os.PathLike, Path)): + if isinstance(init, str | os.PathLike | Path): path, ext = os.path.splitext(init) ext = ext.strip(".") @@ -56,11 +55,11 @@ def check(init: Union[os.PathLike, Path, io.FileIO]) -> str: init.seek(0, 0) if not magic or len(magic) < 5: - raise ValueError(f"Cannot get file type of {str(init)}") + raise ValueError(f"Cannot get file type of {init!s}") if magic == b"#ASDF": return "asdf" return "asn" else: - raise ValueError(f"Cannot get file type of {str(init)}") + raise ValueError(f"Cannot get file type of {init!s}") diff --git a/romancal/dq_init/dq_init_step.py b/romancal/dq_init/dq_init_step.py index 15af1c358..6e5c23b3f 100644 --- a/romancal/dq_init/dq_init_step.py +++ b/romancal/dq_init/dq_init_step.py @@ -1,4 +1,7 @@ #! /usr/bin/env python +from __future__ import annotations + +from typing import TYPE_CHECKING import roman_datamodels as rdm from roman_datamodels.datamodels import RampModel @@ -7,6 +10,9 @@ from romancal.dq_init import dq_initialization from romancal.stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["DQInitStep"] @@ -24,7 +30,7 @@ class DQInitStep(RomanStep): class_alias = "dq_init" - reference_file_types = ["mask"] + reference_file_types: ClassVar = ["mask"] def process(self, input): """Perform the dq_init calibration step diff --git a/romancal/flatfield/flat_field_step.py b/romancal/flatfield/flat_field_step.py index 09b981770..9489a4e93 100644 --- a/romancal/flatfield/flat_field_step.py +++ b/romancal/flatfield/flat_field_step.py @@ -2,11 +2,18 @@ Flat-field a science image """ +from __future__ import annotations + +from typing import TYPE_CHECKING + import roman_datamodels as rdm from ..stpipe import RomanStep from . import flat_field +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["FlatFieldStep"] @@ -15,7 +22,7 @@ class FlatFieldStep(RomanStep): class_alias = "flat_field" - reference_file_types = ["flat"] + reference_file_types: ClassVar = ["flat"] def process(self, input_model): if not isinstance(input_model, rdm.DataModel): diff --git a/romancal/flux/flux_step.py b/romancal/flux/flux_step.py index 6e793db32..8abd81516 100644 --- a/romancal/flux/flux_step.py +++ b/romancal/flux/flux_step.py @@ -1,12 +1,18 @@ """Apply the flux scaling""" +from __future__ import annotations + import logging +from typing import TYPE_CHECKING from roman_datamodels import datamodels from ..datamodels import ModelLibrary from ..stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) @@ -34,14 +40,14 @@ class FluxStep(RomanStep): Notes ----- Currently, the correction is done in-place; the inputs are directly modified if in-memory DataModels are input. - """ # noqa: E501 + """ class_alias = "flux" spec = """ - """ # noqa: E501 + """ - reference_file_types = [] + reference_file_types: ClassVar = [] def process(self, input): if isinstance(input, datamodels.DataModel): diff --git a/romancal/lib/dqflags.py b/romancal/lib/dqflags.py index ed0af9fcf..12d7dc4fb 100644 --- a/romancal/lib/dqflags.py +++ b/romancal/lib/dqflags.py @@ -2,7 +2,7 @@ from roman_datamodels.dqflags import group, pixel -__all__ = ["pixel", "group"] +__all__ = ["group", "pixel"] warnings.warn( "romancal.dqflags is deprecated. Please use roman_datamodels.dqflags instead.", diff --git a/romancal/lib/psf.py b/romancal/lib/psf.py index 46aa31724..96adbb658 100644 --- a/romancal/lib/psf.py +++ b/romancal/lib/psf.py @@ -18,8 +18,8 @@ __all__ = [ "create_gridded_psf_model", - "fit_psf_to_image_model", "dq_to_boolean_mask", + "fit_psf_to_image_model", ] # set loggers to debug level by default: diff --git a/romancal/lib/signal_slot.py b/romancal/lib/signal_slot.py index 5894d8e4d..a1042d6e9 100644 --- a/romancal/lib/signal_slot.py +++ b/romancal/lib/signal_slot.py @@ -1,5 +1,4 @@ -""" A signal/slot implementation -""" +"""A signal/slot implementation""" import inspect import logging diff --git a/romancal/lib/suffix.py b/romancal/lib/suffix.py index 9f8e9728d..03ba4cf3c 100644 --- a/romancal/lib/suffix.py +++ b/romancal/lib/suffix.py @@ -86,7 +86,6 @@ "pipeline", "dq_init", "linearitystep", - "dark_current", "tweakregstep", "outlierdetectionstep", "skymatchstep", @@ -210,9 +209,7 @@ def find_suffixes(): to_add=(calculated_suffixes, SUFFIXES_TO_ADD), to_remove=(SUFFIXES_TO_DISCARD,) ) print( - "Known list has {known_len} suffixes. Found {new_len} suffixes.".format( - known_len=len(KNOW_SUFFIXES), new_len=len(found_suffixes) - ) + f"Known list has {len(KNOW_SUFFIXES)} suffixes. Found {len(found_suffixes)} suffixes." ) print( "Suffixes that have changed are" diff --git a/romancal/lib/tests/helpers.py b/romancal/lib/tests/helpers.py index c61b996a3..3beed2772 100644 --- a/romancal/lib/tests/helpers.py +++ b/romancal/lib/tests/helpers.py @@ -72,7 +72,7 @@ def word_precision_check(str1, str2, length=5): raise AssertionError( f"str1 has different number of words {len(words1)} than str2 {len(words2)}" ) - for w1, w2 in zip(words1, words2): + for w1, w2 in zip(words1, words2, strict=False): if w1[:length] != w2[:length]: raise AssertionError(f"str1 word {w1[:length]} != str2 {w2[:length]}") return True diff --git a/romancal/lib/tests/test_psf.py b/romancal/lib/tests/test_psf.py index f27b3c054..b5e408834 100644 --- a/romancal/lib/tests/test_psf.py +++ b/romancal/lib/tests/test_psf.py @@ -1,5 +1,5 @@ """ - Unit tests for the Roman source detection step code +Unit tests for the Roman source detection step code """ from copy import deepcopy @@ -94,6 +94,7 @@ def setup_method(self): rng.uniform(-1, 1, n_trials), rng.uniform(-1, 1, n_trials), np.geomspace(1_000, 100_000, n_trials), + strict=False, ), ) def test_psf_fit(self, dx, dy, true_flux): diff --git a/romancal/linearity/linearity_step.py b/romancal/linearity/linearity_step.py index df375f9f5..cc1007689 100644 --- a/romancal/linearity/linearity_step.py +++ b/romancal/linearity/linearity_step.py @@ -2,6 +2,10 @@ Apply linearity correction to a science image """ +from __future__ import annotations + +from typing import TYPE_CHECKING + import numpy as np from roman_datamodels import datamodels as rdd from roman_datamodels.dqflags import pixel @@ -9,6 +13,9 @@ from romancal.stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["LinearityStep"] @@ -20,7 +27,7 @@ class LinearityStep(RomanStep): class_alias = "linearity" - reference_file_types = ["linearity"] + reference_file_types: ClassVar = ["linearity"] def process(self, input): # Open the input data model diff --git a/romancal/multiband_catalog/background.py b/romancal/multiband_catalog/background.py index 2f602cd37..94b6d5a78 100644 --- a/romancal/multiband_catalog/background.py +++ b/romancal/multiband_catalog/background.py @@ -22,7 +22,7 @@ def subtract_background(model, box_size=1000): model : ImageModel or MosaicModel The input model with the background subtracted. """ - if not isinstance(model, (ImageModel, MosaicModel)): + if not isinstance(model, ImageModel | MosaicModel): raise ValueError("The input model must be an ImageModel or MosaicModel.") # Subtract the background diff --git a/romancal/multiband_catalog/multiband_catalog_step.py b/romancal/multiband_catalog/multiband_catalog_step.py index 55c2b9fdb..85135d893 100644 --- a/romancal/multiband_catalog/multiband_catalog_step.py +++ b/romancal/multiband_catalog/multiband_catalog_step.py @@ -2,7 +2,10 @@ Module for the multiband source catalog step. """ +from __future__ import annotations + import logging +from typing import TYPE_CHECKING import numpy as np from astropy.table import Table, join @@ -17,6 +20,9 @@ from romancal.source_catalog.source_catalog import RomanSourceCatalog from romancal.stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["MultibandCatalogStep"] log = logging.getLogger(__name__) @@ -37,7 +43,7 @@ class MultibandCatalogStep(RomanStep): """ class_alias = "multiband_catalog" - reference_file_types = [] + reference_file_types: ClassVar = [] spec = """ bkg_boxsize = integer(default=100) # background mesh box size in pixels diff --git a/romancal/outlier_detection/outlier_detection_step.py b/romancal/outlier_detection/outlier_detection_step.py index 1029aad87..4a90ed937 100644 --- a/romancal/outlier_detection/outlier_detection_step.py +++ b/romancal/outlier_detection/outlier_detection_step.py @@ -41,7 +41,7 @@ class OutlierDetectionStep(RomanStep): resample_data = boolean(default=True) # Specifies whether or not to resample the input images when performing outlier detection good_bits = string(default="~DO_NOT_USE+NON_SCIENCE") # DQ bit value to be considered 'good' in_memory = boolean(default=False) # Specifies whether or not to keep all intermediate products and datamodels in memory - """ # noqa: E501 + """ def process(self, input_models): """Perform outlier detection processing on input data.""" diff --git a/romancal/outlier_detection/tests/test_outlier_detection.py b/romancal/outlier_detection/tests/test_outlier_detection.py index 8ee91d9d0..7779dadc9 100644 --- a/romancal/outlier_detection/tests/test_outlier_detection.py +++ b/romancal/outlier_detection/tests/test_outlier_detection.py @@ -187,7 +187,7 @@ def test_find_outliers(tmp_path, base_image, on_disk): expected_crs = [img_0_input_coords, img_1_input_coords, None] with result: - for cr_coords, flagged_img in zip(expected_crs, result): + for cr_coords, flagged_img in zip(expected_crs, result, strict=False): if cr_coords is None: assert not np.any(flagged_img.dq > 0) else: diff --git a/romancal/outlier_detection/utils.py b/romancal/outlier_detection/utils.py index 24b17ed23..21085f389 100644 --- a/romancal/outlier_detection/utils.py +++ b/romancal/outlier_detection/utils.py @@ -71,7 +71,6 @@ def _median_with_resampling( with input_models: for i, indices in enumerate(indices_by_group): - drizzled_model = resamp.resample_group(input_models, indices) if save_intermediate_results: @@ -79,7 +78,7 @@ def _median_with_resampling( _fileio.save_drizzled(drizzled_model, make_output_path) if i == 0: - input_shape = (nresultants,) + drizzled_model.data.shape + input_shape = (nresultants, *drizzled_model.data.shape) dtype = drizzled_model.data.dtype computer = MedianComputer(input_shape, in_memory, buffer_size, dtype) example_model = drizzled_model @@ -151,7 +150,6 @@ def _median_without_resampling( with input_models: for i in range(len(input_models)): - model = input_models.borrow(i) wht = build_driz_weight( model, @@ -165,7 +163,7 @@ def _median_without_resampling( _fileio.save_drizzled(model, make_output_path) if i == 0: - input_shape = (nresultants,) + model.data.shape + input_shape = (nresultants, *model.data.shape) dtype = model.data.dtype computer = MedianComputer(input_shape, in_memory, buffer_size, dtype) example_model = model diff --git a/romancal/patch_match/patch_match.py b/romancal/patch_match/patch_match.py index d456a238d..5b87ab574 100644 --- a/romancal/patch_match/patch_match.py +++ b/romancal/patch_match/patch_match.py @@ -40,8 +40,8 @@ def load_patch_table(tablepath=None): try: with asdf.open(tablepath) as af: PATCH_TABLE = af.tree["patches"].copy() - except FileNotFoundError: - raise FileNotFoundError("Specified patch table file path not found") + except FileNotFoundError as err: + raise FileNotFoundError("Specified patch table file path not found") from err def image_coords_to_vec(image_corners): @@ -219,7 +219,7 @@ def find_closest_tangent_point(patches, image_corners): ((im_center - np.array(sgv.lonlat_to_vector(*tangent_point))) ** 2).sum() for tangent_point in unique_tangent_points ] - sorted_dist_indices = sorted(zip(dist, range(len(dist)))) + sorted_dist_indices = sorted(zip(dist, range(len(dist)), strict=False)) sorted_tangent_points = [ unique_tangent_points[sorted_dist[1]] for sorted_dist in sorted_dist_indices ] diff --git a/romancal/patch_match/patch_plot.py b/romancal/patch_match/patch_plot.py index 93e4e311c..432b2c364 100644 --- a/romancal/patch_match/patch_plot.py +++ b/romancal/patch_match/patch_plot.py @@ -59,7 +59,7 @@ def plot(image_corners, patches_touched_ids, patches_candidate_ids): vec_image_corners = pm.image_coords_to_vec(image_corners) tp_image_corners = pm.veccoords_to_tangent_plane(vec_image_corners, tangent_point) plot_field(tp_image_corners, fill="lightgrey", color="black") - for patch, id in zip(patches_candidate, patches_candidate_ids): + for patch, id in zip(patches_candidate, patches_candidate_ids, strict=False): plot_patch( pm.veccoords_to_tangent_plane( pm.get_cartesian_corners(patch), tangent_point @@ -67,7 +67,7 @@ def plot(image_corners, patches_touched_ids, patches_candidate_ids): id=id, color="lightgray", ) - for patch, id in zip(patches_touched, patches_touched_ids): + for patch, id in zip(patches_touched, patches_touched_ids, strict=False): plot_patch( pm.veccoords_to_tangent_plane( pm.get_cartesian_corners(patch), tangent_point diff --git a/romancal/photom/photom.py b/romancal/photom/photom.py index b06343afa..f375c39a2 100644 --- a/romancal/photom/photom.py +++ b/romancal/photom/photom.py @@ -94,7 +94,8 @@ def apply_photom(input_model, photom): except KeyError: warnings.warn( "No matching photom parameters for" - f" {input_model.meta.instrument.optical_element}" + f" {input_model.meta.instrument.optical_element}", + stacklevel=2, ) return input_model diff --git a/romancal/photom/photom_step.py b/romancal/photom/photom_step.py index 3ed069504..5928a78ad 100644 --- a/romancal/photom/photom_step.py +++ b/romancal/photom/photom_step.py @@ -1,10 +1,16 @@ #! /usr/bin/env python +from __future__ import annotations + +from typing import TYPE_CHECKING import roman_datamodels as rdm from romancal.photom import photom from romancal.stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["PhotomStep"] @@ -16,7 +22,7 @@ class PhotomStep(RomanStep): class_alias = "photom" - reference_file_types = ["photom"] + reference_file_types: ClassVar = ["photom"] def process(self, input): """Perform the photometric calibration step diff --git a/romancal/photom/tests/test_photom.py b/romancal/photom/tests/test_photom.py index c1de8de2e..00bc53a7e 100644 --- a/romancal/photom/tests/test_photom.py +++ b/romancal/photom/tests/test_photom.py @@ -60,7 +60,7 @@ def create_photom_wfi_image(min_r=3.1, delta=0.1): pixelareasr = np.ones(nrows, dtype=np.float64) * area_ster # Bundle values into a list - values = list(zip(photmjsr, uncertainty, pixelareasr)) + values = list(zip(photmjsr, uncertainty, pixelareasr, strict=False)) # Create dictionary containing all values reftab = {} diff --git a/romancal/pipeline/exposure_pipeline.py b/romancal/pipeline/exposure_pipeline.py index 55d5acac2..11b19351f 100644 --- a/romancal/pipeline/exposure_pipeline.py +++ b/romancal/pipeline/exposure_pipeline.py @@ -1,5 +1,8 @@ #!/usr/bin/env python +from __future__ import annotations + import logging +from typing import TYPE_CHECKING import numpy as np from roman_datamodels.dqflags import group @@ -23,6 +26,9 @@ from ..stpipe import RomanPipeline +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["ExposurePipeline"] # Define logging @@ -46,7 +52,7 @@ class ExposurePipeline(RomanPipeline): """ # Define aliases to steps - step_defs = { + step_defs: ClassVar = { "dq_init": dq_init_step.DQInitStep, "saturation": SaturationStep, "refpix": RefPixStep, diff --git a/romancal/pipeline/mosaic_pipeline.py b/romancal/pipeline/mosaic_pipeline.py index eca8d02dd..a155c68b0 100644 --- a/romancal/pipeline/mosaic_pipeline.py +++ b/romancal/pipeline/mosaic_pipeline.py @@ -1,7 +1,10 @@ #!/usr/bin/env python +from __future__ import annotations + import logging import re from os.path import basename, isfile +from typing import TYPE_CHECKING import asdf import numpy as np @@ -23,6 +26,9 @@ from ..stpipe import RomanPipeline +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["MosaicPipeline"] # Define logging @@ -44,7 +50,7 @@ class MosaicPipeline(RomanPipeline): """ # Define aliases to steps - step_defs = { + step_defs: ClassVar = { "flux": FluxStep, "skymatch": SkyMatchStep, "outlier_detection": OutlierDetectionStep, diff --git a/romancal/ramp_fitting/ramp_fit_step.py b/romancal/ramp_fitting/ramp_fit_step.py index 57baa3102..a15838c29 100644 --- a/romancal/ramp_fitting/ramp_fit_step.py +++ b/romancal/ramp_fitting/ramp_fit_step.py @@ -1,6 +1,9 @@ #! /usr/bin/env python # +from __future__ import annotations + import logging +from typing import TYPE_CHECKING import numpy as np from astropy import units as u @@ -13,6 +16,9 @@ from romancal.stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) @@ -34,11 +40,11 @@ class RampFitStep(RomanStep): use_ramp_jump_detection = boolean(default=True) # Use jump detection during ramp fitting threshold_intercept = float(default=None) # Override the intercept parameter for the threshold function in the jump detection algorithm. threshold_constant = float(default=None) # Override the constant parameter for the threshold function in the jump detection algorithm. - """ # noqa: E501 + """ weighting = "optimal" # Only weighting allowed for OLS - reference_file_types = ["readnoise", "gain", "dark"] + reference_file_types: ClassVar = ["readnoise", "gain", "dark"] def process(self, input): with rdd.open(input, mode="rw") as input_model: diff --git a/romancal/ramp_fitting/tests/test_ramp_fit_cas22.py b/romancal/ramp_fitting/tests/test_ramp_fit_cas22.py index e64ebcf08..90cfd8c63 100644 --- a/romancal/ramp_fitting/tests/test_ramp_fit_cas22.py +++ b/romancal/ramp_fitting/tests/test_ramp_fit_cas22.py @@ -318,7 +318,7 @@ def generate_wfi_reffiles( # Create temporary dark reference file # shape needs to be 3D but does not matter because the ramp fitting # step only uses the 2-D dark slope component - dark_ref = maker_utils.mk_dark(shape=(1,) + shape) + dark_ref = maker_utils.mk_dark(shape=(1, *shape)) dark_ref["meta"]["instrument"]["detector"] = "WFI01" dark_ref["meta"]["instrument"]["name"] = "WFI" dark_ref["meta"]["reftype"] = "DARK" diff --git a/romancal/refpix/data.py b/romancal/refpix/data.py index 0a4ee55a8..3e84d7985 100644 --- a/romancal/refpix/data.py +++ b/romancal/refpix/data.py @@ -2,7 +2,7 @@ import abc from itertools import islice -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING if TYPE_CHECKING: from roman_datamodels.datamodels import RampModel, RefpixRefModel @@ -33,7 +33,7 @@ class Const(IntEnum): N_COLUMNS = CHAN_WIDTH * N_DETECT_CHAN -_offset = Optional[np.ndarray] +_offset = np.ndarray | None @dataclass @@ -340,8 +340,10 @@ def remove_trends(self) -> ChannelView: not_zero = self.data != 0 # fit needs to be done for each channel and frame separately - for chan_data, chan_not_zero in zip(self.data, not_zero): - for frame_data, frame_not_zero in zip(chan_data, chan_not_zero): + for chan_data, chan_not_zero in zip(self.data, not_zero, strict=False): + for frame_data, frame_not_zero in zip( + chan_data, chan_not_zero, strict=False + ): # Find the non-zero reference pixels for this channel and frame mask = frame_not_zero[REF_ROWS, :] @@ -613,4 +615,4 @@ def from_ref(cls, ref: RefpixRefModel) -> Coefficients: return cls(ref.gamma, ref.zeta, ref.alpha) def __iter__(self): - return zip(self.gamma, self.zeta, self.alpha) + return zip(self.gamma, self.zeta, self.alpha, strict=False) diff --git a/romancal/refpix/refpix_step.py b/romancal/refpix/refpix_step.py index 38ab125d6..16878f570 100644 --- a/romancal/refpix/refpix_step.py +++ b/romancal/refpix/refpix_step.py @@ -1,10 +1,16 @@ +from __future__ import annotations + import logging +from typing import TYPE_CHECKING import roman_datamodels as rdm from romancal.refpix import refpix from romancal.stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["RefPixStep"] log = logging.getLogger(__name__) @@ -30,7 +36,7 @@ class RefPixStep(RomanStep): # of the reference pixel padded values. """ - reference_file_types = ["refpix"] + reference_file_types: ClassVar = ["refpix"] def process(self, input): """ diff --git a/romancal/refpix/tests/reference_utils.py b/romancal/refpix/tests/reference_utils.py index 78345acd5..a4c5dccd0 100644 --- a/romancal/refpix/tests/reference_utils.py +++ b/romancal/refpix/tests/reference_utils.py @@ -191,7 +191,7 @@ def exec_channel_func_threads(chanIndexRange, targetFunc, funcArgs, multiThread= if multiThread: threadList = [] for c in chanIndexRange: - funcArgsWithChan = (c,) + funcArgs + funcArgsWithChan = (c, *funcArgs) threadList.append( threading.Thread(target=targetFunc, args=funcArgsWithChan) ) @@ -203,7 +203,7 @@ def exec_channel_func_threads(chanIndexRange, targetFunc, funcArgs, multiThread= t.join() else: for c in chanIndexRange: - funcArgsWithChan = (c,) + funcArgs + funcArgsWithChan = (c, *funcArgs) targetFunc(*funcArgsWithChan) diff --git a/romancal/refpix/tests/test_data.py b/romancal/refpix/tests/test_data.py index 75fa2b854..0b9102fa9 100644 --- a/romancal/refpix/tests/test_data.py +++ b/romancal/refpix/tests/test_data.py @@ -308,7 +308,7 @@ def test_apply_offset(self, data, offset): assert (new.data != data).any() # Check that the data has been correctly modified - for new_frame, frame in zip(new.data, data): + for new_frame, frame in zip(new.data, data, strict=False): assert (new_frame == (frame + offset)).all() # Check if no offset is passed diff --git a/romancal/regtest/regtestdata.py b/romancal/regtest/regtestdata.py index 08874cd59..45bd57281 100644 --- a/romancal/regtest/regtestdata.py +++ b/romancal/regtest/regtestdata.py @@ -274,7 +274,7 @@ def get_asn(self, path=None, docopy=True, get_members=True): ) def to_asdf(self, path): - tree = eval(str(self)) + tree = eval(str(self)) # noqa: S307 af = asdf.AsdfFile(tree=tree) af.write_to(path) diff --git a/romancal/regtest/test_catalog.py b/romancal/regtest/test_catalog.py index ad1439a8e..58ee010aa 100644 --- a/romancal/regtest/test_catalog.py +++ b/romancal/regtest/test_catalog.py @@ -1,4 +1,4 @@ -""" Roman tests for source catalog creation """ +"""Roman tests for source catalog creation""" import asdf import pytest diff --git a/romancal/regtest/test_mos_pipeline.py b/romancal/regtest/test_mos_pipeline.py index cf799812b..8fba060af 100644 --- a/romancal/regtest/test_mos_pipeline.py +++ b/romancal/regtest/test_mos_pipeline.py @@ -1,4 +1,4 @@ -""" Roman tests for the High Level Pipeline """ +"""Roman tests for the High Level Pipeline""" import os @@ -53,7 +53,7 @@ def truth_filename(run_mos): def thumbnail_filename(output_filename): thumbnail_filename = output_filename.rsplit("_", 1)[0] + "_thumb.png" preview_cmd = f"stpreview to {output_filename} {thumbnail_filename} 256 256 roman" - os.system(preview_cmd) # nosec + os.system(preview_cmd) # noqa: S605 return thumbnail_filename @@ -61,7 +61,7 @@ def thumbnail_filename(output_filename): def preview_filename(output_filename): preview_filename = output_filename.rsplit("_", 1)[0] + "_preview.png" preview_cmd = f"stpreview to {output_filename} {preview_filename} 1080 1080 roman" - os.system(preview_cmd) # nosec + os.system(preview_cmd) # noqa: S605 return preview_filename diff --git a/romancal/regtest/test_multiband_catalog.py b/romancal/regtest/test_multiband_catalog.py index a5bc1dda0..1583339cc 100644 --- a/romancal/regtest/test_multiband_catalog.py +++ b/romancal/regtest/test_multiband_catalog.py @@ -1,4 +1,4 @@ -""" Roman tests for source catalog creation """ +"""Roman tests for source catalog creation""" import asdf import pytest diff --git a/romancal/regtest/test_ramp_fitting.py b/romancal/regtest/test_ramp_fitting.py index 718eeaec6..5e8f78842 100644 --- a/romancal/regtest/test_ramp_fitting.py +++ b/romancal/regtest/test_ramp_fitting.py @@ -1,4 +1,4 @@ -""" Module to test rampfit with optional output +"""Module to test rampfit with optional output Notes ----- @@ -109,7 +109,7 @@ def cond_science_verification( cond_is_step_complete, cond_is_uneven, ] -CONDITIONS_TRUNC = CONDITIONS_FULL + [cond_is_truncated] +CONDITIONS_TRUNC = [*CONDITIONS_FULL, cond_is_truncated] # ###################### diff --git a/romancal/regtest/test_resample.py b/romancal/regtest/test_resample.py index d669eecc6..a1c715925 100644 --- a/romancal/regtest/test_resample.py +++ b/romancal/regtest/test_resample.py @@ -78,7 +78,7 @@ def test_resample_single_file(rtdata, ignore_asdf_paths): "var_rnoise", ] ) - }""" # noqa: E501 + }""" ) assert all( np.sum(~np.isnan(getattr(resample_out, x))) @@ -126,7 +126,7 @@ def test_resample_single_file(rtdata, ignore_asdf_paths): "members", ] ) - }""" # noqa: E501 + }""" ) assert all( hasattr(resample_out.meta.resample, x) diff --git a/romancal/regtest/test_skycell_generation.py b/romancal/regtest/test_skycell_generation.py index 59ac0f163..f032c25ae 100644 --- a/romancal/regtest/test_skycell_generation.py +++ b/romancal/regtest/test_skycell_generation.py @@ -1,4 +1,4 @@ -""" Roman tests for generating associatinos based on skycells""" +"""Roman tests for generating associatinos based on skycells""" import os diff --git a/romancal/regtest/test_wfi_grism_16resultants.py b/romancal/regtest/test_wfi_grism_16resultants.py index 969291076..06157dad9 100644 --- a/romancal/regtest/test_wfi_grism_16resultants.py +++ b/romancal/regtest/test_wfi_grism_16resultants.py @@ -1,4 +1,4 @@ -""" Roman tests for flat field correction """ +"""Roman tests for flat field correction""" import pytest import roman_datamodels as rdm diff --git a/romancal/regtest/test_wfi_image_16resultants.py b/romancal/regtest/test_wfi_image_16resultants.py index 124d1c535..b4331cb78 100644 --- a/romancal/regtest/test_wfi_image_16resultants.py +++ b/romancal/regtest/test_wfi_image_16resultants.py @@ -1,4 +1,4 @@ -""" Roman tests for flat field correction """ +"""Roman tests for flat field correction""" import pytest import roman_datamodels as rdm diff --git a/romancal/regtest/test_wfi_saturation.py b/romancal/regtest/test_wfi_saturation.py index 18b9fa666..d71de4c1f 100644 --- a/romancal/regtest/test_wfi_saturation.py +++ b/romancal/regtest/test_wfi_saturation.py @@ -1,4 +1,4 @@ -""" Tests for the saturation step""" +"""Tests for the saturation step""" import os diff --git a/romancal/resample/gwcs_drizzle.py b/romancal/resample/gwcs_drizzle.py index 96e1018f8..75eeb2a39 100644 --- a/romancal/resample/gwcs_drizzle.py +++ b/romancal/resample/gwcs_drizzle.py @@ -93,10 +93,8 @@ def __init__( ) elif self.outcon.ndim != 3: raise ValueError( - "Drizzle context image has wrong dimensions: \ - {}".format( - product - ) + f"Drizzle context image has wrong dimensions: \ + {product}" ) # Check field values diff --git a/romancal/resample/resample.py b/romancal/resample/resample.py index 0e11fd629..0e9ad543c 100644 --- a/romancal/resample/resample.py +++ b/romancal/resample/resample.py @@ -1,7 +1,6 @@ import json import logging import os -from typing import List import numpy as np from astropy import units as u @@ -179,7 +178,7 @@ def __init__( ) # FIXME: temporary hack to prevent changes from L2 into L3 schema - for i, model in enumerate(models): + for model in models: # saving ImageModels cal_logs cal_logs = model.meta.cal_logs # removing meta.cal_logs @@ -298,7 +297,7 @@ def resample_many_to_many(self): sky. """ output_models = [] - for group_id, indices in self.input_models.group_indices.items(): + for indices in self.input_models.group_indices.values(): output_model = self.resample_group(self.input_models, indices) if not self.in_memory: @@ -438,13 +437,13 @@ def resample_variance_array(self, name, output_model): if variance is None or variance.size == 0: log.debug( f"No data for '{name}' for model " - f"{repr(model.meta.filename)}. Skipping ..." + f"{model.meta.filename!r}. Skipping ..." ) continue elif variance.shape != model.data.shape: log.warning( f"Data shape mismatch for '{name}' for model " - f"{repr(model.meta.filename)}. Skipping..." + f"{model.meta.filename!r}. Skipping..." ) continue @@ -564,7 +563,7 @@ def update_exposure_times(self, output_model, exptime_tot): ) exposure_times = {"start": [], "end": []} with self.input_models: - for group_id, indices in self.input_models.group_indices.items(): + for indices in self.input_models.group_indices.values(): index = indices[0] model = self.input_models.borrow(index) exposure_times["start"].append(model.meta.exposure.start_time) @@ -947,7 +946,7 @@ def populate_mosaic_basic( def populate_mosaic_individual( - output_model: datamodels.MosaicModel, input_models: [List, ModelLibrary] + output_model: datamodels.MosaicModel, input_models: [list, ModelLibrary] ): """ Populate individual meta fields in the output mosaic model based on input models. diff --git a/romancal/resample/resample_step.py b/romancal/resample/resample_step.py index de0154191..8796a52ff 100644 --- a/romancal/resample/resample_step.py +++ b/romancal/resample/resample_step.py @@ -1,6 +1,9 @@ +from __future__ import annotations + import logging import os from copy import deepcopy +from typing import TYPE_CHECKING import asdf import numpy as np @@ -13,6 +16,9 @@ from ..stpipe import RomanStep from . import resample +if TYPE_CHECKING: + from typing import ClassVar + log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) @@ -44,7 +50,7 @@ class ResampleStep(RomanStep): ------- : `roman_datamodels.datamodels.MosaicModel` A mosaic datamodel with the final output frame. - """ # noqa: E501 + """ class_alias = "resample" @@ -65,9 +71,9 @@ class ResampleStep(RomanStep): allowed_memory = float(default=None) # Fraction of memory to use for the combined image. in_memory = boolean(default=True) good_bits = string(default='~DO_NOT_USE+NON_SCIENCE') # The good bits to use for building the resampling mask. - """ # noqa: E501 + """ - reference_file_types = [] + reference_file_types: ClassVar = [] def process(self, input): if isinstance(input, datamodels.DataModel): @@ -207,7 +213,10 @@ def _check_list_pars(vals, name, min_vals=None): if n == 2: return None elif n == 0: - if min_vals and sum(x >= y for x, y in zip(vals, min_vals)) != 2: + if ( + min_vals + and sum(x >= y for x, y in zip(vals, min_vals, strict=False)) != 2 + ): raise ValueError( f"'{name}' values must be larger or equal to {list(min_vals)}" ) diff --git a/romancal/resample/resample_utils.py b/romancal/resample/resample_utils.py index 0b399e68b..4b8c78aa0 100644 --- a/romancal/resample/resample_utils.py +++ b/romancal/resample/resample_utils.py @@ -1,6 +1,5 @@ import logging import warnings -from typing import Tuple import gwcs import numpy as np @@ -22,8 +21,8 @@ def make_output_wcs( pscale=None, rotation=None, shape=None, - crpix: Tuple[float, float] = None, - crval: Tuple[float, float] = None, + crpix: tuple[float, float] | None = None, + crval: tuple[float, float] | None = None, ): """ Generate output WCS here based on footprints of all input WCS objects @@ -69,7 +68,7 @@ def make_output_wcs( """ wcslist = [i.meta.wcs for i in input_models] - for w, i in zip(wcslist, input_models): + for w, i in zip(wcslist, input_models, strict=False): if w.bounding_box is None: w.bounding_box = wcs_bbox_from_shape(i.data.shape) naxes = wcslist[0].output_frame.naxes @@ -102,7 +101,7 @@ def make_output_wcs( def build_driz_weight( model, weight_type=None, - good_bits: str = None, + good_bits: str | None = None, ): """ Builds the drizzle weight map for resampling. @@ -157,6 +156,7 @@ def build_driz_weight( warnings.warn( "var_rnoise array not available. Setting drizzle weight map to 1", RuntimeWarning, + stacklevel=2, ) inv_variance = 1.0 result = inv_variance * dqmask @@ -206,6 +206,7 @@ def build_mask(dqarr, bitvalue): "Use functions from astropy.nddata.bitmask module instead such as " "bitfield_to_boolean_mask().", DeprecationWarning, + stacklevel=2, ) dqmask = bitfield_to_boolean_mask( dqarr, @@ -383,7 +384,7 @@ def decode_context(context, x, y): return [ np.flatnonzero([v & (1 << k) for v in context[:, yi, xi] for k in range(nbits)]) - for xi, yi in zip(x, y) + for xi, yi in zip(x, y, strict=False) ] diff --git a/romancal/resample/tests/test_resample_step.py b/romancal/resample/tests/test_resample_step.py index dc16a8924..2a479b7c3 100644 --- a/romancal/resample/tests/test_resample_step.py +++ b/romancal/resample/tests/test_resample_step.py @@ -61,7 +61,8 @@ def create_mosaic(self): pscale=self.pscale, shape=self.shape, ) - l3.meta.wcs.forward_transform + # Call the forward transform so its value is pulled from disk + _ = l3.meta.wcs.forward_transform return datamodels.MosaicModel(l3) diff --git a/romancal/saturation/saturation_step.py b/romancal/saturation/saturation_step.py index 4ce1663fd..8afbe085c 100644 --- a/romancal/saturation/saturation_step.py +++ b/romancal/saturation/saturation_step.py @@ -1,4 +1,7 @@ #! /usr/bin/env python +from __future__ import annotations + +from typing import TYPE_CHECKING import roman_datamodels as rdm from roman_datamodels.datamodels import SaturationRefModel @@ -6,6 +9,9 @@ from romancal.saturation import saturation from romancal.stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["SaturationStep"] @@ -16,7 +22,7 @@ class SaturationStep(RomanStep): class_alias = "saturation" - reference_file_types = ["saturation"] + reference_file_types: ClassVar = ["saturation"] def process(self, input): if isinstance(input, rdm.DataModel): diff --git a/romancal/scripts/static_preview.py b/romancal/scripts/static_preview.py index f690c157c..ea9f7902a 100644 --- a/romancal/scripts/static_preview.py +++ b/romancal/scripts/static_preview.py @@ -1,5 +1,4 @@ from pathlib import Path -from typing import Optional import asdf import numpy @@ -7,6 +6,8 @@ def command(): try: + from typing import Annotated + import typer from stpreview.downsample import downsample_asdf_to from stpreview.image import ( @@ -14,11 +15,10 @@ def command(): percentile_normalization, write_image, ) - from typing_extensions import Annotated - except (ImportError, ModuleNotFoundError): + except (ImportError, ModuleNotFoundError) as err: raise ImportError( 'SDP requirements not installed; do `pip install "romancal[sdp]"`' - ) + ) from err app = typer.Typer() @@ -28,14 +28,14 @@ def preview( Path, typer.Argument(help="path to ASDF file with 2D image data") ], output: Annotated[ - Optional[Path], typer.Argument(help="path to output image file") + Path | None, typer.Argument(help="path to output image file") ] = None, shape: Annotated[ - Optional[tuple[int, int]], + tuple[int, int] | None, typer.Argument(help="desired pixel resolution of output image"), ] = (1080, 1080), compass: Annotated[ - Optional[bool], + bool | None, typer.Option(help="whether to draw a north arrow on the image"), ] = True, ): @@ -71,14 +71,14 @@ def thumbnail( Path, typer.Argument(help="path to ASDF file with 2D image data") ], output: Annotated[ - Optional[Path], typer.Argument(help="path to output image file") + Path | None, typer.Argument(help="path to output image file") ] = None, shape: Annotated[ - Optional[tuple[int, int]], + tuple[int, int] | None, typer.Argument(help="desired pixel resolution of output image"), ] = (300, 300), compass: Annotated[ - Optional[bool], + bool | None, typer.Option(help="whether to draw a north arrow on the image"), ] = False, ): diff --git a/romancal/skymatch/region.py b/romancal/skymatch/region.py index fec666fb3..6dc29eea1 100644 --- a/romancal/skymatch/region.py +++ b/romancal/skymatch/region.py @@ -10,7 +10,7 @@ import numpy as np -__all__ = ["Region", "Edge", "Polygon"] +__all__ = ["Edge", "Polygon", "Region"] class ValidationError(Exception): @@ -91,9 +91,8 @@ class Polygon(Region): """ def __init__(self, rid, vertices, coord_system="Cartesian"): - assert len(vertices) >= 4, ( - "Expected vertices to be " "a list of minimum 4 tuples (x,y)" - ) + if len(vertices) < 4: + raise ValueError("Expected vertices to be a list of minimum 4 tuples (x,y)") super().__init__(rid, coord_system) # self._shiftx & self._shifty are introduced to shift the bottom-left @@ -239,7 +238,7 @@ def scan(self, data): y += 1 continue - for i, j in zip(xnew[::2], xnew[1::2]): + for i, j in zip(xnew[::2], xnew[1::2], strict=False): xstart = max(0, i + self._shiftx) xend = min(j + self._shiftx, nx - 1) data[ysh][xstart : xend + 1] = self._rid diff --git a/romancal/skymatch/skyimage.py b/romancal/skymatch/skyimage.py index 9d8ef6fc3..eacf7fe53 100644 --- a/romancal/skymatch/skyimage.py +++ b/romancal/skymatch/skyimage.py @@ -16,11 +16,11 @@ from .skystatistics import SkyStats __all__ = [ - "SkyImage", - "SkyGroup", "DataAccessor", "NDArrayInMemoryAccessor", "NDArrayMappedAccessor", + "SkyGroup", + "SkyImage", ] @@ -90,7 +90,7 @@ def __init__( def get_data(self): self._tmp.seek(0) - return pickle.load(self._tmp) # nosec B301 + return pickle.load(self._tmp) # noqa: S301 def set_data(self, data): data = np.asanyarray(data) @@ -406,13 +406,13 @@ def intersection(self, skyimage): the intersection of this `SkyImage` and `skyimage`. """ - if isinstance(skyimage, (SkyImage, SkyGroup)): + if isinstance(skyimage, SkyImage | SkyGroup): other = skyimage.polygon else: other = skyimage - pts1 = np.sort(list(self._polygon.points)[0], axis=0) - pts2 = np.sort(list(other.points)[0], axis=0) + pts1 = np.sort(next(iter(self._polygon.points)), axis=0) + pts2 = np.sort(next(iter(other.points)), axis=0) if np.allclose(pts1, pts2, rtol=0, atol=5e-9): intersect_poly = self._polygon.copy() else: @@ -624,7 +624,7 @@ def calc_sky(self, overlap=None, delta=True): # set pixels in 'fill_mask' that are inside a polygon to True: x, y = self.wcs_inv(ra, dec) - poly_vert = list(zip(*[x, y])) + poly_vert = list(zip(*[x, y], strict=False)) polygon = region.Polygon(True, poly_vert) fill_mask = polygon.scan(fill_mask) @@ -700,7 +700,7 @@ def _calc_sky_orig(self, overlap=None, delta=True): else: fill_mask = np.zeros(self.image_shape, dtype=bool) - if isinstance(overlap, (SkyImage, SkyGroup, SphericalPolygon)): + if isinstance(overlap, SkyImage | SkyGroup | SphericalPolygon): intersection = self.intersection(overlap) polyarea = np.fabs(intersection.area()) radec = intersection.to_radec() @@ -725,7 +725,7 @@ def _calc_sky_orig(self, overlap=None, delta=True): # set pixels in 'fill_mask' that are inside a polygon to True: x, y = self.wcs_inv(ra, dec) - poly_vert = list(zip(*[x, y])) + poly_vert = list(zip(*[x, y], strict=False)) polygon = region.Polygon(True, poly_vert) fill_mask = polygon.scan(fill_mask) @@ -874,13 +874,13 @@ def intersection(self, skyimage): the intersection of this `SkyImage` and `skyimage`. """ - if isinstance(skyimage, (SkyImage, SkyGroup)): + if isinstance(skyimage, SkyImage | SkyGroup): other = skyimage.polygon else: other = skyimage - pts1 = np.sort(list(self._polygon.points)[0], axis=0) - pts2 = np.sort(list(other.points)[0], axis=0) + pts1 = np.sort(next(iter(self._polygon.points)), axis=0) + pts2 = np.sort(next(iter(other.points)), axis=0) if np.allclose(pts1, pts2, rtol=0, atol=1e-8): intersect_poly = self._polygon.copy() else: diff --git a/romancal/skymatch/skymatch.py b/romancal/skymatch/skymatch.py index 38390b05f..8cc8b806e 100644 --- a/romancal/skymatch/skymatch.py +++ b/romancal/skymatch/skymatch.py @@ -269,9 +269,7 @@ def match(images, skymethod="global+match", match_down=True, subtract=False): raise ValueError("Argument 'images' must contain at least one image") log.debug( - "Total number of images to be sky-subtracted and/or matched: {:d}".format( - nimages - ) + f"Total number of images to be sky-subtracted and/or matched: {nimages:d}" ) # Print conversion factors @@ -284,11 +282,7 @@ def match(images, skymethod="global+match", match_down=True, subtract=False): if img_type == "Group": log.debug(f" * Group ID={img.id}. Conversion factors:") for im in img: - log.debug( - " - Image ID={}. Conversion factor = {:G}".format( - im.id, im.convf - ) - ) + log.debug(f" - Image ID={im.id}. Conversion factor = {im.convf:G}") else: log.debug(f" * Image ID={img.id}. Conversion factor = {img.convf:G}") @@ -350,9 +344,7 @@ def match(images, skymethod="global+match", match_down=True, subtract=False): if minsky is None: log.warning(' Unable to compute "global" sky value') sky_deltas = len(sky_deltas) * [minsky] - log.info( - ' "Global" sky value correction: {} ' "[not converted]".format(minsky) - ) + log.info(f' "Global" sky value correction: {minsky} ' "[not converted]") if do_match: log.info(" ") @@ -365,15 +357,13 @@ def match(images, skymethod="global+match", match_down=True, subtract=False): log.info(" ") log.info(f"***** {__name__:s}.{function_name:s}() ended on {runtime_end}") log.info( - "***** {:s}.{:s}() TOTAL RUN TIME: {}".format( - __name__, function_name, runtime_end - runtime_begin - ) + f"***** {__name__:s}.{function_name:s}() TOTAL RUN TIME: {runtime_end - runtime_begin}" ) log.info(" ") def _apply_sky(images, sky_deltas, do_global, do_skysub, show_old): - for img, sky in zip(images, sky_deltas): + for img, sky in zip(images, sky_deltas, strict=False): is_group = not isinstance(img, SkyImage) if do_global: @@ -403,25 +393,20 @@ def _apply_sky(images, sky_deltas, do_global, do_skysub, show_old): new_img_sky = [im.sky for im in img] # log sky values: - log.info( - " * Group ID={}. Sky background of " - "component images:".format(img.id) - ) + log.info(f" * Group ID={img.id}. Sky background of " "component images:") - for im, old_sky, new_sky in zip(img, old_img_sky, new_img_sky): + for im, old_sky, new_sky in zip( + img, old_img_sky, new_img_sky, strict=False + ): c = 1.0 / im.convf if show_old: log.info( - " - Image ID={}. Sky background: {:G} " - "(old={:G}, delta={:G})".format( - im.id, c * new_sky, c * old_sky, c * sky - ) + f" - Image ID={im.id}. Sky background: {c * new_sky:G} " + f"(old={c * old_sky:G}, delta={c * sky:G})" ) else: log.info( - " - Image ID={}. Sky background: {:G}".format( - im.id, c * new_sky - ) + f" - Image ID={im.id}. Sky background: {c * new_sky:G}" ) im.is_sky_valid = valid @@ -441,17 +426,11 @@ def _apply_sky(images, sky_deltas, do_global, do_skysub, show_old): c = 1.0 / img.convf if show_old: log.info( - " * Image ID={}. Sky background: {:G} " - "(old={:G}, delta={:G})".format( - img.id, c * new_sky, c * old_sky, c * sky - ) + f" * Image ID={img.id}. Sky background: {c * new_sky:G} " + f"(old={c * old_sky:G}, delta={c * sky:G})" ) else: - log.info( - " * Image ID={}. Sky background: {:G}".format( - img.id, c * new_sky - ) - ) + log.info(f" * Image ID={img.id}. Sky background: {c * new_sky:G}") img.is_sky_valid = valid @@ -527,8 +506,7 @@ def is_valid(i, j): if rank < ns - 1: log.warning(f"There are more unknown sky values ({ns}) to be solved for") log.warning( - "than there are independent equations available " - "(matrix rank={}).".format(rank) + "than there are independent equations available " f"(matrix rank={rank})." ) log.warning("Sky matching (delta) values will be computed only for") log.warning("a subset (or more independent subsets) of input images.") diff --git a/romancal/skymatch/skymatch_step.py b/romancal/skymatch/skymatch_step.py index 309f99fbc..bf4c5b185 100644 --- a/romancal/skymatch/skymatch_step.py +++ b/romancal/skymatch/skymatch_step.py @@ -2,8 +2,11 @@ Roman step for sky matching. """ +from __future__ import annotations + import logging from copy import deepcopy +from typing import TYPE_CHECKING import numpy as np from astropy.nddata.bitmask import bitfield_to_boolean_mask, interpret_bit_flags @@ -17,6 +20,9 @@ from .skymatch import match from .skystatistics import SkyStats +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["SkyMatchStep"] @@ -45,9 +51,9 @@ class SkyMatchStep(RomanStep): lsigma = float(min=0.0, default=4.0) # Lower clipping limit, in sigma usigma = float(min=0.0, default=4.0) # Upper clipping limit, in sigma binwidth = float(min=0.0, default=0.1) # Bin width for 'mode' and 'midpt' `skystat`, in sigma - """ # noqa: E501 + """ - reference_file_types = [] + reference_file_types: ClassVar = [] def process(self, input): self.log.setLevel(logging.DEBUG) @@ -73,7 +79,7 @@ def process(self, input): # create a list of "Sky" Images and/or Groups: images = [] with library: - for index, model in enumerate(library): + for model in library: images.append(self._imodel2skyim(model)) # match/compute sky values: @@ -147,7 +153,7 @@ def _imodel2skyim(self, image_model): raise ValueError( "'subtract' step's specification is " "inconsistent with background info already " - "present in image '{:s}' meta.".format(image_model.meta.filename) + f"present in image '{image_model.meta.filename:s}' meta." ) wcs = deepcopy(image_model.meta.wcs) diff --git a/romancal/skymatch/tests/test_skymatch.py b/romancal/skymatch/tests/test_skymatch.py index fc272bb91..58bfe73e7 100644 --- a/romancal/skymatch/tests/test_skymatch.py +++ b/romancal/skymatch/tests/test_skymatch.py @@ -69,8 +69,9 @@ def mk_image_model( sky_offset=[0, 0] * u.arcsec, rotation=0 * u.deg, image_shape=(100, 100), - rng=np.random.default_rng(619), + rng=None, ): + rng = np.random.default_rng(619) if rng is None else rng l2 = mk_level2_image(shape=image_shape) l2_im = ImageModel(l2) l2_im.data = rng.normal( @@ -178,7 +179,7 @@ def test_skymatch(wfi_rate, skymethod, subtract, skystat, match_down): levels = [9.12, 8.28, 2.56] with library: - for i, (im, lev) in enumerate(zip(library, levels)): + for i, (im, lev) in enumerate(zip(library, levels, strict=False)): im.data = rng.normal(loc=lev, scale=0.05, size=im.data.shape) library.shelve(im, i) @@ -208,7 +209,7 @@ def test_skymatch(wfi_rate, skymethod, subtract, skystat, match_down): with result: for i, (im, lev, rlev, slev) in enumerate( - zip(result, levels, ref_levels, sub_levels) + zip(result, levels, ref_levels, sub_levels, strict=False) ): # check that meta was set correctly: assert im.meta.background.method == skymethod @@ -243,7 +244,7 @@ def test_skymatch_overlap(mk_sky_match_image_models, skymethod, subtract, skysta levels = [9.12, 9.12, 8.28, 8.28, 2.56] with library: - for i, (im, lev) in enumerate(zip(library, levels)): + for i, (im, lev) in enumerate(zip(library, levels, strict=False)): im.data = rng.normal(loc=lev, scale=0.01, size=im.data.shape) library.shelve(im, i) @@ -273,7 +274,7 @@ def test_skymatch_overlap(mk_sky_match_image_models, skymethod, subtract, skysta with result: for i, (im, lev, rlev, slev) in enumerate( - zip(result, levels, ref_levels, sub_levels) + zip(result, levels, ref_levels, sub_levels, strict=False) ): # check that meta was set correctly: assert im.meta.background.method == skymethod @@ -325,7 +326,7 @@ def test_skymatch_2x(wfi_rate, skymethod, subtract): levels = [9.12, 8.28, 2.56] with library: - for i, (im, lev) in enumerate(zip(library, levels)): + for i, (im, lev) in enumerate(zip(library, levels, strict=False)): im.data = rng.normal(loc=lev, scale=0.05, size=im.data.shape) library.shelve(im, i) @@ -374,7 +375,7 @@ def test_skymatch_2x(wfi_rate, skymethod, subtract): # compare results with result2: for i, (im, lev, rlev, slev) in enumerate( - zip(result2, levels, ref_levels, sub_levels) + zip(result2, levels, ref_levels, sub_levels, strict=False) ): # check that meta was set correctly: assert im.meta.background.method == skymethod diff --git a/romancal/source_catalog/reference_data.py b/romancal/source_catalog/reference_data.py index 5fa528aea..8974ee3c0 100644 --- a/romancal/source_catalog/reference_data.py +++ b/romancal/source_catalog/reference_data.py @@ -40,7 +40,7 @@ class ReferenceData: """ def __init__(self, model, aperture_ee): - if not isinstance(model, (ImageModel, MosaicModel)): + if not isinstance(model, ImageModel | MosaicModel): raise ValueError("The input model must be a MosaicModel.") self.model = model diff --git a/romancal/source_catalog/source_catalog.py b/romancal/source_catalog/source_catalog.py index f76c920c7..3e06352f1 100644 --- a/romancal/source_catalog/source_catalog.py +++ b/romancal/source_catalog/source_catalog.py @@ -4,7 +4,6 @@ import logging import warnings -from typing import List import astropy.units as u import numpy as np @@ -106,8 +105,7 @@ def __init__( detection_cat=None, flux_unit="uJy", ): - - if not isinstance(model, (ImageModel, MosaicModel)): + if not isinstance(model, ImageModel | MosaicModel): raise ValueError("The input model must be an ImageModel or MosaicModel.") self.model = model # background was previously subtracted @@ -705,7 +703,9 @@ def set_ci_properties(self): Set the concentration indices as dynamic attributes on the class instance. """ - for name, value in zip(self.ci_colnames, self.concentration_indices): + for name, value in zip( + self.ci_colnames, self.concentration_indices, strict=False + ): setattr(self, name, value) @lazyproperty @@ -781,7 +781,7 @@ def _daofind_cutout(self): which has odd dimensions. """ cutout = [] - for xcen, ycen in zip(*np.transpose(self._xypos_aper)): + for xcen, ycen in zip(*np.transpose(self._xypos_aper), strict=False): try: cutout_ = extract_array( self.model.data, @@ -805,7 +805,7 @@ def _daofind_cutout_conv(self): which has odd dimensions. """ cutout = [] - for xcen, ycen in zip(*np.transpose(self._xypos_aper)): + for xcen, ycen in zip(*np.transpose(self._xypos_aper), strict=False): try: cutout_ = extract_array( self._daofind_convolved_data, @@ -1086,7 +1086,7 @@ def _split_skycoord(self, table): return table @staticmethod - def get_psf_photometry_catalog_colnames_mapping() -> List: + def get_psf_photometry_catalog_colnames_mapping() -> list: """ Set the mapping between the PSF photometry table column names and the final catalog column names. @@ -1136,7 +1136,7 @@ def get_psf_photometry_catalog_colnames_mapping() -> List: ] @lazyproperty - def psf_photometry_colnames(self) -> List: + def psf_photometry_colnames(self) -> list: """ Update and return column descriptions for PSF photometry results. diff --git a/romancal/source_catalog/source_catalog_step.py b/romancal/source_catalog/source_catalog_step.py index 1337f44ae..881555126 100644 --- a/romancal/source_catalog/source_catalog_step.py +++ b/romancal/source_catalog/source_catalog_step.py @@ -2,6 +2,10 @@ Module for the source catalog step. """ +from __future__ import annotations + +from typing import TYPE_CHECKING + import numpy as np from astropy.table import Table from roman_datamodels import datamodels, maker_utils @@ -14,6 +18,9 @@ from romancal.source_catalog.source_catalog import RomanSourceCatalog from romancal.stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + __all__ = ["SourceCatalogStep"] @@ -29,7 +36,7 @@ class SourceCatalogStep(RomanStep): """ class_alias = "source_catalog" - reference_file_types = [] + reference_file_types: ClassVar = [] spec = """ bkg_boxsize = integer(default=1000) # background mesh box size in pixels @@ -52,7 +59,7 @@ def process(self, step_input): else: input_model = datamodels.open(step_input) - if not isinstance(input_model, (ImageModel, MosaicModel)): + if not isinstance(input_model, ImageModel | MosaicModel): raise ValueError("The input model must be an ImageModel or MosaicModel.") # Copy the data and error arrays to avoid modifying the input diff --git a/romancal/step.py b/romancal/step.py index 58df99475..64a4bf79b 100644 --- a/romancal/step.py +++ b/romancal/step.py @@ -22,11 +22,12 @@ __all__ = [ "AssignWcsStep", - "DarkCurrentStep", "DQInitStep", + "DarkCurrentStep", "FlatFieldStep", "FluxStep", "LinearityStep", + "MultibandCatalogStep", "OutlierDetectionStep", "PhotomStep", "RampFitStep", @@ -35,6 +36,5 @@ "SaturationStep", "SkyMatchStep", "SourceCatalogStep", - "MultibandCatalogStep", "TweakRegStep", ] diff --git a/romancal/tests/test_dms_requirements.py b/romancal/tests/test_dms_requirements.py index 0ee2b0667..3ace0a49a 100644 --- a/romancal/tests/test_dms_requirements.py +++ b/romancal/tests/test_dms_requirements.py @@ -31,6 +31,4 @@ def test_requirements(): existing_tests.add(test) missing_tests = required_tests - existing_tests - assert ( - not missing_tests - ), f"could not find the following tests correlated with DMS requirements: {missing_tests}" + assert not missing_tests, f"could not find the following tests correlated with DMS requirements: {missing_tests}" diff --git a/romancal/tweakreg/astrometric_utils.py b/romancal/tweakreg/astrometric_utils.py index a1af04bcb..5aed4246c 100644 --- a/romancal/tweakreg/astrometric_utils.py +++ b/romancal/tweakreg/astrometric_utils.py @@ -213,16 +213,18 @@ def get_catalog(ra, dec, epoch=2016.0, sr=0.1, catalog="GAIADR3", timeout=TIMEOU service_url = f"{SERVICELOCATION}/{service_type}?{spec}" try: rawcat = requests.get(service_url, headers=headers, timeout=timeout) - except requests.exceptions.ConnectionError: + except requests.exceptions.ConnectionError as err: raise requests.exceptions.ConnectionError( "Could not connect to the VO API server. Try again later." - ) - except requests.exceptions.Timeout: - raise requests.exceptions.Timeout("The request to the VO API server timed out.") - except requests.exceptions.RequestException: + ) from err + except requests.exceptions.Timeout as err: + raise requests.exceptions.Timeout( + "The request to the VO API server timed out." + ) from err + except requests.exceptions.RequestException as err: raise requests.exceptions.RequestException( "There was an unexpected error with the request." - ) + ) from err # convert from bytes to a String r_contents = rawcat.content.decode() rstr = r_contents.split("\r\n") diff --git a/romancal/tweakreg/tests/test_astrometric_utils.py b/romancal/tweakreg/tests/test_astrometric_utils.py index ee5071f46..49884fa82 100644 --- a/romancal/tweakreg/tests/test_astrometric_utils.py +++ b/romancal/tweakreg/tests/test_astrometric_utils.py @@ -1,6 +1,5 @@ import os from io import StringIO -from typing import Tuple import numpy as np import pytest @@ -59,7 +58,7 @@ def get_parallax_correction_barycenter(epoch, gaia_ref_epoch_coords): correction = get_parallax_correction_earth_barycenter(epoch, gaia_coords) print(correction) (0.001, -0.002) - """ # noqa: E501 + """ obs_date = Time(epoch, format="decimalyear") earths_center_barycentric_coords = coord.get_body_barycentric( @@ -125,7 +124,7 @@ def get_proper_motion_correction(epoch, gaia_ref_epoch_coords, gaia_ref_epoch): } gaia_ref_epoch = 2020.0 get_proper_motion_correction(epoch, gaia_coords, gaia_ref_epoch) - """ # noqa: E501 + """ expected_new_dec = ( np.array( @@ -137,7 +136,9 @@ def get_proper_motion_correction(epoch, gaia_ref_epoch_coords, gaia_ref_epoch): average_dec = np.array( [ np.mean([new, old]) - for new, old in zip(expected_new_dec, gaia_ref_epoch_coords["dec"]) + for new, old in zip( + expected_new_dec, gaia_ref_epoch_coords["dec"], strict=False + ) ] ) pmra = gaia_ref_epoch_coords["pmra"] / np.cos(np.deg2rad(average_dec)) @@ -195,7 +196,7 @@ def get_parallax_correction(epoch, gaia_ref_epoch_coords): "parallax": 2.5 } get_parallax_correction(epoch, gaia_coords) - """ # noqa: E501 + """ # get parallax correction using textbook calculations (i.e. Earth's barycenter) parallax_corr = get_parallax_correction_barycenter( @@ -325,15 +326,15 @@ def _create_tel2sky_model(input_dm): def create_basic_wcs( img_shape: tuple = (100, 100), ref_pix: tuple = (0, 0), - ref_val: Tuple[u.Quantity, u.Quantity] = ( + ref_val: tuple[u.Quantity, u.Quantity] = ( u.Quantity("10 deg"), u.Quantity("0 deg"), ), - pix_scale: Tuple[u.Quantity, u.Quantity] = ( + pix_scale: tuple[u.Quantity, u.Quantity] = ( u.Quantity("0.1 arcsec"), u.Quantity("0.1 arcsec"), ), - theta: u.Quantity = u.Quantity("0 deg"), + theta: u.Quantity | None = None, ): """ Creates a basic WCS (no distortion) to map pixel coordinates @@ -369,6 +370,7 @@ def create_basic_wcs( does not contain the required steps to validate against the TweakReg pipeline. """ + theta = u.Quantity("0 deg") if theta is None else theta # linear transformations shift_pixel_coords = models.Shift(-ref_pix[0]) & models.Shift(-ref_pix[1]) diff --git a/romancal/tweakreg/tests/test_tweakreg.py b/romancal/tweakreg/tests/test_tweakreg.py index 3c44ba976..05a53aeda 100644 --- a/romancal/tweakreg/tests/test_tweakreg.py +++ b/romancal/tweakreg/tests/test_tweakreg.py @@ -4,7 +4,6 @@ import shutil from io import StringIO from pathlib import Path -from typing import Tuple import numpy as np import pytest @@ -204,15 +203,15 @@ def _create_tel2sky_model(input_dm): def create_basic_wcs( img_shape: tuple = (100, 100), ref_pix: tuple = (0, 0), - ref_val: Tuple[u.Quantity, u.Quantity] = ( + ref_val: tuple[u.Quantity, u.Quantity] = ( u.Quantity("10 deg"), u.Quantity("0 deg"), ), - pix_scale: Tuple[u.Quantity, u.Quantity] = ( + pix_scale: tuple[u.Quantity, u.Quantity] = ( u.Quantity("0.1 arcsec"), u.Quantity("0.1 arcsec"), ), - theta: u.Quantity = u.Quantity("0 deg"), + theta: u.Quantity | None = None, ): """ Creates a basic WCS (no distortion) to map pixel coordinates @@ -248,6 +247,7 @@ def create_basic_wcs( does not contain the required steps to validate against the TweakReg pipeline. """ + theta = u.Quantity("0 deg") if theta is None else theta # linear transformations shift_pixel_coords = models.Shift(-ref_pix[0]) & models.Shift(-ref_pix[1]) @@ -347,7 +347,9 @@ def get_catalog_data(input_dm, **kwargs): sr = kwargs.get("sr", 100 / 3600) add_shifts = kwargs.get("add_shifts", False) gaia_cat = get_catalog(right_ascension=ra, declination=dec, search_radius=sr) - gaia_source_coords = [(ra, dec) for ra, dec in zip(gaia_cat["ra"], gaia_cat["dec"])] + gaia_source_coords = [ + (ra, dec) for ra, dec in zip(gaia_cat["ra"], gaia_cat["dec"], strict=False) + ] catalog_data = np.array( [ input_dm.meta.wcs.world_to_pixel_values(ra, dec) @@ -751,7 +753,7 @@ def test_tweakreg_combine_custom_catalogs_and_asn_file(tmp_path, base_image): assert isinstance(res, ModelLibrary) with res: - for i, (model, target) in enumerate(zip(res, [img1, img2, img3])): + for i, (model, target) in enumerate(zip(res, [img1, img2, img3], strict=False)): assert hasattr(model.meta, "asn") assert ( @@ -792,7 +794,9 @@ def test_tweakreg_rotated_plane(tmp_path, theta, offset_x, offset_y, request): gaia_cat = get_catalog( right_ascension=270, declination=66, search_radius=100 / 3600 ) - gaia_source_coords = [(ra, dec) for ra, dec in zip(gaia_cat["ra"], gaia_cat["dec"])] + gaia_source_coords = [ + (ra, dec) for ra, dec in zip(gaia_cat["ra"], gaia_cat["dec"], strict=False) + ] img = request.getfixturevalue("base_image")(shift_1=1000, shift_2=1000) original_wcs = copy.deepcopy(img.meta.wcs) @@ -835,16 +839,18 @@ def test_tweakreg_rotated_plane(tmp_path, theta, offset_x, offset_y, request): # (rounded to the 10th decimal place to avoid floating point issues) dist1 = [ np.round(gref.separation(oref), 10) - for gref, oref in zip(gaia_ref_source, original_ref_source) + for gref, oref in zip(gaia_ref_source, original_ref_source, strict=False) ] # calculate distance between tweaked WCS result and Gaia # (rounded to the 10th decimal place to avoid floating point issues) dist2 = [ np.round(gref.separation(nref), 10) - for gref, nref in zip(gaia_ref_source, new_ref_source) + for gref, nref in zip(gaia_ref_source, new_ref_source, strict=False) ] - assert np.array([np.less_equal(d2, d1) for d1, d2 in zip(dist1, dist2)]).all() + assert np.array( + [np.less_equal(d2, d1) for d1, d2 in zip(dist1, dist2, strict=False)] + ).all() def test_tweakreg_parses_asn_correctly(tmp_path, base_image): @@ -932,7 +938,7 @@ def test_tweakreg_handles_multiple_groups(tmp_path, base_image): assert len(res.group_names) == 2 with res: - for r, i in zip(res, [img1, img2]): + for r, i in zip(res, [img1, img2], strict=False): assert r.meta.group_id == i.meta.observation.observation_id res.shelve(r, modify=False) @@ -950,7 +956,8 @@ def test_parse_catfile_valid_catalog(tmp_path, base_image): catdict = trs._parse_catfile(catfile) assert all( - x.meta.filename == y for x, y in zip(res_dict.get("datamodels"), catdict.keys()) + x.meta.filename == y + for x, y in zip(res_dict.get("datamodels"), catdict.keys(), strict=False) ) diff --git a/romancal/tweakreg/tweakreg_step.py b/romancal/tweakreg/tweakreg_step.py index 974bcd59b..3dfbe497f 100644 --- a/romancal/tweakreg/tweakreg_step.py +++ b/romancal/tweakreg/tweakreg_step.py @@ -2,8 +2,11 @@ Roman pipeline step for image alignment. """ +from __future__ import annotations + import os from pathlib import Path +from typing import TYPE_CHECKING import numpy as np from astropy.table import Table @@ -17,6 +20,9 @@ from ..datamodels import ModelLibrary from ..stpipe import RomanStep +if TYPE_CHECKING: + from typing import ClassVar + DEFAULT_ABS_REFCAT = SINGLE_GROUP_REFCAT[0] __all__ = ["TweakRegStep"] @@ -60,12 +66,11 @@ class TweakRegStep(RomanStep): abs_sigma = float(min=0.0, default=3.0) # Clipping limit in sigma units when performing absolute astrometry output_use_model = boolean(default=True) # When saving use `DataModel.meta.filename` update_source_catalog_coordinates = boolean(default=False) # Update source catalog file with tweaked coordinates? - """ # noqa: E501 + """ - reference_file_types = [] + reference_file_types: ClassVar = [] def process(self, input): - # properly handle input try: if isinstance(input, rdm.DataModel): @@ -250,7 +255,7 @@ def process(self, input): wcs_fit_results = { k: ( v.tolist() - if isinstance(v, (np.ndarray, np.bool_)) + if isinstance(v, np.ndarray | np.bool_) else v ) for k, v in imcat.meta["fit_info"].items()