Repository navigation
fix(utils): treat a blank or non-numeric LOCAL_RANK as unset - #10246
Merged
hjh0119 merged 1 commit intoSep 28, 2026
Merged
Conversation
Logging is configured while swift.utils.logger is imported, so the two bare
int(os.getenv('LOCAL_RANK', -1)) reads raise ValueError from the import itself
when the variable is present but does not parse (e.g. `LOCAL_RANK=` left in a
launcher script or a Dockerfile). Route both through one helper that falls back
to the unset default, so a rank that parses keeps its meaning.
1 of 4 tasks
hjh0119
approved these changes
Sep 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR type
PR information
swift/utils/logger.pyreadLOCAL_RANKwith a bareint()in two places:_is_local_master()— reached while the module is still being imported, becauselogger = get_logger()runs at import time andget_logger()calls_is_local_master();add_file_handler_if_needed()— the same read, guarded by thefind_spec('torch')check.So a
LOCAL_RANKthat is present but not a number raisesValueErrorfrom the import itself:LOCAL_RANK=(declared but left empty) is what a launcher script or a Dockerfile produces when the variable is exported before the value is known —ENV LOCAL_RANK=in an image, orexport LOCAL_RANK=in a wrapper that only fills it in for the distributed path. Today that makesswift.utilsunimportable, which takes down every entry point behind it. Note thatint()already tolerates an unset variable through its-1default, so the crash is specific to a value that exists but does not parse.This PR adds one
_get_local_rank()helper that falls back to the unset default (-1, i.e. local master) when the value does not parse, and routes both sites through it. A rank that does parse keeps its exact meaning, so rank > 0 still silences the logger — the fallback only covers the case that previously raised. This mirrors howLOG_LEVELis resolved right below it (_get_log_level()falls back toINFOrather than raising), which is the same "a blank env var should not break the import" argument that #10245 merged for.Why the read is duplicated in this file at all:
swift/utils/env.pyalready hasget_dist_setting()/is_local_master(), butenv.py:9doesfrom .logger import get_logger, sologger.pycannot import it back — that is what the existing# Avoid circular referencecomment marks. This PR keeps the fallback local tologger.pyfor the same reason, and deliberately does not touchenv.py, whose reads fail at call time rather than at import and therefore need their own discussion.Experiment results
All numbers below were produced on this checkout with the project's own runner (
python -m unittest, sincetests/run.pydiscoverstests/**/test_*.py), Python 3.12 on Windows, againstmain@c08110b.New test
tests/utils/test_local_rank.py, 7 cases, each in a fresh subprocess so the import-time behaviour is what is measured:mainlogger.py+ the new test:Ran 7 tests ... FAILED (failures=4). The four failures are the blank, whitespace, non-numeric and file-handler cases; the three controls (unset,LOCAL_RANK=0,LOCAL_RANK=1) pass, so the test does not simply assert the crash away.Ran 7 tests ... OK.add_file_handler_if_neededread left as a bareint(), the blank value still raises:tests/utilsregression, measured by swappinglogger.pybetween this branch and pristinemainon the same venv: with the fixRan 154 tests ... FAILED (failures=2, errors=7, skipped=22); with pristinelogger.pyand the same test file presentRan 154 tests ... FAILED (failures=6, errors=7, skipped=22). The delta is exactly the 4 new cases above, and the remaining 2 failures plus all 7 errors and 22 skips are identical in both runs — they come from this minimal environment missing optional dependencies, not from the change.pre-commit run --files swift/utils/logger.py tests/utils/test_local_rank.py→ flake8 / isort / yapf / trailing-whitespace / end-of-file / double-quote-string / mixed-line-ending allPassed.Diff is
+12/-3inswift/utils/logger.pyplus the new test file.A note on how this patch was prepared: it was written and measured by an automated agent session working for the account owner. The numbers above are its own measurements, reproduced on
mainand on this branch immediately before opening the PR; the owner has not independently re-run them.