Skip to content

Resolve macOS proxy settings before forking - #619

Open
roberstreams wants to merge 2 commits into
boxed:mainfrom
roberstreams:fix-macos-proxy-fork
Open

roberstreams wants to merge 2 commits into
boxed:mainfrom
roberstreams:fix-macos-proxy-fork

Conversation

@roberstreams

@roberstreams roberstreams commented Oct 30, 2026 •

Copy link
Copy Markdown

On macOS, urllib.request.getproxies() and proxy_bypass() query the SystemConfiguration framework through _scproxy when no *_proxy variable is set. requests, httpx and aiohttp all call them. That framework isn't fork-safe, so any test that builds an HTTP client crashes in the forked child, and every mutant reports as segfault.

Change: _pin_macos_proxy_settings() runs at the start of _run, before any fork. On macOS it calls urllib.request._get_proxies() and _get_proxy_settings() once, in the unforked parent. It then replaces both with functions that return those cached answers. Forked children inherit the patched module, so they never reach SystemConfiguration. Proxy and bypass results stay identical, because urllib's own macOS matcher still evaluates the system's exception list. The environment is not modified. Other platforms are untouched.

A first version exported the settings as *_proxy / no_proxy variables instead. That changed bypass behavior: macOS exceptions such as *.local and 169.254/16 use syntax that urllib's environment matcher doesn't understand. The second commit replaces that approach.

Verification:

  • Repro from macOS: every mutant reports segfault when tests touch urllib.request.getproxies() (SystemConfiguration is not fork-safe) #618. On unpatched main both mutants report segfault. With this patch both report killed.
  • Real macOS settings. With no proxy variables set, getproxies() and proxy_bypass() for build.local, 169.254.1.2, localhost, example.com, 10.0.0.5 and printer give identical results before and after _pin_macos_proxy_settings().
  • New tests in tests/test_macos_proxy.py. The lookups are answered from one pre-fork query, and bypass results come from urllib's real _proxy_bypass_macosx_sysconf (*.local and 169.254/16 match, example.com doesn't). Other platforms are left alone. Both tests run on any OS.
  • Checks. ruff check and ruff format are clean. mypy is clean for __main__.py with both --platform darwin and --platform linux, and main has 1 error there already.
  • Suite. pytest tests --ignore=tests/e2e gives 466 passed and 1 failed. The failure, test_excluded_lines_reports_only_the_first_line_of_each_statement, fails the same way on unpatched main.

Fixes #618

🤖 Generated with Claude Code

On macOS, urllib.request.getproxies() falls back to the SystemConfiguration
framework when no *_proxy variable is set. requests, httpx and aiohttp all call
it, and the framework is not fork-safe, so any test that builds an HTTP client
crashes in the forked child and every mutant reports as a segfault.

Resolve the system settings once in the main process and export them as
*_proxy / no_proxy variables, which urllib reads first. Explicit proxy
variables and other platforms are left untouched.
Copying macOS bypass exceptions into no_proxy changed their meaning:
"*.local" and "169.254/16" use macOS's own syntax, which urllib's
environment matcher does not understand, so hosts the system would bypass
went through the proxy. Query SystemConfiguration once in the parent and
replace urllib.request._get_proxies / _get_proxy_settings with the cached
answers instead; forked children inherit them, and urllib's own macOS
matcher gives the same results. The environment is no longer modified.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

macOS: every mutant reports segfault when tests touch urllib.request.getproxies() (SystemConfiguration is not fork-safe)

1 participant