[ENH]: Introduce junfer.typing module #392

Merged
synchon merged 7 commits from style/typing into main 2024-11-13 08:51:58 +00:00
41 changed files with 240 additions and 171 deletions

View file

@ -39,6 +39,7 @@ The documentation is available at [https://juaml.github.io/junifer](https://juam
* `preprocess`: Preprocessing module.
* `storage`: Storage module.
* `testing`: Testing components module.
* `typing`: Type hints module.
* `utils`: Utilities module (e.g. logging).
## Installation

View file

@ -0,0 +1 @@
Introduce ``junifer.typing`` sub-module to organise internal and external type hints by `Synchon Mandal`_

View file

@ -28,7 +28,7 @@ component depends on. For example, for :class:`.RSSETSMarker`, we have:
.. code-block:: python
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn"}
The type annotation is for documentation and static type checking purposes.
Although not required, we highly recommend you use them, your future self
@ -44,7 +44,7 @@ by having a class attribute like so:
.. code-block:: python
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{
"name": "afni",
"commands": ["3dReHo", "3dAFNItoNIFTI"],
@ -78,7 +78,7 @@ that it shows the problem a bit better and how we solve it:
class SpaceWarper(BasePreprocessor):
# docstring
_CONDITIONAL_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, Type]]]] = [
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
{
"using": "fsl",
"depends_on": FSLWarper,
@ -115,14 +115,14 @@ similar. ``FSLWarper`` looks like this (only the relevant part is shown here):
class FSLWarper:
# docstring
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{
"name": "fsl",
"commands": ["flirt", "applywarp"],
},
]
_DEPENDENCIES: ClassVar[Set[str]] = {"numpy", "nibabel"}
_DEPENDENCIES: ClassVar[Dependencies] = {"numpy", "nibabel"}
def preprocess(
self,

View file

@ -196,15 +196,16 @@ Finally, we need to register the Marker using the ``@register_marker`` decorator
from junifer.api.decorators import register_marker
from junifer.data import get_parcellation
from junifer.markers import BaseMarker
from junifer.typing import Dependencies, MarkerInOutMappings
from nilearn.maskers import NiftiLabelsMasker
@register_marker
class ParcelMean(BaseMarker):
_DEPENDENCIES = {"nilearn", "numpy"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn", "numpy"}
_MARKER_INOUT_MAPPINGS: ClassVar[dict[str, dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"BOLD": {
"parcel_mean": "timeseries",
},

View file

@ -13,6 +13,7 @@ __all__ = [
"external",
"onthefly",
"testing",
"typing",
"__version__",
]
@ -30,6 +31,7 @@ from . import (
utils,
external,
testing,
typing,
onthefly,
)
from ._version import __version__

View file

@ -8,6 +8,7 @@
from typing import Type
from ..pipeline import PipelineComponentRegistry
from ..typing import DataGrabberLike, MarkerLike, PreprocessorLike, StorageLike
__all__ = [
@ -19,7 +20,7 @@ __all__ = [
]
def register_datagrabber(klass: Type) -> Type:
def register_datagrabber(klass: DataGrabberLike) -> DataGrabberLike:
"""Register DataGrabber.
Registers the DataGrabber so it can be used by name.
@ -73,7 +74,7 @@ def register_datareader(klass: Type) -> Type:
return klass
def register_preprocessor(klass: Type) -> Type:
def register_preprocessor(klass: PreprocessorLike) -> PreprocessorLike:
"""Preprocessor registration decorator.
Registers the preprocessor so it can be used by name.
@ -96,7 +97,7 @@ def register_preprocessor(klass: Type) -> Type:
return klass
def register_marker(klass: Type) -> Type:
def register_marker(klass: MarkerLike) -> MarkerLike:
"""Marker registration decorator.
Registers the marker so it can be used by name.
@ -119,7 +120,7 @@ def register_marker(klass: Type) -> Type:
return klass
def register_storage(klass: Type) -> Type:
def register_storage(klass: StorageLike) -> StorageLike:
"""Storage registration decorator.
Registers the storage so it can be used by name.

View file

@ -11,22 +11,23 @@ from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union
from ..api.queue_context import GnuParallelLocalAdapter, HTCondorAdapter
from ..datagrabber.base import BaseDataGrabber
from ..markers.base import BaseMarker
from ..datagrabber import BaseDataGrabber
from ..markers import BaseMarker
from ..pipeline import (
MarkerCollection,
PipelineComponentRegistry,
WorkDirManager,
)
from ..preprocess.base import BasePreprocessor
from ..storage.base import BaseFeatureStorage
from ..preprocess import BasePreprocessor
from ..storage import BaseFeatureStorage
from ..typing import DataGrabberLike, MarkerLike, PreprocessorLike, StorageLike
from ..utils import logger, raise_error, yaml
__all__ = ["run", "collect", "queue", "reset", "list_elements"]
def _get_datagrabber(datagrabber_config: Dict) -> BaseDataGrabber:
def _get_datagrabber(datagrabber_config: Dict) -> DataGrabberLike:
"""Get DataGrabber.
Parameters
@ -48,7 +49,7 @@ def _get_datagrabber(datagrabber_config: Dict) -> BaseDataGrabber:
)
def _get_preprocessor(preprocessing_config: Dict) -> BasePreprocessor:
def _get_preprocessor(preprocessing_config: Dict) -> PreprocessorLike:
"""Get Preprocessor.
Parameters
@ -70,7 +71,7 @@ def _get_preprocessor(preprocessing_config: Dict) -> BasePreprocessor:
)
def _get_marker(marker_config: Dict) -> BaseMarker:
def _get_marker(marker_config: Dict) -> MarkerLike:
"""Get Marker.
Parameters
@ -92,7 +93,7 @@ def _get_marker(marker_config: Dict) -> BaseMarker:
)
def _get_storage(storage_config: Dict) -> BaseFeatureStorage:
def _get_storage(storage_config: Dict) -> StorageLike:
"""Get Storage.
Parameters

View file

@ -8,6 +8,7 @@
from typing import Dict, List, Tuple, Union
from ..api.decorators import register_datagrabber
from ..typing import DataGrabberLike
from ..utils import deep_update, raise_error
from .base import BaseDataGrabber
@ -37,7 +38,7 @@ class MultipleDataGrabber(BaseDataGrabber):
"""
def __init__(self, datagrabbers: List[BaseDataGrabber], **kwargs) -> None:
def __init__(self, datagrabbers: List[DataGrabberLike], **kwargs) -> None:
# Check datagrabbers consistency
# Check for same element keys
first_keys = datagrabbers[0].get_element_keys()

View file

@ -6,16 +6,13 @@
from abc import ABC, abstractmethod
from copy import deepcopy
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
from typing import Any, Dict, List, Optional, Union
from ..pipeline import PipelineStepMixin, UpdateMetaMixin
from ..typing import StorageLike
from ..utils import logger, raise_error
if TYPE_CHECKING:
from junifer.storage import BaseFeatureStorage
__all__ = ["BaseMarker"]
@ -159,7 +156,7 @@ class BaseMarker(ABC, PipelineStepMixin, UpdateMetaMixin):
type_: str,
feature: str,
out: Dict[str, Any],
storage: "BaseFeatureStorage",
storage: StorageLike,
) -> None:
"""Store.
@ -182,7 +179,7 @@ class BaseMarker(ABC, PipelineStepMixin, UpdateMetaMixin):
def _fit_transform(
self,
input: Dict[str, Dict],
storage: Optional["BaseFeatureStorage"] = None,
storage: Optional[StorageLike] = None,
) -> Dict:
"""Fit and transform.

View file

@ -11,7 +11,6 @@ from typing import (
Dict,
List,
Optional,
Set,
Union,
)
@ -25,6 +24,7 @@ from ..external.BrainPrint.brainprint.brainprint import (
)
from ..external.BrainPrint.brainprint.surfaces import surf_to_vtk
from ..pipeline import WorkDirManager
from ..typing import Dependencies, ExternalDependencies, MarkerInOutMappings
from ..utils import logger, run_ext_cmd
from .base import BaseMarker
@ -68,7 +68,7 @@ class BrainPrint(BaseMarker):
"""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{
"name": "freesurfer",
"commands": [
@ -80,9 +80,9 @@ class BrainPrint(BaseMarker):
},
]
_DEPENDENCIES: ClassVar[Set[str]] = {"lapy", "numpy"}
_DEPENDENCIES: ClassVar[Dependencies] = {"lapy", "numpy"}
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"FreeSurfer": {
"eigenvalues": "scalar_table",
"areas": "vector",

View file

@ -11,10 +11,10 @@ from typing import (
Dict,
List,
Optional,
Set,
Union,
)
from ...typing import Dependencies, MarkerInOutMappings
from ...utils import raise_error
from ..base import BaseMarker
from ..parcel_aggregation import ParcelAggregation
@ -51,9 +51,9 @@ class ComplexityBase(BaseMarker):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn", "neurokit2"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn", "neurokit2"}
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"BOLD": {
"complexity": "vector",
},

View file

@ -6,11 +6,12 @@
# Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL
from typing import Any, ClassVar, Dict, List, Optional, Set, Union
from typing import Any, ClassVar, Dict, List, Optional, Union
import numpy as np
from ..api.decorators import register_marker
from ..typing import Dependencies, MarkerInOutMappings
from ..utils import logger
from .base import BaseMarker
from .parcel_aggregation import ParcelAggregation
@ -45,9 +46,9 @@ class RSSETSMarker(BaseMarker):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn"}
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"BOLD": {
"rss_ets": "timeseries",
},

View file

@ -8,22 +8,20 @@ from pathlib import Path
from typing import (
TYPE_CHECKING,
ClassVar,
Dict,
List,
Optional,
Tuple,
Union,
)
import nibabel as nib
from ...pipeline import WorkDirManager
from ...typing import ExternalDependencies
from ...utils import logger, run_ext_cmd
from ...utils.singleton import Singleton
if TYPE_CHECKING:
from nibabel import Nifti1Image
from nibabel.nifti1 import Nifti1Image
__all__ = ["AFNIALFF"]
@ -37,7 +35,7 @@ class AFNIALFF(metaclass=Singleton):
"""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{
"name": "afni",
"commands": ["3dRSFC", "3dAFNItoNIFTI"],

View file

@ -9,7 +9,6 @@ from typing import (
TYPE_CHECKING,
ClassVar,
Optional,
Set,
Tuple,
)
@ -19,12 +18,13 @@ import scipy as sp
from nilearn import image as nimg
from ...pipeline import WorkDirManager
from ...typing import Dependencies
from ...utils import logger
from ...utils.singleton import Singleton
if TYPE_CHECKING:
from nibabel import Nifti1Image
from nibabel.nifti1 import Nifti1Image
__all__ = ["JuniferALFF"]
@ -37,7 +37,7 @@ class JuniferALFF(metaclass=Singleton):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"numpy", "nilearn", "scipy"}
_DEPENDENCIES: ClassVar[Dependencies] = {"numpy", "nilearn", "scipy"}
def __del__(self) -> None:
"""Terminate the class."""

View file

@ -12,13 +12,11 @@ from typing import (
Any,
ClassVar,
Dict,
List,
Optional,
Tuple,
Type,
Union,
)
from ...typing import ConditionalDependencies, MarkerInOutMappings
from ...utils.logging import logger, raise_error
from ..base import BaseMarker
from ._afni_falff import AFNIALFF
@ -26,7 +24,7 @@ from ._junifer_falff import JuniferALFF
if TYPE_CHECKING:
from nibabel import Nifti1Image
from nibabel.nifti1 import Nifti1Image
__all__ = ["ALFFBase"]
@ -72,7 +70,7 @@ class ALFFBase(BaseMarker):
"""
_CONDITIONAL_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, Type]]]] = [
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
{
"using": "afni",
"depends_on": AFNIALFF,
@ -83,7 +81,7 @@ class ALFFBase(BaseMarker):
},
]
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"BOLD": {
"alff": "vector",
"falff": "vector",

View file

@ -4,11 +4,12 @@
# Kaustubh R. Patil <k.patil@fz-juelich.de>
# License: AGPL
from typing import Any, ClassVar, Dict, List, Optional, Set, Union
from typing import Any, ClassVar, Dict, List, Optional, Union
import pandas as pd
from ...api.decorators import register_marker
from ...typing import Dependencies, MarkerInOutMappings
from ...utils import logger, raise_error
from ..base import BaseMarker
from ..parcel_aggregation import ParcelAggregation
@ -49,9 +50,9 @@ class CrossParcellationFC(BaseMarker):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn"}
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"BOLD": {
"functional_connectivity": "matrix",
},

View file

@ -5,11 +5,12 @@
from abc import abstractmethod
from typing import Any, ClassVar, Dict, List, Optional, Set, Union
from typing import Any, ClassVar, Dict, List, Optional, Union
from sklearn.covariance import EmpiricalCovariance, LedoitWolf
from ...external.nilearn import JuniferConnectivityMeasure
from ...typing import Dependencies, MarkerInOutMappings
from ...utils import raise_error
from ..base import BaseMarker
@ -51,9 +52,9 @@ class FunctionalConnectivityBase(BaseMarker):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn", "scikit-learn"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn", "scikit-learn"}
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"BOLD": {
"functional_connectivity": "matrix",
},

View file

@ -4,7 +4,7 @@
# Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL
from typing import Any, ClassVar, Dict, List, Optional, Set, Union
from typing import Any, ClassVar, Dict, List, Optional, Union
import numpy as np
from nilearn.image import math_img
@ -13,6 +13,7 @@ from nilearn.maskers import NiftiMasker
from ..api.decorators import register_marker
from ..data import get_data
from ..stats import get_aggfunc_by_name
from ..typing import Dependencies, MarkerInOutMappings
from ..utils import logger, raise_error, warn_with_log
from .base import BaseMarker
@ -61,9 +62,9 @@ class ParcelAggregation(BaseMarker):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn", "numpy"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn", "numpy"}
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"T1w": {
"aggregation": "vector",
},

