Commit Graph

189 Commits

Author SHA1 Message Date
Zanie Blue e952557bf1
Improve root message when version solving fails (#344)
Matching description at
https://github.com/dart-lang/pub/blob/master/doc/solver.md#linear-error-reporting
2023-11-06 20:07:50 +00:00
Zanie Blue b0720ea5b2
Improve error message for dependencies with no versions available (#342)
Partially addresses https://github.com/astral-sh/puffin/issues/310
Addresses case at
https://github.com/astral-sh/puffin/issues/309#issuecomment-1793541558
Follow-up to #300 ensuring `PuffinExternal` is used consistently when
formatting messages

Example at
https://github.com/astral-sh/puffin/pull/342/files#diff-5c74a74ef34ef1d6e7453de8d2d19134813156e8b6a657e6b5ed71fda5a3a870
2023-11-06 14:04:29 -06:00
Zanie Blue 1748cfb522
Display dependency versions in pip-like format during solve failure (#346)
- Display `==` for exact version ranges
- Remove space between dependency and version range
2023-11-06 13:53:15 -06:00
Charlie Marsh 24e30e6557
Split `puffin-package` into requirements.txt parser and `pypi-types` (#341)
There are only two things left in this crate and they don't really have
anything to do with one another.
2023-11-06 18:19:49 +00:00
Charlie Marsh d9bcfafa16
Write `direct_url.json` in wheel installer (#337)
## Summary

This PR just adds the logic in `install-wheel-rs` to write
`direct_url.json`. We're not actually taking advantage of it yet (or
wiring it through) in Puffin.

Part of https://github.com/astral-sh/puffin/issues/332.
2023-11-06 17:09:28 +00:00
konsti b2439b24a1
Fetch wheel metadata by async range requests on the remote wheel (#301)
Use range requests and async zip to extract the METADATA file from a
remote wheel.

We currently only cache when the remote says the remote declares the
resource as immutable, see
https://github.com/06chaynes/http-cache/issues/57 and
https://github.com/baszalmstra/async_http_range_reader/pull/1 . The
cache is stored as json with the description omitted, this improve cache
deserialization performance.
2023-11-06 15:06:49 +01:00
Charlie Marsh 6d672b8951
Add source distribution support to `pip-compile` (#323)
## Summary

This is a first-pass at adding source distribution support to the
installer.

The previous installation flow was:

1. Come up with a plan.
1. Find a distribution (specific file) for every package that we'll need
to download.
1. Download those distributions.
1. Unzip them (since we assumed they were all wheels).
1. Install them into the virtual environment.

Now, Step (3) downloads both wheels and source distributions, and we
insert a step between Steps (3) and (4) to build any source
distributions into zipped wheels.

There are a bunch of TODOs, the most important (IMO) is that we
basically have two implementations of downloading and building, between
the stuff in `puffin_installer` and `puffin_resolver` (namely in
`crates/puffin-resolver/src/distribution`). I didn't attempt to clean
that up here -- it's already a problem, and it's related to the overall
problem we need to solve around unified caching and resource management.

Closes #243.
2023-11-06 08:22:36 -05:00
konsti 81f380b10e
Validate package and extra name (#290)
`PackageName` and `ExtraName` can now only be constructed from valid
names. They share the same rules, so i gave them the same
implementation. Constructors are split between `new` (owned) and
`from_str` (borrowed), with the owned version avoiding allocations.

Closes #279

---------

Co-authored-by: Zanie <contact@zanie.dev>
2023-11-06 10:04:31 +00:00
Charlie Marsh 1637f1c216
Add source distribution support to the `DistributionFinder` (#322)
## Summary

This just enables the `DistributionFinder` (previously known as the
`WheelFinder`) to select source distributions when there are no matching
wheels for a given platform. As a reminder, the `DistributionFinder` is
a simple resolver that doesn't look at any dependencies: it just takes a
set of pinned packages, and finds a distribution to install to satisfy
each requirement.
2023-11-06 00:16:04 -05:00
Charlie Marsh d785ffdbff
Move `Source` abstraction into `puffin-distribution` (#321)
No code changes, but this will allow it to be shared between the
installer and the resolver.
2023-11-06 02:31:15 +00:00
Charlie Marsh 4b83d8e949
Require URL dependencies to be declared upfront (#319)
In the resolver, our current model for solving URL dependencies requires
that we visit the URL dependency _before_ the registry-based dependency.
This PR encodes a strict requirement that all URL dependencies be
declared upfront, either as requirements or constraints.

I wrote more about how it works and why it's necessary in documentation
[here](https://github.com/astral-sh/puffin/pull/319/files#diff-2b1c4f36af0c62a2b7bebeae9473ae083588f2a6b18a3ec52393a24266adecbbR20).
I think we could relax this constraint over time, but it requires a more
sophisticated model -- and for now, I just want something that's (1)
correct, (2) easy for us to reason about, and (3) easy for users to
reason about.

As additional motivation... allowing arbitrary URL dependencies anywhere
in the tree creates some really confusing situations in which I'm not
even sure what the right answers are. For example, assume you declare a
direct dependency on `Werkzeug==2.0.0`. You then depend on a version of
Flask that depends on a version of `Werkzeug` from some arbitrary URL.
You build the source distribution at that arbitrary URL, and it turns
out it _does_ build to a declared version of 2.0.0. What should happen?
(And if it resolves to a version that _isn't_ 2.0.0, what should happen
_then_?) I suspect different tools handle this differently, but it must
lead to a lot of "silent" failures. In my testing of Poetry, it seems
like Poetry just ignores the URL dependency, which seems wrong, but is
also a behavior we could implement in the future.

Closes https://github.com/astral-sh/puffin/issues/303.
Closes https://github.com/astral-sh/puffin/issues/284.
2023-11-05 17:09:58 +00:00
Charlie Marsh a53188cac7
Avoid unnecessarily fetching non-marker-required first-party dependencies (#318)
E.g., given:

```
flask; python_version < '3.7'
requests
```

We shouldn't request the metadata for Flask when on Python versions 3.7
or later.
2023-11-04 17:03:43 +00:00
Charlie Marsh 051188dce0
Use separate representations for canonical repository vs. commit (#317)
Given `https://github.com/pypa/package.git#subdirectory=pkg_a` and
`https://github.com/pypa/package.git#subdirectory=pkg_b`, we want these
to map to the same shared _resource_ (for locking and cloning), but
different _packages_ (for determining whether the wheel already exists
in the cache). As such, we need two distinct concepts for "canonical
equality".

Closes #316.
2023-11-04 11:46:42 -04:00
Charlie Marsh b589813e59
Enforce that built package name matches declared package name (#315)
Closes https://github.com/astral-sh/puffin/issues/306.
2023-11-03 22:58:12 +00:00
Charlie Marsh 643cf3b3aa
Unify subdirectory handling in `source.rs` (#314)
Avoids having to encode all the `git+` and `subdirectory=` logic in
multiple places.
2023-11-03 19:33:38 +00:00
Charlie Marsh edce4ccb24
Add support for subdirectories in URL dependencies (#312)
Closes https://github.com/astral-sh/puffin/issues/307.
2023-11-03 15:28:38 -04:00
Charlie Marsh aa9882eee8
Use locks to prevent concurrent accesses to the same Git repo (#304)
Ensures that if we need to access the same Git repo twice in a
resolution, we only have one handler to that repo at a time. (Otherwise,
`git2` panics.)
2023-11-03 16:33:14 +00:00
Charlie Marsh fa1bbbbe08
Write fully-precise Git SHAs to `pip-compile` output (#299)
This PR adds a mechanism by which we can ensure that we _always_ try to
refresh Git dependencies when resolving; further, we now write the fully
resolved SHA to the "lockfile". However, nothing in the code _assumes_
we do this, so the installer will remain agnostic to this behavior.

The specific approach taken here is minimally invasive. Specifically,
when we try to fetch a source distribution, we check if it's a Git
dependency; if it is, we fetch, and return the exact SHA, which we then
map back to a new URL. In the resolver, we keep track of URL
"redirects", and then we use the redirect (1) for the actual source
distribution building, and (2) when writing back out to the lockfile. As
such, none of the types outside of the resolver change at all, since
we're just mapping `RemoteDistribution` to `RemoteDistribution`, but
swapping out the internal URLs.

There are some inefficiencies here since, e.g., we do the Git fetch,
send back the "precise" URL, then a moment later, do a Git checkout of
that URL (which will be _mostly_ a no-op -- since we have a full SHA, we
don't have to fetch anything, but we _do_ check back on disk to see if
the SHA is still checked out). A more efficient approach would be to
return the path to the checked-out revision when we do this conversion
to a "precise" URL, since we'd then only interact with the Git repo
exactly once. But this runs the risk that the checked-out SHA changes
between the time we make the "precise" URL and the time we build the
source distribution.

Closes #286.
2023-11-03 16:26:57 +00:00
Zanie Blue addcfe533a
Implement custom resolution failure reporter to hide root package versions (#300)
Extends #295 
Closes #214 

Copies some of the implementations from `pubgrub::report` so we can
implement Puffin `PubGrubPackage` specific display when explaining
failed resolutions.

Here, we just drop the dummy version number if it's a
`PubGrubPackage::Root` package. In the future, we can further customize
reporting.
2023-11-03 10:47:01 -05:00
Zanie Blue e1382cc747
Report project name instead of `root` when using `pyproject.toml` files (#295)
Part of https://github.com/astral-sh/puffin/issues/214

Adds a `project: Option<PackageName>` to the `Manifest`, `Resolver`, and
`RequirementsSpecification`.
To populate an optional `name` for `PubGubPackage::Root`.

I'll work on removing the version number next.

Should we consider using the parent directory name when a
`pyproject.toml` file is not present?
2023-11-03 10:22:10 -05:00
Charlie Marsh a4002fe132
Make cache non-optional in most crates (#293)
This PR makes the cache non-optional in most of Puffin, which simplifies
the code, allows us to reuse the cache within a single command (even
with `--no-cache`), and also allows us to use the cache for disk storage
across an invocation.

I left the cache as optional for the `Virtualenv` and `InterpreterInfo`
abstractions, since those are generic enough that it seems nice to have
a non-cached version, but it's kind of arbitrary.
2023-11-02 13:40:20 -04:00
Charlie Marsh a02bf2e415
Split `source_distribution.rs` into separate wheel and sdist fetchers (#291) 2023-11-02 16:04:51 +00:00
Charlie Marsh 62c474d880
Add support for Git dependencies (#283)
## Summary

This PR adds support for Git dependencies, like:

```
flask @ git+https://github.com/pallets/flask.git
```

Right now, they're only supported in the resolver (and not the
installer), since the installer doesn't yet support source distributions
at all.

The general approach here is based on Cargo's Git implementation.
Specifically, I adapted Cargo's
[`git`](23eb492cf9/src/cargo/sources/git/mod.rs)
module to perform the cloning, which is based on `libgit2`.

As compared to Cargo's implementation, I made the following changes:

- Removed any unnecessary code.
- Fixed any Clippy errors for our stricter ruleset.
- Removed the dependency on `curl`, in favor of `reqwest` which we use
elsewhere.
- Removed the ability to use `gix`. Cargo allows the use of `gix` as an
experimental flag, but it only supports a small subset of the
operations. When Cargo fully adopts `gix`, we should plan to do the
same.
- Removed Cargo's host key checking. We need to re-add this! I'll do it
shortly.
- Removed Cargo's progress bars. We should re-add this too, but we use
`indicatif` and Cargo had their own thing.

There are a few follow-ups to consider:

- Adding support in the installer.
- When we lock, we should write out the Git URL that includes the exact
SHA. This lets us cache in perpetuity and avoids dependencies changing
without re-locking.
- When we resolve, we should _always_ try to refresh Git dependencies.
(Right now, we skip if the wheel was already built.)

I'll work on the latter two in follow-up PRs.

Closes #202.
2023-11-02 15:14:55 +00:00
konsti 4adaa9a700
Wheel filename distribution package name (#278)
The normalized name abstractions were not consistently, this PR uses
them where they were previously missing:
* `WheelFilename::distribution`
* `Requirement::name`
* `Requirement::extras`
* `Metadata21::name`
* `Metadata21::provides_dist`

With `puffin-package` depending on `pep508_rs` this would be cyclical
crate dependency, so `puffin-normalize` gets split out from
`puffin-package`.

`DistInfoName` has the same task and semantics as `PackageName`, so it's
merged into the latter.

`PackageName` and `ExtraName` documentation is moved onto the type and
their constructors are called `new` instead of `normalize`. We now use
these constructors rarely enough the implicit allocation by
`to_string()` shouldn't matter anymore, while more actual cloning
becomes visible.
2023-11-02 11:15:27 +00:00
Charlie Marsh 0c9e975f75
Rename `distribution.rs` to `file.rs` in `puffin-resolver` (#288) 2023-11-01 23:52:53 -04:00
Zanie Blue b8ff32f6be
Respect markers on constraints (#282)
Closes #252
2023-11-01 20:20:32 -05:00
Charlie Marsh 2652caa3e3
Add support for URL dependencies (#251)
## Summary

This PR adds support for resolving and installing dependencies via
direct URLs, like:

```
werkzeug @ 960bb4017c4aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl
```

These are fairly common (e.g., with `torch`), but you most often see
them as Git dependencies.

Broadly, structs like `RemoteDistribution` and friends are now enums
that can represent either registry-based dependencies or URL-based
dependencies:

```rust
/// A built distribution (wheel) that exists as a remote file (e.g., on `PyPI`).
#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)]
pub enum RemoteDistribution {
    /// The distribution exists in a registry, like `PyPI`.
    Registry(PackageName, Version, File),
    /// The distribution exists at an arbitrary URL.
    Url(PackageName, Url),
}
```

In the resolver, we now allow packages to take on an extra, optional
`Url` field:

```rust
#[derive(Debug, Clone, Eq, Derivative)]
#[derivative(PartialEq, Hash)]
pub enum PubGrubPackage {
    Root,
    Package(
        PackageName,
        Option<DistInfoName>,
        #[derivative(PartialEq = "ignore")]
        #[derivative(PartialOrd = "ignore")]
        #[derivative(Hash = "ignore")]
        Option<Url>,
    ),
}
```

However, for the purpose of version satisfaction, we ignore the URL.
This allows for the URL dependency to satisfy the transitive request in
cases like:

```
flask==3.0.0
werkzeug @ 254c3e9b5f5941e900b71206e6313b/werkzeug-3.0.1-py3-none-any.whl
```

There are a couple limitations in the current approach:

- The caching for remote URLs is done separately in the resolver vs. the
installer. I decided not to sweat this too much... We need to figure out
caching holistically.
- We don't support any sort of time-based cache for remote URLs -- they
just exist forever. This will be a problem for URL dependencies, where
we need some way to evict and refresh them. But I've deferred it for
now.
- I think I need to redo how this is modeled in the resolver, because
right now, we don't detect a variety of invalid cases, e.g., providing
two different URLs for a dependency, asking for a URL dependency and a
_different version_ of the same dependency in the list of first-party
dependencies, etc.
- (We don't yet support VCS dependencies.)
2023-11-01 09:21:44 -04:00
Charlie Marsh 079b685c8c
Use distributions for `Reporter` signatures (#266) 2023-11-01 03:19:13 +00:00
Charlie Marsh bee1b0f5ad
Avoid re-parsing wheel filename in source distribution tree (#265) 2023-10-31 21:02:09 +00:00
Charlie Marsh aff26f2301
Reuse distribution structs in Resolver's `source_distribution.rs` (#264) 2023-10-31 20:50:34 +00:00
Charlie Marsh 89dad0c9ad
Move distribution abstraction in shared crate (#258)
This also allows us to get rid of `PinnedPackage` _and_ to remove some
`Result<...>` types due to needless conversions between
otherwise-identical types.
2023-10-31 15:30:06 -04:00
Charlie Marsh 16aac834ee
Move PyPI-oriented types out of `puffin-client` crate (#255)
Just an internal change to avoid a dependency on `puffin-client` for
those crates that need access to PyPI-metadata types.
2023-10-31 17:10:23 +00:00
Charlie Marsh 1f059b30dd
Remove `Box<Pin<...>>` from `process_request` (#242) 2023-10-30 16:34:57 -04:00
konstin 1529def563 Implement mixed PEP 517 and setup.py build
There are packages such as DTLSSocket 0.1.16 that say
```toml
[build-system]
requires = ["Cython<3", "setuptools", "wheel"]
```
In this case we need to install requires PEP 517 style but then call setup.py in the
legacy way

Part of making home-assistant work
2023-10-30 19:11:52 +01:00
konsti d47dc64974
Ignore self requirements (#233)
gps3 0.33.3 depends on itself, which we can ignore. I've also added the
home assistant requirements since it occurred when testing with this.
2023-10-30 17:13:52 +01:00
Charlie Marsh 0be20a41a4
Make version selection wheel-vs.-sdist-agnostic (#232)
Closes https://github.com/astral-sh/puffin/issues/231.
2023-10-30 11:21:10 -04:00
Charlie Marsh e73d3f0ff8
Use bounded ranges rather than constructing manual ranges (#228)
I didn't realize this, but they made a bunch of improvements to how
PubGrub represents versions which lets us greatly simplify our own
PubGrub version wrapper
(https://github.com/pubgrub-rs/guide/pull/6/files).
2023-10-30 03:58:43 +00:00
Charlie Marsh 1d3ea242d4
Re-export from PubGrub module (#226) 2023-10-30 02:03:52 +00:00
Charlie Marsh f2dd0d90be
Add a resolver reporter (#225)
Closes https://github.com/astral-sh/puffin/issues/223.
2023-10-30 02:00:09 +00:00
Charlie Marsh 6da9c2f534
Tweak response buffering (#224)
In my testing, we can both increase the number of concurrent requests
and remove the `ready_chunks`.
2023-10-29 21:07:46 -04:00
Charlie Marsh 1c5cdcd70a
Prioritize packages in visited order (#222) 2023-10-30 00:48:36 +00:00
Charlie Marsh 2ba85bf80e
Add PubGrub's priority queue (#221)
Pulls in https://github.com/pubgrub-rs/pubgrub/pull/104.
2023-10-29 21:16:02 +00:00
Charlie Marsh 4209e77c95
Upgrade `pubgrub-rs` version (#220)
Upgrades our PubGrub to 8951e37fe923a7edd5a78ed5f49f165b0fdc48de.
2023-10-29 20:25:55 +00:00
Charlie Marsh 1e4259a608
Make the resolver deterministic (#218)
At a minor performance cost...

Closes https://github.com/astral-sh/puffin/issues/204.
2023-10-29 18:42:25 +00:00
Charlie Marsh bae3c89ab1
Add a `--prerelease` flag to the CLI (#217) 2023-10-29 18:39:30 +00:00
Charlie Marsh 7e7e9f8a0c
Add support for pre-release versions (#216)
We now accept a pre-release if (1) all versions are pre-releases, or (2)
there was a pre-release marker in the dependency specifiers for a direct
dependency.

The code is written such that we can support a variety of pre-release
strategies.

Closes https://github.com/astral-sh/puffin/issues/191.
2023-10-29 14:31:55 -04:00
Charlie Marsh 8b83385763
Support constraints in `requirements.in` files (#212)
Closes #172.
2023-10-27 00:41:02 +00:00
Charlie Marsh 7bce41498e
Improve debug logging in dispatcher (#206)
Also makes the order of operations more similar to that of the
`pip-compile` command.
2023-10-26 18:54:47 +00:00
konsti 5ad58474ca
Add script to check the top 8k pypi packages (#198)
To check to top 1k (current state):

```bash
scripts/resolve/get_pypi_top_8k.sh
cargo run --bin puffin-dev -- resolve-many scripts/resolve/pypi_top_8k_flat.txt --limit 1000
```

Results:
```
Errors: pywin32, geoip2, maxminddb, pypika, dirac
Success: 995, Error: 5
```
pywin32 has no solution for the build environment, 3 have no
`[build-system]` entry in pyproject.toml, `dirac` is missing cmake
2023-10-26 12:03:59 +00:00
konsti 862c1654a0
Select most recent wheel, most recent sdist (#190)
Select a compatible wheel for a version, even we already found a source
distribution previously.

If no wheel is found, select the most recent source distribution, not
the oldest compatible one.

This fixes the resolution of `mst.in`, which i added
2023-10-26 08:15:26 +00:00
Charlie Marsh 13e4171916
Inline manifest creations in resolver tests (#188) 2023-10-26 04:36:03 +00:00
Charlie Marsh 6faaf4bc24
Respect existing versions in "lockfile" (#187)
Like `pip-compile`, we now respect existing versions from the
`requirements.txt` provided via `--output-file`, unless you pass a
`--upgrade` flag.

Closes #166.
2023-10-26 04:28:58 +00:00
Charlie Marsh 61a61db154
Filter and store all distributions upfront (#185)
Modifies the resolver to remove any incompatible distributions upfront,
and store them in an index by version. This will be necessary to support
`--upgrade` semantics.

This actually does cause a meaningful slowdown right now (since we now
iterate over all files, even if we otherwise never would've needed to
touch them), but we should be able to optimize it out later.
2023-10-26 01:06:44 +00:00
Charlie Marsh 5ed913af50
Rename `SolverCache` (#184)
Everywhere else, we use cache to refer to a filesystem cache, so this is
kind of confusing. It's really an in-memory index that we build up over
the course of the solve.
2023-10-25 23:53:31 +00:00
konsti 889f6173cc
Unify python interpreter abstractions (#178)
Previously, we had two python interpreter metadata structs, one in
gourgeist and one in puffin. Both would spawn a subprocess to query
overlapping metadata and both would appear in the cli crate, if you
weren't careful you could even have to different base interpreters at
once. This change unifies this to one set of metadata, queried and
cached once.

Another effect of this crate is proper separation of python interpreter
and venv. A base interpreter (such as `/usr/bin/python/`, but also pyenv
and conda installed python) has a set of metadata. A venv has a root and
inherits the base python metadata except for `sys.prefix`, which unlike
`sys.base_prefix`, gets set to the venv root. From the root and the
interpreter info we can compute the paths inside the venv. We can reuse
the interpreter info of the base interpreter when creating a venv
without having to query the newly created `python`.
2023-10-25 20:11:36 +00:00
konsti 1fbe328257
Build source distributions in the resolver (#138)
This is isn't ready, but it can resolve
`meine_stadt_transparent==0.2.14`.

The source distributions are currently being built serially one after
the other, i don't know if that is incidentally due to the resolution
order, because sdist building is blocking or because of something in the
resolver that could be improved.

It's a bit annoying that the thing that was supposed to do http requests
now suddenly also has to a whole download/unpack/resolve/install/build
routine, it messes up the type hierarchy. The much bigger problem though
is avoid recursive crate dependencies, it's the reason for the callback
and for splitting the builder into two crates (badly named atm)
2023-10-25 20:05:13 +00:00
Charlie Marsh 0e097874f8
Add support for alternate index URLs (#169)
As elsewhere, we just use the `pip` and `pip-compile` APIs. So we
support `--index-url` to override PyPI, then `--extra-index-url` to add
_additional_ indexes, and `--no-index` to avoid hitting the index at
all.

Closes #156.
2023-10-23 03:18:30 +00:00
Charlie Marsh b665f1489a
Add tests for `puffin sync` (#161)
Closes #158.
2023-10-22 03:25:00 +00:00
Charlie Marsh 3072c3265e
Add support for lowest and lowest-direct resolution modes (#160)
Borrows terminology from pnpm by introducing three resolution modes:

- "Highest": always choose the highest compliant version (default).
- "Lowest": always choose the lowest compliant version.
- "LowestDirect": choose the lowest compliant version of direct
dependencies, and the highest compliant version of any transitive
dependencies. (This makes a bit more sense than "lowest".)

Closes https://github.com/astral-sh/puffin/issues/142.
2023-10-21 22:58:06 -04:00
konsti ae9d1f7572
Add source distribution filename abstraction (#154)
The need for this became clear when working on the source distribution
integration into the resolver.

While at it i also switch the `WheelFilename` version to the parsed
`pep440_rs` version now that we have this crate.
2023-10-20 17:45:57 +02:00
Charlie Marsh 6f52b5ca4d
Use index instead of current selection (#155)
We can also use `swap_remove` because we're discarding the vector.
2023-10-20 14:02:24 +00:00
Charlie Marsh 4645f79237
Use `FxHash` (#151) 2023-10-20 05:26:06 +00:00
Charlie Marsh 8001c792e7
Show requirement sources in `pip-compile` output (#149)
Builds up a complete resolved graph from PubGrub, and shows the sources
that led to each package being included in the resolution, like
`pip-compile`.

Closes https://github.com/astral-sh/puffin/issues/60.
2023-10-20 05:14:59 +00:00
Charlie Marsh e662fe341b
Short-circuit when a dependency has no matching versions (#148)
Kind of an oversight in my initial implementation. If we find that any
package has _no_ matching versions, we should select it! This lets us
short-circuit _immediately_ when top-level dependencies aren't
satisfiable.
2023-10-20 03:49:20 +00:00
Charlie Marsh 9b3405bf0e
Upgrade PubGrub to dev branch (#147)
Updates to `29c48fb9f3daa11bd02794edd55060d0b01ee705` from the
`pubgrub-rs` dev branch. This lets us reduce the number of changes we've
made to PubGrub itself (now, only changing visibility to export a few
things from the `solver.rs` module).
2023-10-20 03:23:26 +00:00
Charlie Marsh 03101c6a5c
Add an autogeneration header to pip-compile (#145)
Closes https://github.com/astral-sh/puffin/issues/132.
2023-10-19 20:57:27 -04:00
Charlie Marsh 0b60804db6
Add support for constraints during pip-compile resolution (#144)
Closes https://github.com/astral-sh/puffin/issues/130.
2023-10-20 00:24:05 +00:00
Charlie Marsh 89db5d79bc
Add support for lenient parsing (#115)
This PR enables us to make "fixups" to bad metadata. I copied over the
one fixup that @konstin made in `monotrail-resolve`, and added a few
common ones for `Requires-Python`.
2023-10-17 22:03:16 -04:00
Charlie Marsh 0d90256151
Store all distributions rather than compatible wheels (#114)
This PR reverts #109 which is actually a performance _regression_ since
we need to iterate over a bunch of wheels that we could otherwise
entirely ignore.
2023-10-17 17:09:31 -04:00
Charlie Marsh 5b046a8102
Use `select!` instead of `tokio::spawn` for network thread (#110) 2023-10-16 15:41:25 -04:00
Charlie Marsh 1b433fdcee
Only store compatible wheels in the resolver (#109)
Rather than constantly iterating over all files and testing their
compatibility with the current platform, just store wheels we can
actually consider in the solver cache.
2023-10-16 19:21:07 +00:00
Charlie Marsh 5f5788e866
Surface PubGrub derivation trees (#108)
I think the derivation trees could be stronger but this exposes
PubGrub's proof-like error messages.

Closes #102.
2023-10-16 14:14:36 -04:00
Charlie Marsh bae52d5edd
Surface request stream errors in the resolver (#107)
Closes https://github.com/astral-sh/puffin/issues/105.
2023-10-16 17:26:46 +00:00
Charlie Marsh 471a1d657d
Migrate resolver proof-of-concept to PubGrub (#97)
## Summary

This PR enables the proof-of-concept resolver to backtrack by way of
using the `pubgrub-rs` crate.

Rather than using PubGrub as a _framework_ (implementing the
`DependencyProvider` trait, letting PubGrub call us), I've instead
copied over PubGrub's primary solver hook (which is only ~100 lines or
so) and modified it for our purposes (e.g., made it async).

There's a lot to improve here, but it's a start that will let us
understand PubGrub's appropriateness for this problem space. A few
observations:

- In simple cases, the resolver is slower than our current (naive)
resolver. I think it's just that the pipelining isn't as efficient as in
the naive case, where we can just stream package and version fetches
concurrently without any bottlenecks.
- A lot of the code here relates to bridging PubGrub with our own
abstractions -- so we need a `PubGrubPackage`, a `PubGrubVersion`, etc.
2023-10-15 22:05:44 -04:00
konsti 530edb6e39
Add output file option to compile (#93)
`pip-compile` has the same option. I need this esp. since piping doesn't
work as we write to stdout.
2023-10-12 20:42:06 +02:00
Charlie Marsh a622345fbc
Replace mocked server with 'real' integration tests (#91)
We can always restore these from history, but right now, it feels a lot
more productive to just hit PyPI directly for our integration tests,
since we don't have to spend time figuring out mocks.
2023-10-12 17:34:48 +00:00
Charlie Marsh 906a482499
Separate unzip into its own install phase (#87) 2023-10-11 15:18:23 +00:00
Charlie Marsh ed68d31e03
Add a basic test for the resolver (#86)
Mocks out the PyPI client using some checked-in fixtures. The test is
very basic, and I'm not very happy with all the ceremony around the
mocks and such, but it's an interesting experiment at least.
2023-10-11 03:30:53 +00:00
Charlie Marsh a0294a510c
Rework `puffin sync` output to summarize (#81)
This also moves away from using `tracing` for user-facing logging,
instead introducing a new `Printer` abstraction.

Closes #66.
2023-10-10 03:29:09 +00:00
Charlie Marsh ba2b200fce
Enable release builds via `cargo-dist` (#79) 2023-10-09 20:48:55 +00:00
Charlie Marsh 485b1dceb6
Use a single requirements iterator in `sync` (#71) 2023-10-09 03:29:38 +00:00
Charlie Marsh ba72950546
Avoid passing cached wheels to the resolver step (#70)
When we go to install a locked `requirements.txt`, if a wheel is already
available in the local cache, and matches the version specifiers, we can
just use it directly without fetching the package metadata. This speeds
up the no-op case by about 33%.

Closes https://github.com/astral-sh/puffin/issues/48.
2023-10-08 22:17:19 -04:00
Charlie Marsh a53f697f62
Use `tracing` for user-facing output (#63)
The setup is now as follows:

- All user-facing logging goes through `tracing` at an `info` leve.
(This excludes messages that go to `stdout`, like the compiled
`requirements.txt` file.)
- We have `--quiet` and `--verbose` command-line flags to set the
tracing filter and format defaults. So if you use `--verbose`, we
include timestamps and targets, and filter at `puffin=debug` level.
- However, we always respect `RUST_LOG`. So you can override the
_filter_ via `RUST_LOG`.

For example: the standard setup filters to `puffin=info`, and doesn't
show timestamps or targets:

<img width="1235" alt="Screen Shot 2023-10-08 at 3 41 22 PM"
src="https://github.com/astral-sh/puffin/assets/1309177/54ca4db6-c66a-439e-bfa3-b86dee136e45">

If you run with `--verbose`, you get debug logging, but confined to our
crates:

<img width="1235" alt="Screen Shot 2023-10-08 at 3 41 57 PM"
src="https://github.com/astral-sh/puffin/assets/1309177/c5c1af11-7f7a-4038-a173-d9eca4c3630b">

If you want verbose logging with _all_ crates, you can add
`RUST_LOG=debug`:

<img width="1235" alt="Screen Shot 2023-10-08 at 3 42 39 PM"
src="https://github.com/astral-sh/puffin/assets/1309177/0b5191f4-4db0-4db9-86ba-6f9fa521bcb6">

I think this is a reasonable setup, though we can see how it feels and
refine over time.

Closes https://github.com/astral-sh/puffin/issues/57.
2023-10-08 15:46:06 -04:00
Charlie Marsh 92160e37df
Surface error when unable to find package (#45) 2023-10-07 19:43:12 +00:00
Charlie Marsh ae28552b3a
Use local copy of `install-wheel-rs` (#34)
This PR modifies the `install-wheel-rs` (and a few other crates) to get
everything playing nicely. Specifically, CI should pass, and all these
crates now use workspace dependencies between one another.

As part of this change, I split out the wheel name parsing into its own
`wheel-filename` crate, and the compatibility tag parsing into its own
`platform-tags` crate.
2023-10-07 01:43:55 +00:00
Charlie Marsh c8477991a9
Use local versions of PEP 440 and PEP 508 crates (#32)
This PR modifies the PEP 440 and PEP 508 crates to pass CI, primarily by
fixing all lint violations.

We're also now using these crates in the workspace via `path`.
(Previously, we were still fetching them from Cargo.)
2023-10-07 00:16:44 +00:00
Charlie Marsh ca6aa207ff
Move to workspace dependencies (#25) 2023-10-06 19:49:41 +00:00
Charlie Marsh dab70a661a
Change `install` to `sync` (with sync semantics) (#24)
For better separate at this stage (and following `pip-tools`), it's now
`puffin sync`, and it assumes `--no-deps`.
2023-10-06 19:42:58 +00:00
Charlie Marsh ff8e24a621
Move `puffin-installer` to its own crate (#23) 2023-10-06 19:31:21 +00:00