Repository navigation
Pin LC_COLLATE for file ordering, as pkgload does - #16
Merged
Merged
Conversation
load_fast() sorted R/ files with a plain order(basename(...)), which follows
the session's LC_COLLATE. glibc's en_US.UTF-8 ignores "_" and "." at the
primary level, so files differing only in those characters came out in a
different order than R CMD INSTALL produces -- and a package whose top-level
code runs in install order then fails to load.
On a 564-file package, 405 of the files changed position versus the C order.
Two concrete breakages: utils_log_instances.R sorted ahead of utils_log.R,
and portfolio_saa_multiasset.R ahead of portfolio.R, each with a top-level
call to a function the earlier-sorting file had not defined yet.
Mirror pkgload: find_code() wraps its listing in withr::local_collate("C"),
and R CMD INSTALL pins lccollate <- "C" in
tools:::.install_package_code_files. The pin covers the ordering only, not the
sourcing, so package code still runs under the user's locale -- and the
caller's LC_COLLATE is restored on exit, which the new test asserts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
felix-andreas-finccam
force-pushed
the
fix/locale-independent-file-order
branch
from
September 22, 2026 12:50
3f09c7e to
f51cde9
Compare
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.
The problem
load_fast()sorts the files inR/before it loads them. It usedorder()with no options.order()sorts text using the locale of the current R session.On Linux the usual locale is
en_US.UTF-8. This locale ignores_and.when it compares names. Two files that differ only in those characters can therefore load in a different order than R itself would use.This breaks any package that needs a fixed load order. A file can run code at the top level. If that code calls a function from another file, the other file must load first.
We hit this in a package with 564 files. 405 of them changed position. Two files then failed:
utils_log_instances.Rloaded beforeutils_log.Rportfolio_saa_multiasset.Rloaded beforeportfolio.REach of those files calls a function at the top level. The function did not exist yet, so the load stopped with "could not find function".
The fix
Two other R loaders already solve this. Both pin the sort order to the "C" locale:
R CMD INSTALLsetslccollate <- "C"intools:::.install_package_code_filesdevtools::load_all()wraps its file listing inwithr::local_collate("C")insidefind_code()This change does the same. A small helper sets
LC_COLLATEto"C", runs the sort, then puts the old value back.The helper covers the sorting only. Package code still runs under the user's own locale. This is how
devtools::load_all()behaves, and it is why the helper does not simply wrap the whole function.loadfastdepends only onrlang, so the helper uses base R rather than addingwithr.Tests
Stage
3d2is new. It builds a package with two files.utils_log.Rdefines a function.utils_log_instances.Rcalls that function at the top level. The test sets the locale toen_US.UTF-8and loads the package.The test checks three things:
LC_COLLATEis unchanged after the load.Check 3 matters because this fix changes global state. The test skips itself if
en_US.UTF-8is not installed.Test results:
I also ran the real 564-file package. It now loads under
en_US.UTF-8with no locale override, and the session locale is the same before and after.🤖 Generated with Claude Code