View file

@ -8,22 +8,20 @@ from pathlib import Path
from typing import (
TYPE_CHECKING,
ClassVar,
Dict,
List,
Optional,
Tuple,
Union,
)
import nibabel as nib
from ...pipeline import WorkDirManager
from ...typing import ExternalDependencies
from ...utils import logger, run_ext_cmd
from ...utils.singleton import Singleton
if TYPE_CHECKING:
from nibabel import Nifti1Image
from nibabel.nifti1 import Nifti1Image
__all__ = ["AFNIReHo"]
@ -37,7 +35,7 @@ class AFNIReHo(metaclass=Singleton):
"""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{
"name": "afni",
"commands": ["3dReHo", "3dAFNItoNIFTI"],

View file

@ -9,7 +9,6 @@ from pathlib import Path
from typing import (
TYPE_CHECKING,
ClassVar,
Set,
Tuple,
)
@ -20,12 +19,13 @@ from nilearn import image as nimg
from nilearn import masking as nmask
from ...pipeline import WorkDirManager
from ...typing import Dependencies
from ...utils import logger, raise_error
from ...utils.singleton import Singleton
if TYPE_CHECKING:
from nibabel import Nifti1Image
from nibabel.nifti1 import Nifti1Image
__all__ = ["JuniferReHo"]
@ -38,7 +38,7 @@ class JuniferReHo(metaclass=Singleton):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"numpy", "nilearn", "scipy"}
_DEPENDENCIES: ClassVar[Dependencies] = {"numpy", "nilearn", "scipy"}
def __del__(self) -> None:
"""Terminate the class."""

View file

@ -10,13 +10,11 @@ from typing import (
Any,
ClassVar,
Dict,
List,
Optional,
Tuple,
Type,
Union,
)
from ...typing import ConditionalDependencies, MarkerInOutMappings
from ...utils import logger, raise_error
from ..base import BaseMarker
from ._afni_reho import AFNIReHo
@ -24,7 +22,7 @@ from ._junifer_reho import JuniferReHo
if TYPE_CHECKING:
from nibabel import Nifti1Image
from nibabel.nifti1 import Nifti1Image
__all__ = ["ReHoBase"]
@ -51,7 +49,7 @@ class ReHoBase(BaseMarker):
"""
_CONDITIONAL_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, Type]]]] = [
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
{
"using": "afni",
"depends_on": AFNIReHo,
@ -62,7 +60,7 @@ class ReHoBase(BaseMarker):
},
]
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"BOLD": {
"reho": "vector",
},

