From 642a9d2827218bd5465d553933f9d75afcc3ce31 Mon Sep 17 00:00:00 2001 From: FNU AKSHANSH <105249360+akshansh47@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:59:08 -0700 Subject: [PATCH 1/4] Warn when zero.Init silently falls back to a single rank (#8084) When a multi-process launcher sets WORLD_SIZE>1 but the distributed process group is not initialized before zero.Init runs (e.g. from_pretrained before deepspeed.init_distributed()), the resolved group collapses to a single rank. zero.Init then materializes every parameter whole on every rank instead of partitioning, so each rank loads the full model and OOMs with no diagnostic. Detect this case and emit an actionable warning pointing at the missing init_distributed() call. Co-authored-by: Cursor Signed-off-by: FNU AKSHANSH <105249360+akshansh47@users.noreply.github.com> Co-authored-by: Cursor --- .../runtime/zero/partition_parameters.py | 33 +++++++++++++++ .../zero/test_zero_init_unsharded_warning.py | 41 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/unit/runtime/zero/test_zero_init_unsharded_warning.py diff --git a/deepspeed/runtime/zero/partition_parameters.py b/deepspeed/runtime/zero/partition_parameters.py index 7b7c50454874..f3b07eb81eae 100755 --- a/deepspeed/runtime/zero/partition_parameters.py +++ b/deepspeed/runtime/zero/partition_parameters.py @@ -880,6 +880,35 @@ def _no_gather_coalesced(params: Iterable[Parameter]) -> AllGatherCoalescedHandl return NoGatherCoalescedHandle(params) +def _unsharded_single_rank_warning(dp_world_size, data_parallel_group, env=None): + """Detect the silent single-rank fallback described in #8084. + + When a multi-process launcher (``deepspeed``, ``torchrun``, accelerate, ...) sets ``WORLD_SIZE > 1`` but the + distributed process group was not initialized before ``zero.Init`` ran, the group resolved here collapses to a + single rank. ``zero.Init`` then creates every parameter whole on every rank instead of partitioning it, so each + rank allocates the full (unsharded) model and typically OOMs. The failure is otherwise silent and looks exactly + like a "model too big" OOM. Return an actionable warning message in that case, else ``None``. + + Only the default (world-group) path is checked: an explicitly supplied ``data_parallel_group`` of size 1 is + treated as intentional. + """ + if dp_world_size != 1 or data_parallel_group is not None: + return None + env = os.environ if env is None else env + try: + launcher_world_size = int(env.get("WORLD_SIZE", "0") or "0") + except (TypeError, ValueError): + return None + if launcher_world_size <= 1: + return None + return ( + "zero.Init resolved a process group of world_size=1, but the launcher environment reports " + f"WORLD_SIZE={launcher_world_size}. The distributed process group was likely not initialized before " + "zero.Init ran (for example, `from_pretrained` executed before `deepspeed.init_distributed()`). Parameters " + "will NOT be partitioned: every rank allocates the full model and will likely OOM. Call " + "`deepspeed.init_distributed()` before constructing the model under zero.Init.") + + # Replaces all parameters in module with Scattered Parameters class Init(InsertPostInitMethodToModuleSubClasses): param_id = 0 @@ -1035,6 +1064,10 @@ def __init__(self, self.rank = dist.get_rank(group=self.ds_process_group) self.dp_world_size = dist.get_world_size(group=self.ds_process_group) + _unsharded_warning = _unsharded_single_rank_warning(self.dp_world_size, data_parallel_group) + if _unsharded_warning is not None: + logger.warning(_unsharded_warning) + self.zero_param_process_group = zero_param_parallel_group if _ds_config is not None and _ds_config.zero_config.zero_hpz_partition_size > 1 and self.zero_param_process_group is None: groups._create_zero_param_parallel_group(_ds_config.zero_config.zero_hpz_partition_size) diff --git a/tests/unit/runtime/zero/test_zero_init_unsharded_warning.py b/tests/unit/runtime/zero/test_zero_init_unsharded_warning.py new file mode 100644 index 000000000000..11fc045b4345 --- /dev/null +++ b/tests/unit/runtime/zero/test_zero_init_unsharded_warning.py @@ -0,0 +1,41 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team + +# Regression coverage for #8084: zero.Init silently falls back to a single-rank (unsharded) group when the +# distributed process group is not initialized before it runs (e.g. `from_pretrained` before +# `deepspeed.init_distributed()`), so every rank allocates the full model and OOMs. The detection helper must warn +# only when the launcher reports a multi-process world but the resolved group collapsed to one rank. + +import pytest + +from deepspeed.runtime.zero.partition_parameters import _unsharded_single_rank_warning + + +def test_warns_when_launcher_multiprocess_but_group_is_single_rank(): + msg = _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=None, env={"WORLD_SIZE": "8"}) + assert msg is not None + assert "WORLD_SIZE=8" in msg + assert "init_distributed" in msg + + +def test_no_warning_for_genuine_single_process(): + assert _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=None, env={"WORLD_SIZE": "1"}) is None + assert _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=None, env={}) is None + + +def test_no_warning_when_group_actually_shards(): + assert _unsharded_single_rank_warning(dp_world_size=8, data_parallel_group=None, env={"WORLD_SIZE": "8"}) is None + + +def test_no_warning_when_explicit_dp_group_supplied(): + # An explicitly provided size-1 data_parallel_group is treated as intentional. + sentinel_group = object() + assert _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=sentinel_group, env={"WORLD_SIZE": + "8"}) is None + + +@pytest.mark.parametrize("bad", ["", "not-an-int", None]) +def test_malformed_world_size_does_not_raise(bad): + assert _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=None, env={"WORLD_SIZE": bad}) is None From 7abdeb98e2c95a1d2781f24236ff85cb990d3924 Mon Sep 17 00:00:00 2001 From: FNU AKSHANSH <105249360+akshansh47@users.noreply.github.com> Date: Thu, 16 Jul 2026 10:15:29 -0700 Subject: [PATCH 2/4] Escalate zero.Init single-rank PG contradiction from warning to error Per maintainer feedback on #8089: ZeRO-3 cannot work correctly with a pre-existing process group that contradicts the launcher world (WORLD_SIZE>1 but resolved group is single-rank), so zero.Init now raises RuntimeError instead of warning. Explicit data_parallel_group and genuine single-process runs remain untouched. Signed-off-by: FNU AKSHANSH <105249360+akshansh47@users.noreply.github.com> Co-authored-by: Cursor --- .../runtime/zero/partition_parameters.py | 27 ++++++----- .../zero/test_zero_init_pg_contradiction.py | 45 +++++++++++++++++++ .../zero/test_zero_init_unsharded_warning.py | 41 ----------------- 3 files changed, 60 insertions(+), 53 deletions(-) create mode 100644 tests/unit/runtime/zero/test_zero_init_pg_contradiction.py delete mode 100644 tests/unit/runtime/zero/test_zero_init_unsharded_warning.py diff --git a/deepspeed/runtime/zero/partition_parameters.py b/deepspeed/runtime/zero/partition_parameters.py index f3b07eb81eae..e8558b087d10 100755 --- a/deepspeed/runtime/zero/partition_parameters.py +++ b/deepspeed/runtime/zero/partition_parameters.py @@ -880,14 +880,16 @@ def _no_gather_coalesced(params: Iterable[Parameter]) -> AllGatherCoalescedHandl return NoGatherCoalescedHandle(params) -def _unsharded_single_rank_warning(dp_world_size, data_parallel_group, env=None): +def _contradicting_single_rank_pg_error(dp_world_size, data_parallel_group, env=None): """Detect the silent single-rank fallback described in #8084. When a multi-process launcher (``deepspeed``, ``torchrun``, accelerate, ...) sets ``WORLD_SIZE > 1`` but the - distributed process group was not initialized before ``zero.Init`` ran, the group resolved here collapses to a - single rank. ``zero.Init`` then creates every parameter whole on every rank instead of partitioning it, so each - rank allocates the full (unsharded) model and typically OOMs. The failure is otherwise silent and looks exactly - like a "model too big" OOM. Return an actionable warning message in that case, else ``None``. + process group resolved by ``zero.Init`` is single-rank (typically because a size-1 group was initialized before + ``zero.Init`` ran, e.g. by ``from_pretrained`` or another library), ``zero.Init`` would create every parameter + whole on every rank instead of partitioning it, so each rank allocates the full (unsharded) model and typically + OOMs. The failure is otherwise silent and looks exactly like a "model too big" OOM. ZeRO-3 cannot work correctly + with a process group that contradicts the launcher world, so return an actionable error message in that case, + else ``None``. Only the default (world-group) path is checked: an explicitly supplied ``data_parallel_group`` of size 1 is treated as intentional. @@ -903,10 +905,11 @@ def _unsharded_single_rank_warning(dp_world_size, data_parallel_group, env=None) return None return ( "zero.Init resolved a process group of world_size=1, but the launcher environment reports " - f"WORLD_SIZE={launcher_world_size}. The distributed process group was likely not initialized before " - "zero.Init ran (for example, `from_pretrained` executed before `deepspeed.init_distributed()`). Parameters " - "will NOT be partitioned: every rank allocates the full model and will likely OOM. Call " - "`deepspeed.init_distributed()` before constructing the model under zero.Init.") + f"WORLD_SIZE={launcher_world_size}. A single-rank process group was likely initialized before zero.Init ran " + "(for example, `from_pretrained` executed before `deepspeed.init_distributed()`). Parameters would NOT be " + "partitioned: every rank would allocate the full model and likely OOM. Call `deepspeed.init_distributed()` " + "before constructing the model under zero.Init, or pass an explicit `data_parallel_group` if a single-rank " + "group is intentional.") # Replaces all parameters in module with Scattered Parameters @@ -1064,9 +1067,9 @@ def __init__(self, self.rank = dist.get_rank(group=self.ds_process_group) self.dp_world_size = dist.get_world_size(group=self.ds_process_group) - _unsharded_warning = _unsharded_single_rank_warning(self.dp_world_size, data_parallel_group) - if _unsharded_warning is not None: - logger.warning(_unsharded_warning) + _pg_contradiction = _contradicting_single_rank_pg_error(self.dp_world_size, data_parallel_group) + if _pg_contradiction is not None: + raise RuntimeError(_pg_contradiction) self.zero_param_process_group = zero_param_parallel_group if _ds_config is not None and _ds_config.zero_config.zero_hpz_partition_size > 1 and self.zero_param_process_group is None: diff --git a/tests/unit/runtime/zero/test_zero_init_pg_contradiction.py b/tests/unit/runtime/zero/test_zero_init_pg_contradiction.py new file mode 100644 index 000000000000..9e78fd6c7540 --- /dev/null +++ b/tests/unit/runtime/zero/test_zero_init_pg_contradiction.py @@ -0,0 +1,45 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team + +# Regression coverage for #8084: zero.Init silently falls back to a single-rank (unsharded) group when a size-1 +# process group exists at zero.Init time under a multi-process launcher (e.g. `from_pretrained` before +# `deepspeed.init_distributed()`), so every rank allocates the full model and OOMs. ZeRO-3 cannot work correctly with +# a process group that contradicts the launcher world, so the detection helper must produce an error message only +# when the launcher reports a multi-process world but the resolved group collapsed to one rank. + +import pytest + +from deepspeed.runtime.zero.partition_parameters import _contradicting_single_rank_pg_error + + +def test_errors_when_launcher_multiprocess_but_group_is_single_rank(): + msg = _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=None, env={"WORLD_SIZE": "8"}) + assert msg is not None + assert "WORLD_SIZE=8" in msg + assert "init_distributed" in msg + + +def test_no_error_for_genuine_single_process(): + assert _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=None, + env={"WORLD_SIZE": "1"}) is None + assert _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=None, env={}) is None + + +def test_no_error_when_group_actually_shards(): + assert _contradicting_single_rank_pg_error(dp_world_size=8, data_parallel_group=None, + env={"WORLD_SIZE": "8"}) is None + + +def test_no_error_when_explicit_dp_group_supplied(): + # An explicitly provided size-1 data_parallel_group is treated as intentional. + sentinel_group = object() + assert _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=sentinel_group, + env={"WORLD_SIZE": "8"}) is None + + +@pytest.mark.parametrize("bad", ["", "not-an-int", None]) +def test_malformed_world_size_does_not_raise(bad): + assert _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=None, + env={"WORLD_SIZE": bad}) is None diff --git a/tests/unit/runtime/zero/test_zero_init_unsharded_warning.py b/tests/unit/runtime/zero/test_zero_init_unsharded_warning.py deleted file mode 100644 index 11fc045b4345..000000000000 --- a/tests/unit/runtime/zero/test_zero_init_unsharded_warning.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# SPDX-License-Identifier: Apache-2.0 - -# DeepSpeed Team - -# Regression coverage for #8084: zero.Init silently falls back to a single-rank (unsharded) group when the -# distributed process group is not initialized before it runs (e.g. `from_pretrained` before -# `deepspeed.init_distributed()`), so every rank allocates the full model and OOMs. The detection helper must warn -# only when the launcher reports a multi-process world but the resolved group collapsed to one rank. - -import pytest - -from deepspeed.runtime.zero.partition_parameters import _unsharded_single_rank_warning - - -def test_warns_when_launcher_multiprocess_but_group_is_single_rank(): - msg = _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=None, env={"WORLD_SIZE": "8"}) - assert msg is not None - assert "WORLD_SIZE=8" in msg - assert "init_distributed" in msg - - -def test_no_warning_for_genuine_single_process(): - assert _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=None, env={"WORLD_SIZE": "1"}) is None - assert _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=None, env={}) is None - - -def test_no_warning_when_group_actually_shards(): - assert _unsharded_single_rank_warning(dp_world_size=8, data_parallel_group=None, env={"WORLD_SIZE": "8"}) is None - - -def test_no_warning_when_explicit_dp_group_supplied(): - # An explicitly provided size-1 data_parallel_group is treated as intentional. - sentinel_group = object() - assert _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=sentinel_group, env={"WORLD_SIZE": - "8"}) is None - - -@pytest.mark.parametrize("bad", ["", "not-an-int", None]) -def test_malformed_world_size_does_not_raise(bad): - assert _unsharded_single_rank_warning(dp_world_size=1, data_parallel_group=None, env={"WORLD_SIZE": bad}) is None From 70b61a0db2c1b9399ff46bf12f0ac4c52756710d Mon Sep 17 00:00:00 2001 From: Akshansh <105249360+akshansh47@users.noreply.github.com> Date: Wed, 29 Jul 2026 15:30:29 -0700 Subject: [PATCH 3/4] Treat deprecated sequence_data_parallel_group as an explicit group in the PG-contradiction guard The deprecated sequence_data_parallel_group is still accepted by zero.Init and assigned to ds_process_group, but the contradiction guard only looked at data_parallel_group, so a size-1 group supplied via the deprecated argument would be misclassified as the default world-group path and could raise spuriously. Resolve whichever explicit group was supplied and pass it to the (renamed) explicit_process_group parameter. Signed-off-by: Akshansh <105249360+akshansh47@users.noreply.github.com> --- deepspeed/runtime/zero/partition_parameters.py | 14 +++++++++----- .../zero/test_zero_init_pg_contradiction.py | 16 +++++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/deepspeed/runtime/zero/partition_parameters.py b/deepspeed/runtime/zero/partition_parameters.py index eb7360152177..68ae9cd225b1 100755 --- a/deepspeed/runtime/zero/partition_parameters.py +++ b/deepspeed/runtime/zero/partition_parameters.py @@ -880,7 +880,7 @@ def _no_gather_coalesced(params: Iterable[Parameter]) -> AllGatherCoalescedHandl return NoGatherCoalescedHandle(params) -def _contradicting_single_rank_pg_error(dp_world_size, data_parallel_group, env=None): +def _contradicting_single_rank_pg_error(dp_world_size, explicit_process_group, env=None): """Detect the silent single-rank fallback described in #8084. When a multi-process launcher (``deepspeed``, ``torchrun``, accelerate, ...) sets ``WORLD_SIZE > 1`` but the @@ -891,10 +891,11 @@ def _contradicting_single_rank_pg_error(dp_world_size, data_parallel_group, env= with a process group that contradicts the launcher world, so return an actionable error message in that case, else ``None``. - Only the default (world-group) path is checked: an explicitly supplied ``data_parallel_group`` of size 1 is - treated as intentional. + Only the default (world-group) path is checked: ``explicit_process_group`` is the process group the caller + explicitly supplied to ``zero.Init``, if any (``data_parallel_group``, or the deprecated + ``sequence_data_parallel_group``); an explicitly supplied group of size 1 is treated as intentional. """ - if dp_world_size != 1 or data_parallel_group is not None: + if dp_world_size != 1 or explicit_process_group is not None: return None env = os.environ if env is None else env try: @@ -1067,7 +1068,10 @@ def __init__(self, self.rank = dist.get_rank(group=self.ds_process_group) self.dp_world_size = dist.get_world_size(group=self.ds_process_group) - _pg_contradiction = _contradicting_single_rank_pg_error(self.dp_world_size, data_parallel_group) + # The deprecated sequence_data_parallel_group also counts as an explicitly supplied group (it is assigned + # to ds_process_group above), so a size-1 group passed through it must not trip the contradiction guard. + _explicit_process_group = data_parallel_group if data_parallel_group is not None else sequence_data_parallel_group + _pg_contradiction = _contradicting_single_rank_pg_error(self.dp_world_size, _explicit_process_group) if _pg_contradiction is not None: raise RuntimeError(_pg_contradiction) diff --git a/tests/unit/runtime/zero/test_zero_init_pg_contradiction.py b/tests/unit/runtime/zero/test_zero_init_pg_contradiction.py index 9e78fd6c7540..96cba6c9f476 100644 --- a/tests/unit/runtime/zero/test_zero_init_pg_contradiction.py +++ b/tests/unit/runtime/zero/test_zero_init_pg_contradiction.py @@ -15,31 +15,33 @@ def test_errors_when_launcher_multiprocess_but_group_is_single_rank(): - msg = _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=None, env={"WORLD_SIZE": "8"}) + msg = _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=None, env={"WORLD_SIZE": "8"}) assert msg is not None assert "WORLD_SIZE=8" in msg assert "init_distributed" in msg def test_no_error_for_genuine_single_process(): - assert _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=None, + assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=None, env={"WORLD_SIZE": "1"}) is None - assert _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=None, env={}) is None + assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=None, env={}) is None def test_no_error_when_group_actually_shards(): - assert _contradicting_single_rank_pg_error(dp_world_size=8, data_parallel_group=None, + assert _contradicting_single_rank_pg_error(dp_world_size=8, explicit_process_group=None, env={"WORLD_SIZE": "8"}) is None def test_no_error_when_explicit_dp_group_supplied(): - # An explicitly provided size-1 data_parallel_group is treated as intentional. + # An explicitly provided size-1 group is treated as intentional. This covers both `data_parallel_group` and the + # deprecated `sequence_data_parallel_group`: zero.Init resolves whichever was supplied into the same explicit + # group argument before calling the helper. sentinel_group = object() - assert _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=sentinel_group, + assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=sentinel_group, env={"WORLD_SIZE": "8"}) is None @pytest.mark.parametrize("bad", ["", "not-an-int", None]) def test_malformed_world_size_does_not_raise(bad): - assert _contradicting_single_rank_pg_error(dp_world_size=1, data_parallel_group=None, + assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=None, env={"WORLD_SIZE": bad}) is None From 0c3aa091a1c34ea343cb7e107a6272f7c50de5af Mon Sep 17 00:00:00 2001 From: Akshansh <105249360+akshansh47@users.noreply.github.com> Date: Thu, 30 Jul 2026 12:25:30 -0700 Subject: [PATCH 4/4] Move PG-contradiction test to tests/unit/v1/zero, DeepSpeed Team header, yapf Per review: relocate the unit test to tests/unit/v1/zero/ as suggested, switch to the DeepSpeed Team copyright header, and reflow the asserts to the repo yapf style. Signed-off-by: Akshansh <105249360+akshansh47@users.noreply.github.com> --- .../zero/test_zero_init_pg_contradiction.py | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) rename tests/unit/{runtime => v1}/zero/test_zero_init_pg_contradiction.py (70%) diff --git a/tests/unit/runtime/zero/test_zero_init_pg_contradiction.py b/tests/unit/v1/zero/test_zero_init_pg_contradiction.py similarity index 70% rename from tests/unit/runtime/zero/test_zero_init_pg_contradiction.py rename to tests/unit/v1/zero/test_zero_init_pg_contradiction.py index 96cba6c9f476..9bcce1a26983 100644 --- a/tests/unit/runtime/zero/test_zero_init_pg_contradiction.py +++ b/tests/unit/v1/zero/test_zero_init_pg_contradiction.py @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. +# Copyright (c) DeepSpeed Team. # SPDX-License-Identifier: Apache-2.0 # DeepSpeed Team @@ -21,27 +21,26 @@ def test_errors_when_launcher_multiprocess_but_group_is_single_rank(): assert "init_distributed" in msg -def test_no_error_for_genuine_single_process(): - assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=None, - env={"WORLD_SIZE": "1"}) is None - assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=None, env={}) is None +@pytest.mark.parametrize("env", [{"WORLD_SIZE": "1"}, {}]) +def test_no_error_for_genuine_single_process(env): + assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=None, env=env) is None def test_no_error_when_group_actually_shards(): - assert _contradicting_single_rank_pg_error(dp_world_size=8, explicit_process_group=None, - env={"WORLD_SIZE": "8"}) is None + env = {"WORLD_SIZE": "8"} + assert _contradicting_single_rank_pg_error(dp_world_size=8, explicit_process_group=None, env=env) is None -def test_no_error_when_explicit_dp_group_supplied(): +def test_no_error_when_explicit_group_supplied(): # An explicitly provided size-1 group is treated as intentional. This covers both `data_parallel_group` and the # deprecated `sequence_data_parallel_group`: zero.Init resolves whichever was supplied into the same explicit # group argument before calling the helper. - sentinel_group = object() - assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=sentinel_group, - env={"WORLD_SIZE": "8"}) is None + group = object() + assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=group, env={"WORLD_SIZE": + "8"}) is None @pytest.mark.parametrize("bad", ["", "not-an-int", None]) def test_malformed_world_size_does_not_raise(bad): - assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=None, - env={"WORLD_SIZE": bad}) is None + env = {"WORLD_SIZE": bad} + assert _contradicting_single_rank_pg_error(dp_world_size=1, explicit_process_group=None, env=env) is None