From 4080b6914c3f01e6f364ed1ace048f6a8f777cce Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Tue, 23 Feb 2021 15:27:24 +0000 Subject: [PATCH 1/4] move transforms out of compose file Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/apps/deepgrow/transforms.py | 2 +- monai/transforms/__init__.py | 3 +- monai/transforms/compose.py | 190 +-------------------- monai/transforms/croppad/array.py | 2 +- monai/transforms/croppad/dictionary.py | 2 +- monai/transforms/intensity/array.py | 2 +- monai/transforms/intensity/dictionary.py | 2 +- monai/transforms/io/array.py | 2 +- monai/transforms/io/dictionary.py | 2 +- monai/transforms/post/array.py | 2 +- monai/transforms/post/dictionary.py | 2 +- monai/transforms/spatial/array.py | 2 +- monai/transforms/spatial/dictionary.py | 2 +- monai/transforms/transform.py | 206 +++++++++++++++++++++++ monai/transforms/utility/array.py | 2 +- monai/transforms/utility/dictionary.py | 2 +- 16 files changed, 224 insertions(+), 201 deletions(-) create mode 100644 monai/transforms/transform.py diff --git a/monai/apps/deepgrow/transforms.py b/monai/apps/deepgrow/transforms.py index f178360031..80b0d1648d 100644 --- a/monai/apps/deepgrow/transforms.py +++ b/monai/apps/deepgrow/transforms.py @@ -17,7 +17,7 @@ from monai.config import IndexSelection, KeysCollection from monai.networks.layers import GaussianFilter from monai.transforms import SpatialCrop -from monai.transforms.compose import MapTransform, Randomizable, Transform +from monai.transforms.transform import MapTransform, Randomizable, Transform from monai.transforms.utils import generate_spatial_bounding_box from monai.utils import min_version, optional_import diff --git a/monai/transforms/__init__.py b/monai/transforms/__init__.py index 6f7c2a4f61..357e00c6dd 100644 --- a/monai/transforms/__init__.py +++ b/monai/transforms/__init__.py @@ -10,7 +10,7 @@ # limitations under the License. from .adaptors import FunctionSignature, adaptor, apply_alias, to_kwargs -from .compose import Compose, MapTransform, Randomizable, Transform +from .compose import Compose from .croppad.array import ( BorderPad, BoundingRect, @@ -234,6 +234,7 @@ ZoomD, ZoomDict, ) +from .transform import MapTransform, Randomizable, Transform from .utility.array import ( AddChannel, AddExtremePointsChannel, diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 3e23377b36..ae06e81d21 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -13,135 +13,15 @@ """ import warnings -from abc import ABC, abstractmethod -from typing import Any, Callable, Hashable, Optional, Sequence, Tuple, Union +from typing import Any, Callable, Optional, Sequence, Union import numpy as np -from monai.config import KeysCollection +from monai.transforms.transform import Randomizable, Transform from monai.transforms.utils import apply_transform from monai.utils import MAX_SEED, ensure_tuple, get_seed -__all__ = ["Transform", "Randomizable", "Compose", "MapTransform"] - - -class Transform(ABC): - """ - An abstract class of a ``Transform``. - A transform is callable that processes ``data``. - - It could be stateful and may modify ``data`` in place, - the implementation should be aware of: - - #. thread safety when mutating its own states. - When used from a multi-process context, transform's instance variables are read-only. - #. ``data`` content unused by this transform may still be used in the - subsequent transforms in a composed transform. - #. storing too much information in ``data`` may not scale. - - See Also - - :py:class:`monai.transforms.Compose` - """ - - @abstractmethod - def __call__(self, data: Any): - """ - ``data`` is an element which often comes from an iteration over an - iterable, such as :py:class:`torch.utils.data.Dataset`. This method should - return an updated version of ``data``. - To simplify the input validations, most of the transforms assume that - - - ``data`` is a Numpy ndarray, PyTorch Tensor or string - - the data shape can be: - - #. string data without shape, `LoadImage` transform expects file paths - #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``, - except that `AddChannel` expects (spatial_dim_1[, spatial_dim_2, ...]) and - `AsChannelFirst` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels) - #. most of the post-processing transforms expect - ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])`` - - - the channel dimension is not omitted even if number of channels is one - - This method can optionally take additional arguments to help execute transformation operation. - - Raises: - NotImplementedError: When the subclass does not override this method. - - """ - raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") - - -class Randomizable(ABC): - """ - An interface for handling random state locally, currently based on a class variable `R`, - which is an instance of `np.random.RandomState`. - This is mainly for randomized data augmentation transforms. For example:: - - class RandShiftIntensity(Randomizable): - def randomize(): - self._offset = self.R.uniform(low=0, high=100) - def __call__(self, img): - self.randomize() - return img + self._offset - - transform = RandShiftIntensity() - transform.set_random_state(seed=0) - - """ - - R: np.random.RandomState = np.random.RandomState() - - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "Randomizable": - """ - Set the random state locally, to control the randomness, the derived - classes should use :py:attr:`self.R` instead of `np.random` to introduce random - factors. - - Args: - seed: set the random state with an integer seed. - state: set the random state with a `np.random.RandomState` object. - - Raises: - TypeError: When ``state`` is not an ``Optional[np.random.RandomState]``. - - Returns: - a Randomizable instance. - - """ - if seed is not None: - _seed = id(seed) if not isinstance(seed, (int, np.integer)) else seed - _seed = _seed % MAX_SEED - self.R = np.random.RandomState(_seed) - return self - - if state is not None: - if not isinstance(state, np.random.RandomState): - raise TypeError(f"state must be None or a np.random.RandomState but is {type(state).__name__}.") - self.R = state - return self - - self.R = np.random.RandomState() - return self - - @abstractmethod - def randomize(self, data: Any) -> None: - """ - Within this method, :py:attr:`self.R` should be used, instead of `np.random`, to introduce random factors. - - all :py:attr:`self.R` calls happen here so that we have a better chance to - identify errors of sync the random state. - - This method can generate the random factors based on properties of the input data. - - Raises: - NotImplementedError: When the subclass does not override this method. - - """ - raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") +__all__ = ["Compose"] class Compose(Randomizable, Transform): @@ -235,67 +115,3 @@ def __call__(self, input_): for _transform in self.transforms: input_ = apply_transform(_transform, input_) return input_ - - -class MapTransform(Transform): - """ - A subclass of :py:class:`monai.transforms.Transform` with an assumption - that the ``data`` input of ``self.__call__`` is a MutableMapping such as ``dict``. - - The ``keys`` parameter will be used to get and set the actual data - item to transform. That is, the callable of this transform should - follow the pattern: - - .. code-block:: python - - def __call__(self, data): - for key in self.keys: - if key in data: - # update output data with some_transform_function(data[key]). - else: - # do nothing or some exceptions handling. - return data - - Raises: - ValueError: When ``keys`` is an empty iterable. - TypeError: When ``keys`` type is not in ``Union[Hashable, Iterable[Hashable]]``. - - """ - - def __init__(self, keys: KeysCollection) -> None: - self.keys: Tuple[Hashable, ...] = ensure_tuple(keys) - if not self.keys: - raise ValueError("keys must be non empty.") - for key in self.keys: - if not isinstance(key, Hashable): - raise TypeError(f"keys must be one of (Hashable, Iterable[Hashable]) but is {type(keys).__name__}.") - - @abstractmethod - def __call__(self, data): - """ - ``data`` often comes from an iteration over an iterable, - such as :py:class:`torch.utils.data.Dataset`. - - To simplify the input validations, this method assumes: - - - ``data`` is a Python dictionary - - ``data[key]`` is a Numpy ndarray, PyTorch Tensor or string, where ``key`` is an element - of ``self.keys``, the data shape can be: - - #. string data without shape, `LoadImaged` transform expects file paths - #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``, - except that `AddChanneld` expects (spatial_dim_1[, spatial_dim_2, ...]) and - `AsChannelFirstd` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels) - #. most of the post-processing transforms expect - ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])`` - - - the channel dimension is not omitted even if number of channels is one - - Raises: - NotImplementedError: When the subclass does not override this method. - - returns: - An updated dictionary version of ``data`` by applying the transform. - - """ - raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index b4444803a4..ef5e0019bd 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -20,7 +20,7 @@ from monai.config import IndexSelection from monai.data.utils import get_random_patch, get_valid_patch_size -from monai.transforms.compose import Randomizable, Transform +from monai.transforms.transform import Randomizable, Transform from monai.transforms.utils import ( generate_pos_neg_label_crop_centers, generate_spatial_bounding_box, diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index 1faed25605..20ae6ac1ed 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -21,7 +21,6 @@ from monai.config import IndexSelection, KeysCollection from monai.data.utils import get_random_patch, get_valid_patch_size -from monai.transforms.compose import MapTransform, Randomizable from monai.transforms.croppad.array import ( BorderPad, BoundingRect, @@ -31,6 +30,7 @@ SpatialCrop, SpatialPad, ) +from monai.transforms.transform import MapTransform, Randomizable from monai.transforms.utils import ( generate_pos_neg_label_crop_centers, generate_spatial_bounding_box, diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index 87091f6237..40bef064eb 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -22,7 +22,7 @@ from monai.config import DtypeLike from monai.networks.layers import GaussianFilter, HilbertTransform, SavitzkyGolayFilter -from monai.transforms.compose import Randomizable, Transform +from monai.transforms.transform import Randomizable, Transform from monai.transforms.utils import rescale_array from monai.utils import PT_BEFORE_1_7, InvalidPyTorchVersionError, dtype_torch_to_numpy, ensure_tuple_size diff --git a/monai/transforms/intensity/dictionary.py b/monai/transforms/intensity/dictionary.py index 1c9b31c120..54a85a57b0 100644 --- a/monai/transforms/intensity/dictionary.py +++ b/monai/transforms/intensity/dictionary.py @@ -22,7 +22,6 @@ import torch from monai.config import DtypeLike, KeysCollection -from monai.transforms.compose import MapTransform, Randomizable from monai.transforms.intensity.array import ( AdjustContrast, GaussianSharpen, @@ -35,6 +34,7 @@ ShiftIntensity, ThresholdIntensity, ) +from monai.transforms.transform import MapTransform, Randomizable from monai.utils import dtype_torch_to_numpy, ensure_tuple_rep, ensure_tuple_size __all__ = [ diff --git a/monai/transforms/io/array.py b/monai/transforms/io/array.py index 9c14f7a689..855621e432 100644 --- a/monai/transforms/io/array.py +++ b/monai/transforms/io/array.py @@ -22,7 +22,7 @@ from monai.data.image_reader import ImageReader, ITKReader, NibabelReader, NumpyReader, PILReader from monai.data.nifti_saver import NiftiSaver from monai.data.png_saver import PNGSaver -from monai.transforms.compose import Transform +from monai.transforms.transform import Transform from monai.utils import GridSampleMode, GridSamplePadMode from monai.utils import ImageMetaKey as Key from monai.utils import InterpolateMode, ensure_tuple, optional_import diff --git a/monai/transforms/io/dictionary.py b/monai/transforms/io/dictionary.py index d3220aa682..55707f750e 100644 --- a/monai/transforms/io/dictionary.py +++ b/monai/transforms/io/dictionary.py @@ -21,8 +21,8 @@ from monai.config import DtypeLike, KeysCollection from monai.data.image_reader import ImageReader -from monai.transforms.compose import MapTransform from monai.transforms.io.array import LoadImage, SaveImage +from monai.transforms.transform import MapTransform from monai.utils import GridSampleMode, GridSamplePadMode, InterpolateMode __all__ = [ diff --git a/monai/transforms/post/array.py b/monai/transforms/post/array.py index 0c60b0cc89..8b4f71093b 100644 --- a/monai/transforms/post/array.py +++ b/monai/transforms/post/array.py @@ -21,7 +21,7 @@ import torch.nn.functional as F from monai.networks import one_hot -from monai.transforms.compose import Transform +from monai.transforms.transform import Transform from monai.transforms.utils import get_largest_connected_component_mask from monai.utils import ensure_tuple diff --git a/monai/transforms/post/dictionary.py b/monai/transforms/post/dictionary.py index 60cda11a91..aff4ae3572 100644 --- a/monai/transforms/post/dictionary.py +++ b/monai/transforms/post/dictionary.py @@ -21,7 +21,6 @@ import torch from monai.config import KeysCollection -from monai.transforms.compose import MapTransform from monai.transforms.post.array import ( Activations, AsDiscrete, @@ -30,6 +29,7 @@ MeanEnsemble, VoteEnsemble, ) +from monai.transforms.transform import MapTransform from monai.utils import ensure_tuple_rep __all__ = [ diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index df10480188..0610982847 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -22,8 +22,8 @@ from monai.config import USE_COMPILED, DtypeLike from monai.data.utils import compute_shape_offset, to_affine_nd, zoom_affine from monai.networks.layers import AffineTransform, GaussianFilter, grid_pull -from monai.transforms.compose import Randomizable, Transform from monai.transforms.croppad.array import CenterSpatialCrop +from monai.transforms.transform import Randomizable, Transform from monai.transforms.utils import ( create_control_grid, create_grid, diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index e612a25ef8..8b546e5e97 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -22,7 +22,6 @@ from monai.config import DtypeLike, KeysCollection from monai.networks.layers.simplelayers import GaussianFilter -from monai.transforms.compose import MapTransform, Randomizable from monai.transforms.croppad.array import CenterSpatialCrop from monai.transforms.spatial.array import ( Flip, @@ -36,6 +35,7 @@ Spacing, Zoom, ) +from monai.transforms.transform import MapTransform, Randomizable from monai.transforms.utils import create_grid from monai.utils import ( GridSampleMode, diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py new file mode 100644 index 0000000000..e5841cbe97 --- /dev/null +++ b/monai/transforms/transform.py @@ -0,0 +1,206 @@ +# Copyright 2020 - 2021 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +A collection of generic interfaces for MONAI transforms. +""" + +from abc import ABC, abstractmethod +from typing import Any, Hashable, Optional, Tuple + +import numpy as np + +from monai.config import KeysCollection +from monai.utils import MAX_SEED, ensure_tuple + +__all__ = ["Randomizable", "Transform", "MapTransform"] + + +class Randomizable(ABC): + """ + An interface for handling random state locally, currently based on a class variable `R`, + which is an instance of `np.random.RandomState`. + This is mainly for randomized data augmentation transforms. For example:: + + class RandShiftIntensity(Randomizable): + def randomize(): + self._offset = self.R.uniform(low=0, high=100) + def __call__(self, img): + self.randomize() + return img + self._offset + + transform = RandShiftIntensity() + transform.set_random_state(seed=0) + + """ + + R: np.random.RandomState = np.random.RandomState() + + def set_random_state( + self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None + ) -> "Randomizable": + """ + Set the random state locally, to control the randomness, the derived + classes should use :py:attr:`self.R` instead of `np.random` to introduce random + factors. + + Args: + seed: set the random state with an integer seed. + state: set the random state with a `np.random.RandomState` object. + + Raises: + TypeError: When ``state`` is not an ``Optional[np.random.RandomState]``. + + Returns: + a Randomizable instance. + + """ + if seed is not None: + _seed = id(seed) if not isinstance(seed, (int, np.integer)) else seed + _seed = _seed % MAX_SEED + self.R = np.random.RandomState(_seed) + return self + + if state is not None: + if not isinstance(state, np.random.RandomState): + raise TypeError(f"state must be None or a np.random.RandomState but is {type(state).__name__}.") + self.R = state + return self + + self.R = np.random.RandomState() + return self + + @abstractmethod + def randomize(self, data: Any) -> None: + """ + Within this method, :py:attr:`self.R` should be used, instead of `np.random`, to introduce random factors. + + all :py:attr:`self.R` calls happen here so that we have a better chance to + identify errors of sync the random state. + + This method can generate the random factors based on properties of the input data. + + Raises: + NotImplementedError: When the subclass does not override this method. + + """ + raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") + + +class Transform(ABC): + """ + An abstract class of a ``Transform``. + A transform is callable that processes ``data``. + + It could be stateful and may modify ``data`` in place, + the implementation should be aware of: + + #. thread safety when mutating its own states. + When used from a multi-process context, transform's instance variables are read-only. + #. ``data`` content unused by this transform may still be used in the + subsequent transforms in a composed transform. + #. storing too much information in ``data`` may not scale. + + See Also + + :py:class:`monai.transforms.Compose` + """ + + @abstractmethod + def __call__(self, data: Any): + """ + ``data`` is an element which often comes from an iteration over an + iterable, such as :py:class:`torch.utils.data.Dataset`. This method should + return an updated version of ``data``. + To simplify the input validations, most of the transforms assume that + + - ``data`` is a Numpy ndarray, PyTorch Tensor or string + - the data shape can be: + + #. string data without shape, `LoadImage` transform expects file paths + #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``, + except that `AddChannel` expects (spatial_dim_1[, spatial_dim_2, ...]) and + `AsChannelFirst` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels) + #. most of the post-processing transforms expect + ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])`` + + - the channel dimension is not omitted even if number of channels is one + + This method can optionally take additional arguments to help execute transformation operation. + + Raises: + NotImplementedError: When the subclass does not override this method. + + """ + raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") + + +class MapTransform(Transform): + """ + A subclass of :py:class:`monai.transforms.Transform` with an assumption + that the ``data`` input of ``self.__call__`` is a MutableMapping such as ``dict``. + + The ``keys`` parameter will be used to get and set the actual data + item to transform. That is, the callable of this transform should + follow the pattern: + + .. code-block:: python + + def __call__(self, data): + for key in self.keys: + if key in data: + # update output data with some_transform_function(data[key]). + else: + # do nothing or some exceptions handling. + return data + + Raises: + ValueError: When ``keys`` is an empty iterable. + TypeError: When ``keys`` type is not in ``Union[Hashable, Iterable[Hashable]]``. + + """ + + def __init__(self, keys: KeysCollection) -> None: + self.keys: Tuple[Hashable, ...] = ensure_tuple(keys) + if not self.keys: + raise ValueError("keys must be non empty.") + for key in self.keys: + if not isinstance(key, Hashable): + raise TypeError(f"keys must be one of (Hashable, Iterable[Hashable]) but is {type(keys).__name__}.") + + @abstractmethod + def __call__(self, data): + """ + ``data`` often comes from an iteration over an iterable, + such as :py:class:`torch.utils.data.Dataset`. + + To simplify the input validations, this method assumes: + + - ``data`` is a Python dictionary + - ``data[key]`` is a Numpy ndarray, PyTorch Tensor or string, where ``key`` is an element + of ``self.keys``, the data shape can be: + + #. string data without shape, `LoadImaged` transform expects file paths + #. most of the pre-processing transforms expect: ``(num_channels, spatial_dim_1[, spatial_dim_2, ...])``, + except that `AddChanneld` expects (spatial_dim_1[, spatial_dim_2, ...]) and + `AsChannelFirstd` expects (spatial_dim_1[, spatial_dim_2, ...], num_channels) + #. most of the post-processing transforms expect + ``(batch_size, num_channels, spatial_dim_1[, spatial_dim_2, ...])`` + + - the channel dimension is not omitted even if number of channels is one + + Raises: + NotImplementedError: When the subclass does not override this method. + + returns: + An updated dictionary version of ``data`` by applying the transform. + + """ + raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index c0ae40de59..8b161a9223 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -21,7 +21,7 @@ import torch from monai.config import DtypeLike, NdarrayTensor -from monai.transforms.compose import Randomizable, Transform +from monai.transforms.transform import Randomizable, Transform from monai.transforms.utils import extreme_points_to_image, get_extreme_points, map_binary_to_indices from monai.utils import ensure_tuple, min_version, optional_import diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index f374b82d76..c4bd7d4cba 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -23,7 +23,7 @@ import torch from monai.config import DtypeLike, KeysCollection, NdarrayTensor -from monai.transforms.compose import MapTransform, Randomizable +from monai.transforms.transform import MapTransform, Randomizable from monai.transforms.utility.array import ( AddChannel, AsChannelFirst, From 201ee51e886373a1015c79ee016380ec1fa5b110 Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Wed, 24 Feb 2021 16:49:02 +0000 Subject: [PATCH 2/4] backwards compatibility Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/compose.py | 2 ++ tests/test_compose.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index d71618d88f..36b187fbf1 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -18,6 +18,8 @@ import numpy as np from monai.transforms.transform import Randomizable, Transform +# For backwards compatiblity (so this still works: from monai.transforms.compose import MapTransform) +from monai.transforms.transform import MapTransform # noqa: F401 from monai.transforms.utils import apply_transform from monai.utils import MAX_SEED, ensure_tuple, get_seed diff --git a/tests/test_compose.py b/tests/test_compose.py index c049044a97..8d51614e2c 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -167,6 +167,9 @@ def test_flatten_and_len(self): # test len self.assertEqual(len(t1), 8) + def test_backwards_compatbile_imports(self): + from monai.transforms.compose import MapTransform, Transform, Randomizable # noqa: F401 + if __name__ == "__main__": unittest.main() From 6f0164b316198100ac5cc654fc65da9993b77a95 Mon Sep 17 00:00:00 2001 From: Richard Brown <33289025+rijobro@users.noreply.github.com> Date: Wed, 24 Feb 2021 16:53:08 +0000 Subject: [PATCH 3/4] typo Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- tests/test_compose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_compose.py b/tests/test_compose.py index 8d51614e2c..46c93b027d 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -167,7 +167,7 @@ def test_flatten_and_len(self): # test len self.assertEqual(len(t1), 8) - def test_backwards_compatbile_imports(self): + def test_backwards_compatible_imports(self): from monai.transforms.compose import MapTransform, Transform, Randomizable # noqa: F401 From 0684752a65332c43bde7ba327cc3b95fccb91b0b Mon Sep 17 00:00:00 2001 From: Rich <33289025+rijobro@users.noreply.github.com> Date: Wed, 24 Feb 2021 17:01:40 +0000 Subject: [PATCH 4/4] code format Signed-off-by: Richard Brown <33289025+rijobro@users.noreply.github.com> --- monai/transforms/compose.py | 2 +- tests/test_compose.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 36b187fbf1..2d612ad2e3 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -17,9 +17,9 @@ import numpy as np -from monai.transforms.transform import Randomizable, Transform # For backwards compatiblity (so this still works: from monai.transforms.compose import MapTransform) from monai.transforms.transform import MapTransform # noqa: F401 +from monai.transforms.transform import Randomizable, Transform from monai.transforms.utils import apply_transform from monai.utils import MAX_SEED, ensure_tuple, get_seed diff --git a/tests/test_compose.py b/tests/test_compose.py index 46c93b027d..3a0a6ea5bb 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -168,7 +168,7 @@ def test_flatten_and_len(self): self.assertEqual(len(t1), 8) def test_backwards_compatible_imports(self): - from monai.transforms.compose import MapTransform, Transform, Randomizable # noqa: F401 + from monai.transforms.compose import MapTransform, Randomizable, Transform # noqa: F401 if __name__ == "__main__":