Skip to content

Commit d0f5460

Browse files
Fix is_interactive_compatible logic after AcceleratorConnector rewrite (#12008)
* fix is_interactive_compatible * improve tests * update message * address review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 4c4b9d5 commit d0f5460

File tree

7 files changed

+42
-17
lines changed

7 files changed

+42
-17
lines changed

pytorch_lightning/strategies/launchers/base.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ class _Launcher(ABC):
2626
cluster environment, hardware, strategy, etc.
2727
"""
2828

29+
@property
30+
@abstractmethod
31+
def is_interactive_compatible(self) -> bool:
32+
"""Returns whether this launcher can work in interactive environments such as Jupyter notebooks."""
33+
2934
@abstractmethod
3035
def launch(self, function: Callable, *args: Any, **kwargs: Any) -> Any:
3136
"""Launches the processes."""

pytorch_lightning/strategies/launchers/spawn.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ def __init__(self, strategy: Strategy) -> None:
4949
self._strategy = strategy
5050
self._start_method = "spawn"
5151

52+
@property
53+
def is_interactive_compatible(self) -> bool:
54+
# The start method 'spawn' is currently the only one that works with DDP and CUDA support
55+
# The start method 'fork' is the only one supported in Jupyter environments but not compatible with CUDA
56+
# For more context, see https://github.com/PyTorchLightning/pytorch-lightning/issues/7550
57+
return self._start_method == "fork" and self._strategy.root_device.type != "cuda"
58+
5259
def launch(self, function: Callable, *args: Any, trainer: Optional["pl.Trainer"] = None, **kwargs: Any) -> Any:
5360
"""Spawns processes that run the given function in parallel.
5461

pytorch_lightning/strategies/launchers/subprocess_script.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class _SubprocessScriptLauncher(_Launcher):
6868
num_nodes: The total number of nodes that participate in this process group.
6969
"""
7070

71+
@property
72+
def is_interactive_compatible(self) -> bool:
73+
return False
74+
7175
def __init__(self, cluster_environment: ClusterEnvironment, num_processes: int, num_nodes: int) -> None:
7276
super().__init__()
7377
self.cluster_environment = cluster_environment

pytorch_lightning/strategies/launchers/xla_spawn.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ def __init__(self, strategy: "Strategy") -> None:
5454
super().__init__(strategy)
5555
self._start_method = "fork"
5656

57+
@property
58+
def is_interactive_compatible(self) -> bool:
59+
return True
60+
5761
def launch(self, function: Callable, *args: Any, trainer: Optional["pl.Trainer"] = None, **kwargs: Any) -> Any:
5862
"""Spawns processes that run the given function in parallel.
5963

pytorch_lightning/trainer/connectors/accelerator_connector.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
TPUSpawnStrategy,
6666
)
6767
from pytorch_lightning.utilities import (
68+
_StrategyType,
6869
AMPType,
6970
device_parser,
7071
LightningEnum,
@@ -734,19 +735,12 @@ def _lazy_init_strategy(self) -> None:
734735

735736
from pytorch_lightning.utilities import _IS_INTERACTIVE
736737

737-
# TODO move is_compatible logic to strategy API
738-
interactive_compatible_strategy = (
739-
DataParallelStrategy.strategy_name,
740-
DDPSpawnStrategy.strategy_name,
741-
DDPSpawnShardedStrategy.strategy_name,
742-
TPUSpawnStrategy.strategy_name,
743-
)
744-
if _IS_INTERACTIVE and self.strategy.strategy_name not in interactive_compatible_strategy:
738+
if _IS_INTERACTIVE and self.strategy.launcher and not self.strategy.launcher.is_interactive_compatible:
745739
raise MisconfigurationException(
746740
f"`Trainer(strategy={self.strategy.strategy_name!r})` or"
747741
f" `Trainer(accelerator={self.strategy.strategy_name!r})` is not compatible with an interactive"
748-
" environment. Run your code as a script, or choose one of the compatible backends:"
749-
f" {', '.join(interactive_compatible_strategy)}."
742+
" environment. Run your code as a script, or choose one of the compatible strategies:"
743+
f" Trainer(strategy=None|{'|'.join(_StrategyType.interactive_compatible_types())})."
750744
" In case you are spawning processes yourself, make sure to include the Trainer"
751745
" creation inside the worker function."
752746
)

pytorch_lightning/utilities/enums.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,6 @@ def interactive_compatible_types() -> list[_StrategyType]:
254254
"""Returns a list containing interactive compatible _StrategyTypes."""
255255
return [
256256
_StrategyType.DP,
257-
_StrategyType.DDP_SPAWN,
258-
_StrategyType.DDP_SHARDED_SPAWN,
259257
_StrategyType.TPU_SPAWN,
260258
]
261259

tests/accelerators/test_accelerator_connector.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import torch
2121
import torch.distributed
2222

23+
import pytorch_lightning
2324
from pytorch_lightning import Trainer
2425
from pytorch_lightning.accelerators.accelerator import Accelerator
2526
from pytorch_lightning.accelerators.cpu import CPUAccelerator
@@ -392,19 +393,31 @@ def test_dist_backend_accelerator_mapping(*_):
392393
assert trainer.strategy.local_rank == 0
393394

394395

395-
@mock.patch("pytorch_lightning.utilities._IS_INTERACTIVE", return_value=True)
396396
@mock.patch("torch.cuda.device_count", return_value=2)
397-
def test_ipython_incompatible_backend_error(*_):
397+
def test_ipython_incompatible_backend_error(_, monkeypatch):
398+
monkeypatch.setattr(pytorch_lightning.utilities, "_IS_INTERACTIVE", True)
398399
with pytest.raises(MisconfigurationException, match=r"strategy='ddp'\)`.*is not compatible"):
399400
Trainer(strategy="ddp", gpus=2)
400401

401402
with pytest.raises(MisconfigurationException, match=r"strategy='ddp2'\)`.*is not compatible"):
402403
Trainer(strategy="ddp2", gpus=2)
403404

405+
with pytest.raises(MisconfigurationException, match=r"strategy='ddp_spawn'\)`.*is not compatible"):
406+
Trainer(strategy="ddp_spawn")
404407

405-
@mock.patch("pytorch_lightning.utilities._IS_INTERACTIVE", return_value=True)
406-
def test_ipython_compatible_backend(*_):
407-
Trainer(strategy="ddp_spawn", num_processes=2)
408+
with pytest.raises(MisconfigurationException, match=r"strategy='ddp_sharded_spawn'\)`.*is not compatible"):
409+
Trainer(strategy="ddp_sharded_spawn")
410+
411+
with pytest.raises(MisconfigurationException, match=r"strategy='ddp'\)`.*is not compatible"):
412+
# Edge case: AcceleratorConnector maps dp to ddp if accelerator != gpu
413+
Trainer(strategy="dp")
414+
415+
416+
@pytest.mark.parametrize("trainer_kwargs", [{}, dict(strategy="dp", accelerator="gpu"), dict(accelerator="tpu")])
417+
def test_ipython_compatible_backend(trainer_kwargs, monkeypatch):
418+
monkeypatch.setattr(pytorch_lightning.utilities, "_IS_INTERACTIVE", True)
419+
trainer = Trainer(**trainer_kwargs)
420+
assert trainer.strategy.launcher is None or trainer.strategy.launcher.is_interactive_compatible
408421

409422

410423
@pytest.mark.parametrize(["accelerator", "plugin"], [("ddp_spawn", "ddp_sharded"), (None, "ddp_sharded")])

0 commit comments

Comments
 (0)