Repository navigation
Description
Follow-up to #210, which tracks the fallout from Ruff 0.16's expanded default rule set.
The rule
B008 (function-call-in-default-argument) is now on by default. It flags any call in an argument default:
Do not perform function call
Xin argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
Why it isn't worth keeping
The genuine bug in this neighborhood — a shared mutable default — is already caught by B006 (mutable-argument-default), which stays enabled and which I'm happy to keep. What B008 adds is a warning that the call is evaluated once at def time. For an immutable value that isn't a hazard; it's the point.
To keep that from drowning every sensible default, B008 carries a hardcoded allowlist of "immutable calls" — and the allowlist is arbitrary. pathlib.Path() is exempt; every one of its siblings is flagged, despite pure paths being the more obviously immutable ones (they touch no filesystem):
| default | B008 |
|---|---|
pathlib.Path() |
ok |
pathlib.PurePath() |
flagged |
pathlib.PosixPath() |
flagged |
pathlib.PurePosixPath() |
flagged |
frozenset() |
ok |
frozendict() (new in 3.15) |
flagged |
A rule that exempts frozenset but not frozendict isn't reasoning about mutability, it's consulting a list that nobody has finished writing.
The escape hatch, extend-immutable-calls, takes fully-qualified names — so suppressing a false positive means enumerating project-specific paths in ruff.toml, which is exactly the downstream config churn the skeleton exists to prevent. For a compatibility shim it's worse: the entry has to name a project-local module (tests.compat.py314.frozendict), and a bare "frozendict" doesn't match the aliased import.
Observed downstream
python/importlib_metadata reports four B008 findings, all benign:
importlib_metadata/__init__.py×2 —def find_distributions(self, context=Context()). This is the publicDistributionFinderAPI and it mirrors CPython's own signature (inspect.signature(importlib.metadata.DistributionFinder.find_distributions)shows the sameContext()default). Changing it would diverge the backport from the stdlib it backports.tests/_path.py—def __init__(self, loc=pathlib.PurePosixPath(), record=None), vendored fromjaraco.path, so the finding recurs anywhere that file lands.tests/test_main.py—def make_pkg(name, files=frozendict(METADATA="VERSION: 1.0")), written while fixing the B006 report from Ruff 0.16 introduces new default checks #210. Replacing a mutable default with an explicitly immutable one trades B006 for B008, which is a good illustration of the rule having no satisfiable target.
Each suggested remedy is worse than the code it replaces: a None sentinel adds a branch and erases the default from the signature, and a module-level singleton moves the value away from its only use.
Plan
Add B008 to the upstream ignore list in ruff.toml, so downstream projects inherit it rather than each carrying a local exclusion or # noqa:
# B008 is over-reaching; see jaraco/skeleton#214
"B008", # function-call-in-default-argument
Activity
Ignore B008 (function-call-in-default-argument). (#215)