View file

@ -4,12 +4,13 @@
# Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL
from typing import Any, ClassVar, Dict, List, Optional, Set, Union
from typing import Any, ClassVar, Dict, List, Optional, Union
from ..api.decorators import register_marker
from ..data import get_data
from ..external.nilearn import JuniferNiftiSpheresMasker
from ..stats import get_aggfunc_by_name
from ..typing import Dependencies, MarkerInOutMappings
from ..utils import logger, raise_error, warn_with_log
from .base import BaseMarker
@ -66,9 +67,9 @@ class SphereAggregation(BaseMarker):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn", "numpy"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn", "numpy"}
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"T1w": {
"aggregation": "vector",
},

View file

@ -5,10 +5,11 @@
from abc import abstractmethod
from typing import Any, ClassVar, Dict, List, Optional, Set, Union
from typing import Any, ClassVar, Dict, List, Optional, Union
from nilearn import image as nimg
from ...typing import Dependencies, MarkerInOutMappings
from ...utils import raise_error
from ..base import BaseMarker
@ -37,9 +38,9 @@ class TemporalSNRBase(BaseMarker):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn"}
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
_MARKER_INOUT_MAPPINGS: ClassVar[MarkerInOutMappings] = {
"BOLD": {
"tsnr": "vector",
},

View file

@ -3,23 +3,20 @@
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL
from typing import TYPE_CHECKING, Dict, Optional, Type
from typing import Dict, Optional
import numpy as np
import pandas as pd
from ..typing import StorageLike
from ..utils import raise_error
if TYPE_CHECKING:
from junifer.storage import BaseFeatureStorage
__all__ = ["normalize", "reweight"]
def normalize(
storage: Type["BaseFeatureStorage"],
storage: StorageLike,
features: Dict[str, Dict[str, Optional[str]]],
kind: str,
) -> pd.DataFrame:
@ -89,7 +86,7 @@ def normalize(
def reweight(
storage: Type["BaseFeatureStorage"],
storage: StorageLike,
feature_name: Optional[str] = None,
feature_md5: Optional[str] = None,
) -> pd.DataFrame:

View file

@ -4,22 +4,19 @@
# License: AGPL
from typing import TYPE_CHECKING, Dict, Optional, Tuple, Type
from typing import Dict, Optional, Tuple
import pandas as pd
from ..typing import StorageLike
from ..utils import logger, raise_error, warn_with_log
if TYPE_CHECKING:
from junifer.storage import BaseFeatureStorage
__all__ = ["read_transform"]
def read_transform(
storage: Type["BaseFeatureStorage"],
storage: StorageLike,
transform: str,
feature_name: Optional[str] = None,
feature_md5: Optional[str] = None,

View file

@ -5,20 +5,14 @@
# License: AGPL
from collections import Counter
from typing import TYPE_CHECKING, Dict, List, Optional
from typing import Dict, List, Optional
from ..datareader import DefaultDataReader
from ..pipeline import PipelineStepMixin, WorkDirManager
from ..typing import DataGrabberLike, MarkerLike, PreprocessorLike, StorageLike
from ..utils import logger, raise_error
if TYPE_CHECKING:
from junifer.datagrabber import BaseDataGrabber
from junifer.markers import BaseMarker
from junifer.preprocess import BasePreprocessor
from junifer.storage import BaseFeatureStorage
__all__ = ["MarkerCollection"]
@ -45,10 +39,10 @@ class MarkerCollection:
def __init__(
self,
markers: List["BaseMarker"],
markers: List[MarkerLike],
datareader: Optional[PipelineStepMixin] = None,
preprocessors: Optional[List["BasePreprocessor"]] = None,
storage: Optional["BaseFeatureStorage"] = None,
preprocessors: Optional[List[PreprocessorLike]] = None,
storage: Optional[StorageLike] = None,
):
# Check that the markers have different names
marker_names = [m.name for m in markers]
@ -111,7 +105,7 @@ class MarkerCollection:
return None if self._storage else out
def validate(self, datagrabber: "BaseDataGrabber") -> None:
def validate(self, datagrabber: DataGrabberLike) -> None:
"""Validate the pipeline.
Without doing any computation, check if the marker collection can

View file

@ -6,18 +6,13 @@
# License: AGPL
import importlib
from typing import TYPE_CHECKING, Dict, List, Mapping, Optional, Union
from typing import Dict, List, Mapping, Optional, Union
from ..typing import DataGrabberLike, MarkerLike, PreprocessorLike, StorageLike
from ..utils import logger, raise_error
from ..utils.singleton import Singleton
if TYPE_CHECKING:
from ..datagrabber import BaseDataGrabber
from ..storage import BaseFeatureStorage
from .pipeline_step_mixin import PipelineStepMixin
__all__ = ["PipelineComponentRegistry"]
@ -241,7 +236,7 @@ class PipelineComponentRegistry(metaclass=Singleton):
name: str,
baseclass: type,
init_params: Optional[Dict] = None,
) -> Union["BaseDataGrabber", "PipelineStepMixin", "BaseFeatureStorage"]:
) -> Union[DataGrabberLike, PreprocessorLike, MarkerLike, StorageLike]:
"""Build an instance of class registered as ``name``.
Parameters

View file

@ -5,12 +5,17 @@
# License: AGPL
import warnings
from typing import ClassVar, Dict, List, Set, Type, Union
from typing import ClassVar, Dict, List
import pytest
from junifer.pipeline.pipeline_step_mixin import PipelineStepMixin
from junifer.pipeline.utils import _check_afni
from junifer.typing import (
ConditionalDependencies,
Dependencies,
ExternalDependencies,
)
def test_PipelineStepMixin_correct_dependencies() -> None:
@ -19,7 +24,7 @@ def test_PipelineStepMixin_correct_dependencies() -> None:
class CorrectMixer(PipelineStepMixin):
"""Test class for validation."""
_DEPENDENCIES: ClassVar[Set[str]] = {"math"}
_DEPENDENCIES: ClassVar[Dependencies] = {"math"}
def validate_input(self, input: List[str]) -> List[str]:
return input
@ -40,7 +45,7 @@ def test_PipelineStepMixin_incorrect_dependencies() -> None:
class IncorrectMixer(PipelineStepMixin):
"""Test class for validation."""
_DEPENDENCIES: ClassVar[Set[str]] = {"foobar"}
_DEPENDENCIES: ClassVar[Dependencies] = {"foobar"}
def validate_input(self, input: List[str]) -> List[str]:
return input
@ -65,7 +70,7 @@ def test_PipelineStepMixin_correct_ext_dependencies() -> None:
class CorrectMixer(PipelineStepMixin):
"""Test class for validation."""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, str]]] = [{"name": "afni"}]
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [{"name": "afni"}]
def validate_input(self, input: List[str]) -> List[str]:
return input
@ -89,7 +94,7 @@ def test_PipelineStepMixin_ext_deps_correct_commands() -> None:
class CorrectMixer(PipelineStepMixin):
"""Test class for validation."""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{"name": "afni", "commands": ["3dReHo"]}
]
@ -117,7 +122,7 @@ def test_PipelineStepMixin_ext_deps_incorrect_commands() -> None:
class CorrectMixer(PipelineStepMixin):
"""Test class for validation."""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{"name": "afni", "commands": ["3d"]}
]
@ -141,7 +146,7 @@ def test_PipelineStepMixin_incorrect_ext_dependencies() -> None:
class IncorrectMixer(PipelineStepMixin):
"""Test class for validation."""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, bool]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{"name": "foobar", "optional": True}
]
@ -163,14 +168,12 @@ def test_PipelineStepMixin_correct_conditional_dependencies() -> None:
"""Test fit-transform with correct conditional dependencies."""
class Dependency:
_DEPENDENCIES: ClassVar[Set[str]] = {"math"}
_DEPENDENCIES: ClassVar[Dependencies] = {"math"}
class CorrectMixer(PipelineStepMixin):
"""Test class for validation."""
_CONDITIONAL_DEPENDENCIES: ClassVar[
List[Dict[str, Union[str, Type]]]
] = [
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
{
"using": "math",
"depends_on": Dependency,
@ -196,14 +199,12 @@ def test_PipelineStepMixin_incorrect_conditional_dependencies() -> None:
"""Test fit-transform with incorrect conditional dependencies."""
class Dependency:
_DEPENDENCIES: ClassVar[Set[str]] = {"math"}
_DEPENDENCIES: ClassVar[Dependencies] = {"math"}
class IncorrectMixer(PipelineStepMixin):
"""Test class for validation."""
_CONDITIONAL_DEPENDENCIES: ClassVar[
List[Dict[str, Union[str, Type]]]
] = [
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
{
"using": "math",
"depends_on": Dependency,
@ -231,14 +232,12 @@ def test_PipelineStepMixin_correct_conditional_ext_dependencies() -> None:
"""Test fit-transform with correct conditional external dependencies."""
class ExternalDependency:
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, str]]] = [{"name": "afni"}]
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [{"name": "afni"}]
class CorrectMixer(PipelineStepMixin):
"""Test class for validation."""
_CONDITIONAL_DEPENDENCIES: ClassVar[
List[Dict[str, Union[str, Type]]]
] = [
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
{
"using": "afni",
"depends_on": ExternalDependency,

View file

@ -103,6 +103,7 @@ def test_workdir_manager_get_and_delete_tempdir(tmp_path: Path) -> None:
# Should remove temporary directory
assert workdir_mgr.root_tempdir is None
def test_workdir_manager_no_cleanup(tmp_path: Path) -> None:
"""Test WorkDirManager correctly bypasses cleanup.

View file

@ -11,7 +11,6 @@ from typing import (
Dict,
List,
Optional,
Set,
Tuple,
Union,
)
@ -23,6 +22,7 @@ from nilearn._utils.niimg_conversions import check_niimg_4d
from ...api.decorators import register_preprocessor
from ...data import get_data
from ...typing import Dependencies
from ...utils import logger, raise_error
from ..base import BasePreprocessor
@ -152,7 +152,7 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"numpy", "nilearn"}
_DEPENDENCIES: ClassVar[Dependencies] = {"numpy", "nilearn"}
def __init__(
self,

View file

@ -6,20 +6,18 @@
from typing import (
TYPE_CHECKING,
ClassVar,
Dict,
List,
Set,
Union,
)
import nibabel as nib
from ...pipeline import WorkDirManager
from ...typing import Dependencies, ExternalDependencies
from ...utils import logger, run_ext_cmd
if TYPE_CHECKING:
from nibabel import Nifti1Image
from nibabel.nifti1 import Nifti1Image
__all__ = ["AFNISmoothing"]
@ -32,14 +30,14 @@ class AFNISmoothing:
"""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{
"name": "afni",
"commands": ["3dBlurToFWHM"],
},
]
_DEPENDENCIES: ClassVar[Set[str]] = {"nibabel"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nibabel"}
def preprocess(
self,

View file

@ -6,20 +6,17 @@
from typing import (
TYPE_CHECKING,
ClassVar,
Dict,
List,
Set,
Union,
)
import nibabel as nib
from ...pipeline import WorkDirManager
from ...typing import Dependencies, ExternalDependencies
from ...utils import logger, run_ext_cmd
if TYPE_CHECKING:
from nibabel import Nifti1Image
from nibabel.nifti1 import Nifti1Image
__all__ = ["FSLSmoothing"]
@ -32,14 +29,14 @@ class FSLSmoothing:
"""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{
"name": "fsl",
"commands": ["susan"],
},
]
_DEPENDENCIES: ClassVar[Set[str]] = {"nibabel"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nibabel"}
def preprocess(
self,

View file

@ -7,18 +7,18 @@ from typing import (
TYPE_CHECKING,
ClassVar,
Literal,
Set,
Union,
)
from nilearn import image as nimg
from numpy.typing import ArrayLike
from ...typing import Dependencies
from ...utils import logger
if TYPE_CHECKING:
from nibabel import Nifti1Image
from nibabel.nifti1 import Nifti1Image
__all__ = ["NilearnSmoothing"]
@ -31,7 +31,7 @@ class NilearnSmoothing:
"""
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn"}
_DEPENDENCIES: ClassVar[Dependencies] = {"nilearn"}
def preprocess(
self,

View file

@ -3,9 +3,10 @@
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL
from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type, Union
from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union
from ...api.decorators import register_preprocessor
from ...typing import ConditionalDependencies
from ...utils import logger, raise_error
from ..base import BasePreprocessor
from ._afni_smoothing import AFNISmoothing
@ -67,7 +68,7 @@ class Smoothing(BasePreprocessor):
"""
_CONDITIONAL_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, Type]]]] = [
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
{
"using": "nilearn",
"depends_on": NilearnSmoothing,

View file

@ -7,9 +7,6 @@ from typing import (
Any,
ClassVar,
Dict,
List,
Set,
Union,
)
import nibabel as nib
@ -17,6 +14,7 @@ import numpy as np
from ...data import get_template, get_xfm
from ...pipeline import WorkDirManager
from ...typing import Dependencies, ExternalDependencies
from ...utils import logger, run_ext_cmd
@ -31,14 +29,14 @@ class ANTsWarper:
"""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{
"name": "ants",
"commands": ["ResampleImage", "antsApplyTransforms"],
},
]
_DEPENDENCIES: ClassVar[Set[str]] = {"numpy", "nibabel"}
_DEPENDENCIES: ClassVar[Dependencies] = {"numpy", "nibabel"}
def preprocess(
self,

View file

@ -7,15 +7,13 @@ from typing import (
Any,
ClassVar,
Dict,
List,
Set,
Union,
)
import nibabel as nib
import numpy as np
from ...pipeline import WorkDirManager
from ...typing import Dependencies, ExternalDependencies
from ...utils import logger, run_ext_cmd
@ -30,14 +28,14 @@ class FSLWarper:
"""
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
{
"name": "fsl",
"commands": ["flirt", "applywarp"],
},
]
_DEPENDENCIES: ClassVar[Set[str]] = {"numpy", "nibabel"}
_DEPENDENCIES: ClassVar[Dependencies] = {"numpy", "nibabel"}
def preprocess(
self,

View file

@ -3,11 +3,12 @@
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL
from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type, Union
from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union
from templateflow import api as tflow
from ...api.decorators import register_preprocessor
from ...typing import ConditionalDependencies
from ...utils import logger, raise_error
from ..base import BasePreprocessor
from ._ants_warper import ANTsWarper
@ -46,7 +47,7 @@ class SpaceWarper(BasePreprocessor):
"""
_CONDITIONAL_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, Type]]]] = [
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
{
"using": "fsl",
"depends_on": FSLWarper,

View file

@ -4,7 +4,7 @@
# License: AGPL
import socket
from typing import TYPE_CHECKING, Tuple, Type
from typing import Tuple, Type
import pytest
from numpy.testing import assert_array_equal, assert_raises
@ -14,10 +14,7 @@ from junifer.datareader import DefaultDataReader
from junifer.pipeline.utils import _check_ants, _check_fsl
from junifer.preprocess import SpaceWarper
from junifer.testing.datagrabbers import PartlyCloudyTestingDataGrabber
if TYPE_CHECKING:
from junifer.datagrabber import BaseDataGrabber
from junifer.typing import DataGrabberLike
@pytest.mark.parametrize(
@ -99,7 +96,7 @@ def test_SpaceWarper_errors(
reason="only for juseless",
)
def test_SpaceWarper_native(
datagrabber: "BaseDataGrabber", element: Tuple[str, ...], using: str
datagrabber: DataGrabberLike, element: Tuple[str, ...], using: str
) -> None:
"""Test SpaceWarper for native space warping.
@ -162,7 +159,7 @@ def test_SpaceWarper_native(
_check_ants() is False, reason="requires ANTs to be in PATH"
)
def test_SpaceWarper_multi_mni(
datagrabber: "BaseDataGrabber",
datagrabber: DataGrabberLike,
element: Tuple[str, ...],
space: str,
) -> None:

View file

@ -0,0 +1,9 @@
"""Type hints for internal and external use."""
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL
import lazy_loader as lazy
__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)

View file

@ -0,0 +1,23 @@
__all__ = [
"DataGrabberLike",
"PreprocessorLike",
"MarkerLike",
"StorageLike",
"PipelineComponent",
"Dependencies",
"ConditionalDependencies",
"ExternalDependencies",
"MarkerInOutMappings",
]
from ._typing import (
DataGrabberLike,
PreprocessorLike,
MarkerLike,
StorageLike,
PipelineComponent,
Dependencies,
ConditionalDependencies,
ExternalDependencies,
MarkerInOutMappings,
)

61
junifer/typing/_typing.py Normal file
View file

@ -0,0 +1,61 @@
"""Provide type hints for internal and external use."""
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL
from typing import (
TYPE_CHECKING,
AbstractSet,
MutableMapping,
Sequence,
Type,
Union,
)
if TYPE_CHECKING:
from ..datagrabber import BaseDataGrabber
from ..datareader import DefaultDataReader
from ..markers import BaseMarker
from ..preprocess import BasePreprocessor
from ..storage import BaseFeatureStorage
__all__ = [
"DataGrabberLike",
"PreprocessorLike",
"MarkerLike",
"StorageLike",
"PipelineComponent",
"Dependencies",
"ConditionalDependencies",
"ExternalDependencies",
"MarkerInOutMappings",
]
DataGrabberLike = Type["BaseDataGrabber"]
PreprocessorLike = Type["BasePreprocessor"]
MarkerLike = Type["BaseMarker"]
StorageLike = Type["BaseFeatureStorage"]
PipelineComponent = Union[
"DataGrabberLike",
"DefaultDataReader",
"PreprocessorLike",
"MarkerLike",
"StorageLike",
]
Dependencies = AbstractSet[str]
ConditionalDependencies = Sequence[
MutableMapping[
str,
Union[
str,
PipelineComponent,
Sequence[str],
Sequence[PipelineComponent],
],
]
]
ExternalDependencies = Sequence[MutableMapping[str, Union[str, Sequence[str]]]]
MarkerInOutMappings = MutableMapping[str, MutableMapping[str, str]]