Skip to content

Commit f926830

Browse files
committed
Consider duplicate packages
1 parent 117d179 commit f926830

File tree

1 file changed

+60
-59
lines changed

1 file changed

+60
-59
lines changed

crates/uv-installer/src/installed_packages.rs

+60-59
Original file line numberDiff line numberDiff line change
@@ -152,75 +152,76 @@ impl InstalledPackages {
152152
path: &Path,
153153
dist_info: &InstalledDist,
154154
) -> bool {
155-
// In most cases, there is no other distribution of the same name in path.
156-
let Some(existing) = by_name
157-
.get(dist_info.name())
158-
.into_iter()
159-
.flatten()
160-
.find_map(|dist_id| distributions[*dist_id].as_ref())
161-
else {
155+
let Some(existing_ids) = by_name.get(dist_info.name()) else {
162156
return false;
163157
};
158+
// In most cases, there is no other distribution of the same name in path.
159+
for existing_id in existing_ids {
160+
let Some(existing) = distributions[*existing_id].as_ref() else {
161+
continue;
162+
};
164163

165-
// Ignore duplicate paths in `sys.path`
166-
if existing == dist_info {
167-
return true;
168-
}
164+
// Ignore duplicate paths in `sys.path`
165+
if existing == dist_info {
166+
return true;
167+
}
169168

170-
// On fedora, purelib and platlib in a venv are `.venv/lib` and `.venv/lib64`,
171-
// with `.venv/lib64` being a symlink to `.venv/lib`. We have to deduplicate
172-
// access across this symlink, such as:
173-
// * `.venv/lib/python3.13/site-packages/foo-1.0.0.dist-info`
174-
// * `.venv/lib64/python3.13/site-packages/foo-1.0.0.dist-info`
175-
if is_same_file(existing.path(), dist_info.path()).unwrap_or(false) {
176-
return true;
177-
}
169+
// On fedora, purelib and platlib in a venv are `.venv/lib` and `.venv/lib64`,
170+
// with `.venv/lib64` being a symlink to `.venv/lib`. We have to deduplicate
171+
// access across this symlink, such as:
172+
// * `.venv/lib/python3.13/site-packages/foo-1.0.0.dist-info`
173+
// * `.venv/lib64/python3.13/site-packages/foo-1.0.0.dist-info`
174+
if is_same_file(existing.path(), dist_info.path()).unwrap_or(false) {
175+
return true;
176+
}
178177

179-
// egg-info directories are special: We may see them twice, once as dist-info
180-
// directory, and then again as egg-info directory in a subdirectory of the
181-
// package of the same name, linked from the `sys.path` entry added by the
182-
// editable. If they both point to the same path, we want to only consider the
183-
// dist-info that already covers the package.
184-
if let InstalledDist::EggInfoDirectory(InstalledEggInfoDirectory {
185-
base_path: Some(base_path),
186-
..
187-
}) = &dist_info
188-
{
189-
if let InstalledDist::Url(InstalledDirectUrlDist { url, .. }) = existing {
190-
if !is_same_file(url.path(), base_path).unwrap_or(false) {
191-
debug!(
192-
"Ignoring already processed egg-info at: `{}`",
193-
path.user_display()
194-
);
195-
return true;
178+
// egg-info directories are special: We may see them twice, once as dist-info
179+
// directory, and then again as egg-info directory in a subdirectory of the
180+
// package of the same name, linked from the `sys.path` entry added by the
181+
// editable. If they both point to the same path, we want to only consider the
182+
// dist-info that already covers the package.
183+
if let InstalledDist::EggInfoDirectory(InstalledEggInfoDirectory {
184+
base_path: Some(base_path),
185+
..
186+
}) = &dist_info
187+
{
188+
if let InstalledDist::Url(InstalledDirectUrlDist { url, .. }) = existing {
189+
if !is_same_file(url.path(), base_path).unwrap_or(false) {
190+
debug!(
191+
"Ignoring already processed egg-info at: `{}`",
192+
path.user_display()
193+
);
194+
return true;
195+
}
196196
}
197197
}
198-
}
199198

200-
// It can be valid to shadow packages, but two different distributions for the
201-
// same package name in the same directory should never happen, see e.g.
202-
// https://github.com/astral-sh/uv/issues/11648. In this case, it is not clear
203-
// of which version the module that Python will pick up is. We must keep both
204-
// to remove both.
205-
if !is_same_file(
206-
existing.path().parent().unwrap_or(Path::new("")),
207-
dist_info.path().parent().unwrap_or(Path::new("")),
208-
)
209-
.unwrap_or(false)
210-
{
211-
debug!(
212-
"The package `{}` has multiple installed distributions:\n\
213-
- version {} at `{}`\n\
214-
- version {} at `{}`",
215-
dist_info.name(),
216-
existing.version(),
217-
existing.path().user_display(),
218-
dist_info.version(),
219-
dist_info.path().user_display(),
220-
);
199+
// It can be valid to shadow packages, but two different distributions for the
200+
// same package name in the same directory should never happen, see e.g.
201+
// https://github.com/astral-sh/uv/issues/11648. In this case, it is not clear
202+
// of which version the module that Python will pick up is. We must keep both
203+
// to remove both.
204+
if !is_same_file(
205+
existing.path().parent().unwrap_or(Path::new("")),
206+
dist_info.path().parent().unwrap_or(Path::new("")),
207+
)
208+
.unwrap_or(false)
209+
{
210+
debug!(
211+
"The package `{}` has multiple installed distributions:\n\
212+
- version {} at `{}`\n\
213+
- version {} at `{}`",
214+
dist_info.name(),
215+
existing.version(),
216+
existing.path().user_display(),
217+
dist_info.version(),
218+
dist_info.path().user_display(),
219+
);
220+
// We can't skip distributions even if they are inexact duplicates as we must
221+
// uninstall them, so we continue to the `false` return.
222+
}
221223
}
222224

223-
// We can't skip duplicate distributions as we must uninstall them
224225
false
225226
}
226227

0 commit comments

Comments
 (0)