Does anyone have a particularly egregious `generat...
# development
w
Does anyone have a particularly egregious
generate-lockfiles
sample repo where I can run that command, and then run a package or test or something to confirm it works? My available repos are small to mid-sized deps, and I can't tell whether my modifications are good on larger sized repos
b
What's small to mid-sized for you? IIRC my work repo takes about 2 minutes or so. Is that large enough? If you post a draft PR, I'm happy to try run it
w
I'm around 30-60 seconds depending on exactly what's in there, but some people have 5-10 minutes !
b
Ah okay. I just ran it now in my work repo, it's 90s with pants 2.21; maybe too small to be interesting.
w
Yeah, that's in the realm of what I can get mine up to pretty quick. I'm just testing directly against pex3 right now, and warm runtimes are similar, but unless I'm imagining something, cold runs seem to be faster with the same args, but it could also just be networking
Trying to create a dry-run equivalent 🤷
I'm using pex3 directly to create the lockfiles, but I'm messing with interpreter constraints here (tried to pull this directly from a
pants
call, but it may not be a perfect 1:1 of what's happening in
pants
):
Copy code
pex3 lock create \
    --no-emit-warnings \
    --output baseline-3.12.json \
    --pip-version "24.1.2" \
    --resolver-version pip-2020-resolver \
    --style universal \
    --target-system linux \
    --target-system mac \
    --indent 2 \
    --manylinux manylinux2014 \
    --interpreter-constraint "CPython==3.12.*" \
    fastapi numpy pandas pydantic scipy sqlalchemy uvicorn
So.... This is one of those things that seems obvious when I run the test, but it's definitely never top of mind while trying to help people with these problems - because Pants kinda magicks it all away. I'm not sure if the results make sense from a
pex
point of view, but intuitively, I would assume that broader interpreter-constraints would take more time to resolve 🤷
Copy code
Cold run 3.12.*:   0m18.203s
No-op run 3.12.*:  0m6.207s

Cold run 3.12.4:   0m15.587s
No-op run 3.12.4:  0m3.531s

Cold run >=3.9,<3.13:  0m48.746s
No-op run >=3.9,<3.13: 0m37.877s
If my results are reproducible by others, I think this would explain why my lockfile generation is pretty quick. I lock down my constraints pretty tightly in production code, and I usually know exactly the interpreter I plan to run against (unlike in my test/sample code, where I do whatever)
b
Make sure torch is in the mix and use the torch cheeseshop
🧀 1
p
You are welcome to mess with github.com/StackStorm/st2 using the
st2
resolve. That takes quite a while to generate. Now however that it is on an older pants version. I've got a WIP PR to upgrade that: https://github.com/StackStorm/st2/pull/6200 You might want to use that branch, as there will be fewer changes you need to make there.
👍 1
Lockfile generation in the st2 repo takes 5-10 min iirc.
b
Dumb question, does Pex do (or inherit) the "range requests" hack of pip/uv?
p
John said that the pip implementation (--fast-deps iirc) did not actually make things faster.
b
That does not match my expectations or my observations (outside of Pex) but okie dokie
p
I have not tested. There's a closed issue in the PEX repo about that.
w
I tested it yesterday, and in my small repo, it was a negligible change in time - but I'm also running some more sets of tests once 24.2 is released shortly to pex (this is not to say that 24.2 necessarily changes anything, just that - I'll be spending more time with fast deps shortly)
b
Make sure you run it against a server that doesnt support the metadata server protocol
Like the torch repo 🙂
p
Using pants 2.18.3, lockfile regeneration on StackStorm/st2 just took:
Copy code
real    4m11.051s
user    0m0.009s
sys     0m0.014s
c
hold on, I am totally going to win this thread
b
(please do)
c
I've ripped out enough references to our internal cheese shop and various forks that this should be runnable. It takes around 15-30 minutes. https://gist.github.com/cburroughs/7b765cc6218a1c4cfbf718e3dca33078 (We also do
Copy code
[python.resolves_to_only_binary]
default = [":all:"]
internally. Perf impact is unclear but that won't work without access to all the wheels we built.)
😆 1
w
@proud-dentist-22844 What are your interpreter constraints for that? Is there a substantial time difference in lockfile regen if you tightly narrow constraints? Trying to get a feel for that
p
CPython>=3.8,<3.10
are the constraints.
w
If you specified
CPython==3.9.19
or whatever, would your lockfiles generate? I'd be curious if they're faster. I'll try to grab that later, just stuck on some terribly boring iOS development that, if I leave it now, I'll never get back to it 😆
😆 1
I've ripped out enough references to our internal cheese shop and various forks that this should be runnable. It takes around 15-30 minutes.
Yeah, this bombs out for me on pyspark. I am getting a nice appreciation of what people might be going through while debugging - this is infuriating
p
Still using pants 2.18.3, narrowing the constraints to a single python version yields:
Copy code
real    1m22.849s
user    0m0.541s
sys     0m0.040s
🤯 1
p
I extracted this from our internal setup (actually a Pipfile), and it's been taking 11 minutes and still going. This is also removing the extra index we had since that just OOMed with a requirements.txt file (Pipfile works fine for that) pants_version = "2.22.0a0" interpreter_constraints = ["CPython>=3.11,<3.12"] pip_version = "latest"
w
"pip: ERROR: No matching distribution found for redis==5.1.2.1" ?
I think this helps a bit with early failures
Copy code
[python]
...
pip_version = "24.2"

[pex-cli]
version = "v2.13.0"
known_versions = [
  "v2.13.0|macos_arm64|dba08dde901cfbc43176a7d99414c7c58a3eeaa41743d2abbdf41a58d1d5ead9|4190750",
  "v2.13.0|macos_x86_64|dba08dde901cfbc43176a7d99414c7c58a3eeaa41743d2abbdf41a58d1d5ead9|4190750",
  "v2.13.0|linux_x86_64|dba08dde901cfbc43176a7d99414c7c58a3eeaa41743d2abbdf41a58d1d5ead9|4190750",
  "v2.13.0|linux_arm64|dba08dde901cfbc43176a7d99414c7c58a3eeaa41743d2abbdf41a58d1d5ead9|4190750"
]
p
ah, I guess the redis version is a custom version I had in my private repo
w
Thanks for the reqs though - letting this run through, to get a baseline
uv
crushed that
uv pip install
- took like 20 seconds
b
uv
isn't doing anything magical. There HAS to be a reason why 🙂
p
random google lead me to believe uv does support multiplatform lock files now: https://github.com/astral-sh/uv/issues/3350 (which I think was discussed as a blocker?)
w
At a glance,
uv
is wildly parallel,
pip
doesn't seem to be (only 1 core on my machine was 100% during a
pip install --report --dry-run --ignore-installed
) And pip seemed to just fall over on the dependency resolver "INFO: pip is still looking at multiple versions of prefect-aws to determine which version is compatible with other requirements. This could take a while." Now... What
uv
did here 🤷 Are the two sets of installs identical? 🤷 But, man was that good dev experienvec
I have to concede, in this quick test, I can't claim to know what pip was doing about downloading - as in, maybe it was downloading in parallel - but just emitting to the CLI in a sequential fashion, or maybe it was legitimately going one by one
p
I see that it is possible to use uv for resolution to concrete versions and produce a requirements.txt that is unambiguous; that could be an easy way to capture uv's speed
w
Yeah, like, this is 1 second per line - that's INSANE... I've done zero research as to why that is, let me be clear, but the time it took for that second (20 lines of text, of thousands), uv was basically done downloading and installing
p
uv has the trick where you only download partial pieces of the packages
w
I see that it is possible to use uv for resolution to concrete versions and produce a requirements.txt that is unambiguous; that could be an easy way to capture uv's speed
There is a pants and pex ticket for some of this. https://github.com/pex-tool/pex/issues/2371 For me, feels like we might end up in a Vite situation, where we use one fast path for dev, one slower path for production 😆
uv has the trick where you only download partial pieces of the packages
Range requests?
p
yeah
w
I thought
--use-feature fast-deps
or some other flag enabled that for pip as well. It looks like there is some juicy perf improvements coming down from some of the pip tickets I've been reading, but I don't know timing. I think one specifically to help with metadata. Re: using
uv
instead of (or within) pex, I think that needs some practical research into what it means, backwards compat, etc. I haven't looked into any of uv's lockfiles yet - as most of my time the past few weeks has been the time between meetings during the day
p
I couldn't quite put it together in the 10 minutes I gave it, but I was thinking of something like this
Copy code
shell_command(
    name="uv_shell",
    command=(
        "uv pip compile <http://requirements.in|requirements.in> -o requirements.txt"
    ),
    tools=["uv"],
)

adhoc_tool(
  name="run_uv",
  runnable=":uv_shell",
  args=[""],
  execution_dependencies=["<http://requirements.in|requirements.in>"],
  output_files=["requirements.txt"],
)
w
Well, that's new
I couldn't quite put it together in the 10 minutes I gave it, but I was thinking of something like this
Yeah, this is an interesting idea - I just did it, and the subsequent pip install was pretty quick (comparatively, obviously)
p
could you share the way you did it to stop me from fumbling around?
w
I did it outside of pants, while messing around - trying to see if I can locally get pip and uv working together before i try pants. The adhoc tool is a good approach, I think it is just missing one other target. I'll try to look into that if I have any reasonable success right now with an experiment
🤣 So close to being awesome...
Copy code
uv pip compile requirements.in | grep -v "#" > requirements.txt
parallel -a requirements.txt -j12 pip install
c
You probably saw this already but https://github.com/pypa/pip/pull/12208 is the one against "The fast-deps feature is not a fast way to obtain dependencies"
w
Yeah, I've been through that chain of issues a few times 🙂
c
Yeah, this bombs out for me on pyspark.
The dep I have listed is just a bare
pyspark
, (yolo!) so I'm not sure what you would be bombing out on there
🤷 1
w
In any case, there are a lot of improvements to be had - but I think they would rely on somehow getting
uv
into pants/pex, or waiting on some upcoming pip improvements to trickle down, and then maybe some pex changes to support a different resolves, or
pip install --report
or something (not saying that would be a panacea, as it might have some quirks of its own when generating the lockfile)
I'm currently playing around with hacks, not solutions, e.g. could we use uv to pre-download deps and then have pex use them as cached, and other shenanigans
c
FWIW Since we are spit-balling, I suspect that https://github.com/python/cpython/issues/81338 would be the biggest overall pip/pex/pants ecosystem performance improvement (all those huge wheels getting copied around), but I have no real expertise for moving that one forward.
p
I am highly interested in hacks around uv if anyone can point me to what I need to do to fix the adhoc_tool thing to generate a requirements.txt that is fully locked
w
Not sure how the interpreter_constraints part would work with uv though - but I think in your case, you were just specifying 3.11 - which should be legit with the hack workflow. Gonna try this out shortly
@powerful-scooter-95162 How about this?
Copy code
file(
    name="requirements",
    source="<http://requirements.in|requirements.in>", 
)

system_binary(
    name="uv",
    binary_name="uv",
    fingerprint_args=["--version"],
)

run_shell_command(
    name="lock",
    command="uv pip compile <http://requirements.in|requirements.in> -o didthiswork.txt",
    runnable_dependencies=[":uv"],
    execution_dependencies=[":requirements"],
)
Then, maybe an alias that runs
pants run :lock
followed by
generate-lockfiles
?
You might not even need the
execution_dependencies
p
Is it possible to make an alias that does those two commands? if not, I can make a shell script too
would have been neat to wire it as a dep of the python_requirements target, but I guess that's actually not necessary at all
w
Good question, not sure - also not sure how caching will work, but that's the simplest example of getting the pre-compilation. I'm sure there can be more coupling, but until I implement it in one of my repos, I won't really know
p
I did some poking at uv, and the picking of a specific version with uv happens in a second, but generate-lockfiles still takes 5 minutes