## Summary
<!-- What's the purpose of the change? What does it do, and why? -->
Changes in this PR https://github.com/astral-sh/ruff/pull/13591 did not
allow correct discovery in pip build environments.
```python
# both of these variables are tuple[str, str] (length is 2)
first, second = os.path.split(paths[0]), os.path.split(paths[1])
# so these length checks are guaranteed to fail even for build environment folders
if (
len(first) >= 3
and len(second) >= 3
...
)
```
~~Here we instead use `pathlib`, and we check all `pip-build-env-` paths
for the folder that is expected to contain the `ruff` executable.~~
Here we update the logic to more properly split out the path components
that we use for `pip-build-env-` inspection.
## Test Plan
I've checked this manually against a workflow that was failing, I'm not
sure what to do for real tests. The same issues apply as with the
previous PR.
---------
Co-authored-by: Jonathan Surany <jsurany@bloomberg.net>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
Resolves https://github.com/astral-sh/ruff/issues/13321.
Contents of overlay:
```bash
/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/overlay/bin:
total 26M
-rwxr-xr-x 1 bgabor8 staff 26M Oct 1 08:22 ruff
drwxr-xr-x 3 bgabor8 staff 96 Oct 1 08:22 .
drwxr-xr-x 4 bgabor8 staff 128 Oct 1 08:22 ..
```
Python executable:
```bash
'/Users/bgabor8/git/github/ruff-find-bin-during-build/.venv/bin/python'
```
PATH is:
```bash
['/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/overlay/bin',
'/private/var/folders/v0/l8q3ghks2gs5ns2_p63tyqh40000gq/T/pip-build-env-e0ukpbvo/normal/bin',
'/Library/Frameworks/Python.framework/Versions/3.11/bin',
'/Library/Frameworks/Python.framework/Versions/3.12/bin',
```
Not sure where to add tests, there does not seem to be any existing one.
Can someone help me with that?
Fixes#11246
## Summary
This change adds an intermediate additional search path for
`find_ruff_bin`.
I would have added this path as the last one, except that the last one
is the one reported to the user, so I made this one second to last.
## Test Plan
It's shown to work with this command:
```
~ @ pip-run git+https://github.com/jaraco/ruff@feature/honor-install-target-bin -- -m ruff --version
ruff 0.4.4
```
I tried running the same command on Windows, which should work in
theory, but building ruff from source on Windows is complicated. Even
after installing Rust, ruff fails to build when `libmimalloc-sys` fails
to build because `gcc` isn't installed (and the error message points to
a [broken
anchor](https://github.com/rust-lang/cc-rs#compile-time-requirements)).
I was really hoping Rust would get us away from the Windows as
second-class-citizen model :(.
This saves a handful of milliseconds on Windows and even more on other
platforms when running `python -m ruff`. On non-Windows systems the
process is replaced directly (impossible on Windows unfortunately).
```
❯ docker run --rm python:3.11 bash -c "for i in {1..15}; do python -m timeit -n 1 -r 1 'from pathlib import Path'; done"
1 loop, best of 1: 25.7 msec per loop
1 loop, best of 1: 3.07 msec per loop
1 loop, best of 1: 3.16 msec per loop
1 loop, best of 1: 3.06 msec per loop
1 loop, best of 1: 3.32 msec per loop
1 loop, best of 1: 3.93 msec per loop
1 loop, best of 1: 3.26 msec per loop
1 loop, best of 1: 3.73 msec per loop
1 loop, best of 1: 3.1 msec per loop
1 loop, best of 1: 3.29 msec per loop
1 loop, best of 1: 3.12 msec per loop
1 loop, best of 1: 3.05 msec per loop
1 loop, best of 1: 3.04 msec per loop
1 loop, best of 1: 3.19 msec per loop
1 loop, best of 1: 3.04 msec per loop
❯ docker run --rm python:3.11 bash -c "for i in {1..15}; do python -m timeit -n 1 -r 1 'import subprocess'; done"
1 loop, best of 1: 31.2 msec per loop
1 loop, best of 1: 3.75 msec per loop
1 loop, best of 1: 4.71 msec per loop
1 loop, best of 1: 3.88 msec per loop
1 loop, best of 1: 4.08 msec per loop
1 loop, best of 1: 4.35 msec per loop
1 loop, best of 1: 3.94 msec per loop
1 loop, best of 1: 4.06 msec per loop
1 loop, best of 1: 3.88 msec per loop
1 loop, best of 1: 3.85 msec per loop
1 loop, best of 1: 3.84 msec per loop
1 loop, best of 1: 4.01 msec per loop
1 loop, best of 1: 4.21 msec per loop
1 loop, best of 1: 4.07 msec per loop
1 loop, best of 1: 4.11 msec per loop
❯ python -m timeit -n 1 -r 1 "from pathlib import Path"
1 loop, best of 1: 5.25 msec per loop
❯ python -m timeit -n 1 -r 1 "import subprocess"
1 loop, best of 1: 7.61 msec per loop
```
## Summary
From the [subprocess
docs](https://docs.python.org/3/library/subprocess.html#subprocess.Popen):
> Changed in version 3.6: args parameter accepts a path-like object if
shell is False and a sequence containing path-like objects on POSIX.
>
> Changed in version 3.8: args parameter accepts a path-like object if
shell is False and a sequence containing bytes and path-like objects on
Windows.
We want to support python 3.7 on windows, so we need to convert the
`Path` into a `str`
Fixes#5077
## Summary
Previously, in a powershell on windows when using `python -m ruff`
instead of `ruff` a call such as `python -m ruff "a b.py"` would fail
because the space would be split into two arguments.
The python docs
[recommend](https://docs.python.org/3/library/os.html#os.spawnv) using
subprocess instead of os.spawn variants, which does fix the problem.
## Test Plan
I manually confirmed that the problem previously occurred and now
doesn't anymore. This only happens in a very specific environment
(maturin build, windows, powershell), so i could try adding a step on CI
for it but i don't think it's worth it.
```
(.venv) PS C:\Users\Konstantin\PycharmProjects\ruff> python -m ruff "a b.py"
warning: Detected debug build without --no-cache.
error: Failed to lint a: The system cannot find the file specified. (os error 2)
error: Failed to lint b.py: The system cannot find the file specified. (os error 2)
a:1:1: E902 The system cannot find the file specified. (os error 2)
b.py:1:1: E902 The system cannot find the file specified. (os error 2)
Found 2 errors.
(.venv) PS C:\Users\Konstantin\PycharmProjects\ruff> python -m ruff "a b.py"
warning: Detected debug build without --no-cache.
a b.py:2:5: F841 [*] Local variable `x` is assigned to but never used
Found 1 error.
[*] 1 potentially fixable with the --fix option.
```
Prefer the version from a currently active virtualenv over a version
from `pip install --user`. Add the .exe extension on Windows, and
find the path for `pip install --user` correctly on Windows.
Signed-off-by: Anders Kaseorg <andersk@mit.edu>