-
Notifications
You must be signed in to change notification settings - Fork 543
Description
Goal
Improve static typing (e.g., mypy, IDE support) for Nipype interface classes that inherit from BaseInterface, so that concrete types of inputs and outputs are correctly inferred.
Background
Inputs and outputs are instantiated dynamically from input_spec / output_spec in BaseInterfaces __init__ function, which prevents mypy from inferring their concrete types in subclasses.
Since InputSpec and OutputSpec classes inherit from traits.HasTraits, and recent versions of traits include type stubs, type hinting these classes is now feasible.
Option 1: Explicit annotations in subclasses
Add inputs: FooInputSpec / outputs: FooOutputSpec to each interface class.
Example:
class NewSegment(SPMCommand):
input_spec = NewSegmentInputSpec
output_spec = NewSegmentOutputSpec
inputs: NewSegmentInputSpec
outputs: NewSegmentOutputSpecPros: Simple, minimal change, no base-class refactor needed
Cons: Must be repeated for every interface
Option 2: Generic base / command classes
Make Interface (and intermediate command classes) use Generic types to propagate input/output types.
Example:
from typing import TypeVar, Generic
# Type variables for input/output specs
I = TypeVar("I", bound=BaseInterfaceInputSpec)
O = TypeVar("O", bound=BaseInterfaceOutputSpec)
# Generic base interface
class Interface(Generic[I, O]):
inputs: I
outputs: O
# Concrete interface specifying generics
class NewSegment(BaseInterface[NewSegmentInputSpec, NewSegmentOutputSpec]):
input_spec = NewSegmentInputSpec
output_spec = NewSegmentOutputSpec
# Usage example
seg = NewSegment()
seg.inputs.warping_regularization # mypy knows type
seg.outputs.bias_corrected_images # mypy knows typePros: DRY, scalable, propagates types
Cons: Requires broader changes
Next steps
I’d like feedback on whether this is desirable and which approach fits Nipype best. In my local install I tried Option 1, but Option 2 may be better overall, as it would also allow base-class methods to have typed return values.
I’m happy to start with a small, incremental PR for SPM interfaces using either approach, based on your preference.
Open to suggestions and alternatives.