[email protected]: sitecustomize.py imports `re` at startup, costing ~12% of interpreter start time
brew config AND brew doctor output OR brew gist-logs <formula> link
brew configHOMEBREW_VERSION: 6.0.20
ORIGIN: https://github.com/Homebrew/brew
HEAD: 3e3da86e4eaccd23f4ce43df9b0a31b16c707347
Last commit: 4 days ago
Branch: stable
Core tap: N/A
Core cask tap: N/A
HOMEBREW_PREFIX: /opt/homebrew
Homebrew Ruby: 4.0.6 => /opt/homebrew/Library/Homebrew/vendor/portable-ruby/4.0.6/bin/ruby
CPU: octa-core 64-bit arm_ibiza
Clang: 21.0.0 build 2100
Git: 2.50.1 => /Library/Developer/CommandLineTools/usr/bin/git
Curl: 8.7.1 => /usr/bin/curl
macOS: 26.6.2-arm64
CLT: 26.6.0.0.1781586589
Xcode: N/A
Rosetta 2: falsebrew doctorWarning: The following taps are not trusted:
anomalyco/tap
updatest/tapThis is the only warning. It concerns trust for two third-party taps and is unrelated to [email protected] interpreter startup timing — the measurements below use python3 -c pass, which touches no tap.
Verification
- My
brew doctoroutput saysYour system is ready to brew.and am still able to reproduce my issue. Not ticked, honestly: the only warning is tap-trust for two third-party taps (above), which cannot affectpython3 -c passstartup. - I ran
brew updateand am still able to reproduce my issue. - I have resolved all warnings from
brew doctorand that did not fix my problem. Not ticked: the remaining warnings are the tap-trust ones above; resolving them would not change interpreter startup. - I searched for recent similar issues at https://github.com/Homebrew/homebrew-core/issues?q=is%3Aissue and found no duplicates.
- My issue is not about a failure to build a formula from source.
- I did not use AI/LLM to create this issue, or I disclosed the tool and model used; I will answer maintainer questions myself without AI/LLM. Disclosure: this report was drafted with Claude Code (Anthropic, Claude Opus 5). All measurements below were run on my machine and are reproducible with the commands given. I will answer maintainer questions myself.
What were you trying to do (and why)?
Reduce Python interpreter startup time. A local test suite spawns roughly 1,320 processes per run, so per-process startup cost is multiplied heavily. Profiling python3 startup pointed at Homebrew's generated sitecustomize.py.
What happened (include all command output)?
sitecustomize.py — generated by the formula and executed on every interpreter start — does a module-level import re (line 4), solely to compile two path-rewriting regexes. re is a relatively expensive import.
$ python3 -X importtime -c pass 2>&1 | grep -E ' re$| sitecustomize$| site$'
import time: 236 | 2672 | re
import time: 485 | 3156 | sitecustomize
import time: 388 | 5142 | sitere accounts for 2,672 µs of sitecustomize's 3,156 µs — about 85% of what sitecustomize costs.
Wall-clock, 200 iterations each:
python3 -c pass : 21.30 ms
python3 -S -c pass : 13.47 ms (site processing skipped)
site machinery : 7.83 ms (37% of startup)The re import alone is roughly 12.5% of a bare interpreter start.
Both regex call sites sit inside branches that are always true for a brewed Python, so simply deferring the import to point of use would not help the common case:
- lines 28-29 —
long_prefix, insideif os.path.realpath(sys.executable).startswith('/opt/homebrew/Cellar/[email protected]') - line 35 —
cellar_prefix, insideif 'PYTHONHOME' not in os.environ
What did you expect to happen?
That a bare python3 -c pass would not spend ~12% of its startup importing re on behalf of sitecustomize, given the regexes rewrite only a handful of sys.path entries.
I prototyped a string-only equivalent and property-tested it over 153 generated inputs (version shapes 3.14.7, 3.14.10_1, 3.14.0rc1, 3.14.0a1, 3.14.0b2; both Frameworks/... and plain lib/... layouts; plus non-matching and malformed paths) — 0 mismatches.
I am deliberately not proposing it as a patch, because it is not strictly equivalent: re.sub substitutes anywhere in the string and repeatedly, a prefix-based string version does not. Counterexample:
input: /some/prefix/opt/homebrew/Cellar/[email protected]/3.14.7/lib/python3.14/site-packages
regex: /some/prefix/opt/homebrew/lib/python3.14/site-packages
string: (unchanged)Whether such a sys.path entry can legitimately occur is a question about the formula's intent, which maintainers are better placed to answer than I am.
Possible directions, in case any is useful:
- Replace the regexes with string operations, if prefix-only matching is acceptable.
- Bake the concrete version string into the generated file at install time — the formula already knows it — reducing this to
str.replace. - Guard the work so
reis imported only when asys.pathentry actually needs rewriting.
Happy to test any of these here and report numbers.
Step-by-step reproduction instructions (by running brew commands)
$ brew install [email protected]
$ python3 -X importtime -c pass 2>&1 | grep -E ' re$| sitecustomize$| site$'
$ # compare against site processing being skipped:
$ python3 -c pass # ~21.3 ms here
$ python3 -S -c pass # ~13.5 ms hereInspect the file directly:
$ sed -n '1,10p;28,29p;35p' "$(brew --prefix [email protected])/Frameworks/Python.framework/Versions/3.14/lib/python3.14/sitecustomize.py"Source: Homebrew/homebrew-core