LOC-7228: Relax psutil upper bound to allow psutil 7.x - #63
Conversation
browserstack-local 1.2.15 pinned psutil>=5.6.6,<7, which conflicts with environments that already have psutil 7.x installed (e.g. alongside the BrowserStack SDK). The only psutil API used is psutil.pid_exists(), which is unchanged in psutil 7, so the upper bound is unnecessary. Fixes LOC-7228 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
07souravkunda
left a comment
There was a problem hiding this comment.
Reviewed the constraint relaxation and re-checked the claims in the description against primary sources — they all hold. The <7 cap came in with the LOC-6720 commit (b5bb4f7) alongside the >=5.6.6 floor; OSV lists exactly one psutil advisory ever (CVE-2019-18874, introduced: 0, fixed: 5.6.6) with nothing affecting 6.x or 7.x, so the floor is the entire mitigation and removing the ceiling reintroduces no known vulnerability; pid_exists() at local.py:131 is the only psutil API in the package or its tests; and 7.2.2 ships cp36-abi3 wheels, so there is no Python 3.6/3.7 sdist regression (the aarch64 wheel also still carries manylinux_2_17, so no glibc floor bump). 1.2.16 is the right version — PyPI latest is 1.2.15 and setup.py is the only place a version is declared.
Also cross-checked the consumer side: browserstack_sdk requires bare psutil plus browserstack-local>=1.2.5, which confirms the cap came solely from this package and means a fresh SDK install picks up 1.2.16 with no SDK release needed.
One non-blocking comment inline: the documented edge case covers win32 but misses 32-bit Linux (i686), which lost wheels on the same boundary and is a platform local_binary.py explicitly branches for. Not a merge blocker — fine to land with the note extended, or with the cap scoped to 32-bit via an environment marker.
| classifiers = [], | ||
| install_requires=[ | ||
| 'psutil>=5.6.6,<7', | ||
| 'psutil>=5.6.6', |
There was a problem hiding this comment.
The "Known edge case" section covers 32-bit Windows, but psutil dropped 32-bit Linux (i686) wheels on the same boundary — and 32-bit Linux is a platform this package explicitly serves.
Evidence — wheel platform tags on PyPI:
| psutil | linux i686 wheel | win32 wheel |
|---|---|---|
| 6.1.1 | yes | yes |
| 7.0.0 | yes | yes |
| 7.2.2 | no | no |
browserstack/local_binary.py:17,31-34 computes is_64bits = sys.maxsize > 2**32 and downloads BrowserStackLocal-linux-ia32 for 32-bit interpreters, so 32-bit Linux is a first-class target here. Windows, by contrast, has no bitness branch at all (local_binary.py:36 → a single BrowserStackLocal.exe). With the cap removed, a fresh install on a 32-bit Linux interpreter resolves psutil 7.2.2 and must compile it from sdist (gcc + Python headers) where 6.1.1/7.0.0 supplied a wheel — a minimal CI image without a toolchain fails the install outright.
Worth adding the flip side to the description as well: 7.2.2 gains musllinux wheels (x86_64 + aarch64) that 6.1.1 lacked entirely, so Alpine users (local_binary.py:26 → BrowserStackLocal-alpine) go from an sdist build to a wheel. Net wheel coverage improves for a supported platform.
Fix — either extend the documented edge case to win32 + linux i686 and accept it knowingly, or scope the cap to 32-bit so wheel coverage is preserved everywhere:
install_requires=[
'psutil>=5.6.6',
'psutil<7; platform_machine in "i686 i386 x86"',
]On "environment markers cannot reliably detect interpreter bitness" — markers do expose platform_machine, which pip evaluates as i686 on 32-bit Linux and x86 for 32-bit CPython on Windows (a WOW64 process reports PROCESSOR_ARCHITECTURE=x86). I haven't tested that resolution myself, and platform_machine reflects the OS/WOW64 view rather than sys.maxsize, so worth a pip install --dry-run --platform manylinux2014_i686 check before relying on it.
Question for author: was 32-bit Linux considered alongside win32, or did the wheel audit look only at the Windows set (the LOC-7228 customer is on Windows)?
Summary
psutilruntime dependency constraint frompsutil>=5.6.6,<7topsutil>=5.6.61.2.16for releaseContext
LOC-7228 — a customer installing the BrowserStack Python SDK hit a dependency conflict:
psutilis a runtime dependency (declared ininstall_requires, imported inbrowserstack/local.py), not a dev dependency. The upper bound is removed entirely (rather than bumped to<8) so this conflict doesn't recur on the next psutil major release, given the minimal API surface (see below).The upper bound was added as a response for a CVE raised in LOC-6720. CVE-2019-18874 affected range (NVD + OSV/GHSA-qfc5-mcwq-26q8): psutil through 5.6.5 has a double free from refcount mishandling; the affected range is [0, 5.6.6) with fixed = 5.6.6. The mitigation is entirely the >=5.6.6 floor. Newer versions, including all of 6.x and 7.x — are fixes, not risks, for this CVE.
Verification
1. API surface audit —
psutil.pid_exists()(browserstack/local.py:131, insideisRunning()) is the only psutil API used anywhere in the package, including tests.2. psutil 7.x changelog review (psutil.io/changelog) — 7.0.0 dropped Python 2.7 and removed only
Process.memory_info_ex()(deprecated since 4.0.0, unused here).pid_exists()was not removed or signature-changed in any 7.x release. One behavior fix: on Windows,pid_exists()now returns the correct answer underERROR_ACCESS_DENIEDinstead of disagreeing withProcess. For our usage (checking the daemonized binary's pid, same user) this is neutral-to-beneficial — the new behavior is the correct answer forisRunning().3. Legacy Python safety — psutil 7.x declares
requires_python >= 3.6on PyPI (verified via PyPI JSON API), so pip on Python 2.7/3.5 automatically resolves to psutil 6.1.x; removing our upper bound cannot break those installs.4. Live install + functional test — clean venv (Python 3.11),
pip install psutil==7.2.2+ this package from source:pip check→No broken requirements found(the exact failure from the ticket, resolved)isRunning()exercised against a live pid / killed pid / missingpidattr → all correct under psutil 7.2.25. Wheel coverage — psutil 7.2.2 ships prebuilt wheels for win_amd64, win_arm64, macOS, manylinux, musllinux.
Known edge case
psutil dropped 32-bit Windows (win32) wheels after 7.0.0 (6.1.1 and 7.0.0 have them; 7.2.2 does not). A user on 32-bit Windows Python without MSVC build tools would fail to build psutil from sdist on a fresh install. Judged acceptable: 32-bit Python is rare in 2026, the SDK ecosystem already pulls psutil 7.x (which is what caused this ticket), and environment markers cannot reliably detect interpreter bitness to special-case it.