diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5df1c376c8..176923763c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,7 @@ jobs: default_python: "3.12" envs: | - linux: check-dependencies + - linux: check-types latest_crds_contexts: uses: spacetelescope/crds/.github/workflows/contexts.yml@d96060f99a7ca75969f6652b050243592f4ebaeb # 12.0.0 crds_context: diff --git a/changes/8852.general.rst b/changes/8852.general.rst new file mode 100644 index 0000000000..0a584e8d63 --- /dev/null +++ b/changes/8852.general.rst @@ -0,0 +1 @@ +Added mypy type checking to CI checks diff --git a/jwst/__init__.py b/jwst/__init__.py index 8ec268c7e6..6fdd1e4afa 100644 --- a/jwst/__init__.py +++ b/jwst/__init__.py @@ -7,6 +7,6 @@ _regex_git_hash = re.compile(r".*\+g(\w+)") __version_commit__ = "" if "+" in __version__: - commit = _regex_git_hash.match(__version__).groups() + commit = _regex_git_hash.match(__version__).groups() # type: ignore if commit: __version_commit__ = commit[0] diff --git a/jwst/ami/ami_analyze.py b/jwst/ami/ami_analyze.py index dd7e24132c..631c780004 100644 --- a/jwst/ami/ami_analyze.py +++ b/jwst/ami/ami_analyze.py @@ -3,7 +3,7 @@ import numpy as np import copy -from jwst.datamodels import CubeModel, ImageModel +from jwst.datamodels import CubeModel, ImageModel # type: ignore[attr-defined] from .find_affine2d_parameters import find_rotation from . import instrument_data diff --git a/jwst/ami/leastsqnrm.py b/jwst/ami/leastsqnrm.py index fbbcd98b5f..d1c1a89524 100644 --- a/jwst/ami/leastsqnrm.py +++ b/jwst/ami/leastsqnrm.py @@ -613,7 +613,7 @@ def matrix_operations(img, model, flux=None, linfit=False, dqm=None): if linfit: try: - from linearfit import linearfit + from linearfit import linearfit # type: ignore[import-not-found] # dependent variables M = np.asmatrix(flatimg) diff --git a/jwst/ami/matrix_dft.py b/jwst/ami/matrix_dft.py index 63ab8074b3..3f47a230c3 100644 --- a/jwst/ami/matrix_dft.py +++ b/jwst/ami/matrix_dft.py @@ -198,7 +198,7 @@ def matrix_idft(*args, **kwargs): return matrix_dft(*args, **kwargs) -matrix_idft.__doc__ = matrix_dft.__doc__.replace( +matrix_idft.__doc__ = matrix_dft.__doc__.replace( # type: ignore 'Perform a matrix discrete Fourier transform', 'Perform an inverse matrix discrete Fourier transform' ) diff --git a/jwst/assign_wcs/util.py b/jwst/assign_wcs/util.py index 1b30ce2320..b33620ed8b 100644 --- a/jwst/assign_wcs/util.py +++ b/jwst/assign_wcs/util.py @@ -99,7 +99,7 @@ def _reproject(x, y): def compute_scale(wcs: WCS, fiducial: Union[tuple, np.ndarray], - disp_axis: int = None, pscale_ratio: float = None) -> float: + disp_axis: int | None = None, pscale_ratio: float | None = None) -> float: """Compute scaling transform. Parameters @@ -137,8 +137,8 @@ def compute_scale(wcs: WCS, fiducial: Union[tuple, np.ndarray], crval_with_offsets = wcs(*crpix_with_offsets, with_bounding_box=False) coords = SkyCoord(ra=crval_with_offsets[spatial_idx[0]], dec=crval_with_offsets[spatial_idx[1]], unit="deg") - xscale = np.abs(coords[0].separation(coords[1]).value) - yscale = np.abs(coords[0].separation(coords[2]).value) + xscale: float = np.abs(coords[0].separation(coords[1]).value) + yscale: float = np.abs(coords[0].separation(coords[2]).value) if pscale_ratio is not None: xscale *= pscale_ratio @@ -149,7 +149,8 @@ def compute_scale(wcs: WCS, fiducial: Union[tuple, np.ndarray], # Assuming disp_axis is consistent with DataModel.meta.wcsinfo.dispersion.direction return yscale if disp_axis == 1 else xscale - return np.sqrt(xscale * yscale) + scale: float = np.sqrt(xscale * yscale) + return scale def calc_rotation_matrix(roll_ref: float, v3i_yang: float, vparity: int = 1) -> List[float]: diff --git a/jwst/associations/__init__.py b/jwst/associations/__init__.py index 0697dca651..da7b016279 100644 --- a/jwst/associations/__init__.py +++ b/jwst/associations/__init__.py @@ -9,7 +9,7 @@ """ # Take version from the upstream package -from .. import __version__ +from jwst import __version__ # Utility diff --git a/jwst/associations/association.py b/jwst/associations/association.py index e6edce4b75..6acfec5c17 100644 --- a/jwst/associations/association.py +++ b/jwst/associations/association.py @@ -79,12 +79,12 @@ class Association(MutableMapping): GLOBAL_CONSTRAINT = None """Global constraints""" - INVALID_VALUES = None + INVALID_VALUES: tuple | None = None """Attribute values that indicate the attribute is not specified. """ - ioregistry = IORegistry() + ioregistry: IORegistry = IORegistry() """The association IO registry""" def __init__( diff --git a/jwst/associations/association_io.py b/jwst/associations/association_io.py index 5ce8c451a1..d768a20118 100644 --- a/jwst/associations/association_io.py +++ b/jwst/associations/association_io.py @@ -14,7 +14,7 @@ logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) -__all__ = [] +__all__: list = [] # Define JSON encoder to convert `Member` to `dict` diff --git a/jwst/associations/lib/constraint.py b/jwst/associations/lib/constraint.py index ed3c3d1c35..a3c270426f 100644 --- a/jwst/associations/lib/constraint.py +++ b/jwst/associations/lib/constraint.py @@ -56,7 +56,7 @@ class SimpleConstraintABC(abc.ABC): """ # Attributes to show in the string representation. - _str_attrs = ('name', 'value') + _str_attrs: tuple = ('name', 'value') def __new__(cls, *args, **kwargs): """Force creation of the constraint attribute dict before anything else.""" diff --git a/jwst/associations/lib/rules_level3_base.py b/jwst/associations/lib/rules_level3_base.py index a1da4f135b..944bc2b63b 100644 --- a/jwst/associations/lib/rules_level3_base.py +++ b/jwst/associations/lib/rules_level3_base.py @@ -97,7 +97,7 @@ class DMS_Level3_Base(DMSBaseMixin, Association): INVALID_VALUES = _EMPTY # Make sequences type-dependent - _sequences = defaultdict(Counter) + _sequences: defaultdict = defaultdict(Counter) def __init__(self, *args, **kwargs): diff --git a/jwst/associations/tests/helpers.py b/jwst/associations/tests/helpers.py index 11878d4dbe..82c77cc41a 100644 --- a/jwst/associations/tests/helpers.py +++ b/jwst/associations/tests/helpers.py @@ -56,11 +56,11 @@ class BasePoolRule(): # Define the pools and testing parameters related to them. # Each entry is a tuple starting with the path of the pool. - pools = [] + pools: list = [] # Define the rules that SHOULD be present. # Each entry is the class name of the rule. - valid_rules = [] + valid_rules: list = [] def test_rules_exist(self): rules = registry_level3_only() diff --git a/jwst/badpix_selfcal/badpix_selfcal.py b/jwst/badpix_selfcal/badpix_selfcal.py index 0691926552..c2fd84bbfd 100644 --- a/jwst/badpix_selfcal/badpix_selfcal.py +++ b/jwst/badpix_selfcal/badpix_selfcal.py @@ -3,7 +3,7 @@ import numpy as np -import jwst.datamodels as dm +from jwst.datamodels import IFUImageModel # type: ignore[attr-defined] from stcal.outlier_detection.utils import medfilt from stdatamodels.jwst.datamodels.dqflags import pixel @@ -12,7 +12,7 @@ def badpix_selfcal(minimg: np.ndarray, flagfrac_lower: float = 0.001, flagfrac_upper: float = 0.001, kernel_size: int = 15, - dispaxis=None) -> np.ndarray: + dispaxis=None) -> tuple: """ Flag residual artifacts as bad pixels in the DQ array of a JWST exposure @@ -59,18 +59,17 @@ def badpix_selfcal(minimg: np.ndarray, flag_low, flag_high = np.nanpercentile(minimg_hpf, [flagfrac_lower * 100, (1 - flagfrac_upper) * 100]) bad = (minimg_hpf > flag_high) | (minimg_hpf < flag_low) flagged_indices = np.where(bad) - return flagged_indices -def apply_flags(input_model: dm.IFUImageModel, flagged_indices: np.ndarray) -> dm.IFUImageModel: +def apply_flags(input_model: IFUImageModel, flagged_indices: np.ndarray) -> IFUImageModel: """ Apply the flagged indices to the input model. Sets the flagged pixels to NaN and the DQ flag to DO_NOT_USE + OTHER_BAD_PIXEL Parameters ---------- - input_model : dm.IFUImageModel + input_model : IFUImageModel Input science data to be corrected flagged_indices : np.ndarray Indices of the flagged pixels, @@ -78,7 +77,7 @@ def apply_flags(input_model: dm.IFUImageModel, flagged_indices: np.ndarray) -> d Returns ------- - output_model : dm.IFUImageModel + output_model : IFUImageModel Flagged data model """ diff --git a/jwst/cube_skymatch/cube_skymatch_step.py b/jwst/cube_skymatch/cube_skymatch_step.py index db61c79bb2..d8f6edb685 100644 --- a/jwst/cube_skymatch/cube_skymatch_step.py +++ b/jwst/cube_skymatch/cube_skymatch_step.py @@ -52,7 +52,7 @@ class CubeSkyMatchStep(Step): binwidth = float(min=0.0, default=0.1) # Bin width for 'mode' and 'midpt' `skystat`, in sigma """ - reference_file_types = [] + reference_file_types: list = [] def process(self, input1, input2): cube_models = ModelContainer(input1) diff --git a/jwst/datamodels/__init__.py b/jwst/datamodels/__init__.py index 1d3e0b23d6..a3d64a70e3 100644 --- a/jwst/datamodels/__init__.py +++ b/jwst/datamodels/__init__.py @@ -34,7 +34,7 @@ _deprecated_modules = ['schema'] # Deprecated models in stdatamodels -_deprecated_models = [] +_deprecated_models: list[str] = [] # Import all submodules from stdatamodels.jwst.datamodels for attr in dir(stdatamodels.jwst.datamodels): diff --git a/jwst/dq_init/dq_initialization.py b/jwst/dq_init/dq_initialization.py index 9c67d16a35..424ef2dbe8 100644 --- a/jwst/dq_init/dq_initialization.py +++ b/jwst/dq_init/dq_initialization.py @@ -5,7 +5,7 @@ from stdatamodels.jwst import datamodels from ..lib import reffile_utils -from jwst.datamodels import dqflags +from jwst.datamodels import dqflags # type: ignore[attr-defined] log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) diff --git a/jwst/dq_init/tests/test_dq_init.py b/jwst/dq_init/tests/test_dq_init.py index f4c5ac73f9..e18fae409c 100644 --- a/jwst/dq_init/tests/test_dq_init.py +++ b/jwst/dq_init/tests/test_dq_init.py @@ -259,12 +259,12 @@ def test_dq_add1_groupdq(): # Set parameters for multiple runs of guider data args = "xstart, ystart, xsize, ysize, nints, ngroups, instrument, exp_type, detector" -test_data = [(1, 1, 2048, 2048, 2, 2, 'FGS', 'FGS_ID-IMAGE', 'GUIDER1'), +test_data_multiple = [(1, 1, 2048, 2048, 2, 2, 'FGS', 'FGS_ID-IMAGE', 'GUIDER1'), (1, 1, 1032, 1024, 1, 5, 'MIRI', 'MIR_IMAGE', 'MIRIMAGE')] ids = ["GuiderRawModel-Image", "RampModel"] -@pytest.mark.parametrize(args, test_data, ids=ids) +@pytest.mark.parametrize(args, test_data_multiple, ids=ids) def test_fullstep(xstart, ystart, xsize, ysize, nints, ngroups, instrument, exp_type, detector): """Test that the full step runs""" diff --git a/jwst/extract_1d/apply_apcorr.py b/jwst/extract_1d/apply_apcorr.py index daf9f67588..3618719f89 100644 --- a/jwst/extract_1d/apply_apcorr.py +++ b/jwst/extract_1d/apply_apcorr.py @@ -1,9 +1,6 @@ import abc -from typing import Tuple, Union, Type from scipy.interpolate import RectBivariateSpline, interp1d -from astropy.io import fits -from stdatamodels import DataModel from stdatamodels.jwst.datamodels import MultiSlitModel from ..assign_wcs.util import compute_scale @@ -55,10 +52,8 @@ class ApCorrBase(abc.ABC): } } - size_key = None - - def __init__(self, input_model: DataModel, apcorr_table: fits.FITS_rec, sizeunit: str, - location: Tuple[float, float, float] = None, slit_name: str = None, **match_kwargs): + def __init__(self, input_model, apcorr_table, sizeunit, + location = None, slit_name = None, **match_kwargs): self.correction = None self.model = input_model @@ -75,6 +70,10 @@ def __init__(self, input_model: DataModel, apcorr_table: fits.FITS_rec, sizeunit self.apcorr_func = self.approximate() self.tabulated_correction = None + @property + def size_key(self): + return None + def _convert_size_units(self): """If the SIZE or Radius column is in units of arcseconds, convert to pixels.""" if self.apcorr_sizeunits.startswith('arcsec'): @@ -102,7 +101,7 @@ def _convert_size_units(self): 'pixels.' ) - def _get_match_keys(self) -> dict: + def _get_match_keys(self): """Get column keys needed for reducing the reference table based on input.""" instrument = self.model.meta.instrument.name.upper() exptype = self.model.meta.exposure.type.upper() @@ -113,7 +112,7 @@ def _get_match_keys(self) -> dict: if key in exptype: return relevant_pars[key] - def _get_match_pars(self) -> dict: + def _get_match_pars(self): """Get meta parameters required for reference table row-selection.""" match_pars = {} @@ -125,7 +124,7 @@ def _get_match_pars(self) -> dict: return match_pars - def _reduce_reftable(self) -> fits.FITS_record: + def _reduce_reftable(self): """Reduce full reference table to a single matched row.""" table = self._reference_table.copy() @@ -145,7 +144,7 @@ def approximate(self): """Generate an approximate aperture correction function based on input data.""" pass - def apply(self, spec_table: fits.FITS_rec): + def apply(self, spec_table): """Apply interpolated aperture correction value to source-related extraction results in-place. Parameters @@ -181,14 +180,14 @@ class ApCorrPhase(ApCorrBase): """ size_key = 'size' - def __init__(self, *args, pixphase: float = 0.5, **kwargs): + def __init__(self, *args, pixphase = 0.5, **kwargs): self.phase = pixphase # In the future we'll attempt to measure the pixel phase from inputs. super().__init__(*args, **kwargs) def approximate(self): """Generate an approximate function for interpolating apcorr values to input wavelength and size.""" - def _approx_func(wavelength: float, size: float, pixel_phase: float) -> RectBivariateSpline: + def _approx_func(wavelength, size, pixel_phase): """Create a 'custom' approximation function that approximates the aperture correction in two stages based on input data. @@ -228,7 +227,7 @@ def _approx_func(wavelength: float, size: float, pixel_phase: float) -> RectBiva def measure_phase(self): # Future method in determining pixel phase pass - def tabulate_correction(self, spec_table: fits.FITS_rec): + def tabulate_correction(self, spec_table): """Tabulate the interpolated aperture correction value. This will save time when applying it later, especially if it is to be applied to multiple integrations. @@ -255,7 +254,7 @@ def tabulate_correction(self, spec_table: fits.FITS_rec): self.tabulated_correction = np.asarray(coefs) - def apply(self, spec_table: fits.FITS_rec, use_tabulated=False): + def apply(self, spec_table, use_tabulated=False): """Apply interpolated aperture correction value to source-related extraction results in-place. Parameters @@ -297,8 +296,8 @@ def apply(self, spec_table: fits.FITS_rec, use_tabulated=False): class ApCorrRadial(ApCorrBase): """Aperture correction class used with spectral data produced from an extraction aperture radius.""" - def __init__(self, input_model: DataModel, apcorr_table, - location: Tuple[float, float, float] = None): + def __init__(self, input_model, apcorr_table, + location = None): self.correction = None self.model = input_model @@ -329,7 +328,7 @@ def _convert_size_units(self): 'pixels.' ) - def apply(self, spec_table: fits.FITS_rec): + def apply(self, spec_table): """Apply interpolated aperture correction value to source-related extraction results in-place. Parameters @@ -410,7 +409,7 @@ def approximate(self): return RectBivariateSpline(size, wavelength, apcorr.T, ky=1, kx=1) -def select_apcorr(input_model: DataModel) -> Union[Type[ApCorr], Type[ApCorrPhase], Type[ApCorrRadial]]: +def select_apcorr(input_model): """Select appropriate Aperture correction class based on input DataModel. Parameters diff --git a/jwst/extract_1d/extract.py b/jwst/extract_1d/extract.py index 7c0198bad7..bd7cda6d8c 100644 --- a/jwst/extract_1d/extract.py +++ b/jwst/extract_1d/extract.py @@ -5,14 +5,11 @@ import math import numpy as np -from typing import Union, Tuple, NamedTuple, List +from dataclasses import dataclass from astropy.modeling import polynomial from astropy.io import fits -from gwcs import WCS -from stdatamodels import DataModel -from stdatamodels.properties import ObjectNode from stdatamodels.jwst import datamodels -from stdatamodels.jwst.datamodels import dqflags, SlitModel, SpecModel +from stdatamodels.jwst.datamodels import dqflags from stdatamodels.jwst.datamodels.apcorr import ( MirLrsApcorrModel, MirMrsApcorrModel, NrcWfssApcorrModel, NrsFsApcorrModel, NrsMosApcorrModel, NrsIfuApcorrModel, NisWfssApcorrModel @@ -88,11 +85,12 @@ EXACT = "exact match" -class Aperture(NamedTuple): # When python 3.6 is no longer supported, consider converting to DataClass - xstart: Union[int, float] - xstop: Union[int, float] - ystart: Union[int, float] - ystop: Union[int, float] +@dataclass +class Aperture: + xstart: float + xstop: float + ystart: float + ystop: float class Extract1dError(Exception): @@ -109,7 +107,7 @@ class ContinueError(Exception): pass -def open_extract1d_ref(refname: str, exptype: str) -> dict: +def open_extract1d_ref(refname, exptype): """Open the extract1d reference file. Parameters @@ -174,7 +172,7 @@ def open_extract1d_ref(refname: str, exptype: str) -> dict: return ref_dict -def open_apcorr_ref(refname: str, exptype: str) -> DataModel: +def open_apcorr_ref(refname, exptype): """Determine the appropriate DataModel class to use when opening the input APCORR reference file. Parameters @@ -212,16 +210,16 @@ def open_apcorr_ref(refname: str, exptype: str) -> DataModel: def get_extract_parameters( - ref_dict: Union[dict, None], - input_model: DataModel, - slitname: str, - sp_order: int, - meta: ObjectNode, - smoothing_length: Union[int, None], - bkg_fit: str, - bkg_order: Union[int, None], - use_source_posn: Union[bool, None] -) -> dict: + ref_dict, + input_model, + slitname, + sp_order, + meta, + smoothing_length, + bkg_fit, + bkg_order, + use_source_posn +): """Get extract1d reference file values. Parameters @@ -449,7 +447,7 @@ def get_extract_parameters( return extract_params -def log_initial_parameters(extract_params: dict): +def log_initial_parameters(extract_params): """Log some of the initial extraction parameters. Parameters @@ -477,9 +475,7 @@ def log_initial_parameters(extract_params: dict): log.debug(f"use_source_posn = {extract_params['use_source_posn']}") -def get_aperture( - im_shape: tuple, wcs: WCS, extract_params: dict -) -> Union[Aperture, dict]: +def get_aperture(im_shape, wcs, extract_params): """Get the extraction limits xstart, xstop, ystart, ystop. Parameters @@ -521,7 +517,7 @@ def get_aperture( return ap_ref -def aperture_from_ref(extract_params: dict, im_shape: Tuple[int]) -> Aperture: +def aperture_from_ref(extract_params, im_shape): """Get extraction region from reference file or image shape. Parameters @@ -560,9 +556,7 @@ def aperture_from_ref(extract_params: dict, im_shape: Tuple[int]) -> Aperture: return ap_ref -def update_from_width( - ap_ref: Aperture, extract_width: Union[int, None], direction: int -) -> Aperture: +def update_from_width(ap_ref, extract_width, direction): """Update XD extraction limits based on extract_width. If extract_width was specified, that value should override @@ -623,9 +617,7 @@ def update_from_width( return ap_width -def update_from_shape( - ap: Aperture, im_shape: Tuple[int] -) -> Tuple[Aperture, bool]: +def update_from_shape(ap, im_shape): """Truncate extraction region based on input image shape. Parameters @@ -675,7 +667,7 @@ def update_from_shape( return ap_shape, truncated -def aperture_from_wcs(wcs: WCS) -> Union[NamedTuple, None]: +def aperture_from_wcs(wcs): """Get the limits over which the WCS is defined. Parameters @@ -685,7 +677,7 @@ def aperture_from_wcs(wcs: WCS) -> Union[NamedTuple, None]: Returns ------- - ap_wcs : namedtuple or None + ap_wcs : Aperture or None Keys are 'xstart', 'xstop', 'ystart', and 'ystop'. These are the limits copied directly from wcs.bounding_box. """ @@ -722,12 +714,7 @@ def aperture_from_wcs(wcs: WCS) -> Union[NamedTuple, None]: return ap_wcs -def update_from_wcs( - ap_ref: Aperture, - ap_wcs: Union[Aperture, None], - extract_width: int, - direction: int, -) -> Aperture: +def update_from_wcs(ap_ref, ap_wcs, extract_width, direction,): """Limit the extraction region to the WCS bounding box. Parameters @@ -783,9 +770,7 @@ def update_from_wcs( return ap -def sanity_check_limits( - ap_ref: Aperture, ap_wcs: Aperture, -) -> bool: +def sanity_check_limits(ap_ref, ap_wcs,): """Sanity check. Parameters @@ -818,9 +803,7 @@ def sanity_check_limits( return flag -def compare_start( - aperture_start: Union[int, float], wcs_bb_lower_lim: Union[int, float] -) -> Union[int, float]: +def compare_start(aperture_start, wcs_bb_lower_lim): """Compare the start limit from the aperture with the WCS lower limit. Extended summary @@ -855,9 +838,7 @@ def compare_start( return math.ceil(wcs_bb_lower_lim) -def compare_stop( - aperture_stop: Union[int, float], wcs_bb_upper_lim: Union[int, float] -) -> Union[int, float]: +def compare_stop(aperture_stop, wcs_bb_upper_lim): """Compare the stop limit from the aperture with the WCS upper limit. The more restrictive (i.e. smaller) limit is the one upon which the @@ -890,7 +871,7 @@ def compare_stop( return math.floor(wcs_bb_upper_lim) -def create_poly(coeff: List[float]) -> Union[polynomial.Polynomial1D, None]: +def create_poly(coeff): """Create a polynomial model from coefficients. Parameters @@ -1035,27 +1016,27 @@ class ExtractBase(abc.ABC): def __init__( self, - input_model: DataModel, - slit: Union[DataModel, None] = None, - ref_image: Union[DataModel, None] = None, - dispaxis: int = HORIZONTAL, - spectral_order: int = 1, - xstart: int = None, - xstop: int = None, - ystart: int = None, - ystop: int = None, - extract_width: int = None, - src_coeff: Union[List[List[float]], None] = None, - bkg_coeff: Union[List[List[float]], None] = None, - independent_var: str = "pixel", - smoothing_length: int = 0, - bkg_fit: str = "poly", - bkg_order: int = 0, - position_correction: float = 0., - subtract_background: Union[bool, None] = None, - use_source_posn: Union[bool, None] = None, - match: Union[str, None] = None, - ref_file_type: Union[str, None] = None + input_model, + slit = None, + ref_image = None, + dispaxis = HORIZONTAL, + spectral_order = 1, + xstart = None, + xstop = None, + ystart = None, + ystop = None, + extract_width = None, + src_coeff = None, + bkg_coeff = None, + independent_var = "pixel", + smoothing_length = 0, + bkg_fit = "poly", + bkg_order = 0, + position_correction = 0., + subtract_background = None, + use_source_posn = None, + match = None, + ref_file_type = None ): """ Parameters @@ -1222,9 +1203,7 @@ def assign_polynomial_limits(self): pass @staticmethod - def get_target_coordinates( - input_model: DataModel, slit: Union[DataModel, None] - ) -> Tuple[Union[float, None], Union[float, None]]: + def get_target_coordinates(input_model, slit): """Get the right ascension and declination of the target. For MultiSlitModel (or similar) data, each slit has the source @@ -1277,9 +1256,7 @@ def get_target_coordinates( return targ_ra, targ_dec - def offset_from_offset( - self, input_model: DataModel, slit: DataModel, - ) -> Tuple[float, Union[float, None]]: + def offset_from_offset(self, input_model, slit): """Get position offset from the target coordinates. Parameters @@ -1347,13 +1324,7 @@ def offset_from_offset( return offset, locn - def locn_from_wcs( - self, - input_model: DataModel, - slit: Union[DataModel, None], - targ_ra: Union[float, None], - targ_dec: Union[float, None], - ) -> Union[Tuple[int, float, float], None]: + def locn_from_wcs(self, input_model, slit, targ_ra, targ_dec): """Get the location of the spectrum, based on the WCS. Parameters @@ -1544,7 +1515,7 @@ def __init__(self, *base_args, **base_kwargs): log.debug("Background subtraction was specified in the reference file,") log.debug("but has been overridden by the step parameter.") - def nominal_locn(self, middle: int, middle_wl: float) -> Union[float, None]: + def nominal_locn(self, middle, middle_wl): """Find the nominal cross-dispersion location of the target spectrum. This version is for the case that the reference file is a JSON file, @@ -1604,7 +1575,7 @@ def nominal_locn(self, middle: int, middle_wl: float) -> Union[float, None]: return location - def add_position_correction(self, shape: tuple): + def add_position_correction(self, shape): """Add the position offset to the extraction location (in-place). Extended summary @@ -1666,7 +1637,7 @@ def _apply_position_corr(self, coeffs): coeff_list[0] += self.position_correction coeffs[i] = copy.copy(coeff_list) - def update_extraction_limits(self, ap: Aperture): + def update_extraction_limits(self, ap): """Update start and stop limits. Extended summary @@ -1796,19 +1767,7 @@ def _poly_per_region(self, coeffs): return result - def extract( - self, - data: np.ndarray, - var_poisson: np.ndarray, - var_rnoise: np.ndarray, - var_flat: np.ndarray, - wl_array: Union[np.ndarray, None], - ) -> Tuple[ - float, float, np.ndarray, - np.ndarray, np.ndarray, np.ndarray, np.ndarray, - np.ndarray, np.ndarray, np.ndarray, np.ndarray, - np.ndarray, np.ndarray - ]: + def extract(self, data, var_poisson, var_rnoise, var_flat, wl_array): """Do the extraction. Extended summary @@ -2048,9 +2007,7 @@ def __init__(self, *base_args, **base_kwargs): """ super().__init__(*base_args, **base_kwargs) - def nominal_locn( - self, middle: float, middle_wl: float - ) -> Union[float, None]: + def nominal_locn(self, middle, middle_wl): """Find the nominal cross-dispersion location of the target spectrum. This version is for the case that the reference file is an image. @@ -2106,7 +2063,7 @@ def nominal_locn( return location - def add_position_correction(self, shape: tuple): + def add_position_correction(self, shape): """Shift the reference image (in-place). Parameters @@ -2163,18 +2120,7 @@ def log_extraction_parameters(self): log.debug(f"smoothing_length = {self.smoothing_length}") log.debug(f"position_correction = {self.position_correction}") - def extract(self, - data: np.ndarray, - var_poisson: np.ndarray, - var_rnoise: np.ndarray, - var_flat: np.ndarray, - wl_array: np.ndarray, - ) -> \ - Tuple[ - float, float, np.ndarray, - np.ndarray, np.ndarray, np.ndarray, np.ndarray, - np.ndarray, np.ndarray, np.ndarray, np.ndarray, - np.ndarray, np.ndarray]: + def extract(self, data, var_poisson, var_rnoise, var_flat, wl_array): """ Do the actual extraction, for the case that the extract1d reference file is an image. @@ -2455,7 +2401,7 @@ def extract(self, background, b_var_poisson, b_var_rnoise, b_var_flat, npixels, dq) - def match_shape(self, shape: tuple) -> np.ndarray: + def match_shape(self, shape): """Truncate or expand reference image to match the science data. Extended summary @@ -2495,9 +2441,7 @@ def match_shape(self, shape: tuple) -> np.ndarray: return buf @staticmethod - def separate_target_and_background( - ref - ) -> Tuple[np.ndarray, Union[np.ndarray, None]]: + def separate_target_and_background(ref): """Create masks for target and background. Parameters @@ -2533,24 +2477,24 @@ def separate_target_and_background( def run_extract1d( - input_model: DataModel, - extract_ref_name: str, - apcorr_ref_name: Union[str, None], - smoothing_length: Union[int, None], - bkg_fit: str, - bkg_order: Union[int, None], - bkg_sigma_clip: float, - log_increment: int, - subtract_background: Union[bool, None], - use_source_posn: Union[bool, None], - center_xy: Union[float, None], - ifu_autocen: Union[bool, None], - ifu_rfcorr: Union[bool, None], - ifu_set_srctype: str, - ifu_rscale: float, - ifu_covar_scale: float, - was_source_model: bool = False, -) -> DataModel: + input_model, + extract_ref_name, + apcorr_ref_name, + smoothing_length, + bkg_fit, + bkg_order, + bkg_sigma_clip, + log_increment, + subtract_background, + use_source_posn, + center_xy, + ifu_autocen, + ifu_rfcorr, + ifu_set_srctype, + ifu_rscale, + ifu_covar_scale, + was_source_model, +): """Extract 1-D spectra. This just reads the reference files (if any) and calls do_extract1d. @@ -2688,7 +2632,7 @@ def run_extract1d( return output_model -def ref_dict_sanity_check(ref_dict: Union[dict, None]) -> Union[dict, None]: +def ref_dict_sanity_check(ref_dict): """Check for required entries. Parameters @@ -2723,24 +2667,24 @@ def ref_dict_sanity_check(ref_dict: Union[dict, None]) -> Union[dict, None]: def do_extract1d( - input_model: DataModel, - extract_ref_dict: Union[dict, None], - apcorr_ref_model=None, - smoothing_length: Union[int, None] = None, - bkg_fit: str = "poly", - bkg_order: Union[int, None] = None, - bkg_sigma_clip: float = 0, - log_increment: int = 50, - subtract_background: Union[int, None] = None, - use_source_posn: Union[bool, None] = None, - center_xy: Union[int, None] = None, - ifu_autocen: Union[bool, None] = None, - ifu_rfcorr: Union[bool, None] = None, - ifu_set_srctype: str = None, - ifu_rscale: float = None, - ifu_covar_scale: float = 1.0, - was_source_model: bool = False -) -> DataModel: + input_model, + extract_ref_dict, + apcorr_ref_model = None, + smoothing_length = None, + bkg_fit = "poly", + bkg_order = None, + bkg_sigma_clip = 0, + log_increment = 50, + subtract_background = None, + use_source_posn = None, + center_xy = None, + ifu_autocen = None, + ifu_rfcorr = None, + ifu_set_srctype = None, + ifu_rscale = None, + ifu_covar_scale = 1.0, + was_source_model = False +): """Extract 1-D spectra. In the pipeline, this function would be called by run_extract1d. @@ -3067,9 +3011,7 @@ def do_extract1d( return output_model -def populate_time_keywords( - input_model: DataModel, output_model: DataModel -) -> Union[DataModel, None]: +def populate_time_keywords(input_model, output_model): """Copy the integration times keywords to header keywords. Parameters @@ -3212,7 +3154,7 @@ def populate_time_keywords( n += 1 -def get_spectral_order(slit: DataModel) -> int: +def get_spectral_order(slit): """Get the spectral order number. Parameters @@ -3241,7 +3183,7 @@ def get_spectral_order(slit: DataModel) -> int: return sp_order -def is_prism(input_model: DataModel) -> bool: +def is_prism(input_model): """Determine whether the current observing mode used a prism. Extended summary @@ -3289,9 +3231,7 @@ def is_prism(input_model: DataModel) -> bool: return prism_mode -def copy_keyword_info( - slit: SlitModel, slitname: Union[str, None], spec: SpecModel -): +def copy_keyword_info(slit, slitname, spec): """Copy metadata from the input to the output spectrum. Parameters @@ -3343,16 +3283,7 @@ def copy_keyword_info( spec.shutter_state = slit.shutter_state -def extract_one_slit( - input_model: DataModel, - slit: SlitModel, - integ: int, - prev_offset: Union[float, str], - extract_params: dict -) -> Tuple[float, float, np.ndarray, - np.ndarray, np.ndarray, np.ndarray, np.ndarray, - np.ndarray, np.ndarray, np.ndarray, np.ndarray, - np.ndarray, np.ndarray, float]: +def extract_one_slit(input_model, slit, integ, prev_offset, extract_params): """Extract data for one slit, or spectral order, or plane. Parameters @@ -3567,11 +3498,7 @@ def extract_one_slit( extraction_values) -def replace_bad_values( - data: np.ndarray, - input_dq: Union[np.ndarray, None], - wl_array: np.ndarray -) -> np.ndarray: +def replace_bad_values(data, input_dq, wl_array): """Replace values flagged with DO_NOT_USE or that have NaN wavelengths. Parameters @@ -3611,10 +3538,7 @@ def replace_bad_values( return data -def nans_at_endpoints( - wavelength: np.ndarray, - dq: np.ndarray, -) -> Tuple[np.ndarray, np.ndarray, slice]: +def nans_at_endpoints(wavelength, dq): """Flag NaNs in the wavelength array. Extended summary @@ -3684,7 +3608,8 @@ def create_extraction(extract_ref_dict, output_model, apcorr_ref_model, log_increment, - is_multiple_slits): + is_multiple_slits +): if slit is None: meta_source = input_model else: diff --git a/jwst/fits_generator/generators.py b/jwst/fits_generator/generators.py index f7f8499a60..8e217a79f5 100644 --- a/jwst/fits_generator/generators.py +++ b/jwst/fits_generator/generators.py @@ -41,10 +41,10 @@ from . import util from jwst import __version__ -bool = __builtins__['bool'] -str = __builtins__['str'] -int = __builtins__['int'] -float = __builtins__['float'] +bool = __builtins__['bool'] # type: ignore[index] +str = __builtins__['str'] # type: ignore[index] +int = __builtins__['int'] # type: ignore[index] +float = __builtins__['float'] # type: ignore[index] def date_and_time_to_cds(date_str, time_str): """ diff --git a/jwst/fits_generator/input_file_types.py b/jwst/fits_generator/input_file_types.py index 163ea9dd28..c0333a572c 100644 --- a/jwst/fits_generator/input_file_types.py +++ b/jwst/fits_generator/input_file_types.py @@ -453,8 +453,8 @@ def is_niriss_spec_horiz(hdulist): InputAPTFile ] -file_type_names = [ +type_names = [ x.type_name for x in input_file_types] -file_type_names[-1] = 'or ' + file_type_names[-1] -file_type_names = ', '.join(file_type_names) +type_names[-1] = 'or ' + type_names[-1] +file_type_names = ', '.join(type_names) diff --git a/jwst/lib/engdb_direct.py b/jwst/lib/engdb_direct.py index 39ddf977ce..906e9b1200 100644 --- a/jwst/lib/engdb_direct.py +++ b/jwst/lib/engdb_direct.py @@ -80,7 +80,7 @@ def __init__(self, base_url=None, default_format='dict', **service_kwargs): ]), timeout=self.timeout) response.raise_for_status() - @property + @property # type: ignore[no-redef] def default_format(self): # noqa: F811 return self._default_format diff --git a/jwst/lib/progress.py b/jwst/lib/progress.py index 4091b62569..c0c9067fce 100644 --- a/jwst/lib/progress.py +++ b/jwst/lib/progress.py @@ -32,7 +32,7 @@ def next(self): # Stub the Bar functionality if the actual module does not exist. try: - from progress.bar import Bar as _Bar + from progress.bar import Bar as _Bar # type: ignore[import-not-found] except ModuleNotFoundError: _Bar = _BarStub diff --git a/jwst/lib/set_telescope_pointing.py b/jwst/lib/set_telescope_pointing.py index 4f46a443ab..e3c8ac4ea5 100644 --- a/jwst/lib/set_telescope_pointing.py +++ b/jwst/lib/set_telescope_pointing.py @@ -63,7 +63,7 @@ from enum import Enum import logging from math import (cos, sin, sqrt) -import typing +from typing import Callable from astropy import units as U from astropy.table import Table @@ -205,7 +205,7 @@ class Methods(Enum): #: Default algorithm under PCS_MODE TRACK/FINEGUIDE/MOVING. TRACK = TRACK_TR_202111 - def __new__(cls: object, value: str, func_name: str, calc_func: str, mnemonics: dict): + def __new__(cls, value, func_name, calc_func, mnemonics): obj = object.__new__(cls) obj._value_ = value obj._func_name = func_name @@ -306,39 +306,43 @@ def __str__(self): GuideStarPosition.__new__.__defaults__ = ((None,) * 3) +def NoneFactory(): + """Set default of any field in the dataclass to None without mypy getting angry""" + return dataclasses.field(default_factory=lambda: None) + # Transforms @dataclasses.dataclass class Transforms: """The matrices used in calculation of the M_eci2siaf transformation """ #: ECI to FGS1 - m_eci2fgs1: np.array = None + m_eci2fgs1: np.ndarray = NoneFactory() #: ECI to Guide Star - m_eci2gs: np.array = None + m_eci2gs: np.ndarray = NoneFactory() #: ECI to J-Frame - m_eci2j: np.array = None + m_eci2j: np.ndarray = NoneFactory() #: ECI to SIAF - m_eci2siaf: np.array = None + m_eci2siaf: np.ndarray = NoneFactory() #: ECI to SIFOV - m_eci2sifov: np.array = None + m_eci2sifov: np.ndarray = NoneFactory() #: ECI to V - m_eci2v: np.array = None + m_eci2v: np.ndarray = NoneFactory() #: FGSX to Guide Stars transformation - m_fgsx2gs: np.array = None + m_fgsx2gs: np.ndarray = NoneFactory() #: FGS1 to SIFOV - m_fgs12sifov: np.array = None + m_fgs12sifov: np.ndarray = NoneFactory() #: Velocity aberration - m_gs2gsapp: np.array = None + m_gs2gsapp: np.ndarray = NoneFactory() #: J-Frame to FGS1 - m_j2fgs1: np.array = None + m_j2fgs1: np.ndarray = NoneFactory() #: FSM correction - m_sifov_fsm_delta: np.array = None + m_sifov_fsm_delta: np.ndarray = NoneFactory() #: SIFOV to V1 - m_sifov2v: np.array = None + m_sifov2v: np.ndarray = NoneFactory() #: V to SIAF - m_v2siaf: np.array = None + m_v2siaf: np.ndarray = NoneFactory() #: Override values. Either another Transforms or dict-like object - override: object = None + override: object = NoneFactory() @classmethod def from_asdf(cls, asdf_file): @@ -426,50 +430,50 @@ class TransformParameters: #: The V3 position angle to use if the pointing information is not found. default_pa_v3: float = 0. #: Detector in use. - detector: str = None + detector: str = NoneFactory() #: Do not write out the modified file. dry_run: bool = False #: URL of the engineering telemetry database REST interface. - engdb_url: str = None + engdb_url: str = NoneFactory() #: Exposure type - exp_type: str = None + exp_type: str = NoneFactory() #: FGS to use as the guiding FGS. If None, will be set to what telemetry provides. - fgsid: int = None + fgsid: int = NoneFactory() #: The version of the FSM correction calculation to use. See `calc_sifov_fsm_delta_matrix` fsmcorr_version: str = 'latest' #: Units of the FSM correction values. Default is 'arcsec'. See `calc_sifov_fsm_delta_matrix` fsmcorr_units: str = 'arcsec' #: Guide star WCS info, typically from the input model. - guide_star_wcs: WCSRef = WCSRef() + guide_star_wcs: WCSRef = WCSRef(None, None, None) #: Transpose the `j2fgs1` matrix. j2fgs_transpose: bool = True #: The [DX, DY, DZ] barycentri velocity vector - jwst_velocity: np.array = None + jwst_velocity: np.ndarray = NoneFactory() #: The method, or algorithm, to use in calculating the transform. If not specified, the default method is used. method: Methods = Methods.default #: Observation end time - obsend: float = None + obsend: float = NoneFactory() #: Observation start time - obsstart: float = None + obsstart: float = NoneFactory() #: If set, matrices that should be used instead of the calculated one. - override_transforms: Transforms = None + override_transforms: Transforms = NoneFactory() #: The tracking mode in use. - pcs_mode: str = None + pcs_mode: str = NoneFactory() #: The observatory orientation, represented by the ECI quaternion, and other engineering mnemonics - pointing: Pointing = None + pointing: Pointing = NoneFactory() #: Reduction function to use on values. - reduce_func: typing.Callable = None + reduce_func: Callable = NoneFactory() #: The SIAF information for the input model - siaf: SIAF = None + siaf: SIAF = NoneFactory() #: The SIAF database - siaf_db: SiafDb = None + siaf_db: SiafDb = NoneFactory() #: If no telemetry can be found during the observation, #: the time, in seconds, beyond the observation time to search for telemetry. tolerance: float = 60. #: The date of observation (`jwst.datamodels.JwstDataModel.meta.date`) - useafter: str = None + useafter: str = NoneFactory() #: V3 position angle at Guide Star (`jwst.datamodels.JwstDataModel.meta.guide_star.gs_v3_pa_science`) - v3pa_at_gs: float = None + v3pa_at_gs: float = NoneFactory() def as_reprdict(self): """Return a dict where all values are REPR of their values""" @@ -878,7 +882,6 @@ def update_wcs_from_telem(model, t_pars: TransformParameters): logger.warning('Exception is %s', exception) logger.info("Setting ENGQLPTG keyword to PLANNED") model.meta.visit.engdb_pointing_quality = "PLANNED" - t_pars.pointing = None else: logger.info('Successful read of engineering quaternions:') logger.info('\tPointing: %s', t_pars.pointing) @@ -2987,7 +2990,7 @@ def gs_ideal_to_subarray(gs_position, aperture, flip=False): return x, y -def calc_wcs_guiding(model, t_pars, default_roll_ref=0.0, default_vparity=1, default_v3yangle=0.0): +def calc_wcs_guiding(model, t_pars: TransformParameters, default_roll_ref=0.0, default_vparity=1, default_v3yangle=0.0): """Calculate WCS info for FGS guiding For Fine Guidance guiding observations, nearly everything @@ -3028,7 +3031,7 @@ def calc_wcs_guiding(model, t_pars, default_roll_ref=0.0, default_vparity=1, def # Retrieve the appropriate mnemonics that represent the X/Y position of guide star # in the image. if t_pars.exp_type in ['fgs_acq1', 'fgs_acq2']: - mnemonics_to_read = FGS_ACQ_MNEMONICS + mnemonics_to_read = set(FGS_ACQ_MNEMONICS.keys()) elif t_pars.exp_type in ['fgs_fineguide', 'fgs_track']: mnemonics_to_read = FGS_GUIDED_MNEMONICS elif t_pars.exp_type in FGS_ID_EXP_TYPES: diff --git a/jwst/lib/set_velocity_aberration.py b/jwst/lib/set_velocity_aberration.py index 4e05f42ed9..7fcff79ffa 100644 --- a/jwst/lib/set_velocity_aberration.py +++ b/jwst/lib/set_velocity_aberration.py @@ -23,7 +23,7 @@ from gwcs.geometry import SphericalToCartesian, CartesianToSpherical from scipy.constants import speed_of_light import jwst.datamodels as dm -from jwst.datamodels import Level1bModel +from jwst.datamodels import Level1bModel # type: ignore[attr-defined] # Configure logging logger = logging.getLogger(__name__) diff --git a/jwst/lib/siafdb.py b/jwst/lib/siafdb.py index a90b74d205..4c9691498a 100644 --- a/jwst/lib/siafdb.py +++ b/jwst/lib/siafdb.py @@ -76,7 +76,7 @@ def __init__(self, source=None, prd=None): log_level = logging.ERROR try: with LoggingContext(logger_pysiaf, level=log_level): - import pysiaf + import pysiaf # type: ignore[import-not-found] except ImportError: raise ValueError('Package "pysiaf" is not installed. Cannot use the pysiaf api') self.pysiaf = pysiaf diff --git a/jwst/lib/tests/test_siafdb.py b/jwst/lib/tests/test_siafdb.py index 2cf0357850..f070140105 100644 --- a/jwst/lib/tests/test_siafdb.py +++ b/jwst/lib/tests/test_siafdb.py @@ -7,7 +7,7 @@ from jwst.lib import siafdb pytest.importorskip('pysiaf') -import pysiaf # noqa: E402 +import pysiaf # type: ignore[import-not-found] # noqa: E402 # Database paths DATA_PATH = Path(__file__).parent / 'data' diff --git a/jwst/mrs_imatch/mrs_imatch_step.py b/jwst/mrs_imatch/mrs_imatch_step.py index 543da7b208..3abf11aa12 100644 --- a/jwst/mrs_imatch/mrs_imatch_step.py +++ b/jwst/mrs_imatch/mrs_imatch_step.py @@ -31,7 +31,7 @@ class MRSIMatchStep(Step): skip = boolean(default=True) # Step must be turned on by parameter reference or user """ - reference_file_types = [] + reference_file_types: list = [] def process(self, images): diff --git a/jwst/outlier_detection/tests/test_fileio.py b/jwst/outlier_detection/tests/test_fileio.py index 6e01557c8e..41ef2c1b2f 100644 --- a/jwst/outlier_detection/tests/test_fileio.py +++ b/jwst/outlier_detection/tests/test_fileio.py @@ -1,6 +1,6 @@ import pytest from jwst.outlier_detection._fileio import _save_intermediate_output -from jwst.datamodels import ImageModel +from jwst.datamodels import ImageModel # type: ignore[attr-defined] from jwst.step import OutlierDetectionStep import os import numpy as np diff --git a/jwst/outlier_detection/tso.py b/jwst/outlier_detection/tso.py index d0161dffe7..2c0020986f 100644 --- a/jwst/outlier_detection/tso.py +++ b/jwst/outlier_detection/tso.py @@ -49,7 +49,7 @@ def detect_outliers( # Save median model if pars['save_intermediate_results'] is True # this will be a CubeModel with rolling median values. if save_intermediate_results: - median_model = dm.CubeModel(data=medians) + median_model = dm.CubeModel(data=medians) # type: ignore[name-defined] with dm.open(weighted_cube) as dm0: median_model.update(dm0) save_median(median_model, make_output_path) @@ -89,7 +89,11 @@ def weight_no_resample(input_model, good_bits): return weighted_cube -def compute_rolling_median(model: dm.CubeModel, weight_threshold: np.ndarray, w: int=25) -> np.ndarray: +def compute_rolling_median( + model: dm.CubeModel, # type: ignore[name-defined] + weight_threshold: np.ndarray, + w: int=25 +) -> np.ndarray: ''' Set bad and low-weight data to NaN, then compute the rolling median over the time axis. @@ -159,7 +163,7 @@ def moving_median_over_zeroth_axis(x: np.ndarray, w: int) -> np.ndarray: for idx in range(w - 1): shifted[idx:-w + idx + 1, idx] = x shifted[idx + 1:, idx + 1] = x - medians = np.median(shifted, axis=1) + medians: np.ndarray = np.median(shifted, axis=1) for idx in range(w - 1): medians[idx] = np.median(shifted[idx, :idx + 1]) medians[-idx - 1] = np.median(shifted[-idx - 1, -idx - 1:]) diff --git a/jwst/pipeline/tests/test_calwebb_image2.py b/jwst/pipeline/tests/test_calwebb_image2.py index e0389f42e3..0e24cc6768 100644 --- a/jwst/pipeline/tests/test_calwebb_image2.py +++ b/jwst/pipeline/tests/test_calwebb_image2.py @@ -2,7 +2,7 @@ import os import shutil from jwst.stpipe import Step -from jwst.datamodels import ImageModel +from jwst.datamodels import ImageModel # type: ignore[attr-defined] INPUT_FILE = "dummy_rate.fits" diff --git a/jwst/pipeline/tests/test_calwebb_image3.py b/jwst/pipeline/tests/test_calwebb_image3.py index 8531674664..3c7f813ac6 100644 --- a/jwst/pipeline/tests/test_calwebb_image3.py +++ b/jwst/pipeline/tests/test_calwebb_image3.py @@ -3,7 +3,7 @@ import shutil from jwst.stpipe import Step from jwst.assign_wcs import AssignWcsStep -from jwst.datamodels import ImageModel +from jwst.datamodels import ImageModel # type: ignore[attr-defined] INPUT_FILE = "dummy_cal.fits" diff --git a/jwst/pipeline/tests/test_calwebb_spec2.py b/jwst/pipeline/tests/test_calwebb_spec2.py index 71a39232f3..1baf57ac9d 100644 --- a/jwst/pipeline/tests/test_calwebb_spec2.py +++ b/jwst/pipeline/tests/test_calwebb_spec2.py @@ -3,7 +3,7 @@ import shutil from jwst.pipeline.calwebb_spec2 import Spec2Pipeline from jwst.stpipe import Step -from jwst.datamodels import IFUImageModel +from jwst.datamodels import IFUImageModel # type: ignore[attr-defined] INPUT_FILE = "dummy_rate.fits" diff --git a/jwst/pixel_replace/pixel_replace_step.py b/jwst/pixel_replace/pixel_replace_step.py index 7aa07b3dfd..b76260d0a0 100644 --- a/jwst/pixel_replace/pixel_replace_step.py +++ b/jwst/pixel_replace/pixel_replace_step.py @@ -3,7 +3,7 @@ from ..stpipe import Step from jwst.stpipe import record_step_status -from .. import datamodels +from jwst import datamodels from .pixel_replace import PixelReplacement __all__ = ["PixelReplaceStep"] diff --git a/jwst/regtest/test_associations_sdp_pools.py b/jwst/regtest/test_associations_sdp_pools.py index 3cbe4afe08..bda356691d 100644 --- a/jwst/regtest/test_associations_sdp_pools.py +++ b/jwst/regtest/test_associations_sdp_pools.py @@ -22,11 +22,11 @@ # Mark expected failures. Key is the pool name # and value is the reason message. -EXPECTED_FAILS = { +EXPECTED_FAILS: dict = { } # Pools that require special handling -SPECIAL_DEFAULT = { +SPECIAL_DEFAULT: dict = { 'args': [], 'xfail': None, 'slow': False, diff --git a/jwst/regtest/test_associations_standards.py b/jwst/regtest/test_associations_standards.py index b8ba75c0b6..6f103385ed 100644 --- a/jwst/regtest/test_associations_standards.py +++ b/jwst/regtest/test_associations_standards.py @@ -34,7 +34,7 @@ ] # Produce general associations -DEF_ARGS = [] +DEF_ARGS: list = [] # Define the standards diff --git a/jwst/regtest/test_fgs_pointing.py b/jwst/regtest/test_fgs_pointing.py index f905da5f6b..ba90dce043 100644 --- a/jwst/regtest/test_fgs_pointing.py +++ b/jwst/regtest/test_fgs_pointing.py @@ -9,7 +9,7 @@ from numpy import array # noqa: E402 from numpy import allclose, isclose # noqa: E402 -from jwst.datamodels import Level1bModel # noqa: E402 +from jwst.datamodels import Level1bModel # type: ignore[attr-defined] # noqa: E402 from jwst.lib import engdb_mast # noqa: E402 from jwst.lib import engdb_tools # noqa: E402 from jwst.lib import set_telescope_pointing as stp # noqa: E402 diff --git a/jwst/resample/resample_step.py b/jwst/resample/resample_step.py index 5790fb4f58..4132850918 100755 --- a/jwst/resample/resample_step.py +++ b/jwst/resample/resample_step.py @@ -4,7 +4,7 @@ import asdf -from jwst.datamodels import ModelLibrary, ImageModel +from jwst.datamodels import ModelLibrary, ImageModel # type: ignore[attr-defined] from jwst.lib.pipe_utils import match_nans_and_flags from . import resample @@ -56,7 +56,7 @@ class ResampleStep(Step): in_memory = boolean(default=True) # Keep images in memory """ - reference_file_types = [] + reference_file_types: list = [] def process(self, input): diff --git a/jwst/skymatch/skymatch_step.py b/jwst/skymatch/skymatch_step.py index c64b63c00d..9762a72048 100644 --- a/jwst/skymatch/skymatch_step.py +++ b/jwst/skymatch/skymatch_step.py @@ -63,7 +63,7 @@ class SkyMatchStep(Step): in_memory = boolean(default=True) # If False, preserve memory using temporary files """ # noqa: E501 - reference_file_types = [] + reference_file_types: list = [] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/jwst/stpipe/core.py b/jwst/stpipe/core.py index 21efa00ef0..0c3df08d71 100644 --- a/jwst/stpipe/core.py +++ b/jwst/stpipe/core.py @@ -11,7 +11,7 @@ from stpipe import Step from stpipe import Pipeline -from .. import __version_commit__, __version__ +from jwst import __version_commit__, __version__ from ..lib.suffix import remove_suffix diff --git a/jwst/tests/test_import.py b/jwst/tests/test_import.py index 5df92128d5..63cd440361 100644 --- a/jwst/tests/test_import.py +++ b/jwst/tests/test_import.py @@ -8,7 +8,7 @@ import jwst -def dependencies(package, exclude: [str]): +def dependencies(package, exclude: list[str]): return [ module[1] for module in pkgutil.walk_packages( diff --git a/jwst/tweakreg/tweakreg_step.py b/jwst/tweakreg/tweakreg_step.py index 0d8363771b..1638189daf 100644 --- a/jwst/tweakreg/tweakreg_step.py +++ b/jwst/tweakreg/tweakreg_step.py @@ -126,7 +126,7 @@ class TweakRegStep(Step): in_memory = boolean(default=True) # If False, preserve memory using temporary files at expense of runtime """ - reference_file_types = [] + reference_file_types: list = [] def process(self, input): if isinstance(input, ModelLibrary): diff --git a/jwst/wfss_contam/tests/test_observations.py b/jwst/wfss_contam/tests/test_observations.py index 71228f6479..422b2900ec 100644 --- a/jwst/wfss_contam/tests/test_observations.py +++ b/jwst/wfss_contam/tests/test_observations.py @@ -12,7 +12,7 @@ from jwst.wfss_contam.observations import background_subtract from jwst.wfss_contam.disperse import dispersed_pixel from jwst.wfss_contam.tests import data -from jwst.datamodels import SegmentationMapModel, ImageModel +from jwst.datamodels import SegmentationMapModel, ImageModel # type: ignore[attr-defined] data_path = os.path.split(os.path.abspath(data.__file__))[0] DIR_IMAGE = "direct_image.fits" diff --git a/pyproject.toml b/pyproject.toml index c73a69fef9..bbe52dca46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -264,6 +264,8 @@ line-length = 130 lint.ignore = [ "E741", # ambiguous variable name (O/0, l/I, etc.) "E722", # Do not use bare `except` + "ANN101", # Missing type annotation for self in method + "ANN102", # Missing type annotation for cls in classmethod ] [tool.ruff.lint.per-file-ignores] @@ -286,6 +288,24 @@ lint.ignore = [ "E402", # module-level import not at top of file ] +[tool.mypy] +python_version = "3.12" +warn_return_any = true +warn_unused_configs = true +disable_error_code = "import-untyped" # do not check imports +exclude = ["build"] + +[[tool.mypy.overrides]] +# don't complain about the installed c parts of this library +module = [ + "jwst.cube_build.cube_match_internal", + "jwst.cube_build.cube_match_sky_driz", + "jwst.cube_build.cube_match_sky_pointcloud", + "jwst.lib.winclip", + "jwst.straylight.calc_xart", +] +ignore_missing_imports = true + [tool.towncrier] filename = "CHANGES.rst" directory = "changes" diff --git a/tox.ini b/tox.ini index 8813c9998a..77df7bbe27 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,14 @@ envlist = # tox -l -v # +[testenv:check-types] +description = check type hints, e.g. with mypy +deps = + mypy + types-requests +commands = + mypy jwst --config-file pyproject.toml + [testenv:check-dependencies] description = verify that install_requires in setup.cfg has correct dependencies # `extras` needs to be empty to check modules without additional dependencies